From 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a Mon Sep 17 00:00:00 2001 From: "Charles.Forsyth" Date: Fri, 22 Dec 2006 21:39:35 +0000 Subject: 20060303 --- os/NOTICE | 31 + os/README | 32 + os/boot/README | 33 + os/boot/arm1110/Mk | 8 + os/boot/arm1110/dat.h | 1 + os/boot/arm1110/donprint.c | 332 +++ os/boot/arm1110/fns.h | 7 + os/boot/arm1110/il.s | 99 + os/boot/arm1110/imain.c | 48 + os/boot/arm1110/inflate.c | 208 ++ os/boot/arm1110/io.h | 261 ++ os/boot/arm1110/l.s | 454 ++++ os/boot/arm1110/lib.h | 143 ++ os/boot/arm1110/map | 10 + os/boot/arm1110/mem.h | 215 ++ os/boot/arm1110/mkfile | 86 + os/boot/arm1110/print.c | 56 + os/boot/arm1110/uart.c | 69 + os/boot/libflate/LICENCE | 237 ++ os/boot/libflate/NOTICE | 8 + os/boot/libflate/adler.c | 71 + os/boot/libflate/crc.c | 39 + os/boot/libflate/deflate.c | 1358 ++++++++++ os/boot/libflate/deflateblock.c | 55 + os/boot/libflate/deflatezlib.c | 59 + os/boot/libflate/deflatezlibblock.c | 33 + os/boot/libflate/flateerr.c | 22 + os/boot/libflate/inflate.c | 692 +++++ os/boot/libflate/inflateblock.c | 53 + os/boot/libflate/inflatezlib.c | 65 + os/boot/libflate/inflatezlibblock.c | 67 + os/boot/libflate/mkfile | 21 + os/boot/libflate/zlib.h | 11 + os/boot/mpc/NOTICE | 3 + os/boot/mpc/alarm.c | 123 + os/boot/mpc/all.h | 6 + os/boot/mpc/archfads.c | 355 +++ os/boot/mpc/archfads.h | 42 + os/boot/mpc/archpaq.c | 240 ++ os/boot/mpc/archpaq.h | 29 + os/boot/mpc/boot.h | 8 + os/boot/mpc/bootp.c | 507 ++++ os/boot/mpc/clock.c | 71 + os/boot/mpc/conf.c | 173 ++ os/boot/mpc/console.c | 173 ++ os/boot/mpc/cpm.c | 162 ++ os/boot/mpc/crc32.c | 42 + os/boot/mpc/dat.h | 217 ++ os/boot/mpc/defont0.c | 216 ++ os/boot/mpc/devether.c | 157 ++ os/boot/mpc/devuart.c | 230 ++ os/boot/mpc/dload.c | 103 + os/boot/mpc/donprint.c | 332 +++ os/boot/mpc/dosboot.c | 614 +++++ os/boot/mpc/dosfs.h | 110 + os/boot/mpc/etherif.h | 59 + os/boot/mpc/etherscc.c | 411 +++ os/boot/mpc/fblt.c | 531 ++++ os/boot/mpc/flash.c | 212 ++ os/boot/mpc/fns.h | 117 + os/boot/mpc/gbitbltclip.c | 52 + os/boot/mpc/gnot.h | 71 + os/boot/mpc/i2c.c | 351 +++ os/boot/mpc/initfads.c | 187 ++ os/boot/mpc/initpaq.c | 101 + os/boot/mpc/initrpcg.c | 91 + os/boot/mpc/io.h | 463 ++++ os/boot/mpc/ip.h | 98 + os/boot/mpc/l.s | 370 +++ os/boot/mpc/lib.h | 106 + os/boot/mpc/main.c | 524 ++++ os/boot/mpc/mem.c | 0 os/boot/mpc/mem.h | 95 + os/boot/mpc/mkfile | 116 + os/boot/mpc/ms2.c | 179 ++ os/boot/mpc/plan9boot.c | 96 + os/boot/mpc/qio.c | 128 + os/boot/mpc/rmap.c | 104 + os/boot/mpc/screen.c | 242 ++ os/boot/mpc/sload.c | 71 + os/boot/mpc/squeeze.h | 34 + os/boot/mpc/trap.c | 233 ++ os/boot/mpc/uartboot.c | 189 ++ os/boot/mpc/ureg.h | 43 + os/boot/mpc/zqs.c | 234 ++ os/boot/pc/8250.c | 308 +++ os/boot/pc/LICENCE | 237 ++ os/boot/pc/NOTICE | 4 + os/boot/pc/alarm.c | 123 + os/boot/pc/apm.c | 16 + os/boot/pc/bcom.c | 460 ++++ os/boot/pc/boot.c | 451 ++++ os/boot/pc/bootld.c | 108 + os/boot/pc/bootp.c | 652 +++++ os/boot/pc/cga.c | 91 + os/boot/pc/clock.c | 305 +++ os/boot/pc/conf.c | 537 ++++ os/boot/pc/console.c | 236 ++ os/boot/pc/dat.h | 212 ++ os/boot/pc/devfloppy.c | 853 ++++++ os/boot/pc/devfloppy.h | 196 ++ os/boot/pc/devi82365.c | 1205 +++++++++ os/boot/pc/devpccard.c | 1957 ++++++++++++++ os/boot/pc/devsd.c | 617 +++++ os/boot/pc/dma.c | 245 ++ os/boot/pc/dosboot.c | 582 +++++ os/boot/pc/dosfs.h | 62 + os/boot/pc/eipfmt.c | 145 ++ os/boot/pc/error.h | 58 + os/boot/pc/ether.c | 282 ++ os/boot/pc/ether2000.c | 110 + os/boot/pc/ether2114x.c | 1652 ++++++++++++ os/boot/pc/ether589.c | 211 ++ os/boot/pc/ether79c970.c | 539 ++++ os/boot/pc/ether8003.c | 258 ++ os/boot/pc/ether8139.c | 614 +++++ os/boot/pc/ether8169.c | 847 ++++++ os/boot/pc/ether82557.c | 881 +++++++ os/boot/pc/ether83815.c | 825 ++++++ os/boot/pc/ether8390.c | 715 ++++++ os/boot/pc/ether8390.h | 71 + os/boot/pc/etherec2t.c | 155 ++ os/boot/pc/etherelnk3.c | 1898 ++++++++++++++ os/boot/pc/etherif.h | 46 + os/boot/pc/etherigbe.c | 1706 ++++++++++++ os/boot/pc/ethermii.c | 224 ++ os/boot/pc/ethermii.h | 116 + os/boot/pc/etherrhine.c | 676 +++++ os/boot/pc/fns.h | 155 ++ os/boot/pc/fs.c | 94 + os/boot/pc/fs.h | 36 + os/boot/pc/getcallerpc.c | 8 + os/boot/pc/ilock.c | 24 + os/boot/pc/inflate.c | 199 ++ os/boot/pc/io.h | 201 ++ os/boot/pc/ip.h | 100 + os/boot/pc/kbd.c | 489 ++++ os/boot/pc/kfs.h | 57 + os/boot/pc/kfsboot.c | 256 ++ os/boot/pc/l.s | 1079 ++++++++ os/boot/pc/lib.h | 102 + os/boot/pc/load.c | 563 ++++ os/boot/pc/mbr.s | 259 ++ os/boot/pc/mem.h | 114 + os/boot/pc/memory.c | 504 ++++ os/boot/pc/mkfile | 241 ++ os/boot/pc/noether.c | 40 + os/boot/pc/part.c | 344 +++ os/boot/pc/pbs.s | 372 +++ os/boot/pc/pbsdisk | Bin 0 -> 509 bytes os/boot/pc/pbsdisk.s | 344 +++ os/boot/pc/pbsdisklba | Bin 0 -> 509 bytes os/boot/pc/pbsdisklba.s | 327 +++ os/boot/pc/pbslba.s | 362 +++ os/boot/pc/pci.c | 852 ++++++ os/boot/pc/print.c | 31 + os/boot/pc/queue.c | 44 + os/boot/pc/sd.h | 127 + os/boot/pc/sd53c8xx.c | 2120 +++++++++++++++ os/boot/pc/sd53c8xx.i | 769 ++++++ os/boot/pc/sdata.c | 1639 ++++++++++++ os/boot/pc/sdmylex.c | 1294 ++++++++++ os/boot/pc/sdscsi.c | 376 +++ os/boot/pc/trap.c | 331 +++ os/boot/pc/ureg.h | 27 + os/boot/pc/x16.h | 159 ++ os/boot/puma/8250.c | 312 +++ os/boot/puma/alarm.c | 123 + os/boot/puma/armv4.h | 99 + os/boot/puma/boot.h | 12 + os/boot/puma/bootp.c | 502 ++++ os/boot/puma/cga.c | 92 + os/boot/puma/clock.c | 154 ++ os/boot/puma/conf.c | 181 ++ os/boot/puma/console.c | 181 ++ os/boot/puma/dat.h | 205 ++ os/boot/puma/div.s | 122 + os/boot/puma/donprint.c | 332 +++ os/boot/puma/dosboot.c | 614 +++++ os/boot/puma/dosfs.h | 110 + os/boot/puma/ebsit.trap.c | 206 ++ os/boot/puma/ether.c | 156 ++ os/boot/puma/ether.h | 82 + os/boot/puma/ether8900.c | 555 ++++ os/boot/puma/flash.c | 226 ++ os/boot/puma/fns.h | 111 + os/boot/puma/hard.c | 773 ++++++ os/boot/puma/io.h | 3 + os/boot/puma/ip.h | 98 + os/boot/puma/kbd.c | 482 ++++ os/boot/puma/l.s | 427 +++ os/boot/puma/lib.h | 107 + os/boot/puma/main.c | 552 ++++ os/boot/puma/mem.h | 38 + os/boot/puma/mkfile | 71 + os/boot/puma/outb.c | 20 + os/boot/puma/plan9boot.c | 93 + os/boot/puma/puma.c | 123 + os/boot/puma/puma.h | 435 ++++ os/boot/puma/qio.c | 128 + os/boot/puma/rmap.c | 104 + os/boot/puma/squeeze.h | 34 + os/boot/puma/sum.c | 13 + os/boot/puma/trap.c | 190 ++ os/boot/puma/ureg.h | 22 + os/boot/puma/zqs.c | 216 ++ os/boot/rpcg/NOTICE | 3 + os/boot/rpcg/alarm.c | 123 + os/boot/rpcg/all.h | 6 + os/boot/rpcg/archrpcg.c | 279 ++ os/boot/rpcg/archrpcg.h | 48 + os/boot/rpcg/boot.h | 8 + os/boot/rpcg/bootp.c | 509 ++++ os/boot/rpcg/clock.c | 71 + os/boot/rpcg/conf.c | 173 ++ os/boot/rpcg/console.c | 173 ++ os/boot/rpcg/cpm.c | 162 ++ os/boot/rpcg/crc32.c | 42 + os/boot/rpcg/dat.h | 217 ++ os/boot/rpcg/defont0.c | 216 ++ os/boot/rpcg/devether.c | 157 ++ os/boot/rpcg/devuart.c | 230 ++ os/boot/rpcg/dload.c | 103 + os/boot/rpcg/donprint.c | 332 +++ os/boot/rpcg/dosboot.c | 614 +++++ os/boot/rpcg/dosfs.h | 110 + os/boot/rpcg/etherif.h | 59 + os/boot/rpcg/etherscc.c | 411 +++ os/boot/rpcg/fblt.c | 531 ++++ os/boot/rpcg/flash.c | 212 ++ os/boot/rpcg/fns.h | 118 + os/boot/rpcg/g.mx | 1987 ++++++++++++++ os/boot/rpcg/gbitbltclip.c | 52 + os/boot/rpcg/gnot.h | 71 + os/boot/rpcg/i2c.c | 360 +++ os/boot/rpcg/initfads.c | 187 ++ os/boot/rpcg/initpaq.c | 101 + os/boot/rpcg/initrpcg.c | 91 + os/boot/rpcg/io.h | 463 ++++ os/boot/rpcg/ip.h | 98 + os/boot/rpcg/l.s | 388 +++ os/boot/rpcg/lib.h | 106 + os/boot/rpcg/libg.h | 418 +++ os/boot/rpcg/main.c | 527 ++++ os/boot/rpcg/mem.c | 0 os/boot/rpcg/mem.h | 94 + os/boot/rpcg/mkfile | 120 + os/boot/rpcg/ms2.c | 179 ++ os/boot/rpcg/plan9boot.c | 96 + os/boot/rpcg/qbromrpcg | Bin 0 -> 91295 bytes os/boot/rpcg/qio.c | 128 + os/boot/rpcg/rmap.c | 104 + os/boot/rpcg/screen.c | 242 ++ os/boot/rpcg/sload | Bin 0 -> 59188 bytes os/boot/rpcg/sload.c | 71 + os/boot/rpcg/squeeze.h | 34 + os/boot/rpcg/trap.c | 233 ++ os/boot/rpcg/uartboot.c | 189 ++ os/boot/rpcg/ureg.h | 43 + os/boot/rpcg/zqs.c | 234 ++ os/cerf1110/Mk | 8 + os/cerf1110/NOTICE | 2 + os/cerf1110/README | 22 + os/cerf1110/archcerf.c | 177 ++ os/cerf1110/cerf | 159 ++ os/cerf1110/dat.h | 135 + os/cerf1110/devata.c | 1200 +++++++++ os/cerf1110/devcerf.c | 128 + os/cerf1110/ether8900.c | 702 +++++ os/cerf1110/fns.h | 179 ++ os/cerf1110/io.h | 1 + os/cerf1110/main.c | 255 ++ os/cerf1110/mem.h | 200 ++ os/cerf1110/mkfile | 101 + os/cerf250/NOTICE | 2 + os/cerf250/README | 44 + os/cerf250/archcerf.c | 266 ++ os/cerf250/cerf | 163 ++ os/cerf250/dat.h | 135 + os/cerf250/devpcf8563.c | 371 +++ os/cerf250/ether91c111.c | 1056 ++++++++ os/cerf250/fns.h | 168 ++ os/cerf250/io.h | 1 + os/cerf250/main.c | 351 +++ os/cerf250/mem.h | 174 ++ os/cerf250/mkfile | 102 + os/cerf250/uart.h | 118 + os/cerf405/NOTICE | 3 + os/cerf405/README | 45 + os/cerf405/cerf | 139 + os/cerf405/clock.c | 159 ++ os/cerf405/compile.c | 34 + os/cerf405/dat.h | 159 ++ os/cerf405/devboot.c | 132 + os/cerf405/devether.c | 617 +++++ os/cerf405/devrtc.c | 369 +++ os/cerf405/devuart.c | 1064 ++++++++ os/cerf405/etheremac.c | 829 ++++++ os/cerf405/etherif.h | 37 + os/cerf405/fns.h | 147 ++ os/cerf405/fpi.h | 61 + os/cerf405/fpipower.c | 970 +++++++ os/cerf405/gpio.c | 106 + os/cerf405/iic.c | 605 +++++ os/cerf405/inb.s | 127 + os/cerf405/io.h | 301 +++ os/cerf405/l.s | 795 ++++++ os/cerf405/main.c | 719 ++++++ os/cerf405/mal.c | 334 +++ os/cerf405/mem.h | 147 ++ os/cerf405/mkfile | 104 + os/cerf405/mmu.c | 122 + os/cerf405/nand.c | 96 + os/cerf405/nofp.s | 31 + os/cerf405/pci.c | 981 +++++++ os/cerf405/physmem.h | 22 + os/cerf405/powerbreak.c | 123 + os/cerf405/rmap.c | 106 + os/cerf405/tlb.s | 30 + os/cerf405/trap.c | 581 +++++ os/cerf405/uart.c | 139 + os/cerf405/uart.h | 101 + os/fads/NOTICE | 4 + os/fads/archfads.c | 619 +++++ os/fads/archfads.h | 33 + os/fads/dat.h | 162 ++ os/fads/fads | 121 + os/fads/fns.h | 117 + os/fads/io.h | 1 + os/fads/main.c | 392 +++ os/fads/mem.h | 157 ++ os/fads/mkfile | 107 + os/fads/mmu.c | 20 + os/fads/tlb.s | 23 + os/init/README | 5 + os/init/bootinit.b | 628 +++++ os/init/cerf405.b | 598 +++++ os/init/cerfinit.b | 612 +++++ os/init/evalinit.b | 119 + os/init/geninit.b | 93 + os/init/i4e.b | 210 ++ os/init/init.b | 613 +++++ os/init/ipaqinit.b | 697 +++++ os/init/ipeinit.b | 620 +++++ os/init/jsinit.b | 213 ++ os/init/mkfile | 27 + os/init/mpcinit.b | 383 +++ os/init/pcdemo.b | 273 ++ os/init/pcinit.b | 405 +++ os/init/reminit.b | 239 ++ os/init/rpcginit.b | 610 +++++ os/init/shell.b | 97 + os/init/soeinit.b | 613 +++++ os/init/srvinit.b | 209 ++ os/init/wminit.b | 231 ++ os/ip/arp.c | 681 +++++ os/ip/bootp.c | 231 ++ os/ip/compress.c | 520 ++++ os/ip/devip.c | 1419 ++++++++++ os/ip/dhcp.c | 447 ++++ os/ip/eipconvtest.c | 152 ++ os/ip/esp.c | 866 +++++++ os/ip/ethermedium.c | 792 ++++++ os/ip/gre.c | 282 ++ os/ip/icmp.c | 490 ++++ os/ip/icmp6.c | 917 +++++++ os/ip/igmp.c | 291 +++ os/ip/ihbootp.c | 323 +++ os/ip/il.c | 1408 ++++++++++ os/ip/ip.c | 805 ++++++ os/ip/ip.h | 673 +++++ os/ip/ipaux.c | 730 ++++++ os/ip/ipifc.c | 1721 +++++++++++++ os/ip/ipmux.c | 857 ++++++ os/ip/iproute.c | 852 ++++++ os/ip/iprouter.c | 56 + os/ip/ipv6.c | 747 ++++++ os/ip/ipv6.h | 185 ++ os/ip/kernel.h | 10 + os/ip/loopbackmedium.c | 121 + os/ip/netdevmedium.c | 153 ++ os/ip/netlog.c | 263 ++ os/ip/nullmedium.c | 39 + os/ip/pktmedium.c | 79 + os/ip/plan9.c | 36 + os/ip/ppp.c | 1656 ++++++++++++ os/ip/ppp.h | 258 ++ os/ip/pppmedium.c | 192 ++ os/ip/ptclbsum.c | 72 + os/ip/rudp.c | 1085 ++++++++ os/ip/tcp.c | 3177 +++++++++++++++++++++++ os/ip/udp.c | 649 +++++ os/ipaq1110/Mk | 8 + os/ipaq1110/NOTICE | 6 + os/ipaq1110/README | 65 + os/ipaq1110/archipaq.c | 376 +++ os/ipaq1110/dat.h | 135 + os/ipaq1110/defont.c | 290 +++ os/ipaq1110/devaudio.c | 1056 ++++++++ os/ipaq1110/devipaq.c | 762 ++++++ os/ipaq1110/etherwavelan.c | 1307 ++++++++++ os/ipaq1110/fns.h | 179 ++ os/ipaq1110/inflate | Bin 0 -> 20480 bytes os/ipaq1110/io.h | 60 + os/ipaq1110/ipaq | 172 ++ os/ipaq1110/lcd.c | 185 ++ os/ipaq1110/main.c | 255 ++ os/ipaq1110/mem.h | 200 ++ os/ipaq1110/mkfile | 103 + os/ipaq1110/screen.c | 917 +++++++ os/ipaq1110/screen.h | 64 + os/ipaq1110/tstdraw.b | 107 + os/ipaq1110/upd | 4 + os/ipengine/NOTICE | 4 + os/ipengine/README | 40 + os/ipengine/archipe.c | 488 ++++ os/ipengine/archipe.h | 32 + os/ipengine/dat.h | 162 ++ os/ipengine/devfpga.c | 389 +++ os/ipengine/flash28f320b3b.c | 43 + os/ipengine/fns.h | 120 + os/ipengine/io.h | 1 + os/ipengine/ipe | 109 + os/ipengine/main.c | 348 +++ os/ipengine/mem.h | 156 ++ os/ipengine/mkfile | 105 + os/ipengine/mmu.c | 20 + os/ipengine/tlb.s | 21 + os/js/README | 10 + os/js/audio.h | 9 + os/js/clock.c | 104 + os/js/cs4231.h | 20 + os/js/dat.h | 163 ++ os/js/devcs4231.c | 1186 +++++++++ os/js/devrtc.c | 413 +++ os/js/fns.h | 110 + os/js/fsv.c | 198 ++ os/js/io.h | 224 ++ os/js/iob.c | 13 + os/js/js | 104 + os/js/kbd.c | 482 ++++ os/js/l.s | 560 ++++ os/js/main.c | 564 ++++ os/js/mem.h | 149 ++ os/js/mkfile | 77 + os/js/mmu.c | 252 ++ os/js/ns16552.h | 81 + os/js/rom.c | 103 + os/js/rom.h | 57 + os/js/screen.c | 483 ++++ os/js/screen.h | 49 + os/js/softcursor.h | 13 + os/js/superio.c | 216 ++ os/js/trap.c | 472 ++++ os/js/ureg.h | 45 + os/ks32/Mk | 7 + os/ks32/NOTICE | 2 + os/ks32/archevaluator7t.c | 161 ++ os/ks32/armv7.h | 19 + os/ks32/clock.c | 287 +++ os/ks32/dat.h | 218 ++ os/ks32/devuart.c | 719 ++++++ os/ks32/download.ps | 1040 ++++++++ os/ks32/evaluator7t | 110 + os/ks32/fns.h | 145 ++ os/ks32/fpi.h | 61 + os/ks32/fpiarm.c | 483 ++++ os/ks32/io.h | 168 ++ os/ks32/l.s | 205 ++ os/ks32/main.c | 289 +++ os/ks32/mem.h | 54 + os/ks32/mkfile | 115 + os/ks32/not.c | 3 + os/ks32/squirt | 2 + os/ks32/trap.c | 525 ++++ os/manga/Mk | 8 + os/manga/archmanga.c | 164 ++ os/manga/clock.c | 166 ++ os/manga/dat.h | 128 + os/manga/devether.c | 765 ++++++ os/manga/devusb.c | 931 +++++++ os/manga/eswnotes | 41 + os/manga/ether8139.c | 744 ++++++ os/manga/etherif.h | 44 + os/manga/etherks8695.c | 1169 +++++++++ os/manga/flashif.h | 82 + os/manga/fns.h | 163 ++ os/manga/fpi.h | 61 + os/manga/fpiarm.c | 483 ++++ os/manga/gpio.c | 75 + os/manga/inb.c | 85 + os/manga/io.h | 320 +++ os/manga/ioring.c | 72 + os/manga/l.s | 404 +++ os/manga/main.c | 317 +++ os/manga/manga | 137 + os/manga/mem.h | 133 + os/manga/mkfile | 89 + os/manga/mmu.c | 244 ++ os/manga/pci.c | 1007 ++++++++ os/manga/pinflate | Bin 0 -> 20480 bytes os/manga/trap.c | 498 ++++ os/manga/uartks8695.c | 629 +++++ os/manga/usb.h | 160 ++ os/manga/usbuhci.c | 1556 +++++++++++ os/mpc/800io.h | 666 +++++ os/mpc/NOTICE | 3 + os/mpc/clock.c | 145 ++ os/mpc/cpm.c | 695 +++++ os/mpc/cpmtimer.c | 179 ++ os/mpc/devata.c | 1194 +++++++++ os/mpc/devbench.c | 243 ++ os/mpc/devboot.c | 132 + os/mpc/devether.c | 617 +++++ os/mpc/devpcmcia.c | 1076 ++++++++ os/mpc/devrtc.c | 258 ++ os/mpc/devtouch.c | 497 ++++ os/mpc/devuart.c | 1450 +++++++++++ os/mpc/dsp.c | 289 +++ os/mpc/dsp.h | 62 + os/mpc/etherif.h | 37 + os/mpc/etherscc.c | 528 ++++ os/mpc/faultpower.c | 49 + os/mpc/fp.s | 205 ++ os/mpc/fpi.h | 61 + os/mpc/fpipower.c | 970 +++++++ os/mpc/i2c.c | 439 ++++ os/mpc/i2c_spi.srx | 149 ++ os/mpc/inb.s | 120 + os/mpc/kbd.c | 20 + os/mpc/l.s | 685 +++++ os/mpc/nofp.s | 31 + os/mpc/pcmcia.h | 19 + os/mpc/pit.c | 68 + os/mpc/powerbreak.c | 123 + os/mpc/rmap.c | 106 + os/mpc/screen.c | 832 ++++++ os/mpc/screen.h | 60 + os/mpc/spi.c | 243 ++ os/mpc/trap.c | 554 ++++ os/mpc/usb.h | 62 + os/omap/README | 4 + os/pc/NOTICE | 8 + os/pc/README | 9 + os/pc/apbootstrap.h | 15 + os/pc/apbootstrap.s | 110 + os/pc/apic.c | 378 +++ os/pc/apm.c | 151 ++ os/pc/apmjump.s | 98 + os/pc/archmp.c | 138 + os/pc/audio.h | 15 + os/pc/cga.c | 127 + os/pc/cgamemscr.c | 203 ++ os/pc/crystal.h | 1118 ++++++++ os/pc/dat.h | 258 ++ os/pc/devarch.c | 940 +++++++ os/pc/devds1620.c | 368 +++ os/pc/devether.c | 539 ++++ os/pc/devfloppy.c | 1082 ++++++++ os/pc/devi82365.c | 1044 ++++++++ os/pc/devlm78.c | 346 +++ os/pc/devlpt.c | 245 ++ os/pc/devmouse.c | 672 +++++ os/pc/devmpeg.c | 1063 ++++++++ os/pc/devpccard.c | 1949 ++++++++++++++ os/pc/devpnp.c | 652 +++++ os/pc/devrtc.c | 461 ++++ os/pc/devtv.c | 1826 +++++++++++++ os/pc/devusb.c | 951 +++++++ os/pc/devvga.c | 620 +++++ os/pc/devzt5512.c | 308 +++ os/pc/dma.c | 237 ++ os/pc/ether2000.c | 230 ++ os/pc/ether2114x.c | 1828 +++++++++++++ os/pc/ether589.c | 214 ++ os/pc/ether79c960.c | 523 ++++ os/pc/ether79c970.c | 640 +++++ os/pc/ether8003.c | 271 ++ os/pc/ether8139.c | 755 ++++++ os/pc/ether82543gc.c | 1367 ++++++++++ os/pc/ether82557.c | 1325 ++++++++++ os/pc/ether83815.c | 994 +++++++ os/pc/ether8390.c | 812 ++++++ os/pc/ether8390.h | 74 + os/pc/etherec2t.c | 173 ++ os/pc/etherelnk3.c | 2132 +++++++++++++++ os/pc/etherga620.c | 1224 +++++++++ os/pc/etherga620fw.h | 4858 +++++++++++++++++++++++++++++++++++ os/pc/etherif.h | 39 + os/pc/etherigbe.c | 1968 ++++++++++++++ os/pc/etherrhine.c | 734 ++++++ os/pc/ethersmc.c | 780 ++++++ os/pc/etherwavelan.c | 196 ++ os/pc/flashif.h | 82 + os/pc/flashzpc.c | 371 +++ os/pc/floppy.h | 183 ++ os/pc/fns.h | 162 ++ os/pc/fpi.h | 61 + os/pc/fpi387.c | 743 ++++++ os/pc/fpsave.s | 9 + os/pc/i8250.c | 328 +++ os/pc/i8253.c | 314 +++ os/pc/i8259.c | 199 ++ os/pc/io.h | 323 +++ os/pc/kbd.c | 477 ++++ os/pc/l.s | 953 +++++++ os/pc/main.c | 457 ++++ os/pc/mem.h | 144 ++ os/pc/memory.c | 570 ++++ os/pc/mkfile | 83 + os/pc/mmu.c | 321 +++ os/pc/mouse.c | 84 + os/pc/mp.c | 815 ++++++ os/pc/mp.h | 225 ++ os/pc/pc | 139 + os/pc/pc4e | 145 ++ os/pc/pcdisk | 154 ++ os/pc/pci.acid | 252 ++ os/pc/pci.c | 1341 ++++++++++ os/pc/pcidb.acid | 2848 ++++++++++++++++++++ os/pc/pcmciamodem.c | 75 + os/pc/piix4smbus.c | 213 ++ os/pc/pix | 153 ++ os/pc/ps2mouse.c | 84 + os/pc/ptclbsum386.s | 126 + os/pc/screen.c | 410 +++ os/pc/screen.h | 173 ++ os/pc/sd53c8xx.c | 2138 +++++++++++++++ os/pc/sd53c8xx.i | 773 ++++++ os/pc/sd53c8xx.n | 448 ++++ os/pc/sdata.c | 2206 ++++++++++++++++ os/pc/sdmylex.c | 1249 +++++++++ os/pc/sdscsi.c | 394 +++ os/pc/trap.c | 571 ++++ os/pc/tv.h | 15 + os/pc/uarti8250.c | 740 ++++++ os/pc/uartisa.c | 98 + os/pc/uartpci.c | 137 + os/pc/usb.h | 160 ++ os/pc/usbuhci.c | 1538 +++++++++++ os/pc/vga.c | 241 ++ os/pc/vga.h | 75 + os/pc/vga3dfx.c | 258 ++ os/pc/vgaark2000pv.c | 190 ++ os/pc/vgabt485.c | 245 ++ os/pc/vgaclgd542x.c | 291 +++ os/pc/vgaclgd546x.c | 277 ++ os/pc/vgact65545.c | 149 ++ os/pc/vgacyber938x.c | 225 ++ os/pc/vgaet4000.c | 270 ++ os/pc/vgahiqvideo.c | 274 ++ os/pc/vgai81x.c | 282 ++ os/pc/vgamach64xx.c | 1250 +++++++++ os/pc/vgamga2164w.c | 289 +++ os/pc/vgamga4xx.c | 603 +++++ os/pc/vganeomagic.c | 541 ++++ os/pc/vganvidia.c | 373 +++ os/pc/vgargb524.c | 236 ++ os/pc/vgas3.c | 620 +++++ os/pc/vgasavage.c | 571 ++++ os/pc/vgat2r4.c | 586 +++++ os/pc/vgatvp3020.c | 216 ++ os/pc/vgatvp3026.c | 189 ++ os/pc/vgavmware.c | 386 +++ os/pc/vgax.c | 102 + os/pc/wavelan.c | 1268 +++++++++ os/pc/wavelan.h | 327 +++ os/pc/x86break.c | 138 + os/pc/zoran.h | 907 +++++++ os/port/NOTICE | 18 + os/port/alarm.c | 41 + os/port/alloc.c | 963 +++++++ os/port/allocb.c | 159 ++ os/port/chan.c | 1431 +++++++++++ os/port/cis.c | 539 ++++ os/port/dev.c | 434 ++++ os/port/devXXX.c | 147 ++ os/port/devaudio.c | 947 +++++++ os/port/devbench.c | 1165 +++++++++ os/port/devboot.c | 150 ++ os/port/devbridge.c | 1206 +++++++++ os/port/devcap.c | 248 ++ os/port/devcons.c | 1274 +++++++++ os/port/devdbg.c | 1000 +++++++ os/port/devdraw.c | 2088 +++++++++++++++ os/port/devds.c | 605 +++++ os/port/devdup.c | 151 ++ os/port/devdynld.c | 365 +++ os/port/devenv.c | 338 +++ os/port/devflash.c | 641 +++++ os/port/devftl.c | 1365 ++++++++++ os/port/devi2c.c | 227 ++ os/port/devindir.c | 40 + os/port/devkprof.c | 230 ++ os/port/devlogfs.c | 1531 +++++++++++ os/port/devloopback.c | 743 ++++++ os/port/devmnt.c | 1204 +++++++++ os/port/devns16552.c | 1090 ++++++++ os/port/devpci.c | 227 ++ os/port/devpipe.c | 464 ++++ os/port/devpointer.c | 279 ++ os/port/devprof.c | 722 ++++++ os/port/devprog.c | 1510 +++++++++++ os/port/devroot.c | 153 ++ os/port/devsd.c | 1474 +++++++++++ os/port/devsign.c | 446 ++++ os/port/devsrv.c | 726 ++++++ os/port/devssl.c | 1436 +++++++++++ os/port/devtest.c | 125 + os/port/devtinyfs.c | 915 +++++++ os/port/devtk.c | 180 ++ os/port/devuart.c | 748 ++++++ os/port/dial.c | 417 +++ os/port/dis.c | 1125 ++++++++ os/port/discall.c | 185 ++ os/port/dynld.c | 75 + os/port/edf.c | 626 +++++ os/port/edf.h | 53 + os/port/error.h | 64 + os/port/ethermii.c | 238 ++ os/port/ethermii.h | 116 + os/port/ethersink.c | 65 + os/port/exception.c | 216 ++ os/port/exportfs.c | 1325 ++++++++++ os/port/flashamd29f0x0.c | 167 ++ os/port/flashcfi16.c | 134 + os/port/flashcfi8.c | 143 ++ os/port/flashif.h | 147 ++ os/port/flashintel | 179 ++ os/port/flashnand.c | 337 +++ os/port/fpi.c | 304 +++ os/port/fpimem.c | 136 + os/port/inferno.c | 1030 ++++++++ os/port/latin1.c | 76 + os/port/latin1.h | 100 + os/port/lib.h | 211 ++ os/port/log.c | 186 ++ os/port/master | 89 + os/port/master.local | 10 + os/port/mkdevc | 216 ++ os/port/mkdevlist | 86 + os/port/mkroot | 119 + os/port/mul64fract.c | 39 + os/port/netaux.c | 67 + os/port/netif.c | 671 +++++ os/port/netif.h | 134 + os/port/nocache.c | 49 + os/port/nodynld.c | 48 + os/port/noenv.c | 33 + os/port/noscreen.c | 9 + os/port/parse.c | 114 + os/port/pgrp.c | 278 ++ os/port/portbreak.c | 161 ++ os/port/portclock.c | 277 ++ os/port/portdat.h | 673 +++++ os/port/portfns.h | 319 +++ os/port/portmkfile | 152 ++ os/port/print.c | 31 + os/port/proc.c | 788 ++++++ os/port/qio.c | 1529 +++++++++++ os/port/qlock.c | 111 + os/port/random.c | 156 ++ os/port/rdb.c | 112 + os/port/sd.h | 132 + os/port/swcursor.c | 358 +++ os/port/sysfile.c | 1125 ++++++++ os/port/taslock.c | 126 + os/port/tod.c | 283 ++ os/port/uart.h | 104 + os/port/xalloc.c | 246 ++ os/pxa/NOTICE | 2 + os/pxa/clock.c | 318 +++ os/pxa/devether.c | 617 +++++ os/pxa/devrtc.c | 169 ++ os/pxa/devuart.c | 1070 ++++++++ os/pxa/dma.c | 244 ++ os/pxa/etherif.h | 41 + os/pxa/fpi.h | 61 + os/pxa/fpiarm.c | 483 ++++ os/pxa/gpio.c | 88 + os/pxa/i2c.c | 561 ++++ os/pxa/l.s | 548 ++++ os/pxa/mmu.c | 252 ++ os/pxa/pxaio.h | 383 +++ os/pxa/sa1110break.c | 360 +++ os/pxa/trap.c | 587 +++++ os/rpcg/NOTICE | 4 + os/rpcg/archrpcg.c | 428 +++ os/rpcg/archrpcg.h | 48 + os/rpcg/clock.c | 145 ++ os/rpcg/dat.h | 162 ++ os/rpcg/fns.h | 119 + os/rpcg/io.h | 1 + os/rpcg/main.c | 392 +++ os/rpcg/mem.h | 157 ++ os/rpcg/mkfile | 108 + os/rpcg/mmu.c | 20 + os/rpcg/rpcg | 122 + os/rpcg/tlb.s | 24 + os/sa1110/clock.c | 317 +++ os/sa1110/devether.c | 617 +++++ os/sa1110/devgpio.c | 165 ++ os/sa1110/devpcmcia.c | 761 ++++++ os/sa1110/devpower.c | 377 +++ os/sa1110/devrtc.c | 169 ++ os/sa1110/devuart.c | 781 ++++++ os/sa1110/dma.c | 233 ++ os/sa1110/etherif.h | 41 + os/sa1110/fpi.h | 61 + os/sa1110/fpiarm.c | 483 ++++ os/sa1110/gscreen.c | 587 +++++ os/sa1110/gscreen.h | 87 + os/sa1110/i2c.h | 16 + os/sa1110/i2cgpio.c | 274 ++ os/sa1110/l.s | 530 ++++ os/sa1110/l3gpio.c | 246 ++ os/sa1110/mmu.c | 140 + os/sa1110/sa1110break.c | 360 +++ os/sa1110/sa1110io.h | 500 ++++ os/sa1110/softcursor.c | 365 +++ os/sa1110/suspend.c | 161 ++ os/sa1110/trap.c | 601 +++++ 822 files changed, 294665 insertions(+) create mode 100644 os/NOTICE create mode 100644 os/README create mode 100644 os/boot/README create mode 100755 os/boot/arm1110/Mk create mode 100644 os/boot/arm1110/dat.h create mode 100644 os/boot/arm1110/donprint.c create mode 100644 os/boot/arm1110/fns.h create mode 100644 os/boot/arm1110/il.s create mode 100644 os/boot/arm1110/imain.c create mode 100644 os/boot/arm1110/inflate.c create mode 100644 os/boot/arm1110/io.h create mode 100644 os/boot/arm1110/l.s create mode 100644 os/boot/arm1110/lib.h create mode 100644 os/boot/arm1110/map create mode 100644 os/boot/arm1110/mem.h create mode 100644 os/boot/arm1110/mkfile create mode 100644 os/boot/arm1110/print.c create mode 100644 os/boot/arm1110/uart.c create mode 100644 os/boot/libflate/LICENCE create mode 100644 os/boot/libflate/NOTICE create mode 100644 os/boot/libflate/adler.c create mode 100644 os/boot/libflate/crc.c create mode 100644 os/boot/libflate/deflate.c create mode 100644 os/boot/libflate/deflateblock.c create mode 100644 os/boot/libflate/deflatezlib.c create mode 100644 os/boot/libflate/deflatezlibblock.c create mode 100644 os/boot/libflate/flateerr.c create mode 100644 os/boot/libflate/inflate.c create mode 100644 os/boot/libflate/inflateblock.c create mode 100644 os/boot/libflate/inflatezlib.c create mode 100644 os/boot/libflate/inflatezlibblock.c create mode 100644 os/boot/libflate/mkfile create mode 100644 os/boot/libflate/zlib.h create mode 100644 os/boot/mpc/NOTICE create mode 100644 os/boot/mpc/alarm.c create mode 100644 os/boot/mpc/all.h create mode 100644 os/boot/mpc/archfads.c create mode 100644 os/boot/mpc/archfads.h create mode 100644 os/boot/mpc/archpaq.c create mode 100644 os/boot/mpc/archpaq.h create mode 100644 os/boot/mpc/boot.h create mode 100644 os/boot/mpc/bootp.c create mode 100644 os/boot/mpc/clock.c create mode 100644 os/boot/mpc/conf.c create mode 100644 os/boot/mpc/console.c create mode 100644 os/boot/mpc/cpm.c create mode 100644 os/boot/mpc/crc32.c create mode 100644 os/boot/mpc/dat.h create mode 100644 os/boot/mpc/defont0.c create mode 100644 os/boot/mpc/devether.c create mode 100644 os/boot/mpc/devuart.c create mode 100644 os/boot/mpc/dload.c create mode 100644 os/boot/mpc/donprint.c create mode 100644 os/boot/mpc/dosboot.c create mode 100644 os/boot/mpc/dosfs.h create mode 100644 os/boot/mpc/etherif.h create mode 100644 os/boot/mpc/etherscc.c create mode 100644 os/boot/mpc/fblt.c create mode 100644 os/boot/mpc/flash.c create mode 100644 os/boot/mpc/fns.h create mode 100644 os/boot/mpc/gbitbltclip.c create mode 100644 os/boot/mpc/gnot.h create mode 100644 os/boot/mpc/i2c.c create mode 100644 os/boot/mpc/initfads.c create mode 100644 os/boot/mpc/initpaq.c create mode 100644 os/boot/mpc/initrpcg.c create mode 100644 os/boot/mpc/io.h create mode 100644 os/boot/mpc/ip.h create mode 100644 os/boot/mpc/l.s create mode 100644 os/boot/mpc/lib.h create mode 100644 os/boot/mpc/main.c create mode 100644 os/boot/mpc/mem.c create mode 100644 os/boot/mpc/mem.h create mode 100644 os/boot/mpc/mkfile create mode 100644 os/boot/mpc/ms2.c create mode 100644 os/boot/mpc/plan9boot.c create mode 100644 os/boot/mpc/qio.c create mode 100644 os/boot/mpc/rmap.c create mode 100644 os/boot/mpc/screen.c create mode 100644 os/boot/mpc/sload.c create mode 100644 os/boot/mpc/squeeze.h create mode 100644 os/boot/mpc/trap.c create mode 100644 os/boot/mpc/uartboot.c create mode 100644 os/boot/mpc/ureg.h create mode 100644 os/boot/mpc/zqs.c create mode 100644 os/boot/pc/8250.c create mode 100644 os/boot/pc/LICENCE create mode 100644 os/boot/pc/NOTICE create mode 100644 os/boot/pc/alarm.c create mode 100644 os/boot/pc/apm.c create mode 100644 os/boot/pc/bcom.c create mode 100644 os/boot/pc/boot.c create mode 100644 os/boot/pc/bootld.c create mode 100644 os/boot/pc/bootp.c create mode 100644 os/boot/pc/cga.c create mode 100644 os/boot/pc/clock.c create mode 100644 os/boot/pc/conf.c create mode 100644 os/boot/pc/console.c create mode 100644 os/boot/pc/dat.h create mode 100644 os/boot/pc/devfloppy.c create mode 100644 os/boot/pc/devfloppy.h create mode 100644 os/boot/pc/devi82365.c create mode 100644 os/boot/pc/devpccard.c create mode 100644 os/boot/pc/devsd.c create mode 100644 os/boot/pc/dma.c create mode 100644 os/boot/pc/dosboot.c create mode 100644 os/boot/pc/dosfs.h create mode 100644 os/boot/pc/eipfmt.c create mode 100644 os/boot/pc/error.h create mode 100644 os/boot/pc/ether.c create mode 100644 os/boot/pc/ether2000.c create mode 100644 os/boot/pc/ether2114x.c create mode 100644 os/boot/pc/ether589.c create mode 100644 os/boot/pc/ether79c970.c create mode 100644 os/boot/pc/ether8003.c create mode 100644 os/boot/pc/ether8139.c create mode 100644 os/boot/pc/ether8169.c create mode 100644 os/boot/pc/ether82557.c create mode 100644 os/boot/pc/ether83815.c create mode 100644 os/boot/pc/ether8390.c create mode 100644 os/boot/pc/ether8390.h create mode 100644 os/boot/pc/etherec2t.c create mode 100644 os/boot/pc/etherelnk3.c create mode 100644 os/boot/pc/etherif.h create mode 100644 os/boot/pc/etherigbe.c create mode 100644 os/boot/pc/ethermii.c create mode 100644 os/boot/pc/ethermii.h create mode 100644 os/boot/pc/etherrhine.c create mode 100644 os/boot/pc/fns.h create mode 100644 os/boot/pc/fs.c create mode 100644 os/boot/pc/fs.h create mode 100644 os/boot/pc/getcallerpc.c create mode 100644 os/boot/pc/ilock.c create mode 100644 os/boot/pc/inflate.c create mode 100644 os/boot/pc/io.h create mode 100644 os/boot/pc/ip.h create mode 100644 os/boot/pc/kbd.c create mode 100644 os/boot/pc/kfs.h create mode 100644 os/boot/pc/kfsboot.c create mode 100644 os/boot/pc/l.s create mode 100644 os/boot/pc/lib.h create mode 100644 os/boot/pc/load.c create mode 100644 os/boot/pc/mbr.s create mode 100644 os/boot/pc/mem.h create mode 100644 os/boot/pc/memory.c create mode 100644 os/boot/pc/mkfile create mode 100644 os/boot/pc/noether.c create mode 100644 os/boot/pc/part.c create mode 100644 os/boot/pc/pbs.s create mode 100755 os/boot/pc/pbsdisk create mode 100644 os/boot/pc/pbsdisk.s create mode 100755 os/boot/pc/pbsdisklba create mode 100644 os/boot/pc/pbsdisklba.s create mode 100644 os/boot/pc/pbslba.s create mode 100644 os/boot/pc/pci.c create mode 100644 os/boot/pc/print.c create mode 100644 os/boot/pc/queue.c create mode 100644 os/boot/pc/sd.h create mode 100644 os/boot/pc/sd53c8xx.c create mode 100644 os/boot/pc/sd53c8xx.i create mode 100644 os/boot/pc/sdata.c create mode 100644 os/boot/pc/sdmylex.c create mode 100644 os/boot/pc/sdscsi.c create mode 100644 os/boot/pc/trap.c create mode 100644 os/boot/pc/ureg.h create mode 100644 os/boot/pc/x16.h create mode 100644 os/boot/puma/8250.c create mode 100644 os/boot/puma/alarm.c create mode 100644 os/boot/puma/armv4.h create mode 100644 os/boot/puma/boot.h create mode 100644 os/boot/puma/bootp.c create mode 100644 os/boot/puma/cga.c create mode 100644 os/boot/puma/clock.c create mode 100644 os/boot/puma/conf.c create mode 100644 os/boot/puma/console.c create mode 100644 os/boot/puma/dat.h create mode 100644 os/boot/puma/div.s create mode 100644 os/boot/puma/donprint.c create mode 100644 os/boot/puma/dosboot.c create mode 100644 os/boot/puma/dosfs.h create mode 100644 os/boot/puma/ebsit.trap.c create mode 100644 os/boot/puma/ether.c create mode 100644 os/boot/puma/ether.h create mode 100644 os/boot/puma/ether8900.c create mode 100644 os/boot/puma/flash.c create mode 100644 os/boot/puma/fns.h create mode 100644 os/boot/puma/hard.c create mode 100644 os/boot/puma/io.h create mode 100644 os/boot/puma/ip.h create mode 100644 os/boot/puma/kbd.c create mode 100644 os/boot/puma/l.s create mode 100644 os/boot/puma/lib.h create mode 100644 os/boot/puma/main.c create mode 100644 os/boot/puma/mem.h create mode 100644 os/boot/puma/mkfile create mode 100644 os/boot/puma/outb.c create mode 100644 os/boot/puma/plan9boot.c create mode 100644 os/boot/puma/puma.c create mode 100644 os/boot/puma/puma.h create mode 100644 os/boot/puma/qio.c create mode 100644 os/boot/puma/rmap.c create mode 100644 os/boot/puma/squeeze.h create mode 100644 os/boot/puma/sum.c create mode 100644 os/boot/puma/trap.c create mode 100644 os/boot/puma/ureg.h create mode 100644 os/boot/puma/zqs.c create mode 100644 os/boot/rpcg/NOTICE create mode 100644 os/boot/rpcg/alarm.c create mode 100644 os/boot/rpcg/all.h create mode 100644 os/boot/rpcg/archrpcg.c create mode 100644 os/boot/rpcg/archrpcg.h create mode 100644 os/boot/rpcg/boot.h create mode 100644 os/boot/rpcg/bootp.c create mode 100644 os/boot/rpcg/clock.c create mode 100644 os/boot/rpcg/conf.c create mode 100644 os/boot/rpcg/console.c create mode 100644 os/boot/rpcg/cpm.c create mode 100644 os/boot/rpcg/crc32.c create mode 100644 os/boot/rpcg/dat.h create mode 100644 os/boot/rpcg/defont0.c create mode 100644 os/boot/rpcg/devether.c create mode 100644 os/boot/rpcg/devuart.c create mode 100644 os/boot/rpcg/dload.c create mode 100644 os/boot/rpcg/donprint.c create mode 100644 os/boot/rpcg/dosboot.c create mode 100644 os/boot/rpcg/dosfs.h create mode 100644 os/boot/rpcg/etherif.h create mode 100644 os/boot/rpcg/etherscc.c create mode 100644 os/boot/rpcg/fblt.c create mode 100644 os/boot/rpcg/flash.c create mode 100644 os/boot/rpcg/fns.h create mode 100644 os/boot/rpcg/g.mx create mode 100644 os/boot/rpcg/gbitbltclip.c create mode 100644 os/boot/rpcg/gnot.h create mode 100644 os/boot/rpcg/i2c.c create mode 100644 os/boot/rpcg/initfads.c create mode 100644 os/boot/rpcg/initpaq.c create mode 100644 os/boot/rpcg/initrpcg.c create mode 100644 os/boot/rpcg/io.h create mode 100644 os/boot/rpcg/ip.h create mode 100644 os/boot/rpcg/l.s create mode 100644 os/boot/rpcg/lib.h create mode 100644 os/boot/rpcg/libg.h create mode 100644 os/boot/rpcg/main.c create mode 100644 os/boot/rpcg/mem.c create mode 100644 os/boot/rpcg/mem.h create mode 100644 os/boot/rpcg/mkfile create mode 100644 os/boot/rpcg/ms2.c create mode 100644 os/boot/rpcg/plan9boot.c create mode 100755 os/boot/rpcg/qbromrpcg create mode 100644 os/boot/rpcg/qio.c create mode 100644 os/boot/rpcg/rmap.c create mode 100644 os/boot/rpcg/screen.c create mode 100755 os/boot/rpcg/sload create mode 100644 os/boot/rpcg/sload.c create mode 100644 os/boot/rpcg/squeeze.h create mode 100644 os/boot/rpcg/trap.c create mode 100644 os/boot/rpcg/uartboot.c create mode 100644 os/boot/rpcg/ureg.h create mode 100644 os/boot/rpcg/zqs.c create mode 100755 os/cerf1110/Mk create mode 100644 os/cerf1110/NOTICE create mode 100644 os/cerf1110/README create mode 100644 os/cerf1110/archcerf.c create mode 100644 os/cerf1110/cerf create mode 100644 os/cerf1110/dat.h create mode 100644 os/cerf1110/devata.c create mode 100644 os/cerf1110/devcerf.c create mode 100644 os/cerf1110/ether8900.c create mode 100644 os/cerf1110/fns.h create mode 100644 os/cerf1110/io.h create mode 100644 os/cerf1110/main.c create mode 100644 os/cerf1110/mem.h create mode 100644 os/cerf1110/mkfile create mode 100644 os/cerf250/NOTICE create mode 100644 os/cerf250/README create mode 100644 os/cerf250/archcerf.c create mode 100644 os/cerf250/cerf create mode 100644 os/cerf250/dat.h create mode 100644 os/cerf250/devpcf8563.c create mode 100644 os/cerf250/ether91c111.c create mode 100644 os/cerf250/fns.h create mode 100644 os/cerf250/io.h create mode 100644 os/cerf250/main.c create mode 100644 os/cerf250/mem.h create mode 100644 os/cerf250/mkfile create mode 100644 os/cerf250/uart.h create mode 100644 os/cerf405/NOTICE create mode 100644 os/cerf405/README create mode 100644 os/cerf405/cerf create mode 100644 os/cerf405/clock.c create mode 100644 os/cerf405/compile.c create mode 100644 os/cerf405/dat.h create mode 100644 os/cerf405/devboot.c create mode 100644 os/cerf405/devether.c create mode 100644 os/cerf405/devrtc.c create mode 100644 os/cerf405/devuart.c create mode 100644 os/cerf405/etheremac.c create mode 100644 os/cerf405/etherif.h create mode 100644 os/cerf405/fns.h create mode 100644 os/cerf405/fpi.h create mode 100644 os/cerf405/fpipower.c create mode 100644 os/cerf405/gpio.c create mode 100644 os/cerf405/iic.c create mode 100644 os/cerf405/inb.s create mode 100644 os/cerf405/io.h create mode 100644 os/cerf405/l.s create mode 100644 os/cerf405/main.c create mode 100644 os/cerf405/mal.c create mode 100644 os/cerf405/mem.h create mode 100644 os/cerf405/mkfile create mode 100644 os/cerf405/mmu.c create mode 100644 os/cerf405/nand.c create mode 100644 os/cerf405/nofp.s create mode 100644 os/cerf405/pci.c create mode 100644 os/cerf405/physmem.h create mode 100644 os/cerf405/powerbreak.c create mode 100644 os/cerf405/rmap.c create mode 100644 os/cerf405/tlb.s create mode 100644 os/cerf405/trap.c create mode 100644 os/cerf405/uart.c create mode 100644 os/cerf405/uart.h create mode 100644 os/fads/NOTICE create mode 100644 os/fads/archfads.c create mode 100644 os/fads/archfads.h create mode 100644 os/fads/dat.h create mode 100644 os/fads/fads create mode 100644 os/fads/fns.h create mode 100644 os/fads/io.h create mode 100644 os/fads/main.c create mode 100644 os/fads/mem.h create mode 100644 os/fads/mkfile create mode 100644 os/fads/mmu.c create mode 100644 os/fads/tlb.s create mode 100644 os/init/README create mode 100644 os/init/bootinit.b create mode 100644 os/init/cerf405.b create mode 100644 os/init/cerfinit.b create mode 100644 os/init/evalinit.b create mode 100644 os/init/geninit.b create mode 100644 os/init/i4e.b create mode 100644 os/init/init.b create mode 100644 os/init/ipaqinit.b create mode 100644 os/init/ipeinit.b create mode 100644 os/init/jsinit.b create mode 100644 os/init/mkfile create mode 100644 os/init/mpcinit.b create mode 100644 os/init/pcdemo.b create mode 100644 os/init/pcinit.b create mode 100644 os/init/reminit.b create mode 100644 os/init/rpcginit.b create mode 100644 os/init/shell.b create mode 100644 os/init/soeinit.b create mode 100644 os/init/srvinit.b create mode 100644 os/init/wminit.b create mode 100644 os/ip/arp.c create mode 100644 os/ip/bootp.c create mode 100644 os/ip/compress.c create mode 100644 os/ip/devip.c create mode 100644 os/ip/dhcp.c create mode 100644 os/ip/eipconvtest.c create mode 100644 os/ip/esp.c create mode 100644 os/ip/ethermedium.c create mode 100644 os/ip/gre.c create mode 100644 os/ip/icmp.c create mode 100644 os/ip/icmp6.c create mode 100644 os/ip/igmp.c create mode 100644 os/ip/ihbootp.c create mode 100644 os/ip/il.c create mode 100644 os/ip/ip.c create mode 100644 os/ip/ip.h create mode 100644 os/ip/ipaux.c create mode 100644 os/ip/ipifc.c create mode 100644 os/ip/ipmux.c create mode 100644 os/ip/iproute.c create mode 100644 os/ip/iprouter.c create mode 100644 os/ip/ipv6.c create mode 100644 os/ip/ipv6.h create mode 100644 os/ip/kernel.h create mode 100644 os/ip/loopbackmedium.c create mode 100644 os/ip/netdevmedium.c create mode 100644 os/ip/netlog.c create mode 100644 os/ip/nullmedium.c create mode 100644 os/ip/pktmedium.c create mode 100644 os/ip/plan9.c create mode 100644 os/ip/ppp.c create mode 100644 os/ip/ppp.h create mode 100644 os/ip/pppmedium.c create mode 100644 os/ip/ptclbsum.c create mode 100644 os/ip/rudp.c create mode 100644 os/ip/tcp.c create mode 100644 os/ip/udp.c create mode 100755 os/ipaq1110/Mk create mode 100644 os/ipaq1110/NOTICE create mode 100644 os/ipaq1110/README create mode 100644 os/ipaq1110/archipaq.c create mode 100644 os/ipaq1110/dat.h create mode 100644 os/ipaq1110/defont.c create mode 100644 os/ipaq1110/devaudio.c create mode 100644 os/ipaq1110/devipaq.c create mode 100644 os/ipaq1110/etherwavelan.c create mode 100644 os/ipaq1110/fns.h create mode 100644 os/ipaq1110/inflate create mode 100644 os/ipaq1110/io.h create mode 100644 os/ipaq1110/ipaq create mode 100644 os/ipaq1110/lcd.c create mode 100644 os/ipaq1110/main.c create mode 100644 os/ipaq1110/mem.h create mode 100644 os/ipaq1110/mkfile create mode 100644 os/ipaq1110/screen.c create mode 100644 os/ipaq1110/screen.h create mode 100644 os/ipaq1110/tstdraw.b create mode 100755 os/ipaq1110/upd create mode 100644 os/ipengine/NOTICE create mode 100644 os/ipengine/README create mode 100644 os/ipengine/archipe.c create mode 100644 os/ipengine/archipe.h create mode 100644 os/ipengine/dat.h create mode 100644 os/ipengine/devfpga.c create mode 100644 os/ipengine/flash28f320b3b.c create mode 100644 os/ipengine/fns.h create mode 100644 os/ipengine/io.h create mode 100644 os/ipengine/ipe create mode 100644 os/ipengine/main.c create mode 100644 os/ipengine/mem.h create mode 100644 os/ipengine/mkfile create mode 100644 os/ipengine/mmu.c create mode 100644 os/ipengine/tlb.s create mode 100644 os/js/README create mode 100644 os/js/audio.h create mode 100644 os/js/clock.c create mode 100644 os/js/cs4231.h create mode 100644 os/js/dat.h create mode 100644 os/js/devcs4231.c create mode 100644 os/js/devrtc.c create mode 100644 os/js/fns.h create mode 100644 os/js/fsv.c create mode 100644 os/js/io.h create mode 100644 os/js/iob.c create mode 100644 os/js/js create mode 100644 os/js/kbd.c create mode 100644 os/js/l.s create mode 100644 os/js/main.c create mode 100644 os/js/mem.h create mode 100644 os/js/mkfile create mode 100644 os/js/mmu.c create mode 100644 os/js/ns16552.h create mode 100644 os/js/rom.c create mode 100644 os/js/rom.h create mode 100644 os/js/screen.c create mode 100644 os/js/screen.h create mode 100644 os/js/softcursor.h create mode 100644 os/js/superio.c create mode 100644 os/js/trap.c create mode 100644 os/js/ureg.h create mode 100755 os/ks32/Mk create mode 100644 os/ks32/NOTICE create mode 100644 os/ks32/archevaluator7t.c create mode 100644 os/ks32/armv7.h create mode 100644 os/ks32/clock.c create mode 100644 os/ks32/dat.h create mode 100644 os/ks32/devuart.c create mode 100644 os/ks32/download.ps create mode 100644 os/ks32/evaluator7t create mode 100644 os/ks32/fns.h create mode 100644 os/ks32/fpi.h create mode 100644 os/ks32/fpiarm.c create mode 100644 os/ks32/io.h create mode 100644 os/ks32/l.s create mode 100644 os/ks32/main.c create mode 100644 os/ks32/mem.h create mode 100644 os/ks32/mkfile create mode 100644 os/ks32/not.c create mode 100755 os/ks32/squirt create mode 100644 os/ks32/trap.c create mode 100755 os/manga/Mk create mode 100644 os/manga/archmanga.c create mode 100644 os/manga/clock.c create mode 100644 os/manga/dat.h create mode 100644 os/manga/devether.c create mode 100644 os/manga/devusb.c create mode 100644 os/manga/eswnotes create mode 100644 os/manga/ether8139.c create mode 100644 os/manga/etherif.h create mode 100644 os/manga/etherks8695.c create mode 100644 os/manga/flashif.h create mode 100644 os/manga/fns.h create mode 100644 os/manga/fpi.h create mode 100644 os/manga/fpiarm.c create mode 100644 os/manga/gpio.c create mode 100644 os/manga/inb.c create mode 100644 os/manga/io.h create mode 100644 os/manga/ioring.c create mode 100644 os/manga/l.s create mode 100644 os/manga/main.c create mode 100644 os/manga/manga create mode 100644 os/manga/mem.h create mode 100644 os/manga/mkfile create mode 100644 os/manga/mmu.c create mode 100644 os/manga/pci.c create mode 100644 os/manga/pinflate create mode 100644 os/manga/trap.c create mode 100644 os/manga/uartks8695.c create mode 100644 os/manga/usb.h create mode 100644 os/manga/usbuhci.c create mode 100644 os/mpc/800io.h create mode 100644 os/mpc/NOTICE create mode 100644 os/mpc/clock.c create mode 100644 os/mpc/cpm.c create mode 100644 os/mpc/cpmtimer.c create mode 100644 os/mpc/devata.c create mode 100644 os/mpc/devbench.c create mode 100644 os/mpc/devboot.c create mode 100644 os/mpc/devether.c create mode 100644 os/mpc/devpcmcia.c create mode 100644 os/mpc/devrtc.c create mode 100644 os/mpc/devtouch.c create mode 100644 os/mpc/devuart.c create mode 100644 os/mpc/dsp.c create mode 100644 os/mpc/dsp.h create mode 100644 os/mpc/etherif.h create mode 100644 os/mpc/etherscc.c create mode 100644 os/mpc/faultpower.c create mode 100644 os/mpc/fp.s create mode 100644 os/mpc/fpi.h create mode 100644 os/mpc/fpipower.c create mode 100644 os/mpc/i2c.c create mode 100644 os/mpc/i2c_spi.srx create mode 100644 os/mpc/inb.s create mode 100644 os/mpc/kbd.c create mode 100644 os/mpc/l.s create mode 100644 os/mpc/nofp.s create mode 100644 os/mpc/pcmcia.h create mode 100644 os/mpc/pit.c create mode 100644 os/mpc/powerbreak.c create mode 100644 os/mpc/rmap.c create mode 100644 os/mpc/screen.c create mode 100644 os/mpc/screen.h create mode 100644 os/mpc/spi.c create mode 100644 os/mpc/trap.c create mode 100644 os/mpc/usb.h create mode 100644 os/omap/README create mode 100644 os/pc/NOTICE create mode 100644 os/pc/README create mode 100644 os/pc/apbootstrap.h create mode 100644 os/pc/apbootstrap.s create mode 100644 os/pc/apic.c create mode 100644 os/pc/apm.c create mode 100644 os/pc/apmjump.s create mode 100644 os/pc/archmp.c create mode 100644 os/pc/audio.h create mode 100644 os/pc/cga.c create mode 100644 os/pc/cgamemscr.c create mode 100644 os/pc/crystal.h create mode 100644 os/pc/dat.h create mode 100644 os/pc/devarch.c create mode 100644 os/pc/devds1620.c create mode 100644 os/pc/devether.c create mode 100644 os/pc/devfloppy.c create mode 100644 os/pc/devi82365.c create mode 100644 os/pc/devlm78.c create mode 100644 os/pc/devlpt.c create mode 100644 os/pc/devmouse.c create mode 100644 os/pc/devmpeg.c create mode 100644 os/pc/devpccard.c create mode 100644 os/pc/devpnp.c create mode 100644 os/pc/devrtc.c create mode 100644 os/pc/devtv.c create mode 100644 os/pc/devusb.c create mode 100644 os/pc/devvga.c create mode 100644 os/pc/devzt5512.c create mode 100644 os/pc/dma.c create mode 100644 os/pc/ether2000.c create mode 100644 os/pc/ether2114x.c create mode 100644 os/pc/ether589.c create mode 100644 os/pc/ether79c960.c create mode 100644 os/pc/ether79c970.c create mode 100644 os/pc/ether8003.c create mode 100644 os/pc/ether8139.c create mode 100644 os/pc/ether82543gc.c create mode 100644 os/pc/ether82557.c create mode 100644 os/pc/ether83815.c create mode 100644 os/pc/ether8390.c create mode 100644 os/pc/ether8390.h create mode 100644 os/pc/etherec2t.c create mode 100644 os/pc/etherelnk3.c create mode 100644 os/pc/etherga620.c create mode 100644 os/pc/etherga620fw.h create mode 100644 os/pc/etherif.h create mode 100644 os/pc/etherigbe.c create mode 100644 os/pc/etherrhine.c create mode 100644 os/pc/ethersmc.c create mode 100644 os/pc/etherwavelan.c create mode 100644 os/pc/flashif.h create mode 100644 os/pc/flashzpc.c create mode 100644 os/pc/floppy.h create mode 100644 os/pc/fns.h create mode 100644 os/pc/fpi.h create mode 100644 os/pc/fpi387.c create mode 100644 os/pc/fpsave.s create mode 100644 os/pc/i8250.c create mode 100644 os/pc/i8253.c create mode 100644 os/pc/i8259.c create mode 100644 os/pc/io.h create mode 100644 os/pc/kbd.c create mode 100644 os/pc/l.s create mode 100644 os/pc/main.c create mode 100644 os/pc/mem.h create mode 100644 os/pc/memory.c create mode 100644 os/pc/mkfile create mode 100644 os/pc/mmu.c create mode 100644 os/pc/mouse.c create mode 100644 os/pc/mp.c create mode 100644 os/pc/mp.h create mode 100644 os/pc/pc create mode 100644 os/pc/pc4e create mode 100644 os/pc/pcdisk create mode 100644 os/pc/pci.acid create mode 100644 os/pc/pci.c create mode 100644 os/pc/pcidb.acid create mode 100644 os/pc/pcmciamodem.c create mode 100644 os/pc/piix4smbus.c create mode 100644 os/pc/pix create mode 100644 os/pc/ps2mouse.c create mode 100644 os/pc/ptclbsum386.s create mode 100644 os/pc/screen.c create mode 100644 os/pc/screen.h create mode 100644 os/pc/sd53c8xx.c create mode 100644 os/pc/sd53c8xx.i create mode 100644 os/pc/sd53c8xx.n create mode 100644 os/pc/sdata.c create mode 100644 os/pc/sdmylex.c create mode 100644 os/pc/sdscsi.c create mode 100644 os/pc/trap.c create mode 100644 os/pc/tv.h create mode 100644 os/pc/uarti8250.c create mode 100644 os/pc/uartisa.c create mode 100644 os/pc/uartpci.c create mode 100644 os/pc/usb.h create mode 100644 os/pc/usbuhci.c create mode 100644 os/pc/vga.c create mode 100644 os/pc/vga.h create mode 100644 os/pc/vga3dfx.c create mode 100644 os/pc/vgaark2000pv.c create mode 100644 os/pc/vgabt485.c create mode 100644 os/pc/vgaclgd542x.c create mode 100644 os/pc/vgaclgd546x.c create mode 100644 os/pc/vgact65545.c create mode 100644 os/pc/vgacyber938x.c create mode 100644 os/pc/vgaet4000.c create mode 100644 os/pc/vgahiqvideo.c create mode 100644 os/pc/vgai81x.c create mode 100644 os/pc/vgamach64xx.c create mode 100644 os/pc/vgamga2164w.c create mode 100644 os/pc/vgamga4xx.c create mode 100644 os/pc/vganeomagic.c create mode 100644 os/pc/vganvidia.c create mode 100644 os/pc/vgargb524.c create mode 100644 os/pc/vgas3.c create mode 100644 os/pc/vgasavage.c create mode 100644 os/pc/vgat2r4.c create mode 100644 os/pc/vgatvp3020.c create mode 100644 os/pc/vgatvp3026.c create mode 100644 os/pc/vgavmware.c create mode 100644 os/pc/vgax.c create mode 100644 os/pc/wavelan.c create mode 100644 os/pc/wavelan.h create mode 100644 os/pc/x86break.c create mode 100644 os/pc/zoran.h create mode 100644 os/port/NOTICE create mode 100644 os/port/alarm.c create mode 100644 os/port/alloc.c create mode 100644 os/port/allocb.c create mode 100644 os/port/chan.c create mode 100644 os/port/cis.c create mode 100644 os/port/dev.c create mode 100644 os/port/devXXX.c create mode 100644 os/port/devaudio.c create mode 100644 os/port/devbench.c create mode 100644 os/port/devboot.c create mode 100644 os/port/devbridge.c create mode 100644 os/port/devcap.c create mode 100644 os/port/devcons.c create mode 100644 os/port/devdbg.c create mode 100644 os/port/devdraw.c create mode 100644 os/port/devds.c create mode 100644 os/port/devdup.c create mode 100644 os/port/devdynld.c create mode 100644 os/port/devenv.c create mode 100644 os/port/devflash.c create mode 100644 os/port/devftl.c create mode 100644 os/port/devi2c.c create mode 100644 os/port/devindir.c create mode 100644 os/port/devkprof.c create mode 100755 os/port/devlogfs.c create mode 100644 os/port/devloopback.c create mode 100644 os/port/devmnt.c create mode 100644 os/port/devns16552.c create mode 100644 os/port/devpci.c create mode 100644 os/port/devpipe.c create mode 100644 os/port/devpointer.c create mode 100644 os/port/devprof.c create mode 100644 os/port/devprog.c create mode 100644 os/port/devroot.c create mode 100644 os/port/devsd.c create mode 100644 os/port/devsign.c create mode 100644 os/port/devsrv.c create mode 100644 os/port/devssl.c create mode 100644 os/port/devtest.c create mode 100644 os/port/devtinyfs.c create mode 100644 os/port/devtk.c create mode 100644 os/port/devuart.c create mode 100644 os/port/dial.c create mode 100644 os/port/dis.c create mode 100644 os/port/discall.c create mode 100644 os/port/dynld.c create mode 100644 os/port/edf.c create mode 100644 os/port/edf.h create mode 100644 os/port/error.h create mode 100644 os/port/ethermii.c create mode 100644 os/port/ethermii.h create mode 100644 os/port/ethersink.c create mode 100644 os/port/exception.c create mode 100644 os/port/exportfs.c create mode 100644 os/port/flashamd29f0x0.c create mode 100644 os/port/flashcfi16.c create mode 100644 os/port/flashcfi8.c create mode 100644 os/port/flashif.h create mode 100644 os/port/flashintel create mode 100644 os/port/flashnand.c create mode 100644 os/port/fpi.c create mode 100644 os/port/fpimem.c create mode 100644 os/port/inferno.c create mode 100644 os/port/latin1.c create mode 100644 os/port/latin1.h create mode 100644 os/port/lib.h create mode 100644 os/port/log.c create mode 100644 os/port/master create mode 100644 os/port/master.local create mode 100644 os/port/mkdevc create mode 100644 os/port/mkdevlist create mode 100644 os/port/mkroot create mode 100644 os/port/mul64fract.c create mode 100644 os/port/netaux.c create mode 100644 os/port/netif.c create mode 100644 os/port/netif.h create mode 100644 os/port/nocache.c create mode 100644 os/port/nodynld.c create mode 100644 os/port/noenv.c create mode 100644 os/port/noscreen.c create mode 100644 os/port/parse.c create mode 100644 os/port/pgrp.c create mode 100644 os/port/portbreak.c create mode 100644 os/port/portclock.c create mode 100644 os/port/portdat.h create mode 100644 os/port/portfns.h create mode 100644 os/port/portmkfile create mode 100644 os/port/print.c create mode 100644 os/port/proc.c create mode 100644 os/port/qio.c create mode 100644 os/port/qlock.c create mode 100644 os/port/random.c create mode 100644 os/port/rdb.c create mode 100644 os/port/sd.h create mode 100644 os/port/swcursor.c create mode 100644 os/port/sysfile.c create mode 100644 os/port/taslock.c create mode 100644 os/port/tod.c create mode 100644 os/port/uart.h create mode 100644 os/port/xalloc.c create mode 100644 os/pxa/NOTICE create mode 100644 os/pxa/clock.c create mode 100644 os/pxa/devether.c create mode 100644 os/pxa/devrtc.c create mode 100644 os/pxa/devuart.c create mode 100644 os/pxa/dma.c create mode 100644 os/pxa/etherif.h create mode 100644 os/pxa/fpi.h create mode 100644 os/pxa/fpiarm.c create mode 100644 os/pxa/gpio.c create mode 100644 os/pxa/i2c.c create mode 100644 os/pxa/l.s create mode 100644 os/pxa/mmu.c create mode 100644 os/pxa/pxaio.h create mode 100644 os/pxa/sa1110break.c create mode 100644 os/pxa/trap.c create mode 100644 os/rpcg/NOTICE create mode 100644 os/rpcg/archrpcg.c create mode 100644 os/rpcg/archrpcg.h create mode 100644 os/rpcg/clock.c create mode 100644 os/rpcg/dat.h create mode 100644 os/rpcg/fns.h create mode 100644 os/rpcg/io.h create mode 100644 os/rpcg/main.c create mode 100644 os/rpcg/mem.h create mode 100644 os/rpcg/mkfile create mode 100644 os/rpcg/mmu.c create mode 100644 os/rpcg/rpcg create mode 100644 os/rpcg/tlb.s create mode 100644 os/sa1110/clock.c create mode 100644 os/sa1110/devether.c create mode 100644 os/sa1110/devgpio.c create mode 100644 os/sa1110/devpcmcia.c create mode 100644 os/sa1110/devpower.c create mode 100644 os/sa1110/devrtc.c create mode 100644 os/sa1110/devuart.c create mode 100644 os/sa1110/dma.c create mode 100644 os/sa1110/etherif.h create mode 100644 os/sa1110/fpi.h create mode 100644 os/sa1110/fpiarm.c create mode 100644 os/sa1110/gscreen.c create mode 100644 os/sa1110/gscreen.h create mode 100644 os/sa1110/i2c.h create mode 100644 os/sa1110/i2cgpio.c create mode 100644 os/sa1110/l.s create mode 100644 os/sa1110/l3gpio.c create mode 100644 os/sa1110/mmu.c create mode 100644 os/sa1110/sa1110break.c create mode 100644 os/sa1110/sa1110io.h create mode 100644 os/sa1110/softcursor.c create mode 100644 os/sa1110/suspend.c create mode 100644 os/sa1110/trap.c (limited to 'os') diff --git a/os/NOTICE b/os/NOTICE new file mode 100644 index 00000000..39682bb2 --- /dev/null +++ b/os/NOTICE @@ -0,0 +1,31 @@ +This copyright NOTICE applies to all files in this directory and +subdirectories, unless another copyright notice appears in a given +file or subdirectory. If you take substantial code from this software to use in +other programs, you must somehow include with it an appropriate +copyright notice that includes the copyright notice and the other +notices below. It is fine (and often tidier) to do that in a separate +file such as NOTICE, LICENCE or COPYING. + + Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. + Portions Copyright © 1997-1999 Vita Nuova Limited + Portions Copyright © 2000-2006 Vita Nuova Holdings Limited (www.vitanuova.com) + Revisions Copyright © 2000-2006 Lucent Technologies Inc. and others + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/os/README b/os/README new file mode 100644 index 00000000..d3d45872 --- /dev/null +++ b/os/README @@ -0,0 +1,32 @@ +# a few sample native ports for different architectures + +cerf1110 arm Intrinsyc SA-1110 Cerf Cube +ipaq1110 arm Strongarm SA1110 native kernel for Compaq iPAQ 36xx +sa1110 arm SA1110 code common to most/all SA1110 platforms (eg, ipaq, cerf) + +cerf250 arm Intrinsyc Cerfboard250 (PXA250) +gum arm Gumstix PXA255 +pxa arm general PXA support + +js sparc original port to Javastation-1, not recently tested + +ks32 arm ARM Evaluator 7t demo + +manga arm Micrel KS8695P ARM922 core with 4-port switch and PCI support + +cerf405 power Cerfcube 405EP (based on IBM 405EP processor) + +ipengine power Bright Star Engineering (www.brightstareng.com) ipEngine-1 (MPC823) +rpcg power RPCG-Lite MPC823/850 board +fads power MPC8xx FADS development board +mpc power code common to ports to Motorola MPC8xx boards + +pc 386 basis for ports to various PC-based platforms + +# basic Inferno native kernel components +port common native operating system components +ip common Internet protocol stack, Ether and PPP media +init common Limbo initialisation program (run by native kernel on startup) + +# sample bootstraps +boot - directory of several bootstrap programs; see README there diff --git a/os/boot/README b/os/boot/README new file mode 100644 index 00000000..c208a2ee --- /dev/null +++ b/os/boot/README @@ -0,0 +1,33 @@ +Often the devices we use now come with some +form of bootstrap (often annoyingly complicated, +which gets in the way). Older boards were sometimes +bare, and we had to provide something (often annoyingly +complicated...). On the PC it's currently helpful to +have something that can boot from disc or ether; +since it's a thankless task to write such a thing, we +use Plan 9's, and thank its authors. + +The current scheme on newer devices is to have a simple +program that can put a stripped-down Inferno kernel into +flash, and use that to boot from other devices (including over the net) +as required during development. + +There are two distinct models for bootstrap in this directory. + + Model I + +Each member of the first model is represented by a self-contained directory. +They are derived from various ages of Plan 9's /sys/src/boot/pc. + +arm1110 arm a prefix to a gzip'd kernel to decompress it (runs after basic bootloader) +pc 386 pc-specific bootstrap essentially identical to current Plan 9 + and covered by the Lucent Public License; it uses +libflate - zlib-style inflate/deflate library +mpc power PowerPC bootstrap for FADS board derived from an older version + of Plan 9 but covered by our Inferno licence (because it came with Inferno) +puma arm SA110 bootstrap for Teralogics Puma, also covered by the Inferno licence + + Model II +omap purpose-built bootstrap for the OMAP processor + +Not all of these are being distributed. diff --git a/os/boot/arm1110/Mk b/os/boot/arm1110/Mk new file mode 100755 index 00000000..51d69a44 --- /dev/null +++ b/os/boot/arm1110/Mk @@ -0,0 +1,8 @@ +#!/bin/rc +rfork ne +ROOT=/usr/inferno +fn cd +NPROC=3 +path=(/usr/inferno/Plan9/$cputype/bin $path) +#bind /usr/inferno/mkconfig.dist /usr/inferno/mkconfig +exec mk $* diff --git a/os/boot/arm1110/dat.h b/os/boot/arm1110/dat.h new file mode 100644 index 00000000..ae139dc0 --- /dev/null +++ b/os/boot/arm1110/dat.h @@ -0,0 +1 @@ +/* deliberately empty */ diff --git a/os/boot/arm1110/donprint.c b/os/boot/arm1110/donprint.c new file mode 100644 index 00000000..4125e690 --- /dev/null +++ b/os/boot/arm1110/donprint.c @@ -0,0 +1,332 @@ +#include "u.h" +#include "lib.h" + +#define PTR sizeof(char*) +#define SHORT sizeof(int) +#define INT sizeof(int) +#define LONG sizeof(long) +#define IDIGIT 30 +#define MAXCON 30 + +#define FLONG (1<<0) +#define FSHORT (1<<1) +#define FUNSIGN (1<<2) + +typedef struct Op Op; +struct Op +{ + char *p; + char *ep; + void *argp; + int f1; + int f2; + int f3; +}; + +static int noconv(Op*); +static int cconv(Op*); +static int dconv(Op*); +static int hconv(Op*); +static int lconv(Op*); +static int oconv(Op*); +static int sconv(Op*); +static int uconv(Op*); +static int xconv(Op*); +static int Xconv(Op*); +static int percent(Op*); + +static +int (*fmtconv[MAXCON])(Op*) = +{ + noconv, + cconv, dconv, hconv, lconv, + oconv, sconv, uconv, xconv, + Xconv, percent, +}; +static +char fmtindex[128] = +{ + ['c'] 1, + ['d'] 2, + ['h'] 3, + ['l'] 4, + ['o'] 5, + ['s'] 6, + ['u'] 7, + ['x'] 8, + ['X'] 9, + ['%'] 10, +}; + +static int convcount = { 11 }; +static int ucase; + +static void +PUT(Op *o, int c) +{ + static int pos; + int opos; + + if(c == '\t'){ + opos = pos; + pos = (opos+8) & ~7; + while(opos++ < pos && o->p < o->ep) + *o->p++ = ' '; + return; + } + if(o->p < o->ep){ + *o->p++ = c; + pos++; + } + if(c == '\n') + pos = 0; +} + +int +fmtinstall(char c, int (*f)(Op*)) +{ + + c &= 0177; + if(fmtindex[c] == 0) { + if(convcount >= MAXCON) + return 1; + fmtindex[c] = convcount++; + } + fmtconv[fmtindex[c]] = f; + return 0; +} + +char* +donprint(char *p, char *ep, char *fmt, void *argp) +{ + int sf1, c; + Op o; + + o.p = p; + o.ep = ep; + o.argp = argp; + +loop: + c = *fmt++; + if(c != '%') { + if(c == 0) { + if(o.p < o.ep) + *o.p = 0; + return o.p; + } + PUT(&o, c); + goto loop; + } + o.f1 = 0; + o.f2 = -1; + o.f3 = 0; + c = *fmt++; + sf1 = 0; + if(c == '-') { + sf1 = 1; + c = *fmt++; + } + while(c >= '0' && c <= '9') { + o.f1 = o.f1*10 + c-'0'; + c = *fmt++; + } + if(sf1) + o.f1 = -o.f1; + if(c != '.') + goto l1; + c = *fmt++; + while(c >= '0' && c <= '9') { + if(o.f2 < 0) + o.f2 = 0; + o.f2 = o.f2*10 + c-'0'; + c = *fmt++; + } +l1: + if(c == 0) + fmt--; + c = (*fmtconv[fmtindex[c&0177]])(&o); + if(c < 0) { + o.f3 |= -c; + c = *fmt++; + goto l1; + } + o.argp = (char*)o.argp + c; + goto loop; +} + +void +strconv(char *o, Op *op, int f1, int f2) +{ + int n, c; + char *p; + + n = strlen(o); + if(f1 >= 0) + while(n < f1) { + PUT(op, ' '); + n++; + } + for(p=o; c = *p++;) + if(f2 != 0) { + PUT(op, c); + f2--; + } + if(f1 < 0) { + f1 = -f1; + while(n < f1) { + PUT(op, ' '); + n++; + } + } +} + +int +numbconv(Op *op, int base) +{ + char b[IDIGIT]; + int i, f, n, r; + long v; + short h; + + f = 0; + switch(op->f3 & (FLONG|FSHORT|FUNSIGN)) { + case FLONG: + v = *(long*)op->argp; + r = LONG; + break; + + case FUNSIGN|FLONG: + v = *(ulong*)op->argp; + r = LONG; + break; + + case FSHORT: + h = *(int*)op->argp; + v = h; + r = SHORT; + break; + + case FUNSIGN|FSHORT: + h = *(int*)op->argp; + v = (ushort)h; + r = SHORT; + break; + + default: + v = *(int*)op->argp; + r = INT; + break; + + case FUNSIGN: + v = *(unsigned*)op->argp; + r = INT; + break; + } + if(!(op->f3 & FUNSIGN) && v < 0) { + v = -v; + f = 1; + } + b[IDIGIT-1] = 0; + for(i = IDIGIT-2;; i--) { + n = (ulong)v % base; + n += '0'; + if(n > '9'){ + n += 'a' - ('9'+1); + if(ucase) + n += 'A'-'a'; + } + b[i] = n; + if(i < 2) + break; + v = (ulong)v / base; + if(op->f2 >= 0 && i >= IDIGIT-op->f2) + continue; + if(v <= 0) + break; + } + if(f) + b[--i] = '-'; + strconv(b+i, op, op->f1, -1); + return r; +} + +static int +noconv(Op *op) +{ + + strconv("***", op, 0, -1); + return 0; +} + +static int +cconv(Op *op) +{ + char b[2]; + + b[0] = *(int*)op->argp; + b[1] = 0; + strconv(b, op, op->f1, -1); + return INT; +} + +static int +dconv(Op *op) +{ + return numbconv(op, 10); +} + +static int +hconv(Op*) +{ + return -FSHORT; +} + +static int +lconv(Op*) +{ + return -FLONG; +} + +static int +oconv(Op *op) +{ + return numbconv(op, 8); +} + +static int +sconv(Op *op) +{ + strconv(*(char**)op->argp, op, op->f1, op->f2); + return PTR; +} + +static int +uconv(Op*) +{ + return -FUNSIGN; +} + +static int +xconv(Op *op) +{ + return numbconv(op, 16); +} + +static int +Xconv(Op *op) +{ + int r; + + ucase = 1; + r = numbconv(op, 16); + ucase = 0; + return r; +} + +static int +percent(Op *op) +{ + + PUT(op, '%'); + return 0; +} diff --git a/os/boot/arm1110/fns.h b/os/boot/arm1110/fns.h new file mode 100644 index 00000000..53af19bf --- /dev/null +++ b/os/boot/arm1110/fns.h @@ -0,0 +1,7 @@ +/* + * functions defined locally + */ +extern int gunzip(uchar *out, int outn, uchar *in, int inn); +extern void delay(int ms); +extern void serialputs(char *str, int n); +extern void draincache(void); diff --git a/os/boot/arm1110/il.s b/os/boot/arm1110/il.s new file mode 100644 index 00000000..680750c1 --- /dev/null +++ b/os/boot/arm1110/il.s @@ -0,0 +1,99 @@ +#include "mem.h" +/* + * Entered here from Compaq's bootldr. First relocate to + * the location we're linked for and then copy back the + * decompressed kernel. + * + * All + */ +TEXT _start(SB), $-4 + MOVW $setR12(SB), R12 /* load the SB */ + MOVW $1, R0 /* dance to make 5l think that the magic */ + MOVW $1, R1 /* numbers in WORDs below are being used */ + CMP.S R0, R1 /* and to align them to where bootldr wants */ + BEQ _start2 + WORD $0x016f2818 /* magic number to say we are a kernel */ + WORD $0xc0008000 /* entry point address */ + WORD $0 /* size?, or end of data? */ + +_start2: + + /* SVC mode, interrupts disabled */ + MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R1 + MOVW R1, CPSR + + /* disable the MMU */ + MOVW $0x130, R1 + MCR CpMMU, 0, R1, C(CpControl), C(0x0) + + /* enable caches */ + MRC CpMMU, 0, R0, C(CpControl), C(0x0) + ORR $(CpCdcache|CpCicache|CpCwb), R0 + MCR CpMMU, 0, R0, C(CpControl), C(0x0) + + /* flush caches */ + MCR CpMMU, 0, R0, C(CpCacheFlush), C(0x7), 0 + /* drain prefetch */ + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + + /* drain write buffer */ + MCR CpMMU, 0, R0, C(CpCacheFlush), C(0xa), 4 + + /* relocate to where we expect to be */ + MOVW $(FLATESIZE),R3 + MOVW $0xC0008000,R1 + MOVW $0xC0200000,R2 + ADD R1,R3 +_relloop: + MOVW (R1),R0 + MOVW R0,(R2) + ADD $4,R1 + ADD $4,R2 + CMP.S R1,R3 + BNE _relloop + + MOVW $(MACHADDR+BY2PG), R13 /* stack */ + SUB $4, R13 /* link */ + + /* jump to where we've been relocated */ + MOVW $_relocated(SB),R15 + +TEXT _relocated(SB),$-4 + BL main(SB) + BL exit(SB) + /* we shouldn't get here */ +_mainloop: + B _mainloop + BL _div(SB) /* hack to get _div etc loaded */ + +TEXT mypc(SB),$-4 + MOVW R14,R0 + RET + +TEXT draincache(SB),$-4 + /* write back any dirty data */ + MOVW $0xe0000000,R0 + ADD $(8*1024),R0,R1 +_cfloop: + MOVW.P 32(R0),R2 + CMP.S R0,R1 + BNE _cfloop + + /* drain write buffer and invalidate i&d cache contents */ + MCR CpMMU, 0, R0, C(CpCacheFlush), C(0xa), 4 + MCR CpMMU, 0, R0, C(CpCacheFlush), C(0x7), 0 + + /* drain prefetch */ + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + + /* disable caches */ + MRC CpMMU, 0, R0, C(CpControl), C(0x0) + BIC $(CpCdcache|CpCicache|CpCwb), R0 + MCR CpMMU, 0, R0, C(CpControl), C(0x0) + RET diff --git a/os/boot/arm1110/imain.c b/os/boot/arm1110/imain.c new file mode 100644 index 00000000..b8461f85 --- /dev/null +++ b/os/boot/arm1110/imain.c @@ -0,0 +1,48 @@ +#include "u.h" +#include "lib.h" +#include "fns.h" +#include "dat.h" +#include "mem.h" + +void +main(void) +{ + void (*f)(void); + ulong *kernel; + + print("inflating kernel\n"); + + kernel = (ulong*)(0xc0200000+20*1024); + if(gunzip((uchar*)0xc0008000, 2*1024*1024, (uchar*)kernel, FLATESIZE) > 0){ + f = (void (*)(void))0xc0008010; + draincache(); + } else { + print("inflation failed\n"); + f = nil; + } + (*f)(); +} + +void +exit(void) +{ + + void (*f)(void); + + delay(1000); + + print("it's a wonderful day to die\n"); + f = nil; + (*f)(); +} + +void +delay(int ms) +{ + int i; + + while(ms-- > 0){ + for(i = 0; i < 1000; i++) + ; + } +} diff --git a/os/boot/arm1110/inflate.c b/os/boot/arm1110/inflate.c new file mode 100644 index 00000000..abfed034 --- /dev/null +++ b/os/boot/arm1110/inflate.c @@ -0,0 +1,208 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include + +typedef struct Biobuf Biobuf; + +struct Biobuf +{ + uchar *bp; + uchar *p; + uchar *ep; +}; + +static int header(Biobuf*); +static int trailer(Biobuf*, Biobuf*); +static int getc(void*); +static ulong offset(Biobuf*); +static int crcwrite(void *out, void *buf, int n); +static ulong get4(Biobuf *b); +static ulong Boffset(Biobuf *bp); + +/* GZIP flags */ +enum { + Ftext= (1<<0), + Fhcrc= (1<<1), + Fextra= (1<<2), + Fname= (1<<3), + Fcomment= (1<<4), + + GZCRCPOLY = 0xedb88320UL, +}; + +static ulong *crctab; +static ulong crc; + +int +gunzip(uchar *out, int outn, uchar *in, int inn) +{ + Biobuf bin, bout; + int err; + + crc = 0; + crctab = mkcrctab(GZCRCPOLY); + err = inflateinit(); + if(err != FlateOk) + print("inflateinit failed: %s\n", flateerr(err)); + + bin.bp = bin.p = in; + bin.ep = in+inn; + bout.bp = bout.p = out; + bout.ep = out+outn; + + err = header(&bin); + if(err != FlateOk) + return err; + + err = inflate(&bout, crcwrite, &bin, getc); + if(err != FlateOk) + print("inflate failed: %s\n", flateerr(err)); + + err = trailer(&bout, &bin); + if(err != FlateOk) + return err; + + return Boffset(&bout); +} + +static int +header(Biobuf *bin) +{ + int i, flag; + + if(getc(bin) != 0x1f || getc(bin) != 0x8b){ + print("bad magic\n"); + return FlateCorrupted; + } + if(getc(bin) != 8){ + print("unknown compression type\n"); + return FlateCorrupted; + } + + flag = getc(bin); + + /* mod time */ + get4(bin); + + /* extra flags */ + getc(bin); + + /* OS type */ + getc(bin); + + if(flag & Fextra) + for(i=getc(bin); i>0; i--) + getc(bin); + + /* name */ + if(flag&Fname) + while(getc(bin) != 0) + ; + + /* comment */ + if(flag&Fcomment) + while(getc(bin) != 0) + ; + + /* crc16 */ + if(flag&Fhcrc) { + getc(bin); + getc(bin); + } + + return FlateOk; +} + +static int +trailer(Biobuf *bout, Biobuf *bin) +{ + /* crc32 */ + ulong x; + + x = get4(bin); + if(crc != x){ + print("crc mismatch %lux %lux\n", crc, x); + return FlateCorrupted; + } + + /* length */ + if(get4(bin) != Boffset(bout)){ + print("bad output len\n"); + return FlateCorrupted; + } + return FlateOk; +} + +static ulong +get4(Biobuf *b) +{ + ulong v; + int i, c; + + v = 0; + for(i = 0; i < 4; i++){ + c = getc(b); + v |= c << (i * 8); + } + return v; +} + +static int +getc(void *in) +{ + Biobuf *bp = in; + + if((bp->p - bp->bp) % 10000 == 0) + print("."); + if(bp->p >= bp->ep) + return -1; + return *bp->p++; +} + +static ulong +Boffset(Biobuf *bp) +{ + return bp->p - bp->bp; +} + +static int +crcwrite(void *out, void *buf, int n) +{ + Biobuf *bp; + + crc = blockcrc(crctab, crc, buf, n); + bp = out; + if(n > bp->ep-bp->p) + n = bp->ep-bp->p; + memmove(bp->p, buf, n); + bp->p += n; + return n; +} + +#undef malloc +#undef free + +static ulong ibrkp = ~0; + +void * +malloc(ulong n) +{ + ulong rv; + + if(ibrkp == ~0) + ibrkp = ((ulong)end)+1024*1024; + n = (n+3)>>2; + n <<= 2; + rv = ibrkp; + ibrkp += n; + return (void*)rv; +} + +void +free(void *) +{ +} diff --git a/os/boot/arm1110/io.h b/os/boot/arm1110/io.h new file mode 100644 index 00000000..6791a662 --- /dev/null +++ b/os/boot/arm1110/io.h @@ -0,0 +1,261 @@ +/* + * Definitions for IO devices. Used only in C. + */ + +enum +{ + /* hardware counter frequency */ + ClockFreq= 3686400, +}; + +/* + * IRQ's defined by SA1100 + */ +enum +{ + IRQgpio0= 0, + IRQgpio1= 1, + IRQgpio2= 2, + IRQgpio3= 3, + IRQgpio4= 4, + IRQgpio5= 5, + IRQgpio6= 6, + IRQgpio7= 7, + IRQgpio8= 8, + IRQgpio9= 9, + IRQgpio10= 10, + IRQgpiohi= 11, + IRQlcd= 12, + IRQudc= 13, + IRQuart1b= 15, + IRQuart2= 16, + IRQuart3= 17, + IRQmcp= 18, + IRQssp= 19, + IRQdma0= 20, + IRQdma1= 21, + IRQdma2= 22, + IRQdma3= 23, + IRQdma4= 24, + IRQdma5= 25, + IRQtimer0= 26, + IRQtimer1= 27, + IRQtimer2= 28, + IRQtimer3= 29, + IRQsecond= 30, + IRQrtc= 31, +}; + +/* + * GPIO lines (signal names from compaq document). _i indicates input + * and _o output. + */ +enum +{ + GPIO_PWR_ON_i= 1<<0, /* power button */ + GPIO_UP_IRQ_i= 1<<1, /* microcontroller interrupts */ + GPIO_LDD8_o= 1<<2, /* LCD data 8-15 */ + GPIO_LDD9_o= 1<<3, + GPIO_LDD10_o= 1<<4, + GPIO_LDD11_o= 1<<5, + GPIO_LDD12_o= 1<<6, + GPIO_LDD13_o= 1<<7, + GPIO_LDD14_o= 1<<8, + GPIO_LDD15_o= 1<<9, + GPIO_CARD_IND1_i= 1<<10, /* card inserted in PCMCIA socket 1 */ + GPIO_CARD_IRQ1_i= 1<<11, /* PCMCIA socket 1 interrupt */ + GPIO_CLK_SET0_o= 1<<12, /* clock selects for audio codec */ + GPIO_CLK_SET1_o= 1<<13, + GPIO_L3_SDA_io= 1<<14, /* UDA1341 interface */ + GPIO_L3_MODE_o= 1<<15, + GPIO_L3_SCLK_o= 1<<16, + GPIO_CARD_IND0_i= 1<<17, /* card inserted in PCMCIA socket 0 */ + GPIO_KEY_ACT_i= 1<<18, /* hot key from cradle */ + GPIO_SYS_CLK_i= 1<<19, /* clock from codec */ + GPIO_BAT_FAULT_i= 1<<20, /* battery fault */ + GPIO_CARD_IRQ0_i= 1<<21, /* PCMCIA socket 0 interrupt */ + GPIO_LOCK_i= 1<<22, /* expansion pack lock/unlock */ + GPIO_COM_DCD_i= 1<<23, /* DCD from UART3 */ + GPIO_OPT_IRQ_i= 1<<24, /* expansion pack IRQ */ + GPIO_COM_CTS_i= 1<<25, /* CTS from UART3 */ + GPIO_COM_RTS_o= 1<<26, /* RTS to UART3 */ + GPIO_OPT_IND_i= 1<<27, /* expansion pack inserted */ + +/* Peripheral Unit GPIO pin assignments: alternate functions */ + GPIO_SSP_TXD_o= 1<<10, /* SSP Transmit Data */ + GPIO_SSP_RXD_i= 1<<11, /* SSP Receive Data */ + GPIO_SSP_SCLK_o= 1<<12, /* SSP Sample CLocK */ + GPIO_SSP_SFRM_o= 1<<13, /* SSP Sample FRaMe */ + /* ser. port 1: */ + GPIO_UART_TXD_o= 1<<14, /* UART Transmit Data */ + GPIO_UART_RXD_i= 1<<15, /* UART Receive Data */ + GPIO_SDLC_SCLK_io= 1<<16, /* SDLC Sample CLocK (I/O) */ + GPIO_SDLC_AAF_o= 1<<17, /* SDLC Abort After Frame */ + GPIO_UART_SCLK1_i= 1<<18, /* UART Sample CLocK 1 */ + /* ser. port 4: */ + GPIO_SSP_CLK_i= 1<<19, /* SSP external CLocK */ + /* ser. port 3: */ + GPIO_UART_SCLK3_i= 1<<20, /* UART Sample CLocK 3 */ + /* ser. port 4: */ + GPIO_MCP_CLK_i= 1<<21, /* MCP CLocK */ + /* test controller: */ + GPIO_TIC_ACK_o= 1<<21, /* TIC ACKnowledge */ + GPIO_MBGNT_o= 1<<21, /* Memory Bus GraNT */ + GPIO_TREQA_i= 1<<22, /* TIC REQuest A */ + GPIO_MBREQ_i= 1<<22, /* Memory Bus REQuest */ + GPIO_TREQB_i= 1<<23, /* TIC REQuest B */ + GPIO_1Hz_o= 1<<25, /* 1 Hz clock */ + GPIO_RCLK_o= 1<<26, /* internal (R) CLocK (O, fcpu/2) */ + GPIO_32_768kHz_o= 1<<27, /* 32.768 kHz clock (O, RTC) */ +}; + +/* + * types of interrupts + */ +enum +{ + GPIOrising, + GPIOfalling, + GPIOboth, + IRQ, +}; + +/* hardware registers */ +typedef struct Uartregs Uartregs; +struct Uartregs +{ + ulong ctl[4]; + ulong dummya; + ulong data; + ulong dummyb; + ulong status[2]; +}; +Uartregs *uart3regs; + +/* general purpose I/O lines control registers */ +typedef struct GPIOregs GPIOregs; +struct GPIOregs +{ + ulong level; /* 1 == high */ + ulong direction; /* 1 == output */ + ulong set; /* a 1 sets the bit, 0 leaves it alone */ + ulong clear; /* a 1 clears the bit, 0 leaves it alone */ + ulong rising; /* rising edge detect enable */ + ulong falling; /* falling edge detect enable */ + ulong edgestatus; /* writing a 1 bit clears */ + ulong altfunc; /* turn on alternate function for any set bits */ +}; + +extern GPIOregs *gpioregs; + +/* extra general purpose I/O bits, output only */ +enum +{ + EGPIO_prog_flash= 1<<0, + EGPIO_pcmcia_reset= 1<<1, + EGPIO_exppack_reset= 1<<2, + EGPIO_codec_reset= 1<<3, + EGPIO_exp_nvram_power= 1<<4, + EGPIO_exp_full_power= 1<<5, + EGPIO_lcd_3v= 1<<6, + EGPIO_rs232_power= 1<<7, + EGPIO_lcd_ic_power= 1<<8, + EGPIO_ir_power= 1<<9, + EGPIO_audio_power= 1<<10, + EGPIO_audio_ic_power= 1<<11, + EGPIO_audio_mute= 1<<12, + EGPIO_fir= 1<<13, /* not set is sir */ + EGPIO_lcd_5v= 1<<14, + EGPIO_lcd_9v= 1<<15, +}; +extern ulong *egpioreg; + +/* Peripheral pin controller registers */ +typedef struct PPCregs PPCregs; +struct PPCregs { + ulong direction; + ulong state; + ulong assignment; + ulong sleepdir; + ulong flags; +}; +extern PPCregs *ppcregs; + +/* Synchronous Serial Port controller registers */ +typedef struct SSPregs SSPregs; +struct SSPregs { + ulong control0; + ulong control1; + ulong dummy0; + ulong data; + ulong dummy1; + ulong status; +}; +extern SSPregs *sspregs; + +/* Multimedia Communications Port controller registers */ +typedef struct MCPregs MCPregs; +struct MCPregs { + ulong control0; + ulong reserved0; + ulong data0; + ulong data1; + ulong data2; + ulong reserved1; + ulong status; + ulong reserved[11]; + ulong control1; +}; +extern MCPregs *mcpregs; + +/* + * memory configuration + */ +enum +{ + /* bit shifts for pcmcia access time counters */ + MECR_io0= 0, + MECR_attr0= 5, + MECR_mem0= 10, + MECR_fast0= 11, + MECR_io1= MECR_io0+16, + MECR_attr1= MECR_attr0+16, + MECR_mem1= MECR_mem0+16, + MECR_fast1= MECR_fast0+16, +}; + +typedef struct MemConfRegs MemConfRegs; +struct MemConfRegs +{ + ulong mdcnfg; /* dram */ + ulong mdcas00; /* dram banks 0/1 */ + ulong mdcas01; + ulong mdcas02; + ulong msc0; /* static */ + ulong msc1; + ulong mecr; /* pcmcia */ + ulong mdrefr; /* dram refresh */ + ulong mdcas20; /* dram banks 2/3 */ + ulong mdcas21; + ulong mdcas22; + ulong msc2; /* static */ + ulong smcnfg; /* SMROM config */ +}; +extern MemConfRegs *memconfregs; + +/* + * power management + */ +typedef struct PowerRegs PowerRegs; +struct PowerRegs +{ + ulong pmcr; /* Power manager control register */ + ulong pssr; /* Power manager sleep status register */ + ulong pspr; /* Power manager scratch pad register */ + ulong pwer; /* Power manager wakeup enable register */ + ulong pcfr; /* Power manager general configuration register */ + ulong ppcr; /* Power manager PPL configuration register */ + ulong pgsr; /* Power manager GPIO sleep state register */ + ulong posr; /* Power manager oscillator status register */ +}; +extern PowerRegs *powerregs; diff --git a/os/boot/arm1110/l.s b/os/boot/arm1110/l.s new file mode 100644 index 00000000..0afdb8ba --- /dev/null +++ b/os/boot/arm1110/l.s @@ -0,0 +1,454 @@ +#include "mem.h" + +/* + * Entered here from Compaq's bootldr with MMU disabled. + */ +TEXT _start(SB), $-4 + MOVW $setR12(SB), R12 /* load the SB */ +_main: + /* SVC mode, interrupts disabled */ + MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R1 + MOVW R1, CPSR + + /* disable the MMU */ + MOVW $0x130, R1 + MCR CpMMU, 0, R1, C(CpControl), C(0x0) + + /* flush caches */ + MCR CpMMU, 0, R0, C(CpCacheFlush), C(0x7), 0 + /* drain prefetch */ + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + + /* drain write buffer */ + MCR CpMMU, 0, R0, C(CpCacheFlush), C(0xa), 4 + + MOVW $(MACHADDR+BY2PG), R13 /* stack */ + SUB $4, R13 /* link */ + BL main(SB) + BL exit(SB) + /* we shouldn't get here */ +_mainloop: + B _mainloop + BL _div(SB) /* hack to get _div etc loaded */ + +/* flush tlb's */ +TEXT mmuinvalidate(SB), $-4 + MCR CpMMU, 0, R0, C(CpTLBFlush), C(0x7) + RET + +/* flush tlb's */ +TEXT mmuinvalidateaddr(SB), $-4 + MCR CpMMU, 0, R0, C(CpTLBFlush), C(0x6), 1 + RET + +/* write back and invalidate i and d caches */ +TEXT cacheflush(SB), $-4 + /* write back any dirty data */ + MOVW $0xe0000000,R0 + ADD $(8*1024),R0,R1 +_cfloop: + MOVW.P 32(R0),R2 + CMP.S R0,R1 + BNE _cfloop + + /* drain write buffer and invalidate i&d cache contents */ + MCR CpMMU, 0, R0, C(CpCacheFlush), C(0xa), 4 + MCR CpMMU, 0, R0, C(CpCacheFlush), C(0x7), 0 + + /* drain prefetch */ + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + RET + +/* write back d cache */ +TEXT cachewb(SB), $-4 + /* write back any dirty data */ +_cachewb: + MOVW $0xe0000000,R0 + ADD $(8*1024),R0,R1 +_cwbloop: + MOVW.P 32(R0),R2 + CMP.S R0,R1 + BNE _cwbloop + + /* drain write buffer */ + MCR CpMMU, 0, R0, C(CpCacheFlush), C(0xa), 4 + RET + +/* write back a single cache line */ +TEXT cachewbaddr(SB), $-4 + BIC $31,R0 + MCR CpMMU, 0, R0, C(CpCacheFlush), C(0xa), 1 + B _wbflush + +/* write back a region of cache lines */ +TEXT cachewbregion(SB), $-4 + MOVW 4(FP),R1 + CMP.S $(4*1024),R1 + BGT _cachewb + ADD R0,R1 + BIC $31,R0 +_cwbrloop: + MCR CpMMU, 0, R0, C(CpCacheFlush), C(0xa), 1 + ADD $32,R0 + CMP.S R0,R1 + BGT _cwbrloop + B _wbflush + +/* invalidate the dcache */ +TEXT dcacheinvalidate(SB), $-4 + MCR CpMMU, 0, R0, C(CpCacheFlush), C(0x6) + RET + +/* invalidate the icache */ +TEXT icacheinvalidate(SB), $-4 + MCR CpMMU, 0, R0, C(CpCacheFlush), C(0x9) + RET + +/* drain write buffer */ +TEXT wbflush(SB), $-4 +_wbflush: + MCR CpMMU, 0, R0, C(CpCacheFlush), C(0xa), 4 + RET + +/* return cpu id */ +TEXT getcpuid(SB), $-4 + MRC CpMMU, 0, R0, C(CpCPUID), C(0x0) + RET + +/* return fault status */ +TEXT getfsr(SB), $-4 + MRC CpMMU, 0, R0, C(CpFSR), C(0x0) + RET + +/* return fault address */ +TEXT getfar(SB), $-4 + MRC CpMMU, 0, R0, C(CpFAR), C(0x0) + RET + +/* return fault address */ +TEXT putfar(SB), $-4 + MRC CpMMU, 0, R0, C(CpFAR), C(0x0) + RET + +/* set the translation table base */ +TEXT putttb(SB), $-4 + MCR CpMMU, 0, R0, C(CpTTB), C(0x0) + RET + +/* + * enable mmu, i and d caches + */ +TEXT mmuenable(SB), $-4 + MRC CpMMU, 0, R0, C(CpControl), C(0x0) + ORR $(CpCmmuena|CpCdcache|CpCicache|CpCwb), R0 + MCR CpMMU, 0, R0, C(CpControl), C(0x0) + RET + +TEXT mmudisable(SB), $-4 + MRC CpMMU, 0, R0, C(CpControl), C(0x0) + BIC $(CpCmmuena|CpCdcache|CpCicache|CpCwb|CpCvivec), R0 + MCR CpMMU, 0, R0, C(CpControl), C(0x0) + RET + +/* + * use exception vectors at 0xffff0000 + */ +TEXT mappedIvecEnable(SB), $-4 + MRC CpMMU, 0, R0, C(CpControl), C(0x0) + ORR $(CpCvivec), R0 + MCR CpMMU, 0, R0, C(CpControl), C(0x0) + RET +TEXT mappedIvecDisable(SB), $-4 + MRC CpMMU, 0, R0, C(CpControl), C(0x0) + BIC $(CpCvivec), R0 + MCR CpMMU, 0, R0, C(CpControl), C(0x0) + RET + +/* set the translation table base */ +TEXT putdac(SB), $-4 + MCR CpMMU, 0, R0, C(CpDAC), C(0x0) + RET + +/* set address translation pid */ +TEXT putpid(SB), $-4 + MCR CpMMU, 0, R0, C(CpPID), C(0x0) + RET + +/* + * set the stack value for the mode passed in R0 + */ +TEXT setr13(SB), $-4 + MOVW 4(FP), R1 + + MOVW CPSR, R2 + BIC $PsrMask, R2, R3 + ORR R0, R3 + MOVW R3, CPSR + + MOVW R13, R0 + MOVW R1, R13 + + MOVW R2, CPSR + RET + +/* + * exception vectors, copied by trapinit() to somewhere useful + */ + +TEXT vectors(SB), $-4 + MOVW 0x18(R15), R15 /* reset */ + MOVW 0x18(R15), R15 /* undefined */ + MOVW 0x18(R15), R15 /* SWI */ + MOVW 0x18(R15), R15 /* prefetch abort */ + MOVW 0x18(R15), R15 /* data abort */ + MOVW 0x18(R15), R15 /* reserved */ + MOVW 0x18(R15), R15 /* IRQ */ + MOVW 0x18(R15), R15 /* FIQ */ + +TEXT vtable(SB), $-4 + WORD $_vsvc(SB) /* reset, in svc mode already */ + WORD $_vund(SB) /* undefined, switch to svc mode */ + WORD $_vsvc(SB) /* swi, in svc mode already */ + WORD $_vpabt(SB) /* prefetch abort, switch to svc mode */ + WORD $_vdabt(SB) /* data abort, switch to svc mode */ + WORD $_vsvc(SB) /* reserved */ + WORD $_virq(SB) /* IRQ, switch to svc mode */ + WORD $_vfiq(SB) /* FIQ, switch to svc mode */ + +TEXT _vrst(SB), $-4 + BL resettrap(SB) + +TEXT _vsvc(SB), $-4 /* SWI */ + MOVW.W R14, -4(R13) /* ureg->pc = interupted PC */ + MOVW SPSR, R14 /* ureg->psr = SPSR */ + MOVW.W R14, -4(R13) /* ... */ + MOVW $PsrMsvc, R14 /* ureg->type = PsrMsvc */ + MOVW.W R14, -4(R13) /* ... */ + MOVM.DB.W.S [R0-R14], (R13) /* save user level registers, at end r13 points to ureg */ + MOVW $setR12(SB), R12 /* Make sure we've got the kernel's SB loaded */ + MOVW R13, R0 /* first arg is pointer to ureg */ + SUB $8, R13 /* space for argument+link */ + + BL syscall(SB) + + ADD $(8+4*15), R13 /* make r13 point to ureg->type */ + MOVW 8(R13), R14 /* restore link */ + MOVW 4(R13), R0 /* restore SPSR */ + MOVW R0, SPSR /* ... */ + MOVM.DB.S (R13), [R0-R14] /* restore registers */ + ADD $8, R13 /* pop past ureg->{type+psr} */ + RFE /* MOVM.IA.S.W (R13), [R15] */ + +TEXT _vund(SB), $-4 /* undefined */ + MOVM.IA [R0-R4], (R13) /* free some working space */ + MOVW $PsrMund, R0 + B _vswitch + +TEXT _vpabt(SB), $-4 /* prefetch abort */ + MOVM.IA [R0-R4], (R13) /* free some working space */ + MOVW $PsrMabt, R0 /* r0 = type */ + B _vswitch + +TEXT _vdabt(SB), $-4 /* prefetch abort */ + MOVM.IA [R0-R4], (R13) /* free some working space */ + MOVW $(PsrMabt+1), R0 /* r0 = type */ + B _vswitch + +TEXT _virq(SB), $-4 /* IRQ */ + MOVM.IA [R0-R4], (R13) /* free some working space */ + MOVW $PsrMirq, R0 /* r0 = type */ + B _vswitch + + /* + * come here with type in R0 and R13 pointing above saved [r0-r4] + * and type in r0. we'll switch to SVC mode and then call trap. + */ +_vswitch: + MOVW SPSR, R1 /* save SPSR for ureg */ + MOVW R14, R2 /* save interrupted pc for ureg */ + MOVW R13, R3 /* save pointer to where the original [R0-R3] are */ + + /* switch to svc mode */ + MOVW CPSR, R14 + BIC $PsrMask, R14 + ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14 + MOVW R14, CPSR + + /* interupted code kernel or user? */ + AND.S $0xf, R1, R4 + BEQ _userexcep + + /* here for trap from SVC mode */ + MOVM.DB.W [R0-R2], (R13) /* set ureg->{type, psr, pc}; r13 points to ureg->type */ + MOVM.IA (R3), [R0-R4] /* restore [R0-R4] from previous mode's stack */ + MOVM.DB.W [R0-R14], (R13) /* save kernel level registers, at end r13 points to ureg */ + MOVW $setR12(SB), R12 /* Make sure we've got the kernel's SB loaded */ + MOVW R13, R0 /* first arg is pointer to ureg */ + SUB $8, R13 /* space for argument+link (for debugger) */ + MOVW $0xdeaddead,R11 /* marker */ + + BL trap(SB) + + ADD $(8+4*15), R13 /* make r13 point to ureg->type */ + MOVW 8(R13), R14 /* restore link */ + MOVW 4(R13), R0 /* restore SPSR */ + MOVW R0, SPSR /* ... */ + MOVM.DB (R13), [R0-R14] /* restore registers */ + ADD $8, R13 /* pop past ureg->{type+psr} */ + RFE /* MOVM.IA.S.W (R13), [R15] */ + + /* here for trap from USER mode */ +_userexcep: + MOVM.DB.W [R0-R2], (R13) /* set ureg->{type, psr, pc}; r13 points to ureg->type */ + MOVM.IA (R3), [R0-R4] /* restore [R0-R4] from previous mode's stack */ + MOVM.DB.W.S [R0-R14], (R13) /* save kernel level registers, at end r13 points to ureg */ + MOVW $setR12(SB), R12 /* Make sure we've got the kernel's SB loaded */ + MOVW R13, R0 /* first arg is pointer to ureg */ + SUB $8, R13 /* space for argument+link (for debugger) */ + + BL trap(SB) + + ADD $(8+4*15), R13 /* make r13 point to ureg->type */ + MOVW 8(R13), R14 /* restore link */ + MOVW 4(R13), R0 /* restore SPSR */ + MOVW R0, SPSR /* ... */ + MOVM.DB.S (R13), [R0-R14] /* restore registers */ + ADD $8, R13 /* pop past ureg->{type+psr} */ + RFE /* MOVM.IA.S.W (R13), [R15] */ + +TEXT _vfiq(SB), $-4 /* FIQ */ + RFE /* FIQ is special, ignore it for now */ + +/* + * This is the first jump from kernel to user mode. + * Fake a return from interrupt. + * + * Enter with R0 containing the user stack pointer. + * UTZERO + 0x20 is always the entry point. + * + */ +TEXT touser(SB),$-4 + /* store the user stack pointer into the USR_r13 */ + MOVM.DB.W [R0], (R13) + MOVM.S.IA.W (R13),[R13] + + /* set up a PSR for user level */ + MOVW $(PsrMusr), R0 + MOVW R0,SPSR + + /* save the PC on the stack */ + MOVW $(UTZERO+0x20), R0 + MOVM.DB.W [R0],(R13) + + /* return from interrupt */ + RFE /* MOVM.IA.S.W (R13), [R15] */ + +/* + * here to jump to a newly forked process + */ +TEXT forkret(SB),$-4 + ADD $(4*15), R13 /* make r13 point to ureg->type */ + MOVW 8(R13), R14 /* restore link */ + MOVW 4(R13), R0 /* restore SPSR */ + MOVW R0, SPSR /* ... */ + MOVM.DB.S (R13), [R0-R14] /* restore registers */ + ADD $8, R13 /* pop past ureg->{type+psr} */ + RFE /* MOVM.IA.S.W (R13), [R15] */ + +TEXT splhi(SB), $-4 + /* save caller pc in Mach */ + MOVW $(MACHADDR+0x04),R2 + MOVW R14,0(R2) + /* turn off interrupts */ + MOVW CPSR, R0 + ORR $(PsrDfiq|PsrDirq), R0, R1 + MOVW R1, CPSR + RET + +TEXT spllo(SB), $-4 + MOVW CPSR, R0 + BIC $(PsrDfiq|PsrDirq), R0, R1 + MOVW R1, CPSR + RET + +TEXT splx(SB), $-4 + /* save caller pc in Mach */ + MOVW $(MACHADDR+0x04),R2 + MOVW R14,0(R2) + /* reset interrupt level */ + MOVW R0, R1 + MOVW CPSR, R0 + MOVW R1, CPSR + RET + +TEXT splxpc(SB), $-4 /* for iunlock */ + MOVW R0, R1 + MOVW CPSR, R0 + MOVW R1, CPSR + RET + +TEXT spldone(SB), $0 + RET + +TEXT islo(SB), $-4 + MOVW CPSR, R0 + AND $(PsrDfiq|PsrDirq), R0 + EOR $(PsrDfiq|PsrDirq), R0 + RET + +TEXT cpsrr(SB), $-4 + MOVW CPSR, R0 + RET + +TEXT spsrr(SB), $-4 + MOVW SPSR, R0 + RET + +TEXT getcallerpc(SB), $-4 + MOVW 0(R13), R0 + RET + +TEXT tas(SB), $-4 + MOVW R0, R1 + MOVW $0xDEADDEAD, R2 + SWPW R2, (R1), R0 + RET + +TEXT setlabel(SB), $-4 + MOVW R13, 0(R0) /* sp */ + MOVW R14, 4(R0) /* pc */ + MOVW $0, R0 + RET + +TEXT gotolabel(SB), $-4 + MOVW 0(R0), R13 /* sp */ + MOVW 4(R0), R14 /* pc */ + MOVW $1, R0 + RET + + +/* The first MCR instruction of this function needs to be on a cache-line + * boundary; to make this happen, it will be copied (in trap.c). + * + * Doze puts the machine into idle mode. Any interrupt will get it out + * at the next instruction (the RET, to be precise). + */ +TEXT _doze(SB), $-4 + MOVW $UCDRAMZERO, R1 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MCR CpPWR, 0, R0, C(CpTest), C(0x2), 2 + MOVW (R1), R0 + MCR CpPWR, 0, R0, C(CpTest), C(0x8), 2 + RET diff --git a/os/boot/arm1110/lib.h b/os/boot/arm1110/lib.h new file mode 100644 index 00000000..a25aca6b --- /dev/null +++ b/os/boot/arm1110/lib.h @@ -0,0 +1,143 @@ +/* + * functions (possibly) linked in, complete, from libc. + */ + +/* + * mem routines + */ +extern void *memccpy(void*, void*, int, long); +extern void *memset(void*, int, long); +extern int memcmp(void*, void*, long); +extern void *memmove(void*, void*, long); +extern void *memchr(void*, int, long); + +/* + * string routines + */ +extern char *strcat(char*, char*); +extern char *strchr(char*, char); +extern char *strrchr(char*, char); +extern int strcmp(char*, char*); +extern char *strcpy(char*, char*); +extern char *strncat(char*, char*, long); +extern char *strncpy(char*, char*, long); +extern int strncmp(char*, char*, long); +extern long strlen(char*); +extern char* strstr(char*, char*); +extern int atoi(char*); + +enum +{ + UTFmax = 3, /* maximum bytes per rune */ + Runesync = 0x80, /* cannot represent part of a UTF sequence */ + Runeself = 0x80, /* rune and UTF sequences are the same (<) */ + Runeerror = 0x80, /* decoding error in UTF */ +}; + +/* + * rune routines + */ +extern int runetochar(char*, Rune*); +extern int chartorune(Rune*, char*); +extern char* utfrune(char*, long); +extern int utflen(char*); +extern int runelen(long); + +extern int abs(int); + +/* + * print routines + */ +typedef struct Cconv Fconv; +extern char* donprint(char*, char*, char*, void*); +extern int sprint(char*, char*, ...); +extern char* seprint(char*, char*, char*, ...); +extern int snprint(char*, int, char*, ...); +extern int print(char*, ...); + +/* + * one-of-a-kind + */ +extern char* cleanname(char*); +extern ulong getcallerpc(void*); +extern long strtol(char*, char**, int); +extern ulong strtoul(char*, char**, int); +extern vlong strtoll(char*, char**, int); +extern uvlong strtoull(char*, char**, int); +extern char etext[]; +extern char edata[]; +extern char end[]; +extern int getfields(char*, char**, int, int, char*); + +/* + * Syscall data structures + */ +#define MORDER 0x0003 /* mask for bits defining order of mounting */ +#define MREPL 0x0000 /* mount replaces object */ +#define MBEFORE 0x0001 /* mount goes before others in union directory */ +#define MAFTER 0x0002 /* mount goes after others in union directory */ +#define MCREATE 0x0004 /* permit creation in mounted directory */ +#define MCACHE 0x0010 /* cache some data */ +#define MMASK 0x001F /* all bits on */ + +#define OREAD 0 /* open for read */ +#define OWRITE 1 /* write */ +#define ORDWR 2 /* read and write */ +#define OEXEC 3 /* execute, == read but check execute permission */ +#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ +#define OCEXEC 32 /* or'ed in, close on exec */ +#define ORCLOSE 64 /* or'ed in, remove on close */ + +#define NCONT 0 /* continue after note */ +#define NDFLT 1 /* terminate after note */ +#define NSAVE 2 /* clear note but hold state */ +#define NRSTR 3 /* restore saved state */ + +typedef struct Qid Qid; +typedef struct Dir Dir; +typedef struct Waitmsg Waitmsg; + +#define ERRLEN 64 +#define DIRLEN 116 +#define NAMELEN 28 + +struct Qid +{ + ulong path; + ulong vers; +}; + +struct Dir +{ + char name[NAMELEN]; + char uid[NAMELEN]; + char gid[NAMELEN]; + Qid qid; + ulong mode; + long atime; + long mtime; + vlong length; + short type; + short dev; +}; + +struct Waitmsg +{ + char pid[12]; /* of loved one */ + char time[3*12]; /* of loved one and descendants */ + char msg[ERRLEN]; +}; + +/* + * locks + */ +typedef +struct Lock { + int val; +} Lock; + +extern int _tas(int*); + +extern void lock(Lock*); +extern void unlock(Lock*); +extern int canlock(Lock*); diff --git a/os/boot/arm1110/map b/os/boot/arm1110/map new file mode 100644 index 00000000..87f67bec --- /dev/null +++ b/os/boot/arm1110/map @@ -0,0 +1,10 @@ +defn acidmap() +{ + local dfoffset; + + dfoffset = map()[1][3]; + map({"text", _start, etext, 0x20}); + map({"data", etext+1, edata, dfoffset}); + print("Set map for plan 9 kernel image\n"); + print("btext ", _start, " etext ", etext, "\n"); +} diff --git a/os/boot/arm1110/mem.h b/os/boot/arm1110/mem.h new file mode 100644 index 00000000..a00b0fea --- /dev/null +++ b/os/boot/arm1110/mem.h @@ -0,0 +1,215 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ + +/* + * Sizes + */ +#define BI2BY 8 /* bits per byte */ +#define BI2WD 32 /* bits per word */ +#define BY2WD 4 /* bytes per word */ +#define BY2V 8 /* bytes per double word */ +#define BY2PG 4096 /* bytes per page */ +#define WD2PG (BY2PG/BY2WD) /* words per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1)) +#define PGROUND(s) ROUND(s, BY2PG) +#define BLOCKALIGN 8 + +#define MAXMACH 1 /* max # cpus system can run */ + +/* + * Time + */ +#define HZ (20) /* clock frequency */ +#define MS2HZ (1000/HZ) /* millisec per clock tick */ +#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ +#define TK2MS(t) ((((ulong)(t))*1000)/HZ) /* ticks to milliseconds */ +#define MS2TK(t) ((((ulong)(t))*HZ)/1000) /* milliseconds to ticks */ + +/* + * Virtual addresses: + * + * We direct map all discovered DRAM and the area twixt 0xe0000000 and + * 0xe8000000 used to provide zeros for cache flushing. + * + * Flash is mapped to 0xb0000000 and special registers are mapped + * on demand to areas starting at 0xa0000000. + * + * The direct mapping is convenient but not necessary. It means + * that we don't have to turn on the MMU till well into the + * kernel. This can be changed by providing a mapping in l.s + * before calling main. + */ +#define UZERO 0 /* base of user address space */ +#define UTZERO (UZERO+BY2PG) /* first address in user text */ +#define KZERO 0xC0000000 /* base of kernel address space */ +#define KTZERO 0xC0008000 /* first address in kernel text */ +#define EMEMZERO 0x90000000 /* 256 meg for add on memory */ +#define EMEMTOP 0xA0000000 /* ... */ +#define REGZERO 0xA0000000 /* 128 meg for mapspecial regs */ +#define REGTOP 0xA8000000 /* ... */ +#define FLASHZERO 0xB0000000 /* 128 meg for flash */ +#define FLASHTOP 0xB8000000 /* ... */ +#define DRAMZERO 0xC0000000 /* 128 meg for dram */ +#define DRAMTOP 0xC8000000 /* ... */ +#define UCDRAMZERO 0xC8000000 /* 128 meg for dram (uncached/unbuffered) */ +#define UCDRAMTOP 0xD0000000 /* ... */ +#define NULLZERO 0xE0000000 /* 128 meg for cache flush zeroes */ +#define NULLTOP 0xE8000000 /* ... */ +#define USTKTOP 0x2000000 /* byte just beyond user stack */ +#define USTKSIZE (8*1024*1024) /* size of user stack */ +#define TSTKTOP (USTKTOP-USTKSIZE) /* end of new stack in sysexec */ +#define TSTKSIZ 100 +#define MACHADDR (KZERO+0x00001000) +#define EVECTORS 0xFFFF0000 /* virt base of exception vectors */ + +#define KSTACK (16*1024) /* Size of kernel stack */ + +#define FLATESIZE (700*1024) /* maximum size of compressed image */ + +/* + * Offsets into flash + */ +#define Flash_bootldr (FLASHZERO+0x0) /* boot loader */ +#define Flash_kernel (FLASHZERO+0x10000) /* boot kernel */ +#define Flash_tar (FLASHZERO+0x100000) /* tar file containing fs.sac */ + +/* + * virtual MMU + */ +#define PTEMAPMEM (1024*1024) +#define PTEPERTAB (PTEMAPMEM/BY2PG) +#define SEGMAPSIZE 1984 +#define SSEGMAPSIZE 16 +#define PPN(x) ((x)&~(BY2PG-1)) + +/* + * SA1110 definitions + */ + +/* + * memory physical addresses + */ +#define PHYSFLASH0 0x00000000 +#define PHYSDRAM0 0xC0000000 +#define PHYSNULL0 0xE0000000 + +/* + * peripheral control module physical addresses + */ +#define USBREGS 0x80000000 /* serial port 0 - USB */ +#define UART1REGS 0x80010000 /* serial port 1 - UART */ +#define GPCLKREGS 0x80020060 /* serial port 1 - general purpose clock */ +#define UART2REGS 0x80030000 /* serial port 2 - low speed IR */ +#define HSSPREGS 0x80040060 /* serial port 2 - high speed IR */ +#define UART3REGS 0x80050000 /* serial port 3 - RS232 UART */ +#define MCPREGS 0x80060000 /* serial port 4 - multimedia comm port */ +#define SSPREGS 0x80070060 /* serial port 4 - synchronous serial port */ +#define OSTIMERREGS 0x90000000 /* operating system timer registers */ +#define POWERREGS 0x90020000 /* power management */ +#define GPIOREGS 0x90040000 /* 28 general purpose IO pins */ +#define INTRREGS 0x90050000 /* interrupt registers */ +#define PPCREGS 0x90060000 /* peripheral pin controller */ +#define MEMCONFREGS 0xA0000000 /* memory configuration */ +#define LCDREGS 0xB0100000 /* display */ + +/* + * PCMCIA addresses + */ +#define PHYSPCM0REGS 0x20000000 +#define PYHSPCM0ATTR 0x28000000 +#define PYHSPCM0MEM 0x2C000000 +#define PHYSPCM1REGS 0x30000000 +#define PYHSPCM1ATTR 0x38000000 +#define PYHSPCM1MEM 0x3C000000 + +/* + * Program Status Registers + */ +#define PsrMusr 0x00000010 /* mode */ +#define PsrMfiq 0x00000011 +#define PsrMirq 0x00000012 +#define PsrMsvc 0x00000013 +#define PsrMabt 0x00000017 +#define PsrMund 0x0000001B +#define PsrMask 0x0000001F + +#define PsrDfiq 0x00000040 /* disable FIQ interrupts */ +#define PsrDirq 0x00000080 /* disable IRQ interrupts */ + +#define PsrV 0x10000000 /* overflow */ +#define PsrC 0x20000000 /* carry/borrow/extend */ +#define PsrZ 0x40000000 /* zero */ +#define PsrN 0x80000000 /* negative/less than */ + +/* + * Coprocessors + */ +#define CpMMU 15 +#define CpPWR 15 + +/* + * Internal MMU coprocessor registers + */ +#define CpCPUID 0 /* R: */ +#define CpControl 1 /* R: */ +#define CpTTB 2 /* RW: translation table base */ +#define CpDAC 3 /* RW: domain access control */ +#define CpFSR 5 /* RW: fault status */ +#define CpFAR 6 /* RW: fault address */ +#define CpCacheFlush 7 /* W: cache flushing, wb draining*/ +#define CpTLBFlush 8 /* W: TLB flushing */ +#define CpRBFlush 9 /* W: Read Buffer ops */ +#define CpPID 13 /* RW: PID for virtual mapping */ +#define CpBpt 14 /* W: Breakpoint register */ +#define CpTest 15 /* W: Test, Clock and Idle Control */ + +/* + * CpControl + */ +#define CpCmmuena 0x00000001 /* M: MMU enable */ +#define CpCalign 0x00000002 /* A: alignment fault enable */ +#define CpCdcache 0x00000004 /* C: data cache on */ +#define CpCwb 0x00000008 /* W: write buffer turned on */ +#define CpCi32 0x00000010 /* P: 32-bit program space */ +#define CpCd32 0x00000020 /* D: 32-bit data space */ +#define CpCbe 0x00000080 /* B: big-endian operation */ +#define CpCsystem 0x00000100 /* S: system permission */ +#define CpCrom 0x00000200 /* R: ROM permission */ +#define CpCicache 0x00001000 /* I: instruction cache on */ +#define CpCvivec 0x00002000 /* X: virtual interrupt vector adjust */ + +/* + * fault codes + */ +#define FCterm 0x2 /* terminal */ +#define FCvec 0x0 /* vector */ +#define FCalignf 0x1 /* unaligned full word data access */ +#define FCalignh 0x3 /* unaligned half word data access */ +#define FCl1abort 0xc /* level 1 external abort on translation */ +#define FCl2abort 0xe /* level 2 external abort on translation */ +#define FCtransSec 0x5 /* section translation */ +#define FCtransPage 0x7 /* page translation */ +#define FCdomainSec 0x9 /* section domain */ +#define FCdomainPage 0x11 /* page domain */ +#define FCpermSec 0x9 /* section permissions */ +#define FCpermPage 0x11 /* page permissions */ +#define FCabortLFSec 0x4 /* external abort on linefetch for section */ +#define FCabortLFPage 0x6 /* external abort on linefetch for page */ +#define FCabortNLFSec 0x8 /* external abort on non-linefetch for section */ +#define FCabortNLFPage 0xa /* external abort on non-linefetch for page */ + +/* + * PTE bits used by fault.h. mmu.c translates them to real values. + */ +#define PTEVALID (1<<0) +#define PTERONLY 0 /* this is implied by the absence of PTEWRITE */ +#define PTEWRITE (1<<1) +#define PTEUNCACHED (1<<2) +#define PTEKERNEL (1<<3) /* no user access */ + +/* + * H3650 specific definitions + */ +#define EGPIOREGS 0x49000000 /* Additional GPIO register */ diff --git a/os/boot/arm1110/mkfile b/os/boot/arm1110/mkfile new file mode 100644 index 00000000..215196df --- /dev/null +++ b/os/boot/arm1110/mkfile @@ -0,0 +1,86 @@ +<../../../mkconfig +objtype=arm +SYSTARG=$OSTARG +OBJTYPE=arm +BIN=$ROOT/Inferno/$OBJTYPE +LIBDIR=$ROOT/Inferno/$OBJTYPE/lib +LIBDIRS=../libflate $ROOT/libkern +LIBS=\ + libflate\ + libkern\ + +LIBFILES=${LIBS:%=$LIBDIR/%.a} +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE + +BIN=$ROOT/Inferno/$OBJTYPE + +TARG=\ + inflate\ + +INFLATE=\ + il.$O\ + imain.$O\ + +CORE=\ + uart.$O\ + inflate.$O\ + donprint.$O\ + print.$O\ + +HFILES=\ + mem.h\ + +CFLAGS=-FVw -I. -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include + +all:V: $TARG + +install:V: $BIN/$TARG + +$BIN/%: % + cp $stem $BIN/$stem + +inflate: $INFLATE $CORE $LIBFILES + $LD -o s$target -R4 -T0xC0200010 -l $prereq + $LD -o _$target -H5 -R4 -T0xC0200010 -l $prereq + dd -conv sync -ibs 20k -if _$target -of $target + +%.$O: %.s + $AS $stem.s + +%.$O: %.c + $CC $CFLAGS $stem.c + +%.$O: $HFILES + +clean: + rm -f *.[$OS] [$OS].out y.tab.? y.debug y.output $TARG _$TARG s$TARG + + +# added to cause libflate to be made automatically: + +$ROOT/Inferno/$OBJTYPE/lib/lib%.a:Q: all-$SHELLTYPE + # + +rc-lib%.a nt-lib%.a:VQ: + echo '@{builtin cd ' $ROOT/lib$stem ';mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install}' + @{builtin cd $ROOT/lib$stem ;mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install} + +sh-lib%.a:VQ: + echo "(cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install)" + (cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install) + +%-sh:QV: + for i in $LIBDIRS + do + echo "(cd $i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE $stem)" + (cd $i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE $stem) + done + +%-rc %-nt:QV: + for (i in $LIBDIRS) + { + echo '@{cd $i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE $stem}' + @{cd $i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE $stem} + } + +nuke:V: clean nuke-$SHELLTYPE diff --git a/os/boot/arm1110/print.c b/os/boot/arm1110/print.c new file mode 100644 index 00000000..f2b85287 --- /dev/null +++ b/os/boot/arm1110/print.c @@ -0,0 +1,56 @@ +#include "u.h" +#include "lib.h" +#include "fns.h" +#include "dat.h" + + +#define SIZE 1024 + +int +print(char *fmt, ...) +{ + char buf[SIZE], *out; + va_list arg; + + va_start(arg, fmt); + out = donprint(buf, buf+SIZE, fmt, arg); + va_end(arg); + serialputs(buf, out-buf); + return out-buf; +} + +int +sprint(char *buf, char *fmt, ...) +{ + char *out; + va_list arg; + + va_start(arg, fmt); + out = donprint(buf, buf+SIZE, fmt, arg); + va_end(arg); + return out-buf; +} + +int +snprint(char *buf, int len, char *fmt, ...) +{ + char *out; + va_list arg; + + va_start(arg, fmt); + out = donprint(buf, buf+len, fmt, arg); + va_end(arg); + return out-buf; +} + +char* +seprint(char *buf, char *e, char *fmt, ...) +{ + char *out; + va_list arg; + + va_start(arg, fmt); + out = donprint(buf, e, fmt, arg); + va_end(arg); + return out; +} diff --git a/os/boot/arm1110/uart.c b/os/boot/arm1110/uart.c new file mode 100644 index 00000000..e84a06eb --- /dev/null +++ b/os/boot/arm1110/uart.c @@ -0,0 +1,69 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +enum +{ + /* ctl[0] bits */ + Parity= 1<<0, + Even= 1<<1, + Stop2= 1<<2, + Bits8= 1<<3, + SCE= 1<<4, /* synchronous clock enable */ + RCE= 1<<5, /* rx on falling edge of clock */ + TCE= 1<<6, /* tx on falling edge of clock */ + + /* ctl[3] bits */ + Rena= 1<<0, /* receiver enable */ + Tena= 1<<1, /* transmitter enable */ + Break= 1<<2, /* force TXD3 low */ + Rintena= 1<<3, /* enable receive interrupt */ + Tintena= 1<<4, /* enable transmitter interrupt */ + Loopback= 1<<5, /* loop back data */ + + /* data bits */ + DEparity= 1<<8, /* parity error */ + DEframe= 1<<9, /* framing error */ + DEoverrun= 1<<10, /* overrun error */ + + /* status[0] bits */ + Tint= 1<<0, /* transmit fifo half full interrupt */ + Rint0= 1<<1, /* receiver fifo 1/3-2/3 full */ + Rint1= 1<<2, /* receiver fifo not empty and receiver idle */ + Breakstart= 1<<3, + Breakend= 1<<4, + Fifoerror= 1<<5, /* fifo error */ + + /* status[1] bits */ + Tbusy= 1<<0, /* transmitting */ + Rnotempty= 1<<1, /* receive fifo not empty */ + Tnotfull= 1<<2, /* transmit fifo not full */ + ParityError= 1<<3, + FrameError= 1<<4, + Overrun= 1<<5, +}; + +Uartregs *uart3regs = UART3REGS; + + +/* + * for iprint, just write it + */ +void +serialputs(char *str, int n) +{ + Uartregs *ur; + + ur = uart3regs; + while(n-- > 0){ + /* wait for output ready */ + while((ur->status[1] & Tnotfull) == 0) + ; + ur->data = *str++; + } + while((ur->status[1] & Tbusy)) + ; +} diff --git a/os/boot/libflate/LICENCE b/os/boot/libflate/LICENCE new file mode 100644 index 00000000..a4418218 --- /dev/null +++ b/os/boot/libflate/LICENCE @@ -0,0 +1,237 @@ +Lucent Public License Version 1.02 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS PUBLIC +LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE +PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + + a. in the case of Lucent Technologies Inc. ("LUCENT"), the Original + Program, and + b. in the case of each Contributor, + + i. changes to the Program, and + ii. additions to the Program; + + where such changes and/or additions to the Program were added to the + Program by such Contributor itself or anyone acting on such + Contributor's behalf, and the Contributor explicitly consents, in + accordance with Section 3C, to characterization of the changes and/or + additions as Contributions. + +"Contributor" means LUCENT and any other entity that has Contributed a +Contribution to the Program. + +"Distributor" means a Recipient that distributes the Program, +modifications to the Program, or any part thereof. + +"Licensed Patents" mean patent claims licensable by a Contributor +which are necessarily infringed by the use or sale of its Contribution +alone or when combined with the Program. + +"Original Program" means the original version of the software +accompanying this Agreement as released by LUCENT, including source +code, object code and documentation, if any. + +"Program" means the Original Program and Contributions or any part +thereof + +"Recipient" means anyone who receives the Program under this +Agreement, including all Contributors. + +2. GRANT OF RIGHTS + + a. Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free copyright + license to reproduce, prepare derivative works of, publicly display, + publicly perform, distribute and sublicense the Contribution of such + Contributor, if any, and such derivative works, in source code and + object code form. + + b. Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free patent + license under Licensed Patents to make, use, sell, offer to sell, + import and otherwise transfer the Contribution of such Contributor, if + any, in source code and object code form. The patent license granted + by a Contributor shall also apply to the combination of the + Contribution of that Contributor and the Program if, at the time the + Contribution is added by the Contributor, such addition of the + Contribution causes such combination to be covered by the Licensed + Patents. The patent license granted by a Contributor shall not apply + to (i) any other combinations which include the Contribution, nor to + (ii) Contributions of other Contributors. No hardware per se is + licensed hereunder. + + c. Recipient understands that although each Contributor grants the + licenses to its Contributions set forth herein, no assurances are + provided by any Contributor that the Program does not infringe the + patent or other intellectual property rights of any other entity. Each + Contributor disclaims any liability to Recipient for claims brought by + any other entity based on infringement of intellectual property rights + or otherwise. As a condition to exercising the rights and licenses + granted hereunder, each Recipient hereby assumes sole responsibility + to secure any other intellectual property rights needed, if any. For + example, if a third party patent license is required to allow + Recipient to distribute the Program, it is Recipient's responsibility + to acquire that license before distributing the Program. + + d. Each Contributor represents that to its knowledge it has sufficient + copyright rights in its Contribution, if any, to grant the copyright + license set forth in this Agreement. + +3. REQUIREMENTS + +A. Distributor may choose to distribute the Program in any form under +this Agreement or under its own license agreement, provided that: + + a. it complies with the terms and conditions of this Agreement; + + b. if the Program is distributed in source code or other tangible + form, a copy of this Agreement or Distributor's own license agreement + is included with each copy of the Program; and + + c. if distributed under Distributor's own license agreement, such + license agreement: + + i. effectively disclaims on behalf of all Contributors all warranties + and conditions, express and implied, including warranties or + conditions of title and non-infringement, and implied warranties or + conditions of merchantability and fitness for a particular purpose; + ii. effectively excludes on behalf of all Contributors all liability + for damages, including direct, indirect, special, incidental and + consequential damages, such as lost profits; and + iii. states that any provisions which differ from this Agreement are + offered by that Contributor alone and not by any other party. + +B. Each Distributor must include the following in a conspicuous + location in the Program: + + Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights + Reserved. + +C. In addition, each Contributor must identify itself as the +originator of its Contribution in a manner that reasonably allows +subsequent Recipients to identify the originator of the Contribution. +Also, each Contributor must agree that the additions and/or changes +are intended to be a Contribution. Once a Contribution is contributed, +it may not thereafter be revoked. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain +responsibilities with respect to end users, business partners and the +like. While this license is intended to facilitate the commercial use +of the Program, the Distributor who includes the Program in a +commercial product offering should do so in a manner which does not +create potential liability for Contributors. Therefore, if a +Distributor includes the Program in a commercial product offering, +such Distributor ("Commercial Distributor") hereby agrees to defend +and indemnify every Contributor ("Indemnified Contributor") against +any losses, damages and costs (collectively"Losses") arising from +claims, lawsuits and other legal actions brought by a third party +against the Indemnified Contributor to the extent caused by the acts +or omissions of such Commercial Distributor in connection with its +distribution of the Program in a commercial product offering. The +obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. +In order to qualify, an Indemnified Contributor must: a) promptly +notify the Commercial Distributor in writing of such claim, and b) +allow the Commercial Distributor to control, and cooperate with the +Commercial Distributor in, the defense and any related settlement +negotiations. The Indemnified Contributor may participate in any such +claim at its own expense. + +For example, a Distributor might include the Program in a commercial +product offering, Product X. That Distributor is then a Commercial +Distributor. If that Commercial Distributor then makes performance +claims, or offers warranties related to Product X, those performance +claims and warranties are such Commercial Distributor's responsibility +alone. Under this section, the Commercial Distributor would have to +defend claims against the Contributors related to those performance +claims and warranties, and if a court requires any Contributor to pay +any damages as a result, the Commercial Distributor must pay those +damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS +PROVIDED ON AN"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY +WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY +OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely +responsible for determining the appropriateness of using and +distributing the Program and assumes all risks associated with its +exercise of rights under this Agreement, including but not limited to +the risks and costs of program errors, compliance with applicable +laws, damage to or loss of data, programs or equipment, and +unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR +ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING +WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR +DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED +HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. EXPORT CONTROL + +Recipient agrees that Recipient alone is responsible for compliance +with the United States export administration regulations (and the +export control laws and regulation of any other countries). + +8. GENERAL + +If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of +the remainder of the terms of this Agreement, and without further +action by the parties hereto, such provision shall be reformed to the +minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against a Contributor with +respect to a patent applicable to software (including a cross-claim or +counterclaim in a lawsuit), then any patent licenses granted by that +Contributor to such Recipient under this Agreement shall terminate as +of the date such litigation is filed. In addition, if Recipient +institutes patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Program +itself (excluding combinations of the Program with other software or +hardware) infringes such Recipient's patent(s), then such Recipient's +rights granted under Section 2(b) shall terminate as of the date such +litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it +fails to comply with any of the material terms or conditions of this +Agreement and does not cure such failure in a reasonable period of +time after becoming aware of such noncompliance. If all Recipient's +rights under this Agreement terminate, Recipient agrees to cease use +and distribution of the Program as soon as reasonably practicable. +However, Recipient's obligations under this Agreement and any licenses +granted by Recipient relating to the Program shall continue and +survive. + +LUCENT may publish new versions (including revisions) of this +Agreement from time to time. Each new version of the Agreement will be +given a distinguishing version number. The Program (including +Contributions) may always be distributed subject to the version of the +Agreement under which it was received. In addition, after a new +version of the Agreement is published, Contributor may elect to +distribute the Program (including its Contributions) under the new +version. No one other than LUCENT has the right to modify this +Agreement. Except as expressly stated in Sections 2(a) and 2(b) above, +Recipient receives no rights or licenses to the intellectual property +of any Contributor under this Agreement, whether expressly, by +implication, estoppel or otherwise. All rights in the Program not +expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and +the intellectual property laws of the United States of America. No +party to this Agreement will bring a legal action under this Agreement +more than one year after the cause of action arose. Each party waives +its rights to a jury trial in any resulting litigation. + diff --git a/os/boot/libflate/NOTICE b/os/boot/libflate/NOTICE new file mode 100644 index 00000000..bd67649a --- /dev/null +++ b/os/boot/libflate/NOTICE @@ -0,0 +1,8 @@ +Copyright © 2002 Lucent Technologies Inc. +All Rights Reserved + +This software was originally developed for Plan 9. +It is provided under the terms of the Lucent Public License, Version 1.02. + +Trivial modifications have been made to make it compile for Inferno. + Vita Nuova Holdings Limited. diff --git a/os/boot/libflate/adler.c b/os/boot/libflate/adler.c new file mode 100644 index 00000000..0318f4a4 --- /dev/null +++ b/os/boot/libflate/adler.c @@ -0,0 +1,71 @@ +#include "lib9.h" +#include + +enum +{ + ADLERITERS = 5552, /* max iters before can overflow 32 bits */ + ADLERBASE = 65521 /* largest prime smaller than 65536 */ +}; + +ulong +adler32(ulong adler, void *vbuf, int n) +{ + ulong s1, s2; + uchar *buf, *ebuf; + int m; + + buf = vbuf; + s1 = adler & 0xffff; + s2 = (adler >> 16) & 0xffff; + for(; n >= 16; n -= m){ + m = n; + if(m > ADLERITERS) + m = ADLERITERS; + m &= ~15; + for(ebuf = buf + m; buf < ebuf; buf += 16){ + s1 += buf[0]; + s2 += s1; + s1 += buf[1]; + s2 += s1; + s1 += buf[2]; + s2 += s1; + s1 += buf[3]; + s2 += s1; + s1 += buf[4]; + s2 += s1; + s1 += buf[5]; + s2 += s1; + s1 += buf[6]; + s2 += s1; + s1 += buf[7]; + s2 += s1; + s1 += buf[8]; + s2 += s1; + s1 += buf[9]; + s2 += s1; + s1 += buf[10]; + s2 += s1; + s1 += buf[11]; + s2 += s1; + s1 += buf[12]; + s2 += s1; + s1 += buf[13]; + s2 += s1; + s1 += buf[14]; + s2 += s1; + s1 += buf[15]; + s2 += s1; + } + s1 %= ADLERBASE; + s2 %= ADLERBASE; + } + if(n){ + for(ebuf = buf + n; buf < ebuf; buf++){ + s1 += buf[0]; + s2 += s1; + } + s1 %= ADLERBASE; + s2 %= ADLERBASE; + } + return (s2 << 16) + s1; +} diff --git a/os/boot/libflate/crc.c b/os/boot/libflate/crc.c new file mode 100644 index 00000000..76709308 --- /dev/null +++ b/os/boot/libflate/crc.c @@ -0,0 +1,39 @@ +#include "lib9.h" +#include + +ulong* +mkcrctab(ulong poly) +{ + ulong *crctab; + ulong crc; + int i, j; + + crctab = malloc(256 * sizeof(ulong)); + if(crctab == nil) + return nil; + + for(i = 0; i < 256; i++){ + crc = i; + for(j = 0; j < 8; j++){ + if(crc & 1) + crc = (crc >> 1) ^ poly; + else + crc >>= 1; + } + crctab[i] = crc; + } + return crctab; +} + +ulong +blockcrc(ulong *crctab, ulong crc, void *vbuf, int n) +{ + uchar *buf, *ebuf; + + crc ^= 0xffffffff; + buf = vbuf; + ebuf = buf + n; + while(buf < ebuf) + crc = crctab[(crc & 0xff) ^ *buf++] ^ (crc >> 8); + return crc ^ 0xffffffff; +} diff --git a/os/boot/libflate/deflate.c b/os/boot/libflate/deflate.c new file mode 100644 index 00000000..e96c7ba0 --- /dev/null +++ b/os/boot/libflate/deflate.c @@ -0,0 +1,1358 @@ +#include "lib9.h" +#include + +typedef struct Chain Chain; +typedef struct Chains Chains; +typedef struct Dyncode Dyncode; +typedef struct Huff Huff; +typedef struct LZblock LZblock; +typedef struct LZstate LZstate; + +enum +{ + /* + * deflate format paramaters + */ + DeflateUnc = 0, /* uncompressed block */ + DeflateFix = 1, /* fixed huffman codes */ + DeflateDyn = 2, /* dynamic huffman codes */ + + DeflateEob = 256, /* end of block code in lit/len book */ + DeflateMaxBlock = 64*1024-1, /* maximum size of uncompressed block */ + + DeflateMaxExp = 10, /* maximum expansion for a block */ + + LenStart = 257, /* start of length codes in litlen */ + Nlitlen = 288, /* number of litlen codes */ + Noff = 30, /* number of offset codes */ + Nclen = 19, /* number of codelen codes */ + + MaxOff = 32*1024, + MinMatch = 3, /* shortest match possible */ + MaxMatch = 258, /* longest match possible */ + + /* + * huffman code paramaters + */ + MaxLeaf = Nlitlen, + MaxHuffBits = 16, /* max bits in a huffman code */ + ChainMem = 2 * (MaxHuffBits - 1) * MaxHuffBits, + + /* + * coding of the lz parse + */ + LenFlag = 1 << 3, + LenShift = 4, /* leaves enough space for MinMatchMaxOff */ + MaxLitRun = LenFlag - 1, + + /* + * internal lz paramaters + */ + DeflateOut = 4096, /* output buffer size */ + BlockSize = 8192, /* attempted input read quanta */ + DeflateBlock = DeflateMaxBlock & ~(BlockSize - 1), + MinMatchMaxOff = 4096, /* max profitable offset for small match; + * assumes 8 bits for len, 5+10 for offset + * DONT CHANGE WITHOUT CHANGING LZPARSE CONSTANTS + */ + HistSlop = 512, /* must be at lead MaxMatch */ + HistBlock = 64*1024, + HistSize = HistBlock + HistSlop, + + HashLog = 13, + HashSize = 1<> (24 - HashLog)) +*/ +#define hashit(c) ((((ulong)(c) & 0xffffff) * 0x6b43a9b5) >> (32 - HashLog)) + +/* + * lempel-ziv style compression state + */ +struct LZstate +{ + uchar hist[HistSize]; + ulong pos; /* current location in history buffer */ + ulong avail; /* data available after pos */ + int eof; + ushort hash[HashSize]; /* hash chains */ + ushort nexts[MaxOff]; + int now; /* pos in hash chains */ + int dot; /* dawn of time in history */ + int prevlen; /* lazy matching state */ + int prevoff; + int maxcheck; /* compressor tuning */ + + uchar obuf[DeflateOut]; + uchar *out; /* current position in the output buffer */ + uchar *eout; + ulong bits; /* bit shift register */ + int nbits; + int rbad; /* got an error reading the buffer */ + int wbad; /* got an error writing the buffer */ + int (*w)(void*, void*, int); + void *wr; + + ulong totr; /* total input size */ + ulong totw; /* total output size */ + int debug; +}; + +struct LZblock +{ + ushort parse[DeflateMaxBlock / 2 + 1]; + int lastv; /* value being constucted for parse */ + ulong litlencount[Nlitlen]; + ulong offcount[Noff]; + ushort *eparse; /* limit for parse table */ + int bytes; /* consumed from the input */ + int excost; /* cost of encoding extra len & off bits */ +}; + +/* + * huffman code table + */ +struct Huff +{ + short bits; /* length of the code */ + ushort encode; /* the code */ +}; + +/* + * encoding of dynamic huffman trees + */ +struct Dyncode +{ + int nlit; + int noff; + int nclen; + int ncode; + Huff codetab[Nclen]; + uchar codes[Nlitlen+Noff]; + uchar codeaux[Nlitlen+Noff]; +}; + +static int deflateb(LZstate *lz, LZblock *lzb, void *rr, int (*r)(void*, void*, int)); +static int lzcomp(LZstate*, LZblock*, uchar*, ushort*, int finish); +static void wrblock(LZstate*, int, ushort*, ushort*, Huff*, Huff*); +static int bitcost(Huff*, ulong*, int); +static int huffcodes(Dyncode*, Huff*, Huff*); +static void wrdyncode(LZstate*, Dyncode*); +static void lzput(LZstate*, ulong bits, int nbits); +static void lzflushbits(LZstate*); +static void lzflush(LZstate *lz); +static void lzwrite(LZstate *lz, void *buf, int n); + +static int hufftabinit(Huff*, int, ulong*, int); +static int mkgzprecode(Huff*, ulong *, int, int); + +static int mkprecode(Huff*, ulong *, int, int, ulong*); +static void nextchain(Chains*, int); +static void leafsort(ulong*, ushort*, int, int); + +/* conversion from len to code word */ +static int lencode[MaxMatch]; + +/* + * conversion from off to code word + * off <= MaxOffCode ? offcode[off] : bigoffcode[off >> 7] +*/ +static int offcode[MaxOffCode]; +static int bigoffcode[256]; + +/* litlen code words LenStart-285 extra bits */ +static int litlenbase[Nlitlen-LenStart]; +static int litlenextra[Nlitlen-LenStart] = +{ +/* 257 */ 0, 0, 0, +/* 260 */ 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, +/* 270 */ 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, +/* 280 */ 4, 5, 5, 5, 5, 0, 0, 0 +}; + +/* offset code word extra bits */ +static int offbase[Noff]; +static int offextra[] = +{ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, + 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, + 0, 0, +}; + +/* order code lengths */ +static int clenorder[Nclen] = +{ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 +}; + +/* static huffman tables */ +static Huff litlentab[Nlitlen]; +static Huff offtab[Noff]; +static Huff hofftab[Noff]; + +/* bit reversal for brain dead endian swap in huffman codes */ +static uchar revtab[256]; +static ulong nlits; +static ulong nmatches; + +int +deflateinit(void) +{ + ulong bitcount[MaxHuffBits]; + int i, j, ci, n; + + /* byte reverse table */ + for(i=0; i<256; i++) + for(j=0; j<8; j++) + if(i & (1<> j; + + /* static Litlen bit lengths */ + for(i=0; i<144; i++) + litlentab[i].bits = 8; + for(i=144; i<256; i++) + litlentab[i].bits = 9; + for(i=256; i<280; i++) + litlentab[i].bits = 7; + for(i=280; i> 7; + for(; i < 30; i++){ + n = ci + (1 << (offextra[i] - 7)); + offbase[i] = ci << 7; + for(; ci < n; ci++) + bigoffcode[ci] = i; + } + return FlateOk; +} + +static void +deflatereset(LZstate *lz, int level, int debug) +{ + memset(lz->nexts, 0, sizeof lz->nexts); + memset(lz->hash, 0, sizeof lz->hash); + lz->totr = 0; + lz->totw = 0; + lz->pos = 0; + lz->avail = 0; + lz->out = lz->obuf; + lz->eout = &lz->obuf[DeflateOut]; + lz->prevlen = MinMatch - 1; + lz->prevoff = 0; + lz->now = MaxOff + 1; + lz->dot = lz->now; + lz->bits = 0; + lz->nbits = 0; + lz->maxcheck = (1 << level); + lz->maxcheck -= lz->maxcheck >> 2; + if(lz->maxcheck < 2) + lz->maxcheck = 2; + else if(lz->maxcheck > 1024) + lz->maxcheck = 1024; + + lz->debug = debug; +} + +int +deflate(void *wr, int (*w)(void*, void*, int), void *rr, int (*r)(void*, void*, int), int level, int debug) +{ + LZstate *lz; + LZblock *lzb; + int ok; + + lz = malloc(sizeof *lz + sizeof *lzb); + if(lz == nil) + return FlateNoMem; + lzb = (LZblock*)&lz[1]; + + deflatereset(lz, level, debug); + lz->w = w; + lz->wr = wr; + lz->wbad = 0; + lz->rbad = 0; + lz->eof = 0; + ok = FlateOk; + while(!lz->eof || lz->avail){ + ok = deflateb(lz, lzb, rr, r); + if(ok != FlateOk) + break; + } + if(ok == FlateOk && lz->rbad) + ok = FlateInputFail; + if(ok == FlateOk && lz->wbad) + ok = FlateOutputFail; + free(lz); + return ok; +} + +static int +deflateb(LZstate *lz, LZblock *lzb, void *rr, int (*r)(void*, void*, int)) +{ + Dyncode dyncode, hdyncode; + Huff dlitlentab[Nlitlen], dofftab[Noff], hlitlentab[Nlitlen]; + ulong litcount[Nlitlen]; + long nunc, ndyn, nfix, nhuff; + uchar *slop, *hslop; + ulong ep; + int i, n, m, mm, nslop; + + memset(lzb->litlencount, 0, sizeof lzb->litlencount); + memset(lzb->offcount, 0, sizeof lzb->offcount); + lzb->litlencount[DeflateEob]++; + + lzb->bytes = 0; + lzb->eparse = lzb->parse; + lzb->lastv = 0; + lzb->excost = 0; + + slop = &lz->hist[lz->pos]; + n = lz->avail; + while(n < DeflateBlock && (!lz->eof || lz->avail)){ + /* + * fill the buffer as much as possible, + * while leaving room for MaxOff history behind lz->pos, + * and not reading more than we can handle. + * + * make sure we read at least HistSlop bytes. + */ + if(!lz->eof){ + ep = lz->pos + lz->avail; + if(ep >= HistBlock) + ep -= HistBlock; + m = HistBlock - MaxOff - lz->avail; + if(m > HistBlock - n) + m = HistBlock - n; + if(m > (HistBlock + HistSlop) - ep) + m = (HistBlock + HistSlop) - ep; + if(m & ~(BlockSize - 1)) + m &= ~(BlockSize - 1); + + /* + * be nice to the caller: stop reads that are too small. + * can only get here when we've already filled the buffer some + */ + if(m < HistSlop){ + if(!m || !lzb->bytes) + return FlateInternal; + break; + } + + mm = (*r)(rr, &lz->hist[ep], m); + if(mm > 0){ + /* + * wrap data to end if we're read it from the beginning + * this way, we don't have to wrap searches. + * + * wrap reads past the end to the beginning. + * this way, we can guarantee minimum size reads. + */ + if(ep < HistSlop) + memmove(&lz->hist[ep + HistBlock], &lz->hist[ep], HistSlop - ep); + else if(ep + mm > HistBlock) + memmove(&lz->hist[0], &lz->hist[HistBlock], ep + mm - HistBlock); + + lz->totr += mm; + n += mm; + lz->avail += mm; + }else{ + if(mm < 0) + lz->rbad = 1; + lz->eof = 1; + } + } + ep = lz->pos + lz->avail; + if(ep > HistSize) + ep = HistSize; + if(lzb->bytes + ep - lz->pos > DeflateMaxBlock) + ep = lz->pos + DeflateMaxBlock - lzb->bytes; + m = lzcomp(lz, lzb, &lz->hist[ep], lzb->eparse, lz->eof); + lzb->bytes += m; + lz->pos = (lz->pos + m) & (HistBlock - 1); + lz->avail -= m; + } + if(lzb->lastv) + *lzb->eparse++ = lzb->lastv; + if(lzb->eparse > lzb->parse + nelem(lzb->parse)) + return FlateInternal; + nunc = lzb->bytes; + + if(!mkgzprecode(dlitlentab, lzb->litlencount, Nlitlen, MaxHuffBits) + || !mkgzprecode(dofftab, lzb->offcount, Noff, MaxHuffBits)) + return FlateInternal; + + ndyn = huffcodes(&dyncode, dlitlentab, dofftab); + if(ndyn < 0) + return FlateInternal; + ndyn += bitcost(dlitlentab, lzb->litlencount, Nlitlen) + + bitcost(dofftab, lzb->offcount, Noff) + + lzb->excost; + + memset(litcount, 0, sizeof litcount); + + nslop = nunc; + if(nslop > &lz->hist[HistSize] - slop) + nslop = &lz->hist[HistSize] - slop; + + for(i = 0; i < nslop; i++) + litcount[slop[i]]++; + hslop = &lz->hist[HistSlop - nslop]; + for(; i < nunc; i++) + litcount[hslop[i]]++; + litcount[DeflateEob]++; + + if(!mkgzprecode(hlitlentab, litcount, Nlitlen, MaxHuffBits)) + return FlateInternal; + nhuff = huffcodes(&hdyncode, hlitlentab, hofftab); + if(nhuff < 0) + return FlateInternal; + nhuff += bitcost(hlitlentab, litcount, Nlitlen); + + nfix = bitcost(litlentab, lzb->litlencount, Nlitlen) + + bitcost(offtab, lzb->offcount, Noff) + + lzb->excost; + + lzput(lz, lz->eof && !lz->avail, 1); + + if(lz->debug){ + fprint(2, "block: bytes=%lud entries=%ld extra bits=%d\n\tuncompressed=%lud fixed=%lud dynamic=%lud huffman=%lud\n", + nunc, lzb->eparse - lzb->parse, lzb->excost, (nunc + 4) * 8, nfix, ndyn, nhuff); + fprint(2, "\tnlit=%lud matches=%lud eof=%d\n", nlits, nmatches, lz->eof && !lz->avail); + } + + if((nunc + 4) * 8 < ndyn && (nunc + 4) * 8 < nfix && (nunc + 4) * 8 < nhuff){ + lzput(lz, DeflateUnc, 2); + lzflushbits(lz); + + lzput(lz, nunc & 0xff, 8); + lzput(lz, (nunc >> 8) & 0xff, 8); + lzput(lz, ~nunc & 0xff, 8); + lzput(lz, (~nunc >> 8) & 0xff, 8); + lzflush(lz); + + lzwrite(lz, slop, nslop); + lzwrite(lz, &lz->hist[HistSlop], nunc - nslop); + }else if(ndyn < nfix && ndyn < nhuff){ + lzput(lz, DeflateDyn, 2); + + wrdyncode(lz, &dyncode); + wrblock(lz, slop - lz->hist, lzb->parse, lzb->eparse, dlitlentab, dofftab); + lzput(lz, dlitlentab[DeflateEob].encode, dlitlentab[DeflateEob].bits); + }else if(nhuff < nfix){ + lzput(lz, DeflateDyn, 2); + + wrdyncode(lz, &hdyncode); + + m = 0; + for(i = nunc; i > MaxLitRun; i -= MaxLitRun) + lzb->parse[m++] = MaxLitRun; + lzb->parse[m++] = i; + + wrblock(lz, slop - lz->hist, lzb->parse, lzb->parse + m, hlitlentab, hofftab); + lzput(lz, hlitlentab[DeflateEob].encode, hlitlentab[DeflateEob].bits); + }else{ + lzput(lz, DeflateFix, 2); + + wrblock(lz, slop - lz->hist, lzb->parse, lzb->eparse, litlentab, offtab); + lzput(lz, litlentab[DeflateEob].encode, litlentab[DeflateEob].bits); + } + + if(lz->eof && !lz->avail){ + lzflushbits(lz); + lzflush(lz); + } + return FlateOk; +} + +static void +lzwrite(LZstate *lz, void *buf, int n) +{ + int nw; + + if(n && lz->w){ + nw = (*lz->w)(lz->wr, buf, n); + if(nw != n){ + lz->w = nil; + lz->wbad = 1; + }else + lz->totw += n; + } +} + +static void +lzflush(LZstate *lz) +{ + lzwrite(lz, lz->obuf, lz->out - lz->obuf); + lz->out = lz->obuf; +} + +static void +lzput(LZstate *lz, ulong bits, int nbits) +{ + bits = (bits << lz->nbits) | lz->bits; + for(nbits += lz->nbits; nbits >= 8; nbits -= 8){ + *lz->out++ = bits; + if(lz->out == lz->eout) + lzflush(lz); + bits >>= 8; + } + lz->bits = bits; + lz->nbits = nbits; +} + +static void +lzflushbits(LZstate *lz) +{ + if(lz->nbits) + lzput(lz, 0, 8 - (lz->nbits & 7)); +} + +/* + * write out a block of n samples, + * given lz encoding and counts for huffman tables + */ +static void +wrblock(LZstate *out, int litoff, ushort *soff, ushort *eoff, Huff *litlentab, Huff *offtab) +{ + ushort *off; + int i, run, offset, lit, len, c; + + if(out->debug > 2){ + for(off = soff; off < eoff; ){ + offset = *off++; + run = offset & MaxLitRun; + if(run){ + for(i = 0; i < run; i++){ + lit = out->hist[litoff & (HistBlock - 1)]; + litoff++; + fprint(2, "\tlit %.2ux %c\n", lit, lit); + } + if(!(offset & LenFlag)) + continue; + len = offset >> LenShift; + offset = *off++; + }else if(offset & LenFlag){ + len = offset >> LenShift; + offset = *off++; + }else{ + len = 0; + offset >>= LenShift; + } + litoff += len + MinMatch; + fprint(2, "\t<%d, %d>\n", offset + 1, len + MinMatch); + } + } + + for(off = soff; off < eoff; ){ + offset = *off++; + run = offset & MaxLitRun; + if(run){ + for(i = 0; i < run; i++){ + lit = out->hist[litoff & (HistBlock - 1)]; + litoff++; + lzput(out, litlentab[lit].encode, litlentab[lit].bits); + } + if(!(offset & LenFlag)) + continue; + len = offset >> LenShift; + offset = *off++; + }else if(offset & LenFlag){ + len = offset >> LenShift; + offset = *off++; + }else{ + len = 0; + offset >>= LenShift; + } + litoff += len + MinMatch; + c = lencode[len]; + lzput(out, litlentab[c].encode, litlentab[c].bits); + c -= LenStart; + if(litlenextra[c]) + lzput(out, len - litlenbase[c], litlenextra[c]); + + if(offset < MaxOffCode) + c = offcode[offset]; + else + c = bigoffcode[offset >> 7]; + lzput(out, offtab[c].encode, offtab[c].bits); + if(offextra[c]) + lzput(out, offset - offbase[c], offextra[c]); + } +} + +/* + * look for the longest, closest string which matches + * the next prefix. the clever part here is looking for + * a string 1 longer than the previous best match. + * + * follows the recommendation of limiting number of chains + * which are checked. this appears to be the best heuristic. + */ +static int +lzmatch(int now, int then, uchar *p, uchar *es, ushort *nexts, uchar *hist, int runlen, int check, int *m) +{ + uchar *s, *t; + int ml, off, last; + + ml = check; + if(runlen >= 8) + check >>= 2; + *m = 0; + if(p + runlen >= es) + return runlen; + last = 0; + for(; check-- > 0; then = nexts[then & (MaxOff-1)]){ + off = (ushort)(now - then); + if(off <= last || off > MaxOff) + break; + s = p + runlen; + t = hist + (((p - hist) - off) & (HistBlock-1)); + t += runlen; + for(; s >= p; s--){ + if(*s != *t) + goto matchloop; + t--; + } + + /* + * we have a new best match. + * extend it to it's maximum length + */ + t += runlen + 2; + s += runlen + 2; + for(; s < es; s++){ + if(*s != *t) + break; + t++; + } + runlen = s - p; + *m = off - 1; + if(s == es || runlen > ml) + break; +matchloop:; + last = off; + } + return runlen; +} + +static int +lzcomp(LZstate *lz, LZblock *lzb, uchar *ep, ushort *parse, int finish) +{ + ulong cont, excost, *litlencount, *offcount; + uchar *p, *q, *s, *es; + ushort *nexts, *hash; + int v, i, h, runlen, n, now, then, m, prevlen, prevoff, maxdefer; + + litlencount = lzb->litlencount; + offcount = lzb->offcount; + nexts = lz->nexts; + hash = lz->hash; + now = lz->now; + + p = &lz->hist[lz->pos]; + if(lz->prevlen != MinMatch - 1) + p++; + + /* + * hash in the links for any hanging link positions, + * and calculate the hash for the current position. + */ + n = MinMatch; + if(n > ep - p) + n = ep - p; + cont = 0; + for(i = 0; i < n - 1; i++){ + m = now - ((MinMatch-1) - i); + if(m < lz->dot) + continue; + s = lz->hist + (((p - lz->hist) - (now - m)) & (HistBlock-1)); + + cont = (s[0] << 16) | (s[1] << 8) | s[2]; + h = hashit(cont); + prevoff = 0; + for(then = hash[h]; ; then = nexts[then & (MaxOff-1)]){ + v = (ushort)(now - then); + if(v <= prevoff || v >= (MinMatch-1) - i) + break; + prevoff = v; + } + if(then == (ushort)m) + continue; + nexts[m & (MaxOff-1)] = hash[h]; + hash[h] = m; + } + for(i = 0; i < n; i++) + cont = (cont << 8) | p[i]; + + /* + * now must point to the index in the nexts array + * corresponding to p's position in the history + */ + prevlen = lz->prevlen; + prevoff = lz->prevoff; + maxdefer = lz->maxcheck >> 2; + excost = 0; + v = lzb->lastv; + for(;;){ + es = p + MaxMatch; + if(es > ep){ + if(!finish || p >= ep) + break; + es = ep; + } + + h = hashit(cont); + runlen = lzmatch(now, hash[h], p, es, nexts, lz->hist, prevlen, lz->maxcheck, &m); + + /* + * back out of small matches too far in the past + */ + if(runlen == MinMatch && m >= MinMatchMaxOff){ + runlen = MinMatch - 1; + m = 0; + } + + /* + * record the encoding and increment counts for huffman trees + * if we get a match, defer selecting it until we check for + * a longer match at the next position. + */ + if(prevlen >= runlen && prevlen != MinMatch - 1){ + /* + * old match at least as good; use that one + */ + n = prevlen - MinMatch; + if(v || n){ + *parse++ = v | LenFlag | (n << LenShift); + *parse++ = prevoff; + }else + *parse++ = prevoff << LenShift; + v = 0; + + n = lencode[n]; + litlencount[n]++; + excost += litlenextra[n - LenStart]; + + if(prevoff < MaxOffCode) + n = offcode[prevoff]; + else + n = bigoffcode[prevoff >> 7]; + offcount[n]++; + excost += offextra[n]; + + runlen = prevlen - 1; + prevlen = MinMatch - 1; + nmatches++; + }else if(runlen == MinMatch - 1){ + /* + * no match; just put out the literal + */ + if(++v == MaxLitRun){ + *parse++ = v; + v = 0; + } + litlencount[*p]++; + nlits++; + runlen = 1; + }else{ + if(prevlen != MinMatch - 1){ + /* + * longer match now. output previous literal, + * update current match, and try again + */ + if(++v == MaxLitRun){ + *parse++ = v; + v = 0; + } + litlencount[p[-1]]++; + nlits++; + } + + prevoff = m; + + if(runlen < maxdefer){ + prevlen = runlen; + runlen = 1; + }else{ + n = runlen - MinMatch; + if(v || n){ + *parse++ = v | LenFlag | (n << LenShift); + *parse++ = prevoff; + }else + *parse++ = prevoff << LenShift; + v = 0; + + n = lencode[n]; + litlencount[n]++; + excost += litlenextra[n - LenStart]; + + if(prevoff < MaxOffCode) + n = offcode[prevoff]; + else + n = bigoffcode[prevoff >> 7]; + offcount[n]++; + excost += offextra[n]; + + prevlen = MinMatch - 1; + nmatches++; + } + } + + /* + * update the hash for the newly matched data + * this is constructed so the link for the old + * match in this position must be at the end of a chain, + * and will expire when this match is added, ie it will + * never be examined by the match loop. + * add to the hash chain only if we have the real hash data. + */ + for(q = p + runlen; p != q; p++){ + if(p + MinMatch <= ep){ + h = hashit(cont); + nexts[now & (MaxOff-1)] = hash[h]; + hash[h] = now; + if(p + MinMatch < ep) + cont = (cont << 8) | p[MinMatch]; + } + now++; + } + } + + /* + * we can just store away the lazy state and + * pick it up next time. the last block will have finish set + * so we won't have any pending matches + * however, we need to correct for how much we've encoded + */ + if(prevlen != MinMatch - 1) + p--; + + lzb->excost += excost; + lzb->eparse = parse; + lzb->lastv = v; + + lz->now = now; + lz->prevlen = prevlen; + lz->prevoff = prevoff; + + return p - &lz->hist[lz->pos]; +} + +/* + * make up the dynamic code tables, and return the number of bits + * needed to transmit them. + */ +static int +huffcodes(Dyncode *dc, Huff *littab, Huff *offtab) +{ + Huff *codetab; + uchar *codes, *codeaux; + ulong codecount[Nclen], excost; + int i, n, m, v, c, nlit, noff, ncode, nclen; + + codetab = dc->codetab; + codes = dc->codes; + codeaux = dc->codeaux; + + /* + * trim the sizes of the tables + */ + for(nlit = Nlitlen; nlit > 257 && littab[nlit-1].bits == 0; nlit--) + ; + for(noff = Noff; noff > 1 && offtab[noff-1].bits == 0; noff--) + ; + + /* + * make the code-length code + */ + for(i = 0; i < nlit; i++) + codes[i] = littab[i].bits; + for(i = 0; i < noff; i++) + codes[i + nlit] = offtab[i].bits; + + /* + * run-length compress the code-length code + */ + excost = 0; + c = 0; + ncode = nlit+noff; + for(i = 0; i < ncode; ){ + n = i + 1; + v = codes[i]; + while(n < ncode && v == codes[n]) + n++; + n -= i; + i += n; + if(v == 0){ + while(n >= 11){ + m = n; + if(m > 138) + m = 138; + codes[c] = 18; + codeaux[c++] = m - 11; + n -= m; + excost += 7; + } + if(n >= 3){ + codes[c] = 17; + codeaux[c++] = n - 3; + n = 0; + excost += 3; + } + } + while(n--){ + codes[c++] = v; + while(n >= 3){ + m = n; + if(m > 6) + m = 6; + codes[c] = 16; + codeaux[c++] = m - 3; + n -= m; + excost += 3; + } + } + } + + memset(codecount, 0, sizeof codecount); + for(i = 0; i < c; i++) + codecount[codes[i]]++; + if(!mkgzprecode(codetab, codecount, Nclen, 8)) + return -1; + + for(nclen = Nclen; nclen > 4 && codetab[clenorder[nclen-1]].bits == 0; nclen--) + ; + + dc->nlit = nlit; + dc->noff = noff; + dc->nclen = nclen; + dc->ncode = c; + + return 5 + 5 + 4 + nclen * 3 + bitcost(codetab, codecount, Nclen) + excost; +} + +static void +wrdyncode(LZstate *out, Dyncode *dc) +{ + Huff *codetab; + uchar *codes, *codeaux; + int i, v, c; + + /* + * write out header, then code length code lengths, + * and code lengths + */ + lzput(out, dc->nlit-257, 5); + lzput(out, dc->noff-1, 5); + lzput(out, dc->nclen-4, 4); + + codetab = dc->codetab; + for(i = 0; i < dc->nclen; i++) + lzput(out, codetab[clenorder[i]].bits, 3); + + codes = dc->codes; + codeaux = dc->codeaux; + c = dc->ncode; + for(i = 0; i < c; i++){ + v = codes[i]; + lzput(out, codetab[v].encode, codetab[v].bits); + if(v >= 16){ + if(v == 16) + lzput(out, codeaux[i], 2); + else if(v == 17) + lzput(out, codeaux[i], 3); + else /* v == 18 */ + lzput(out, codeaux[i], 7); + } + } +} + +static int +bitcost(Huff *tab, ulong *count, int n) +{ + ulong tot; + int i; + + tot = 0; + for(i = 0; i < n; i++) + tot += count[i] * tab[i].bits; + return tot; +} + +static int +mkgzprecode(Huff *tab, ulong *count, int n, int maxbits) +{ + ulong bitcount[MaxHuffBits]; + int i, nbits; + + nbits = mkprecode(tab, count, n, maxbits, bitcount); + for(i = 0; i < n; i++){ + if(tab[i].bits == -1) + tab[i].bits = 0; + else if(tab[i].bits == 0){ + if(nbits != 0 || bitcount[0] != 1) + return 0; + bitcount[1] = 1; + bitcount[0] = 0; + nbits = 1; + tab[i].bits = 1; + } + } + if(bitcount[0] != 0) + return 0; + return hufftabinit(tab, n, bitcount, nbits); +} + +static int +hufftabinit(Huff *tab, int n, ulong *bitcount, int nbits) +{ + ulong code, nc[MaxHuffBits]; + int i, bits; + + code = 0; + for(bits = 1; bits <= nbits; bits++){ + code = (code + bitcount[bits-1]) << 1; + nc[bits] = code; + } + + for(i = 0; i < n; i++){ + bits = tab[i].bits; + if(bits){ + code = nc[bits]++ << (16 - bits); + if(code & ~0xffff) + return 0; + tab[i].encode = revtab[code >> 8] | (revtab[code & 0xff] << 8); + } + } + return 1; +} + + +/* + * this should be in a library + */ +struct Chain +{ + ulong count; /* occurances of everything in the chain */ + ushort leaf; /* leaves to the left of chain, or leaf value */ + char col; /* ref count for collecting unused chains */ + char gen; /* need to generate chains for next lower level */ + Chain *up; /* Chain up in the lists */ +}; + +struct Chains +{ + Chain *lists[(MaxHuffBits - 1) * 2]; + ulong leafcount[MaxLeaf]; /* sorted list of leaf counts */ + ushort leafmap[MaxLeaf]; /* map to actual leaf number */ + int nleaf; /* number of leaves */ + Chain chains[ChainMem]; + Chain *echains; + Chain *free; + char col; + int nlists; +}; + +/* + * fast, low space overhead algorithm for max depth huffman type codes + * + * J. Katajainen, A. Moffat and A. Turpin, "A fast and space-economical + * algorithm for length-limited coding," Proc. Intl. Symp. on Algorithms + * and Computation, Cairns, Australia, Dec. 1995, Lecture Notes in Computer + * Science, Vol 1004, J. Staples, P. Eades, N. Katoh, and A. Moffat, eds., + * pp 12-21, Springer Verlag, New York, 1995. + */ +static int +mkprecode(Huff *tab, ulong *count, int n, int maxbits, ulong *bitcount) +{ + Chains cs; + Chain *c; + int i, m, em, bits; + + /* + * set up the sorted list of leaves + */ + m = 0; + for(i = 0; i < n; i++){ + tab[i].bits = -1; + tab[i].encode = 0; + if(count[i] != 0){ + cs.leafcount[m] = count[i]; + cs.leafmap[m] = i; + m++; + } + } + if(m < 2){ + if(m != 0){ + tab[cs.leafmap[0]].bits = 0; + bitcount[0] = 1; + }else + bitcount[0] = 0; + return 0; + } + cs.nleaf = m; + leafsort(cs.leafcount, cs.leafmap, 0, m); + + for(i = 0; i < m; i++) + cs.leafcount[i] = count[cs.leafmap[i]]; + + /* + * set up free list + */ + cs.free = &cs.chains[2]; + cs.echains = &cs.chains[ChainMem]; + cs.col = 1; + + /* + * initialize chains for each list + */ + c = &cs.chains[0]; + c->count = cs.leafcount[0]; + c->leaf = 1; + c->col = cs.col; + c->up = nil; + c->gen = 0; + cs.chains[1] = cs.chains[0]; + cs.chains[1].leaf = 2; + cs.chains[1].count = cs.leafcount[1]; + for(i = 0; i < maxbits-1; i++){ + cs.lists[i * 2] = &cs.chains[0]; + cs.lists[i * 2 + 1] = &cs.chains[1]; + } + + cs.nlists = 2 * (maxbits - 1); + m = 2 * m - 2; + for(i = 2; i < m; i++) + nextchain(&cs, cs.nlists - 2); + + bits = 0; + bitcount[0] = cs.nleaf; + for(c = cs.lists[cs.nlists - 1]; c != nil; c = c->up){ + m = c->leaf; + bitcount[bits++] -= m; + bitcount[bits] = m; + } + m = 0; + for(i = bits; i >= 0; i--) + for(em = m + bitcount[i]; m < em; m++) + tab[cs.leafmap[m]].bits = i; + + return bits; +} + +/* + * calculate the next chain on the list + * we can always toss out the old chain + */ +static void +nextchain(Chains *cs, int list) +{ + Chain *c, *oc; + int i, nleaf, sumc; + + oc = cs->lists[list + 1]; + cs->lists[list] = oc; + if(oc == nil) + return; + + /* + * make sure we have all chains needed to make sumc + * note it is possible to generate only one of these, + * use twice that value for sumc, and then generate + * the second if that preliminary sumc would be chosen. + * however, this appears to be slower on current tests + */ + if(oc->gen){ + nextchain(cs, list - 2); + nextchain(cs, list - 2); + oc->gen = 0; + } + + /* + * pick up the chain we're going to add; + * collect unused chains no free ones are left + */ + for(c = cs->free; ; c++){ + if(c >= cs->echains){ + cs->col++; + for(i = 0; i < cs->nlists; i++) + for(c = cs->lists[i]; c != nil; c = c->up) + c->col = cs->col; + c = cs->chains; + } + if(c->col != cs->col) + break; + } + + /* + * pick the cheapest of + * 1) the next package from the previous list + * 2) the next leaf + */ + nleaf = oc->leaf; + sumc = 0; + if(list > 0 && cs->lists[list-1] != nil) + sumc = cs->lists[list-2]->count + cs->lists[list-1]->count; + if(sumc != 0 && (nleaf >= cs->nleaf || cs->leafcount[nleaf] > sumc)){ + c->count = sumc; + c->leaf = oc->leaf; + c->up = cs->lists[list-1]; + c->gen = 1; + }else if(nleaf >= cs->nleaf){ + cs->lists[list + 1] = nil; + return; + }else{ + c->leaf = nleaf + 1; + c->count = cs->leafcount[nleaf]; + c->up = oc->up; + c->gen = 0; + } + cs->free = c + 1; + + cs->lists[list + 1] = c; + c->col = cs->col; +} + +static int +pivot(ulong *c, int a, int n) +{ + int j, pi, pj, pk; + + j = n/6; + pi = a + j; /* 1/6 */ + j += j; + pj = pi + j; /* 1/2 */ + pk = pj + j; /* 5/6 */ + if(c[pi] < c[pj]){ + if(c[pi] < c[pk]){ + if(c[pj] < c[pk]) + return pj; + return pk; + } + return pi; + } + if(c[pj] < c[pk]){ + if(c[pi] < c[pk]) + return pi; + return pk; + } + return pj; +} + +static void +leafsort(ulong *leafcount, ushort *leafmap, int a, int n) +{ + ulong t; + int j, pi, pj, pn; + + while(n > 1){ + if(n > 10){ + pi = pivot(leafcount, a, n); + }else + pi = a + (n>>1); + + t = leafcount[pi]; + leafcount[pi] = leafcount[a]; + leafcount[a] = t; + t = leafmap[pi]; + leafmap[pi] = leafmap[a]; + leafmap[a] = t; + pi = a; + pn = a + n; + pj = pn; + for(;;){ + do + pi++; + while(pi < pn && (leafcount[pi] < leafcount[a] || leafcount[pi] == leafcount[a] && leafmap[pi] > leafmap[a])); + do + pj--; + while(pj > a && (leafcount[pj] > leafcount[a] || leafcount[pj] == leafcount[a] && leafmap[pj] < leafmap[a])); + if(pj < pi) + break; + t = leafcount[pi]; + leafcount[pi] = leafcount[pj]; + leafcount[pj] = t; + t = leafmap[pi]; + leafmap[pi] = leafmap[pj]; + leafmap[pj] = t; + } + t = leafcount[a]; + leafcount[a] = leafcount[pj]; + leafcount[pj] = t; + t = leafmap[a]; + leafmap[a] = leafmap[pj]; + leafmap[pj] = t; + j = pj - a; + + n = n-j-1; + if(j >= n){ + leafsort(leafcount, leafmap, a, j); + a += j+1; + }else{ + leafsort(leafcount, leafmap, a + (j+1), n); + n = j; + } + } +} diff --git a/os/boot/libflate/deflateblock.c b/os/boot/libflate/deflateblock.c new file mode 100644 index 00000000..60af31db --- /dev/null +++ b/os/boot/libflate/deflateblock.c @@ -0,0 +1,55 @@ +#include "lib9.h" +#include + +typedef struct Block Block; + +struct Block +{ + uchar *pos; + uchar *limit; +}; + +static int +blread(void *vb, void *buf, int n) +{ + Block *b; + + b = vb; + if(n > b->limit - b->pos) + n = b->limit - b->pos; + memmove(buf, b->pos, n); + b->pos += n; + return n; +} + +static int +blwrite(void *vb, void *buf, int n) +{ + Block *b; + + b = vb; + + if(n > b->limit - b->pos) + n = b->limit - b->pos; + memmove(b->pos, buf, n); + b->pos += n; + return n; +} + +int +deflateblock(uchar *dst, int dsize, uchar *src, int ssize, int level, int debug) +{ + Block bd, bs; + int ok; + + bs.pos = src; + bs.limit = src + ssize; + + bd.pos = dst; + bd.limit = dst + dsize; + + ok = deflate(&bd, blwrite, &bs, blread, level, debug); + if(ok != FlateOk) + return ok; + return bd.pos - dst; +} diff --git a/os/boot/libflate/deflatezlib.c b/os/boot/libflate/deflatezlib.c new file mode 100644 index 00000000..9a7e051e --- /dev/null +++ b/os/boot/libflate/deflatezlib.c @@ -0,0 +1,59 @@ +#include "lib9.h" +#include +#include "zlib.h" + +typedef struct ZRead ZRead; + +struct ZRead +{ + ulong adler; + void *rr; + int (*r)(void*, void*, int); +}; + +static int +zlread(void *vzr, void *buf, int n) +{ + ZRead *zr; + + zr = vzr; + n = (*zr->r)(zr->rr, buf, n); + if(n <= 0) + return n; + zr->adler = adler32(zr->adler, buf, n); + return n; +} + +int +deflatezlib(void *wr, int (*w)(void*, void*, int), void *rr, int (*r)(void*, void*, int), int level, int debug) +{ + ZRead zr; + uchar buf[4]; + int ok; + + buf[0] = ZlibDeflate | ZlibWin32k; + + /* bogus zlib encoding of compression level */ + buf[1] = ((level > 2) + (level > 5) + (level > 8)) << 6; + + /* header check field */ + buf[1] |= 31 - ((buf[0] << 8) | buf[1]) % 31; + if((*w)(wr, buf, 2) != 2) + return FlateOutputFail; + + zr.rr = rr; + zr.r = r; + zr.adler = 1; + ok = deflate(wr, w, &zr, zlread, level, debug); + if(ok != FlateOk) + return ok; + + buf[0] = zr.adler >> 24; + buf[1] = zr.adler >> 16; + buf[2] = zr.adler >> 8; + buf[3] = zr.adler; + if((*w)(wr, buf, 4) != 4) + return FlateOutputFail; + + return FlateOk; +} diff --git a/os/boot/libflate/deflatezlibblock.c b/os/boot/libflate/deflatezlibblock.c new file mode 100644 index 00000000..696cddf2 --- /dev/null +++ b/os/boot/libflate/deflatezlibblock.c @@ -0,0 +1,33 @@ +#include "lib9.h" +#include +#include "zlib.h" + +int +deflatezlibblock(uchar *dst, int dsize, uchar *src, int ssize, int level, int debug) +{ + ulong adler; + int n; + + if(dsize < 6) + return FlateOutputFail; + + n = deflateblock(dst + 2, dsize - 6, src, ssize, level, debug); + if(n < 0) + return n; + + dst[0] = ZlibDeflate | ZlibWin32k; + + /* bogus zlib encoding of compression level */ + dst[1] = ((level > 2) + (level > 5) + (level > 8)) << 6; + + /* header check field */ + dst[1] |= 31 - ((dst[0] << 8) | dst[1]) % 31; + + adler = adler32(1, src, ssize); + dst[n + 2] = adler >> 24; + dst[n + 3] = adler >> 16; + dst[n + 4] = adler >> 8; + dst[n + 5] = adler; + + return n + 6; +} diff --git a/os/boot/libflate/flateerr.c b/os/boot/libflate/flateerr.c new file mode 100644 index 00000000..76763fb4 --- /dev/null +++ b/os/boot/libflate/flateerr.c @@ -0,0 +1,22 @@ +#include "lib9.h" +#include + +char * +flateerr(int err) +{ + switch(err){ + case FlateOk: + return "no error"; + case FlateNoMem: + return "out of memory"; + case FlateInputFail: + return "input error"; + case FlateOutputFail: + return "output error"; + case FlateCorrupted: + return "corrupted data"; + case FlateInternal: + return "internal error"; + } + return "unknown error"; +} diff --git a/os/boot/libflate/inflate.c b/os/boot/libflate/inflate.c new file mode 100644 index 00000000..8b105e24 --- /dev/null +++ b/os/boot/libflate/inflate.c @@ -0,0 +1,692 @@ +#include "lib9.h" +#include + +enum { + HistorySize= 32*1024, + BufSize= 4*1024, + MaxHuffBits= 17, /* maximum bits in a encoded code */ + Nlitlen= 288, /* number of litlen codes */ + Noff= 32, /* number of offset codes */ + Nclen= 19, /* number of codelen codes */ + LenShift= 10, /* code = len<> j; + + for(i=257,base=3; icp = his->his; + his->full = 0; + in.getr = getr; + in.get = get; + in.wr = wr; + in.w = w; + in.nbits = 0; + in.sreg = 0; + in.error = FlateOk; + + do { + if(!sregfill(&in, 3)) + goto bad; + final = in.sreg & 0x1; + type = (in.sreg>>1) & 0x3; + in.sreg >>= 3; + in.nbits -= 3; + switch(type) { + default: + in.error = FlateCorrupted; + goto bad; + case 0: + /* uncompressed */ + if(!uncblock(&in, his)) + goto bad; + break; + case 1: + /* fixed huffman */ + if(!fixedblock(&in, his)) + goto bad; + break; + case 2: + /* dynamic huffman */ + if(!dynamicblock(&in, his)) + goto bad; + break; + } + } while(!final); + + if(his->cp != his->his && (*w)(wr, his->his, his->cp - his->his) != his->cp - his->his) { + in.error = FlateOutputFail; + goto bad; + } + + if(!sregunget(&in)) + goto bad; + + free(his); + if(in.error != FlateOk) + return FlateInternal; + return FlateOk; + +bad: + free(his); + if(in.error == FlateOk) + return FlateInternal; + return in.error; +} + +static int +uncblock(Input *in, History *his) +{ + int len, nlen, c; + uchar *hs, *hp, *he; + + if(!sregunget(in)) + return 0; + len = (*in->get)(in->getr); + len |= (*in->get)(in->getr)<<8; + nlen = (*in->get)(in->getr); + nlen |= (*in->get)(in->getr)<<8; + if(len != (~nlen&0xffff)) { + in->error = FlateCorrupted; + return 0; + } + + hp = his->cp; + hs = his->his; + he = hs + HistorySize; + + while(len > 0) { + c = (*in->get)(in->getr); + if(c < 0) + return 0; + *hp++ = c; + if(hp == he) { + his->full = 1; + if((*in->w)(in->wr, hs, HistorySize) != HistorySize) { + in->error = FlateOutputFail; + return 0; + } + hp = hs; + } + len--; + } + + his->cp = hp; + + return 1; +} + +static int +fixedblock(Input *in, History *his) +{ + return decode(in, his, &litlentab, &offtab); +} + +static int +dynamicblock(Input *in, History *his) +{ + Huff *lentab, *offtab; + char *len; + int i, j, n, c, nlit, ndist, nclen, res, nb; + + if(!sregfill(in, 14)) + return 0; + nlit = (in->sreg&0x1f) + 257; + ndist = ((in->sreg>>5) & 0x1f) + 1; + nclen = ((in->sreg>>10) & 0xf) + 4; + in->sreg >>= 14; + in->nbits -= 14; + + if(nlit > Nlitlen || ndist > Noff || nlit < 257) { + in->error = FlateCorrupted; + return 0; + } + + /* huff table header */ + len = malloc(Nlitlen+Noff); + lentab = malloc(sizeof(Huff)); + offtab = malloc(sizeof(Huff)); + if(len == nil || lentab == nil || offtab == nil){ + in->error = FlateNoMem; + goto bad; + } + for(i=0; i < Nclen; i++) + len[i] = 0; + for(i=0; isreg & 0x7; + in->sreg >>= 3; + in->nbits -= 3; + } + + if(!hufftab(lentab, len, Nclen, ClenBits)){ + in->error = FlateCorrupted; + goto bad; + } + + n = nlit+ndist; + for(i=0; iminbits; + for(;;){ + if(in->nbitsflat[in->sreg & lentab->flatmask]; + nb = c & 0xff; + if(nb > in->nbits){ + if(nb != 0xff) + continue; + c = hdecsym(in, lentab, c); + if(c < 0) + goto bad; + }else{ + c >>= 8; + in->sreg >>= nb; + in->nbits -= nb; + } + break; + } + + if(c < 16) { + j = 1; + } else if(c == 16) { + if(in->nbits<2 && !sregfill(in, 2)) + goto bad; + j = (in->sreg&0x3)+3; + in->sreg >>= 2; + in->nbits -= 2; + if(i == 0) { + in->error = FlateCorrupted; + goto bad; + } + c = len[i-1]; + } else if(c == 17) { + if(in->nbits<3 && !sregfill(in, 3)) + goto bad; + j = (in->sreg&0x7)+3; + in->sreg >>= 3; + in->nbits -= 3; + c = 0; + } else if(c == 18) { + if(in->nbits<7 && !sregfill(in, 7)) + goto bad; + j = (in->sreg&0x7f)+11; + in->sreg >>= 7; + in->nbits -= 7; + c = 0; + } else { + in->error = FlateCorrupted; + goto bad; + } + + if(i+j > n) { + in->error = FlateCorrupted; + goto bad; + } + + while(j) { + len[i] = c; + i++; + j--; + } + } + + if(!hufftab(lentab, len, nlit, LitlenBits) + || !hufftab(offtab, &len[nlit], ndist, OffBits)){ + in->error = FlateCorrupted; + goto bad; + } + + res = decode(in, his, lentab, offtab); + + free(len); + free(lentab); + free(offtab); + + return res; + +bad: + free(len); + free(lentab); + free(offtab); + return 0; +} + +static int +decode(Input *in, History *his, Huff *litlentab, Huff *offtab) +{ + int len, off; + uchar *hs, *hp, *hq, *he; + int c; + int nb; + + hs = his->his; + he = hs + HistorySize; + hp = his->cp; + + for(;;) { + nb = litlentab->minbits; + for(;;){ + if(in->nbitsflat[in->sreg & litlentab->flatmask]; + nb = c & 0xff; + if(nb > in->nbits){ + if(nb != 0xff) + continue; + c = hdecsym(in, litlentab, c); + if(c < 0) + return 0; + }else{ + c >>= 8; + in->sreg >>= nb; + in->nbits -= nb; + } + break; + } + + if(c < 256) { + /* literal */ + *hp++ = c; + if(hp == he) { + his->full = 1; + if((*in->w)(in->wr, hs, HistorySize) != HistorySize) { + in->error = FlateOutputFail; + return 0; + } + hp = hs; + } + continue; + } + + if(c == 256) + break; + + if(c > 285) { + in->error = FlateCorrupted; + return 0; + } + + c -= 257; + nb = litlenextra[c]; + if(in->nbits < nb && !sregfill(in, nb)) + return 0; + len = litlenbase[c] + (in->sreg & ((1<sreg >>= nb; + in->nbits -= nb; + + /* get offset */ + nb = offtab->minbits; + for(;;){ + if(in->nbitsflat[in->sreg & offtab->flatmask]; + nb = c & 0xff; + if(nb > in->nbits){ + if(nb != 0xff) + continue; + c = hdecsym(in, offtab, c); + if(c < 0) + return 0; + }else{ + c >>= 8; + in->sreg >>= nb; + in->nbits -= nb; + } + break; + } + + if(c > 29) { + in->error = FlateCorrupted; + return 0; + } + + nb = offextra[c]; + if(in->nbits < nb && !sregfill(in, nb)) + return 0; + + off = offbase[c] + (in->sreg & ((1<sreg >>= nb; + in->nbits -= nb; + + hq = hp - off; + if(hq < hs) { + if(!his->full) { + in->error = FlateCorrupted; + return 0; + } + hq += HistorySize; + } + + /* slow but correct */ + while(len) { + *hp = *hq; + hq++; + hp++; + if(hq >= he) + hq = hs; + if(hp == he) { + his->full = 1; + if((*in->w)(in->wr, hs, HistorySize) != HistorySize) { + in->error = FlateOutputFail; + return 0; + } + hp = hs; + } + len--; + } + + } + + his->cp = hp; + + return 1; +} + +static int +revcode(int c, int b) +{ + /* shift encode up so it starts on bit 15 then reverse */ + c <<= (16-b); + c = revtab[c>>8] | (revtab[c&0xff]<<8); + return c; +} + +/* + * construct the huffman decoding arrays and a fast lookup table. + * the fast lookup is a table indexed by the next flatbits bits, + * which returns the symbol matched and the number of bits consumed, + * or the minimum number of bits needed and 0xff if more than flatbits + * bits are needed. + * + * flatbits can be longer than the smallest huffman code, + * because shorter codes are assigned smaller lexical prefixes. + * this means assuming zeros for the next few bits will give a + * conservative answer, in the sense that it will either give the + * correct answer, or return the minimum number of bits which + * are needed for an answer. + */ +static int +hufftab(Huff *h, char *hb, int maxleaf, int flatbits) +{ + ulong bitcount[MaxHuffBits]; + ulong c, fc, ec, mincode, code, nc[MaxHuffBits]; + int i, b, minbits, maxbits; + + for(i = 0; i < MaxHuffBits; i++) + bitcount[i] = 0; + maxbits = -1; + minbits = MaxHuffBits + 1; + for(i=0; i < maxleaf; i++){ + b = hb[i]; + if(b){ + bitcount[b]++; + if(b < minbits) + minbits = b; + if(b > maxbits) + maxbits = b; + } + } + + h->maxbits = maxbits; + if(maxbits <= 0){ + h->maxbits = 0; + h->minbits = 0; + h->flatmask = 0; + return 1; + } + code = 0; + c = 0; + for(b = 0; b <= maxbits; b++){ + h->last[b] = c; + c += bitcount[b]; + mincode = code << 1; + nc[b] = mincode; + code = mincode + bitcount[b]; + if(code > (1 << b)) + return 0; + h->maxcode[b] = code - 1; + h->last[b] += code - 1; + } + + if(flatbits > maxbits) + flatbits = maxbits; + h->flatmask = (1 << flatbits) - 1; + if(minbits > flatbits) + minbits = flatbits; + h->minbits = minbits; + + b = 1 << flatbits; + for(i = 0; i < b; i++) + h->flat[i] = ~0; + + /* + * initialize the flat table to include the minimum possible + * bit length for each code prefix + */ + for(b = maxbits; b > flatbits; b--){ + code = h->maxcode[b]; + if(code == -1) + break; + mincode = code + 1 - bitcount[b]; + mincode >>= b - flatbits; + code >>= b - flatbits; + for(; mincode <= code; mincode++) + h->flat[revcode(mincode, flatbits)] = (b << 8) | 0xff; + } + + for(i = 0; i < maxleaf; i++){ + b = hb[i]; + if(b <= 0) + continue; + c = nc[b]++; + if(b <= flatbits){ + code = (i << 8) | b; + ec = (c + 1) << (flatbits - b); + if(ec > (1<flat[revcode(fc, flatbits)] = code; + } + if(b > minbits){ + c = h->last[b] - c; + if(c >= maxleaf) + return 0; + h->decode[c] = i; + } + } + return 1; +} + +static int +hdecsym(Input *in, Huff *h, int nb) +{ + long c; + + if((nb & 0xff) == 0xff) + nb = nb >> 8; + else + nb = nb & 0xff; + for(; nb <= h->maxbits; nb++){ + if(in->nbitssreg&0xff]<<8; + c |= revtab[(in->sreg>>8)&0xff]; + c >>= (16-nb); + if(c <= h->maxcode[nb]){ + in->sreg >>= nb; + in->nbits -= nb; + return h->decode[h->last[nb] - c]; + } + } + in->error = FlateCorrupted; + return -1; +} + +static int +sregfill(Input *in, int n) +{ + int c; + + while(n > in->nbits) { + c = (*in->get)(in->getr); + if(c < 0){ + in->error = FlateInputFail; + return 0; + } + in->sreg |= c<nbits; + in->nbits += 8; + } + return 1; +} + +static int +sregunget(Input *in) +{ + if(in->nbits >= 8) { + in->error = FlateInternal; + return 0; + } + + /* throw other bits on the floor */ + in->nbits = 0; + in->sreg = 0; + return 1; +} diff --git a/os/boot/libflate/inflateblock.c b/os/boot/libflate/inflateblock.c new file mode 100644 index 00000000..864bdeca --- /dev/null +++ b/os/boot/libflate/inflateblock.c @@ -0,0 +1,53 @@ +#include "lib9.h" +#include + +typedef struct Block Block; + +struct Block +{ + uchar *pos; + uchar *limit; +}; + +static int +blgetc(void *vb) +{ + Block *b; + + b = vb; + if(b->pos >= b->limit) + return -1; + return *b->pos++; +} + +static int +blwrite(void *vb, void *buf, int n) +{ + Block *b; + + b = vb; + + if(n > b->limit - b->pos) + n = b->limit - b->pos; + memmove(b->pos, buf, n); + b->pos += n; + return n; +} + +int +inflateblock(uchar *dst, int dsize, uchar *src, int ssize) +{ + Block bd, bs; + int ok; + + bs.pos = src; + bs.limit = src + ssize; + + bd.pos = dst; + bd.limit = dst + dsize; + + ok = inflate(&bd, blwrite, &bs, blgetc); + if(ok != FlateOk) + return ok; + return bd.pos - dst; +} diff --git a/os/boot/libflate/inflatezlib.c b/os/boot/libflate/inflatezlib.c new file mode 100644 index 00000000..961a191e --- /dev/null +++ b/os/boot/libflate/inflatezlib.c @@ -0,0 +1,65 @@ +#include "lib9.h" +#include +#include "zlib.h" + +typedef struct ZWrite ZWrite; + +struct ZWrite +{ + ulong adler; + void *wr; + int (*w)(void*, void*, int); +}; + +static int +zlwrite(void *vzw, void *buf, int n) +{ + ZWrite *zw; + + zw = vzw; + zw->adler = adler32(zw->adler, buf, n); + n = (*zw->w)(zw->wr, buf, n); + if(n <= 0) + return n; + return n; +} + +int +inflatezlib(void *wr, int (*w)(void*, void*, int), void *getr, int (*get)(void*)) +{ + ZWrite zw; + ulong v; + int c, i; + + c = (*get)(getr); + if(c < 0) + return FlateInputFail; + i = (*get)(getr); + if(i < 0) + return FlateInputFail; + + if(((c << 8) | i) % 31) + return FlateCorrupted; + if((c & ZlibMeth) != ZlibDeflate + || (c & ZlibCInfo) > ZlibWin32k) + return FlateCorrupted; + + zw.wr = wr; + zw.w = w; + zw.adler = 1; + i = inflate(&zw, zlwrite, getr, get); + if(i != FlateOk) + return i; + + v = 0; + for(i = 0; i < 4; i++){ + c = (*get)(getr); + if(c < 0) + return FlateInputFail; + v = (v << 8) | c; + } + if(zw.adler != v) + return FlateCorrupted; + + return FlateOk; +} diff --git a/os/boot/libflate/inflatezlibblock.c b/os/boot/libflate/inflatezlibblock.c new file mode 100644 index 00000000..d149cf87 --- /dev/null +++ b/os/boot/libflate/inflatezlibblock.c @@ -0,0 +1,67 @@ +#include "lib9.h" +#include +#include "zlib.h" + +typedef struct Block Block; + +struct Block +{ + uchar *pos; + uchar *limit; +}; + +static int +blgetc(void *vb) +{ + Block *b; + + b = vb; + if(b->pos >= b->limit) + return -1; + return *b->pos++; +} + +static int +blwrite(void *vb, void *buf, int n) +{ + Block *b; + + b = vb; + + if(n > b->limit - b->pos) + n = b->limit - b->pos; + memmove(b->pos, buf, n); + b->pos += n; + return n; +} + +int +inflatezlibblock(uchar *dst, int dsize, uchar *src, int ssize) +{ + Block bd, bs; + int ok; + + if(ssize < 6) + return FlateInputFail; + + if(((src[0] << 8) | src[1]) % 31) + return FlateCorrupted; + if((src[0] & ZlibMeth) != ZlibDeflate + || (src[0] & ZlibCInfo) > ZlibWin32k) + return FlateCorrupted; + + bs.pos = src + 2; + bs.limit = src + ssize - 6; + + bd.pos = dst; + bd.limit = dst + dsize; + + ok = inflate(&bd, blwrite, &bs, blgetc); + if(ok != FlateOk) + return ok; + + if(adler32(1, dst, bs.pos - dst) != ((bs.pos[0] << 24) | (bs.pos[1] << 16) | (bs.pos[2] << 8) | bs.pos[3])) + return FlateCorrupted; + + return bd.pos - dst; +} diff --git a/os/boot/libflate/mkfile b/os/boot/libflate/mkfile new file mode 100644 index 00000000..ea2e8e78 --- /dev/null +++ b/os/boot/libflate/mkfile @@ -0,0 +1,21 @@ +<../../../mkconfig + +LIB=libflate.a +OFILES=\ + deflate.$O\ + deflatezlib.$O\ + deflateblock.$O\ + deflatezlibblock.$O\ + inflate.$O\ + inflatezlib.$O\ + inflateblock.$O\ + inflatezlibblock.$O\ + flateerr.$O\ + crc.$O\ + adler.$O\ + +HFILES=\ + $ROOT/include/flate.h\ + zlib.h\ + +<$ROOT/mkfiles/mksyslib-$SHELLTYPE diff --git a/os/boot/libflate/zlib.h b/os/boot/libflate/zlib.h new file mode 100644 index 00000000..9669d86a --- /dev/null +++ b/os/boot/libflate/zlib.h @@ -0,0 +1,11 @@ +/* + * zlib header fields + */ +enum +{ + ZlibMeth = 0x0f, /* mask of compression methods */ + ZlibDeflate = 0x08, + + ZlibCInfo = 0xf0, /* mask of compression aux. info */ + ZlibWin32k = 0x70, /* 32k history window */ +}; diff --git a/os/boot/mpc/NOTICE b/os/boot/mpc/NOTICE new file mode 100644 index 00000000..b0086fa7 --- /dev/null +++ b/os/boot/mpc/NOTICE @@ -0,0 +1,3 @@ +Inferno® Copyright © 1996-1999 Lucent Technologies Inc. All rights reserved. +PowerPC support Copyright © 1995-1997 C H Forsyth (forsyth@caldo.demon.co.uk). All rights reserved. +MPC8xx Inferno PowerPC port Copyright © 1998-2003 Vita Nuova Holdings Limited. All rights reserved. diff --git a/os/boot/mpc/alarm.c b/os/boot/mpc/alarm.c new file mode 100644 index 00000000..2aa4431a --- /dev/null +++ b/os/boot/mpc/alarm.c @@ -0,0 +1,123 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#define MAXALARM 10 + +Alarm alarmtab[MAXALARM]; + +/* + * Insert new into list after where + */ +void +insert(List **head, List *where, List *new) +{ + if(where == 0){ + new->next = *head; + *head = new; + }else{ + new->next = where->next; + where->next = new; + } + +} + +/* + * Delete old from list. where->next is known to be old. + */ +void +delete(List **head, List *where, List *old) +{ + if(where == 0){ + *head = old->next; + return; + } + where->next = old->next; +} + +Alarm* +newalarm(void) +{ + int i; + Alarm *a; + + for(i=0,a=alarmtab; i < nelem(alarmtab); i++,a++) + if(a->busy==0 && a->f==0){ + a->f = 0; + a->arg = 0; + a->busy = 1; + return a; + } + panic("newalarm"); + return 0; /* not reached */ +} + +Alarm* +alarm(int ms, void (*f)(Alarm*), void *arg) +{ + Alarm *a, *w, *pw; + ulong s; + + if(ms < 0) + ms = 0; + s = splhi(); + a = newalarm(); + a->dt = MS2TK(ms); + a->f = f; + a->arg = arg; + pw = 0; + for(w=m->alarm; w; pw=w, w=w->next){ + if(w->dt <= a->dt){ + a->dt -= w->dt; + continue; + } + w->dt -= a->dt; + break; + } + insert(&m->alarm, pw, a); + splx(s); + return a; +} + +void +cancel(Alarm *a) +{ + a->f = 0; +} + +void +alarminit(void) +{ +} + +#define NA 10 /* alarms per clock tick */ +void +checkalarms(void) +{ + int i, n, s; + Alarm *a; + void (*f)(Alarm*); + Alarm *alist[NA]; + + s = splhi(); + a = m->alarm; + if(a){ + for(n=0; a && a->dt<=0 && nalarm, 0, a); + a = m->alarm; + } + if(a) + a->dt--; + + for(i = 0; i < n; i++){ + f = alist[i]->f; /* avoid race with cancel */ + if(f) + (*f)(alist[i]); + alist[i]->busy = 0; + } + } + splx(s); +} diff --git a/os/boot/mpc/all.h b/os/boot/mpc/all.h new file mode 100644 index 00000000..0612bf04 --- /dev/null +++ b/os/boot/mpc/all.h @@ -0,0 +1,6 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "mem.h" +#include "io.h" diff --git a/os/boot/mpc/archfads.c b/os/boot/mpc/archfads.c new file mode 100644 index 00000000..39f86e03 --- /dev/null +++ b/os/boot/mpc/archfads.c @@ -0,0 +1,355 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "archfads.h" + +/* + * board-specific support for the 8xxFADS (including 860/21 development system) + */ + +enum { + USESDRAM = 0, /* set to 1 if kernel is to use SDRAM as well as DRAM */ + + /* sccr */ + RTSEL = IBIT(8), /* =0, select main oscillator (OSCM); =1, select external crystal (EXTCLK) */ + RTDIV = IBIT(7), /* =0, divide by 4; =1, divide by 512 */ + CRQEN = IBIT(9), /* =1, switch to high frequency when CPM active */ + PRQEN = IBIT(10), /* =1, switch to high frequency when interrupt pending */ + + /* plprcr */ + CSRC = IBIT(21), /* =0, clock is DFNH; =1, clock is DFNL */ +}; + +/* + * called early in main.c, after machinit: + * using board and architecture specific registers, initialise + * 8xx registers that need it and complete initialisation of the Mach structure. + */ +void +archinit(void) +{ + IMM *io; + int mf, isfads, sysmhz, t; + ulong v; + + v = getimmr() & 0xFFFF; + isfads = 0; /* assume it's the 860/821 board */ + sysmhz = 40; + switch(v>>8){ + case 0x00: t = 0x86000; break; + case 0x20: t = 0x82300; isfads = 1; break; + case 0x21: t = 0x823a0; isfads = 1; break; + default: t = 0; break; + } + m->cputype = t; + m->bcsr = KADDR(BCSRMEM); + m->bcsr[1] |= DisableRS232a | DisableIR | DisableEther | DisablePCMCIA | DisableRS232b; + m->bcsr[1] &= ~(DisableDRAM|DisableFlash); + if(isfads){ + sysmhz = 50; + m->bcsr[1] &= ~EnableSDRAM; + m->bcsr[4] &= ~(EnableVideoClock|EnableVideoPort); + m->bcsr[4] |= DisableVideoLamp; + } + io = m->iomem; + if(1 || io->sccr & IBIT(7)){ /* RTDIV=1 */ + /* oscillator frequency can't be determined independently: check a switch */ + if((m->bcsr[2]>>19)&(1<<2)) + m->clockgen = 5*MHz; + else + m->clockgen = 4*MHz; + } else + m->clockgen = 32768; + mf = (sysmhz*MHz)/m->clockgen; + m->cpuhz = m->clockgen*mf; + io->plprcrk = KEEP_ALIVE_KEY; + io->plprcr &= ~IBIT(21); /* general system clock is DFNH */ + io->plprcr = (io->plprcr & ((1<<20)-1)) | ((mf-1)<<20); + io->mptpr = 0x0400; /* memory prescaler = 16 for refresh */ + io->plprcrk = ~KEEP_ALIVE_KEY; + if(isfads){ + m->bcsr[1] |= EnableSDRAM; + sdraminit(SDRAMMEM); + if(!USESDRAM) + m->bcsr[1] &= ~EnableSDRAM; /* tells kernel not to map it */ + } +} + +static void +archidprint(void) +{ + int f, i; + ulong v; + + /* 8xx and FADS specific */ + print("IMMR: "); + v = getimmr() & 0xFFFF; + switch(v>>8){ + case 0x00: print("MPC860/821"); break; + case 0x20: print("MPC823"); break; + case 0x21: print("MPC823A"); break; + default: print("Type #%lux", v>>8); break; + } + print(", mask #%lux\n", v&0xFF); + v = m->bcsr[3]>>16; + print("MPC8xxFADS rev %lud, DB: ", ((v>>4)&8)|((v>>1)&4)|(v&3)); + f = (v>>8)&0x3F; + switch(f){ + default: print("ID#%x", f); break; + case 0x00: print("MPC860/821"); break; + case 0x01: print("MPC813"); break; + case 0x02: print("MPC821"); break; + case 0x03: print("MPC823"); break; + case 0x20: print("MPC801"); break; + case 0x21: print("MPC850"); break; + case 0x22: print("MPC860"); break; + case 0x23: print("MPC860SAR"); break; + case 0x24: print("MPC860T"); break; + } + print("ADS, rev #%lux\n", (m->bcsr[2]>>16)&7); + for(i=0; i<=4; i++) + print("BCSR%d: %8.8lux\n", i, m->bcsr[i]); + v = m->bcsr[2]; + f = (v>>28)&0xF; + switch(f){ + default: print("Unknown"); break; + case 4: print("SM732A2000/SM73228 - 8M SIMM"); break; + case 5: print("SM732A1000A/SM73218 - 4M SIMM"); break; + case 6: print("MCM29080 - 8M SIMM"); break; + case 7: print("MCM29040 - 4M SIMM"); break; + case 8: print("MCM29020 - 2M SIMM"); break; + } + switch((m->bcsr[3]>>20)&7){ + default: i = 0; break; + case 1: i = 150; break; + case 2: i = 120; break; + case 3: i = 90; break; + } + print(" flash, %dns\n", i); + f = (v>>23)&0xF; + switch(f&3){ + case 0: i = 4; break; + case 1: i = 32; break; + case 2: i = 16; break; + case 3: i = 8; break; + } + print("%dM SIMM, ", i); + switch(f>>2){ + default: i = 0; break; + case 2: i = 70; break; + case 3: i = 60; break; + } + print("%dns\n", i); + print("options: #%lux\n", (m->bcsr[2]>>19)&0xF); + print("plprcr=%8.8lux sccr=%8.8lux\n", m->iomem->plprcr, m->iomem->sccr); +} + +void +cpuidprint(void) +{ + int t; + + print("PVR: "); + t = getpvr()>>16; + switch(t){ + case 0x01: print("MPC601"); break; + case 0x03: print("MPC603"); break; + case 0x04: print("MPC604"); break; + case 0x06: print("MPC603e"); break; + case 0x07: print("MPC603e-v7"); break; + case 0x50: print("MPC8xx"); break; + default: print("PowerPC version #%x", t); break; + } + print(", revision #%lux\n", getpvr()&0xffff); + archidprint(); + print("%lud MHz system\n", m->cpuhz/MHz); + print("\n"); +} + +static char* defplan9ini[2] = { + /* 860/821 */ + "ether0=type=SCC port=1 ea=00108bf12900\r\n" + "vgasize=640x480x8\r\n" + "kernelpercent=40\r\n" + "console=0 lcd\r\nbaud=9600\r\n", + + /* 823 */ + "ether0=type=SCC port=2 ea=00108bf12900\r\n" + "vgasize=640x480x8\r\n" + "kernelpercent=40\r\n" + "console=0 lcd\r\nbaud=9600\r\n", +}; + +char * +archconfig(void) +{ + print("Using default configuration\n"); + return defplan9ini[MPCMODEL(m->cputype) == 0x823]; +} + +/* + * provide value for #r/switch (devrtc.c) + */ +int +archoptionsw(void) +{ + return (m->bcsr[2]>>19)&0xF; /* value of switch DS1 */ +} + +/* + * invoked by clock.c:/^clockintr + */ +static void +twinkle(void) +{ + if(m->ticks%MS2TK(1000) == 0) + m->bcsr[4] ^= DisableLamp; +} + +void (*archclocktick)(void) = twinkle; + +/* + * for flash.c:/^flashreset + * retrieve flash type, virtual base and length and return 0; + * return -1 on error (no flash) + */ +int +archflashreset(char *type, void **addr, long *length) +{ + char *t; + int mbyte; + + if((m->iomem->memc[0].base & 1) == 0) + return -1; /* shouldn't happen */ + switch((m->bcsr[2]>>28)&0xF){ + default: return -1; /* unknown or not there */ + case 4: mbyte=8; t = "SM732x8"; break; + case 5: mbyte=4; t = "SM732x8"; break; + case 6: mbyte=8; t = "AMD29F0x0"; break; + case 7: mbyte=4; t = "AMD29F0x0"; break; + case 8: mbyte=2; t = "AMD29F0x0"; break; + } + strcpy(type, t); + *addr = KADDR(FLASHMEM); + *length = mbyte*1024*1024; + return 0; +} + +/* + * enable the clocks for the given SCC ether and reveal them to the caller. + * do anything else required to prepare the transceiver (eg, set full-duplex, reset loopback). + */ +int +archetherenable(int cpmid, int *rcs, int *tcs) +{ + IMM *io; + + switch(cpmid){ + default: + /* no other SCCs are wired on the FADS board */ + return -1; + + case SCC2ID: /* assume 8xxFADS board with 823DABS */ + io = ioplock(); + m->bcsr[1] |= DisableIR|DisableRS232b; + m->bcsr[1] &= ~DisableEther; + io->papar |= SIBIT(6)|SIBIT(5); /* enable CLK2 and CLK3 */ + io->padir &= ~(SIBIT(6)|SIBIT(5)); + + /* ETHLOOP etc set in BCSR elsewhere */ + *rcs = CLK2; + *tcs = CLK3; + iopunlock(); + break; + + case SCC1ID: /* assume 860/21 development board */ + io = ioplock(); + m->bcsr[1] |= DisableIR|DisableRS232b; /* TO DO: might not be shared with RS232b */ + m->bcsr[1] &= ~DisableEther; + io->papar |= SIBIT(6)|SIBIT(7); /* enable CLK2 and CLK1 */ + io->padir &= ~(SIBIT(6)|SIBIT(7)); + + /* settings peculiar to 860/821 development board */ + io->pcpar &= ~(SIBIT(4)|SIBIT(5)|SIBIT(6)); /* ETHLOOP, TPFULDL~, TPSQEL~ */ + io->pcdir |= SIBIT(4)|SIBIT(5)|SIBIT(6); + io->pcdat &= ~SIBIT(4); + io->pcdat |= SIBIT(5)|SIBIT(6); + *rcs = CLK2; + *tcs = CLK1; + iopunlock(); + break; + } + return 0; +} + +void +archetherdisable(int id) +{ + USED(id); + m->bcsr[1] |= DisableEther|DisableIR|DisableRS232b; +} + +/* + * do anything extra required to enable the UART on the given CPM port + */ +void +archenableuart(int id, int irda) +{ + switch(id){ + case SMC1ID: + m->bcsr[1] &= ~DisableRS232a; + break; + case SCC2ID: + m->bcsr[1] |= DisableEther|DisableIR|DisableRS232b; + if(irda) + m->bcsr[1] &= ~DisableIR; + else + m->bcsr[1] &= ~DisableRS232b; + break; + default: + /* nothing special */ + break; + } +} + +/* + * do anything extra required to disable the UART on the given CPM port + */ +void +archdisableuart(int id) +{ + switch(id){ + case SMC1ID: + m->bcsr[1] |= DisableRS232a; + break; + case SCC2ID: + m->bcsr[1] |= DisableIR|DisableRS232b; + break; + default: + /* nothing special */ + break; + } +} + +/* + * enable/disable the LCD panel's backlight via + * York touch panel interface (does no harm without it) + */ +void +archbacklight(int on) +{ + IMM *io; + + delay(2); + io = ioplock(); + io->papar &= ~SIBIT(4); + io->padir |= SIBIT(4); + if(on) + io->padat |= SIBIT(4); + else + io->padat &= ~SIBIT(4); + iopunlock(); +} diff --git a/os/boot/mpc/archfads.h b/os/boot/mpc/archfads.h new file mode 100644 index 00000000..fbe30361 --- /dev/null +++ b/os/boot/mpc/archfads.h @@ -0,0 +1,42 @@ + +enum { + /* BCSR1 bits */ + DisableFlash= IBIT(0), + DisableDRAM= IBIT(1), + DisableEther= IBIT(2), + DisableIR= IBIT(3), + DisableRS232a= IBIT(7), + DisablePCMCIA= IBIT(8), + PCCVCCMask= IBIT(9)|IBIT(15), + PCCVPPMask= IBIT(10)|IBIT(11), + DisableRS232b= IBIT(13), + EnableSDRAM= IBIT(14), + + PCCVCC0V= IBIT(15)|IBIT(9), + PCCVCC5V= IBIT(9), /* active low */ + PCCVCC3V= IBIT(15), /* active low */ + PCCVPP0V= IBIT(10)|IBIT(11), /* active low */ + PCCVPP5V= IBIT(10), /* active low */ + PCCVPP12V= IBIT(11), /* active low */ + PCCVPPHiZ= IBIT(10)|IBIT(11), + + /* BCSR4 bits */ + DisableTPDuplex= IBIT(1), + DisableLamp= IBIT(3), + DisableUSB= IBIT(4), + USBFullSpeed= IBIT(5), + DisableUSBVcc= IBIT(6), + DisableVideoLamp= IBIT(8), + EnableVideoClock= IBIT(9), + EnableVideoPort= IBIT(10), + DisableModem= IBIT(11), +}; + +enum { + /* memory controller CS assignment on FADS boards */ + BOOTCS = 0, + BCSRCS = 1, + DRAM1 = 2, + DRAM2 = 3, + SDRAM = 4, +}; diff --git a/os/boot/mpc/archpaq.c b/os/boot/mpc/archpaq.c new file mode 100644 index 00000000..abffe266 --- /dev/null +++ b/os/boot/mpc/archpaq.c @@ -0,0 +1,240 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "archpaq.h" + +/* + * board-specific support for the 82x PowerPAQ + */ + +enum { + SYSMHZ = 50, /* desired system clock in MHz */ + + /* sccr */ + RTSEL = IBIT(8), /* =0, select main oscillator (OSCM); =1, select external crystal (EXTCLK) */ + RTDIV = IBIT(7), /* =0, divide by 4; =1, divide by 512 */ + CRQEN = IBIT(9), /* =1, switch to high frequency when CPM active */ + PRQEN = IBIT(10), /* =1, switch to high frequency when interrupt pending */ + + /* plprcr */ + CSRC = IBIT(21), /* =0, clock is DFNH; =1, clock is DFNL */ +}; + +/* + * called early in main.c, after machinit: + * using board and architecture specific registers, initialise + * 8xx registers that need it and complete initialisation of the Mach structure. + */ +void +archinit(void) +{ + IMM *io; + int mf, t; + + switch((getimmr()>>8)&0xFF){ + case 0x00: t = 0x86000; break; /* also 821 */ + case 0x20: t = 0x82300; break; + case 0x21: t = 0x823a0; break; + default: t = 0; break; + } + m->cputype = t; + m->bcsr = nil; /* there isn't one */ + m->clockgen = 32*1024; /* crystal frequency */ + io = m->iomem; + io->sccrk = KEEP_ALIVE_KEY; + io->sccr &= ~RTDIV; /* divide 32k by 4 */ + io->sccr |= RTSEL; + io->sccrk = ~KEEP_ALIVE_KEY; + mf = (SYSMHZ*MHz)/m->clockgen; + m->cpuhz = m->clockgen*mf; + io->plprcrk = KEEP_ALIVE_KEY; + io->plprcr &= ~IBIT(21); /* general system clock is DFNH */ + io->plprcr = (io->plprcr & ((1<<20)-1)) | ((mf-1)<<20); + io->mptpr = 0x0400; /* memory prescaler = 16 for refresh */ + io->plprcrk = ~KEEP_ALIVE_KEY; +} + +void +cpuidprint(void) +{ + int t, v; + + print("PVR: "); + t = getpvr()>>16; + switch(t){ + case 0x01: print("MPC601"); break; + case 0x03: print("MPC603"); break; + case 0x04: print("MPC604"); break; + case 0x06: print("MPC603e"); break; + case 0x07: print("MPC603e-v7"); break; + case 0x50: print("MPC8xx"); break; + default: print("PowerPC version #%x", t); break; + } + print(", revision #%lux\n", getpvr()&0xffff); + print("IMMR: "); + v = getimmr() & 0xFFFF; + switch(v>>8){ + case 0x00: print("MPC860/821"); break; + case 0x20: print("MPC823"); break; + case 0x21: print("MPC823A"); break; + default: print("Type #%lux", v>>8); break; + } + print(", mask #%lux\n", v&0xFF); + print("plprcr=%8.8lux sccr=%8.8lux\n", m->iomem->plprcr, m->iomem->sccr); + print("%lud MHz system\n", m->cpuhz/MHz); + print("\n"); +} + +static char* defplan9ini[2] = { + /* 860/821 */ + "ether0=type=SCC port=1 ea=00108bf12900\r\n" + "vgasize=640x480x8\r\n" + "kernelpercent=40\r\n" + "console=0 lcd\r\nbaud=19200\r\n", + + /* 823 */ + "ether0=type=SCC port=2 ea=00108bf12900\r\n" + "vgasize=640x480x8\r\n" + "kernelpercent=40\r\n" + "console=0 lcd\r\nbaud=19200\r\n", +}; + +char * +archconfig(void) +{ + print("Using default configuration\n"); + return defplan9ini[MPCMODEL(m->cputype) == 0x823]; +} + +/* + * provide value for #r/switch (devrtc.c) + */ +int +archoptionsw(void) +{ + return 0; +} + +/* + * invoked by clock.c:/^clockintr + */ +static void +twinkle(void) +{ + /* no easy-to-use LED on PAQ (they use i2c) */ +} + +void (*archclocktick)(void) = twinkle; + +/* + * for flash.c:/^flashinit + * retrieve flash type, virtual base and length and return 0; + * return -1 on error (no flash) + */ +int +archflashreset(char *type, void **addr, long *length) +{ + strcpy(type, "AMD29F0x0"); + *addr = KADDR(FLASHMEM); + *length = 8*1024*1024; /* 8mbytes on some models */ + return 0; +} + +/* + * enable the clocks for the given SCC ether and reveal them to the caller. + * do anything else required to prepare the transceiver (eg, set full-duplex, reset loopback). + */ +int +archetherenable(int cpmid, int *rcs, int *tcs) +{ + USED(cpmid, rcs, tcs); + return -1; /* there isn't an ether on the PAQs */ +} + +void +archetherdisable(int id) +{ + USED(id); +} + +/* + * do anything extra required to enable the UART on the given CPM port + */ +void +archenableuart(int id, int irda) +{ + IMM *io; + + USED(irda); + switch(id){ + case SMC1ID: + io = ioplock(); + io->pbodr &= ~0xc0; + io->pbdat |= 0xc0; + io->pcdat |= 0x400; + io->pcpar &= ~0x400; + io->pcdir |= 0x400; + io->pcdat &= ~0x400; /* enable SMC RS232 buffer */ + iopunlock(); + break; + case SCC2ID: + /* TO DO */ + break; + default: + /* nothing special */ + break; + } +} + +/* + * do anything extra required to disable the UART on the given CPM port + */ +void +archdisableuart(int id) +{ + switch(id){ + case SMC1ID: + /* TO DO */ + break; + case SCC2ID: + /* TO DO */ + break; + default: + /* nothing special */ + break; + } +} + +/* + * enable/disable the LCD panel's backlight via i2c + */ +void +archbacklight(int on) +{ + uchar msg; + IMM *io; + + i2csetup(); + msg = ~7; + i2csend(LEDRegI2C, &msg, 1); + io = ioplock(); + io->pbpar &= ~EnableLCD; + io->pbodr &= ~EnableLCD; + io->pbdir |= EnableLCD; + if(on) + io->pbdat |= EnableLCD; + else + io->pbdat &= ~EnableLCD; + iopunlock(); + if(on){ + msg = ~(DisablePanelVCC5|DisableTFT); + i2csend(PanelI2C, &msg, 1); + }else{ + msg = ~0; + i2csend(PanelI2C, &msg, 1); + } +} diff --git a/os/boot/mpc/archpaq.h b/os/boot/mpc/archpaq.h new file mode 100644 index 00000000..095abfaa --- /dev/null +++ b/os/boot/mpc/archpaq.h @@ -0,0 +1,29 @@ +enum { + /* memory controller CS assignment on PowerPAQ */ + BOOTCS = 0, + DRAM1 = 1, /* UPMB */ + DRAM2 = 2, /* UPMB */ + /* CS3 also connected to DRAM */ + /* CS4 128mbyte 8-bit gpcm, trlx, 15 wait; it's DAC */ + /* CS5 is external*/ +}; + +enum { + /* I2C addresses */ + PanelI2C = 0x21<<1, + /* the control bits are active low enables, or high disables */ + DisableVGA = ~0xFD, /* disable VGA signals */ + DisableTFT = ~0xFB, /* disable TFT panel signals */ + DisableSPIBus = ~0xF7, /* disable SPI/I2C to panel */ + DisablePanelVCC5 = ~0xEF, /* disable +5V to panel(s) */ + DisablePanelVCC3 = ~0xDF, /* disable +3.3V to panel(s) */ + DisableMonoPanel = ~0xBF, /* disable mono panel signals */ + DisableSPISelect = ~0x7F, /* disable SPI chip select to LVDS panel */ + ContrastI2C = 0x2E<<1, + LEDRegI2C = 0x20<<1, + DisableGreenLED = ~0xFE, + DisableYellowLED = ~0xFD, + DisableRedLED = ~0xFB, + + EnableLCD = IBIT(23), /* LCD enable bit in i/o port B */ +}; diff --git a/os/boot/mpc/boot.h b/os/boot/mpc/boot.h new file mode 100644 index 00000000..1b2a09fb --- /dev/null +++ b/os/boot/mpc/boot.h @@ -0,0 +1,8 @@ +#include +#include "lib.h" + +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" diff --git a/os/boot/mpc/bootp.c b/os/boot/mpc/bootp.c new file mode 100644 index 00000000..94b188f4 --- /dev/null +++ b/os/boot/mpc/bootp.c @@ -0,0 +1,507 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "ip.h" + +enum { + CHECKSUM = 1, /* set zero if trouble booting from Linux */ +}; + +uchar broadcast[Eaddrlen] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +}; + +static ushort tftpport = 5000; +static int Id = 1; +static Netaddr myaddr; +static Netaddr server; + +typedef struct { + uchar header[4]; + uchar data[Segsize]; +} Tftp; +static Tftp tftpb; + +static void +hnputs(uchar *ptr, ushort val) +{ + ptr[0] = val>>8; + ptr[1] = val; +} + +static void +hnputl(uchar *ptr, ulong val) +{ + ptr[0] = val>>24; + ptr[1] = val>>16; + ptr[2] = val>>8; + ptr[3] = val; +} + +static ulong +nhgetl(uchar *ptr) +{ + return ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]); +} + +static ushort +nhgets(uchar *ptr) +{ + return ((ptr[0]<<8) | ptr[1]); +} + +static short endian = 1; +static char* aendian = (char*)&endian; +#define LITTLE *aendian + +static ushort +ptcl_csum(void *a, int len) +{ + uchar *addr; + ulong t1, t2; + ulong losum, hisum, mdsum, x; + + addr = a; + losum = 0; + hisum = 0; + mdsum = 0; + + x = 0; + if((ulong)addr & 1) { + if(len) { + hisum += addr[0]; + len--; + addr++; + } + x = 1; + } + while(len >= 16) { + t1 = *(ushort*)(addr+0); + t2 = *(ushort*)(addr+2); mdsum += t1; + t1 = *(ushort*)(addr+4); mdsum += t2; + t2 = *(ushort*)(addr+6); mdsum += t1; + t1 = *(ushort*)(addr+8); mdsum += t2; + t2 = *(ushort*)(addr+10); mdsum += t1; + t1 = *(ushort*)(addr+12); mdsum += t2; + t2 = *(ushort*)(addr+14); mdsum += t1; + mdsum += t2; + len -= 16; + addr += 16; + } + while(len >= 2) { + mdsum += *(ushort*)addr; + len -= 2; + addr += 2; + } + if(x) { + if(len) + losum += addr[0]; + if(LITTLE) + losum += mdsum; + else + hisum += mdsum; + } else { + if(len) + hisum += addr[0]; + if(LITTLE) + hisum += mdsum; + else + losum += mdsum; + } + + losum += hisum >> 8; + losum += (hisum & 0xff) << 8; + while(hisum = losum>>16) + losum = hisum + (losum & 0xffff); + + return ~losum; +} + +static ushort +ip_csum(uchar *addr) +{ + int len; + ulong sum = 0; + + len = (addr[0]&0xf)<<2; + + while(len > 0) { + sum += addr[0]<<8 | addr[1] ; + len -= 2; + addr += 2; + } + + sum = (sum & 0xffff) + (sum >> 16); + sum = (sum & 0xffff) + (sum >> 16); + return (sum^0xffff); +} + +static void +udpsend(int ctlrno, Netaddr *a, void *data, int dlen) +{ + Udphdr *uh; + Etherhdr *ip; + static Etherpkt pkt; + int len, ptcllen; + + + uh = (Udphdr*)&pkt; + + memset(uh, 0, sizeof(Etherpkt)); + memmove(uh->udpcksum+sizeof(uh->udpcksum), data, dlen); + + /* + * UDP portion + */ + ptcllen = dlen + (UDP_HDRSIZE-UDP_PHDRSIZE); + uh->ttl = 0; + uh->udpproto = IP_UDPPROTO; + uh->frag[0] = 0; + uh->frag[1] = 0; + hnputs(uh->udpplen, ptcllen); + hnputl(uh->udpsrc, myaddr.ip); + hnputs(uh->udpsport, myaddr.port); + hnputl(uh->udpdst, a->ip); + hnputs(uh->udpdport, a->port); + hnputs(uh->udplen, ptcllen); + uh->udpcksum[0] = 0; + uh->udpcksum[1] = 0; + /*dlen = (dlen+1)&~1; */ + hnputs(uh->udpcksum, ptcl_csum(&uh->ttl, dlen+UDP_HDRSIZE)); + + /* + * IP portion + */ + ip = (Etherhdr*)&pkt; + len = sizeof(Udphdr)+dlen; + ip->vihl = IP_VER|IP_HLEN; + ip->tos = 0; + ip->ttl = 255; + hnputs(ip->length, len-ETHER_HDR); + hnputs(ip->id, Id++); + ip->frag[0] = 0; + ip->frag[1] = 0; + ip->cksum[0] = 0; + ip->cksum[1] = 0; + hnputs(ip->cksum, ip_csum(&ip->vihl)); + + /* + * Ethernet MAC portion + */ + hnputs(ip->type, ET_IP); + memmove(ip->d, a->ea, sizeof(ip->d)); + + ethertxpkt(ctlrno, &pkt, len, Timeout); +} + +static void +nak(int ctlrno, Netaddr *a, int code, char *msg, int report) +{ + int n; + char buf[128]; + + buf[0] = 0; + buf[1] = Tftp_ERROR; + buf[2] = 0; + buf[3] = code; + strcpy(buf+4, msg); + n = strlen(msg) + 4 + 1; + udpsend(ctlrno, a, buf, n); + if(report) + print("\ntftp: error(%d): %s\n", code, msg); +} + +static int +udprecv(int ctlrno, Netaddr *a, void *data, int dlen) +{ + int n, len; + ushort csm; + Udphdr *h; + ulong addr, timo; + static Etherpkt pkt; + static int rxactive; + + if(rxactive == 0) + timo = 1000; + else + timo = Timeout; + timo += TK2MS(m->ticks); + while(timo > TK2MS(m->ticks)){ + n = etherrxpkt(ctlrno, &pkt, timo-TK2MS(m->ticks)); + if(n <= 0) + continue; + + h = (Udphdr*)&pkt; + if(nhgets(h->type) != ET_IP) + continue; + + if(ip_csum(&h->vihl)) { + print("ip chksum error\n"); + continue; + } + if(h->vihl != (IP_VER|IP_HLEN)) { + print("ip bad vers/hlen\n"); + continue; + } + + if(h->udpproto != IP_UDPPROTO) + continue; + + h->ttl = 0; + len = nhgets(h->udplen); + hnputs(h->udpplen, len); + + if(CHECKSUM && nhgets(h->udpcksum)) { + csm = ptcl_csum(&h->ttl, len+UDP_PHDRSIZE); + if(csm != 0) { + print("udp chksum error csum #%4lux len %d\n", csm, n); + break; + } + } + + if(a->port != 0 && nhgets(h->udpsport) != a->port) + continue; + + addr = nhgetl(h->udpsrc); + if(a->ip != Bcastip && addr != a->ip) + continue; + + len -= UDP_HDRSIZE-UDP_PHDRSIZE; + if(len > dlen) { + print("udp: packet too big\n"); + continue; + } + + memmove(data, h->udpcksum+sizeof(h->udpcksum), len); + a->ip = addr; + a->port = nhgets(h->udpsport); + memmove(a->ea, pkt.s, sizeof(a->ea)); + + rxactive = 1; + return len; + } + + return 0; +} + +static int tftpblockno; + +static int +tftpopen(int ctlrno, Netaddr *a, char *name, Tftp *tftp) +{ + int i, len, rlen, oport; + char buf[Segsize+2]; + + buf[0] = 0; + buf[1] = Tftp_READ; + len = sprint(buf+2, "%s", name) + 2; + len += sprint(buf+len+1, "octet") + 2; + + oport = a->port; + for(i = 0; i < 5; i++){ + a->port = oport; + udpsend(ctlrno, a, buf, len); + a->port = 0; + if((rlen = udprecv(ctlrno, a, tftp, sizeof(Tftp))) < sizeof(tftp->header)) + continue; + + switch((tftp->header[0]<<8)|tftp->header[1]){ + + case Tftp_ERROR: + print("tftpopen: error (%d): %s\n", + (tftp->header[2]<<8)|tftp->header[3], tftp->data); + return -1; + + case Tftp_DATA: + tftpblockno = 1; + len = (tftp->header[2]<<8)|tftp->header[3]; + if(len != tftpblockno){ + print("tftpopen: block error: %d\n", len); + nak(ctlrno, a, 1, "block error", 0); + return -1; + } + return rlen-sizeof(tftp->header); + } + } + + print("tftpopen: failed to connect to server\n"); + return -1; +} + +static int +tftpread(int ctlrno, Netaddr *a, Tftp *tftp, int dlen) +{ + int blockno, len, retry; + uchar buf[4]; + + buf[0] = 0; + buf[1] = Tftp_ACK; + buf[2] = tftpblockno>>8; + buf[3] = tftpblockno; + tftpblockno++; + + dlen += sizeof(tftp->header); + + retry = 0; +buggery: + udpsend(ctlrno, a, buf, sizeof(buf)); + + if((len = udprecv(ctlrno, a, tftp, dlen)) < dlen){ + print("tftpread: %d != %d\n", len, dlen); + nak(ctlrno, a, 2, "short read", 0); + if(retry++ < 5) + goto buggery; + return -1; + } + + blockno = (tftp->header[2]<<8)|tftp->header[3]; + if(blockno != tftpblockno){ + print("?"); + + if(blockno == tftpblockno-1 && retry++ < 8) + goto buggery; + print("tftpread: block error: %d, expected %d\n", blockno, tftpblockno); + nak(ctlrno, a, 1, "block error", 0); + + return -1; + } + + return len-sizeof(tftp->header); +} + +int +bootp(int ctlrno, char *file) +{ + Bootp req, rep; + int i, dlen, segsize, text, data, bss, total; + uchar *ea, *addr, *p; + ulong entry; + Exec *exec; + char name[128], *filename, *sysname; + + if((ea = etheraddr(ctlrno)) == 0){ + print("invalid ctlrno %d\n", ctlrno); + return -1; + } + + filename = 0; + sysname = 0; + if(file && *file){ + strcpy(name, file); + if(filename = strchr(name, ':')){ + if(filename != name && *(filename-1) != '\\'){ + sysname = name; + *filename++ = 0; + } + } + else + filename = name; + } + + + memset(&req, 0, sizeof(req)); + req.op = Bootrequest; + req.htype = 1; /* ethernet */ + req.hlen = Eaddrlen; /* ethernet */ + memmove(req.chaddr, ea, Eaddrlen); + + myaddr.ip = 0; + myaddr.port = BPportsrc; + memmove(myaddr.ea, ea, Eaddrlen); + + for(i = 0; i < 10; i++) { + server.ip = Bcastip; + server.port = BPportdst; + memmove(server.ea, broadcast, sizeof(server.ea)); + udpsend(ctlrno, &server, &req, sizeof(req)); + if(udprecv(ctlrno, &server, &rep, sizeof(rep)) <= 0) + continue; + if(memcmp(req.chaddr, rep.chaddr, Eaddrlen)) + continue; + if(rep.htype != 1 || rep.hlen != Eaddrlen) + continue; + if(sysname == 0 || strcmp(sysname, rep.sname) == 0) + break; + } + if(i >= 10) { + print("bootp timed out\n"); + return -1; + } + + if(filename == 0 || *filename == 0) + filename = rep.file; + + if(rep.sname[0] != '\0') + print("%s ", rep.sname); + print("(%d.%d.%d.%d!%d): %s\n", + rep.siaddr[0], + rep.siaddr[1], + rep.siaddr[2], + rep.siaddr[3], + server.port, + filename);uartwait(); + + myaddr.ip = nhgetl(rep.yiaddr); + myaddr.port = tftpport++; + server.ip = nhgetl(rep.siaddr); + server.port = TFTPport; + + if((dlen = tftpopen(ctlrno, &server, filename, &tftpb)) < 0) + return -1; + exec = (Exec*)(tftpb.data); + if(dlen < sizeof(Exec) || GLLONG(exec->magic) != Q_MAGIC){ + nak(ctlrno, &server, 0, "bad magic number", 1); + return -1; + } + text = GLLONG(exec->text); + data = GLLONG(exec->data); + bss = GLLONG(exec->bss); + total = text+data+bss; + entry = GLLONG(exec->entry); +print("load@%8.8lux: ", PADDR(entry));uartwait(); + print("%d", text); + + addr = (uchar*)PADDR(entry); + p = tftpb.data+sizeof(Exec); + dlen -= sizeof(Exec); + segsize = text; + for(;;){ + if(dlen == 0){ + if((dlen = tftpread(ctlrno, &server, &tftpb, sizeof(tftpb.data))) < 0) + return -1; + p = tftpb.data; + } + if(segsize <= dlen) + i = segsize; + else + i = dlen; + memmove(addr, p, i); + + addr += i; + p += i; + segsize -= i; + dlen -= i; + + if(segsize <= 0){ + if(data == 0) + break; + print("+%d", data); + segsize = data; + data = 0; + addr = (uchar*)PGROUND((ulong)addr); + } + } + nak(ctlrno, &server, 3, "ok", 0); /* tftpclose */ + print("+%d=%d\n", bss, total); + print("entry: 0x%lux\n", entry); + uartwait(); + scc2stop(); + splhi(); + (*(void(*)(void))(PADDR(entry)))(); + + return 0; +} diff --git a/os/boot/mpc/clock.c b/os/boot/mpc/clock.c new file mode 100644 index 00000000..4d2d6bfd --- /dev/null +++ b/os/boot/mpc/clock.c @@ -0,0 +1,71 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +enum { + Timebase = 4, /* system clock cycles per time base cycle */ +}; + +void (*archclocktick)(void); /* set by arch*.c when desired */ + +static ulong clkreload; + +void +delay(int l) +{ + ulong i, j; + + j = m->delayloop; + while(l-- > 0) + for(i=0; i < j; i++) + ; +} + +void +microdelay(int l) +{ + ulong i; + + l *= m->delayloop; + l /= 1000; + if(l <= 0) + l = 1; + for(i = 0; i < l; i++) + ; +} + +void +clockintr(Ureg*, void*) +{ + putdec(clkreload); + m->ticks++; + checkalarms(); + if(archclocktick != nil) + archclocktick(); +} + +void +clockinit(void) +{ + long x; + + m->delayloop = m->cpuhz/1000; /* initial estimate */ + do { + x = gettbl(); + delay(10); + x = gettbl() - x; + } while(x < 0); + + /* + * fix count + */ + m->delayloop = ((vlong)m->delayloop*(10*m->clockgen/1000))/(x*Timebase); + if(m->delayloop == 0) + m->delayloop = 1; + clkreload = (m->clockgen/Timebase)/HZ-1; + putdec(clkreload); +} diff --git a/os/boot/mpc/conf.c b/os/boot/mpc/conf.c new file mode 100644 index 00000000..4ecb4b3e --- /dev/null +++ b/os/boot/mpc/conf.c @@ -0,0 +1,173 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "dosfs.h" + +static char *confname[MAXCONF]; +static char *confval[MAXCONF]; +static int nconf; + +extern char **ini; + +char* +getconf(char *name) +{ + int i; + + for(i = 0; i < nconf; i++) + if(strcmp(confname[i], name) == 0) + return confval[i]; + return 0; +} + +/* + * read configuration file + */ +int +plan9ini(Dos *dos, char *val) +{ + Dosfile rc; + int i, n; + char *cp, *p, *q, *line[MAXCONF]; + + cp = BOOTARGS; + if(dos) { + if(dosstat(dos, *ini, &rc) <= 0) + return -1; + + *cp = 0; + n = dosread(&rc, cp, BOOTARGSLEN-1); + if(n <= 0) + return -1; + cp[n] = 0; + } else if(val != nil){ + if(memchr(val, 0, BOOTARGSLEN-1) == nil) + return -1; + print("Using flash configuration\n"); + strcpy(cp, val); + n = strlen(cp); + }else{ + strcpy(cp, archconfig()); + n = strlen(cp); + } + + /* + * Make a working copy. + * We could change this to pass the parsed strings + * to the booted programme instead of the raw + * string, then it only gets done once. + */ + memmove(cp+BOOTARGSLEN, cp, n+1); + cp += BOOTARGSLEN; + + /* + * Strip out '\r', change '\t' -> ' '. + */ + p = cp; + for(q = cp; *q; q++){ + if(*q == '\r') + continue; + if(*q == '\t') + *q = ' '; + *p++ = *q; + } + *p = 0; + n = getcfields(cp, line, MAXCONF, "\n"); + for(i = 0; i < n; i++){ + cp = strchr(line[i], '='); + if(cp == 0) + continue; + *cp++ = 0; + if(cp - line[i] >= NAMELEN+1) + *(line[i]+NAMELEN-1) = 0; + confname[nconf] = line[i]; + confval[nconf] = cp; + nconf++; + } + return 0; +} + +int +parseether(uchar *to, char *from) +{ + char nip[4]; + char *p; + int i; + + p = from; + while(*p == ' ') + ++p; + for(i = 0; i < 6; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +int +isaconfig(char *class, int ctlrno, ISAConf *isa) +{ + char cc[NAMELEN], *p, *q, *r; + int n; + + sprint(cc, "%s%d", class, ctlrno); + for(n = 0; n < nconf; n++){ + if(strncmp(confname[n], cc, NAMELEN)) + continue; + isa->nopt = 0; + p = confval[n]; + while(*p){ + while(*p == ' ' || *p == '\t') + p++; + if(*p == '\0') + break; + if(strncmp(p, "type=", 5) == 0){ + p += 5; + for(q = isa->type; q < &isa->type[NAMELEN-1]; q++){ + if(*p == '\0' || *p == ' ' || *p == '\t') + break; + *q = *p++; + } + *q = '\0'; + } + else if(strncmp(p, "port=", 5) == 0) + isa->port = strtoul(p+5, &p, 0); + else if(strncmp(p, "irq=", 4) == 0) + isa->irq = strtoul(p+4, &p, 0); + else if(strncmp(p, "mem=", 4) == 0) + isa->mem = strtoul(p+4, &p, 0); + else if(strncmp(p, "size=", 5) == 0) + isa->size = strtoul(p+5, &p, 0); + else if(strncmp(p, "ea=", 3) == 0){ + if(parseether(isa->ea, p+3) == -1) + memset(isa->ea, 0, 6); + } + else if(isa->nopt < NISAOPT){ + r = isa->opt[isa->nopt]; + while(*p && *p != ' ' && *p != '\t'){ + *r++ = *p++; + if(r-isa->opt[isa->nopt] >= ISAOPTLEN-1) + break; + } + *r = '\0'; + isa->nopt++; + } + while(*p && *p != ' ' && *p != '\t') + p++; + } + return 1; + } + return 0; +} diff --git a/os/boot/mpc/console.c b/os/boot/mpc/console.c new file mode 100644 index 00000000..9a05c122 --- /dev/null +++ b/os/boot/mpc/console.c @@ -0,0 +1,173 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +static Queue* consiq; +static Queue* consoq; + +void +bothputs(char *s, int n) +{ + uartputs(s, n); + screenputs(s, n); +} + +static void (*consputs)(char*, int) = bothputs; /* or screenputs */ + +void +consinit(void) +{ + char *p; + int baud, port; + static int cgadone; + + p = getconf("console"); + if(0) + if(p == 0 || strcmp(p, "lcd") == 0 || strcmp(p, "screen") == 0){ + consiq = qopen(4*1024, 0, 0, 0); + consoq = qopen(8*1024, 0, 0, 0); + consputs = screenputs; + return; + } + if(p!=0 && strstr(p, "lcd") == 0) + consputs = bothputs; + else + consputs = uartputs; +//consputs = screenputs; + port = 0; + if(p) + port = strtoul(p, 0, 0); + baud = 0; + if(p = getconf("baud")) + baud = strtoul(p, 0, 0); + if(baud == 0) + baud = 9600; + uartspecial(port, baud, &consiq, &consoq, kbdchar); +} + +void +kbdchar(Queue *q, int c) +{ + c &= 0x7F; + if(c == 0x10) + panic("^p"); + qbputc(q, c); +} + +static int +getline(char *buf, int size, int dotimeout) +{ + int c, i=0; + ulong start; + char echo; + + for (;;) { + start = m->ticks; + do{ + if(dotimeout && ((m->ticks - start) > 5*HZ)) + return -2; + c = qbgetc(consiq); + }while(c == -1); + if(c == '\r') + c = '\n'; /* turn carriage return into newline */ + if(c == '\177') + c = '\010'; /* turn delete into backspace */ + if(c == '\025') + echo = '\n'; /* echo ^U as a newline */ + else + echo = c; + (*consputs)(&echo, 1); + + if(c == '\010'){ + if(i > 0) + i--; /* bs deletes last character */ + continue; + } + /* a newline ends a line */ + if (c == '\n') + break; + /* ^U wipes out the line */ + if (c =='\025') + return -1; + if(i == size) + return size; + buf[i++] = c; + } + buf[i] = 0; + return i; +} + +int +getstr(char *prompt, char *buf, int size, char *def) +{ + int len, isdefault; + + buf[0] = 0; + isdefault = (def && *def); + for (;;) { + if(isdefault) + print("%s[default==%s]: ", prompt, def); + else + print("%s: ", prompt); + len = getline(buf, size, isdefault); + switch(len){ + case -1: + /* ^U typed */ + continue; + case -2: + /* timeout, use default */ + (*consputs)("\n", 1); + len = 0; + break; + default: + break; + } + if(len >= size){ + print("line too long\n"); + continue; + } + break; + } + if(len == 0 && isdefault) + strcpy(buf, def); + return 0; +} + +int +sprint(char *s, char *fmt, ...) +{ + return donprint(s, s+PRINTSIZE, fmt, (&fmt+1)) - s; +} + +int +print(char *fmt, ...) +{ + char buf[PRINTSIZE]; + int n; + + if(consputs == 0) + return 0; + n = donprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf; + (*consputs)(buf, n); + return n; +} + +void +panic(char *fmt, ...) +{ + char buf[PRINTSIZE]; + int n; + + if(consputs){ + (*consputs)("panic: ", 7); + n = donprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf; + (*consputs)(buf, n); + (*consputs)("\n", 1); + } + spllo(); + for(;;) + idle(); +} diff --git a/os/boot/mpc/cpm.c b/os/boot/mpc/cpm.c new file mode 100644 index 00000000..a9a45d60 --- /dev/null +++ b/os/boot/mpc/cpm.c @@ -0,0 +1,162 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +enum { + BDSIZE= 1024, /* TO DO: check this */ +}; + +static Map bdmapv[BDSIZE/sizeof(BD)]; +static RMap bdmap = {"buffer descriptors"}; + +void +cpminit(void) +{ + IMM *io; + + io = m->iomem; + io->sdcr = 1; + io->lccr &= ~1; /* disable LCD */ + io->pcint = 0; /* disable all port C interrupts */ + io->pcso = 0; + io->pcdir =0; + io->pcpar = 0; + io->pcdat = 0; + io->papar = 0; + io->padir = 0; + io->paodr = 0; + io->padat = 0; + io->pbpar = 0; + io->pbdir = 0; + io->pbodr = 0; + io->pbdat = 0; + eieio(); + + for(io->cpcr = 0x8001; io->cpcr & 1;) /* reset all CPM channels */ + eieio(); + + mapinit(&bdmap, bdmapv, sizeof(bdmapv)); + mapfree(&bdmap, DPBASE, BDSIZE); +} + +void +cpmop(int op, int cno, int param) +{ + IMM *io; + int s; + + s = splhi(); + io = m->iomem; + eieio(); + while(io->cpcr & 1) + eieio(); + io->cpcr = (op<<8)|(cno<<4)|(param<<1)|1; + eieio(); + while(io->cpcr & 1) + eieio(); + splx(s); +} + +/* + * connect SCCx clocks in NSMI mode (x=1 for USB) + */ +void +sccnmsi(int x, int rcs, int tcs) +{ + IMM *io; + ulong v; + int sh; + + sh = (x-1)*8; /* each SCCx field in sicr is 8 bits */ + v = (((rcs&7)<<3) | (tcs&7)) << sh; + io = ioplock(); + io->sicr = (io->sicr & ~(0xFF<gsmrl & (3<<4)){ + cpmop(GracefulStopTx, SCC2ID, 0); + cpmop(CloseRxBD, SCC2ID, 0); + delay(1); + scc->gsmrl &= ~(3<<4); /* disable current use */ + archetherdisable(SCC2ID); + } +} + +BD * +bdalloc(int n) +{ + ulong a; + + a = mapalloc(&bdmap, 0, n*sizeof(BD), 0); + if(a == 0) + panic("bdalloc"); + return KADDR(a); +} + +void +bdfree(BD *b, int n) +{ + if(b){ + eieio(); + mapfree(&bdmap, PADDR(b), n*sizeof(BD)); + } +} + +/* + * initialise receive and transmit buffer rings. + */ +int +ioringinit(Ring* r, int nrdre, int ntdre, int bufsize) +{ + int i, x; + + /* the ring entries must be aligned on sizeof(BD) boundaries */ + r->nrdre = nrdre; + if(r->rdr == nil) + r->rdr = bdalloc(nrdre); + /* the buffer size must align with cache lines since the cache doesn't snoop */ + bufsize = (bufsize+CACHELINESZ-1)&~(CACHELINESZ-1); + if(r->rrb == nil) + r->rrb = malloc(nrdre*bufsize); + if(r->rdr == nil || r->rrb == nil) + return -1; + dcflush(r->rrb, nrdre*bufsize); + x = PADDR(r->rrb); + for(i = 0; i < nrdre; i++){ + r->rdr[i].length = 0; + r->rdr[i].addr = x; + r->rdr[i].status = BDEmpty|BDInt; + x += bufsize; + } + r->rdr[i-1].status |= BDWrap; + r->rdrx = 0; + + r->ntdre = ntdre; + if(r->tdr == nil) + r->tdr = bdalloc(ntdre); + if(r->txb == nil) + r->txb = malloc(ntdre*sizeof(Block*)); + if(r->tdr == nil || r->txb == nil) + return -1; + for(i = 0; i < ntdre; i++){ + r->txb[i] = nil; + r->tdr[i].addr = 0; + r->tdr[i].length = 0; + r->tdr[i].status = 0; + } + r->tdr[i-1].status |= BDWrap; + r->tdrh = 0; + r->tdri = 0; + r->ntq = 0; + return 0; +} diff --git a/os/boot/mpc/crc32.c b/os/boot/mpc/crc32.c new file mode 100644 index 00000000..78bdacba --- /dev/null +++ b/os/boot/mpc/crc32.c @@ -0,0 +1,42 @@ +#include "boot.h" + +/* + * from Rob Warnock + */ +static ulong crc32tab[256]; /* initialised on first call to crc32 */ + +enum { + CRC32POLY = 0x04c11db7 /* AUTODIN II, Ethernet, & FDDI */ +}; + +/* + * Build auxiliary table for parallel byte-at-a-time CRC-32. + */ +static void +initcrc32(void) +{ + int i, j; + ulong c; + + for(i = 0; i < 256; i++) { + for(c = i << 24, j = 8; j > 0; j--) + if(c & (1<<31)) + c = (c<<1) ^ CRC32POLY; + else + c <<= 1; + crc32tab[i] = c; + } +} + +ulong +crc32(void *buf, int n, ulong crc) +{ + uchar *p; + + if(crc32tab[1] == 0) + initcrc32(); + crc = ~crc; + for(p = buf; --n >= 0;) + crc = (crc << 8) ^ crc32tab[(crc >> 24) ^ *p++]; + return ~crc; +} diff --git a/os/boot/mpc/dat.h b/os/boot/mpc/dat.h new file mode 100644 index 00000000..edadc030 --- /dev/null +++ b/os/boot/mpc/dat.h @@ -0,0 +1,217 @@ +typedef struct Alarm Alarm; +typedef struct Block Block; +typedef struct IMM IMM; +typedef struct Queue Queue; + +typedef struct List { + void *next; +} List; + +typedef struct { + int fake; + int pri; +} Lock; +#define lock(x) +#define unlock(x) + +struct Alarm { + List; + int busy; + long dt; + void (*f)(Alarm*); + void *arg; +}; + +enum { + Eaddrlen = 6, + ETHERMINTU = 60, /* minimum transmit size */ + ETHERMAXTU = 1514, /* maximum transmit size */ + ETHERHDRSIZE = 14, /* size of an ethernet header */ + + MaxEther = 4, +}; + +typedef struct { + uchar d[Eaddrlen]; + uchar s[Eaddrlen]; + uchar type[2]; + uchar data[1500]; + uchar crc[4]; +} Etherpkt; + +extern uchar broadcast[Eaddrlen]; + +enum { + Npart = 20+2, /* 8 sub partitions, disk, and partition */ + Maxxfer = 16*1024, /* maximum transfer size/cmd */ +}; + +typedef struct { + ulong start; + ulong end; + char name[NAMELEN+1]; +} Partition; + +typedef struct { + int online; + int npart; /* number of real partitions */ + Partition p[Npart]; + ulong offset; + Partition *current; /* current partition */ + + ulong cap; /* total bytes */ + int bytes; /* bytes/sector */ + int sectors; /* sectors/track */ + int heads; /* heads/cyl */ + long cyl; /* cylinders/drive */ + + char lba; /* true if drive has logical block addressing */ + char multi; /* non-zero if drive does multiple block xfers */ +} Disc; + +enum { + ScsiTestunit = 0x00, + ScsiExtsens = 0x03, + ScsiInquiry = 0x12, + ScsiModesense = 0x1a, + ScsiStartunit = 0x1B, + ScsiStopunit = 0x1B, + ScsiGetcap = 0x25, + ScsiRead = 0x08, + ScsiWrite = 0x0a, + ScsiExtread = 0x28, + ScsiExtwrite = 0x2a, + + /* data direction */ + ScsiIn = 1, + ScsiOut = 0, +}; + +typedef struct Scsibuf Scsibuf; +typedef struct Scsibuf { + void* virt; + void* phys; + Scsibuf* next; +}; + +typedef struct Scsidata { + uchar* base; + uchar* lim; + uchar* ptr; +} Scsidata; + +typedef struct Ureg Ureg; + +typedef struct Scsi { + ulong pid; + ushort target; + ushort lun; + ushort rflag; + ushort status; + Scsidata cmd; + Scsidata data; + Scsibuf* b; + uchar* save; + uchar cmdblk[16]; +} Scsi; + +typedef struct Segdesc { + ulong d0; + ulong d1; +} Segdesc; + +typedef struct Mach { + ulong ticks; /* of the clock since boot time */ + ulong delayloop; + long cpuhz; /* general system clock (cycles) */ + long clockgen; /* clock generator frequency (cycles) */ + ulong cpupvr; /* cpu type in processor version register */ + ulong cputype; /* cpu variant in BCD (eg, 0x823xx) */ + void* alarm; /* alarms bound to this clock */ + ulong* bcsr; + IMM* iomem; +} Mach; + +/* Mach.cputype */ +#define MPCREV(x) ((x) & 0xFF) +#define MPCMODEL(x) (((x)>>8) & 0xFFF) +#define MPCFAMILY(x) (((x)>>24) & 0x0F) + + +extern Mach *m; + +#define Q_MAGIC ((((4*21)+0)*21)+7) + +typedef struct Exec Exec; +struct Exec +{ + uchar magic[4]; /* magic number */ + uchar text[4]; /* size of text segment */ + uchar data[4]; /* size of initialized data */ + uchar bss[4]; /* size of uninitialized data */ + uchar syms[4]; /* size of symbol table */ + uchar entry[4]; /* entry point */ + uchar spsz[4]; /* size of sp/pc offset table */ + uchar pcsz[4]; /* size of pc/line number table */ +}; + +/* + * bootline passed by boot program + */ +#define BOOTLINE ((char *)0x200000-150) + +/* + * Where we leave configuration info. + */ +#define BOOTARGS ((char*)(0x200000)) +#define BOOTARGSLEN 1024 +#define MAXCONF 32 + +/* + * a parsed plan9.ini line + */ +#define ISAOPTLEN 16 +#define NISAOPT 8 + +typedef struct ISAConf { + char type[NAMELEN]; + ulong port; + ulong irq; + ulong mem; + ulong size; + uchar ea[6]; + + int nopt; + char opt[NISAOPT][ISAOPTLEN]; +} ISAConf; + +typedef struct { + int size; + ulong addr; +} Map; + +typedef struct { + char* name; + Map* map; + Map* mapend; + + Lock; +} RMap; + +typedef struct PCIcfg PCIcfg; + +extern uchar* vgamem; + +struct Block { + uchar *rp; + uchar *wp; + uchar *lim; + uchar *data; + Block* next; + ulong magic; +}; +#define BLEN(b) ((b)->wp-(b)->rp) + +typedef struct QLock { + int dummy; +} QLock; diff --git a/os/boot/mpc/defont0.c b/os/boot/mpc/defont0.c new file mode 100644 index 00000000..9a25e1bd --- /dev/null +++ b/os/boot/mpc/defont0.c @@ -0,0 +1,216 @@ +#include +#include +#include +#include + + + +static ulong bits0[] = { + 0x907070f0, 0xf0f07000, 0xf0888888, 0xf8707070, 0xe0e0e0e0, 0xe09070f0, 0x70f870f0, 0xf870f088, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000000e0, + 0xd0808080, 0x80808800, 0x8888c888, 0x80888888, 0x90909090, 0x90d08080, 0x80808080, 0x80888888, + 0x00000000, 0x08000000, 0x0c300000, 0x00000006, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000003c, 0xc03c0000, + 0x00006000, 0x06001e00, 0x60181860, 0x78000000, 0x00000000, 0x00000000, 0x0000001c, 0x18380090, + 0xb06060e0, 0xe0e0f800, 0xf088a888, 0x80808080, 0x90909090, 0x90b060e0, 0x808060e0, 0x80808888, + 0x00182428, 0x3e707018, 0x18180000, 0x00000006, 0x3c183c3c, 0x1c3e3c7e, 0x3c3c0000, 0x0200403c, + 0x3c187c1e, 0x787e7e1e, 0x663c7c66, 0x6066623c, 0x7c3c7c3c, 0x7e6266c2, 0x66667e30, 0xc00c1000, + 0x08006000, 0x06003000, 0x60181860, 0x18000000, 0x00000000, 0x10000000, 0x00000030, 0x180c0090, + 0x90101080, 0x80808818, 0x88f8a888, 0xe0807070, 0xe0e0e0e0, 0xe0901080, 0x70e01080, 0xe098f088, + 0x00182428, 0x6adad818, 0x18181000, 0x0000000c, 0x66386666, 0x2c3e667e, 0x66660000, 0x06006066, + 0x42186632, 0x6c606032, 0x66181864, 0x60667224, 0x66246666, 0x186262da, 0x62620630, 0x600c3800, + 0x10006000, 0x06003000, 0x60000060, 0x18000000, 0x00000000, 0x30000000, 0x00000030, 0x180c00e0, + 0x00e0e0f0, 0xf0f00018, 0x88889850, 0x80880808, 0x201c1c1c, 0x1c00e0f0, 0x0080e0f0, 0x80888888, + 0x00182428, 0x68dad808, 0x300c5418, 0x0000000c, 0x66580606, 0x2c206002, 0x66661818, 0x0cfe3006, + 0x9e2c6660, 0x66606060, 0x6618186c, 0x60667266, 0x66666660, 0x186262da, 0x36660c30, 0x600c2800, + 0x103c6c3c, 0x3e3c7e3e, 0x6c787866, 0x18d46c3c, 0x6c3e763c, 0x7e6666c2, 0x66667e18, 0x18180000, + 0x44180000, 0x18241c24, 0xf0888820, 0x8070f0f0, 0x20202020, 0x201c243e, 0x1cf8241c, 0x80708870, + 0x0018247c, 0x78745008, 0x300c3818, 0x00000018, 0x66180606, 0x4c206006, 0x76661818, 0x18fe180c, + 0xb62c6660, 0x66606060, 0x66181868, 0x607e5a66, 0x66666470, 0x186266da, 0x34340c30, 0x300c6c00, + 0x18667666, 0x66663066, 0x76181864, 0x18fe7666, 0x76663666, 0x306662da, 0x62620608, 0x1810323c, + 0x44247c7c, 0x24342042, 0x00000000, 0x00000000, 0x20202020, 0x20222408, 0x22002420, 0x00000000, + 0x00180028, 0x3c287610, 0x300cee7e, 0x00fe0018, 0x66180c18, 0x4c3c7c0c, 0x3c3e0000, 0x30000c18, + 0xb62c7c60, 0x667c7c6e, 0x7e181878, 0x605a5a66, 0x6466783c, 0x186234da, 0x18341830, 0x300c4400, + 0x18066660, 0x66663066, 0x66181868, 0x18d66666, 0x66663860, 0x306662da, 0x34620c30, 0x180c5a20, + 0x44241010, 0x242c2042, 0x0e3e103e, 0x3e3c1c3e, 0x3c1c1c1c, 0x1c3e1c08, 0x3e222418, 0x0e0e0e0e, + 0x0008007c, 0x1e5cdc00, 0x300c387e, 0x00fe0030, 0x66181806, 0x7e066618, 0x6e060000, 0x18001818, + 0xb67e6660, 0x66606066, 0x6618186c, 0x605a4e66, 0x78666c0e, 0x1862346c, 0x2c183030, 0x180c4400, + 0x003e6660, 0x667e3066, 0x66181878, 0x18d66666, 0x6666303c, 0x306634da, 0x18341808, 0x18104c38, + 0x3c181010, 0x18241c42, 0x11081008, 0x20222208, 0x00000000, 0x00220408, 0x22361804, 0x11111111, + 0x00000028, 0x16b6cc00, 0x300c5418, 0x00000030, 0x66183006, 0x7e066618, 0x66060000, 0x0cfe3000, + 0x9a466660, 0x66606066, 0x6618186c, 0x605a4e66, 0x60666606, 0x1862346c, 0x6c183030, 0x180c0000, + 0x00666660, 0x66603066, 0x6618186c, 0x18d66666, 0x66663006, 0x3066346c, 0x2c343018, 0x18180020, + 0x00091010, 0x000e0942, 0x10081008, 0x20222208, 0x0f06060f, 0x0a09041e, 0x002a0e38, 0x10101010, + 0x00180028, 0x56b6cc00, 0x300c1018, 0x18001860, 0x66187e66, 0x0c666630, 0x66661818, 0x06fe6018, + 0x40466632, 0x6c606036, 0x66181866, 0x605a4624, 0x60246666, 0x1834186c, 0x46186030, 0x0c0c0000, + 0x006e6666, 0x6e66306e, 0x66181866, 0x18d66666, 0x666e3066, 0x306e186c, 0x46186030, 0x180c003c, + 0x08090909, 0x1f110aff, 0x0e081008, 0x382c2208, 0x08020901, 0x0a0a0911, 0x09220907, 0x0e0e0e0e, + 0x00180028, 0x7c1c7600, 0x18180000, 0x18001860, 0x3c7e7e3c, 0x0c3c3c30, 0x3c3c1818, 0x02004018, + 0x3e467c1e, 0x787e601e, 0x663c1866, 0x7e42463c, 0x603c663c, 0x1818186c, 0x66187e30, 0x0c0c0000, + 0x00367c3c, 0x363c7c36, 0x667e1866, 0x7ed6663c, 0x7c367c3c, 0x1e36186c, 0x66187e30, 0x180c0008, + 0x080f0606, 0x04110c18, 0x01081008, 0x20222208, 0x0e020203, 0x0a0c0d1e, 0x0d220e08, 0x01010101, + 0x00000000, 0x10000000, 0x18180000, 0x080000c0, 0x00000000, 0x00000000, 0x00000008, 0x00000000, + 0x00000000, 0x00000000, 0x00001800, 0x00000000, 0x000c0000, 0x00000000, 0x00000030, 0x060c00fe, + 0x00000000, 0x00000006, 0x00001800, 0x00000000, 0x60060000, 0x00000000, 0x0010001c, 0x18380008, + 0x08090606, 0x040e0a18, 0x11081f08, 0x20221c3e, 0x08020401, 0x0f0a0b11, 0x0b220908, 0x11111111, + 0x00000000, 0x00000000, 0x0c300000, 0x080000c0, 0x00000000, 0x00000000, 0x00000008, 0x00000000, + 0x00000000, 0x00000000, 0x00007000, 0x00000000, 0x00060000, 0x00000000, 0x0000003c, 0x063c0000, + 0x00000000, 0x00000066, 0x00001800, 0x00000000, 0x60060000, 0x00000000, 0x00300000, 0x00000008, + 0x0f090909, 0x04030900, 0x0e000000, 0x00000000, 0x0f0f0f0f, 0x0209091e, 0x09000f07, 0x0e0e0e0e, + 0x00000000, 0x00000000, 0x00000000, 0x10000000, 0x00000000, 0x00000000, 0x00000010, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x0000003c, 0x00007000, 0x00000000, 0x60060000, 0x00000000, 0x00600000, 0x0000000f, + +}; + +static GBitmap strike0 = { + bits0, + 0, + 32, + 0, + {0, 0, 1024, 14}, + {0, 0, 1024, 14}, +}; + +static Fontchar info0[] = { + { 0, 0, 14, 0, 8 }, + { 8, 0, 14, 0, 8 }, + { 16, 0, 14, 0, 8 }, + { 24, 0, 14, 0, 8 }, + { 32, 0, 14, 0, 8 }, + { 40, 0, 14, 0, 8 }, + { 48, 0, 14, 0, 8 }, + { 56, 0, 14, 0, 8 }, + { 64, 0, 14, 0, 8 }, + { 72, 0, 14, 0, 8 }, + { 80, 0, 14, 0, 8 }, + { 88, 0, 14, 0, 8 }, + { 96, 0, 14, 0, 8 }, + { 104, 0, 14, 0, 8 }, + { 112, 0, 14, 0, 8 }, + { 120, 0, 14, 0, 8 }, + { 128, 0, 14, 0, 8 }, + { 136, 0, 14, 0, 8 }, + { 144, 0, 14, 0, 8 }, + { 152, 0, 14, 0, 8 }, + { 160, 0, 14, 0, 8 }, + { 168, 0, 14, 0, 8 }, + { 176, 0, 14, 0, 8 }, + { 184, 0, 14, 0, 8 }, + { 192, 0, 14, 0, 8 }, + { 200, 0, 14, 0, 8 }, + { 208, 0, 14, 0, 8 }, + { 216, 0, 14, 0, 8 }, + { 224, 0, 14, 0, 8 }, + { 232, 0, 14, 0, 8 }, + { 240, 0, 14, 0, 8 }, + { 248, 0, 14, 0, 8 }, + { 256, 0, 0, 0, 8 }, + { 264, 2, 11, 0, 8 }, + { 272, 2, 6, 0, 8 }, + { 280, 2, 11, 0, 8 }, + { 288, 1, 12, 0, 8 }, + { 296, 2, 11, 0, 8 }, + { 304, 2, 11, 0, 8 }, + { 312, 2, 7, 0, 8 }, + { 320, 1, 13, 0, 8 }, + { 328, 1, 13, 0, 8 }, + { 336, 3, 10, 0, 8 }, + { 344, 4, 10, 0, 8 }, + { 352, 9, 14, 0, 8 }, + { 360, 6, 8, 0, 8 }, + { 368, 9, 11, 0, 8 }, + { 376, 1, 13, 0, 8 }, + { 384, 2, 11, 0, 8 }, + { 392, 2, 11, 0, 8 }, + { 400, 2, 11, 0, 8 }, + { 408, 2, 11, 0, 8 }, + { 416, 2, 11, 0, 8 }, + { 424, 2, 11, 0, 8 }, + { 432, 2, 11, 0, 8 }, + { 440, 2, 11, 0, 8 }, + { 448, 2, 11, 0, 8 }, + { 456, 2, 11, 0, 8 }, + { 464, 4, 11, 0, 8 }, + { 472, 4, 14, 0, 8 }, + { 480, 2, 11, 0, 8 }, + { 488, 4, 10, 0, 8 }, + { 496, 2, 11, 0, 8 }, + { 504, 2, 11, 0, 8 }, + { 512, 2, 11, 0, 8 }, + { 520, 2, 11, 0, 8 }, + { 528, 2, 11, 0, 8 }, + { 536, 2, 11, 0, 8 }, + { 544, 2, 11, 0, 8 }, + { 552, 2, 11, 0, 8 }, + { 560, 2, 11, 0, 8 }, + { 568, 2, 11, 0, 8 }, + { 576, 2, 11, 0, 8 }, + { 584, 2, 11, 0, 8 }, + { 592, 2, 13, 0, 8 }, + { 600, 2, 11, 0, 8 }, + { 608, 2, 11, 0, 8 }, + { 616, 2, 11, 0, 8 }, + { 624, 2, 11, 0, 8 }, + { 632, 2, 11, 0, 8 }, + { 640, 2, 11, 0, 8 }, + { 648, 2, 13, 0, 8 }, + { 656, 2, 11, 0, 8 }, + { 664, 2, 11, 0, 8 }, + { 672, 2, 11, 0, 8 }, + { 680, 2, 11, 0, 8 }, + { 688, 2, 11, 0, 8 }, + { 696, 2, 11, 0, 8 }, + { 704, 2, 11, 0, 8 }, + { 712, 2, 11, 0, 8 }, + { 720, 2, 11, 0, 8 }, + { 728, 1, 13, 0, 8 }, + { 736, 1, 13, 0, 8 }, + { 744, 1, 13, 0, 8 }, + { 752, 2, 8, 0, 8 }, + { 760, 11, 12, 0, 8 }, + { 768, 2, 7, 0, 8 }, + { 776, 4, 11, 0, 8 }, + { 784, 1, 11, 0, 8 }, + { 792, 4, 11, 0, 8 }, + { 800, 1, 11, 0, 8 }, + { 808, 4, 11, 0, 8 }, + { 816, 1, 11, 0, 8 }, + { 824, 4, 14, 0, 8 }, + { 832, 1, 11, 0, 8 }, + { 840, 1, 11, 0, 8 }, + { 848, 1, 14, 0, 8 }, + { 856, 1, 11, 0, 8 }, + { 864, 1, 11, 0, 8 }, + { 872, 4, 11, 0, 8 }, + { 880, 4, 11, 0, 8 }, + { 888, 4, 11, 0, 8 }, + { 896, 4, 14, 0, 8 }, + { 904, 4, 14, 0, 8 }, + { 912, 4, 11, 0, 8 }, + { 920, 4, 11, 0, 8 }, + { 928, 2, 11, 0, 8 }, + { 936, 4, 11, 0, 8 }, + { 944, 4, 11, 0, 8 }, + { 952, 4, 11, 0, 8 }, + { 960, 4, 11, 0, 8 }, + { 968, 4, 14, 0, 8 }, + { 976, 4, 11, 0, 8 }, + { 984, 1, 12, 0, 8 }, + { 992, 1, 12, 0, 8 }, + { 1000, 1, 12, 0, 8 }, + { 1008, 5, 8, 0, 8 }, + { 1016, 0, 14, 0, 8 }, + { 1024, 0, 14, 0, 8 }, + { 0, 0, 0, 0, 0 } +}; + +GSubfont defont0 = { + 129, + 14, + 2, + info0, + &strike0, +}; diff --git a/os/boot/mpc/devether.c b/os/boot/mpc/devether.c new file mode 100644 index 00000000..5c5ac792 --- /dev/null +++ b/os/boot/mpc/devether.c @@ -0,0 +1,157 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "etherif.h" + +static Ctlr ether[MaxEther]; + +static struct { + char *type; + int (*reset)(Ctlr*); +} cards[] = { + { "SCC", sccethreset, }, + { "SCC2", sccethreset, }, + { 0, } +}; + +int +etherinit(void) +{ + Ctlr *ctlr; + int ctlrno, i, mask, n; + + mask = 0; + for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){ + ctlr = ðer[ctlrno]; + memset(ctlr, 0, sizeof(Ctlr)); + if(isaconfig("ether", ctlrno, &ctlr->card) == 0) + continue; + for(n = 0; cards[n].type; n++){ + if(strcmp(cards[n].type, ctlr->card.type)) + continue; + ctlr->ctlrno = ctlrno; + if((*cards[n].reset)(ctlr)) + break; + + ctlr->iq = qopen(16*1024, 1, 0, 0); + ctlr->oq = qopen(16*1024, 1, 0, 0); + + ctlr->present = 1; + mask |= 1<ctlrno, ctlr->card.type, ctlr->card.port, ctlr->card.irq); + if(ctlr->card.mem) + print(" addr 0x%luX", PADDR(ctlr->card.mem)); + if(ctlr->card.size) + print(" size 0x%luX", ctlr->card.size); + print(":"); + for(i = 0; i < sizeof(ctlr->card.ea); i++) + print(" %2.2uX", ctlr->card.ea[i]); + print("\n"); uartwait(); + setvec(VectorPIC + ctlr->card.irq, ctlr->card.intr, ctlr); + break; + } + } + + return mask; +} + +static Ctlr* +attach(int ctlrno) +{ + Ctlr *ctlr; + + if(ctlrno >= MaxEther || ether[ctlrno].present == 0) + return 0; + + ctlr = ðer[ctlrno]; + if(ctlr->present == 1){ + ctlr->present = 2; + (*ctlr->card.attach)(ctlr); + } + + return ctlr; +} + +uchar* +etheraddr(int ctlrno) +{ + Ctlr *ctlr; + + if((ctlr = attach(ctlrno)) == 0) + return 0; + + return ctlr->card.ea; +} + +int +etherrxpkt(int ctlrno, Etherpkt *pkt, int timo) +{ + int n; + Ctlr *ctlr; + Block *b; + ulong start; + + if((ctlr = attach(ctlrno)) == 0) + return 0; + + start = m->ticks; + while((b = qget(ctlr->iq)) == 0){ + if(TK2MS(m->ticks - start) >= timo){ + /* + print("ether%d: rx timeout\n", ctlrno); + */ + return 0; + } + } + + n = BLEN(b); + memmove(pkt, b->rp, n); + freeb(b); + + return n; +} + +int +etheriq(Ctlr *ctlr, Block *b, int freebp) +{ + if(memcmp(((Etherpkt*)b->rp)->d, ctlr->card.ea, Eaddrlen) != 0 && + memcmp(((Etherpkt*)b->rp)->d, broadcast, Eaddrlen) != 0){ + if(freebp) + freeb(b); + return 0; + } + qbwrite(ctlr->iq, b); + return 1; +} + +int +ethertxpkt(int ctlrno, Etherpkt *pkt, int len, int) +{ + Ctlr *ctlr; + Block *b; + int s; + + if((ctlr = attach(ctlrno)) == 0) + return 0; + + if(qlen(ctlr->oq) > 16*1024){ + print("ether%d: tx queue full\n", ctlrno); + return 0; + } + b = iallocb(sizeof(Etherpkt)); + memmove(b->wp, pkt, len); + memmove(((Etherpkt*)b->wp)->s, ctlr->card.ea, Eaddrlen); + b->wp += len; + qbwrite(ctlr->oq, b); + s = splhi(); + (*ctlr->card.transmit)(ctlr); + splx(s); + + return 1; +} diff --git a/os/boot/mpc/devuart.c b/os/boot/mpc/devuart.c new file mode 100644 index 00000000..14e38592 --- /dev/null +++ b/os/boot/mpc/devuart.c @@ -0,0 +1,230 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +/* + * SMC1 in UART mode + */ + +typedef struct Uartsmc Uartsmc; +struct Uartsmc { + IOCparam; + ushort maxidl; + ushort idlc; + ushort brkln; + ushort brkec; + ushort brkcr; + ushort rmask; +}; + +typedef struct Uart Uart; +struct Uart +{ + int port; + int setup; + uchar txbusy; + + Queue* iq; + Queue* oq; + void (*rx)(Queue*, int); + void (*boot)(uchar*, int); + + ulong frame; + ulong overrun; + uchar rxbuf[128]; + char txbuf[16]; + BD* rxb; + BD* txb; +}; + +Uart uart[1]; +int predawn = 1; + +static void uartintr(Ureg*, void*); +static void uartkick(void*); + +static int +baudgen(int baud) +{ + int d; + + d = ((m->cpuhz/baud)+8)>>4; + if(d >= (1<<12)) + return ((d+15)>>3)|1; + return d<<1; +} + +static void +smcsetup(Uart *up, int baud) +{ + IMM *io; + Uartsmc *p; + BD *bd; + SMC *smc; + + archenableuart(SMC1ID, 0); + io = m->iomem; + io->pbpar |= IBIT(24)|IBIT(25); /* enable SMC1 TX/RX */ + io->pbdir &= ~(IBIT(24)|IBIT(25)); + io->brgc1 = baudgen(baud) | BaudEnable; + io->simode &= ~0xF000; /* SMC1 to NMSI mode, Tx/Rx clocks are BRG1 */ + + bd = bdalloc(1); + p = (Uartsmc*)KADDR(SMC1P); + p->rbase = (ushort)bd; + up->rxb = bd; + bd->status = BDEmpty|BDWrap|BDInt; + bd->length = 0; + bd->addr = PADDR(up->rxbuf); + bd = bdalloc(1); + p->tbase = (ushort)bd; + up->txb = bd; + bd->status = BDWrap|BDInt; + bd->length = 0; + bd->addr = PADDR(up->txbuf); + + cpmop(InitRxTx, SMC1ID, 0); + + /* protocol parameters */ + p->rfcr = 0x18; + p->tfcr = 0x18; + p->mrblr = 1; + p->maxidl = 1; + p->brkln = 0; + p->brkec = 0; + p->brkcr = 1; + smc = IOREGS(0xA80, SMC); + smc->smce = 0xff; /* clear events */ + smc->smcm = 0x17; /* enable all possible interrupts */ + setvec(VectorCPIC+4, uartintr, up); + smc->smcmr = 0x4820; /* 8-bit mode, no parity, 1 stop bit, UART mode, ... */ + smc->smcmr |= 3; /* enable rx/tx */ +} + +static void +uartintr(Ureg*, void *arg) +{ + Uart *up; + int ch, i; + BD *bd; + SMC *smc; + Block *b; + + up = arg; + smc = IOREGS(0xA80, SMC); + smc->smce = 0xff; /* clear all events */ + if((bd = up->rxb) != nil && (bd->status & BDEmpty) == 0){ + if(up->iq != nil && bd->length > 0){ + if(up->boot != nil){ + up->boot(up->rxbuf, bd->length); + }else if(up->rx != nil){ + for(i=0; ilength; i++){ + ch = up->rxbuf[i]; + up->rx(up->iq, ch); + } + }else{ + b = iallocb(bd->length); + memmove(b->wp, up->rxbuf, bd->length); + b->wp += bd->length; + qbwrite(up->iq, b); + } + } + bd->status |= BDEmpty|BDInt; + } else if((bd = up->txb) != nil && (bd->status & BDReady) == 0){ + ch = -1; + if(up->oq) + ch = qbgetc(up->oq); + if(ch != -1){ + up->txbuf[0] = ch; + bd->length = 1; + bd->status |= BDReady; + }else + up->txbusy = 0; + } + /* TO DO: modem status, errors, etc */ +} + +static void +uartkick(void *arg) +{ + Uart *up = arg; + int s, c, i; + + s = splhi(); + while(up->txbusy == 0 && (c = qbgetc(up->oq)) != -1){ + if(predawn){ + while(up->txb->status & BDReady) + ; + } else { + for(i = 0; i < 100; i++){ + if((up->txb->status & BDReady) == 0) + break; + delay(1); + } + } + up->txbuf[0] = c; + up->txb->length = 1; + up->txb->status |= BDReady; + up->txbusy = !predawn; + } + splx(s); +} + +void +uartspecial(int port, int baud, Queue **iq, Queue **oq, void (*rx)(Queue*,int)) +{ + Uart *up = &uart[0]; + + if(up->setup) + return; + up->setup = 1; + + *iq = up->iq = qopen(4*1024, 0, 0, 0); + *oq = up->oq = qopen(16*1024, 0, uartkick, up); + up->rx = rx; + USED(port); + up->port = SMC1ID; + if(baud == 0) + baud = 9600; + smcsetup(up, baud); + /* if using SCCn's UART, would also set DTR and RTS, but SMC doesn't use them */ +} + +void +uartsetboot(void (*f)(uchar*, int)) +{ + uart[0].boot = f; +} + +void +uartputs(char *s, int n) +{ + Uart *up = &uart[0]; + Block *b; + int nl; + char *p; + + nl = 0; + for(p = s; p < s+n; p++) + if(*p == '\n') + nl++; + b = iallocb(n+nl); + while(n--){ + if(*s == '\n') + *b->wp++ = '\r'; + *b->wp++ = *s++; + } + qbwrite(up->oq, b); +} + +void +uartwait(void) +{ + Uart *up = &uart[0]; + + while(up->txbusy) + ; +} diff --git a/os/boot/mpc/dload.c b/os/boot/mpc/dload.c new file mode 100644 index 00000000..c05423a2 --- /dev/null +++ b/os/boot/mpc/dload.c @@ -0,0 +1,103 @@ +#include +#include +#include +#include + +static char *kernelfile = "/power/ipaq"; +ulong crc32(void *buf, int n, ulong crc); + +void +main(int argc, char **argv) +{ + int ifd, n; + char buf[64], reply[1]; + int i, execsize; + Fhdr f; + ulong csum; + + ARGBEGIN{ + }ARGEND + ifd = open(kernelfile, OREAD); + if(ifd < 0){ + fprint(2, "dload: can't open %s: %r\n", kernelfile); + exits("open"); + } + i = 0; + if(crackhdr(ifd, &f) == 0){ + fprint(2, "dload: not an executable file: %r\n"); + exits("format"); + } + if(f.magic != Q_MAGIC){ + fprint(2, "dload: not a powerpc executable\n"); + exits("format"); + } + execsize = f.txtsz + f.datsz + f.txtoff; + seek(ifd, 0, 0); + csum = ~0; + while(execsize > 0 && (n = read(ifd, buf, sizeof(buf))) > 0){ + if(n > execsize) + n = execsize; + for(;;){ + if(write(1, buf, sizeof(buf)) != sizeof(buf)){ /* always writes full buffer */ + fprint(2, "dload: write error: %r\n"); + exits("write"); + } + if(read(0, reply, 1) != 1){ + fprint(2, "dload: bad reply\n"); + exits("read"); + } + if(reply[0] != 'n') + break; + fprint(2, "!"); + } + if(reply[0] != 'y'){ + fprint(2, "dload: bad ack: %c\n", reply[0]); + exits("reply"); + } + if(++i%10 == 0) + fprint(2, "."); + execsize -= n; + } + exits(0); +} + +/* + * from Rob Warnock + */ +static ulong crc32tab[256]; /* initialised on first call to crc32 */ + +enum { + CRC32POLY = 0x04c11db7 /* AUTODIN II, Ethernet, & FDDI */ +}; + +/* + * Build auxiliary table for parallel byte-at-a-time CRC-32. + */ +static void +initcrc32(void) +{ + int i, j; + ulong c; + + for(i = 0; i < 256; i++) { + for(c = i << 24, j = 8; j > 0; j--) + if(c & (1<<31)) + c = (c<<1) ^ CRC32POLY; + else + c <<= 1; + crc32tab[i] = c; + } +} + +ulong +crc32(void *buf, int n, ulong crc) +{ + uchar *p; + + if(crc32tab[1] == 0) + initcrc32(); + crc = ~crc; + for(p = buf; --n >= 0;) + crc = (crc << 8) ^ crc32tab[(crc >> 24) ^ *p++]; + return ~crc; +} diff --git a/os/boot/mpc/donprint.c b/os/boot/mpc/donprint.c new file mode 100644 index 00000000..4125e690 --- /dev/null +++ b/os/boot/mpc/donprint.c @@ -0,0 +1,332 @@ +#include "u.h" +#include "lib.h" + +#define PTR sizeof(char*) +#define SHORT sizeof(int) +#define INT sizeof(int) +#define LONG sizeof(long) +#define IDIGIT 30 +#define MAXCON 30 + +#define FLONG (1<<0) +#define FSHORT (1<<1) +#define FUNSIGN (1<<2) + +typedef struct Op Op; +struct Op +{ + char *p; + char *ep; + void *argp; + int f1; + int f2; + int f3; +}; + +static int noconv(Op*); +static int cconv(Op*); +static int dconv(Op*); +static int hconv(Op*); +static int lconv(Op*); +static int oconv(Op*); +static int sconv(Op*); +static int uconv(Op*); +static int xconv(Op*); +static int Xconv(Op*); +static int percent(Op*); + +static +int (*fmtconv[MAXCON])(Op*) = +{ + noconv, + cconv, dconv, hconv, lconv, + oconv, sconv, uconv, xconv, + Xconv, percent, +}; +static +char fmtindex[128] = +{ + ['c'] 1, + ['d'] 2, + ['h'] 3, + ['l'] 4, + ['o'] 5, + ['s'] 6, + ['u'] 7, + ['x'] 8, + ['X'] 9, + ['%'] 10, +}; + +static int convcount = { 11 }; +static int ucase; + +static void +PUT(Op *o, int c) +{ + static int pos; + int opos; + + if(c == '\t'){ + opos = pos; + pos = (opos+8) & ~7; + while(opos++ < pos && o->p < o->ep) + *o->p++ = ' '; + return; + } + if(o->p < o->ep){ + *o->p++ = c; + pos++; + } + if(c == '\n') + pos = 0; +} + +int +fmtinstall(char c, int (*f)(Op*)) +{ + + c &= 0177; + if(fmtindex[c] == 0) { + if(convcount >= MAXCON) + return 1; + fmtindex[c] = convcount++; + } + fmtconv[fmtindex[c]] = f; + return 0; +} + +char* +donprint(char *p, char *ep, char *fmt, void *argp) +{ + int sf1, c; + Op o; + + o.p = p; + o.ep = ep; + o.argp = argp; + +loop: + c = *fmt++; + if(c != '%') { + if(c == 0) { + if(o.p < o.ep) + *o.p = 0; + return o.p; + } + PUT(&o, c); + goto loop; + } + o.f1 = 0; + o.f2 = -1; + o.f3 = 0; + c = *fmt++; + sf1 = 0; + if(c == '-') { + sf1 = 1; + c = *fmt++; + } + while(c >= '0' && c <= '9') { + o.f1 = o.f1*10 + c-'0'; + c = *fmt++; + } + if(sf1) + o.f1 = -o.f1; + if(c != '.') + goto l1; + c = *fmt++; + while(c >= '0' && c <= '9') { + if(o.f2 < 0) + o.f2 = 0; + o.f2 = o.f2*10 + c-'0'; + c = *fmt++; + } +l1: + if(c == 0) + fmt--; + c = (*fmtconv[fmtindex[c&0177]])(&o); + if(c < 0) { + o.f3 |= -c; + c = *fmt++; + goto l1; + } + o.argp = (char*)o.argp + c; + goto loop; +} + +void +strconv(char *o, Op *op, int f1, int f2) +{ + int n, c; + char *p; + + n = strlen(o); + if(f1 >= 0) + while(n < f1) { + PUT(op, ' '); + n++; + } + for(p=o; c = *p++;) + if(f2 != 0) { + PUT(op, c); + f2--; + } + if(f1 < 0) { + f1 = -f1; + while(n < f1) { + PUT(op, ' '); + n++; + } + } +} + +int +numbconv(Op *op, int base) +{ + char b[IDIGIT]; + int i, f, n, r; + long v; + short h; + + f = 0; + switch(op->f3 & (FLONG|FSHORT|FUNSIGN)) { + case FLONG: + v = *(long*)op->argp; + r = LONG; + break; + + case FUNSIGN|FLONG: + v = *(ulong*)op->argp; + r = LONG; + break; + + case FSHORT: + h = *(int*)op->argp; + v = h; + r = SHORT; + break; + + case FUNSIGN|FSHORT: + h = *(int*)op->argp; + v = (ushort)h; + r = SHORT; + break; + + default: + v = *(int*)op->argp; + r = INT; + break; + + case FUNSIGN: + v = *(unsigned*)op->argp; + r = INT; + break; + } + if(!(op->f3 & FUNSIGN) && v < 0) { + v = -v; + f = 1; + } + b[IDIGIT-1] = 0; + for(i = IDIGIT-2;; i--) { + n = (ulong)v % base; + n += '0'; + if(n > '9'){ + n += 'a' - ('9'+1); + if(ucase) + n += 'A'-'a'; + } + b[i] = n; + if(i < 2) + break; + v = (ulong)v / base; + if(op->f2 >= 0 && i >= IDIGIT-op->f2) + continue; + if(v <= 0) + break; + } + if(f) + b[--i] = '-'; + strconv(b+i, op, op->f1, -1); + return r; +} + +static int +noconv(Op *op) +{ + + strconv("***", op, 0, -1); + return 0; +} + +static int +cconv(Op *op) +{ + char b[2]; + + b[0] = *(int*)op->argp; + b[1] = 0; + strconv(b, op, op->f1, -1); + return INT; +} + +static int +dconv(Op *op) +{ + return numbconv(op, 10); +} + +static int +hconv(Op*) +{ + return -FSHORT; +} + +static int +lconv(Op*) +{ + return -FLONG; +} + +static int +oconv(Op *op) +{ + return numbconv(op, 8); +} + +static int +sconv(Op *op) +{ + strconv(*(char**)op->argp, op, op->f1, op->f2); + return PTR; +} + +static int +uconv(Op*) +{ + return -FUNSIGN; +} + +static int +xconv(Op *op) +{ + return numbconv(op, 16); +} + +static int +Xconv(Op *op) +{ + int r; + + ucase = 1; + r = numbconv(op, 16); + ucase = 0; + return r; +} + +static int +percent(Op *op) +{ + + PUT(op, '%'); + return 0; +} diff --git a/os/boot/mpc/dosboot.c b/os/boot/mpc/dosboot.c new file mode 100644 index 00000000..cd8d1276 --- /dev/null +++ b/os/boot/mpc/dosboot.c @@ -0,0 +1,614 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "dosfs.h" + +extern char *premature; + +/* + * predeclared + */ +static void bootdump(Dosboot*); +static void setname(Dosfile*, char*); +long dosreadseg(Dosfile*, long, long); + +/* + * debugging + */ +#define chatty 1 +#define chat if(chatty)print + +/* + * block io buffers + */ +enum +{ + Nbio= 16, +}; +typedef struct Clustbuf Clustbuf; +struct Clustbuf +{ + int age; + long sector; + uchar *iobuf; + Dos *dos; + int size; +}; +Clustbuf bio[Nbio]; + +/* + * get an io block from an io buffer + */ +Clustbuf* +getclust(Dos *dos, long sector) +{ + Clustbuf *p, *oldest; + int size; + + chat("getclust @ %d\n", sector); + + /* + * if we have it, just return it + */ + for(p = bio; p < &bio[Nbio]; p++){ + if(sector == p->sector && dos == p->dos){ + p->age = m->ticks; + chat("getclust %d in cache\n", sector); + return p; + } + } + + /* + * otherwise, reuse the oldest entry + */ + oldest = bio; + for(p = &bio[1]; p < &bio[Nbio]; p++){ + if(p->age <= oldest->age) + oldest = p; + } + p = oldest; + + /* + * make sure the buffer is big enough + */ + size = dos->clustsize*dos->sectsize; + if(p->iobuf==0 || p->size < size) + p->iobuf = ialloc(size, 0); + p->size = size; + + /* + * read in the cluster + */ + chat("getclust addr %d\n", (sector+dos->start)*dos->sectsize); + if((*dos->seek)(dos->dev, (sector+dos->start)*dos->sectsize) < 0){ + chat("can't seek block\n"); + return 0; + } + if((*dos->read)(dos->dev, p->iobuf, size) != size){ + chat("can't read block\n"); + return 0; + } + + p->age = m->ticks; + p->dos = dos; + p->sector = sector; + chat("getclust %d read\n", sector); + return p; +} + +/* + * walk the fat one level ( n is a current cluster number ). + * return the new cluster number or -1 if no more. + */ +static long +fatwalk(Dos *dos, int n) +{ + ulong k, sect; + Clustbuf *p; + int o; + + chat("fatwalk %d\n", n); + + if(n < 2 || n >= dos->fatclusters) + return -1; + + switch(dos->fatbits){ + case 12: + k = (3*n)/2; break; + case 16: + k = 2*n; break; + default: + return -1; + } + if(k >= dos->fatsize*dos->sectsize) + panic("getfat"); + + sect = (k/(dos->sectsize*dos->clustsize))*dos->clustsize + dos->fataddr; + o = k%(dos->sectsize*dos->clustsize); + p = getclust(dos, sect); + k = p->iobuf[o++]; + if(o >= dos->sectsize*dos->clustsize){ + p = getclust(dos, sect+dos->clustsize); + o = 0; + } + k |= p->iobuf[o]<<8; + if(dos->fatbits == 12){ + if(n&1) + k >>= 4; + else + k &= 0xfff; + if(k >= 0xff8) + k |= 0xf000; + } + k = k < 0xfff8 ? k : -1; + chat("fatwalk %d -> %d\n", n, k); + return k; +} + +/* + * map a file's logical cluster address to a physical sector address + */ +static long +fileaddr(Dosfile *fp, long ltarget) +{ + Dos *dos = fp->dos; + long l; + long p; + + chat("fileaddr %8.8s %d\n", fp->name, ltarget); + /* + * root directory is contiguous and easy + */ + if(fp->pstart == 0){ + if(ltarget*dos->sectsize*dos->clustsize >= dos->rootsize*sizeof(Dosdir)) + return -1; + l = dos->rootaddr + ltarget*dos->clustsize; + chat("fileaddr %d -> %d\n", ltarget, l); + return l; + } + + /* + * anything else requires a walk through the fat + */ + if(ltarget >= fp->lcurrent && fp->pcurrent){ + /* start at the currrent point */ + l = fp->lcurrent; + p = fp->pcurrent; + } else { + /* go back to the beginning */ + l = 0; + p = fp->pstart; + } + while(l != ltarget){ + /* walk the fat */ + p = fatwalk(dos, p); + if(p < 0) + return -1; + l++; + } + fp->lcurrent = l; + fp->pcurrent = p; + + /* + * clusters start at 2 instead of 0 (why? - presotto) + */ + l = dos->dataaddr + (p-2)*dos->clustsize; + chat("fileaddr %d -> %d\n", ltarget, l); + return l; +} + +/* + * read from a dos file + */ +long +dosread(Dosfile *fp, void *a, long n) +{ + long addr; + long rv; + int i; + int off; + Clustbuf *p; + uchar *from, *to; + + if((fp->attr & DDIR) == 0){ + if(fp->offset >= fp->length) + return 0; + if(fp->offset+n > fp->length) + n = fp->length - fp->offset; + } + + to = a; + for(rv = 0; rv < n; rv+=i){ + /* + * read the cluster + */ + addr = fileaddr(fp, fp->offset/fp->dos->clustbytes); + if(addr < 0) + return -1; + p = getclust(fp->dos, addr); + if(p == 0) + return -1; + + /* + * copy the bytes we need + */ + off = fp->offset % fp->dos->clustbytes; + from = &p->iobuf[off]; + i = n - rv; + if(i > fp->dos->clustbytes - off) + i = fp->dos->clustbytes - off; + memmove(to, from, i); + to += i; + fp->offset += i; + } + + return rv; +} + +/* + * walk a directory returns + * -1 if something went wrong + * 0 if not found + * 1 if found + */ +int +doswalk(Dosfile *file, char *name) +{ + Dosdir d; + long n; + + if((file->attr & DDIR) == 0){ + chat("walking non-directory!\n"); + return -1; + } + + setname(file, name); + + file->offset = 0; /* start at the beginning */ + while((n = dosread(file, &d, sizeof(d))) == sizeof(d)){ + chat("comparing to %8.8s.%3.3s\n", d.name, d.ext); + if(memcmp(file->name, d.name, sizeof(d.name)) != 0) + continue; + if(memcmp(file->ext, d.ext, sizeof(d.ext)) != 0) + continue; + if(d.attr & DVLABEL){ + chat("%8.8s.%3.3s is a LABEL\n", d.name, d.ext); + continue; + } + file->attr = d.attr; + file->pstart = GSHORT(d.start); + file->length = GLONG(d.length); + file->pcurrent = 0; + file->lcurrent = 0; + file->offset = 0; + return 1; + } + return n >= 0 ? 0 : -1; +} + + +/* + * instructions that boot blocks can start with + */ +#define JMPSHORT 0xeb +#define JMPNEAR 0xe9 + +/* + * read dos file system properties + */ +int +dosinit(Dos *dos, int start, int ishard) +{ + Dosboot *b; + int i; + Clustbuf *p; + Dospart *dp; + ulong mbroffset, offset; + + /* defaults till we know better */ + dos->start = start; + dos->sectsize = 512; + dos->clustsize = 1; + mbroffset = 0; + +dmddo: + /* get first sector */ + p = getclust(dos, mbroffset); + if(p == 0){ + chat("can't read boot block\n"); + return -1; + } + + /* + * If it's a hard disc then look for an MBR and pick either an + * active partition or the FAT with the lowest starting LBA. + * Things are tricky because we could be pointing to, amongst others: + * 1) a floppy BPB; + * 2) a hard disc MBR; + * 3) a hard disc extended partition table; + * 4) a logical drive on a hard disc; + * 5) a disc-manager boot block. + * They all have the same magic at the end of the block. + */ + if(p->iobuf[0x1FE] != 0x55 || p->iobuf[0x1FF] != 0xAA) { + chat("not DOS\n"); + return -1; + } + p->dos = 0; + b = (Dosboot *)p->iobuf; + if(ishard && b->mediadesc != 0xF8){ + dp = (Dospart*)&p->iobuf[0x1BE]; + offset = 0xFFFFFFFF; + for(i = 0; i < 4; i++, dp++){ + if(dp->type == DMDDO){ + mbroffset = 63; + goto dmddo; + } + if(dp->type != FAT12 && dp->type != FAT16 && dp->type != FATHUGE) + continue; + if(dp->flag & 0x80){ + offset = GLONG(dp->start); + break; + } + if(GLONG(dp->start) < offset) + offset = GLONG(dp->start); + } + if(i != 4 || offset != 0xFFFFFFFF){ + dos->start = mbroffset+offset; + p = getclust(dos, 0); + if(p == 0 || p->iobuf[0x1FE] != 0x55 || p->iobuf[0x1FF] != 0xAA) + return -1; + } + p->dos = 0; + } + + b = (Dosboot *)p->iobuf; + if(b->magic[0] != JMPNEAR && (b->magic[0] != JMPSHORT || b->magic[2] != 0x90)){ + chat("no dos file system\n"); + return -1; + } + + if(chatty) + bootdump(b); + + /* + * determine the systems' wondersous properties + */ + dos->sectsize = GSHORT(b->sectsize); + dos->clustsize = b->clustsize; + dos->clustbytes = dos->sectsize*dos->clustsize; + dos->nresrv = GSHORT(b->nresrv); + dos->nfats = b->nfats; + dos->rootsize = GSHORT(b->rootsize); + dos->volsize = GSHORT(b->volsize); + if(dos->volsize == 0) + dos->volsize = GLONG(b->bigvolsize); + dos->mediadesc = b->mediadesc; + dos->fatsize = GSHORT(b->fatsize); + dos->fataddr = dos->nresrv; + dos->rootaddr = dos->fataddr + dos->nfats*dos->fatsize; + i = dos->rootsize*sizeof(Dosdir) + dos->sectsize - 1; + i = i/dos->sectsize; + dos->dataaddr = dos->rootaddr + i; + dos->fatclusters = 2+(dos->volsize - dos->dataaddr)/dos->clustsize; + if(dos->fatclusters < 4087) + dos->fatbits = 12; + else + dos->fatbits = 16; + dos->freeptr = 2; + + /* + * set up the root + */ + dos->root.dos = dos; + dos->root.pstart = 0; + dos->root.pcurrent = dos->root.lcurrent = 0; + dos->root.offset = 0; + dos->root.attr = DDIR; + dos->root.length = dos->rootsize*sizeof(Dosdir); + + return 0; +} + +static void +bootdump(Dosboot *b) +{ + if(chatty == 0) + return; + print("magic: 0x%2.2x 0x%2.2x 0x%2.2x\n", + b->magic[0], b->magic[1], b->magic[2]); + print("version: \"%8.8s\"\n", b->version); + print("sectsize: %d\n", GSHORT(b->sectsize)); + print("allocsize: %d\n", b->clustsize); + print("nresrv: %d\n", GSHORT(b->nresrv)); + print("nfats: %d\n", b->nfats); + print("rootsize: %d\n", GSHORT(b->rootsize)); + print("volsize: %d\n", GSHORT(b->volsize)); + print("mediadesc: 0x%2.2x\n", b->mediadesc); + print("fatsize: %d\n", GSHORT(b->fatsize)); + print("trksize: %d\n", GSHORT(b->trksize)); + print("nheads: %d\n", GSHORT(b->nheads)); + print("nhidden: %d\n", GLONG(b->nhidden)); + print("bigvolsize: %d\n", GLONG(b->bigvolsize)); + print("driveno: %d\n", b->driveno); + print("reserved0: 0x%2.2x\n", b->reserved0); + print("bootsig: 0x%2.2x\n", b->bootsig); + print("volid: 0x%8.8x\n", GLONG(b->volid)); + print("label: \"%11.11s\"\n", b->label); +} + +/* + * grab next element from a path, return the pointer to unprocessed portion of + * path. + */ +static char * +nextelem(char *path, char *elem) +{ + int i; + + while(*path == '/') + path++; + if(*path==0 || *path==' ') + return 0; + for(i=0; *path!='\0' && *path!='/' && *path!=' '; i++){ + if(i==28){ + print("name component too long\n"); + return 0; + } + *elem++ = *path++; + } + *elem = '\0'; + return path; +} + +int +dosstat(Dos *dos, char *path, Dosfile *f) +{ + char element[NAMELEN]; + + *f = dos->root; + while(path = nextelem(path, element)){ + switch(doswalk(f, element)){ + case -1: + return -1; + case 0: + return 0; + } + } + return 1; +} + +/* + * boot + */ +int +dosboot(Dos *dos, char *path) +{ + Dosfile file; + long n; + long addr; + Exec *ep; + void (*b)(void); + + switch(dosstat(dos, path, &file)){ + + case -1: + print("error walking to %s\n", path); + return -1; + case 0: + print("%s not found\n", path); + return -1; + case 1: + print("found %8.8s.%3.3s attr 0x%ux start 0x%lux len %d\n", file.name, + file.ext, file.attr, file.pstart, file.length); + break; + } + + /* + * read header + */ + ep = (Exec*)ialloc(sizeof(Exec), 0); + n = sizeof(Exec); + if(dosreadseg(&file, n, (ulong) ep) != n){ + print(premature); + return -1; + } + if(GLLONG(ep->magic) != Q_MAGIC){ + print("bad magic 0x%lux not a plan 9 executable!\n", GLLONG(ep->magic)); + return -1; + } + + /* + * read text + */ + addr = PADDR(GLLONG(ep->entry)); + n = GLLONG(ep->text); + print("+%d", n); + if(dosreadseg(&file, n, addr) != n){ + print(premature); + return -1; + } + + /* + * read data (starts at first page after kernel) + */ + addr = PGROUND(addr+n); + n = GLLONG(ep->data); + print("+%d", n); + if(dosreadseg(&file, n, addr) != n){ + print(premature); + return -1; + } + + /* + * bss and entry point + */ + print("+%d\nstart at 0x%lux\n", GLLONG(ep->bss), GLLONG(ep->entry)); + + /* + * Go to new code. It's up to the program to get its PC relocated to + * the right place. + */ + b = (void (*)(void))(PADDR(GLLONG(ep->entry))); + (*b)(); + return 0; +} + +/* + * read in a segment + */ +long +dosreadseg(Dosfile *fp, long len, long addr) +{ + char *a; + long n, sofar; + + a = (char *)addr; + for(sofar = 0; sofar < len; sofar += n){ + n = 8*1024; + if(len - sofar < n) + n = len - sofar; + n = dosread(fp, a + sofar, n); + if(n <= 0) + break; + print("."); + } + return sofar; +} + +/* + * set up a dos file name + */ +static void +setname(Dosfile *fp, char *from) +{ + char *to; + + to = fp->name; + for(; *from && to-fp->name < 8; from++, to++){ + if(*from == '.'){ + from++; + break; + } + if(*from >= 'a' && *from <= 'z') + *to = *from + 'A' - 'a'; + else + *to = *from; + } + while(to - fp->name < 8) + *to++ = ' '; + + to = fp->ext; + for(; *from && to-fp->ext < 3; from++, to++){ + if(*from >= 'a' && *from <= 'z') + *to = *from + 'A' - 'a'; + else + *to = *from; + } + while(to-fp->ext < 3) + *to++ = ' '; + + chat("name is %8.8s %3.3s\n", fp->name, fp->ext); +} diff --git a/os/boot/mpc/dosfs.h b/os/boot/mpc/dosfs.h new file mode 100644 index 00000000..a45065a6 --- /dev/null +++ b/os/boot/mpc/dosfs.h @@ -0,0 +1,110 @@ +typedef struct Dosboot Dosboot; +typedef struct Dos Dos; +typedef struct Dosdir Dosdir; +typedef struct Dosfile Dosfile; +typedef struct Dospart Dospart; + +struct Dospart +{ + uchar flag; /* active flag */ + uchar shead; /* starting head */ + uchar scs[2]; /* starting cylinder/sector */ + uchar type; /* partition type */ + uchar ehead; /* ending head */ + uchar ecs[2]; /* ending cylinder/sector */ + uchar start[4]; /* starting sector */ + uchar len[4]; /* length in sectors */ +}; + +#define FAT12 0x01 +#define FAT16 0x04 +#define FATHUGE 0x06 +#define DMDDO 0x54 + +struct Dosboot{ + uchar magic[3]; + uchar version[8]; + uchar sectsize[2]; + uchar clustsize; + uchar nresrv[2]; + uchar nfats; + uchar rootsize[2]; + uchar volsize[2]; + uchar mediadesc; + uchar fatsize[2]; + uchar trksize[2]; + uchar nheads[2]; + uchar nhidden[4]; + uchar bigvolsize[4]; + uchar driveno; + uchar reserved0; + uchar bootsig; + uchar volid[4]; + uchar label[11]; + uchar reserved1[8]; +}; + +struct Dosfile{ + Dos *dos; /* owning dos file system */ + char name[8]; + char ext[3]; + uchar attr; + long length; + long pstart; /* physical start cluster address */ + long pcurrent; /* physical current cluster address */ + long lcurrent; /* logical current cluster address */ + long offset; +}; + +struct Dos{ + int dev; /* device id */ + long (*read)(int, void*, long); /* read routine */ + long (*seek)(int, long); /* seek routine */ + + int start; /* start of file system */ + int sectsize; /* in bytes */ + int clustsize; /* in sectors */ + int clustbytes; /* in bytes */ + int nresrv; /* sectors */ + int nfats; /* usually 2 */ + int rootsize; /* number of entries */ + int volsize; /* in sectors */ + int mediadesc; + int fatsize; /* in sectors */ + int fatclusters; + int fatbits; /* 12 or 16 */ + long fataddr; /* sector number */ + long rootaddr; + long dataaddr; + long freeptr; + + Dosfile root; +}; + +struct Dosdir{ + uchar name[8]; + uchar ext[3]; + uchar attr; + uchar reserved[10]; + uchar time[2]; + uchar date[2]; + uchar start[2]; + uchar length[4]; +}; + +#define DRONLY 0x01 +#define DHIDDEN 0x02 +#define DSYSTEM 0x04 +#define DVLABEL 0x08 +#define DDIR 0x10 +#define DARCH 0x20 + +extern int chatty; + +extern int dosboot(Dos*, char*); +extern int dosinit(Dos*, int, int); +extern long dosread(Dosfile*, void*, long); +extern int dosstat(Dos*, char*, Dosfile*); +extern int doswalk(Dosfile*, char*); + +extern int plan9ini(Dos*, char*); diff --git a/os/boot/mpc/etherif.h b/os/boot/mpc/etherif.h new file mode 100644 index 00000000..a4e790a6 --- /dev/null +++ b/os/boot/mpc/etherif.h @@ -0,0 +1,59 @@ +/* + * All the goo for PC ethernet cards. + */ +typedef struct Card Card; +typedef struct Type Type; +typedef struct Ctlr Ctlr; + +/* + * Hardware interface. + */ +struct Card { + ISAConf; + + int (*reset)(Ctlr*); + void (*attach)(Ctlr*); + + void *(*read)(Ctlr*, void*, ulong, ulong); + void *(*write)(Ctlr*, ulong, void*, ulong); + + void (*receive)(Ctlr*); + void (*transmit)(Ctlr*); + void (*intr)(Ureg*, void*); + void (*overflow)(Ctlr*); + + uchar bit16; /* true if a 16 bit interface */ + uchar ram; /* true if card has shared memory */ + + ulong dp8390; /* I/O address of 8390 (if any) */ + ulong data; /* I/O data port if no shared memory */ + uchar nxtpkt; /* software bndry */ + uchar tstart; /* 8390 ring addresses */ + uchar pstart; + uchar pstop; + + uchar dummyrr; /* do dummy remote read */ +}; + +/* + * Software controller. + */ +struct Ctlr { + Card card; /* hardware info */ + int ctlrno; + int present; + + Queue* iq; + Queue* oq; + + int inpackets; + int outpackets; + int crcs; /* input crc errors */ + int oerrs; /* output errors */ + int frames; /* framing errors */ + int overflows; /* packet overflows */ + int buffs; /* buffering errors */ +}; + +extern int sccethreset(Ctlr*); +extern int etheriq(Ctlr*, Block*, int); diff --git a/os/boot/mpc/etherscc.c b/os/boot/mpc/etherscc.c new file mode 100644 index 00000000..1e47d473 --- /dev/null +++ b/os/boot/mpc/etherscc.c @@ -0,0 +1,411 @@ +/* + * SCCn ethernet + */ + +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "etherif.h" + +enum { + Nrdre = 32, /* receive descriptor ring entries */ + Ntdre = 4, /* transmit descriptor ring entries */ + + Rbsize = ETHERMAXTU+4, /* ring buffer size (+4 for CRC) */ + Bufsize = (Rbsize+7)&~7, /* aligned */ +}; + +enum { + /* ether-specific Rx BD bits */ + RxMiss= 1<<8, + RxeLG= 1<<5, + RxeNO= 1<<4, + RxeSH= 1<<3, + RxeCR= 1<<2, + RxeOV= 1<<1, + RxeCL= 1<<0, + RxError= (RxeLG|RxeNO|RxeSH|RxeCR|RxeOV|RxeCL), /* various error flags */ + + /* ether-specific Tx BD bits */ + TxPad= 1<<14, /* pad short frames */ + TxTC= 1<<10, /* transmit CRC */ + TxeDEF= 1<<9, + TxeHB= 1<<8, + TxeLC= 1<<7, + TxeRL= 1<<6, + TxeUN= 1<<1, + TxeCSL= 1<<0, + + /* scce */ + RXB= 1<<0, + TXB= 1<<1, + BSY= 1<<2, + RXF= 1<<3, + TXE= 1<<4, + + /* gsmrl */ + ENR= 1<<5, + ENT= 1<<4, + + /* port A */ + RXD1= SIBIT(15), + TXD1= SIBIT(14), + + /* port B */ + RTS1= IBIT(19), + + /* port C */ + CTS1= SIBIT(11), + CD1= SIBIT(10), +}; + +typedef struct Etherparam Etherparam; +struct Etherparam { + SCCparam; + ulong c_pres; /* preset CRC */ + ulong c_mask; /* constant mask for CRC */ + ulong crcec; /* CRC error counter */ + ulong alec; /* alighnment error counter */ + ulong disfc; /* discard frame counter */ + ushort pads; /* short frame PAD characters */ + ushort ret_lim; /* retry limit threshold */ + ushort ret_cnt; /* retry limit counter */ + ushort mflr; /* maximum frame length reg */ + ushort minflr; /* minimum frame length reg */ + ushort maxd1; /* maximum DMA1 length reg */ + ushort maxd2; /* maximum DMA2 length reg */ + ushort maxd; /* rx max DMA */ + ushort dma_cnt; /* rx dma counter */ + ushort max_b; /* max bd byte count */ + ushort gaddr[4]; /* group address filter */ + ulong tbuf0_data0; /* save area 0 - current frm */ + ulong tbuf0_data1; /* save area 1 - current frm */ + ulong tbuf0_rba0; + ulong tbuf0_crc; + ushort tbuf0_bcnt; + ushort paddr[3]; /* physical address LSB to MSB increasing */ + ushort p_per; /* persistence */ + ushort rfbd_ptr; /* rx first bd pointer */ + ushort tfbd_ptr; /* tx first bd pointer */ + ushort tlbd_ptr; /* tx last bd pointer */ + ulong tbuf1_data0; /* save area 0 - next frame */ + ulong tbuf1_data1; /* save area 1 - next frame */ + ulong tbuf1_rba0; + ulong tbuf1_crc; + ushort tbuf1_bcnt; + ushort tx_len; /* tx frame length counter */ + ushort iaddr[4]; /* individual address filter*/ + ushort boff_cnt; /* back-off counter */ + ushort taddr[3]; /* temp address */ +}; + +typedef struct { + SCC* scc; + int port; + int cpm; + + BD* rdr; /* receive descriptor ring */ + void* rrb; /* receive ring buffers */ + int rdrx; /* index into rdr */ + + BD* tdr; /* transmit descriptor ring */ + void* trb; /* transmit ring buffers */ + int tdrx; /* index into tdr */ +} Mot; +static Mot mot[MaxEther]; + +static int sccid[] = {-1, SCC1ID, SCC2ID, SCC3ID, SCC4ID}; +static int sccparam[] = {-1, SCC1P, SCC2P, SCC3P, SCC4P}; +static int sccreg[] = {-1, 0xA00, 0xA20, 0xA40, 0xA60}; +static int sccirq[] = {-1, 0x1E, 0x1D, 0x1C, 0x1B}; + +static void +attach(Ctlr *ctlr) +{ + mot[ctlr->ctlrno].scc->gsmrl |= ENR|ENT; + eieio(); +} + +static void +transmit(Ctlr *ctlr) +{ + int len; + Mot *motp; + Block *b; + BD *tdre; + + motp = &mot[ctlr->ctlrno]; + while(((tdre = &motp->tdr[motp->tdrx])->status & BDReady) == 0){ + b = qget(ctlr->oq); + if(b == 0) + break; + + /* + * Copy the packet to the transmit buffer. + */ + len = BLEN(b); + memmove(KADDR(tdre->addr), b->rp, len); + + /* + * Give ownership of the descriptor to the chip, increment the + * software ring descriptor pointer and tell the chip to poll. + */ + tdre->length = len; + eieio(); + tdre->status = (tdre->status & BDWrap) | BDReady|TxPad|BDInt|BDLast|TxTC; + eieio(); + motp->scc->todr = 1<<15; /* transmit now */ + eieio(); + motp->tdrx = NEXT(motp->tdrx, Ntdre); + + freeb(b); + + } +} + +static void +interrupt(Ureg*, void *ap) +{ + int len, events, status; + Mot *motp; + BD *rdre; + Block *b; + Ctlr *ctlr; + + ctlr = ap; + motp = &mot[ctlr->ctlrno]; + + /* + * Acknowledge all interrupts and whine about those that shouldn't + * happen. + */ + events = motp->scc->scce; + eieio(); + motp->scc->scce = events; + eieio(); + if(events & (TXE|BSY|RXB)) + print("ETHER.SCC#%d: scce = 0x%uX\n", ctlr->ctlrno, events); + //print(" %ux|", events); + /* + * Receiver interrupt: run round the descriptor ring logging + * errors and passing valid receive data up to the higher levels + * until we encounter a descriptor still owned by the chip. + */ + if(events & (RXF|RXB) || 1){ + rdre = &motp->rdr[motp->rdrx]; + while(((status = rdre->status) & BDEmpty) == 0){ + if(status & RxError || (status & (BDFirst|BDLast)) != (BDFirst|BDLast)){ + //if(status & RxBuff) + // ctlr->buffs++; + if(status & (1<<2)) + ctlr->crcs++; + if(status & (1<<1)) + ctlr->overflows++; + //print("eth rx: %ux\n", status); + if(status & RxError) + print("~"); + else if((status & BDLast) == 0) + print("@"); + } + else{ + /* + * We have a packet. Read it into the next + * free ring buffer, if any. + */ + len = rdre->length-4; + if((b = iallocb(len)) != 0){ + memmove(b->wp, KADDR(rdre->addr), len); + b->wp += len; + etheriq(ctlr, b, 1); + } + } + + /* + * Finished with this descriptor, reinitialise it, + * give it back to the chip, then on to the next... + */ + rdre->length = 0; + rdre->status = (rdre->status & BDWrap) | BDEmpty | BDInt; + eieio(); + + motp->rdrx = NEXT(motp->rdrx, Nrdre); + rdre = &motp->rdr[motp->rdrx]; + } + } + + /* + * Transmitter interrupt: handle anything queued for a free descriptor. + */ + if(events & TXB) + transmit(ctlr); + if(events & TXE) + cpmop(RestartTx, motp->cpm, 0); +} + +static void +ringinit(Mot* motp) +{ + int i, x; + + /* + * Initialise the receive and transmit buffer rings. The ring + * entries must be aligned on 16-byte boundaries. + */ + if(motp->rdr == 0) + motp->rdr = bdalloc(Nrdre); + if(motp->rrb == 0) + motp->rrb = ialloc(Nrdre*Bufsize, 0); + x = PADDR(motp->rrb); + for(i = 0; i < Nrdre; i++){ + motp->rdr[i].length = 0; + motp->rdr[i].addr = x; + motp->rdr[i].status = BDEmpty|BDInt; + x += Bufsize; + } + motp->rdr[i-1].status |= BDWrap; + motp->rdrx = 0; + + if(motp->tdr == 0) + motp->tdr = bdalloc(Ntdre); + if(motp->trb == 0) + motp->trb = ialloc(Ntdre*Bufsize, 0); + x = PADDR(motp->trb); + for(i = 0; i < Ntdre; i++){ + motp->tdr[i].addr = x; + motp->tdr[i].length = 0; + motp->tdr[i].status = TxPad|BDInt|BDLast|TxTC; + x += Bufsize; + } + motp->tdr[i-1].status |= BDWrap; + motp->tdrx = 0; +} + +/* + * This follows the MPC823 user guide: section16.9.23.7's initialisation sequence, + * except that it sets the right bits for the MPC823ADS board when SCC2 is used, + * and those for the 860/821 development board for SCC1. + */ +static void +sccsetup(Mot *ctlr, SCC *scc, uchar *ea) +{ + int i, rcs, tcs, w; + Etherparam *p; + IMM *io; + + + i = 2*(ctlr->port-1); + io = ioplock(); + w = (TXD1|RXD1)<papar |= w; /* enable TXDn and RXDn pins */ + io->padir &= ~w; + io->paodr &= ~w; /* not open drain */ + + w = (CD1|CTS1)<pcpar &= ~w; /* enable CLSN (CTSn) and RENA (CDn) */ + io->pcdir &= ~w; + io->pcso |= w; + iopunlock(); + + /* clocks and transceiver control: details depend on the board's wiring */ + archetherenable(ctlr->cpm, &rcs, &tcs); + + sccnmsi(ctlr->port, rcs, tcs); /* connect the clocks */ + + p = (Etherparam*)KADDR(sccparam[ctlr->port]); + memset(p, 0, sizeof(*p)); + p->rfcr = 0x18; + p->tfcr = 0x18; + p->mrblr = Bufsize; + p->rbase = PADDR(ctlr->rdr); + p->tbase = PADDR(ctlr->tdr); + + cpmop(InitRxTx, ctlr->cpm, 0); + + p->c_pres = ~0; + p->c_mask = 0xDEBB20E3; + p->crcec = 0; + p->alec = 0; + p->disfc = 0; + p->pads = 0x8888; + p->ret_lim = 0xF; + p->mflr = Rbsize; + p->minflr = ETHERMINTU+4; + p->maxd1 = Bufsize; + p->maxd2 = Bufsize; + p->p_per = 0; /* only moderate aggression */ + + for(i=0; ipaddr[2-i/2] = (ea[i+1]<<8)|ea[i]; /* it's not the obvious byte order */ + + scc->psmr = (2<<10)|(5<<1); /* 32-bit CRC, ignore 22 bits before SFD */ + scc->dsr = 0xd555; + scc->gsmrh = 0; /* normal operation */ + scc->gsmrl = (1<<28)|(4<<21)|(1<<19)|0xC; /* transmit clock invert, 48 bit preamble, repetitive 10 preamble, ethernet */ + eieio(); + scc->scce = ~0; /* clear all events */ + eieio(); + scc->sccm = TXE | RXF | TXB; /* enable interrupts */ + eieio(); + + io = ioplock(); + w = RTS1<<(ctlr->port-1); /* enable TENA pin (RTSn) */ + io->pbpar |= w; + io->pbdir |= w; + iopunlock(); + + /* gsmrl enable is deferred until attach */ +} + +/* + * Prepare the SCCx ethernet for booting. + */ +int +sccethreset(Ctlr* ctlr) +{ + uchar ea[Eaddrlen]; + Mot *motp; + SCC *scc; + char line[50], def[50]; + + /* + * Since there's no EPROM, insist that the configuration entry + * (see conf.c and flash.c) holds the Ethernet address. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ctlr->card.ea, Eaddrlen) == 0){ + print("no preset Ether address\n"); + for(;;){ + strcpy(def, "00108bf12900"); /* valid MAC address to be used only for initial configuration */ + if(getstr("ether MAC address", line, sizeof(line), def) < 0) + return -1; + if(parseether(ctlr->card.ea, line) >= 0 || ctlr->card.ea[0] == 0xFF) + break; + print("invalid MAC address\n"); + } + } + + scc = IOREGS(sccreg[ctlr->card.port], SCC); + ctlr->card.irq = VectorCPIC+sccirq[ctlr->card.port]; + + motp = &mot[ctlr->ctlrno]; + motp->scc = scc; + motp->port = ctlr->card.port; + motp->cpm = sccid[ctlr->card.port]; + + ringinit(motp); + + sccsetup(motp, scc, ctlr->card.ea); + + /* enable is deferred until attach */ + + ctlr->card.reset = sccethreset; + ctlr->card.attach = attach; + ctlr->card.transmit = transmit; + ctlr->card.intr = interrupt; + + return 0; +} diff --git a/os/boot/mpc/fblt.c b/os/boot/mpc/fblt.c new file mode 100644 index 00000000..5014e35c --- /dev/null +++ b/os/boot/mpc/fblt.c @@ -0,0 +1,531 @@ +#include +#include +#include +#include + +/* + * bitblt operates a 'word' at a time. + * WBITS is the number of bits in a word + * LWBITS=log2(WBITS), + * W2L is the number of words in a long + * WMASK has bits set for the low order word of a long + * WType is a pointer to a word + */ +#ifndef WBITS +#define WBITS 32 +#define LWBITS 5 +#define W2L 1 +#define WMASK ~0UL +typedef ulong *WType; +#endif + +#define DEBUG + +#ifdef TEST +/* + * globals used for testing + */ +int FORCEFORW; +int FORCEBAKW; +GBitmap *curdm, *cursm; +Point curpt; +Rectangle curr; +Fcode curf; +void *mem; +#endif + +static void +gbitexplode(ulong sw, ulong *buf, int sdep, int x) +{ + int j, o, q, n, nw, inc, qinc; + ulong s, dw, pix; + + inc = 1 << sdep; + pix = (1 << inc) - 1; + nw = 1 << x; + n = 32 >> x; + qinc = (nw << sdep) - inc; + for(o = 32 - n; o >= 0; o -= n){ + dw = 0; + s = sw >> o; + q = 0; + for(j = 0; j < n; j += inc){ + dw |= (s & (pix << j)) << q; + q += qinc; + } + for(j = 0; j < x; j++) + dw |= dw << (inc << j); + *buf++ = dw; + } +} + +/* +void +main(void) +{ + ulong buf[128]; + + gbitexplode(0x7777, buf, 0, 3); + exits(0); +} +*/ + +void +gbitblt(GBitmap *dm, Point pt, GBitmap *sm, Rectangle r, Fcode fcode) +{ + int width; /* width in bits of dst */ + int wwidth; /* floor width in words */ + int height; /* height in pixels minus 1 */ + int sdep; /* src ldepth */ + int ddep; /* dst ldepth */ + int deltadep; /* diff between ldepths */ + int sspan; /* words between scanlines in src */ + int dspan; /* words between scanlines in dst */ + int soff; /* bit offset of src start point */ + int sdest; /* bit offset of src start point that matches doff when expanded */ + int doff; /* bit offset of dst start point */ + int delta; /* amount to shift src by */ + int sign; /* of delta */ + ulong *saddr; + ulong *daddr; + ulong *s; + ulong *d; + ulong mask; + ulong tmp; /* temp storage source word */ + ulong sw; /* source word constructed */ + ulong dw; /* dest word fetched */ + ulong lmask; /* affected pixels in leftmost dst word */ + ulong rmask; /* affected pixels in rightmost dst word */ + int i; + int j; + ulong buf[32]; /* for expanding a source */ + ulong *p; /* pointer into buf */ + int spare; /* number of words already converted */ + + +#ifdef TEST + curdm = dm; + cursm = sm; + curpt = pt; + curr = r; + curf = fcode; +#endif + + gbitbltclip(&dm); + + width = r.max.x - r.min.x; + if(width <= 0) + return; + height = r.max.y - r.min.y - 1; + if(height < 0) + return; + + ddep = dm->ldepth; + pt.x <<= ddep; + width <<= ddep; + + sdep = sm->ldepth; + r.min.x <<= sdep; + r.max.x <<= sdep; + + dspan = dm->width * W2L; + sspan = sm->width * W2L; + + daddr = (ulong*)((WType)dm->base + + dm->zero*W2L + pt.y*dspan + + (pt.x >> LWBITS)); + saddr = (ulong*)((WType)sm->base + + sm->zero*W2L + r.min.y*sspan + + (r.min.x >> LWBITS)); + + doff = pt.x & (WBITS - 1); + lmask = WMASK >> doff; + rmask = (WMASK << (WBITS - ((doff+width) & (WBITS-1))))&WMASK; + if(!rmask) + rmask = WMASK; + soff = r.min.x & (WBITS-1); + wwidth = ((pt.x+width-1)>>LWBITS) - (pt.x>>LWBITS); + + if(sm == dm){ +#ifdef TEST + if(!FORCEBAKW && + (FORCEFORW || sm != dm || saddr > daddr || + (saddr == daddr && soff > doff))) + ; + else{ + daddr += height * dspan; + saddr += height * sspan; + sspan -= 2 * W2L * sm->width; + dspan -= 2 * W2L * dm->width; + } +#else + if(r.min.y < pt.y){ /* bottom to top */ + daddr += height * dspan; + saddr += height * sspan; + sspan -= 2 * W2L * sm->width; + dspan -= 2 * W2L * dm->width; + }else if(r.min.y == pt.y && r.min.x < pt.x) + abort()/*goto right*/; +#endif + } + if(wwidth == 0) /* collapse masks for narrow cases */ + lmask &= rmask; + fcode &= F; + + deltadep = ddep - sdep; + sdest = doff >> deltadep; + delta = soff - sdest; + sign = 0; + if(delta < 0){ + sign = 1; + delta = -delta; + } + + p = 0; + for(j = 0; j <= height; j++){ + d = daddr; + s = saddr; + mask = lmask; + tmp = 0; + if(!sign) + tmp = *s++; + spare = 0; + for(i = wwidth; i >= 0; i--){ + if(spare) + sw = *p++; + else{ + if(sign){ + sw = tmp << (WBITS-delta); + tmp = *s++; + sw |= tmp >> delta; + }else{ + sw = tmp << delta; + tmp = *s++; + if(delta) + sw |= tmp >> (WBITS-delta); + } + spare = 1 << deltadep; + if(deltadep >= 1){ + gbitexplode(sw, buf, sdep, deltadep); + p = buf; + sw = *p++; + } + } + + dw = *d; + switch(fcode){ /* ltor bit aligned */ + case Zero: *d = dw & ~mask; break; + case DnorS: *d = dw ^ ((~sw | dw) & mask); break; + case DandnotS: *d = dw ^ ((sw & dw) & mask); break; + case notS: *d = dw ^ ((~sw ^ dw) & mask); break; + case notDandS: *d = dw ^ ((sw | dw) & mask); break; + case notD: *d = dw ^ mask; break; + case DxorS: *d = dw ^ (sw & mask); break; + case DnandS: *d = dw ^ ((sw | ~dw) & mask); break; + case DandS: *d = dw ^ ((~sw & dw) & mask); break; + case DxnorS: *d = dw ^ (~sw & mask); break; + case D: break; + case DornotS: *d = dw | (~sw & mask); break; + case S: *d = dw ^ ((sw ^ dw) & mask); break; + case notDorS: *d = dw ^ (~(sw & dw) & mask); break; + case DorS: *d = dw | (sw & mask); break; + case F: *d = dw | mask; break; + } + d++; + + mask = WMASK; + if(i == 1) + mask = rmask; + spare--; + } + saddr += sspan; + daddr += dspan; + } +} + +#ifdef TEST +void prprog(void); +GBitmap *bb1, *bb2; +ulong *src, *dst, *xdst, *xans; +int swds, dwds; +long ticks; +int timeit; + +long +func(int f, long s, int sld, long d, int dld) +{ + long a; + int sh, i, db, sb; + + db = 1 << dld; + sb = 1 << sld; + sh = db - sb; + if(sh > 0) { + a = s; + for(i = sb; i>= -sh; + + switch(f){ + case Zero: d = 0; break; + case DnorS: d = ~(d|s); break; + case DandnotS: d = d & ~s; break; + case notS: d = ~s; break; + case notDandS: d = ~d & s; break; + case notD: d = ~d; break; + case DxorS: d = d ^ s; break; + case DnandS: d = ~(d&s); break; + case DandS: d = d & s; break; + case DxnorS: d = ~(d^s); break; + case S: d = s; break; + case DornotS: d = d | ~s; break; + case D: d = d; break; + case notDorS: d = ~d | s; break; + case DorS: d = d | s; break; + case F: d = ~0; break; + } + + d &= ((1<r.min.x; + to += bb1->r.min.x; + fy = bb2->r.min.y + 1; + ty = bb1->r.min.y + 1; + if(timeit) { + memcpy(dst, xdst, dwds * sizeof(long)); + ticks -= *_clock; + gbitblt(bb1, Pt(to,ty), bb2, Rect(fr,fy,fr+w,fy+2), op); + ticks += *_clock; + return; + } + f = fr; + t = to; + memcpy(dst, xdst, dwds * sizeof(long)); + for(i=0; ibase, bb1->zero, bb1->width, bb1->ldepth, + bb1->r.min.x, bb1->r.min.y, bb1->r.max.x, bb1->r.max.y); + print("src bitmap b %#lux, z %d, w %d, ld %d, r [%d,%d][%d,%d]\n", + bb2->base, bb2->zero, bb2->width, bb2->ldepth, + bb2->r.min.x, bb2->r.min.y, bb2->r.max.x, bb2->r.max.y); + for(j=0; 7*j < dwds; j++) { + print("\ns"); + for(i=0; i<7 && 7*j+i < dwds; i++) + print(" %.8lux", src[7*j + i]); + print("\nd"); + for(i=0; i<7 && 7*j+i < dwds; i++) + print(" %.8lux", xdst[7*j + i]); + print("\ng"); + for(i=0; i<7 && 7*j+i < dwds; i++) + print(" %.8lux", xans[7*j + i]); + print("\nb"); + for(i=0; i<7 && 7*j+i < dwds; i++) + print(" %.8lux", dst[7*j + i]); + print("\n"); + } + prprog(); + } +} + +void +prprog(void) +{ + exits(0); +} + +int +main(int argc, char *argv[]) +{ + int f, t, w, i, sld, dld, op, iters, simple; + ulong s, d, spix, dpix, apix, fpix, m, *ps, *pd; + Point sorg, dorg; + GBitmap *bs, *bd; + long seed; + char *ct; + + sld = 0; + dld = 0; + timeit = 0; + iters = 200; + simple = 0; + ARGBEGIN { + case 'i': + iters = atoi(ARGF()); + break; + case 's': + simple = 1; + break; + case 't': + timeit = 1; + ct = ARGF(); + if(ct) + iters = atoi(ct); + break; + } ARGEND + if(argc > 0) + sld = atoi(argv[0]); + if(argc > 1) + dld = atoi(argv[1]); + if(!timeit && !simple) { + seed = time(0); + print("seed %lux\n", seed); srand(seed); /**/ + } + + print("sld %d dld %d\n", sld, dld); + op = 1; + + /* bitmaps for 1-bit tests */ + bd = gballoc(Rect(0,0,32,1), dld); + bs = gballoc(Rect(0,0,32,1), sld); + for(i=0; iwidth; i++) + bs->base[i] = lrand(); + + /* bitmaps for rect tests */ + if(simple) { + dorg = Pt(0,0); + sorg = Pt(0,0); + } else { + dorg = Pt(nrand(63)-31,nrand(63)-31); + sorg = Pt(nrand(63)-31,nrand(63)-31); + } + bb1 = gballoc(Rpt(dorg,add(dorg,Pt(200,4))), dld); + bb2 = gballoc(Rpt(sorg,add(sorg,Pt(200,4))), sld); + dwds = bb1->width * Dy(bb1->r); + swds = bb2->width * Dy(bb2->r); + dst = bb1->base; + src = bb2->base; + xdst = malloc(dwds * sizeof(long)); + xans = malloc(dwds * sizeof(long)); + for(i=0; ibase; + pd = bd->base; + FORCEFORW = 1; + FORCEBAKW = 0; + for(i=0; i<1000; i++, FORCEFORW = !FORCEFORW, FORCEBAKW = !FORCEBAKW) { + f = nrand(32 >> sld); + t = nrand(32 >> dld); + s = lrand(); + d = lrand(); + ps[0] = s; + pd[0] = d; +#ifdef T386 + spix = (byterev(s) >> (32 - ((f+1)<> (32 - ((t+1)<> (32 - ((f+1)<> (32 - ((t+1)<= 0;) + s += *p++; + return s; +} + +static int +validptr(Flalloc *ap, uchar *p) +{ + return p > (uchar*)&end && p < (uchar*)ap; +} + +static int +flashcheck(Flalloc *ap, char **val, int *len) +{ + uchar *base; + int n; + + if(ap->base == Noval || ap->base >= FLASHSEG || ap->tag == Tnone) + return 0; + base = flash.base+ap->base; + if(!validptr(ap, base)) + return 0; + n = (((ap->len[0]<<8)|ap->len[1])<<8)|ap->len[2]; + if(n == 0xFFFFFF) + n = 0; + if(n < 0) + return 0; + if(n > 0 && !validptr(ap, base+n-1)) + return 0; + if(ap->check != Noval && checksum(base, n) != ap->check){ + print("flash: bad checksum\n"); + return 0; + } + *val = (char*)base; + *len = n; + return 1; +} + +int +flashinit(void) +{ + int len; + char *val; + Flalloc *ap; + void *addr; + long mbytes; + char type[20]; + + flash.base = 0; + flash.exec = 0; + flash.size = 0; + if(archflashreset(type, &addr, &mbytes) < 0){ + print("flash: flash not present or not enabled\n"); /* shouldn't happen */ + return 0; + } + flash.size = mbytes; + flash.base = addr; + flash.exec = flash.base + BOOTOFF; + flash.config = nil; + flash.conflen = 0; + + for(ap = (Flalloc*)(flash.base+CONFIGLIM)-1; memcmp(ap->sig, flashsig, 4) == 0; ap--){ + if(0) + print("conf #%8.8lux: #%x #%6.6lux\n", ap, ap->tag, ap->base); + if(ap->tag == Tconf && + flashcheck(ap, &val, &len) && + len >= sizeof(conftag)-1 && + memcmp(val, conftag, sizeof(conftag)-1) == 0){ + flash.config = val; + flash.conflen = len; + if(0) + print("flash: found config %8.8lux(%d):\n%s\n", val, len, val); + } + } + if(flash.config == nil) + print("flash: no config\n"); + else + print("flash config %8.8lux(%d):\n%s\n", flash.config, flash.conflen, flash.config); + if(issqueezed(flash.exec) == Q_MAGIC){ + print("flash: squeezed powerpc kernel installed\n"); + return 1<<0; + } + if(GLLONG(flash.exec) == Q_MAGIC){ + print("flash: unsqueezed powerpc kernel installed\n"); + return 1<<0; + } + flash.exec = 0; + print("flash: no powerpc kernel in Flash\n"); + return 0; +} + +char* +flashconfig(int) +{ + return flash.config; +} + +int +flashbootable(int) +{ + return flash.exec != nil && (issqueezed(flash.exec) || GLLONG(flash.exec) == Q_MAGIC); +} + +int +flashboot(int) +{ + ulong entry, addr; + void (*b)(void); + Exec *ep; + Block in; + long n; + uchar *p; + + if(flash.exec == 0) + return -1; + p = flash.exec; + if(GLLONG(p) == Q_MAGIC){ + /* unsqueezed: copy data and perhaps text, then jump to it */ + ep = (Exec*)p; + entry = PADDR(GLLONG(ep->entry)); + p += sizeof(Exec); + addr = entry; + n = GLLONG(ep->text); + if(addr != (ulong)p){ + memmove((void*)addr, p, n); + print("text: %8.8lux <- %8.8lux [%ld]\n", addr, p, n); + } + p += n; + if(entry >= FLASHMEM) + addr = 3*BY2PG; /* kernel text is in Flash, data in RAM */ + else + addr = PGROUND(addr+n); + n = GLLONG(ep->data); + memmove((void*)addr, p, n); + print("data: %8.8lux <- %8.8lux [%ld]\n", addr, p, n); + }else{ + in.data = p; + in.rp = in.data; + in.lim = p+BOOTLEN; + in.wp = in.lim; + n = unsqueezef(&in, &entry); + if(n < 0) + return -1; + } + print("entry=0x%lux\n", entry); + uartwait(); + scc2stop(); + /* + * Go to new code. It's up to the program to get its PC relocated to + * the right place. + */ + b = (void (*)(void))KADDR(PADDR(entry)); + (*b)(); + return -1; +} diff --git a/os/boot/mpc/fns.h b/os/boot/mpc/fns.h new file mode 100644 index 00000000..5bf5d90e --- /dev/null +++ b/os/boot/mpc/fns.h @@ -0,0 +1,117 @@ +Alarm* alarm(int, void (*)(Alarm*), void*); +void alarminit(void); +void archbacklight(int); +char* archconfig(void); +void archdisableuart(int); +void archenableuart(int, int); +void archenableusb(int); +void archetherdisable(int); +int archetherenable(int, int*, int*); +int archflashreset(char*, void**, long*); +void archinit(void); +int archoptionsw(void); +int bootp(int, char*); +void cancel(Alarm*); +void checkalarms(void); +void clockinit(void); +void clockintr(Ureg*, void*); +void consinit(void); +void cpminit(void); +void cpuidprint(void); +#define dcflush(a,b) +void delay(int); +void eieio(void); +uchar* etheraddr(int); +int etherinit(void); +int etherrxpkt(int, Etherpkt*, int); +int ethertxpkt(int, Etherpkt*, int, int); +void exception(void); +int flashboot(int); +int flashbootable(int); +char* flashconfig(int); +int flashinit(void); +void free(void*); +void freeb(Block*); +int getcfields(char*, char**, int, char*); +char* getconf(char*); +ulong getdec(void); +ulong gethid0(void); +ulong getimmr(void); +ulong getmsr(void); +ulong getpvr(void); +int getstr(char*, char*, int, char*); +ulong gettbl(void); +ulong gettbu(void); +int hardinit(void); +long hardread(int, void*, long); +long hardseek(int, long); +long hardwrite(int, void*, long); +long i2csend(int, void*, long); +void i2csetup(void); +void* ialloc(ulong, int); +Block* iallocb(int); +void idle(void); +int isaconfig(char*, int, ISAConf*); +int issqueezed(uchar*); +void kbdchar(Queue*, int); +void kbdinit(void); +void kbdreset(void); +void machinit(void); +void* malloc(ulong); +ulong mapalloc(RMap*, ulong, int, int); +void mapfree(RMap*, ulong, int); +void mapinit(RMap*, Map*, int); +void meminit(void); +void microdelay(int); +void mmuinit(void); +int optionsw(void); +void panic(char*, ...); +int parseether(uchar*, char*); +int plan9boot(int, long (*)(int, long), long (*)(int, void*, long)); +void putdec(ulong); +void puthid0(ulong); +void putmsr(ulong); +int qbgetc(Queue*); +void qbputc(Queue*, int); +void qbwrite(Queue*, Block*); +Block* qget(Queue*); +long qlen(Queue*); +Queue* qopen(int, int, void (*)(void*), void*); +#define qpass qbwrite +void scc2stop(void); +void sccnmsi(int, int, int); +void sched(void); +void screeninit(void); +void screenputs(char*, int); +void sdraminit(ulong); +Partition* sethardpart(int, char*); +Partition* setscsipart(int, char*); +void setvec(int, void (*)(Ureg*, void*), void*); +int splhi(void); +int spllo(void); +void splx(int); +void trapinit(void); +void uartputs(char*, int); +void uartsetboot(void (*f)(uchar*, int)); +void uartspecial(int, int, Queue**, Queue**, void(*)(Queue*,int)); +void uartwait(void); +long unsqueezef(Block*, ulong*); + +#define GSHORT(p) (((p)[1]<<8)|(p)[0]) +#define GLONG(p) ((GSHORT(p+2)<<16)|GSHORT(p)) +#define GLSHORT(p) (((p)[0]<<8)|(p)[1]) +#define GLLONG(p) ((GLSHORT(p)<<16)|GLSHORT(p+2)) + +#define KADDR(a) ((void*)((ulong)(a)|KZERO)) +#define PADDR(a) ((ulong)(a)&~KSEGM) + +/* IBM bit field order */ +#define IBIT(b) ((ulong)1<<(31-(b))) +#define SIBIT(n) ((ushort)1<<(15-(n))) + +#define IOREGS(x, T) ((T*)((char*)m->iomem+(x))) + +int uartinit(void); +Partition* setuartpart(int, char*); +long uartread(int, void*, long); +long uartseek(int, long); diff --git a/os/boot/mpc/gbitbltclip.c b/os/boot/mpc/gbitbltclip.c new file mode 100644 index 00000000..cbf877f5 --- /dev/null +++ b/os/boot/mpc/gbitbltclip.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include + +void +gbitbltclip(void *vp) +{ + int dx, dy; + int i; + struct{ + GBitmap *dm; + Point p; + GBitmap *sm; + Rectangle r; + Fcode f; + }*bp; + + bp = vp; + dx = Dx(bp->r); + dy = Dy(bp->r); + if(bp->p.x < bp->dm->clipr.min.x){ + i = bp->dm->clipr.min.x-bp->p.x; + bp->r.min.x += i; + bp->p.x += i; + dx -= i; + } + if(bp->p.y < bp->dm->clipr.min.y){ + i = bp->dm->clipr.min.y-bp->p.y; + bp->r.min.y += i; + bp->p.y += i; + dy -= i; + } + if(bp->p.x+dx > bp->dm->clipr.max.x) + bp->r.max.x -= bp->p.x+dx-bp->dm->clipr.max.x; + if(bp->p.y+dy > bp->dm->clipr.max.y) + bp->r.max.y -= bp->p.y+dy-bp->dm->clipr.max.y; + if(bp->r.min.x < bp->sm->clipr.min.x){ + i = bp->sm->clipr.min.x-bp->r.min.x; + bp->p.x += i; + bp->r.min.x += i; + } + if(bp->r.min.y < bp->sm->clipr.min.y){ + i = bp->sm->clipr.min.y-bp->r.min.y; + bp->p.y += i; + bp->r.min.y += i; + } + if(bp->r.max.x > bp->sm->clipr.max.x) + bp->r.max.x = bp->sm->clipr.max.x; + if(bp->r.max.y > bp->sm->clipr.max.y) + bp->r.max.y = bp->sm->clipr.max.y; +} diff --git a/os/boot/mpc/gnot.h b/os/boot/mpc/gnot.h new file mode 100644 index 00000000..7b99e6bb --- /dev/null +++ b/os/boot/mpc/gnot.h @@ -0,0 +1,71 @@ + +extern void *bbmalloc(int); +extern void bbfree(void *, int); +extern int bbonstack(void); +extern void bbexec(void(*)(void), int, int); + +/* + * Graphics types + */ + +typedef struct GBitmap GBitmap; +typedef struct GFont GFont; +typedef struct GSubfont GSubfont; +typedef struct GCacheinfo GCacheinfo; + +struct GBitmap +{ + ulong *base; /* pointer to start of data */ + long zero; /* base+zero=&word containing (0,0) */ + ulong width; /* width in 32 bit words of total data area */ + int ldepth; /* log base 2 of number of bits per pixel */ + Rectangle r; /* rectangle in data area, local coords */ + Rectangle clipr; /* clipping region */ + GBitmap *cache; /* zero; distinguishes bitmap from layer */ +}; + + +/* + * GFont etc. are not used in the library, only in devbit.c. + * GSubfont is only barely used. + */ +struct GSubfont +{ + short n; /* number of chars in font */ + char height; /* height of bitmap */ + char ascent; /* top of bitmap to baseline */ + Fontchar *info; /* n+1 character descriptors */ + GBitmap *bits; /* where the characters are */ +}; +struct GCacheinfo +{ + ulong xright; /* right edge of bits */ + Fontchar; +}; + +struct GFont +{ + uchar height; /* max height of bitmap, interline spacing */ + char ascent; /* top of bitmap to baseline */ + char width; /* widest so far; used in caching only */ + char ldepth; /* of images */ + short id; /* of font */ + int ncache; /* number of entries in cache */ + GCacheinfo *cache; /* cached characters */ + GBitmap *b; /* cached images */ +}; + +extern ulong *gaddr(GBitmap*, Point); +extern uchar *gbaddr(GBitmap*, Point); +extern void gbitblt(GBitmap*, Point, GBitmap*, Rectangle, Fcode); +extern void gbitbltclip(void*); +extern void gtexture(GBitmap*, Rectangle, GBitmap*, Fcode); +extern Point gsubfstrsize(GSubfont*, char*); +extern int gsubfstrwidth(GSubfont*, char*); +extern Point gsubfstring(GBitmap*, Point, GSubfont*, char*, Fcode); +extern Point gbitbltstring(GBitmap*, Point, GSubfont*, char*, Fcode); +extern void gsegment(GBitmap*, Point, Point, int, Fcode); +extern void gpoint(GBitmap*, Point, int, Fcode); +extern void gflushcpucache(void); +extern GBitmap* gballoc(Rectangle, int); +extern void gbfree(GBitmap*); diff --git a/os/boot/mpc/i2c.c b/os/boot/mpc/i2c.c new file mode 100644 index 00000000..1cf9243c --- /dev/null +++ b/os/boot/mpc/i2c.c @@ -0,0 +1,351 @@ +#include "boot.h" + +/* + * basic read/write interface to mpc8xx I2C bus (master mode) + */ + +typedef struct I2C I2C; + +struct I2C { + uchar i2mod; + uchar rsv12a[3]; + uchar i2add; + uchar rsv12b[3]; + uchar i2brg; + uchar rsv12c[3]; + uchar i2com; + uchar rsv12d[3]; + uchar i2cer; + uchar rsv12e[3]; + uchar i2cmr; +}; + +enum { + /* i2c-specific BD flags */ + RxeOV= 1<<1, /* overrun */ + TxS= 1<<10, /* transmit start condition */ + TxeNAK= 1<<2, /* last transmitted byte not acknowledged */ + TxeUN= 1<<1, /* underflow */ + TxeCL= 1<<0, /* collision */ + TxERR= (TxeNAK|TxeUN|TxeCL), + + /* i2cmod */ + REVD= 1<<5, /* =1, LSB first */ + GCD= 1<<4, /* =1, general call address disabled */ + FLT= 1<<3, /* =0, not filtered; =1, filtered */ + PDIV= 3<<1, /* predivisor field */ + EN= 1<<0, /* enable */ + + /* i2com */ + STR= 1<<7, /* start transmit */ + I2CM= 1<<0, /* master */ + I2CS= 0<<0, /* slave */ + + /* i2cer */ + TXE = 1<<4, + BSY = 1<<2, + TXB = 1<<1, + RXB = 1<<0, + + /* port B bits */ + I2CSDA = IBIT(27), + I2CSCL = IBIT(26), + + Rbit = 1<<0, /* bit in address byte denoting read */ + + /* maximum I2C I/O (can change) */ + Bufsize = 64, + Tbuflen= Bufsize+4, /* extra address bytes and alignment */ + Freq = 100000, + I2CTimeout = 250, /* msec */ +}; + +/* data cache needn't be flushed if buffers allocated in uncached INTMEM */ +#define DCFLUSH(a,n) + +/* + * I2C software structures + */ + +struct Ctlr { + Lock; + QLock io; + int init; + I2C* i2c; + IOCparam* sp; + + BD* rd; + BD* td; + int phase; + char* addr; + char* txbuf; + char* rxbuf; +}; +typedef struct Ctlr Ctlr; + +static Ctlr i2ctlr[1]; +extern int predawn; + +static void interrupt(Ureg*, void*); + +static void +enable(void) +{ + I2C *i2c; + + i2c = i2ctlr->i2c; + i2c->i2cer = ~0; /* clear events */ + eieio(); + i2c->i2mod |= EN; + eieio(); + i2c->i2cmr = TXE|BSY|TXB|RXB; /* enable all interrupts */ + eieio(); +} + +static void +disable(void) +{ + I2C *i2c; + + i2c = i2ctlr->i2c; + i2c->i2cmr = 0; /* mask all interrupts */ + i2c->i2mod &= ~EN; +} + +/* + * called by the reset routine of any driver using the I2C + */ +void +i2csetup(void) +{ + IMM *io; + I2C *i2c; + IOCparam *sp; + Ctlr *ctlr; + long f, e, emin; + int p, d, dmax; + + ctlr = i2ctlr; + if(ctlr->init) + return; + print("i2c setup...\n"); + ctlr->init = 1; + i2c = KADDR(INTMEM+0x860); + ctlr->i2c = i2c; + sp = KADDR(INTMEM+0x3c80); + ctlr->sp = sp; + disable(); + + if(ctlr->txbuf == nil){ + ctlr->txbuf = ialloc(Tbuflen, 2); + ctlr->addr = ctlr->txbuf+Bufsize; + } + if(ctlr->rxbuf == nil) + ctlr->rxbuf = ialloc(Bufsize, 2); + if(ctlr->rd == nil){ + ctlr->rd = bdalloc(1); + ctlr->rd->addr = PADDR(ctlr->rxbuf); + ctlr->rd->length = 0; + ctlr->rd->status = BDWrap; + } + if(ctlr->td == nil){ + ctlr->td = bdalloc(2); + ctlr->td->addr = PADDR(ctlr->txbuf); + ctlr->td->length = 0; + ctlr->td->status = BDWrap|BDLast; + } + + /* select port pins */ + io = ioplock(); + io->pbdir |= I2CSDA | I2CSCL; + io->pbodr |= I2CSDA | I2CSCL; + io->pbpar |= I2CSDA | I2CSCL; + iopunlock(); + + /* explicitly initialise parameters, because InitRxTx can't be used (see i2c/spi relocation errata) */ + sp = ctlr->sp; + sp->rbase = PADDR(ctlr->rd); + sp->tbase = PADDR(ctlr->td); + sp->rfcr = 0x18; + sp->tfcr = 0x18; + sp->mrblr = Bufsize; + sp->rstate = 0; + sp->rptr = 0; + sp->rbptr = sp->rbase; + sp->rcnt = 0; + sp->tstate = 0; + sp->tbptr = sp->tbase; + sp->tptr = 0; + sp->tcnt = 0; + eieio(); + + i2c->i2com = I2CM; + i2c->i2mod = 0; /* normal mode */ + i2c->i2add = 0; + + emin = Freq; + dmax = (m->cpuhz/Freq)/2-3; + for(d=0; d < dmax; d++){ + for(p=3; p>=0; p--){ + f = (m->cpuhz>>(p+2))/(2*(d+3)); + e = Freq - f; + if(e < 0) + e = -e; + if(e < emin){ + emin = e; + i2c->i2brg = d; + i2c->i2mod = (i2c->i2mod&~PDIV)|((3-p)<<1); /* set PDIV */ + } + } + } + //print("i2brg=%d i2mod=#%2.2ux\n", i2c->i2brg, i2c->i2mod); + setvec(VectorCPIC+0x10, interrupt, i2ctlr); +} + +enum { + Idling, + Done, + Busy, + Sending, + Recving, +}; + +static void +interrupt(Ureg*, void *arg) +{ + int events; + Ctlr *ctlr; + I2C *i2c; + + ctlr = arg; + i2c = ctlr->i2c; + events = i2c->i2cer; + eieio(); + i2c->i2cer = events; + if(events & (BSY|TXE)){ + //print("I2C#%x\n", events); + if(ctlr->phase != Idling){ + ctlr->phase = Idling; + } + }else{ + if(events & TXB){ + //print("i2c: xmt %d %4.4ux %4.4ux\n", ctlr->phase, ctlr->td->status, ctlr->td[1].status); + if(ctlr->phase == Sending){ + ctlr->phase = Done; + } + } + if(events & RXB){ + //print("i2c: rcv %d %4.4ux %d\n", ctlr->phase, ctlr->rd->status, ctlr->rd->length); + if(ctlr->phase == Recving){ + ctlr->phase = Done; + } + } + } +} + +static int +done(void *a) +{ + return ((Ctlr*)a)->phase < Busy; +} + +static void +i2cwait(Ctlr *ctlr) +{ + /* TO DO: timeout */ + while(!done(ctlr)){ + if(predawn) + interrupt(nil, ctlr); + } +} + +long +i2csend(int addr, void *buf, long n) +{ + Ctlr *ctlr; + int i, p, s; + + ctlr = i2ctlr; + if(n > Bufsize) + return -1; + i = 1; + ctlr->txbuf[0] = addr & ~1; + if(addr & 1){ + ctlr->txbuf[1] = addr>>8; + i++; + } + memmove(ctlr->txbuf+i, buf, n); + DCFLUSH(ctlr->txbuf, Tbuflen); + ctlr->phase = Sending; + ctlr->rd->status = BDEmpty|BDWrap|BDInt; + ctlr->td->addr = PADDR(ctlr->txbuf); + ctlr->td->length = n+i; + ctlr->td->status = BDReady|BDWrap|BDLast|BDInt; + enable(); + ctlr->i2c->i2com = STR|I2CM; + eieio(); + i2cwait(ctlr); + disable(); + p = ctlr->phase; + s = ctlr->td->status; + if(s & BDReady || s & TxERR || p != Done) + return -1; + return n; +} + +long +i2crecv(int addr, void *buf, long n) +{ + Ctlr *ctlr; + int p, s, flag; + BD *td; + long nr; + + ctlr = i2ctlr; + if(n > Bufsize) + return -1; + ctlr->txbuf[0] = addr|Rbit; + if(addr & 1){ /* special select sequence */ + ctlr->addr[0] = addr &~ 1; + ctlr->addr[1] = addr>>8; + } + DCFLUSH(ctlr->txbuf, Tbuflen); + DCFLUSH(ctlr->rxbuf, Bufsize); + ctlr->phase = Recving; + ctlr->rd->addr = PADDR(ctlr->rxbuf); + ctlr->rd->status = BDEmpty|BDWrap|BDInt; + flag = 0; + td = ctlr->td; + td[1].status = 0; + if(addr & 1){ + /* special select sequence */ + td->addr = PADDR(ctlr->addr); + td->length = 2; + /* td->status made BDReady below */ + td++; + flag = TxS; + } + td->addr = PADDR(ctlr->txbuf); + td->length = n+1; + td->status = BDReady|BDWrap|BDLast | flag; /* not BDInt: leave that to receive */ + if(flag) + ctlr->td->status = BDReady; + enable(); + ctlr->i2c->i2com = STR|I2CM; + eieio(); + i2cwait(ctlr); + disable(); + p = ctlr->phase; + s = ctlr->td->status; + if(flag) + s |= ctlr->td[1].status; + nr = ctlr->rd->length; + if(nr > n) + nr = n; /* shouldn't happen */ + if(s & TxERR || s & BDReady || ctlr->rd->status & BDEmpty) + return -1; + if(p != Done) + return -1; + memmove(buf, ctlr->rxbuf, nr); + return nr; +} diff --git a/os/boot/mpc/initfads.c b/os/boot/mpc/initfads.c new file mode 100644 index 00000000..eed7c319 --- /dev/null +++ b/os/boot/mpc/initfads.c @@ -0,0 +1,187 @@ +/* + * Called from l.s in EPROM to set up a minimal working environment. + * Since there is no DRAM yet, and therefore no stack, no function + * calls may be made from sysinit0, and values can't be stored, + * except to INTMEM. Global values are accessed by offset from SB, + * which has been set by l.s to point into EPROM. + * + * This is FADS-specific in CS assignment and access of the FADS BCSR + * to discover memory size and speed. + */ + +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "archfads.h" + +#define MB (1024*1024) + +enum { + UPMSIZE = 64, /* memory controller instruction RAM */ + SPEED = 50, /* maximum memory clock in MHz */ + SDRAMSIZE = 4*MB, + + /* mcr */ + WriteRAM = 0<<30, + ReadRAM = 1<<30, + ExecRAM = 2<<30, + + SelUPMA = 0<<23, + SelUPMB = 1<<23, + + Once = 1<<8, +}; + +/* + * mpc8bug uses the following for 60ns EDO DRAMs 32-50MHz + */ +static ulong upma50[UPMSIZE] = { + 0x8FFFEC24, 0xFFFEC04, 0xCFFEC04, 0xFFEC04, + 0xFFEC00, 0x37FFEC47, 0xFFFFFFFF, 0xFFFFFFFF, + 0x8FFFEC24, 0xFFFEC04, 0x8FFEC04, 0xFFEC0C, + 0x3FFEC00, 0xFFEC44, 0xFFCC08, 0xCFFCC44, + 0xFFEC0C, 0x3FFEC00, 0xFFEC44, 0xFFCC00, + 0x3FFFC847, 0x3FFFEC47, 0xFFFFFFFF, 0xFFFFFFFF, + 0x8FAFCC24, 0xFAFCC04, 0xCAFCC00, 0x11BFCC47, + 0xC0FFCC84, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x8FAFCC24, 0xFAFCC04, 0xCAFCC00, 0x3AFCC4C, + 0xCAFCC00, 0x3AFCC4C, 0xCAFCC00, 0x3AFCC4C, + 0xCAFCC00, 0x33BFCC4F, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xC0FFCC84, 0xFFCC04, 0x7FFCC04, 0x3FFFCC06, + 0xFFFFCC85, 0xFFFFCC05, 0xFFFFCC05, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x33FFCC07, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +}; + +/* + * the FADS manual table 3-7 suggests the following for 60ns EDO DRAMs at 20MHz + */ +static ulong upma20[UPMSIZE] = { + 0x8FFFCC04, 0x08FFCC00, 0x33FFCC47, ~0, ~0, ~0, ~0, ~0, + [0x08] 0x8FFFCC04, 0x08FFCC08, 0x08FFCC08, 0x08FFCC08, 0x08FFCC00, 0x3FFFCC47, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, + [0x18] 0x8FEFCC00, 0x39BFCC47, ~0, ~0, ~0, ~0, ~0, ~0, + [0x20] 0x8FEFCC00, 0x09AFCC48, 0x09AFCC48, 0x08AFCC48, 0x39BFCC47, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, + [0x30] 0x80FFCC84, 0x17FFCC04, 0xFFFFCC86, 0xFFFFCC05, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, + [0x3C] 0x33FFCC07, ~0, ~0, ~0, +}; + +void +sysinit0(int inrom) +{ + ulong *upm, *bcsr; + IMM *io; + int i, mb; + + io = (IMM*)INTMEM; /* running before maps, no KADDR */ + + /* system interface unit initialisation, FADS manual table 3-2, except as noted */ + io->siumcr = 0x01012440; + io->sypcr = 0xFFFFFF88; + io->tbscrk = KEEP_ALIVE_KEY; + io->tbscr = 0xC3; /* time base enabled */ + io->rtcsck = KEEP_ALIVE_KEY; + io->rtcsc = 0xC1; /* don't FRZ, real-time clock enabled */ + io->rtcsck = ~KEEP_ALIVE_KEY; + io->piscrk = KEEP_ALIVE_KEY; + io->piscr = 0x82; + + io->memc[BCSRCS].option = 0xFFFF8110; /* 32k block, all types access, CS early negate, 1 ws */ + io->memc[BCSRCS].base = BCSRMEM | 1; /* base, 32-bit port, no parity, GPCM */ + + io->memc[BOOTCS].base = FLASHMEM | 1; + io->memc[BOOTCS].option = 0xFF800D54; + + if(!inrom) + return; /* can't initialise DRAM controller from DRAM */ + + bcsr = (ulong*)BCSRMEM; +// bcsr[1] &= ~DisableDRAM; + /* could check DRAM speed here; assume 60ns */ + switch((bcsr[2]>>23)&3){ + default: return; /* can't happen; for the compiler */ + case 0: mb = 4; break; + case 1: mb = 32; break; + case 2: mb = 16; break; + case 3: mb = 8; break; + } + + upm = upma50; + for(i=0; imdr = upm[i]; + io->mcr = WriteRAM | SelUPMA | i; + } + io->mptpr = 0x0400; + if(SPEED >= 32) + io->mamr = (0x9C<<24) | 0xA21114; /* 50MHz BRGCLK; FADS manual says 0xC0, mpc8bug sets 0x9C */ + else if(SPEED >= 20) + io->mamr = (0x60<<24) | 0xA21114; /* 25MHz BRGCLK */ + else + io->mamr = (0x40<<24) | 0xA21114; /* 16.67MHz BRGCLK */ + io->memc[DRAM1].option = ~((mb<<20)-1)|0x0800; /* address mask, SAM=1 */ + io->memc[DRAM1].base = 0 | 0x81; /* base at 0, 32-bit port size, no parity, UPMA */ +} + +/* + * the FADS manual table 3-9's suggestion for MB811171622A-100 32+MHz-50MHz + */ +static ulong upmb50[UPMSIZE] = { + [0x00] 0x1F07FC04, 0xEEAEFC04, 0x11ADFC04, 0xEFBBBC00, 0x1FF77C47, + [0x05] 0x1FF77C34, 0xEFEABC34, 0x1FB57C35, + [0x08] 0x1F07FC04, 0xEEAEFC04, 0x10ADFC04, 0xF0AFFC00, 0xF0AFFC00, 0xF1AFFC00, 0xEFBBBC00, 0x1FF77C47, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, + [0x18] 0x1F27FC04, 0xEEAEBC00, 0x01B93C04, 0x1FF77C47, ~0, ~0, ~0, ~0, + [0x20] 0x1F07FC04, 0xEEAEBC00, 0x10AD7C00, 0xF0AFFC00, 0xF0AFFC00, 0xE1BBBC04, 0x1FF77C47, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, + [0x30] 0x1FF5FC84, 0xFFFFFC04, 0xFFFFFC04, 0xFFFFFC04, 0xFFFFFC84, 0xFFFFFC07, ~0, ~0, ~0, ~0, ~0, ~0, + [0x3C] 0x7FFFFC07, ~0, ~0, ~0, +}; + +/* + * the FADS manual table 3-8's suggestion for MB811171622A-100 up to 32MHz + */ +static ulong upmb32[UPMSIZE] = { + [0x00] 0x126CC04, 0xFB98C00, 0x1FF74C45, ~0, ~0, + [0x05] 0x1FE77C34, 0xEFAABC34, 0x1FA57C35, + [0x08] 0x0026FC04, 0x10ADFC00, 0xF0AFFC00, 0xF1AFFC00, 0xEFBBBC00, 0x1FF77C45, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, + [0x18] 0x0E26BC04, 0x01B93C00, 0x1FF77C45, ~0, ~0, ~0, ~0, ~0, + [0x20] 0x0E26BC00, 0x10AD7C00, 0xF0AFFC00, 0xF0AFFC00, 0xE1BBBC04, 0x1FF77C45, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, + [0x30] 0x1FF5FC84, 0xFFFFFC04, 0xFFFFFC84, 0xFFFFFC05, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, + [0x3C] 0x7FFFFC07, ~0, ~0, ~0, +}; + +/* + * optionally called by archfads.c:/^archinit to initialise access to SDRAM + */ +void +sdraminit(ulong base) +{ + ulong *upm; + IMM *io; + int i; + + io = (IMM*)INTMEM; /* running before maps, no KADDR */ + if(SPEED > 32) + upm = upmb50; + else + upm = upmb32; + for(i=0; imdr = upm[i]; + io->mcr = WriteRAM | SelUPMB | i; + } + io->memc[SDRAM].option = ~(SDRAMSIZE-1)|0x0A00; /* address mask, SAM=1, G5LS=1 */ + io->memc[SDRAM].base = base | 0xC1; + if(SPEED > 32){ + io->mbmr = 0xD0802114; /* 50MHz BRGCLK */ + io->mar = 0x88; + }else{ + io->mbmr = 0x80802114; /* 32MHz BRGCLK */ + io->mar = 0x48; + } + io->mcr = ExecRAM | SelUPMB | (SDRAM<<13) | Once | 5; /* run MRS command in locations 5-8 of UPMB */ + io->mbmr = (io->mbmr & ~0xF) | 8; + io->mcr = ExecRAM | SelUPMB | (SDRAM<<13) | Once | 0x30; /* run refresh sequence */ + io->mbmr = (io->mbmr & ~0xF) | 4; /* 4-beat refresh bursts */ +} diff --git a/os/boot/mpc/initpaq.c b/os/boot/mpc/initpaq.c new file mode 100644 index 00000000..d10d92ce --- /dev/null +++ b/os/boot/mpc/initpaq.c @@ -0,0 +1,101 @@ +/* + * Called from l.s in EPROM to set up a minimal working environment. + * Since there is no DRAM yet, and therefore no stack, no function + * calls may be made from sysinit0, and values can't be stored, + * except to INTMEM. Global values are accessed by offset from SB, + * which has been set by l.s to point into EPROM. + * + * This is PowerPAQ-specific: + * - assumes 8mbytes + * - powerpaq CS assignment + */ + +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "archpaq.h" + +#define MB (1024*1024) + +enum { + DRAMSIZE = 8*MB, + FLASHSIZE = 8*MB, + + UPMSIZE = 64, /* memory controller instruction RAM */ + SPEED = 50, /* maximum memory clock in MHz */ + + /* mcr */ + WriteRAM = 0<<30, + ReadRAM = 1<<30, + ExecRAM = 2<<30, + + SelUPMA = 0<<23, + SelUPMB = 1<<23, + + Once = 1<<8, +}; + +/* + * mpc8bug uses the following for 60ns EDO DRAMs 32-50MHz + */ +static ulong upmb50[UPMSIZE] = { + 0x8FFFEC24, 0xFFFEC04, 0xCFFEC04, 0xFFEC04, + 0xFFEC00, 0x37FFEC47, 0xFFFFFFFF, 0xFFFFFFFF, + 0x8FFFEC24, 0xFFFEC04, 0x8FFEC04, 0xFFEC0C, + 0x3FFEC00, 0xFFEC44, 0xFFCC08, 0xCFFCC44, + 0xFFEC0C, 0x3FFEC00, 0xFFEC44, 0xFFCC00, + 0x3FFFC847, 0x3FFFEC47, 0xFFFFFFFF, 0xFFFFFFFF, + 0x8FAFCC24, 0xFAFCC04, 0xCAFCC00, 0x11BFCC47, + 0xC0FFCC84, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x8FAFCC24, 0xFAFCC04, 0xCAFCC00, 0x3AFCC4C, + 0xCAFCC00, 0x3AFCC4C, 0xCAFCC00, 0x3AFCC4C, + 0xCAFCC00, 0x33BFCC4F, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xC0FFCC84, 0xFFCC04, 0x7FFCC04, 0x3FFFCC06, + 0xFFFFCC85, 0xFFFFCC05, 0xFFFFCC05, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x33FFCC07, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +}; + +void +sysinit0(int inrom) +{ + ulong *upm; + IMM *io; + int i; + + io = (IMM*)INTMEM; /* running before maps, no KADDR */ + + /* system interface unit initialisation, FADS manual table 3-2, except as noted */ + io->siumcr = 0x01012440; + io->sypcr = 0xFFFFFF88; + io->tbscrk = KEEP_ALIVE_KEY; + io->tbscr = 0xC3; /* time base enabled */ + io->rtcsck = KEEP_ALIVE_KEY; + io->rtcsc = 0xC1; /* don't FRZ, real-time clock enabled */ + io->rtcsck = ~KEEP_ALIVE_KEY; + io->piscrk = KEEP_ALIVE_KEY; + io->piscr = 0x82; + + io->memc[BOOTCS].base = FLASHMEM | 1; + io->memc[BOOTCS].option = ~(FLASHSIZE-1)|(1<<8)|(2<<4); /* mask, BIH, 2 wait states */ + + if(!inrom) + return; /* can't initialise DRAM controller from DRAM */ + + /* could check DRAM speed here; assume 60ns */ + /* could probe DRAM for size here; assume DRAMSIZE */ + io->mptpr = 0x400; /* powerpaq flash has 0x1000 */ + io->mbmr = (0xC0<<24) | 0xA21114; /* 50MHz BRGCLK */ + upm = upmb50; + for(i=0; imdr = upm[i]; + io->mcr = WriteRAM | SelUPMB | i; + } + io->memc[DRAM1].option = ~(DRAMSIZE-1)|0x0800; /* address mask, SAM=1 */ + io->memc[DRAM1].base = 0 | 0xC1; /* base at 0, 32-bit port size, no parity, UPMB */ +} diff --git a/os/boot/mpc/initrpcg.c b/os/boot/mpc/initrpcg.c new file mode 100644 index 00000000..55219548 --- /dev/null +++ b/os/boot/mpc/initrpcg.c @@ -0,0 +1,91 @@ + +/* + * Called from l.s in EPROM to set up a minimal working environment. + * Since there is no DRAM yet, and therefore no stack, no function + * calls may be made from sysinit, and values can't be stored, + * except to INTMEM. Global values are accessed by offset from SB, + * which has been set by l.s to point into EPROM. + */ + +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "archrpcg.h" + +#define MB (1024*1024) + +enum { + UPMSIZE = 64, /* memory controller instruction RAM */ + DRAMSIZE = 16*MB, + FLASHSIZE = 4*MB, + + WriteRAM = 0<<30, + ReadRAM = 1<<30, + ExecRAM = 2<<30, + + SelUPMA = 0<<23, + SelUPMB = 1<<23, +}; +/* RPCG values for RPXLite AW */ +static ulong upma50[UPMSIZE] = { + 0xCFFFCC24, 0x0FFFCC04, 0x0CAFCC04, 0x03AFCC08, + 0x3FBFCC27, 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, + 0xCFFFCC24, 0x0FFFCC04, 0x0CAFCC84, 0x03AFCC88, + 0x3FBFCC27, 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, + 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, + 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, + 0xCFFFCC24, 0x0FFFCC04, 0x0CFFCC04, 0x03FFCC00, + 0x3FFFCC27, 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, + 0xCFFFCC24, 0x0FFFCC04, 0x0CFFCC84, 0x03FFCC84, + 0x0CFFCC00, 0x33FFCC27, 0xFFFFCC25, 0xFFFFCC25, + 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, + 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, + 0xC0FFCC24, 0x03FFCC24, 0x0FFFCC24, 0x0FFFCC24, + 0x3FFFCC27, 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, + 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, + 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, +}; + +void +sysinit0(int inrom) +{ + ulong *upm; + IMM *io; + int i; + + io = (IMM*)INTMEM; /* running before maps, no KADDR */ + io->siumcr = 0x01012440; + io->sypcr = 0xFFFFFF88; + io->tbscrk = KEEP_ALIVE_KEY; + io->tbscr = 0xC3; + io->rtcsck = KEEP_ALIVE_KEY; + io->rtcsc = 0xC1; + io->rtcsck = ~KEEP_ALIVE_KEY; + io->piscrk = KEEP_ALIVE_KEY; + io->piscr = 0x82; +return; + io->memc[BCSRCS].option = 0xFFFF8910; /* 32k block, all types access, CSNT, CS early negate, burst inhibit, 1 ws */ + io->memc[BCSRCS].base = BCSRMEM | 1; /* base, 32-bit port, no parity, GPCM */ + + io->memc[BOOTCS].base = FLASHMEM | 0x801; /* base, 16 bit port */ + io->memc[BOOTCS].option = ~(FLASHSIZE-1)|(1<<8)|(4<<4); /* mask, BIH, 4 wait states */ + + if(1||!inrom) + return; /* can't initialise DRAM controller from DRAM */ + + /* TO DO: could check DRAM size and speed now */ + + upm = upma50; + for(i=0; imdr = upm[i]; + io->mcr = WriteRAM | SelUPMA | i; + } + io->mptpr = 0x0800; /* divide by 8 */ + io->mamr = (0x58<<24) | 0xA01430; /* 40MHz BRGCLK */ + io->memc[DRAM1].option = ~(DRAMSIZE-1)|0x0E00; /* address mask, SAM=1, G5LA/S=3 */ + io->memc[DRAM1].base = 0 | 0x81; /* base at 0, 32-bit port size, no parity, UPMA */ +} diff --git a/os/boot/mpc/io.h b/os/boot/mpc/io.h new file mode 100644 index 00000000..8ae1d5b3 --- /dev/null +++ b/os/boot/mpc/io.h @@ -0,0 +1,463 @@ +enum +{ + /* software interrupt vectors (SIU and CPM) */ + VectorPIC= 0, /* level 0 to level 7, assigned by software */ + CPIClevel= 4, + VectorIRQ= VectorPIC+8, /* IRQ0 to IRQ7 */ + VectorCPIC= VectorIRQ+8, /* 32 CPM interrupts: 0 (error) to 0x1F (PC15) */ +}; + +enum +{ + BUSUNKNOWN = 0, +}; + +/* + * Buffer Descriptors and IO Rings + */ + +typedef struct BD BD; +struct BD { + ushort status; + ushort length; + ulong addr; +}; + +BD* bdalloc(int); +void bdfree(BD*, int); + +enum { + /* Rx BDs, bits common to all protocols */ + BDEmpty= 1<<15, + BDWrap= 1<<13, + BDInt= 1<<12, + BDLast= 1<<11, + BDFirst= 1<<10, + + /* Tx BDs */ + BDReady= 1<<15, + /* BDWrap, BDInt, BDLast */ +}; + +typedef struct Ring Ring; +struct Ring { + BD* rdr; /* receive descriptor ring */ + void* rrb; /* receive ring buffers */ + int rdrx; /* index into rdr */ + int nrdre; /* length of rdr */ + + BD* tdr; /* transmit descriptor ring */ + Block** txb; /* corresponding transmit ring buffers */ + int tdrh; /* host index into tdr */ + int tdri; /* interface index into tdr */ + int ntdre; /* length of tdr */ + int ntq; /* pending transmit requests */ +}; + +#define NEXT(x, l) (((x)+1)%(l)) +#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1) +#define HOWMANY(x, y) (((x)+((y)-1))/(y)) +#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) + +int ioringinit(Ring*, int, int, int); + +/* + * CPM + */ +enum { + /* commands */ + InitRxTx = 0, + InitRx = 1, + InitTx = 2, + EnterHunt= 3, + StopTx= 4, + GracefulStopTx = 5, + InitIDMA = 5, + RestartTx = 6, + CloseRxBD = 7, + SetGroupAddr = 8, + SetTimer = 8, + GCITimeout = 9, + GCIAbort = 10, + StopIDMA = 11, + StartDSP = 12, + ArmIDMA = 13, + InitDSP = 13, + USBCmd = 15, + + /* channel IDs */ + SCC1ID= 0, + USBID= 0, + I2CID= 1, + IDMA1ID= 1, + SCC2ID= 4, + SPIID= 5, + IDMA2ID= 5, + TIMERID= 5, + SCC3ID= 8, + SMC1ID= 9, + DSP1ID=9, + SCC4ID= 12, + SMC2ID= 13, + DSP2ID= 13, + + BaudEnable = 1<<16, + + /* sicr */ + CLK1 = 4, /* SCC1,2 */ + CLK2 = 5, + CLK3 = 6, + CLK4 = 7, + CLK5 = CLK1, /* SCC3,4 */ + CLK6 = CLK2, + CLK7 = CLK3, + CLK8 = CLK4, +}; + +void cpmop(int, int, int); +#define ioplock() (m->iomem) +#define iopunlock() + +/* + * the structures below follow hardware/firmware layouts in the 8xx manuals: + * mind the data types, offsets and alignment + */ + +/* + * basic IO controller parameters (SMC and SCC) + */ +typedef struct IOCparam IOCparam; +struct IOCparam { + ushort rbase; + ushort tbase; + uchar rfcr; + uchar tfcr; + ushort mrblr; + ulong rstate; + ulong rptr; + ushort rbptr; + ushort rcnt; + ulong rtmp; + ulong tstate; + ulong tptr; + ushort tbptr; + ushort tcnt; + ulong ttmp; +}; + +typedef struct SCCparam SCCparam; +struct SCCparam { + IOCparam; + ulong rcrc; + ulong tcrc; +}; + +typedef struct SCC SCC; +struct SCC { + ulong gsmrl; + ulong gsmrh; + ushort psmr; + uchar rsvscc0[2]; + ushort todr; + ushort dsr; + ushort scce; + uchar rsvscc1[2]; + ushort sccm; + uchar rsvscc3; + uchar sccs; + ushort irmode; + ushort irsip; +}; + +typedef struct SMC SMC; +struct SMC { + uchar pad1[2]; + ushort smcmr; + uchar pad2[2]; + uchar smce; + uchar pad3[3]; + uchar smcm; + uchar pad4[5]; +}; + +typedef struct SPI SPI; +struct SPI { + ushort spmode; + uchar res1[4]; + uchar spie; + uchar res2[3]; + uchar spim; + uchar res3[2]; + uchar spcom; + uchar res4[10]; +}; + +typedef struct USB USB; +struct USB { /* 823 only */ + uchar usmod; + uchar usadr; + uchar uscom; + uchar rsvu1; + ushort usep[4]; + uchar rsvu2[4]; + ushort usber; + uchar rsvu3[2]; + ushort usbmr; + uchar rsvu4; + uchar usbs; + uchar rsvu5[8]; +}; + +typedef struct IMM IMM; +struct IMM { + struct { /* general SIU */ + ulong siumcr; + ulong sypcr; + uchar rsv0[0xE-0x8]; + ushort swsr; + ulong sipend; + ulong simask; + ulong siel; + uchar sivec; + uchar padv[3]; + ulong tesr; + uchar rsv1[0x30-0x24]; + ulong sdcr; + uchar rsv2[0x80-0x34]; + }; + struct { /* PCMCIA */ + struct { + ulong base; + ulong option; + } pcmr[8]; + uchar rsv3[0xe0-0xc0]; + ulong pgcra; + ulong pgcrb; + ulong pscr; + uchar rsv4[0xf0-0xec]; + ulong pipr; + uchar rsv5[4]; + ulong per; + uchar rsv6[4]; + }; + struct { /* MEMC */ + struct { + ulong base; + ulong option; + } memc[8]; + uchar rsv7a[0x24]; + ulong mar; + ulong mcr; + uchar rsv7b[4]; + ulong mamr; + ulong mbmr; + ushort mstat; + ushort mptpr; + ulong mdr; + uchar rsv7c[0x80]; + }; + struct { /* system integration timers */ + ushort tbscr; + uchar rsv8a[2]; + ulong tbrefu; + ulong tbrefl; + uchar rsv8b[0x14]; + ushort rtcsc; + uchar rsv8c[2]; + ulong rtc; + ulong rtsec; + ulong rtcal; + uchar rsv8d[0x10]; + ushort piscr; + ushort rsv8e; + ulong pitc; + ulong pitr; + uchar rsv8f[0x34]; + }; + struct { /* 280: clocks and resets */ + ulong sccr; + ulong plprcr; + ulong rsr; + uchar rsv9[0x300-0x28c]; + }; + struct { /* 300: system integration timers keys */ + ulong tbscrk; + ulong tbrefuk; + ulong tbreflk; + ulong tbk; + uchar rsv10a[0x10]; + ulong rtcsck; + ulong rtck; + ulong rtseck; + ulong rtcalk; + uchar rsv10b[0x10]; + ulong piscrk; + ulong pitck; + uchar rsv10c[0x38]; + }; + struct { /* 380: clocks and resets keys */ + ulong sccrk; + ulong plprcrk; + ulong rsrk; + uchar rsv11[0x800-0x38C]; + }; + struct { /* 800: video controller */ + ushort vccr; + ushort pad11a; + uchar vsr; + uchar pad11b; + uchar vcmr; + uchar pad11c; + ulong vbcb; + ulong pad11d; + ulong vfcr0; + ulong vfaa0; + ulong vfba0; + ulong vfcr1; + ulong vfaa1; + ulong vfba1; + uchar rsv11a[0x840-0x828]; + }; + struct { /* 840: LCD */ + ulong lccr; + ulong lchcr; + ulong lcvcr; + ulong rsv11b; + ulong lcfaa; + ulong lcfba; + uchar lcsr; + uchar rsv11c[0x860-0x859]; + }; + struct { /* 860: I2C */ + uchar i2mod; + uchar rsv12a[3]; + uchar i2add; + uchar rsv12b[3]; + uchar i2brg; + uchar rsv12c[3]; + uchar i2com; + uchar rsv12d[3]; + uchar i2cer; + uchar rsv12e[3]; + uchar i2cmr; + uchar rsv12[0x900-0x875]; + }; + struct { /* 900: DMA */ + uchar rsv13[4]; + ulong sdar; + uchar sdsr; + uchar pad1[3]; + uchar sdmr; + uchar pad2[3]; + uchar idsr1; + uchar pad3[3]; + uchar idmr1; + uchar pad4[3]; + uchar idsr2; + uchar pad5[3]; + uchar idmr2; + uchar pad6[0x930-0x91D]; + }; + struct { /* CPM interrupt control */ + ushort civr; + uchar pad7[0x940-0x932]; + ulong cicr; + ulong cipr; + ulong cimr; + ulong cisr; + }; + struct { /* input/output port */ + ushort padir; + ushort papar; + ushort paodr; + ushort padat; + uchar pad8[8]; + ushort pcdir; + ushort pcpar; + ushort pcso; + ushort pcdat; + ushort pcint; + uchar pad9[6]; + ushort pddir; + ushort pdpar; + ushort rsv14a; + ushort pddat; + uchar rsv14[0x980-0x978]; + }; + struct { /* CPM timers */ + ushort tgcr; + uchar rsv15a[0x990-0x982]; + ushort tmr1; + ushort tmr2; + ushort trr1; + ushort trr2; + ushort tcr1; + ushort tcr2; + ushort tcn1; + ushort tcn2; + ushort tmr3; + ushort tmr4; + ushort trr3; + ushort trr4; + ushort tcr3; + ushort tcr4; + ushort tcn3; + ushort tcn4; + ushort ter1; + ushort ter2; + ushort ter3; + ushort ter4; + uchar rsv15[0x9C0-0x9B8]; + }; + struct { /* CPM */ + ushort cpcr; + uchar res0[2]; + ushort rccr; + uchar res1; + uchar rmds; + uchar res2a[4]; + ushort rctr1; + ushort rctr2; + ushort rctr3; + ushort rctr4; + uchar res2[2]; + ushort rter; + uchar res3[2]; + ushort rtmr; + uchar rsv16[0x9F0-0x9DC]; + }; + union { /* BRG */ + struct { + ulong brgc1; + ulong brgc2; + ulong brgc3; + ulong brgc4; + }; + ulong brgc[4]; + }; + uchar skip0[0xAB2-0xA00]; /* USB, SCC, SMC, SPI: address using cpmdev(CP...)->regs */ + struct { /* PIP */ + ushort pipc; /* not 823 */ + ushort ptpr; /* not 823 */ + ulong pbdir; + ulong pbpar; + uchar pad10[2]; + ushort pbodr; + ulong pbdat; + uchar pad11[0xAE0-0xAC8]; + }; + struct { /* SI */ + ulong simode; + uchar sigmr; + uchar pad12; + uchar sistr; + uchar sicmr; + uchar pad13[4]; + ulong sicr; + ulong sirp; + uchar pad14[0xB00-0xAF4]; + }; + ulong vcram[64]; + ushort siram[256]; + ushort lcdmap[256]; +}; diff --git a/os/boot/mpc/ip.h b/os/boot/mpc/ip.h new file mode 100644 index 00000000..a39b5b4b --- /dev/null +++ b/os/boot/mpc/ip.h @@ -0,0 +1,98 @@ +typedef struct Udphdr Udphdr; +struct Udphdr +{ + uchar d[6]; /* Ethernet destination */ + uchar s[6]; /* Ethernet source */ + uchar type[2]; /* Ethernet packet type */ + + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + + /* Udp pseudo ip really starts here */ + uchar ttl; + uchar udpproto; /* Protocol */ + uchar udpplen[2]; /* Header plus data length */ + uchar udpsrc[4]; /* Ip source */ + uchar udpdst[4]; /* Ip destination */ + uchar udpsport[2]; /* Source port */ + uchar udpdport[2]; /* Destination port */ + uchar udplen[2]; /* data length */ + uchar udpcksum[2]; /* Checksum */ +}; + +typedef struct Etherhdr Etherhdr; +struct Etherhdr +{ + uchar d[6]; + uchar s[6]; + uchar type[2]; + + /* Now we have the ip fields */ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar ttl; /* Time to live */ + uchar proto; /* Protocol */ + uchar cksum[2]; /* Header checksum */ + uchar src[4]; /* Ip source */ + uchar dst[4]; /* Ip destination */ +}; + +enum +{ + IP_VER = 0x40, + IP_HLEN = 0x05, + UDP_EHSIZE = 22, + UDP_PHDRSIZE = 12, + UDP_HDRSIZE = 20, + ETHER_HDR = 14, + IP_UDPPROTO = 17, + ET_IP = 0x800, + Bcastip = 0xffffffff, + BPportsrc = 68, + BPportdst = 67, + TFTPport = 69, + Timeout = 5000, /* milliseconds */ + Bootrequest = 1, + Bootreply = 2, + Tftp_READ = 1, + Tftp_WRITE = 2, + Tftp_DATA = 3, + Tftp_ACK = 4, + Tftp_ERROR = 5, + Segsize = 512, + TFTPSZ = Segsize+10, +}; + +typedef struct Bootp Bootp; +struct Bootp +{ + uchar op; /* opcode */ + uchar htype; /* hardware type */ + uchar hlen; /* hardware address len */ + uchar hops; /* hops */ + uchar xid[4]; /* a random number */ + uchar secs[2]; /* elapsed snce client started booting */ + uchar pad[2]; + uchar ciaddr[4]; /* client IP address (client tells server) */ + uchar yiaddr[4]; /* client IP address (server tells client) */ + uchar siaddr[4]; /* server IP address */ + uchar giaddr[4]; /* gateway IP address */ + uchar chaddr[16]; /* client hardware address */ + char sname[64]; /* server host name (optional) */ + char file[128]; /* boot file name */ + char vend[128]; /* vendor-specific goo */ +}; + +typedef struct Netaddr Netaddr; +struct Netaddr +{ + ulong ip; + ushort port; + char ea[Eaddrlen]; +}; diff --git a/os/boot/mpc/l.s b/os/boot/mpc/l.s new file mode 100644 index 00000000..225a2d59 --- /dev/null +++ b/os/boot/mpc/l.s @@ -0,0 +1,370 @@ +#include "mem.h" + +/* special instruction definitions */ +#define BDNE BC 0,2, +#define BDNZ BC 16,0, +#define NOOP OR R0,R0,R0 + +/* + * common ppc special purpose registers + */ +#define DSISR 18 +#define DAR 19 /* Data Address Register */ +#define DEC 22 /* Decrementer */ +#define SRR0 26 /* Saved Registers (exception) */ +#define SRR1 27 +#define SPRG0 272 /* Supervisor Private Registers */ +#define SPRG1 273 +#define SPRG2 274 +#define SPRG3 275 +#define TBRU 269 /* Time base Upper/Lower (Reading) */ +#define TBRL 268 +#define TBWU 285 /* Time base Upper/Lower (Writing) */ +#define TBWL 284 +#define PVR 287 /* Processor Version */ + +/* + * mpc82x-specific special purpose registers of interest here + */ +#define EIE 80 +#define EID 81 +#define NRI 82 +#define IMMR 638 +#define IC_CST 560 +#define IC_ADR 561 +#define IC_DAT 562 +#define DC_CST 568 +#define DC_ADR 569 +#define DC_DAT 570 +#define MI_CTR 784 +#define MI_AP 786 +#define MI_EPN 787 +#define MI_TWC 789 +#define MI_RPN 790 +#define MI_DBCAM 816 +#define MI_DBRAM0 817 +#define MI_DBRAM1 818 +#define MD_CTR 792 +#define M_CASID 793 +#define MD_AP 794 +#define MD_EPN 795 +#define M_TWB 796 +#define MD_TWC 797 +#define MD_RPN 798 +#define M_TW 799 +#define MD_DBCAM 824 +#define MD_DBRAM0 825 +#define MD_DBRAM1 826 + +/* as on 603e, apparently mtmsr needs help in some chip revisions */ +#define WAITMSR SYNC; ISYNC + +/* use of SPRG registers in save/restore */ +#define SAVER0 SPRG0 +#define SAVER1 SPRG1 +#define SAVELR SPRG2 +#define SAVECR SPRG3 + +#define UREGSIZE ((8+32)*4) +#define UREGSPACE (UREGSIZE+8) /* allow for arg to trap, and align */ + +/* + * This code is loaded by the ROM loader at location 0x3000, + * or lives in flash memory at 0x2800100. + * Move it to high memory so that it can load the kernel at 0x0000. + */ + +#define LOADCODEBASE 0x3000 /* when downloaded in S records */ +#define FLASHCODEBASE (FLASHMEM+0x100) /* when in flash */ + + TEXT start(SB), $-4 + MOVW MSR, R3 + MOVW $(EE|IP|RI), R4 + ANDN R4, R3 + OR $ME, R3 + SYNC + MOVW R3, MSR /* turn off interrupts but enable traps */ + WAITMSR + +/* + * reset the caches and disable them for now + */ + MOVW SPR(IC_CST), R4 /* read and clear */ + MOVW $(5<<25), R4 + MOVW R4, SPR(IC_CST) /* unlock all */ + ISYNC + MOVW $(6<<25), R4 + MOVW R4, SPR(IC_CST) /* invalidate all */ + ISYNC + MOVW $(2<<25), R4 + MOVW R4, SPR(IC_CST) /* disable i-cache */ + ISYNC + + SYNC + MOVW SPR(DC_CST), R4 /* read and clear */ + MOVW $(10<<24), R4 + MOVW R4, SPR(DC_CST) /* unlock all */ + ISYNC + MOVW $(12<<24), R4 + MOVW R4, SPR(DC_CST) /* invalidate all */ + ISYNC + MOVW $(4<<24), R4 + MOVW R4, SPR(DC_CST) /* disable i-cache */ + ISYNC + + MOVW $7, R4 +ANDN R4, R4, R4 + MOVW R4, SPR(158) /* cancel `show cycle' for normal instruction execution */ + +/* + * set other system configuration values + */ + MOVW SPR(IMMR), R5 /* save initial space pointer */ + MOVW $INTMEM, R4 + MOVW R4, SPR(IMMR) /* set internal memory base */ + MOVW $0xFFFFFF88, R3 + MOVW R3, 4(R4) /* disable watchdog in sypcr */ + MOVW $0x01012440, R3 + MOVW R3, 0(R4) /* siumcr */ + +/* + * system initialisation (init and map DRAM) + */ + MOVW $0, R0 + MOVW $setSB(SB), R2 + MOVW $(0xF000<<16), R3 +/*MOVW R0, R3*/ + ANDCC R5, R3 /* initial space is high? */ + BEQ notrom + MOVW $FLASHCODEBASE, R5 /* where $start(SB) actually is now */ + MOVW $start(SB), R4 /* logical start address */ + SUB R4, R5, R6 /* text relocation value */ + MOVW $etext(SB), R7 + SUB R4, R7 + ADD R5, R7 /* data address in ROM */ + MOVW $bdata(SB), R8 + SUB R8, R2 + ADD R7, R2 /* relocate SB: SB' = romdata+(SB-bdata) */ + MOVW $sysinit0(SB), R4 + ADD R6, R4 /* relocate sysinit0's address */ + MOVW R4, CTR + MOVW $inmem(SB), R4 + ADD R6, R4 + MOVW R4, LR /* and the return address */ + BR (CTR) /* call sysinit0 */ + TEXT inmem(SB), $-4 + MOVW $FLASHCODEBASE, R3 + BR cpu0 +notrom: + MOVW $start(SB), R6 + SUB R6, R2 + ADD $LOADCODEBASE, R2 + BL sysinit0(SB) + MOVW $LOADCODEBASE, R3 + +/* + * cpu 0 + * relocate bootstrap to our link addresses for text and data + * set new PC + */ +cpu0: + MOVW $setSB(SB), R2 /* set correct static base register */ + MOVW $start(SB), R4 + MOVW $etext(SB), R5 + SUB R4, R5 + CMP R4, R3 /* already there? */ + BNE copytext + ADD R5, R3 /* start of data image */ + BR copydata + +copytext: + ADD $3, R5 + SRAW $2, R5 + MOVW R5, CTR + SUB $4, R4 + SUB $4, R3 +copyt: /* copy text */ + MOVWU 4(R3), R5 + MOVWU R5, 4(R4) + BDNZ copyt + ADD $4, R3 + +copydata: + /* copy data */ + MOVW $bdata(SB), R4 + CMP R4, R3 /* already there? */ + BEQ loadkpc + MOVW $edata(SB), R5 + SUB R4, R5 + ADD $3, R5 + SRAW $2, R5 + MOVW R5, CTR + SUB $4, R4 + SUB $4, R3 +copyd: + MOVWU 4(R3), R5 + MOVWU R5, 4(R4) + BDNZ copyd + + /* load correct PC */ +loadkpc: + MOVW $start1(SB), R3 + MOVW R3, LR + BR (LR) +TEXT start1(SB), $-4 + MOVW $edata(SB), R3 + MOVW $end(SB), R4 + SUBCC R3, R4 + BLE skipz + SRAW $2, R4 + MOVW R4, CTR + SUB $4, R3 + MOVW $0, R0 +zero: + MOVWU R0, 4(R3) + BDNZ zero +skipz: + MOVW $mach0(SB), R1 + MOVW R1, m(SB) + ADD $(MACHSIZE-8), R1 + MOVW $0, R0 + BL main(SB) + BR 0(PC) + +TEXT getmsr(SB), $0 + MOVW MSR, R3 + RETURN + +TEXT putmsr(SB), $0 + SYNC + MOVW R3, MSR + WAITMSR + RETURN + +TEXT eieio(SB), $0 + EIEIO + RETURN + +TEXT idle(SB), $0 + RETURN + +TEXT spllo(SB), $0 + MOVW MSR, R3 + OR $EE, R3, R4 + SYNC + MOVW R4, MSR + WAITMSR + RETURN + +TEXT splhi(SB), $0 + MOVW MSR, R3 + RLWNM $0, R3, $~EE, R4 + SYNC + MOVW R4, MSR + WAITMSR + RETURN + +TEXT splx(SB), $0 + MOVW MSR, R4 + RLWMI $0, R3, $EE, R4 + SYNC + MOVW R4, MSR + WAITMSR + RETURN + +TEXT gettbl(SB), $0 +/* MOVW SPR(TBRL), R3 */ + WORD $0x7c6c42e6 /* mftbl on 8xx series */ + RETURN + +TEXT getpvr(SB), $0 + MOVW SPR(PVR), R3 + RETURN + +TEXT getimmr(SB), $0 + MOVW SPR(IMMR), R3 + RETURN + +TEXT getdec(SB), $0 + MOVW SPR(DEC), R3 + RETURN + +TEXT putdec(SB), $0 + MOVW R3, SPR(DEC) + RETURN + +/* + * save state in Ureg on kernel stack. + * enter with R0 giving the PC from the call to `exception' from the vector. + * on return, SB (R2) has been set, and R3 has the Ureg* + */ +TEXT saveureg(SB), $-4 + SUB $UREGSPACE, R1 + MOVMW R2, 48(R1) /* r2:r31 */ + MOVW $setSB(SB), R2 + MOVW SPR(SAVER1), R4 + MOVW R4, 44(R1) + MOVW SPR(SAVER0), R5 + MOVW R5, 40(R1) + MOVW CTR, R6 + MOVW R6, 36(R1) + MOVW XER, R4 + MOVW R4, 32(R1) + MOVW SPR(SAVECR), R5 /* CR */ + MOVW R5, 28(R1) + MOVW SPR(SAVELR), R6 /* LR */ + MOVW R6, 24(R1) + /* pad at 20(R1) */ + MOVW SPR(SRR0), R4 + MOVW R4, 16(R1) /* old PC */ + MOVW SPR(SRR1), R5 + MOVW R5, 12(R1) + MOVW R0, 8(R1) /* cause/vector, encoded in LR from vector */ + ADD $8, R1, R3 /* Ureg* */ + STWCCC R3, (R1) /* break any pending reservations */ + MOVW $0, R0 /* R0ISZERO */ + BR (LR) + +/* + * restore state from Ureg + * SB (R2) is unusable on return + */ +TEXT restoreureg(SB), $-4 + MOVMW 48(R1), R2 /* r2:r31 */ + /* defer R1 */ + MOVW 40(R1), R0 + MOVW R0, SPR(SAVER0) + MOVW 36(R1), R0 + MOVW R0, CTR + MOVW 32(R1), R0 + MOVW R0, XER + MOVW 28(R1), R0 + MOVW R0, CR /* CR */ + MOVW 24(R1), R0 + MOVW R0, SPR(SAVELR) /* LR */ + /* pad, skip */ + MOVW 16(R1), R0 + MOVW R0, SPR(SRR0) /* old PC */ + MOVW 12(R1), R0 + MOVW R0, SPR(SRR1) /* old MSR */ + /* cause, skip */ + MOVW 44(R1), R1 /* old SP */ + BR (LR) + +TEXT exception(SB), $-4 + MOVW R1, SPR(SAVER1) + MOVW CR, R0 + MOVW R0, SPR(SAVECR) + MOVW LR, R0 + BL saveureg(SB) + MOVW $0, R0 + BL trap(SB) + BL restoreureg(SB) + MOVW SPR(SAVELR), R0 + MOVW R0, LR + MOVW SPR(SAVER0), R0 + ISYNC + RFI + +GLOBL mach0+0(SB), $MACHSIZE +GLOBL m(SB), $4 diff --git a/os/boot/mpc/lib.h b/os/boot/mpc/lib.h new file mode 100644 index 00000000..1eb0532d --- /dev/null +++ b/os/boot/mpc/lib.h @@ -0,0 +1,106 @@ +/* + * functions (possibly) linked in, complete, from libc. + */ + +/* + * mem routines + */ +extern void* memccpy(void*, void*, int, long); +extern void* memset(void*, int, long); +extern int memcmp(void*, void*, long); +extern void* memmove(void*, void*, long); +extern void* memchr(void*, int, long); + +/* + * string routines + */ +extern char* strcat(char*, char*); +extern char* strchr(char*, char); +extern int strcmp(char*, char*); +extern char* strcpy(char*, char*); +extern char* strncat(char*, char*, long); +extern char* strncpy(char*, char*, long); +extern int strncmp(char*, char*, long); +extern long strlen(char*); +extern char* strrchr(char*, char); +extern char* strstr(char*, char*); + +/* + * print routines + * Fconv isn't used but is defined to satisfy prototypes in libg.h + * that are never called. + */ +typedef struct Fconv Fconv; + +extern char* donprint(char*, char*, char*, void*); +extern int sprint(char*, char*, ...); +extern int print(char*, ...); + +#define PRINTSIZE 256 + +/* + * one-of-a-kind + */ +extern int atoi(char*); +extern long strtol(char*, char**, int); +extern ulong strtoul(char*, char**, int); +extern long end; + +/* + * Syscall data structures + */ + +#define MORDER 0x0003 /* mask for bits defining order of mounting */ +#define MREPL 0x0000 /* mount replaces object */ +#define MBEFORE 0x0001 /* mount goes before others in union directory */ +#define MAFTER 0x0002 /* mount goes after others in union directory */ +#define MCREATE 0x0004 /* permit creation in mounted directory */ +#define MMASK 0x0007 /* all bits on */ + +#define OREAD 0 /* open for read */ +#define OWRITE 1 /* write */ +#define ORDWR 2 /* read and write */ +#define OEXEC 3 /* execute, == read but check execute permission */ +#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ +#define OCEXEC 32 /* or'ed in, close on exec */ +#define ORCLOSE 64 /* or'ed in, remove on close */ + +#define NCONT 0 /* continue after note */ +#define NDFLT 1 /* terminate after note */ + +typedef struct Qid Qid; +typedef struct Dir Dir; +typedef struct Waitmsg Waitmsg; + +#define ERRLEN 64 +#define DIRLEN 116 +#define NAMELEN 28 + +struct Qid +{ + ulong path; + ulong vers; +}; + +struct Dir +{ + char name[NAMELEN]; + char uid[NAMELEN]; + char gid[NAMELEN]; + Qid qid; + ulong mode; + long atime; + long mtime; + vlong length; + short type; + short dev; +}; + +struct Waitmsg +{ + int pid; /* of loved one */ + int status; /* unused; a placeholder */ + ulong time[3]; /* of loved one */ + char msg[ERRLEN]; +}; +#define nelem(x) (sizeof(x)/sizeof((x)[0])) diff --git a/os/boot/mpc/main.c b/os/boot/mpc/main.c new file mode 100644 index 00000000..a6250471 --- /dev/null +++ b/os/boot/mpc/main.c @@ -0,0 +1,524 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "dosfs.h" + +typedef struct Type Type; +typedef struct Medium Medium; +typedef struct Mode Mode; + +enum { + Dany = -1, + Nmedia = 16, + + /* DS1 switch options */ + Sflashfs = 1<<0, /* take local fs from flash */ + Snotflash = 1<<1, /* don't boot from flash */ +}; + +enum { /* type */ + Tflash, + Tuart, + Tether, + Thard, + + Tany = -1, +}; + +enum { /* flag and name */ + Fnone = 0x00, + + Fdos = 0x01, + Ndos = 0x00, + Fboot = 0x02, + Nboot = 0x01, + Fbootp = 0x04, + Nbootp = 0x02, + Fflash = 0x08, + Fuart = 0x10, + NName = 0x03, + + Fany = Fbootp|Fboot|Fdos|Fflash|Fuart, + + Fini = 0x10, + Fprobe = 0x80, +}; + +enum { /* mode */ + Mauto = 0x00, + Mlocal = 0x01, + Manual = 0x02, + NMode = 0x03, +}; + +typedef struct Type { + int type; + char *cname; + int flag; + int (*init)(void); + long (*read)(int, void*, long); + long (*seek)(int, long); + Partition* (*setpart)(int, char*); + char* name[NName]; + + int mask; + Medium* media; +} Type; + +typedef struct Medium { + Type* type; + int flag; + Partition* partition; + Dos; + + Medium* next; +} Medium; + +typedef struct Mode { + char* name; + int mode; +} Mode; + +static Type types[] = { + { Tflash, "flash", + Fflash, + flashinit, 0, 0, 0, + { 0, "F", 0, } + }, +/* + { Tuart, "uart", + Fuart|Fboot, + uartinit, uartread, uartseek, setuartpart, + { 0, "u", 0, } + }, +*/ + { Tether, "ether", + Fbootp, + etherinit, 0, 0, 0, + { 0, 0, "e", }, + }, + { Thard, "ata", + Fini|Fboot|Fdos, + 0, 0, 0, 0, /* not used now, will be later with PCMCIA */ + { "hd", "h", 0, }, + }, + {-1}, +}; + +static Medium media[Nmedia]; +static Medium *curmedium = media; + +static Mode modes[NMode+1] = { + [Mauto] { "auto", Mauto, }, + [Mlocal] { "local", Mlocal, }, + [Manual] { "manual", Manual, }, +}; + +static char *inis[] = { + "inferno/inferno.ini", + "inferno.ini", + "plan9/plan9.ini", + "plan9.ini", + 0, +}; +char **ini; +int predawn; + +static int +parse(char *line, int *type, int *flag, int *dev, char *file) +{ + Type *tp; + char buf[2*NAMELEN], *v[4], *p; + int i; + + strcpy(buf, line); + switch(getcfields(buf, v, 4, "!")){ + + case 3: + break; + + case 2: + v[2] = ""; + break; + + default: + return 0; + } + + *flag = 0; + for(tp = types; tp->cname; tp++){ + for(i = 0; i < NName; i++){ + + if(tp->name[i] == 0 || strcmp(v[0], tp->name[i])) + continue; + *type = tp->type; + *flag |= 1<type->name[Nbootp], mp->dev); + return bootp(mp->dev, file); + } + + if(flag & Fflash){ + if(mp->flag & Fflash && flashbootable(0)) + flashboot(mp->dev); + } + + if(flag & Fboot){ + + if(mp->flag & Fini){ + (*mp->type->setpart)(mp->dev, "disk"); + plan9ini(mp, nil); + } + if(file == 0 || *file == 0) + file = mp->partition->name; + (*mp->type->setpart)(mp->dev, file); + sprint(BOOTLINE, "%s!%d!%s", mp->type->name[Nboot], mp->dev, file); + r = plan9boot(mp->dev, mp->seek, mp->read); + uartsetboot(0); + return r; + } + + if(flag & Fdos){ + if(mp->type->setpart) + (*mp->type->setpart)(mp->dev, "disk"); + if(mp->flag & Fini) + plan9ini(mp, nil); + if(file == 0 || *file == 0){ + strcpy(ixdos, *ini); + if(p = strrchr(ixdos, '/')) + p++; + else + p = ixdos; + strcpy(p, "impc"); + if(dosstat(mp, ixdos, &df) <= 0) + return -1; + } + else + strcpy(ixdos, file); + sprint(BOOTLINE, "%s!%d!%s", mp->type->name[Ndos], mp->dev, ixdos); + return dosboot(mp, ixdos); + } + + return -1; +} + +static Medium* +allocm(Type *tp) +{ + Medium **l; + + if(curmedium >= &media[Nmedia]) + return 0; + + for(l = &tp->media; *l; l = &(*l)->next) + ; + *l = curmedium++; + return *l; +} + +Medium* +probe(int type, int flag, int dev) +{ + Type *tp; + int dombr, i, start; + Medium *mp; + Dosfile df; + Partition *pp; + + for(tp = types; tp->cname; tp++){ + if(type != Tany && type != tp->type || tp->init == 0) + continue; + + if(flag != Fnone){ + for(mp = tp->media; mp; mp = mp->next){ + if((flag & mp->flag) && (dev == Dany || dev == mp->dev)) + return mp; + } + } + if((tp->flag & Fprobe) == 0){ + tp->flag |= Fprobe; + tp->mask = (*tp->init)(); + } + + for(i = 0; tp->mask; i++){ + if((tp->mask & (1<mask &= ~(1<dev = i; + mp->flag = tp->flag; + mp->seek = tp->seek; + mp->read = tp->read; + mp->type = tp; + + if(mp->flag & Fboot){ + if((mp->partition = (*tp->setpart)(i, "boot")) == 0) + mp->flag &= ~Fboot; + if((mp->flag & (Fflash|Fuart)) == 0) + (*tp->setpart)(i, "disk"); + } + + if(mp->flag & Fdos){ + start = 0; + dombr = 1; + if(mp->type->setpart){ + if(pp = (*mp->type->setpart)(i, "dos")){ + if(start = pp->start) + dombr = 0; + } + (*tp->setpart)(i, "disk"); + } + if(dosinit(mp, start, dombr) < 0) + mp->flag &= ~(Fini|Fdos); + else + print("dos init failed\n"); + } + + if(mp->flag & Fini){ + mp->flag &= ~Fini; + for(ini = inis; *ini; ini++){ + if(dosstat(mp, *ini, &df) <= 0) + continue; + mp->flag |= Fini; + break; + } + } + + if((flag & mp->flag) && (dev == Dany || dev == i)) + return mp; + } + } + + return 0; +} + +void +main(void) +{ + Medium *mp; + int dev, flag, i, mode, tried, type, options; + char def[2*NAMELEN], file[2*NAMELEN], line[80], *p; + Type *tp; + + machinit(); + archinit(); + meminit(); + cpminit(); + trapinit(); + consinit(); /* screen and keyboard initially */ + screeninit(); + cpuidprint(); + alarminit(); + clockinit(); + predawn = 0; + spllo(); + options = archoptionsw(); + + mp = 0; + for(tp = types; tp->cname; tp++){ + if(tp->type == Tether) + continue; + if((mp = probe(tp->type, Fini, Dany)) && (mp->flag & Fini)){ + plan9ini(mp, nil); + break; + } + } + + if(mp == 0 || (mp->flag & Fini) == 0) + plan9ini(nil, flashconfig(0)); + + //consinit(); /* establish new console location */ + + if((options & Snotflash) == 0 && flashbootable(0)){ + print("Flash boot\n"); + flashboot(0); + } + + tried = 0; + mode = Mauto; + p = getconf("bootfile"); + flag = 0; + + if(p != 0) { + mode = Manual; + for(i = 0; i < NMode; i++){ + if(strcmp(p, modes[i].name) == 0){ + mode = modes[i].mode; + goto done; + } + } + if(parse(p, &type, &flag, &dev, file) == 0) { + print("Bad bootfile syntax: %s\n", p); + goto done; + } + mp = probe(type, flag, dev); + if(mp == 0) { + print("Cannot access device: %s\n", p); + goto done; + } + tried = boot(mp, flag, file); + } +done: + if(tried == 0 && mode != Manual){ + flag = Fany; + if(mode == Mlocal) + flag &= ~Fbootp; + if(options & Snotflash) + flag &= ~Fflash; + if((mp = probe(Tany, flag, Dany)) != 0) + boot(mp, flag & mp->flag, 0); + } + + def[0] = 0; + probe(Tany, Fnone, Dany); + + flag = 0; + for(tp = types; tp->cname; tp++){ + for(mp = tp->media; mp; mp = mp->next){ + if(flag == 0){ + flag = 1; + print("Boot devices:"); + } + + if(mp->flag & Fbootp) + print(" %s!%d", mp->type->name[Nbootp], mp->dev); + if(mp->flag & Fdos) + print(" %s!%d", mp->type->name[Ndos], mp->dev); + if(mp->flag & (Fflash|Fuart) || mp->flag & Fboot) + print(" %s!%d", mp->type->name[Nboot], mp->dev); + } + } + if(flag) + print("\n"); + + for(;;){ + if(getstr("boot from", line, sizeof(line), def) >= 0){ + if(parse(line, &type, &flag, &dev, file)){ + if(mp = probe(type, flag, dev)) + boot(mp, flag, file); + } + } + def[0] = 0; + } +} + +void +machinit(void) +{ + memset(m, 0, sizeof(*m)); + m->delayloop = 20000; + m->cpupvr = getpvr(); + m->iomem = KADDR(INTMEM); +} + +int +getcfields(char* lp, char** fields, int n, char* sep) +{ + int i; + + for(i = 0; lp && *lp && i < n; i++){ + while(*lp && strchr(sep, *lp) != 0) + *lp++ = 0; + if(*lp == 0) + break; + fields[i] = lp; + while(*lp && strchr(sep, *lp) == 0){ + if(*lp == '\\' && *(lp+1) == '\n') + *lp++ = ' '; + lp++; + } + } + + return i; +} + +static Map memv[512]; +static RMap rammap = {"physical memory"}; + +void +meminit(void) +{ + ulong e; + + mapinit(&rammap, memv, sizeof(memv)); + e = PADDR(&end); + mapfree(&rammap, e, 4*1024*1024-e); /* fixed 4Mbytes is plenty for bootstrap */ +} + +void* +ialloc(ulong n, int align) +{ + ulong a; + int s; + + if(align <= 0) + align = 4; + s = splhi(); + a = mapalloc(&rammap, 0, n, align); + splx(s); + if(a == 0) + panic("ialloc"); + return memset(KADDR(a), 0, n); +} + +void* +malloc(ulong n) +{ + ulong *p; + + n = ((n+sizeof(int)-1)&~(sizeof(int)-1))+2*sizeof(int); + p = ialloc(n, sizeof(int)); + *p++ = 0xcafebeef; + *p++ = n; + return p; +} + +void +free(void *ap) +{ + int s; + ulong *p; + + p = ap; + if(p){ + if(*(p -= 2) != 0xcafebeef) + panic("free"); + s = splhi(); + mapfree(&rammap, (ulong)p, p[1]); + splx(s); + } +} + +void +sched(void) +{ +} diff --git a/os/boot/mpc/mem.c b/os/boot/mpc/mem.c new file mode 100644 index 00000000..e69de29b diff --git a/os/boot/mpc/mem.h b/os/boot/mpc/mem.h new file mode 100644 index 00000000..f76867a8 --- /dev/null +++ b/os/boot/mpc/mem.h @@ -0,0 +1,95 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ + +/* + * Sizes + */ +#define BI2BY 8 /* bits per byte */ +#define BI2WD 32 /* bits per word */ +#define BY2WD 4 /* bytes per word */ +#define BY2PG 4096 /* bytes per page */ +#define WD2PG (BY2PG/BY2WD) /* words per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define PGROUND(s) (((s)+(BY2PG-1))&~(BY2PG-1)) + +#define MAXMACH 1 /* max # cpus system can run */ +#define CACHELINELOG 4 +#define CACHELINESZ (1<k.mx + +f.mx: qbrom$ARCH + ms2 -S 0x100 -a 0x2800100 -p 4 $prereq >f.mx + +%.$O: %.s + $AS $stem.s + +%.$O: %.c + $CC $CFLAGS $stem.c + +%.$O: $HFILES + +lib%.a:V: $SHELLTYPE-lib%.a + +rc-lib%.a nt-lib%.a:VQ: + echo '@{builtin cd ' $ROOT/lib$stem ';mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install}' + @{builtin cd $ROOT/lib$stem ;mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install} + +sh-lib%.a:VQ: + echo "(cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install)" + (cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install) + +clock.$O floppy.$O trap.$O: ureg.h +conf.$O dosboot.$O main.$O: dosfs.h +ether.$O etherscc.$O: etherif.h +bootp.$O: ip.h + +clean:V: + rm -f *.[$OS] [$OS].out y.tab.? y.debug y.output $TARG qboot k.mx f.mx romboot + +nuke-sh:QV: + for i in $LIBDIRS + do + echo "(cd $ROOT/lib$i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE nuke)" + (cd $ROOT/lib$i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE nuke) + done + +nuke-rc nuke-nt:QV: + for (i in $LIBDIRS) + { + echo '@{cd $ROOT/lib$i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE nuke}' + @{cd $ROOT/lib$i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE nuke} + } + +nuke:V: clean nuke-$SHELLTYPE diff --git a/os/boot/mpc/ms2.c b/os/boot/mpc/ms2.c new file mode 100644 index 00000000..ce96df78 --- /dev/null +++ b/os/boot/mpc/ms2.c @@ -0,0 +1,179 @@ +#include +#include +#include +#include + +void record(uchar*, int); +void usage(void); +void dosegment(long, int); +void trailer(ulong); + +enum +{ + Recordsize = 32, +}; + +int dsegonly; +int supressend; +int binary; +int addr4; +ulong addr; +ulong psize = 4096; +ulong startaddr = 0x030000; +Biobuf stdout; +Biobuf bio; + +void +main(int argc, char **argv) +{ + Dir dir; + Fhdr f; + int fd; + + ARGBEGIN{ + case 'd': + dsegonly++; + break; + case 's': + supressend++; + break; + case 'a': + addr = strtoul(ARGF(), 0, 0); + break; + case 'p': + psize = strtoul(ARGF(), 0, 0); + break; + case 'b': + binary++; + break; + case 'S': + startaddr = strtoul(ARGF(), 0, 0); + break; + case '4': + addr4++; + break; + default: + usage(); + }ARGEND + + if(argc != 1) + usage(); + + Binit(&stdout, 1, OWRITE); + + fd = open(argv[0], OREAD); + if(fd < 0) { + fprint(2, "ms2: open %s: %r\n", argv[0]); + exits("open"); + } + + if(binary) { + if(dirfstat(fd, &dir) < 0) { + fprint(2, "ms2: stat failed %r"); + exits("dirfstat"); + } + Binit(&bio, fd, OREAD); + dosegment(0, dir.length); + if(supressend == 0) + trailer(startaddr); + Bterm(&stdout); + Bterm(&bio); + exits(0); + } + + if(crackhdr(fd, &f) == 0){ + fprint(2, "ms2: bad magic: %r\n"); + exits("magic"); + } + seek(fd, 0, 0); + + Binit(&bio, fd, OREAD); + + if(dsegonly) + dosegment(f.datoff, f.datsz); + else { + dosegment(f.txtoff, f.txtsz); + addr = (addr+(psize-1))&~(psize-1); + dosegment(f.datoff, f.datsz); + } + + if(supressend == 0) + trailer(startaddr); + + Bterm(&stdout); + Bterm(&bio); + exits(0); +} + +void +dosegment(long foff, int len) +{ + int l, n; + uchar buf[2*Recordsize]; + + Bseek(&bio, foff, 0); + for(;;) { + l = len; + if(l > Recordsize) + l = Recordsize; + n = Bread(&bio, buf, l); + if(n == 0) + break; + if(n < 0) { + fprint(2, "ms2: read error: %r\n"); + exits("read"); + } + record(buf, l); + len -= l; + } +} + +void +record(uchar *s, int l) +{ + int i; + ulong cksum; + + if(addr4 || addr & (0xFF<<24)){ + Bprint(&stdout, "S3%.2X%.8luX", l+5, addr); + cksum = l+5; + cksum += (addr>>24)&0xff; + }else{ + Bprint(&stdout, "S2%.2X%.6X", l+4, addr); + cksum = l+4; + } + cksum += addr&0xff; + cksum += (addr>>8)&0xff; + cksum += (addr>>16)&0xff; + + for(i = 0; i < l; i++) { + cksum += *s; + Bprint(&stdout, "%.2X", *s++); + } + Bprint(&stdout, "%.2X\n", (~cksum)&0xff); + addr += l; +} + +void +trailer(ulong a) +{ + ulong cksum; + + cksum = 0; + if(addr4 || a & (0xFF<<24)){ + Bprint(&stdout, "S7%.8luX", a); + cksum += (a>>24)&0xff; + }else + Bprint(&stdout, "S9%.6X", a); + cksum += a&0xff; + cksum += (a>>8)&0xff; + cksum += (a>>16)&0xff; + Bprint(&stdout, "%.2X\n", (~cksum)&0xff); +} + +void +usage(void) +{ + fprint(2, "usage: ms2 [-ds] [-a address] [-p pagesize] ?.out\n"); + exits("usage"); +} diff --git a/os/boot/mpc/plan9boot.c b/os/boot/mpc/plan9boot.c new file mode 100644 index 00000000..047474e3 --- /dev/null +++ b/os/boot/mpc/plan9boot.c @@ -0,0 +1,96 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +char *premature = "premature EOF\n"; + +/* + * read in a segment + */ +static long +readseg(int dev, long (*read)(int, void*, long), long len, long addr) +{ + char *a; + long n, sofar; + + a = (char *)addr; + for(sofar = 0; sofar < len; sofar += n){ + n = 8*1024; + if(len - sofar < n) + n = len - sofar; + n = (*read)(dev, a + sofar, n); + if(n <= 0) + break; + print("."); + } + return sofar; +} + +/* + * boot + */ +int +plan9boot(int dev, long (*seek)(int, long), long (*read)(int, void*, long)) +{ + long n; + long addr; + void (*b)(void); + Exec *ep; + + if((*seek)(dev, 0) < 0) + return -1; + + /* + * read header + */ + ep = (Exec *) ialloc(sizeof(Exec), 0); + n = sizeof(Exec); + if(readseg(dev, read, n, (long) ep) != n){ + print(premature); + return -1; + } + if(GLLONG(ep->magic) != Q_MAGIC){ + print("bad magic 0x%lux not a plan 9 executable!\n", GLLONG(ep->magic)); + return -1; + } + + /* + * read text + */ + addr = PADDR(GLLONG(ep->entry)); + n = GLLONG(ep->text); + print("%d", n); + if(readseg(dev, read, n, addr) != n){ + print(premature); + return -1; + } + + /* + * read data (starts at first page after kernel) + */ + addr = PGROUND(addr+n); + n = GLLONG(ep->data); + print("+%d@%8.8lux", n, addr); + if(readseg(dev, read, n, addr) != n){ + print(premature); + return -1; + } + + /* + * bss and entry point + */ + print("+%d\nstart at 0x%lux\n", GLLONG(ep->bss), GLLONG(ep->entry)); + uartwait(); + scc2stop(); + splhi(); + + /* + * Go to new code. It's up to the program to get its PC relocated to + * the right place. + */ + b = (void (*)(void))(PADDR(GLLONG(ep->entry))); + (*b)(); + return 0; +} diff --git a/os/boot/mpc/qio.c b/os/boot/mpc/qio.c new file mode 100644 index 00000000..e014433e --- /dev/null +++ b/os/boot/mpc/qio.c @@ -0,0 +1,128 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +struct Queue { + Block* first; + Block* last; + void (*kick)(void*); + void* arg; + long len; +}; + +Block * +iallocb(int n) +{ + Block *b; + + b = (Block*)malloc(sizeof(Block)+n); + b->data = (uchar*)b + sizeof(Block); + b->rp = b->wp = b->data; + b->lim = b->data + n; + b->next = 0; + b->magic = 0xcafebee0; + return b; +} + +void +freeb(Block *b) +{ + if(b){ + if(b->magic != 0xcafebee0) + panic("freeb"); + b->magic = 0; + b->next = (Block*)0xdeadbabe; + free(b); + } +} + +Queue * +qopen(int limit, int msg, void (*kick)(void*), void *arg) +{ + Queue *q; + + USED(limit, msg); + q = (Queue*)malloc(sizeof(Queue)); + q->first = q->last = 0; + q->kick = kick; + q->arg = arg; + q->len = 0; + return q; +} + +Block * +qget(Queue *q) +{ + int s; + Block *b; + + s = splhi(); + if((b = q->first) != 0){ + q->first = b->next; + b->next = 0; + q->len -= BLEN(b); + if(q->len < 0) + panic("qget"); + } + splx(s); + return b; +} + +void +qbwrite(Queue *q, Block *b) +{ + int s; + + s = splhi(); + b->next = 0; + if(q->first == 0) + q->first = b; + else + q->last->next = b; + q->last = b; + q->len += BLEN(b); + splx(s); + if(q->kick) + q->kick(q->arg); +} + +long +qlen(Queue *q) +{ + return q->len; +} + +int +qbgetc(Queue *q) +{ + Block *b; + int s, c; + + c = -1; + s = splhi(); + while(c < 0 && (b = q->first) != nil){ + if(b->rp < b->wp){ + c = *b->rp++; + q->len--; + } + if(b->rp >= b->wp){ + q->first = b->next; + b->next = nil; + } + } + splx(s); + return c; +} + +void +qbputc(Queue *q, int c) +{ + Block *b; + + b = iallocb(1); + *b->wp++ = c; + qbwrite(q, b); +} diff --git a/os/boot/mpc/rmap.c b/os/boot/mpc/rmap.c new file mode 100644 index 00000000..8b8a0bef --- /dev/null +++ b/os/boot/mpc/rmap.c @@ -0,0 +1,104 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +void +mapinit(RMap *rmap, Map *map, int size) +{ + lock(rmap); + rmap->map = map; + rmap->mapend = map+(size/sizeof(Map)); + unlock(rmap); +} + +void +mapfree(RMap* rmap, ulong addr, int size) +{ + Map *mp; + ulong t; + + if(size <= 0) + return; + + lock(rmap); + for(mp = rmap->map; mp->addr <= addr && mp->size; mp++) + ; + + if(mp > rmap->map && (mp-1)->addr+(mp-1)->size == addr){ + (mp-1)->size += size; + if(addr+size == mp->addr){ + (mp-1)->size += mp->size; + while(mp->size){ + mp++; + (mp-1)->addr = mp->addr; + (mp-1)->size = mp->size; + } + } + } + else{ + if(addr+size == mp->addr && mp->size){ + mp->addr -= size; + mp->size += size; + } + else do{ + if(mp >= rmap->mapend){ + print("mapfree: %s: losing 0x%uX, %d\n", + rmap->name, addr, size); + break; + } + t = mp->addr; + mp->addr = addr; + addr = t; + t = mp->size; + mp->size = size; + mp++; + }while(size = t); + } + unlock(rmap); +} + +ulong +mapalloc(RMap* rmap, ulong addr, int size, int align) +{ + Map *mp; + ulong maddr, oaddr; + + lock(rmap); + for(mp = rmap->map; mp->size; mp++){ + maddr = mp->addr; + + if(addr){ + if(maddr > addr) + continue; + if(addr+size > maddr+mp->size) + break; + maddr = addr; + } + + if(align > 0) + maddr = ((maddr+align-1)/align)*align; + if(mp->addr+mp->size-maddr < size) + continue; + + oaddr = mp->addr; + mp->addr = maddr+size; + mp->size -= maddr-oaddr+size; + if(mp->size == 0){ + do{ + mp++; + (mp-1)->addr = mp->addr; + }while((mp-1)->size = mp->size); + } + + unlock(rmap); + if(oaddr != maddr) + mapfree(rmap, oaddr, maddr-oaddr); + + return maddr; + } + unlock(rmap); + + return 0; +} diff --git a/os/boot/mpc/screen.c b/os/boot/mpc/screen.c new file mode 100644 index 00000000..ec420ee9 --- /dev/null +++ b/os/boot/mpc/screen.c @@ -0,0 +1,242 @@ +#include "all.h" +#include +#include + +enum { + Colldepth = 3, + Colmaxx = 640, + Colmaxxvis = 640, + Colmaxy = 480, +}; + +#define MINX 8 + +extern GSubfont defont0; + +struct{ + Point pos; + int bwid; +}out; + +typedef struct Mode Mode; +struct Mode { + int x; + int y; + int d; + char* aperture; + int apsize; +}; + +GBitmap gscreen; +Point gchar(GBitmap*, Point, GFont*, int, Fcode); +int setcolor(ulong, ulong, ulong, ulong); +static void lcdinit(Mode*); + +void +screeninit(void) +{ + Mode m; + + m.x = Colmaxx; + m.y = Colmaxy; + m.d = Colldepth; + m.aperture = 0; + lcdinit(&m); + if(m.aperture == 0) + return; + gscreen.ldepth = 3; + gscreen.base = (ulong*)m.aperture; + gscreen.width = Colmaxx/BY2WD; + gscreen.r = Rect(0, 0, Colmaxxvis, Colmaxy); + gscreen.clipr = gscreen.r; + /* + * For now, just use a fixed colormap: + * 0 == white and 255 == black + */ + setcolor(0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF); + setcolor(255, 0x00000000, 0x00000000, 0x00000000); + + gbitblt(&gscreen, Pt(0, 0), &gscreen, gscreen.r, Zero); + out.pos.x = MINX; + out.pos.y = 0; + out.bwid = defont0.info[' '].width; +} + +void +screenputc(int c) +{ + Fontchar *i; + Point p; + + if(gscreen.base == nil) + return; + switch(c){ + case '\n': + out.pos.x = MINX; + out.pos.y += defont0.height; + if(out.pos.y > gscreen.r.max.y-defont0.height) + out.pos.y = gscreen.r.min.y; + gbitblt(&gscreen, Pt(0, out.pos.y), &gscreen, + Rect(0, out.pos.y, gscreen.r.max.x, out.pos.y+2*defont0.height), + Zero); + break; + case '\t': + out.pos.x += (8-((out.pos.x-MINX)/out.bwid&7))*out.bwid; + if(out.pos.x >= gscreen.r.max.x) + screenputc('\n'); + break; + case '\b': + if(out.pos.x >= out.bwid+MINX){ + out.pos.x -= out.bwid; + screenputc(' '); + out.pos.x -= out.bwid; + } + break; + default: + if(out.pos.x >= gscreen.r.max.x-out.bwid) + screenputc('\n'); + c &= 0x7f; + if(c <= 0 || c >= defont0.n) + break; + i = defont0.info + c; + p = out.pos; + gbitblt(&gscreen, Pt(p.x+i->left, p.y), defont0.bits, + Rect(i[0].x, 0, i[1].x, defont0.height), + S); + out.pos.x = p.x + i->width; + break; + } +} + +void +screenputs(char *s, int n) +{ + while(n-- > 0) + screenputc(*s++); +} + +/* + * See section 5.2.1 (page 5-6) of the MPC823 manual + */ +static uchar lcdclock[17] = { /* (a<<2)|b => divisor of (1<iomem; + mode->y = ROWS; + mode->x = COLS; + mode->d = LDEPTH; + mode->aperture = ialloc(mode->x*mode->y, 16); + mode->apsize = mode->x*mode->y; + + io->sdcr &= ~LAM; /* MPC823 errata: turn off LAM before disabling controller */ + io->lcfaa = PADDR(mode->aperture); + io->lccr = (((mode->x*mode->y*(1<lcdmap[i] = i; + break; + case 2: + /* 4-bit grey scale map */ + for(i=0; i<16; i++) + io->lcdmap[0] = (i<<8)|(i<<4)|i; + break; + case 3: + /* 8-bit linear map */ + for(i=0; i<256; i++) + io->lcdmap[i] = (i<<8)|(i<<4)|i; + break; + } + + io->lcvcr = (mode->y << 11) | (1<<28) | 33; /* 2 line vsync pulse, 34 line wait between frames */ + io->lchcr = (mode->x<<10) | BigEndian | 228; /* clock cycles between lines */ + + hz = m->cpuhz; + d = hz/LCDFREQ; + if(hz/d > LCDFREQ) + d++; + if(d >= 16) + d = 16; + + /* + * enable LCD outputs + */ + io->pddat = 0; + io->pdpar = 0x1fff; +io->pdpar &= ~SIBIT(6); /* 823 bug fix? */ + io->pddir = 0x1fff; + io->pbpar |= IBIT(31) | IBIT(19) | IBIT(17); + io->pbdir |= IBIT(31) | IBIT(19) | IBIT(17); + io->pbodr &= ~(IBIT(31) | IBIT(19) | IBIT(17)); + + eieio(); + io->sccrk = KEEP_ALIVE_KEY; + eieio(); + io->sccr = (io->sccr & ~0x1F) | lcdclock[d]; + eieio(); + io->sccrk = ~KEEP_ALIVE_KEY; + eieio(); + gscreen.width = gscreen.width; /* access external memory before enabling (mpc823 errata) */ + io->lcsr = 7; /* clear status */ + eieio(); + io->lccr |= Enable; + archbacklight(1); +} + +int +setcolor(ulong p, ulong r, ulong g, ulong b) +{ + r >>= 28; + g >>= 28; + b >>= 28; + m->iomem->lcdmap[~p&0xFF] = (r<<8) | (g<<4) | b; /* TO DO: it's a function of the ldepth */ + return 1; +} diff --git a/os/boot/mpc/sload.c b/os/boot/mpc/sload.c new file mode 100644 index 00000000..0eb02805 --- /dev/null +++ b/os/boot/mpc/sload.c @@ -0,0 +1,71 @@ +/* + * send S records to rpcg + */ + +#include +#include +#include + +static int dbg; +static char buf[2048]; +static int run=1; +static void stuffbym(char*, int, int); +static void getdot(void); + +void +main(int argc, char **argv) +{ + int n; + char *l; + Biobuf *f; + static int p; + + ARGBEGIN{ + case 'd': dbg++; break; + case 'n': run=0; break; + }ARGEND + + f = Bopen(*argv? *argv: "k.mx", OREAD); + if(f == 0) { + fprint(2, "sload: cannot open k.mx: %r\n"); + exits("sload"); + } + getdot(); + while((l = Brdline(f, '\n')) != 0) { + l[Blinelen(f)-1] = '\r'; + stuffbym(l, Blinelen(f), 16); + getdot(); + if(++p % 25 == 0) + write(2, ".", 1); + } + exits(0); +} + +static void +stuffbym(char *l, int n, int m) +{ + int nr, ns; + + while(n > 0) { + ns = n; + if(ns > m) + ns = m; + write(1, l, ns); + l += ns; + n -= ns; + } +} + +static void +getdot(void) +{ + char c; + + for(;;){ + if(read(0, &c, 1) != 1) + exits("bang"); + write(2, &c, 1); + if(c == '.') + break; + } +} diff --git a/os/boot/mpc/squeeze.h b/os/boot/mpc/squeeze.h new file mode 100644 index 00000000..b06c1b79 --- /dev/null +++ b/os/boot/mpc/squeeze.h @@ -0,0 +1,34 @@ + +/* + * squeezed file format: + * Sqhdr + * original Exec header + * two Squeeze tables + * squeezed segment + * unsqueezed segment, if any + */ +#define SQMAGIC (ulong)0xFEEF0F1E + +typedef struct Sqhdr Sqhdr; +struct Sqhdr { + uchar magic[4]; /* SQMAGIC */ + uchar text[4]; /* squeezed length of text (excluding tables) */ + uchar data[4]; /* squeezed length of data (excluding tables) */ + uchar asis[4]; /* length of unsqueezed segment */ + uchar toptxt[4]; /* value for 0 encoding in text */ + uchar topdat[4]; /* value for 0 encoding in data */ + uchar sum[4]; /* simple checksum of unsqueezed data */ + uchar flags[4]; +}; +#define SQHDRLEN (8*4) + +/* + * certain power instruction types are rearranged by sqz + * so as to move the variable part of the instruction word to the + * low order bits. note that the mapping is its own inverse. + */ +#define QREMAP(X)\ + switch((X)>>26){\ + case 19: case 31: case 59: case 63:\ + (X) = (((X) & 0xFC00F801) | (((X)>>15)&0x7FE) | (((X)&0x7FE)<<15));\ + } diff --git a/os/boot/mpc/trap.c b/os/boot/mpc/trap.c new file mode 100644 index 00000000..3440ee1f --- /dev/null +++ b/os/boot/mpc/trap.c @@ -0,0 +1,233 @@ +#include "boot.h" + +enum +{ + Maxhandler= 32+16, /* max number of interrupt handlers */ +}; + +typedef struct Handler Handler; +struct Handler +{ + void (*r)(Ureg*, void*); + void *arg; + Handler *next; + int edge; +}; + +struct +{ + Handler *ivec[128]; + Handler h[Maxhandler]; + int free; +} halloc; + +char *excname[] = { + "reserved 0", + "system reset", + "machine check", + "data access", + "instruction access", + "external interrupt", + "alignment", + "program exception", + "floating-point unavailable", + "decrementer", + "reserved A", + "reserved B", + "system call", + "trace trap", + "floating point assist", + "reserved F", + "software emulation", + "ITLB miss", + "DTLB miss", + "ITLB error", + "DTLB error", +}; + +char *regname[]={ + "CAUSE", "SRR1", + "PC", "GOK", + "LR", "CR", + "XER", "CTR", + "R0", "R1", + "R2", "R3", + "R4", "R5", + "R6", "R7", + "R8", "R9", + "R10", "R11", + "R12", "R13", + "R14", "R15", + "R16", "R17", + "R18", "R19", + "R20", "R21", + "R22", "R23", + "R24", "R25", + "R26", "R27", + "R28", "R29", + "R30", "R31", +}; + +static void intr(Ureg*); + +void +sethvec(int v, void (*r)(void)) +{ + ulong *vp, pa, o; + + if((ulong)r & 3) + panic("sethvec"); + vp = (ulong*)KADDR(v); + vp[0] = 0x7c1043a6; /* MOVW R0, SPR(SPRG0) */ + vp[1] = 0x7c0802a6; /* MOVW LR, R0 */ + vp[2] = 0x7c1243a6; /* MOVW R0, SPR(SPRG2) */ + pa = PADDR(r); + o = pa >> 25; + if(o != 0 && o != 0x7F){ + /* a branch too far: running from ROM */ + vp[3] = (15<<26)|(pa>>16); /* MOVW $r&~0xFFFF, R0 */ + vp[4] = (24<<26)|(pa&0xFFFF); /* OR $r&0xFFFF, R0 */ + vp[5] = 0x7c0803a6; /* MOVW R0, LR */ + vp[6] = 0x4e800021; /* BL (LR) */ + }else + vp[3] = (18<<26)|(pa&0x3FFFFFC)|3; /* bla */ +} + +#define LEV(n) (((n)<<1)|1) +#define IRQ(n) (((n)<<1)|0) + +void +setvec(int v, void (*r)(Ureg*, void*), void *arg) +{ + Handler *h; + IMM *io; + + if(halloc.free >= Maxhandler) + panic("out of interrupt handlers"); + v -= VectorPIC; + h = &halloc.h[halloc.free++]; + h->next = halloc.ivec[v]; + h->r = r; + h->arg = arg; + halloc.ivec[v] = h; + + /* + * enable corresponding interrupt in SIU/CPM + */ + + io = m->iomem; + if(v >= VectorCPIC){ + v -= VectorCPIC; + io->cimr |= 1<<(v&0x1F); + } + else if(v >= VectorIRQ) + io->simask |= 1<<(31-IRQ(v&7)); + else + io->simask |= 1<<(31-LEV(v)); +} + +void +trapinit(void) +{ + int i; + IMM *io; + + io = m->iomem; + io->sypcr &= ~(3<<2); /* disable watchdog (821/823) */ + io->simask = 0; /* mask all */ + io->siel = ~0; /* edge sensitive, wake on all */ + io->cicr = 0; /* disable CPM interrupts */ + io->cipr = ~0; /* clear all interrupts */ + io->cimr = 0; /* mask all events */ + io->cicr = (0xE1<<16)|(CPIClevel<<13)|(0x1F<<8); + io->cicr |= 1 << 7; /* enable */ + io->tbscrk = KEEP_ALIVE_KEY; + io->tbscr = 1; /* TBE */ + io->simask |= 1<<(31-LEV(CPIClevel)); /* CPM's level */ + io->tbk = KEEP_ALIVE_KEY; + eieio(); + putdec(~0); + + /* + * set all exceptions to trap + */ + for(i = 0x0; i < 0x3000; i += 0x100) + sethvec(i, exception); +} + +void +dumpregs(Ureg *ur) +{ + int i; + ulong *l; + l = &ur->cause; + for(i=0; icause >> 8; + switch(c){ + default: + {extern int predawn; predawn = 1;} + if(c < 0 || c >= nelem(excname)) + print("exception/interrupt #%x\n", c); + else + print("exception %s\n", excname[c]); + dumpregs(ur); + /* spllo(); */ + print("^P to reset\n"); + for(;;) + ; + + case 0x09: /* decrementer */ + clockintr(ur, 0); + return; + + case 0x05: /* external interrupt */ + intr(ur); + break; + } +} + +static void +intr(Ureg *ur) +{ + int b, v; + Handler *h; + IMM *io; + + io = m->iomem; + b = io->sivec>>2; + v = b>>1; + if(b & 1) { + if(v == CPIClevel){ + io->civr = 1; + eieio(); + v = VectorCPIC+(io->civr>>11); + } + }else + v += VectorIRQ; + h = halloc.ivec[v]; + if(h == nil){ + for(;;) + ; + //print("unknown interrupt %d pc=0x%lux\n", v, ur->pc); + return; + } + if(h->edge) + io->sipend |= 1<<(31-b); + /* + * call the interrupt handlers + */ + do { + (*h->r)(ur, h->arg); + h = h->next; + } while(h != nil); + if(v >= VectorCPIC) + io->cisr |= 1<<(v-VectorCPIC); +} diff --git a/os/boot/mpc/uartboot.c b/os/boot/mpc/uartboot.c new file mode 100644 index 00000000..0b11b5d5 --- /dev/null +++ b/os/boot/mpc/uartboot.c @@ -0,0 +1,189 @@ +#include "boot.h" + +/* + * this doesn't yet use the crc + */ + +typedef struct Uboot Uboot; +struct Uboot { + Queue* iq; + Block* partial; + ulong csum; + long bno; + uchar buf[64]; + int nleft; + int ntimeout; +}; + +static Uboot uboot; +ulong crc32(void *buf, int n, ulong crc); + +static void +uartbrecv(uchar *p, int n) +{ + Uboot *ub; + Block *b; + + ub = &uboot; + if(n > 0 && ub->iq != nil){ + b = iallocb(n); + memmove(b->wp, p, n); + b->wp += n; + qbwrite(ub->iq, b); + } +} + +int +uartinit(void) +{ + return 1<<0; +} + +Partition* +setuartpart(int, char *s) +{ + static Partition pp[1]; + + if(strcmp(s, "boot") != 0 && strcmp(s, "disk") != 0) + return 0; + pp[0].start = 0; + pp[0].end = 2*1024*1024; + strcpy(pp[0].name, "boot"); + return pp; +} + +long +uartseek(int, long) +{ + /* start the boot */ + if(uboot.iq == nil) + uboot.iq = qopen(64*1024, 0, 0, 0); + if(uboot.partial){ + freeb(uboot.partial); + uboot.partial = 0; + } + print("uart: start transmission\n"); + uartsetboot(uartbrecv); + uboot.csum = ~0; + uboot.bno = 0; + uboot.nleft = 0; + uboot.ntimeout = 0; + return 0; +} + +static long +uartreadn(void *buf, int nb) +{ + ulong start; + Uboot *ub; + int l; + Block *b; + uchar *p; + + p = buf; + ub = &uboot; + start = m->ticks; + while(nb > 0){ + b = ub->partial; + ub->partial = nil; + if(b == nil){ + ub->ntimeout = 0; + while((b = qget(ub->iq)) == 0){ + if(TK2MS(m->ticks - start) >= 15*1000){ + if(++ub->ntimeout >= 3){ + print("uart: timeout\n"); + return 0; + } + uartputs("n", 1); + } + } + } + l = BLEN(b); + if(l > nb) + l = nb; + memmove(p, b->rp, l); + b->rp += l; + if(b->rp >= b->wp) + freeb(b); + else + ub->partial = b; + nb -= l; + p += l; + } + return p-(uchar*)buf; +} + +long +uartread(int, void *buf, long n) +{ + uchar *p; + int l; + static uchar lbuf[64]; + + p = buf; + if((l = uboot.nleft) > 0){ + if(l > n) + l = n; + uboot.nleft -= l; + memmove(p, uboot.buf, l); + p += l; + n -= l; + } + while(n > 0){ + l = uartreadn(lbuf, sizeof(lbuf)); + if(l < sizeof(lbuf)) + return 0; + if(l > n){ + uboot.nleft = l-n; + memmove(uboot.buf, lbuf+n, uboot.nleft); + l = n; + } + memmove(p, lbuf, l); + n -= l; + p += l; + uboot.bno++; + uartputs("y", 1); + } + return p-(uchar*)buf; +} + +/* + * from Rob Warnock + */ +static ulong crc32tab[256]; /* initialised on first call to crc32 */ + +enum { + CRC32POLY = 0x04c11db7 /* AUTODIN II, Ethernet, & FDDI */ +}; + +/* + * Build auxiliary table for parallel byte-at-a-time CRC-32. + */ +static void +initcrc32(void) +{ + int i, j; + ulong c; + + for(i = 0; i < 256; i++) { + for(c = i << 24, j = 8; j > 0; j--) + if(c & (1<<31)) + c = (c<<1) ^ CRC32POLY; + else + c <<= 1; + crc32tab[i] = c; + } +} + +ulong +crc32(void *buf, int n, ulong crc) +{ + uchar *p; + + if(crc32tab[1] == 0) + initcrc32(); + crc = ~crc; + for(p = buf; --n >= 0;) + crc = (crc << 8) ^ crc32tab[(crc >> 24) ^ *p++]; + return ~crc; +} diff --git a/os/boot/mpc/ureg.h b/os/boot/mpc/ureg.h new file mode 100644 index 00000000..7ccdb492 --- /dev/null +++ b/os/boot/mpc/ureg.h @@ -0,0 +1,43 @@ +struct Ureg +{ + ulong cause; + union { ulong srr1; ulong status;}; + ulong pc; /* SRR0 */ + ulong pad; + ulong lr; + ulong cr; + ulong xer; + ulong ctr; + ulong r0; + union{ ulong r1; ulong sp; ulong usp; }; + ulong r2; + ulong r3; + ulong r4; + ulong r5; + ulong r6; + ulong r7; + ulong r8; + ulong r9; + ulong r10; + ulong r11; + ulong r12; + ulong r13; + ulong r14; + ulong r15; + ulong r16; + ulong r17; + ulong r18; + ulong r19; + ulong r20; + ulong r21; + ulong r22; + ulong r23; + ulong r24; + ulong r25; + ulong r26; + ulong r27; + ulong r28; + ulong r29; + ulong r30; + ulong r31; +}; diff --git a/os/boot/mpc/zqs.c b/os/boot/mpc/zqs.c new file mode 100644 index 00000000..b6296786 --- /dev/null +++ b/os/boot/mpc/zqs.c @@ -0,0 +1,234 @@ +#include "boot.h" +#include "squeeze.h" + +/* + * for details of `unsqueeze' see: + * + * %A Mark Taunton + * %T Compressed Executables: An Exercise in Thinking Small + * %P 385-404 + * %I USENIX + * %B USENIX Conference Proceedings + * %D Summer 1991 + * %C Nashville, TN + * + * several of the unimplemented improvements described in the paper + * have been implemented here + * + * there is a further transformation on the powerpc (QFLAG!=0) to shuffle bits + * in certain instructions so as to push the fixed bits to the top of the word. + */ + +#define EXECHDRLEN (8*4) + +typedef struct Squeeze Squeeze; +struct Squeeze { + int n; + ulong tab[7*256]; +}; + +#define GET4(p) (((((((p)[0]<<8)|(p)[1])<<8)|(p)[2])<<8)|(p)[3]) + +/* + * for speed of unsqueezing from Flash, certain checks are + * not done inside the loop (as they would be in the unsqueeze program zqs), + * but instead the checksum is expected to catch corrupted files. + * in fact the Squeeze array bounds can't be exceeded in practice + * because the tables are always full for a squeezed kernel. + */ +enum { + QFLAG = 1, /* invert powerpc-specific code transformation */ + CHECK = 0, /* check precise bounds in Squeeze array (otherwise checksum detects error) */ +}; + +static ulong chksum; +static int rdtab(Block*, Squeeze*, int); +static ulong* unsqueeze(ulong*, uchar*, uchar*, Squeeze*, Squeeze*, ulong); +static uchar* unsqzseg(uchar*, Block*, long, long, char*); +static Alarm* unsqzal; + +int +issqueezed(uchar *b) +{ + return GET4(b) == SQMAGIC? GET4(b+SQHDRLEN): 0; +} + +static void +unsqzdot(Alarm*) +{ + unsqzal = alarm(500, unsqzdot, nil); + print("."); +} + +long +unsqueezef(Block *b, ulong *entryp) +{ + uchar *loada, *wp; + ulong toptxt, topdat, oldsum; + long asis, nst, nsd; + Sqhdr *sqh; + Exec *ex; + + if(BLEN(b) < SQHDRLEN+EXECHDRLEN) + return -1; + sqh = (Sqhdr*)b->rp; + if(GET4(sqh->magic) != SQMAGIC) + return -1; + chksum = 0; + toptxt = GET4(sqh->toptxt); + topdat = GET4(sqh->topdat); + oldsum = GET4(sqh->sum); + asis = GET4(sqh->asis); + nst = GET4(sqh->text); + nsd = GET4(sqh->data); + b->rp += SQHDRLEN; + ex = (Exec*)b->rp; + if(GET4(ex->magic) != Q_MAGIC){ + print("zqs: not powerPC executable\n"); + return -1; + } + *entryp = GET4(ex->entry); + b->rp += EXECHDRLEN; + loada = KADDR(PADDR(*entryp)); + wp = unsqzseg(loada, b, nst, toptxt, "text"); + if(wp == nil){ + print("zqs: format error\n"); + return -1; + } + if(nsd){ + wp = (uchar*)PGROUND((ulong)wp); + wp = unsqzseg(wp, b, nsd, topdat, "data"); + if(wp == nil){ + print("zqs: format error\n"); + return -1; + } + } + if(asis){ + memmove(wp, b->rp, asis); + wp += asis; + b->rp += asis; + } + if(chksum != oldsum){ + print("\nsqueezed kernel: checksum error: %8.8lux need %8.8lux\n", chksum, oldsum); + return -1; + } + return wp-loada; +} + +static uchar * +unsqzseg(uchar *wp, Block *b, long ns, long top, char *what) +{ + static Squeeze sq3, sq4; + + print("unpack %s %8.8lux %lud:", what, wp, ns); + if(ns == 0) + return wp; + if(rdtab(b, &sq3, 0) < 0) + return nil; + if(rdtab(b, &sq4, 8) < 0) + return nil; + if(BLEN(b) < ns){ + print(" **size error\n"); + return nil; + } + unsqzal = alarm(500, unsqzdot, nil); + wp = (uchar*)unsqueeze((ulong*)wp, b->rp, b->rp+ns, &sq3, &sq4, top); + cancel(unsqzal); + unsqzal = nil; + print("\n"); + if(wp == nil){ + print("zqs: corrupt squeezed data stream\n"); + return nil; + } + b->rp += ns; + return wp; +} + +static ulong* +unsqueeze(ulong *wp, uchar *rp, uchar *ep, Squeeze *sq3, Squeeze *sq4, ulong top) +{ + ulong nx, csum; + int code, n; + + if(QFLAG){ + QREMAP(top); /* adjust top just once, outside the loop */ + } + csum = chksum; + while(rp < ep){ + /* no function calls within this loop for speed */ + code = *rp; + rp++; + n = 0; + nx = code>>4; + do{ + if(nx == 0){ + nx = top; + }else{ + if(nx==1){ + nx = (((((rp[3]<<8)|rp[2])<<8)|rp[1])<<8)|rp[0]; + rp += 4; + }else if(nx <= 8){ /* 2 to 8 */ + nx = ((nx-2)<<8) | rp[0]; + if(CHECK && nx >= sq4->n) + return nil; /* corrupted file */ + nx = sq4->tab[nx] | rp[1]; + rp += 2; + }else{ /* 9 to 15 */ + nx = ((nx-9)<<8) | rp[0]; + if(CHECK && nx >= sq3->n) + return nil; /* corrupted file */ + nx = sq3->tab[nx]; + rp++; + } + if(rp > ep) + return nil; /* corrupted file */ + if(QFLAG){ + QREMAP(nx); + } + } + *wp = nx; + wp++; + csum += nx; + nx = code & 0xF; + }while(++n == 1); + } + chksum = csum; + return wp; +} + +static int +rdtab(Block *b, Squeeze *sq, int shift) +{ + uchar *p, *ep; + ulong v, w; + int i; + + if(BLEN(b) < 2) + return -1; + i = (b->rp[0]<<8) | b->rp[1]; + if(1) + print(" T%d", i); + b->rp += 2; + if((i -= 2) > 0){ + if(BLEN(b) < i) + return -1; + } + sq->n = 0; + p = b->rp; + ep = b->rp+i; + b->rp += i; + v = 0; + while(p < ep){ + w = 0; + do{ + if(p >= ep) + return -1; + w = (w<<7) | (*p & 0x7F); + }while(*p++ & 0x80); + v += w; + if(0) + print("%d %8.8lux %8.8lux\n", sq->n, v, w); + sq->tab[sq->n++] = v<port + r, (u)->sticky[r] | (v)) +#define uartrdreg(u,r) inb((u)->port + r) + +/* + * set the baud rate by calculating and setting the baudrate + * generator constant. This will work with fairly non-standard + * baud rates. + */ +static void +uartsetbaud(Uart *up, int rate) +{ + ulong brconst; + + brconst = (UartFREQ+8*rate-1)/(16*rate); + + uartwrreg(up, Format, Dra); + outb(up->port+Dmsb, (brconst>>8) & 0xff); + outb(up->port+Dlsb, brconst & 0xff); + uartwrreg(up, Format, 0); +} + +/* + * toggle DTR + */ +static void +uartdtr(Uart *up, int n) +{ + if(n) + up->sticky[Mctl] |= Dtr; + else + up->sticky[Mctl] &= ~Dtr; + uartwrreg(up, Mctl, 0); +} + +/* + * toggle RTS + */ +static void +uartrts(Uart *up, int n) +{ + if(n) + up->sticky[Mctl] |= Rts; + else + up->sticky[Mctl] &= ~Rts; + uartwrreg(up, Mctl, 0); +} + +static void +uartintr(Ureg*, void *arg) +{ + Uart *up; + int ch; + int s, l, loops; + + up = arg; + for(loops = 0; loops < 1024; loops++){ + s = uartrdreg(up, Istat); + switch(s & 0x3F){ + case 6: /* receiver line status */ + l = uartrdreg(up, Lstat); + if(l & Ferror) + up->frame++; + if(l & Oerror) + up->overrun++; + break; + + case 4: /* received data available */ + case 12: + ch = inb(up->port+Data); + if(up->rx) + (*up->rx)(ch); + break; + + case 2: /* transmitter empty */ + ch = -1; + if(up->tx) + ch = (*up->tx)(); + if(ch != -1) + outb(up->port+Data, ch); + else + up->txbusy = 0; + break; + + case 0: /* modem status */ + uartrdreg(up, Mstat); + break; + + default: + if(s&1) + return; + print("weird modem interrupt #%2.2ux\n", s); + break; + } + } + panic("uartintr: 0x%2.2ux\n", uartrdreg(up, Istat)); +} + +/* + * turn on a port's interrupts. set DTR and RTS + */ +static void +uartenable(Uart *up) +{ + /* + * turn on interrupts + */ + up->sticky[Iena] = 0; + if(up->tx) + up->sticky[Iena] |= Ixmt; + if(up->rx) + up->sticky[Iena] |= Ircv|Irstat; + uartwrreg(up, Iena, 0); + + /* + * turn on DTR and RTS + */ + uartdtr(up, 1); + uartrts(up, 1); +} + +static void +uartdisable(Uart* up) +{ + /* + * Disable interrupts. + */ + up->sticky[Iena] = 0; + uartwrreg(up, Iena, 0); + uartdtr(up, 0); + uartrts(up, 0); +} + +void +uartspecial(int port, void (*rx)(int), int (*tx)(void), int baud) +{ + Uart *up; + int vector; + + switch(port){ + case 0: + port = 0x3F8; + vector = VectorUART0; + up = &com[0]; + break; + case 1: + port = 0x2F8; + vector = VectorUART1; + up = &com[1]; + break; + default: + return; + } + + if(uart != nil && uart != up) + uartdisable(uart); + uart = up; + + if(up->port == 0){ + up->port = port; + setvec(vector, uartintr, up); + } + + /* + * set rate to 9600 baud. + * 8 bits/character. + * 1 stop bit. + * interrupts enabled. + */ + uartsetbaud(up, 9600); + up->sticky[Format] = Bits8; + uartwrreg(up, Format, 0); + up->sticky[Mctl] |= Inton; + uartwrreg(up, Mctl, 0x0); + + up->rx = rx; + up->tx = tx; + uartenable(up); + if(baud) + uartsetbaud(up, baud); +} + +void +uartputc(int c) +{ + int i; + Uart *up; + + if((up = uart) == nil) + return; + for(i = 0; i < 100; i++){ + if(uartrdreg(up, Lstat) & Outready) + break; + delay(1); + } + outb(up->port+Data, c); +} + +void +uartputs(IOQ *q, char *s, int n) +{ + Uart *up; + int c, x; + + if((up = uart) == nil) + return; + while(n--){ + if(*s == '\n') + q->putc(q, '\r'); + q->putc(q, *s++); + } + x = splhi(); + if(up->txbusy == 0 && (c = q->getc(q)) != -1){ + uartputc(c & 0xFF); + up->txbusy = 1; + } + splx(x); +} + +void +uartdrain(void) +{ + Uart *up; + int timeo; + + if((up = uart) == nil) + return; + for(timeo = 0; timeo < 10000 && up->txbusy; timeo++) + delay(1); +} diff --git a/os/boot/pc/LICENCE b/os/boot/pc/LICENCE new file mode 100644 index 00000000..a4418218 --- /dev/null +++ b/os/boot/pc/LICENCE @@ -0,0 +1,237 @@ +Lucent Public License Version 1.02 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS PUBLIC +LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE +PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + + a. in the case of Lucent Technologies Inc. ("LUCENT"), the Original + Program, and + b. in the case of each Contributor, + + i. changes to the Program, and + ii. additions to the Program; + + where such changes and/or additions to the Program were added to the + Program by such Contributor itself or anyone acting on such + Contributor's behalf, and the Contributor explicitly consents, in + accordance with Section 3C, to characterization of the changes and/or + additions as Contributions. + +"Contributor" means LUCENT and any other entity that has Contributed a +Contribution to the Program. + +"Distributor" means a Recipient that distributes the Program, +modifications to the Program, or any part thereof. + +"Licensed Patents" mean patent claims licensable by a Contributor +which are necessarily infringed by the use or sale of its Contribution +alone or when combined with the Program. + +"Original Program" means the original version of the software +accompanying this Agreement as released by LUCENT, including source +code, object code and documentation, if any. + +"Program" means the Original Program and Contributions or any part +thereof + +"Recipient" means anyone who receives the Program under this +Agreement, including all Contributors. + +2. GRANT OF RIGHTS + + a. Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free copyright + license to reproduce, prepare derivative works of, publicly display, + publicly perform, distribute and sublicense the Contribution of such + Contributor, if any, and such derivative works, in source code and + object code form. + + b. Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free patent + license under Licensed Patents to make, use, sell, offer to sell, + import and otherwise transfer the Contribution of such Contributor, if + any, in source code and object code form. The patent license granted + by a Contributor shall also apply to the combination of the + Contribution of that Contributor and the Program if, at the time the + Contribution is added by the Contributor, such addition of the + Contribution causes such combination to be covered by the Licensed + Patents. The patent license granted by a Contributor shall not apply + to (i) any other combinations which include the Contribution, nor to + (ii) Contributions of other Contributors. No hardware per se is + licensed hereunder. + + c. Recipient understands that although each Contributor grants the + licenses to its Contributions set forth herein, no assurances are + provided by any Contributor that the Program does not infringe the + patent or other intellectual property rights of any other entity. Each + Contributor disclaims any liability to Recipient for claims brought by + any other entity based on infringement of intellectual property rights + or otherwise. As a condition to exercising the rights and licenses + granted hereunder, each Recipient hereby assumes sole responsibility + to secure any other intellectual property rights needed, if any. For + example, if a third party patent license is required to allow + Recipient to distribute the Program, it is Recipient's responsibility + to acquire that license before distributing the Program. + + d. Each Contributor represents that to its knowledge it has sufficient + copyright rights in its Contribution, if any, to grant the copyright + license set forth in this Agreement. + +3. REQUIREMENTS + +A. Distributor may choose to distribute the Program in any form under +this Agreement or under its own license agreement, provided that: + + a. it complies with the terms and conditions of this Agreement; + + b. if the Program is distributed in source code or other tangible + form, a copy of this Agreement or Distributor's own license agreement + is included with each copy of the Program; and + + c. if distributed under Distributor's own license agreement, such + license agreement: + + i. effectively disclaims on behalf of all Contributors all warranties + and conditions, express and implied, including warranties or + conditions of title and non-infringement, and implied warranties or + conditions of merchantability and fitness for a particular purpose; + ii. effectively excludes on behalf of all Contributors all liability + for damages, including direct, indirect, special, incidental and + consequential damages, such as lost profits; and + iii. states that any provisions which differ from this Agreement are + offered by that Contributor alone and not by any other party. + +B. Each Distributor must include the following in a conspicuous + location in the Program: + + Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights + Reserved. + +C. In addition, each Contributor must identify itself as the +originator of its Contribution in a manner that reasonably allows +subsequent Recipients to identify the originator of the Contribution. +Also, each Contributor must agree that the additions and/or changes +are intended to be a Contribution. Once a Contribution is contributed, +it may not thereafter be revoked. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain +responsibilities with respect to end users, business partners and the +like. While this license is intended to facilitate the commercial use +of the Program, the Distributor who includes the Program in a +commercial product offering should do so in a manner which does not +create potential liability for Contributors. Therefore, if a +Distributor includes the Program in a commercial product offering, +such Distributor ("Commercial Distributor") hereby agrees to defend +and indemnify every Contributor ("Indemnified Contributor") against +any losses, damages and costs (collectively"Losses") arising from +claims, lawsuits and other legal actions brought by a third party +against the Indemnified Contributor to the extent caused by the acts +or omissions of such Commercial Distributor in connection with its +distribution of the Program in a commercial product offering. The +obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. +In order to qualify, an Indemnified Contributor must: a) promptly +notify the Commercial Distributor in writing of such claim, and b) +allow the Commercial Distributor to control, and cooperate with the +Commercial Distributor in, the defense and any related settlement +negotiations. The Indemnified Contributor may participate in any such +claim at its own expense. + +For example, a Distributor might include the Program in a commercial +product offering, Product X. That Distributor is then a Commercial +Distributor. If that Commercial Distributor then makes performance +claims, or offers warranties related to Product X, those performance +claims and warranties are such Commercial Distributor's responsibility +alone. Under this section, the Commercial Distributor would have to +defend claims against the Contributors related to those performance +claims and warranties, and if a court requires any Contributor to pay +any damages as a result, the Commercial Distributor must pay those +damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS +PROVIDED ON AN"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY +WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY +OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely +responsible for determining the appropriateness of using and +distributing the Program and assumes all risks associated with its +exercise of rights under this Agreement, including but not limited to +the risks and costs of program errors, compliance with applicable +laws, damage to or loss of data, programs or equipment, and +unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR +ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING +WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR +DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED +HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. EXPORT CONTROL + +Recipient agrees that Recipient alone is responsible for compliance +with the United States export administration regulations (and the +export control laws and regulation of any other countries). + +8. GENERAL + +If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of +the remainder of the terms of this Agreement, and without further +action by the parties hereto, such provision shall be reformed to the +minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against a Contributor with +respect to a patent applicable to software (including a cross-claim or +counterclaim in a lawsuit), then any patent licenses granted by that +Contributor to such Recipient under this Agreement shall terminate as +of the date such litigation is filed. In addition, if Recipient +institutes patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Program +itself (excluding combinations of the Program with other software or +hardware) infringes such Recipient's patent(s), then such Recipient's +rights granted under Section 2(b) shall terminate as of the date such +litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it +fails to comply with any of the material terms or conditions of this +Agreement and does not cure such failure in a reasonable period of +time after becoming aware of such noncompliance. If all Recipient's +rights under this Agreement terminate, Recipient agrees to cease use +and distribution of the Program as soon as reasonably practicable. +However, Recipient's obligations under this Agreement and any licenses +granted by Recipient relating to the Program shall continue and +survive. + +LUCENT may publish new versions (including revisions) of this +Agreement from time to time. Each new version of the Agreement will be +given a distinguishing version number. The Program (including +Contributions) may always be distributed subject to the version of the +Agreement under which it was received. In addition, after a new +version of the Agreement is published, Contributor may elect to +distribute the Program (including its Contributions) under the new +version. No one other than LUCENT has the right to modify this +Agreement. Except as expressly stated in Sections 2(a) and 2(b) above, +Recipient receives no rights or licenses to the intellectual property +of any Contributor under this Agreement, whether expressly, by +implication, estoppel or otherwise. All rights in the Program not +expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and +the intellectual property laws of the United States of America. No +party to this Agreement will bring a legal action under this Agreement +more than one year after the cause of action arose. Each party waives +its rights to a jury trial in any resulting litigation. + diff --git a/os/boot/pc/NOTICE b/os/boot/pc/NOTICE new file mode 100644 index 00000000..2982c51d --- /dev/null +++ b/os/boot/pc/NOTICE @@ -0,0 +1,4 @@ +Copyright © 1995-2006 Lucent Technologies Inc. and others. All rights reserved. +Revisions for use with Inferno © 2003-2006 Vita Nuova Holdings Limited. + +This software is provided under the terms of the Lucent Public License, Version 1.02. diff --git a/os/boot/pc/alarm.c b/os/boot/pc/alarm.c new file mode 100644 index 00000000..2aa4431a --- /dev/null +++ b/os/boot/pc/alarm.c @@ -0,0 +1,123 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#define MAXALARM 10 + +Alarm alarmtab[MAXALARM]; + +/* + * Insert new into list after where + */ +void +insert(List **head, List *where, List *new) +{ + if(where == 0){ + new->next = *head; + *head = new; + }else{ + new->next = where->next; + where->next = new; + } + +} + +/* + * Delete old from list. where->next is known to be old. + */ +void +delete(List **head, List *where, List *old) +{ + if(where == 0){ + *head = old->next; + return; + } + where->next = old->next; +} + +Alarm* +newalarm(void) +{ + int i; + Alarm *a; + + for(i=0,a=alarmtab; i < nelem(alarmtab); i++,a++) + if(a->busy==0 && a->f==0){ + a->f = 0; + a->arg = 0; + a->busy = 1; + return a; + } + panic("newalarm"); + return 0; /* not reached */ +} + +Alarm* +alarm(int ms, void (*f)(Alarm*), void *arg) +{ + Alarm *a, *w, *pw; + ulong s; + + if(ms < 0) + ms = 0; + s = splhi(); + a = newalarm(); + a->dt = MS2TK(ms); + a->f = f; + a->arg = arg; + pw = 0; + for(w=m->alarm; w; pw=w, w=w->next){ + if(w->dt <= a->dt){ + a->dt -= w->dt; + continue; + } + w->dt -= a->dt; + break; + } + insert(&m->alarm, pw, a); + splx(s); + return a; +} + +void +cancel(Alarm *a) +{ + a->f = 0; +} + +void +alarminit(void) +{ +} + +#define NA 10 /* alarms per clock tick */ +void +checkalarms(void) +{ + int i, n, s; + Alarm *a; + void (*f)(Alarm*); + Alarm *alist[NA]; + + s = splhi(); + a = m->alarm; + if(a){ + for(n=0; a && a->dt<=0 && nalarm, 0, a); + a = m->alarm; + } + if(a) + a->dt--; + + for(i = 0; i < n; i++){ + f = alist[i]->f; /* avoid race with cancel */ + if(f) + (*f)(alist[i]); + alist[i]->busy = 0; + } + } + splx(s); +} diff --git a/os/boot/pc/apm.c b/os/boot/pc/apm.c new file mode 100644 index 00000000..6c662d6c --- /dev/null +++ b/os/boot/pc/apm.c @@ -0,0 +1,16 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +Apminfo apm; + +void +apminit(void) +{ + if(getconf("apm0") && apm.haveinfo) + changeconf("apm0=ax=%x ebx=%x cx=%x dx=%x di=%x esi=%x\n", + apm.ax, apm.ebx, apm.cx, apm.dx, apm.di, apm.esi); +} diff --git a/os/boot/pc/bcom.c b/os/boot/pc/bcom.c new file mode 100644 index 00000000..62a50a10 --- /dev/null +++ b/os/boot/pc/bcom.c @@ -0,0 +1,460 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "fs.h" + +Type types[] = { + { Tfloppy, + Fini|Ffs, + floppyinit, floppyinitdev, + floppygetfspart, 0, floppyboot, + }, + { Tsd, + Fini|Ffs, + sdinit, sdinitdev, + sdgetfspart, sdaddconf, sdboot, + }, + { Tnil, + 0, + 0, 0, + 0, 0, 0, + }, +}; + +#include "sd.h" + +extern SDifc sdataifc; +extern SDifc sdmylexifc; +extern SDifc sd53c8xxifc; +SDifc* sdifc[] = { + &sdataifc, +// &sdmylexifc, +// &sd53c8xxifc, + nil, +}; + +typedef struct Mode Mode; + +enum { + Maxdev = 7, + Dany = -1, + Nmedia = 16, + Nini = 10, +}; + +enum { /* mode */ + Mauto = 0x00, + Mlocal = 0x01, + Manual = 0x02, + NMode = 0x03, +}; + +typedef struct Medium Medium; +struct Medium { + Type* type; + int flag; + int dev; + char name[NAMELEN]; + Fs* inifs; + + Medium* next; +}; + +typedef struct Mode { + char* name; + int mode; +} Mode; + +static Medium media[Nmedia]; +static Medium *curmedium = media; + +static Mode modes[NMode+1] = { + [Mauto] { "auto", Mauto, }, + [Mlocal] { "local", Mlocal, }, + [Manual] { "manual", Manual, }, +}; + +char *defaultpartition = "new"; + +static Medium* +parse(char *line, char **file) +{ + char *p; + Type *tp; + Medium *mp; + + if(p = strchr(line, '!')) { + *p++ = 0; + *file = p; + } else + *file = ""; + + for(tp = types; tp->type != Tnil; tp++) + for(mp = tp->media; mp; mp = mp->next) + if(strcmp(mp->name, line) == 0) + return mp; + return nil; +} + +static int +boot(Medium *mp, char *file) +{ + static Boot b; + + memset(&b, 0, sizeof b); + b.state = INIT9LOAD; + +// sprint(BOOTLINE, "%s!%s", mp->name, file); + return (*mp->type->boot)(mp->dev, file, &b); +} + +static Medium* +allocm(Type *tp) +{ + Medium **l; + + if(curmedium >= &media[Nmedia]) + return 0; + + for(l = &tp->media; *l; l = &(*l)->next) + ; + *l = curmedium++; + return *l; +} + +char *parts[] = { "dos", "9fat", "fs", 0 }; + +Medium* +probe(int type, int flag, int dev) +{ + Type *tp; + int i; + Medium *mp; + + for(tp = types; tp->type != Tnil; tp++){ + if(type != Tany && type != tp->type) + continue; + + if(flag != Fnone){ + for(mp = tp->media; mp; mp = mp->next){ + if((flag & mp->flag) && (dev == Dany || dev == mp->dev)) + return mp; + } + } + + if((tp->flag & Fprobe) == 0){ + tp->flag |= Fprobe; + tp->mask = (*tp->init)(); + } + + for(i = 0; tp->mask; i++){ + if((tp->mask & (1<mask &= ~(1<dev = i; + mp->flag = tp->flag; + mp->type = tp; + (*tp->initdev)(i, mp->name); + + if((flag & mp->flag) && (dev == Dany || dev == i)) + return mp; + } + } + + return 0; +} + +extern int loopconst; +void +main(void) +{ + Medium *mp; + int flag; + char def[2*NAMELEN], line[80], *p, *file; + Type *tp; + + i8042a20(); + memset(m, 0, sizeof(Mach)); + trapinit(); + clockinit(); + alarminit(); + spllo(); + + kbdinit(); + + if((ulong)&end > (KZERO|(640*1024))) + panic("i'm too big"); + + /* + * If there were any arguments, MS-DOS leaves a character + * count followed by the arguments in the runtime header. + * Step over the leading space. + */ + p = (char*)0x80080080; + if(p[0]){ + p[p[0]+1] = 0; + p += 2; + } + else + p = 0; + + /* + * Advance command line to first option, if any + */ + if(p) { + while(*p==' ' || *p=='\t') + p++; + if(*p == 0) + p = nil; + } + + /* + * Probe everything, to collect device names. + */ + probe(Tany, Fnone, Dany); + + if(p != 0) { + if((mp = parse(p, &file)) == nil) { + print("bad loadfile syntax: %s\n", p); + goto done; + } + boot(mp, file); + } + +done: + flag = 0; + for(tp = types; tp->type != Tnil; tp++){ + for(mp = tp->media; mp; mp = mp->next){ + if(flag == 0){ + flag = 1; + print("Load devices:"); + } + print(" %s", mp->name); + } + } + if(flag) + print("\n"); + + for(;;){ + if(getstr("load from", line, sizeof(line), nil, 0) >= 0) + if(mp = parse(line, &file)) + boot(mp, file); + def[0] = 0; + } +} + +int +getfields(char *lp, char **fields, int n, char sep) +{ + int i; + + for(i = 0; lp && *lp && i < n; i++){ + while(*lp == sep) + *lp++ = 0; + if(*lp == 0) + break; + fields[i] = lp; + while(*lp && *lp != sep){ + if(*lp == '\\' && *(lp+1) == '\n') + *lp++ = ' '; + lp++; + } + } + return i; +} + +int +cistrcmp(char *a, char *b) +{ + int ac, bc; + + for(;;){ + ac = *a++; + bc = *b++; + + if(ac >= 'A' && ac <= 'Z') + ac = 'a' + (ac - 'A'); + if(bc >= 'A' && bc <= 'Z') + bc = 'a' + (bc - 'A'); + ac -= bc; + if(ac) + return ac; + if(bc == 0) + break; + } + return 0; +} + +int +cistrncmp(char *a, char *b, int n) +{ + unsigned ac, bc; + + while(n > 0){ + ac = *a++; + bc = *b++; + n--; + + if(ac >= 'A' && ac <= 'Z') + ac = 'a' + (ac - 'A'); + if(bc >= 'A' && bc <= 'Z') + bc = 'a' + (bc - 'A'); + + ac -= bc; + if(ac) + return ac; + if(bc == 0) + break; + } + + return 0; +} + +void* +ialloc(ulong n, int align) +{ + + static ulong palloc; + ulong p; + int a; + + if(palloc == 0) + palloc = 3*1024*1024; + + p = palloc; + if(align <= 0) + align = 4; + if(a = n % align) + n += align - a; + if(a = p % align) + p += align - a; + + palloc = p+n; + + return memset((void*)(p|KZERO), 0, n); +} + +void* +xspanalloc(ulong size, int align, ulong span) +{ + ulong a, v; + + a = (ulong)ialloc(size+align+span, 0); + + if(span > 2) + v = (a + span) & ~(span-1); + else + v = a; + + if(align > 1) + v = (v + align) & ~(align-1); + + return (void*)v; +} + +static Block *allocbp; + +Block* +allocb(int size) +{ + Block *bp, **lbp; + ulong addr; + + lbp = &allocbp; + for(bp = *lbp; bp; bp = bp->next){ + if((bp->lim - bp->base) >= size){ + *lbp = bp->next; + break; + } + lbp = &bp->next; + } + if(bp == 0){ + bp = ialloc(sizeof(Block)+size+64, 0); + addr = (ulong)bp; + addr = ROUNDUP(addr + sizeof(Block), 8); + bp->base = (uchar*)addr; + bp->lim = ((uchar*)bp) + sizeof(Block)+size+64; + } + + if(bp->flag) + panic("allocb reuse\n"); + + bp->rp = bp->base; + bp->wp = bp->rp; + bp->next = 0; + bp->flag = 1; + + return bp; +} + +void +freeb(Block* bp) +{ + bp->next = allocbp; + allocbp = bp; + + bp->flag = 0; +} + +enum { + Paddr= 0x70, /* address port */ + Pdata= 0x71, /* data port */ +}; + +uchar +nvramread(int offset) +{ + outb(Paddr, offset); + return inb(Pdata); +} + +void (*etherdetach)(void); +void (*floppydetach)(void); +void (*sddetach)(void); + +void +warp9(ulong entry) +{ + if(etherdetach) + etherdetach(); + consdrain(); + (*(void(*)(void))(PADDR(entry)))(); +} + +char* +getconf(char*) +{ + return nil; +} + +void +addconf(char*, ...) +{ +} + +void +uartspecial(int, void(*)(int), int(*)(void), int) +{ +} + +void +uartputs(IOQ*, char*, int) +{ +} + +void +uartputc(int) +{} + +void +uartdrain(void) +{ +} diff --git a/os/boot/pc/boot.c b/os/boot/pc/boot.c new file mode 100644 index 00000000..0659edbd --- /dev/null +++ b/os/boot/pc/boot.c @@ -0,0 +1,451 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "../../../utils/libmach/elf.h" + +static uchar elfident[7] = { + '\177', 'E', 'L', 'F', '\1', '\1', '\1' +}; +static Ehdr ehdr, rehdr; +static Phdr *phdr; +static int curphdr; +static ulong curoff; +static ulong elftotal; +static long (*swal)(long); +static ushort (*swab)(ushort); + +/* + * big-endian short + */ +ushort +beswab(ushort s) +{ + uchar *p; + + p = (uchar*)&s; + return (p[0]<<8) | p[1]; +} + +/* + * big-endian long + */ +long +beswal(long l) +{ + uchar *p; + + p = (uchar*)&l; + return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; +} + +/* + * little-endian short + */ +ushort +leswab(ushort s) +{ + uchar *p; + + p = (uchar*)&s; + return (p[1]<<8) | p[0]; +} + +/* + * little-endian long + */ +long +leswal(long l) +{ + uchar *p; + + p = (uchar*)&l; + return (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0]; +} + +/* + * Convert header to canonical form + */ +static void +hswal(long *lp, int n, long (*swap) (long)) +{ + while (n--) { + *lp = (*swap) (*lp); + lp++; + } +} + +static int +readehdr(Boot *b) +{ + int i; + + /* bitswap the header according to the DATA format */ + if(ehdr.ident[CLASS] != ELFCLASS32) { + print("bad ELF class - not 32 bit\n"); + return 0; + } + if(ehdr.ident[DATA] == ELFDATA2LSB) { + swab = leswab; + swal = leswal; + } else if(ehdr.ident[DATA] == ELFDATA2MSB) { + swab = beswab; + swal = beswal; + } else { + print("bad ELF encoding - not big or little endian\n"); + return 0; + } + memmove(&rehdr, &ehdr, sizeof(Ehdr)); + + ehdr.type = swab(ehdr.type); + ehdr.machine = swab(ehdr.machine); + ehdr.version = swal(ehdr.version); + ehdr.elfentry = swal(ehdr.elfentry); + ehdr.phoff = swal(ehdr.phoff); + ehdr.shoff = swal(ehdr.shoff); + ehdr.flags = swal(ehdr.flags); + ehdr.ehsize = swab(ehdr.ehsize); + ehdr.phentsize = swab(ehdr.phentsize); + ehdr.phnum = swab(ehdr.phnum); + ehdr.shentsize = swab(ehdr.shentsize); + ehdr.shnum = swab(ehdr.shnum); + ehdr.shstrndx = swab(ehdr.shstrndx); + if(ehdr.type != EXEC || ehdr.version != CURRENT) + return 0; + if(ehdr.phentsize != sizeof(Phdr)) + return 0; + + if(debug) + print("readehdr OK entry 0x%lux\n", ehdr.elfentry); + + curoff = sizeof(Ehdr); + i = ehdr.phoff+ehdr.phentsize*ehdr.phnum - curoff; + b->state = READPHDR; + b->bp = (char*)malloc(i); + b->wp = b->bp; + b->ep = b->wp + i; + phdr = (Phdr*)(b->bp + ehdr.phoff-sizeof(Ehdr)); + if(debug) + print("phdr..."); + + return 1; +} + +static int +nextphdr(Boot *b) +{ + Phdr *php; + ulong entry, offset; + char *paddr; + + if(debug) + print("readedata %d\n", curphdr); + + for(; curphdr < ehdr.phnum; curphdr++){ + php = phdr+curphdr; + if(php->type != LOAD) + continue; + offset = php->offset; + paddr = (char*)PADDR(php->paddr); + if(offset < curoff){ + /* + * Can't (be bothered to) rewind the + * input, it might be from tftp. If we + * did then we could boot FreeBSD kernels + * too maybe. + */ + return 0; + } + if(php->offset > curoff){ + b->state = READEPAD; + b->bp = (char*)malloc(offset - curoff); + b->wp = b->bp; + b->ep = b->wp + offset - curoff; + if(debug) + print("nextphdr %lud...\n", offset - curoff); + return 1; + } + b->state = READEDATA; + b->bp = paddr; + b->wp = b->bp; + b->ep = b->wp+php->filesz; + print("%ud+", php->filesz); + elftotal += php->filesz; + if(debug) + print("nextphdr %ud@0x%p\n", php->filesz, paddr); + + return 1; + } + + if(curphdr != 0){ + print("=%lud\n", elftotal); + b->state = TRYBOOT; + entry = ehdr.elfentry & ~0xF0000000; + PLLONG(b->exec.entry, entry); + return 1; + } + + return 0; +} + +static int +readepad(Boot *b) +{ + Phdr *php; + + php = phdr+curphdr; + if(debug) + print("readepad %d\n", curphdr); + curoff = php->offset; + + return nextphdr(b); +} + +static int +readedata(Boot *b) +{ + Phdr *php; + + php = phdr+curphdr; + if(debug) + print("readedata %d\n", curphdr); + if(php->filesz < php->memsz){ + print("%lud", php->memsz-php->filesz); + elftotal += php->memsz-php->filesz; + memset((char*)(PADDR(php->paddr)+php->filesz), 0, php->memsz-php->filesz); + } + curoff = php->offset+php->filesz; + curphdr++; + + return nextphdr(b); +} + +static int +readphdr(Boot *b) +{ + Phdr *php; + + php = phdr; + hswal((long*)php, ehdr.phentsize*ehdr.phnum/sizeof(long), swal); + if(debug) + print("phdr curoff %lud vaddr 0x%lux paddr 0x%lux\n", + curoff, php->vaddr, php->paddr); + + curoff = ehdr.phoff+ehdr.phentsize*ehdr.phnum; + curphdr = 0; + + return nextphdr(b); +} + +static int +addbytes(char **dbuf, char *edbuf, char **sbuf, char *esbuf) +{ + int n; + + n = edbuf - *dbuf; + if(n <= 0) + return 0; + if(n > esbuf - *sbuf) + n = esbuf - *sbuf; + if(n <= 0) + return -1; + + memmove(*dbuf, *sbuf, n); + *sbuf += n; + *dbuf += n; + return edbuf - *dbuf; +} + +int +bootpass(Boot *b, void *vbuf, int nbuf) +{ + char *buf, *ebuf; + Exec *ep; + ulong entry, data, text, bss; + + if(b->state == FAILED) + return FAIL; + + if(nbuf == 0) + goto Endofinput; + + buf = vbuf; + ebuf = buf+nbuf; + while(addbytes(&b->wp, b->ep, &buf, ebuf) == 0) { + switch(b->state) { + case INITKERNEL: + b->state = READEXEC; + b->bp = (char*)&b->exec; + b->wp = b->bp; + b->ep = b->bp+sizeof(Exec); + break; + case READEXEC: + ep = &b->exec; + if(GLLONG(ep->magic) == I_MAGIC) { + b->state = READ9TEXT; + b->bp = (char*)PADDR(GLLONG(ep->entry)); + b->wp = b->bp; + b->ep = b->wp+GLLONG(ep->text); + print("%lud", GLLONG(ep->text)); + break; + } + + /* check for gzipped kernel */ + if(b->bp[0] == 0x1F && (uchar)b->bp[1] == 0x8B && b->bp[2] == 0x08) { + b->state = READGZIP; + b->bp = (char*)malloc(1440*1024); + b->wp = b->bp; + b->ep = b->wp + 1440*1024; + memmove(b->bp, &b->exec, sizeof(Exec)); + b->wp += sizeof(Exec); + print("gz..."); + break; + } + + /* + * Check for ELF. + */ + if(memcmp(b->bp, elfident, 4) == 0){ + b->state = READEHDR; + b->bp = (char*)&ehdr; + b->wp = b->bp; + b->ep = b->wp + sizeof(Ehdr); + memmove(b->bp, &b->exec, sizeof(Exec)); + b->wp += sizeof(Exec); + print("elf..."); + break; + } + + print("bad kernel format\n"); + b->state = FAILED; + return FAIL; + + case READ9TEXT: + ep = &b->exec; + b->state = READ9DATA; + b->bp = (char*)PGROUND(PADDR(GLLONG(ep->entry))+GLLONG(ep->text)); + b->wp = b->bp; + b->ep = b->wp + GLLONG(ep->data); + print("+%ld", GLLONG(ep->data)); + break; + + case READ9DATA: + ep = &b->exec; + bss = GLLONG(ep->bss); + print("+%ld=%ld\n", + bss, GLLONG(ep->text)+GLLONG(ep->data)+bss); + b->state = TRYBOOT; + return ENOUGH; + + case READEHDR: + if(!readehdr(b)){ + print("readehdr failed\n"); + b->state = FAILED; + return FAIL; + } + break; + + case READPHDR: + if(!readphdr(b)){ + b->state = FAILED; + return FAIL; + } + break; + + case READEPAD: + if(!readepad(b)){ + b->state = FAILED; + return FAIL; + } + break; + + case READEDATA: + if(!readedata(b)){ + b->state = FAILED; + return FAIL; + } + if(b->state == TRYBOOT) + return ENOUGH; + break; + + case TRYBOOT: + case READGZIP: + return ENOUGH; + + case READ9LOAD: + case INIT9LOAD: + panic("9load"); + + default: + panic("bootstate"); + } + } + return MORE; + + +Endofinput: + /* end of input */ + switch(b->state) { + case INITKERNEL: + case READEXEC: + case READ9TEXT: + case READ9DATA: + case READEHDR: + case READPHDR: + case READEPAD: + case READEDATA: + print("premature EOF\n"); + b->state = FAILED; + return FAIL; + + case TRYBOOT: + entry = GLLONG(b->exec.entry); + print("entry: 0x%lux\n", entry); + warp9(PADDR(entry)); + b->state = FAILED; + return FAIL; + + case READGZIP: + ep = &b->exec; + if(b->bp[0] != 0x1F || (uchar)b->bp[1] != 0x8B || b->bp[2] != 0x08) + print("lost magic\n"); + + print("%ld => ", b->wp - b->bp); + if(gunzip((uchar*)ep, sizeof(*ep), (uchar*)b->bp, b->wp - b->bp) < sizeof(*ep)) { + print("badly compressed kernel\n"); + return FAIL; + } + + entry = GLLONG(ep->entry); + text = GLLONG(ep->text); + data = GLLONG(ep->data); + bss = GLLONG(ep->bss); + print("%lud+%lud+%lud=%lud\n", text, data, bss, text+data+bss); + + if(gunzip((uchar*)PADDR(entry)-sizeof(Exec), sizeof(Exec)+text+data, + (uchar*)b->bp, b->wp-b->bp) < sizeof(Exec)+text+data) { + print("error uncompressing kernel\n"); + return FAIL; + } + + /* relocate data to start at page boundary */ + memmove((void*)PGROUND(PADDR(entry+text)), (void*)(PADDR(entry+text)), data); + + print("entry: %lux\n", entry); + warp9(PADDR(entry)); + b->state = FAILED; + return FAIL; + + case INIT9LOAD: + case READ9LOAD: + panic("end 9load"); + + default: + panic("bootdone"); + } + b->state = FAILED; + return FAIL; +} diff --git a/os/boot/pc/bootld.c b/os/boot/pc/bootld.c new file mode 100644 index 00000000..e60b2389 --- /dev/null +++ b/os/boot/pc/bootld.c @@ -0,0 +1,108 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +static int +addbytes(char **dbuf, char *edbuf, char **sbuf, char *esbuf) +{ + int n; + + n = edbuf - *dbuf; + if(n <= 0) + return 0; + if(n > esbuf - *sbuf) + n = esbuf - *sbuf; + if(n <= 0) + return -1; + + memmove(*dbuf, *sbuf, n); + *sbuf += n; + *dbuf += n; + return edbuf - *dbuf; +} + +extern void origin(void); + +int +bootpass(Boot *b, void *vbuf, int nbuf) +{ + char *buf, *ebuf, *p, *q; + ulong size; + + if(b->state == FAILED) + return FAIL; + + if(nbuf == 0) + goto Endofinput; + + buf = vbuf; + ebuf = buf+nbuf; + while(addbytes(&b->wp, b->ep, &buf, ebuf) == 0) { + switch(b->state) { + case INIT9LOAD: + b->state = READ9LOAD; + b->bp = (char*)0x10000; + b->wp = b->bp; + b->ep = b->bp + 256*1024; + break; + + case READ9LOAD: + return ENOUGH; + + default: + panic("bootstate"); + } + } + return MORE; + + +Endofinput: + /* end of input */ + print("\n"); + switch(b->state) { + case INIT9LOAD: + print("premature EOF\n"); + b->state = FAILED; + return FAIL; + + case READ9LOAD: + size = b->wp - b->bp; + if(memcmp(b->bp, origin, 16) != 0) { + print("beginning of file does not look right\n"); + b->state = FAILED; + return FAIL; + } + if(size < 32*1024 || size > 256*1024) { + print("got %lud byte loader; not likely\n", size); + b->state = FAILED; + return FAIL; + } + + p = b->bp; + q = b->wp; + if(q - p > 10000) /* don't search much past l.s */ + q = p+10000; + + /* + * The string `JUMP' appears right before + * tokzero, which is where we want to jump. + */ + for(; pstate = FAILED; + return FAIL; + + default: + panic("bootdone"); + } + b->state = FAILED; + return FAIL; +} diff --git a/os/boot/pc/bootp.c b/os/boot/pc/bootp.c new file mode 100644 index 00000000..64df7e14 --- /dev/null +++ b/os/boot/pc/bootp.c @@ -0,0 +1,652 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "ip.h" + +uchar broadcast[Eaddrlen] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +}; + +static ushort tftpport = 5000; +static int Id = 1; +static Netaddr myaddr; +static Netaddr server; + +typedef struct { + uchar header[4]; + uchar data[Segsize]; +} Tftp; +static Tftp tftpb; + +static void +hnputs(uchar *ptr, ushort val) +{ + ptr[0] = val>>8; + ptr[1] = val; +} + +static void +hnputl(uchar *ptr, ulong val) +{ + ptr[0] = val>>24; + ptr[1] = val>>16; + ptr[2] = val>>8; + ptr[3] = val; +} + +static ulong +nhgetl(uchar *ptr) +{ + return ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]); +} + +static ushort +nhgets(uchar *ptr) +{ + return ((ptr[0]<<8) | ptr[1]); +} + +static short endian = 1; +static char* aendian = (char*)&endian; +#define LITTLE *aendian + +static ushort +ptcl_csum(void *a, int len) +{ + uchar *addr; + ulong t1, t2; + ulong losum, hisum, mdsum, x; + + addr = a; + losum = 0; + hisum = 0; + mdsum = 0; + + x = 0; + if((ulong)addr & 1) { + if(len) { + hisum += addr[0]; + len--; + addr++; + } + x = 1; + } + while(len >= 16) { + t1 = *(ushort*)(addr+0); + t2 = *(ushort*)(addr+2); mdsum += t1; + t1 = *(ushort*)(addr+4); mdsum += t2; + t2 = *(ushort*)(addr+6); mdsum += t1; + t1 = *(ushort*)(addr+8); mdsum += t2; + t2 = *(ushort*)(addr+10); mdsum += t1; + t1 = *(ushort*)(addr+12); mdsum += t2; + t2 = *(ushort*)(addr+14); mdsum += t1; + mdsum += t2; + len -= 16; + addr += 16; + } + while(len >= 2) { + mdsum += *(ushort*)addr; + len -= 2; + addr += 2; + } + if(x) { + if(len) + losum += addr[0]; + if(LITTLE) + losum += mdsum; + else + hisum += mdsum; + } else { + if(len) + hisum += addr[0]; + if(LITTLE) + hisum += mdsum; + else + losum += mdsum; + } + + losum += hisum >> 8; + losum += (hisum & 0xff) << 8; + while(hisum = losum>>16) + losum = hisum + (losum & 0xffff); + + return ~losum; +} + +static ushort +ip_csum(uchar *addr) +{ + int len; + ulong sum = 0; + + len = (addr[0]&0xf)<<2; + + while(len > 0) { + sum += addr[0]<<8 | addr[1] ; + len -= 2; + addr += 2; + } + + sum = (sum & 0xffff) + (sum >> 16); + sum = (sum & 0xffff) + (sum >> 16); + return (sum^0xffff); +} + +enum { + /* this is only true of IPv4, but we're not doing v6 yet */ + Min_udp_payload = ETHERMINTU - ETHERHDRSIZE - UDP_HDRSIZE, +}; + +static void +udpsend(int ctlrno, Netaddr *a, void *data, int dlen) +{ + char payload[ETHERMAXTU]; + Udphdr *uh; + Etherhdr *ip; + Etherpkt pkt; + int len, ptcllen; + + /* + * if packet is too short, make it longer rather than relying + * on ethernet interface or lower layers to pad it. + */ + if (dlen < Min_udp_payload) { + memmove(payload, data, dlen); + data = payload; + dlen = Min_udp_payload; + } + + uh = (Udphdr*)&pkt; + + memset(uh, 0, sizeof(Etherpkt)); + memmove(uh->udpcksum+sizeof(uh->udpcksum), data, dlen); + + /* + * UDP portion + */ + ptcllen = dlen + (UDP_HDRSIZE-UDP_PHDRSIZE); + uh->ttl = 0; + uh->udpproto = IP_UDPPROTO; + uh->frag[0] = 0; + uh->frag[1] = 0; + hnputs(uh->udpplen, ptcllen); + hnputl(uh->udpsrc, myaddr.ip); + hnputs(uh->udpsport, myaddr.port); + hnputl(uh->udpdst, a->ip); + hnputs(uh->udpdport, a->port); + hnputs(uh->udplen, ptcllen); + uh->udpcksum[0] = 0; + uh->udpcksum[1] = 0; + dlen = (dlen+1)&~1; + hnputs(uh->udpcksum, ptcl_csum(&uh->ttl, dlen+UDP_HDRSIZE)); + + /* + * IP portion + */ + ip = (Etherhdr*)&pkt; + len = UDP_EHSIZE+UDP_HDRSIZE+dlen; /* non-descriptive names */ + ip->vihl = IP_VER|IP_HLEN; + ip->tos = 0; + ip->ttl = 255; + hnputs(ip->length, len-ETHER_HDR); + hnputs(ip->id, Id++); + ip->frag[0] = 0; + ip->frag[1] = 0; + ip->cksum[0] = 0; + ip->cksum[1] = 0; + hnputs(ip->cksum, ip_csum(&ip->vihl)); + + /* + * Ethernet MAC portion + */ + hnputs(ip->type, ET_IP); + memmove(ip->d, a->ea, sizeof(ip->d)); + +if(debug) { + print("udpsend "); +} + ethertxpkt(ctlrno, &pkt, len, Timeout); +} + +static void +nak(int ctlrno, Netaddr *a, int code, char *msg, int report) +{ + int n; + char buf[128]; + + buf[0] = 0; + buf[1] = Tftp_ERROR; + buf[2] = 0; + buf[3] = code; + strcpy(buf+4, msg); + n = strlen(msg) + 4 + 1; + udpsend(ctlrno, a, buf, n); + if(report) + print("\ntftp: error(%d): %s\n", code, msg); +} + +static int +udprecv(int ctlrno, Netaddr *a, void *data, int dlen) +{ + int n, len; + ushort csm; + Udphdr *h; + ulong addr, timo; + Etherpkt pkt; + static int rxactive; + + if(rxactive == 0) + timo = 1000; + else + timo = Timeout; + timo += TK2MS(m->ticks); + while(timo > TK2MS(m->ticks)){ + n = etherrxpkt(ctlrno, &pkt, timo-TK2MS(m->ticks)); + if(n <= 0) + continue; + + h = (Udphdr*)&pkt; + if(debug) + print("udprecv %E to %E...\n", h->s, h->d); + + if(nhgets(h->type) != ET_IP) { + if(debug) + print("not ip..."); + continue; + } + + if(ip_csum(&h->vihl)) { + print("ip chksum error\n"); + continue; + } + if(h->vihl != (IP_VER|IP_HLEN)) { + print("ip bad vers/hlen\n"); + continue; + } + + if(h->udpproto != IP_UDPPROTO) { + if(debug) + print("not udp (%d)...", h->udpproto); + continue; + } + + if(debug) + print("okay udp..."); + + h->ttl = 0; + len = nhgets(h->udplen); + hnputs(h->udpplen, len); + + if(nhgets(h->udpcksum)) { + csm = ptcl_csum(&h->ttl, len+UDP_PHDRSIZE); + if(csm != 0) { + print("udp chksum error csum #%4ux len %d\n", + csm, n); + break; + } + } + + if(a->port != 0 && nhgets(h->udpsport) != a->port) { + if(debug) + print("udpport %ux %ux\n", + nhgets(h->udpsport), a->port); + continue; + } + + addr = nhgetl(h->udpsrc); + if(a->ip != Bcastip && addr != a->ip) { + if(debug) + print("bad ip\n"); + continue; + } + + len -= UDP_HDRSIZE-UDP_PHDRSIZE; + if(len > dlen) { + print("udp: packet too big: %d > %d; from addr %E\n", + len, dlen, h->udpsrc); + continue; + } + + memmove(data, h->udpcksum+sizeof(h->udpcksum), len); + a->ip = addr; + a->port = nhgets(h->udpsport); + memmove(a->ea, pkt.s, sizeof(a->ea)); + + rxactive = 1; + return len; + } + + return 0; +} + +static int tftpblockno; + +/* + * format of a request packet, from the RFC: + * + 2 bytes string 1 byte string 1 byte + ------------------------------------------------ + | Opcode | Filename | 0 | Mode | 0 | + ------------------------------------------------ + */ +static int +tftpopen(int ctlrno, Netaddr *a, char *name, Tftp *tftp) +{ + int i, len, rlen, oport; + char buf[Segsize+2]; + + buf[0] = 0; + buf[1] = Tftp_READ; + len = 2 + sprint(buf+2, "%s", name) + 1; + len += sprint(buf+len, "octet") + 1; + + oport = a->port; + for(i = 0; i < 5; i++){ + a->port = oport; + udpsend(ctlrno, a, buf, len); + a->port = 0; + if((rlen = udprecv(ctlrno, a, tftp, sizeof(Tftp))) < sizeof(tftp->header)) + continue; + + switch((tftp->header[0]<<8)|tftp->header[1]){ + + case Tftp_ERROR: + print("tftpopen: error (%d): %s\n", + (tftp->header[2]<<8)|tftp->header[3], (char*)tftp->data); + return -1; + + case Tftp_DATA: + tftpblockno = 1; + len = (tftp->header[2]<<8)|tftp->header[3]; + if(len != tftpblockno){ + print("tftpopen: block error: %d\n", len); + nak(ctlrno, a, 1, "block error", 0); + return -1; + } + rlen -= sizeof(tftp->header); + if(rlen < Segsize){ + /* ACK now, in case we don't later */ + buf[0] = 0; + buf[1] = Tftp_ACK; + buf[2] = tftpblockno>>8; + buf[3] = tftpblockno; + udpsend(ctlrno, a, buf, sizeof(tftp->header)); + } + return rlen; + } + } + + print("tftpopen: failed to connect to server\n"); + return -1; +} + +static int +tftpread(int ctlrno, Netaddr *a, Tftp *tftp, int dlen) +{ + uchar buf[4]; + int try, blockno, len; + + dlen += sizeof(tftp->header); + + for(try = 0; try < 10; try++) { + buf[0] = 0; + buf[1] = Tftp_ACK; + buf[2] = tftpblockno>>8; + buf[3] = tftpblockno; + + udpsend(ctlrno, a, buf, sizeof(buf)); + len = udprecv(ctlrno, a, tftp, dlen); + if(len <= sizeof(tftp->header)){ + if(debug) + print("tftpread: too short %d <= %d\n", + len, sizeof(tftp->header)); + continue; + } + blockno = (tftp->header[2]<<8)|tftp->header[3]; + if(blockno <= tftpblockno){ + if(debug) + print("tftpread: blkno %d <= %d\n", + blockno, tftpblockno); + continue; + } + + if(blockno == tftpblockno+1) { + tftpblockno++; + if(len < dlen) { /* last packet; send final ack */ + tftpblockno++; + buf[0] = 0; + buf[1] = Tftp_ACK; + buf[2] = tftpblockno>>8; + buf[3] = tftpblockno; + udpsend(ctlrno, a, buf, sizeof(buf)); + } + return len-sizeof(tftp->header); + } + print("tftpread: block error: %d, expected %d\n", + blockno, tftpblockno+1); + } + + return -1; +} + +static int +bootpopen(int ctlrno, char *file, Bootp *rep, int dotftpopen) +{ + Bootp req; + int i, n; + uchar *ea; + char name[128], *filename, *sysname; + + if((ea = etheraddr(ctlrno)) == 0){ + print("invalid ctlrno %d\n", ctlrno); + return -1; + } + + filename = 0; + sysname = 0; + if(file && *file){ + strcpy(name, file); + if(filename = strchr(name, '!')){ + sysname = name; + *filename++ = 0; + } + else + filename = name; + } + + memset(&req, 0, sizeof(req)); + req.op = Bootrequest; + req.htype = 1; /* ethernet */ + req.hlen = Eaddrlen; /* ethernet */ + memmove(req.chaddr, ea, Eaddrlen); + if(filename != nil) + strncpy(req.file, filename, sizeof(req.file)); + if(sysname != nil) + strncpy(req.sname, sysname, sizeof(req.sname)); + + myaddr.ip = 0; + myaddr.port = BPportsrc; + memmove(myaddr.ea, ea, Eaddrlen); + + etherrxflush(ctlrno); + for(i = 0; i < 10; i++) { + server.ip = Bcastip; + server.port = BPportdst; + memmove(server.ea, broadcast, sizeof(server.ea)); + udpsend(ctlrno, &server, &req, sizeof(req)); + if(udprecv(ctlrno, &server, rep, sizeof(*rep)) <= 0) + continue; + if(memcmp(req.chaddr, rep->chaddr, Eaddrlen)) + continue; + if(rep->htype != 1 || rep->hlen != Eaddrlen) + continue; + if(sysname == 0 || strcmp(sysname, rep->sname) == 0) + break; + } + if(i >= 10) { + print("bootp timed out\n"); + return -1; + } + + if(!dotftpopen) + return 0; + + if(filename == 0 || *filename == 0){ + if(strcmp(rep->file, "/386/9pxeload") == 0) + return -1; + filename = rep->file; + } + + if(rep->sname[0] != '\0') + print("%s ", rep->sname); + print("(%d.%d.%d.%d!%d): %s\n", + rep->siaddr[0], + rep->siaddr[1], + rep->siaddr[2], + rep->siaddr[3], + server.port, + filename); + + myaddr.ip = nhgetl(rep->yiaddr); + myaddr.port = tftpport++; + server.ip = nhgetl(rep->siaddr); + server.port = TFTPport; + + if((n = tftpopen(ctlrno, &server, filename, &tftpb)) < 0) + return -1; + + return n; +} + +int +bootpboot(int ctlrno, char *file, Boot *b) +{ + int n; + Bootp rep; + + if((n = bootpopen(ctlrno, file, &rep, 1)) < 0) + return -1; + + while(bootpass(b, tftpb.data, n) == MORE){ + n = tftpread(ctlrno, &server, &tftpb, sizeof(tftpb.data)); + if(n < sizeof(tftpb.data)) + break; + } + + if(0 < n && n < sizeof(tftpb.data)) /* got to end of file */ + bootpass(b, tftpb.data, n); + else + nak(ctlrno, &server, 3, "ok", 0); /* tftpclose to abort transfer */ + bootpass(b, nil, 0); /* boot if possible */ + return -1; +} + +#include "fs.h" + +#define INIPATHLEN 64 + +static struct { + Fs fs; + char ini[INIPATHLEN]; +} pxether[MaxEther]; + +static vlong +pxediskseek(Fs*, vlong) +{ + return -1LL; +} + +static long +pxediskread(Fs*, void*, long) +{ + return -1; +} + +static long +pxeread(File* f, void* va, long len) +{ + int n; + Bootp rep; + char *p, *v; + + if((n = bootpopen(f->fs->dev, pxether[f->fs->dev].ini, &rep, 1)) < 0) + return -1; + + p = v = va; + while(n > 0) { + if((p-v)+n > len) + n = len - (p-v); + memmove(p, tftpb.data, n); + p += n; + if(n != Segsize) + break; + if((n = tftpread(f->fs->dev, &server, &tftpb, sizeof(tftpb.data))) < 0) + return -1; + } + return p-v; +} + +static int +pxewalk(File* f, char* name) +{ + Bootp rep; + char *ini; + + switch(f->walked){ + default: + return -1; + case 0: + if(strcmp(name, "cfg") == 0){ + f->walked = 1; + return 1; + } + break; + case 1: + if(strcmp(name, "pxe") == 0){ + f->walked = 2; + return 1; + } + break; + case 2: + if(strcmp(name, "%E") != 0) + break; + f->walked = 3; + + if(bootpopen(f->fs->dev, nil, &rep, 0) < 0) + return 0; + + ini = pxether[f->fs->dev].ini; + snprint(ini, INIPATHLEN, "/cfg/pxe/%E", rep.chaddr); + f->path = ini; + + return 1; + } + return 0; +} + +void* +pxegetfspart(int ctlrno, char* part, int) +{ + if(!pxe) + return nil; + if(strcmp(part, "*") != 0) + return nil; + if(ctlrno >= MaxEther) + return nil; + + pxether[ctlrno].fs.dev = ctlrno; + pxether[ctlrno].fs.diskread = pxediskread; + pxether[ctlrno].fs.diskseek = pxediskseek; + + pxether[ctlrno].fs.read = pxeread; + pxether[ctlrno].fs.walk = pxewalk; + + pxether[ctlrno].fs.root.fs = &pxether[ctlrno].fs; + pxether[ctlrno].fs.root.walked = 0; + + return &pxether[ctlrno].fs; +} diff --git a/os/boot/pc/cga.c b/os/boot/pc/cga.c new file mode 100644 index 00000000..90e08aaf --- /dev/null +++ b/os/boot/pc/cga.c @@ -0,0 +1,91 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +enum { + Width = 160, + Height = 25, + + Attr = 7, /* white on black */ +}; + +#define CGASCREENBASE ((uchar*)KADDR(0xB8000)) + +static int pos; +static int screeninitdone; + +static uchar +cgaregr(int index) +{ + outb(0x3D4, index); + return inb(0x3D4+1) & 0xFF; +} + +static void +cgaregw(int index, int data) +{ + outb(0x3D4, index); + outb(0x3D4+1, data); +} + +static void +movecursor(void) +{ + cgaregw(0x0E, (pos/2>>8) & 0xFF); + cgaregw(0x0F, pos/2 & 0xFF); + CGASCREENBASE[pos+1] = Attr; +} + +static void +cgascreenputc(int c) +{ + int i; + + if(c == '\n'){ + pos = pos/Width; + pos = (pos+1)*Width; + } + else if(c == '\t'){ + i = 8 - ((pos/2)&7); + while(i-->0) + cgascreenputc(' '); + } + else if(c == '\b'){ + if(pos >= 2) + pos -= 2; + cgascreenputc(' '); + pos -= 2; + } + else{ + CGASCREENBASE[pos++] = c; + CGASCREENBASE[pos++] = Attr; + } + if(pos >= Width*Height){ + memmove(CGASCREENBASE, &CGASCREENBASE[Width], Width*(Height-1)); + memset(&CGASCREENBASE[Width*(Height-1)], 0, Width); + pos = Width*(Height-1); + } + movecursor(); +} + +static void +screeninit(void) +{ + if(screeninitdone == 0){ + pos = cgaregr(0x0E)<<8; + pos |= cgaregr(0x0F); + pos *= 2; + screeninitdone = 1; + } +} + +void +cgascreenputs(char* s, int n) +{ + if(screeninitdone == 0) + screeninit(); + while(n-- > 0) + cgascreenputc(*s++); +} diff --git a/os/boot/pc/clock.c b/os/boot/pc/clock.c new file mode 100644 index 00000000..6c5694cd --- /dev/null +++ b/os/boot/pc/clock.c @@ -0,0 +1,305 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +/* + * 8253 timer + */ +enum +{ + T0cntr= 0x40, /* counter ports */ + T1cntr= 0x41, /* ... */ + T2cntr= 0x42, /* ... */ + Tmode= 0x43, /* mode port */ + + /* commands */ + Latch0= 0x00, /* latch counter 0's value */ + Load0= 0x30, /* load counter 0 with 2 bytes */ + + /* modes */ + Square= 0x36, /* perioic square wave */ + + Freq= 1193182, /* Real clock frequency */ +}; + +static uvlong cpuhz = 66000000; +static int cpumhz = 66; +static int loopconst = 100; +int cpuidax, cpuiddx; +int havetsc; + +extern void _cycles(uvlong*); /* in l.s */ +extern void wrmsr(int, vlong); + +static void +clockintr(Ureg*, void*) +{ + m->ticks++; + checkalarms(); +} + +#define STEPPING(x) ((x)&0xf) +#define X86MODEL(x) (((x)>>4)&0xf) +#define X86FAMILY(x) (((x)>>8)&0xf) + +enum +{ + /* flags */ + CpuidFPU = 0x001, /* on-chip floating point unit */ + CpuidMCE = 0x080, /* machine check exception */ + CpuidCX8 = 0x100, /* CMPXCHG8B instruction */ +}; + +typedef struct +{ + int family; + int model; + int aalcycles; + char *name; +} X86type; + +X86type x86intel[] = +{ + { 4, 0, 22, "486DX", }, /* known chips */ + { 4, 1, 22, "486DX50", }, + { 4, 2, 22, "486SX", }, + { 4, 3, 22, "486DX2", }, + { 4, 4, 22, "486SL", }, + { 4, 5, 22, "486SX2", }, + { 4, 7, 22, "DX2WB", }, /* P24D */ + { 4, 8, 22, "DX4", }, /* P24C */ + { 4, 9, 22, "DX4WB", }, /* P24CT */ + { 5, 0, 23, "P5", }, + { 5, 1, 23, "P5", }, + { 5, 2, 23, "P54C", }, + { 5, 3, 23, "P24T", }, + { 5, 4, 23, "P55C MMX", }, + { 5, 7, 23, "P54C VRT", }, + { 6, 1, 16, "PentiumPro", },/* trial and error */ + { 6, 3, 16, "PentiumII", }, + { 6, 5, 16, "PentiumII/Xeon", }, + { 6, 6, 16, "Celeron", }, + { 6, 7, 16, "PentiumIII/Xeon", }, + { 6, 8, 16, "PentiumIII/Xeon", }, + { 6, 0xB, 16, "PentiumIII/Xeon", }, + { 0xF, 1, 16, "P4", }, /* P4 */ + { 0xF, 2, 16, "PentiumIV/Xeon", }, + + { 3, -1, 32, "386", }, /* family defaults */ + { 4, -1, 22, "486", }, + { 5, -1, 23, "P5", }, + { 6, -1, 16, "P6", }, + { 0xF, -1, 16, "P4", }, /* P4 */ + + { -1, -1, 16, "unknown", }, /* total default */ +}; + + +/* + * The AMD processors all implement the CPUID instruction. + * The later ones also return the processor name via functions + * 0x80000002, 0x80000003 and 0x80000004 in registers AX, BX, CX + * and DX: + * K5 "AMD-K5(tm) Processor" + * K6 "AMD-K6tm w/ multimedia extensions" + * K6 3D "AMD-K6(tm) 3D processor" + * K6 3D+ ? + */ +static X86type x86amd[] = +{ + { 5, 0, 23, "AMD-K5", }, /* guesswork */ + { 5, 1, 23, "AMD-K5", }, /* guesswork */ + { 5, 2, 23, "AMD-K5", }, /* guesswork */ + { 5, 3, 23, "AMD-K5", }, /* guesswork */ + { 5, 6, 11, "AMD-K6", }, /* trial and error */ + { 5, 7, 11, "AMD-K6", }, /* trial and error */ + { 5, 8, 11, "AMD-K6-2", }, /* trial and error */ + { 5, 9, 11, "AMD-K6-III", },/* trial and error */ + + { 6, 1, 11, "AMD-Athlon", },/* trial and error */ + { 6, 2, 11, "AMD-Athlon", },/* trial and error */ + + { 4, -1, 22, "Am486", }, /* guesswork */ + { 5, -1, 23, "AMD-K5/K6", }, /* guesswork */ + { 6, -1, 11, "AMD-Athlon", },/* guesswork */ + { 0xF, -1, 11, "AMD64", }, /* guesswork */ + + { -1, -1, 11, "unknown", }, /* total default */ +}; + +static X86type *cputype; + + +void +delay(int millisecs) +{ + millisecs *= loopconst; + if(millisecs <= 0) + millisecs = 1; + aamloop(millisecs); +} + +void +microdelay(int microsecs) +{ + microsecs *= loopconst; + microsecs /= 1000; + if(microsecs <= 0) + microsecs = 1; + aamloop(microsecs); +} + +extern void cpuid(char*, int*, int*); + +X86type* +cpuidentify(void) +{ + int family, model; + X86type *t; + char cpuidid[16]; + int cpuidax, cpuiddx; + + cpuid(cpuidid, &cpuidax, &cpuiddx); + if(strncmp(cpuidid, "AuthenticAMD", 12) == 0) + t = x86amd; + else + t = x86intel; + family = X86FAMILY(cpuidax); + model = X86MODEL(cpuidax); + if (0) + print("cpuidentify: cpuidax 0x%ux cpuiddx 0x%ux\n", + cpuidax, cpuiddx); + while(t->name){ + if((t->family == family && t->model == model) + || (t->family == family && t->model == -1) + || (t->family == -1)) + break; + t++; + } + if(t->name == nil) + panic("cpuidentify"); + + if(cpuiddx & 0x10){ + havetsc = 1; + if(cpuiddx & 0x20) + wrmsr(0x10, 0); + } + + return t; +} + +void +clockinit(void) +{ + uvlong a, b, cpufreq; + int loops, incr, x, y; + X86type *t; + + /* + * set vector for clock interrupts + */ + setvec(VectorCLOCK, clockintr, 0); + + t = cpuidentify(); + + /* + * set clock for 1/HZ seconds + */ + outb(Tmode, Load0|Square); + outb(T0cntr, (Freq/HZ)); /* low byte */ + outb(T0cntr, (Freq/HZ)>>8); /* high byte */ + + /* + * Introduce a little delay to make sure the count is + * latched and the timer is counting down; with a fast + * enough processor this may not be the case. + * The i8254 (which this probably is) has a read-back + * command which can be used to make sure the counting + * register has been written into the counting element. + */ + x = (Freq/HZ); + for(loops = 0; loops < 100000 && x >= (Freq/HZ); loops++){ + outb(Tmode, Latch0); + x = inb(T0cntr); + x |= inb(T0cntr)<<8; + } + + /* find biggest loop that doesn't wrap */ + incr = 16000000/(t->aalcycles*HZ*2); + x = 2000; + for(loops = incr; loops < 64*1024; loops += incr) { + + /* + * measure time for the loop + * + * MOVL loops,CX + * aaml1: AAM + * LOOP aaml1 + * + * the time for the loop should be independent of external + * cache and memory system since it fits in the execution + * prefetch buffer. + * + */ + outb(Tmode, Latch0); + if(havetsc) + _cycles(&a); + x = inb(T0cntr); + x |= inb(T0cntr)<<8; + aamloop(loops); + outb(Tmode, Latch0); + if(havetsc) + _cycles(&b); + y = inb(T0cntr); + y |= inb(T0cntr)<<8; + x -= y; + + if(x < 0) + x += Freq/HZ; + + if(x > Freq/(3*HZ)) + break; + } + + /* + * figure out clock frequency and a loop multiplier for delay(). + * counter goes at twice the frequency, once per transition, + * i.e., twice per square wave + */ + cpufreq = (vlong)loops*((t->aalcycles*2*Freq)/x); + loopconst = (cpufreq/1000)/t->aalcycles; /* AAM+LOOP's for 1 ms */ + + if(havetsc){ + /* counter goes up by 2*Freq */ + b = (b-a)<<1; + b *= Freq; + b /= x; + + /* + * round to the nearest megahz + */ + cpumhz = (b+500000)/1000000L; + cpuhz = b; + } + else{ + /* + * add in possible .5% error and convert to MHz + */ + cpumhz = (cpufreq + cpufreq/200)/1000000; + cpuhz = cpufreq; + } + + if(debug){ + int timeo; + + print("%dMHz %s loop %d\n", cpumhz, t->name, loopconst); + print("tick..."); + for(timeo = 0; timeo < 10; timeo++) + delay(1000); + print("tock...\n"); + } +} diff --git a/os/boot/pc/conf.c b/os/boot/pc/conf.c new file mode 100644 index 00000000..4109f9bb --- /dev/null +++ b/os/boot/pc/conf.c @@ -0,0 +1,537 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "fs.h" + +/* + * Where configuration info is left for the loaded programme. + * This will turn into a structure as more is done by the boot loader + * (e.g. why parse the .ini file twice?). + * There are 3584 bytes available at CONFADDR. + * + * The low-level boot routines in l.s leave data for us at CONFADDR, + * which we pick up before reading the plan9.ini file. + */ +#define BOOTLINELEN 64 +#define BOOTARGS ((char*)(CONFADDR+BOOTLINELEN)) +#define BOOTARGSLEN (3584-0x200-BOOTLINELEN) +#define MAXCONF 100 + +static char *confname[MAXCONF]; +static char *confval[MAXCONF]; +static int nconf; + +extern char **ini; + +typedef struct { + char* name; + int start; + int end; +} Mblock; + +typedef struct { + char* tag; + Mblock* mb; +} Mitem; + +static Mblock mblock[MAXCONF]; +static int nmblock; +static Mitem mitem[MAXCONF]; +static int nmitem; +static char* mdefault; +static char mdefaultbuf[10]; +static int mtimeout; + +static char* +comma(char* line, char** residue) +{ + char *q, *r; + + if((q = strchr(line, ',')) != nil){ + *q++ = 0; + if(*q == ' ') + q++; + } + *residue = q; + + if((r = strchr(line, ' ')) != nil) + *r = 0; + + if(*line == ' ') + line++; + return line; +} + +static Mblock* +findblock(char* name, char** residue) +{ + int i; + char *p; + + p = comma(name, residue); + for(i = 0; i < nmblock; i++){ + if(strcmp(p, mblock[i].name) == 0) + return &mblock[i]; + } + return nil; +} + +static Mitem* +finditem(char* name, char** residue) +{ + int i; + char *p; + + p = comma(name, residue); + for(i = 0; i < nmitem; i++){ + if(strcmp(p, mitem[i].mb->name) == 0) + return &mitem[i]; + } + return nil; +} + +static void +parsemenu(char* str, char* scratch, int len) +{ + Mitem *mi; + Mblock *mb, *menu; + char buf[20], *p, *q, *line[MAXCONF]; + int i, inblock, n, show; + + inblock = 0; + menu = nil; + memmove(scratch, str, len); + n = getfields(scratch, line, MAXCONF, '\n'); + if(n >= MAXCONF) + print("warning: possibly too many lines in plan9.ini\n"); + for(i = 0; i < n; i++){ + p = line[i]; + if(inblock && *p == '['){ + mblock[nmblock].end = i; + if(strcmp(mblock[nmblock].name, "menu") == 0) + menu = &mblock[nmblock]; + nmblock++; + inblock = 0; + } + if(*p == '['){ + if(nmblock == 0 && i != 0){ + mblock[nmblock].name = "common"; + mblock[nmblock].start = 0; + mblock[nmblock].end = i; + nmblock++; + } + q = strchr(p+1, ']'); + if(q == nil || *(q+1) != 0){ + print("malformed menu block header - %s\n", p); + return; + } + *q = 0; + mblock[nmblock].name = p+1; + mblock[nmblock].start = i+1; + inblock = 1; + } + } + + if(inblock){ + mblock[nmblock].end = i; + nmblock++; + } + if(menu == nil) + return; + if(nmblock < 2){ + print("incomplete menu specification\n"); + return; + } + + for(i = menu->start; i < menu->end; i++){ + p = line[i]; + if(cistrncmp(p, "menuitem=", 9) == 0){ + p += 9; + if((mb = findblock(p, &q)) == nil){ + print("no block for menuitem %s\n", p); + return; + } + if(q != nil) + mitem[nmitem].tag = q; + else + mitem[nmitem].tag = mb->name; + mitem[nmitem].mb = mb; + nmitem++; + } + else if(cistrncmp(p, "menudefault=", 12) == 0){ + p += 12; + if((mi = finditem(p, &q)) == nil){ + print("no item for menudefault %s\n", p); + return; + } + if(q != nil) + mtimeout = strtol(q, 0, 0); + sprint(mdefaultbuf, "%ld", mi-mitem+1); + mdefault = mdefaultbuf; + } + else if(cistrncmp(p, "menuconsole=", 12) == 0){ + p += 12; + p = comma(p, &q); + consinit(p, q); + } + else{ + print("invalid line in [menu] block - %s\n", p); + return; + } + } + +again: + print("\nPlan 9 Startup Menu:\n====================\n"); + for(i = 0; i < nmitem; i++) + print(" %d. %s\n", i+1, mitem[i].tag); + for(;;){ + getstr("Selection", buf, sizeof(buf), mdefault, mtimeout); + mtimeout = 0; + i = strtol(buf, &p, 0)-1; + if(i < 0 || i >= nmitem) + goto again; + switch(*p){ + case 'p': + case 'P': + show = 1; + print("\n"); + break; + case 0: + show = 0; + break; + default: + continue; + + } + mi = &mitem[i]; + + p = str; + p += sprint(p, "menuitem=%s\n", mi->mb->name); + for(i = 0; i < nmblock; i++){ + mb = &mblock[i]; + if(mi->mb != mb && cistrcmp(mb->name, "common") != 0) + continue; + for(n = mb->start; n < mb->end; n++) + p += sprint(p, "%s\n", line[n]); + } + + if(show){ + for(q = str; q < p; q += i){ + if((i = print(q)) <= 0) + break; + } + goto again; + } + break; + } + print("\n"); +} + +/* +static void +msleep(int msec) +{ + ulong start; + + for(start = m->ticks; TK2MS(m->ticks - start) < msec; ) + ; +} +*/ + +void +readlsconf(void) +{ + uchar *p; + + p = (uchar*)CONFADDR; + for(;;) { + if(strcmp((char*)p, "APM") == 0){ + apm.haveinfo = 1; + apm.ax = *(ushort*)(p+4); + apm.cx = *(ushort*)(p+6); + apm.dx = *(ushort*)(p+8); + apm.di = *(ushort*)(p+10); + apm.ebx = *(ulong*)(p+12); + apm.esi = *(ulong*)(p+16); + print("apm ax=%x cx=%x dx=%x di=%x ebx=%x esi=%x\n", + apm.ax, apm.cx, apm.dx, apm.di, apm.ebx, apm.esi); + p += 20; + continue; + } + break; + } +} + +char* +getconf(char *name) +{ + int i, n, nmatch; + char buf[20]; + + nmatch = 0; + for(i = 0; i < nconf; i++) + if(cistrcmp(confname[i], name) == 0) + nmatch++; + + switch(nmatch) { + default: + print("\n"); + nmatch = 0; + for(i = 0; i < nconf; i++) + if(cistrcmp(confname[i], name) == 0) + print("%d. %s\n", ++nmatch, confval[i]); + print("%d. none of the above\n", ++nmatch); + do { + getstr(name, buf, sizeof(buf), nil, 0); + n = atoi(buf); + } while(n < 1 || n > nmatch); + + for(i = 0; i < nconf; i++) + if(cistrcmp(confname[i], name) == 0) + if(--n == 0) + return confval[i]; + break; + + case 1: + for(i = 0; i < nconf; i++) + if(cistrcmp(confname[i], name) == 0) + return confval[i]; + break; + + case 0: + break; + } + return nil; +} + +void +addconf(char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + vseprint(BOOTARGS+strlen(BOOTARGS), BOOTARGS+BOOTARGSLEN, fmt, arg); + va_end(arg); +} + +void +changeconf(char *fmt, ...) +{ + va_list arg; + char *p, *q, pref[20], buf[128]; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof buf, fmt, arg); + va_end(arg); + strncpy(pref+1, buf, 19); + pref[19] = '\0'; + if(p = strchr(pref, '=')) + *(p+1) = '\0'; + else + print("warning: did not change %s in plan9.ini\n", buf); + + /* find old line by looking for \nwhat= */ + pref[0] = '\n'; + if(strncmp(BOOTARGS, pref+1, strlen(pref+1)) == 0) + p = BOOTARGS; + else if(p = strstr(BOOTARGS, pref)) + p++; + else + p = nil; + + /* move rest of args up, deleting what= line. */ + if(p != nil && (q = strchr(p, '\n')) != nil) + memmove(p, q+1, strlen(q+1)+1); + + /* add replacement to end */ + addconf("%s", buf); +} + +/* + * read configuration file + */ +static char inibuf[BOOTARGSLEN]; +static char id[8] = "ZORT 0\r\n"; + +int +dotini(Fs *fs) +{ + File rc; + int blankline, i, incomment, inspace, n; + char *cp, *p, *q, *line[MAXCONF]; + + if(fswalk(fs, *ini, &rc) <= 0) + return -1; + + cp = inibuf; + *cp = 0; + n = fsread(&rc, cp, BOOTARGSLEN-1); + if(n <= 0) + return -1; + + cp[n] = 0; + + /* + * Strip out '\r', change '\t' -> ' '. + * Change runs of spaces into single spaces. + * Strip out trailing spaces, blank lines. + * + * We do this before we make the copy so that if we + * need to change the copy, it is already fairly clean. + * The main need is in the case when plan9.ini has been + * padded with lots of trailing spaces, as is the case + * for those created during a distribution install. + */ + p = cp; + blankline = 1; + incomment = inspace = 0; + for(q = cp; *q; q++){ + if(*q == '\r') + continue; + if(*q == '\t') + *q = ' '; + if(*q == ' '){ + inspace = 1; + continue; + } + if(*q == '\n'){ + if(!blankline){ + if(!incomment) + *p++ = '\n'; + blankline = 1; + } + incomment = inspace = 0; + continue; + } + if(inspace){ + if(!blankline && !incomment) + *p++ = ' '; + inspace = 0; + } + if(blankline && *q == '#') + incomment = 1; + blankline = 0; + if(!incomment) + *p++ = *q; + } + if(p > cp && p[-1] != '\n') + *p++ = '\n'; + *p++ = 0; + n = p-cp; + + parsemenu(cp, BOOTARGS, n); + + /* + * Keep a copy. + * We could change this to pass the parsed strings + * to the booted programme instead of the raw + * string, then it only gets done once. + */ + if(strncmp(cp, id, sizeof(id))){ + memmove(BOOTARGS, id, sizeof(id)); + if(n+1+sizeof(id) >= BOOTARGSLEN) + n -= sizeof(id); + memmove(BOOTARGS+sizeof(id), cp, n+1); + } + else + memmove(BOOTARGS, cp, n+1); + + n = getfields(cp, line, MAXCONF, '\n'); + for(i = 0; i < n; i++){ + cp = strchr(line[i], '='); + if(cp == 0) + continue; + *cp++ = 0; + if(cp - line[i] >= NAMELEN+1) + *(line[i]+NAMELEN-1) = 0; + confname[nconf] = line[i]; + confval[nconf] = cp; + nconf++; + } + return 0; +} + +static int +parseether(uchar *to, char *from) +{ + char nip[4]; + char *p; + int i; + + p = from; + while(*p == ' ') + ++p; + for(i = 0; i < 6; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +int +isaconfig(char *class, int ctlrno, ISAConf *isa) +{ + char cc[NAMELEN], *p, *q, *r; + int n; + + sprint(cc, "%s%d", class, ctlrno); + for(n = 0; n < nconf; n++){ + if(cistrncmp(confname[n], cc, NAMELEN)) + continue; + isa->nopt = 0; + p = confval[n]; + while(*p){ + while(*p == ' ' || *p == '\t') + p++; + if(*p == '\0') + break; + if(cistrncmp(p, "type=", 5) == 0){ + p += 5; + for(q = isa->type; q < &isa->type[NAMELEN-1]; q++){ + if(*p == '\0' || *p == ' ' || *p == '\t') + break; + *q = *p++; + } + *q = '\0'; + } + else if(cistrncmp(p, "port=", 5) == 0) + isa->port = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "irq=", 4) == 0) + isa->irq = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "mem=", 4) == 0) + isa->mem = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "size=", 5) == 0) + isa->size = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "ea=", 3) == 0){ + if(parseether(isa->ea, p+3) == -1) + memset(isa->ea, 0, 6); + } + else if(isa->nopt < NISAOPT){ + r = isa->opt[isa->nopt]; + while(*p && *p != ' ' && *p != '\t'){ + *r++ = *p++; + if(r-isa->opt[isa->nopt] >= ISAOPTLEN-1) + break; + } + *r = '\0'; + isa->nopt++; + } + while(*p && *p != ' ' && *p != '\t') + p++; + } + return 1; + } + return 0; +} diff --git a/os/boot/pc/console.c b/os/boot/pc/console.c new file mode 100644 index 00000000..9883728c --- /dev/null +++ b/os/boot/pc/console.c @@ -0,0 +1,236 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +IOQ consiq; +IOQ consoq; + +static int useuart; + +int debug = 0; + +void +kbdchar(int c) +{ + c &= 0x7F; + if(c == 0x10) + warp86("\n^P\n", 0); + if(c == 0x12) + debug = !debug; + consiq.putc(&consiq, c); +} + +static int +consputc(void) +{ + return consoq.getc(&consoq); +} + +void +kbdinit(void) +{ + i8042init(); + qinit(&consiq); +} + +void +consinit(char* name, char* speed) +{ + int baud, port; + + if(name == nil || cistrcmp(name, "cga") == 0) + return; + port = strtoul(name, 0, 0); + if(port < 0 || port > 1) + return; + if(speed == nil || (baud = strtoul(speed, 0, 0)) == 0) + baud = 9600; + + qinit(&consoq); + + uartspecial(port, kbdchar, consputc, baud); + useuart = 1; + uartputs(&consoq, "\n", 1); +} + +void +consdrain(void) +{ + if(useuart) + uartdrain(); +} + +void +consputs(char* s, int n) +{ + cgascreenputs(s, n); + if(useuart) + uartputs(&consoq, s, n); +} + +void +warp86(char* s, ulong) +{ + if(s != nil) + print(s); + spllo(); + consdrain(); + + i8042reset(); + print("Takes a licking and keeps on ticking...\n"); + for(;;) + idle(); +} + +static int +getline(char *buf, int size, int timeout) +{ + int c, i=0; + ulong start; + char echo; + + for (;;) { + start = m->ticks; + do{ + /* timeout seconds to first char */ + if(timeout && ((m->ticks - start) > timeout*HZ)) + return -2; + c = consiq.getc(&consiq); + }while(c == -1); + timeout = 0; + + if(c == '\r') + c = '\n'; /* turn carriage return into newline */ + if(c == '\177') + c = '\010'; /* turn delete into backspace */ + if(c == '\025') + echo = '\n'; /* echo ^U as a newline */ + else + echo = c; + consputs(&echo, 1); + + if(c == '\010'){ + if(i > 0) + i--; /* bs deletes last character */ + continue; + } + /* a newline ends a line */ + if (c == '\n') + break; + /* ^U wipes out the line */ + if (c =='\025') + return -1; + if(i == size) + return size; + buf[i++] = c; + } + buf[i] = 0; + return i; +} + +int +getstr(char *prompt, char *buf, int size, char *def, int timeout) +{ + int len, isdefault; + char pbuf[PRINTSIZE]; + + buf[0] = 0; + isdefault = (def && *def); + if(isdefault == 0){ + timeout = 0; + sprint(pbuf, "%s: ", prompt); + } + else if(timeout) + sprint(pbuf, "%s[default==%s (%ds timeout)]: ", prompt, def, timeout); + else + sprint(pbuf, "%s[default==%s]: ", prompt, def); + for (;;) { + print(pbuf); + len = getline(buf, size, timeout); + switch(len){ + case 0: + /* RETURN */ + if(isdefault) + break; + continue; + case -1: + /* ^U typed */ + continue; + case -2: + /* timeout, use default */ + consputs("\n", 1); + len = 0; + break; + default: + break; + } + if(len >= size){ + print("line too long\n"); + continue; + } + break; + } + if(len == 0 && isdefault) + strcpy(buf, def); + return 0; +} + +int +print(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + consputs(buf, n); + + return n; +} + +int +sprint(char *s, char *fmt, ...) +{ + int n; + va_list arg; + + va_start(arg, fmt); + n = vseprint(s, s+PRINTSIZE, fmt, arg) - s; + va_end(arg); + + return n; +} + +void +panic(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + strcpy(buf, "panic: "); + va_start(arg, fmt); + n = vseprint(buf+7, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + buf[n] = '\n'; + consputs(buf, n+1); + +//floppymemwrite(); +//splhi(); for(;;); + if(etherdetach) + etherdetach(); + if(sddetach) + sddetach(); + + consputs("\nPress almost any key to reset...", 32); + spllo(); + while(consiq.getc(&consiq) == -1) + ; + + warp86(nil, 0); +} diff --git a/os/boot/pc/dat.h b/os/boot/pc/dat.h new file mode 100644 index 00000000..f6d02d56 --- /dev/null +++ b/os/boot/pc/dat.h @@ -0,0 +1,212 @@ +typedef struct List { + void *next; +} List; + +typedef struct Alarm Alarm; +typedef struct Alarm { + List; + int busy; + long dt; + void (*f)(Alarm*); + void *arg; +} Alarm; + +typedef struct Apminfo { + int haveinfo; + int ax; + int cx; + int dx; + int di; + int ebx; + int esi; +} Apminfo; + +typedef struct Block Block; +struct Block { + Block* next; + uchar* rp; /* first unconsumed byte */ + uchar* wp; /* first empty byte */ + uchar* lim; /* 1 past the end of the buffer */ + uchar* base; /* start of the buffer */ + ulong flag; +}; +#define BLEN(s) ((s)->wp - (s)->rp) + +typedef struct IOQ IOQ; +typedef struct IOQ { + uchar buf[4096]; + uchar *in; + uchar *out; + int state; + int (*getc)(IOQ*); + int (*putc)(IOQ*, int); + void *ptr; +}; + +enum { + Eaddrlen = 6, + /* next two exclude 4-byte ether CRC */ + ETHERMINTU = 60, /* minimum transmit size */ + ETHERMAXTU = 1514, /* maximum transmit size */ + ETHERHDRSIZE = 14, /* size of an ethernet header */ + + MaxEther = 6, +}; + +typedef struct { + uchar d[Eaddrlen]; + uchar s[Eaddrlen]; + uchar type[2]; + uchar data[1500]; + uchar crc[4]; +} Etherpkt; + +extern uchar broadcast[Eaddrlen]; + +typedef struct Ureg Ureg; + +typedef struct Segdesc { + ulong d0; + ulong d1; +} Segdesc; + +typedef struct Mach { + ulong ticks; /* of the clock since boot time */ + void *alarm; /* alarms bound to this clock */ +} Mach; + +extern Mach *m; + +#define I_MAGIC ((((4*11)+0)*11)+7) + +typedef struct Exec Exec; +struct Exec +{ + uchar magic[4]; /* magic number */ + uchar text[4]; /* size of text segment */ + uchar data[4]; /* size of initialized data */ + uchar bss[4]; /* size of uninitialized data */ + uchar syms[4]; /* size of symbol table */ + uchar entry[4]; /* entry point */ + uchar spsz[4]; /* size of sp/pc offset table */ + uchar pcsz[4]; /* size of pc/line number table */ +}; + +/* + * a parsed .ini line + */ +#define ISAOPTLEN 32 +#define NISAOPT 8 + +typedef struct ISAConf { + char type[NAMELEN]; + ulong port; + ulong irq; + ulong mem; + ulong size; + uchar ea[6]; + + int nopt; + char opt[NISAOPT][ISAOPTLEN]; +} ISAConf; + +typedef struct Pcidev Pcidev; +typedef struct PCMmap PCMmap; + +#define BOOTLINE ((char*)CONFADDR) + +enum { + MB = (1024*1024), +}; +#define ROUND(s, sz) (((s)+((sz)-1))&~((sz)-1)) + + +typedef struct Type Type; +typedef struct Medium Medium; +typedef struct Boot Boot; + +enum { /* type */ + Tnil = 0x00, + + Tfloppy = 0x01, + Tsd = 0x02, + Tether = 0x03, + Tcd = 0x04, + + Tany = -1, +}; + +enum { /* name and flag */ + Fnone = 0x00, + + Nfs = 0x00, + Ffs = (1< + +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +#include "fs.h" +#include "devfloppy.h" + + +/* Intel 82077A (8272A compatible) floppy controller */ + +/* This module expects the following functions to be defined + * elsewhere: + * + * inb() + * outb() + * floppyexec() + * floppyeject() + * floppysetup0() + * floppysetup1() + * dmainit() + * dmasetup() + * dmaend() + * + * On DMA systems, floppyexec() should be an empty function; + * on non-DMA systems, dmaend() should be an empty function; + * dmasetup() may enforce maximum transfer sizes. + */ + +enum { + /* file types */ + Qdir= 0, + Qdata= (1<<2), + Qctl= (2<<2), + Qmask= (3<<2), + + DMAchan= 2, /* floppy dma channel */ +}; + +#define DPRINT if(0)print + +FType floppytype[] = +{ + { "3½HD", T1440kb, 512, 18, 2, 1, 80, 0x1B, 0x54, 0, }, + { "3½DD", T1440kb, 512, 9, 2, 1, 80, 0x1B, 0x54, 2, }, + { "3½DD", T720kb, 512, 9, 2, 1, 80, 0x1B, 0x54, 2, }, + { "5¼HD", T1200kb, 512, 15, 2, 1, 80, 0x2A, 0x50, 0, }, + { "5¼DD", T1200kb, 512, 9, 2, 2, 40, 0x2A, 0x50, 1, }, + { "ATT3B1", T1200kb, 512, 8, 2, 2, 48, 0x2A, 0x50, 1, }, + { "5¼DD", T360kb, 512, 9, 2, 1, 40, 0x2A, 0x50, 2, }, +}; + +/* + * bytes per sector encoding for the controller. + * - index for b2c is is (bytes per sector/128). + * - index for c2b is code from b2c + */ +static int b2c[] = +{ +[1] 0, +[2] 1, +[4] 2, +[8] 3, +}; +static int c2b[] = +{ + 128, + 256, + 512, + 1024, +}; + +FController fl; + +#define MOTORBIT(i) (1<<((i)+4)) + +/* + * predeclared + */ +static int cmddone(void*); +static void floppyformat(FDrive*, char*); +static void floppykproc(void*); +static void floppypos(FDrive*,long); +static int floppyrecal(FDrive*); +static int floppyresult(void); +static void floppyrevive(void); +static vlong pcfloppyseek(FDrive*, vlong); +static int floppysense(void); +static void floppywait(int); +static long floppyxfer(FDrive*, int, void*, long, long); + +static void +fldump(void) +{ + DPRINT("sra %ux srb %ux dor %ux msr %ux dir %ux\n", inb(Psra), inb(Psrb), + inb(Pdor), inb(Pmsr), inb(Pdir)); +} + +static void +floppyalarm(Alarm* a) +{ + FDrive *dp; + + for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){ + if((fl.motor&MOTORBIT(dp->dev)) && TK2SEC(m->ticks - dp->lasttouched) > 5) + floppyoff(dp); + } + + alarm(5*1000, floppyalarm, 0); + cancel(a); +} + +/* + * set floppy drive to its default type + */ +static void +floppysetdef(FDrive *dp) +{ + FType *t; + + for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++) + if(dp->dt == t->dt){ + dp->t = t; + break; + } +} + +static void +_floppydetach(void) +{ + /* + * stop the motors + */ + fl.motor = 0; + delay(10); + outb(Pdor, fl.motor | Fintena | Fena); + delay(10); +} + +int +floppyinit(void) +{ + FDrive *dp; + FType *t; + ulong maxtsize; + int mask; + + dmainit(DMAchan); + + floppysetup0(&fl); + + /* + * init dependent parameters + */ + maxtsize = 0; + for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){ + t->cap = t->bytes * t->heads * t->sectors * t->tracks; + t->bcode = b2c[t->bytes/128]; + t->tsize = t->bytes * t->sectors; + if(maxtsize < t->tsize) + maxtsize = t->tsize; + } + + fl.selected = fl.d; + + floppydetach = _floppydetach; + floppydetach(); + + /* + * init drives + */ + mask = 0; + for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){ + dp->dev = dp - fl.d; + if(dp->dt == Tnone) + continue; + mask |= 1<dev; + floppysetdef(dp); + dp->cyl = -1; /* because we don't know */ + dp->cache = (uchar*)xspanalloc(maxtsize, BY2PG, 64*1024); + dp->ccyl = -1; + dp->vers = 0; + dp->maxtries = 5; + } + + /* + * first operation will recalibrate + */ + fl.confused = 1; + + floppysetup1(&fl); + + /* to turn the motor off when inactive */ + alarm(5*1000, floppyalarm, 0); + + return mask; +} + +void +floppyinitdev(int i, char *name) +{ + if(i >= fl.ndrive) + panic("floppyinitdev"); + sprint(name, "fd%d", i); +} + +void +floppyprintdevs(int i) +{ + if(i >= fl.ndrive) + panic("floppyprintdevs"); + print(" fd%d", i); +} + +int +floppyboot(int dev, char *file, Boot *b) +{ + Fs *fs; + + if(strncmp(file, "dos!", 4) == 0) + file += 4; + else if(strchr(file, '!') || strcmp(file, "")==0) { + print("syntax is fd0!file\n"); + return -1; + } + + fs = floppygetfspart(dev, "dos", 1); + if(fs == nil) + return -1; + + return fsboot(fs, file, b); +} + +void +floppyprintbootdevs(int dev) +{ + print(" fd%d", dev); +} + +/* + * check if the floppy has been replaced under foot. cause + * an error if it has. + * + * a seek and a read clears the condition. this was determined + * experimentally, there has to be a better way. + * + * if the read fails, cycle through the possible floppy + * density till one works or we've cycled through all + * possibilities for this drive. + */ +static int +changed(FDrive *dp) +{ + FType *start; + + /* + * if floppy has changed or first time through + */ + if((inb(Pdir)&Fchange) || dp->vers == 0){ + DPRINT("changed\n"); + fldump(); + dp->vers++; + floppysetdef(dp); + dp->maxtries = 3; + start = dp->t; + + /* flopppyon fails if there's no drive */ + dp->confused = 1; /* make floppyon recal */ + if(floppyon(dp) < 0) + return -1; + + pcfloppyseek(dp, dp->t->heads*dp->t->tsize); + + while(floppyxfer(dp, Fread, dp->cache, 0, dp->t->tsize) <= 0){ + + /* + * if the xfer attempt doesn't clear the changed bit, + * there's no floppy in the drive + */ + if(inb(Pdir)&Fchange) + return -1; + + while(++dp->t){ + if(dp->t == &floppytype[nelem(floppytype)]) + dp->t = floppytype; + if(dp->dt == dp->t->dt) + break; + } + + /* flopppyon fails if there's no drive */ + if(floppyon(dp) < 0) + return -1; + + DPRINT("changed: trying %s\n", dp->t->name); + fldump(); + if(dp->t == start) + return -1; + } + } + + return 0; +} + +static int +readtrack(FDrive *dp, int cyl, int head) +{ + int i, nn, sofar; + ulong pos; + + nn = dp->t->tsize; + if(dp->ccyl==cyl && dp->chead==head) + return nn; + pos = (cyl*dp->t->heads+head) * nn; + for(sofar = 0; sofar < nn; sofar += i){ + dp->ccyl = -1; + i = floppyxfer(dp, Fread, dp->cache + sofar, pos + sofar, nn - sofar); + if(i <= 0) + return -1; + } + dp->ccyl = cyl; + dp->chead = head; + return nn; +} + +long +floppyread(Fs *fs, void *a, long n) +{ + FDrive *dp; + long rv, offset; + int sec, head, cyl; + long len; + uchar *aa; + + aa = a; + dp = &fl.d[fs->dev]; + + offset = dp->offset; + floppyon(dp); + if(changed(dp)) + return -1; + + for(rv = 0; rv < n; rv += len){ + /* + * all xfers come out of the track cache + */ + dp->len = n - rv; + floppypos(dp, offset+rv); + cyl = dp->tcyl; + head = dp->thead; + len = dp->len; + sec = dp->tsec; + if(readtrack(dp, cyl, head) < 0) + break; + memmove(aa+rv, dp->cache + (sec-1)*dp->t->bytes, len); + } + dp->offset = offset+rv; + + return rv; +} + +void* +floppygetfspart(int i, char *name, int chatty) +{ + static Fs fs; + + if(strcmp(name, "dos") != 0){ + if(chatty) + print("unknown partition fd%d!%s (use fd%d!dos)\n", i, name, i); + return nil; + } + + fs.dev = i; + fs.diskread = floppyread; + fs.diskseek = floppyseek; + + /* sometimes we get spurious errors and doing it again works */ + if(dosinit(&fs) < 0 && dosinit(&fs) < 0){ + if(chatty) + print("fd%d!%s does not contain a FAT file system\n", i, name); + return nil; + } + return &fs; +} + +static int +return0(void*) +{ + return 0; +} + +static void +timedsleep(int (*f)(void*), void* arg, int ms) +{ + int s; + ulong end; + + end = m->ticks + 1 + MS2TK(ms); + while(m->ticks < end && !(*f)(arg)){ + s = spllo(); + delay(10); + splx(s); + } +} + +/* + * start a floppy drive's motor. + */ +static int +floppyon(FDrive *dp) +{ + int alreadyon; + int tries; + + if(fl.confused) + floppyrevive(); + + /* start motor and select drive */ + dp->lasttouched = m->ticks; + alreadyon = fl.motor & MOTORBIT(dp->dev); + if(!alreadyon){ + fl.motor |= MOTORBIT(dp->dev); + outb(Pdor, fl.motor | Fintena | Fena | dp->dev); + /* wait for drive to spin up */ + timedsleep(return0, 0, 750); + + /* clear any pending interrupts */ + floppysense(); + } + + /* set transfer rate */ + if(fl.rate != dp->t->rate){ + fl.rate = dp->t->rate; + outb(Pdsr, fl.rate); + } + + /* get drive to a known cylinder */ + if(dp->confused) + for(tries = 0; tries < 4; tries++) + if(floppyrecal(dp) >= 0) + break; + dp->lasttouched = m->ticks; + fl.selected = dp; + if(dp->confused) + return -1; + return 0; +} + +/* + * stop the floppy if it hasn't been used in 5 seconds + */ +static void +floppyoff(FDrive *dp) +{ + fl.motor &= ~MOTORBIT(dp->dev); + outb(Pdor, fl.motor | Fintena | Fena | dp->dev); +} + +/* + * send a command to the floppy + */ +static int +floppycmd(void) +{ + int i; + int tries; + + fl.nstat = 0; + for(i = 0; i < fl.ncmd; i++){ + for(tries = 0; ; tries++){ + if((inb(Pmsr)&(Ffrom|Fready)) == Fready) + break; + if(tries > 1000){ + DPRINT("cmd %ux can't be sent (%d)\n", fl.cmd[0], i); + fldump(); + + /* empty fifo, might have been a bad command */ + floppyresult(); + return -1; + } + microdelay(1); + } + outb(Pfdata, fl.cmd[i]); + } + return 0; +} + +/* + * get a command result from the floppy + * + * when the controller goes ready waiting for a command + * (instead of sending results), we're done + * + */ +static int +floppyresult(void) +{ + int i, s; + int tries; + + /* get the result of the operation */ + for(i = 0; i < sizeof(fl.stat); i++){ + /* wait for status byte */ + for(tries = 0; ; tries++){ + s = inb(Pmsr)&(Ffrom|Fready); + if(s == Fready){ + fl.nstat = i; + return fl.nstat; + } + if(s == (Ffrom|Fready)) + break; + if(tries > 1000){ + DPRINT("floppyresult: %d stats\n", i); + fldump(); + fl.confused = 1; + return -1; + } + microdelay(1); + } + fl.stat[i] = inb(Pfdata); + } + fl.nstat = sizeof(fl.stat); + return fl.nstat; +} + +/* + * calculate physical address of a logical byte offset into the disk + * + * truncate dp->length if it crosses a track boundary + */ +static void +floppypos(FDrive *dp, long off) +{ + int lsec; + int ltrack; + int end; + + lsec = off/dp->t->bytes; + ltrack = lsec/dp->t->sectors; + dp->tcyl = ltrack/dp->t->heads; + dp->tsec = (lsec % dp->t->sectors) + 1; + dp->thead = (lsec/dp->t->sectors) % dp->t->heads; + + /* + * can't read across track boundaries. + * if so, decrement the bytes to be read. + */ + end = (ltrack+1)*dp->t->sectors*dp->t->bytes; + if(off+dp->len > end) + dp->len = end - off; +} + +/* + * get the interrupt cause from the floppy. + */ +static int +floppysense(void) +{ + fl.ncmd = 0; + fl.cmd[fl.ncmd++] = Fsense; + if(floppycmd() < 0) + return -1; + if(floppyresult() < 2){ + DPRINT("can't read sense response\n"); + fldump(); + fl.confused = 1; + return -1; + } + return 0; +} + +static int +cmddone(void *a) +{ + USED(a); + return fl.ncmd == 0; +} + +/* + * Wait for a floppy interrupt. If none occurs in 5 seconds, we + * may have missed one. This only happens on some portables which + * do power management behind our backs. Call the interrupt + * routine to try to clear any conditions. + */ +static void +floppywait(int slow) +{ + timedsleep(cmddone, 0, slow ? 5000 : 1000); + if(!cmddone(0)){ + floppyintr(0); + fl.confused = 1; + } +} + +/* + * we've lost the floppy position, go to cylinder 0. + */ +static int +floppyrecal(FDrive *dp) +{ + dp->ccyl = -1; + dp->cyl = -1; + + fl.ncmd = 0; + fl.cmd[fl.ncmd++] = Frecal; + fl.cmd[fl.ncmd++] = dp->dev; + if(floppycmd() < 0) + return -1; + floppywait(1); + if(fl.nstat < 2){ + DPRINT("recalibrate: confused %ux\n", inb(Pmsr)); + fl.confused = 1; + return -1; + } + if((fl.stat[0] & (Codemask|Seekend)) != Seekend){ + DPRINT("recalibrate: failed\n"); + dp->confused = 1; + return -1; + } + dp->cyl = fl.stat[1]; + if(dp->cyl != 0){ + DPRINT("recalibrate: wrong cylinder %d\n", dp->cyl); + dp->cyl = -1; + dp->confused = 1; + return -1; + } + + dp->confused = 0; + return 0; +} + +/* + * if the controller or a specific drive is in a confused state, + * reset it and get back to a kown state + */ +static void +floppyrevive(void) +{ + FDrive *dp; + + /* + * reset the controller if it's confused + */ + if(fl.confused){ + DPRINT("floppyrevive in\n"); + fldump(); + + /* reset controller and turn all motors off */ + splhi(); + fl.ncmd = 1; + fl.cmd[0] = 0; + outb(Pdor, 0); + delay(10); + outb(Pdor, Fintena|Fena); + delay(10); + spllo(); + fl.motor = 0; + fl.confused = 0; + floppywait(0); + + /* mark all drives in an unknown state */ + for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++) + dp->confused = 1; + + /* set rate to a known value */ + outb(Pdsr, 0); + fl.rate = 0; + + DPRINT("floppyrevive out\n"); + fldump(); + } +} + +/* + * seek to the target cylinder + * + * interrupt, no results + */ +static vlong +pcfloppyseek(FDrive *dp, vlong off) +{ + floppypos(dp, off); + if(dp->cyl == dp->tcyl){ + dp->offset = off; + return off; + } + dp->cyl = -1; + + fl.ncmd = 0; + fl.cmd[fl.ncmd++] = Fseek; + fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev; + fl.cmd[fl.ncmd++] = dp->tcyl * dp->t->steps; + if(floppycmd() < 0) + return -1; + floppywait(1); + if(fl.nstat < 2){ + DPRINT("seek: confused\n"); + fl.confused = 1; + return -1; + } + if((fl.stat[0] & (Codemask|Seekend)) != Seekend){ + DPRINT("seek: failed\n"); + dp->confused = 1; + return -1; + } + + dp->cyl = dp->tcyl; + dp->offset = off; + DPRINT("seek to %d succeeded\n", dp->offset); + + return dp->offset; +} + +/* + * read or write to floppy. try up to three times. + */ +static long +floppyxfer(FDrive *dp, int cmd, void *a, long off, long n) +{ + long offset; + int tries; + + if(off >= dp->t->cap) + return 0; + if(off + n > dp->t->cap) + n = dp->t->cap - off; + + /* retry on error (until it gets ridiculous) */ + for(tries = 0; tries < dp->maxtries; tries++){ + + dp->len = n; + if(pcfloppyseek(dp, off) < 0){ + DPRINT("xfer: seek failed\n"); + dp->confused = 1; + continue; + } + + /* + * set up the dma (dp->len may be trimmed) + */ + dp->len = dmasetup(DMAchan, a, dp->len, cmd==Fread); + if(dp->len < 0){ + buggery: + dmaend(DMAchan); + continue; + } + + /* + * start operation + */ + fl.ncmd = 0; + fl.cmd[fl.ncmd++] = cmd | (dp->t->heads > 1 ? Fmulti : 0); + fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev; + fl.cmd[fl.ncmd++] = dp->tcyl; + fl.cmd[fl.ncmd++] = dp->thead; + fl.cmd[fl.ncmd++] = dp->tsec; + fl.cmd[fl.ncmd++] = dp->t->bcode; + fl.cmd[fl.ncmd++] = dp->t->sectors; + fl.cmd[fl.ncmd++] = dp->t->gpl; + fl.cmd[fl.ncmd++] = 0xFF; + if(floppycmd() < 0) + goto buggery; + + /* + * give bus to DMA, floppyintr() will read result + */ + floppywait(0); + dmaend(DMAchan); + + /* + * check for errors + */ + if(fl.nstat < 7){ + DPRINT("xfer: confused\n"); + fl.confused = 1; + continue; + } + if((fl.stat[0] & Codemask)!=0 || fl.stat[1] || fl.stat[2]){ + DPRINT("xfer: failed %ux %ux %ux\n", fl.stat[0], + fl.stat[1], fl.stat[2]); + DPRINT("offset %lud len %ld\n", off, dp->len); + if((fl.stat[0]&Codemask)==Cmdexec && fl.stat[1]==Overrun){ + DPRINT("DMA overrun: retry\n"); + } else + dp->confused = 1; + continue; + } + + /* + * check for correct cylinder + */ + offset = fl.stat[3] * dp->t->heads + fl.stat[4]; + offset = offset*dp->t->sectors + fl.stat[5] - 1; + offset = offset * c2b[fl.stat[6]]; + if(offset != off+dp->len){ + DPRINT("xfer: ends on wrong cyl\n"); + dp->confused = 1; + continue; + } + + dp->lasttouched = m->ticks; + dp->maxtries = 20; + return dp->len; + } + + return -1; +} + +/* +void +floppymemwrite(void) +{ + int i; + int n; + uchar *a; + FDrive *dp; + + dp = &fl.d[0]; + a = (uchar*)0x80000000; + n = 0; + while(n < 1440*1024){ + i = floppyxfer(dp, Fwrite, a+n, n, 1440*1024-n); + if(i <= 0) + break; + n += i; + } + print("floppymemwrite wrote %d bytes\n", n); +splhi(); for(;;); +} +*/ + +static void +floppyintr(Ureg *ur) +{ + USED(ur); + switch(fl.cmd[0]&~Fmulti){ + case Fread: + case Fwrite: + case Fformat: + case Fdumpreg: + floppyresult(); + break; + case Fseek: + case Frecal: + default: + floppysense(); /* to clear interrupt */ + break; + } + fl.ncmd = 0; +} diff --git a/os/boot/pc/devfloppy.h b/os/boot/pc/devfloppy.h new file mode 100644 index 00000000..1d9a76a8 --- /dev/null +++ b/os/boot/pc/devfloppy.h @@ -0,0 +1,196 @@ +typedef struct FController FController; +typedef struct FDrive FDrive; +typedef struct FType FType; + +static void floppyintr(Ureg*); +static int floppyon(FDrive*); +static void floppyoff(FDrive*); +static void floppysetdef(FDrive*); + +/* + * a floppy drive + */ +struct FDrive +{ + FType *t; /* floppy type */ + int dt; /* drive type */ + int dev; + + ulong lasttouched; /* time last touched */ + int cyl; /* current arm position */ + int confused; /* needs to be recalibrated */ + int offset; /* current offset */ + int vers; + int maxtries; + + int tcyl; /* target cylinder */ + int thead; /* target head */ + int tsec; /* target sector */ + long len; /* size of xfer */ + + uchar *cache; /* track cache */ + int ccyl; + int chead; + +// Rendez r; /* waiting here for motor to spin up */ + void *aux; +}; + +/* + * controller for 4 floppys + */ +struct FController +{ +// QLock; /* exclusive access to the contoller */ + + int ndrive; + FDrive *d; /* the floppy drives */ + FDrive *selected; + int rate; /* current rate selected */ + uchar cmd[14]; /* command */ + int ncmd; /* # command bytes */ + uchar stat[14]; /* command status */ + int nstat; /* # status bytes */ + int confused; /* controler needs to be reset */ +// Rendez r; /* wait here for command termination */ + int motor; /* bit mask of spinning disks */ +// Rendez kr; /* for motor watcher */ +}; + +/* + * floppy types (all MFM encoding) + */ +struct FType +{ + char *name; + int dt; /* compatible drive type */ + int bytes; /* bytes/sector */ + int sectors; /* sectors/track */ + int heads; /* number of heads */ + int steps; /* steps per cylinder */ + int tracks; /* tracks/disk */ + int gpl; /* intersector gap length for read/write */ + int fgpl; /* intersector gap length for format */ + int rate; /* rate code */ + + /* + * these depend on previous entries and are set filled in + * by floppyinit + */ + int bcode; /* coded version of bytes for the controller */ + long cap; /* drive capacity in bytes */ + long tsize; /* track size in bytes */ +}; +/* bits in the registers */ +enum +{ + /* status registers a & b */ + Psra= 0x3f0, + Psrb= 0x3f1, + + /* digital output register */ + Pdor= 0x3f2, + Fintena= 0x8, /* enable floppy interrupt */ + Fena= 0x4, /* 0 == reset controller */ + + /* main status register */ + Pmsr= 0x3f4, + Fready= 0x80, /* ready to be touched */ + Ffrom= 0x40, /* data from controller */ + Ffloppybusy= 0x10, /* operation not over */ + + /* data register */ + Pfdata= 0x3f5, + Frecal= 0x07, /* recalibrate cmd */ + Fseek= 0x0f, /* seek cmd */ + Fsense= 0x08, /* sense cmd */ + Fread= 0x66, /* read cmd */ + Freadid= 0x4a, /* read track id */ + Fspec= 0x03, /* set hold times */ + Fwrite= 0x45, /* write cmd */ + Fformat= 0x4d, /* format cmd */ + Fmulti= 0x80, /* or'd with Fread or Fwrite for multi-head */ + Fdumpreg= 0x0e, /* dump internal registers */ + + /* digital input register */ + Pdir= 0x3F7, /* disk changed port (read only) */ + Pdsr= 0x3F7, /* data rate select port (write only) */ + Fchange= 0x80, /* disk has changed */ + + /* status 0 byte */ + Drivemask= 3<<0, + Seekend= 1<<5, + Codemask= (3<<6)|(3<<3), + Cmdexec= 1<<6, + + /* status 1 byte */ + Overrun= 0x10, +}; + +/* + * types of drive (from PC equipment byte) + */ +enum +{ + Tnone= 0, + T360kb= 1, + T1200kb= 2, + T720kb= 3, + T1440kb= 4, +}; + +static void +pcfloppyintr(Ureg *ur, void *a) +{ + USED(a); + + floppyintr(ur); +} + +void +floppysetup0(FController *fl) +{ + uchar equip; + + /* + * Read nvram for types of floppies 0 & 1. + * Always try floppy 0. + */ + equip = nvramread(0x10); + fl->ndrive = 1; + + if(equip & 0xf) + fl->ndrive++; + + /* + * Allocate the drive storage. + * There's always one. + */ + fl->d = xalloc(fl->ndrive*sizeof(FDrive)); + fl->d[0].dt = (equip >> 4) & 0xf; + if(fl->d[0].dt == Tnone) + fl->d[0].dt = T1440kb; + + if(fl->ndrive == 2) + fl->d[1].dt = equip & 0xf; +} + +void +floppysetup1(FController*) +{ +// intrenable(VectorFLOPPY, pcfloppyintr, fl, BUSUNKNOWN); + setvec(VectorFLOPPY, pcfloppyintr, 0); +} + + +static vlong pcfloppyseek(FDrive*, vlong); +FController fl; + +vlong +floppyseek(Fs *fs, vlong off) +{ + FDrive *dp; + + dp = &fl.d[fs->dev]; + return pcfloppyseek(dp, off); +} diff --git a/os/boot/pc/devi82365.c b/os/boot/pc/devi82365.c new file mode 100644 index 00000000..a4e09d2d --- /dev/null +++ b/os/boot/pc/devi82365.c @@ -0,0 +1,1205 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "io.h" + +/* + * Support for up to 4 Slot card slots. Generalizing above that is hard + * since addressing is not obvious. - presotto + * + * WARNING: This has never been tried with more than one card slot. + */ + +/* + * Intel 82365SL PCIC controller for the PCMCIA or + * Cirrus Logic PD6710/PD6720 which is mostly register compatible + */ +enum +{ + /* + * registers indices + */ + Rid= 0x0, /* identification and revision */ + Ris= 0x1, /* interface status */ + Rpc= 0x2, /* power control */ + Foutena= (1<<7), /* output enable */ + Fautopower= (1<<5), /* automatic power switching */ + Fcardena= (1<<4), /* PC card enable */ + Rigc= 0x3, /* interrupt and general control */ + Fiocard= (1<<5), /* I/O card (vs memory) */ + Fnotreset= (1<<6), /* reset if not set */ + FSMIena= (1<<4), /* enable change interrupt on SMI */ + Rcsc= 0x4, /* card status change */ + Rcscic= 0x5, /* card status change interrupt config */ + Fchangeena= (1<<3), /* card changed */ + Fbwarnena= (1<<1), /* card battery warning */ + Fbdeadena= (1<<0), /* card battery dead */ + Rwe= 0x6, /* address window enable */ + Fmem16= (1<<5), /* use A23-A12 to decode address */ + Rio= 0x7, /* I/O control */ + Fwidth16= (1<<0), /* 16 bit data width */ + Fiocs16= (1<<1), /* IOCS16 determines data width */ + Fzerows= (1<<2), /* zero wait state */ + Ftiming= (1<<3), /* timing register to use */ + Riobtm0lo= 0x8, /* I/O address 0 start low byte */ + Riobtm0hi= 0x9, /* I/O address 0 start high byte */ + Riotop0lo= 0xa, /* I/O address 0 stop low byte */ + Riotop0hi= 0xb, /* I/O address 0 stop high byte */ + Riobtm1lo= 0xc, /* I/O address 1 start low byte */ + Riobtm1hi= 0xd, /* I/O address 1 start high byte */ + Riotop1lo= 0xe, /* I/O address 1 stop low byte */ + Riotop1hi= 0xf, /* I/O address 1 stop high byte */ + Rmap= 0x10, /* map 0 */ + + /* + * CL-PD67xx extension registers + */ + Rmisc1= 0x16, /* misc control 1 */ + F5Vdetect= (1<<0), + Fvcc3V= (1<<1), + Fpmint= (1<<2), + Fpsirq= (1<<3), + Fspeaker= (1<<4), + Finpack= (1<<7), + Rfifo= 0x17, /* fifo control */ + Fflush= (1<<7), /* flush fifo */ + Rmisc2= 0x1E, /* misc control 2 */ + Flowpow= (1<<1), /* low power mode */ + Rchipinfo= 0x1F, /* chip information */ + Ratactl= 0x26, /* ATA control */ + + /* + * offsets into the system memory address maps + */ + Mbtmlo= 0x0, /* System mem addr mapping start low byte */ + Mbtmhi= 0x1, /* System mem addr mapping start high byte */ + F16bit= (1<<7), /* 16-bit wide data path */ + Mtoplo= 0x2, /* System mem addr mapping stop low byte */ + Mtophi= 0x3, /* System mem addr mapping stop high byte */ + Ftimer1= (1<<6), /* timer set 1 */ + Mofflo= 0x4, /* Card memory offset address low byte */ + Moffhi= 0x5, /* Card memory offset address high byte */ + Fregactive= (1<<6), /* attribute memory */ + + Mbits= 13, /* msb of Mchunk */ + Mchunk= 1<cp->xreg, pp->base + index); + return inb(pp->cp->dreg); +} +static void +wrreg(Slot *pp, int index, uchar val) +{ + outb(pp->cp->xreg, pp->base + index); + outb(pp->cp->dreg, val); +} + +/* + * get info about card + */ +static void +slotinfo(Slot *pp) +{ + uchar isr; + + isr = rdreg(pp, Ris); + pp->occupied = (isr & (3<<2)) == (3<<2); + pp->powered = isr & (1<<6); + pp->battery = (isr & 3) == 3; + pp->wrprot = isr & (1<<4); + pp->busy = isr & (1<<5); +} + +static int +vcode(int volt) +{ + switch(volt){ + case 5: + return 1; + case 12: + return 2; + default: + return 0; + } +} + +/* + * enable the slot card + */ +static void +slotena(Slot *pp) +{ + if(pp->enabled) + return; + + /* power up and unreset, wait's are empirical (???) */ + wrreg(pp, Rpc, Fautopower|Foutena|Fcardena); + delay(300); + wrreg(pp, Rigc, 0); + delay(100); + wrreg(pp, Rigc, Fnotreset); + delay(500); + + /* get configuration */ + slotinfo(pp); + if(pp->occupied){ + cisread(pp); + pp->enabled = 1; + } else + wrreg(pp, Rpc, Fautopower); +} + +/* + * disable the slot card + */ +static void +slotdis(Slot *pp) +{ + wrreg(pp, Rpc, 0); /* turn off card power */ + wrreg(pp, Rwe, 0); /* no windows */ + pp->enabled = 0; +} + +/* + * status change interrupt + */ +static void +i82365intr(Ureg *, void *) +{ + uchar csc, was; + Slot *pp; + + if(slot == 0) + return; + + for(pp = slot; pp < lastslot; pp++){ + csc = rdreg(pp, Rcsc); + was = pp->occupied; + slotinfo(pp); + if(csc & (1<<3) && was != pp->occupied){ + if(!pp->occupied) + slotdis(pp); + } + } +} + +enum +{ + Mshift= 12, + Mgran= (1<mlock); + + /* convert offset to granularity */ + if(len <= 0) + len = 1; + e = ROUND(offset+len, Mgran); + offset &= Mmask; + len = e - offset; + + /* look for a map that covers the right area */ + we = rdreg(pp, Rwe); + bit = 1; + nm = 0; + for(m = pp->mmap; m < &pp->mmap[Nmap]; m++){ + if((we & bit)) + if(m->attr == attr) + if(offset >= m->ca && e <= m->cea){ + + m->ref++; + unlock(&pp->mlock); + return m; + } + bit <<= 1; + if(nm == 0 && m->ref == 0) + nm = m; + } + m = nm; + if(m == 0){ + unlock(&pp->mlock); + return 0; + } + + /* if isa space isn't big enough, free it and get more */ + if(m->len < len){ + if(m->isa){ + umbfree(m->isa, m->len); + m->len = 0; + } + m->isa = PADDR(umbmalloc(0, len, Mgran)); + if(m->isa == 0){ + print("pcmmap %d: out of isa space\n", len); + unlock(&pp->mlock); + return 0; + } + m->len = len; + } + + /* set up new map */ + m->ca = offset; + m->cea = m->ca + m->len; + m->attr = attr; + i = m-pp->mmap; + bit = 1<isa>>Mshift); + wrreg(pp, MAP(i, Mbtmhi), (m->isa>>(Mshift+8)) | F16bit); + wrreg(pp, MAP(i, Mtoplo), (m->isa+m->len-1)>>Mshift); + wrreg(pp, MAP(i, Mtophi), ((m->isa+m->len-1)>>(Mshift+8))); + offset -= m->isa; + offset &= (1<<25)-1; + offset >>= Mshift; + wrreg(pp, MAP(i, Mofflo), offset); + wrreg(pp, MAP(i, Moffhi), (offset>>8) | (attr ? Fregactive : 0)); + wrreg(pp, Rwe, we | bit); /* enable map */ + m->ref = 1; + + unlock(&pp->mlock); + return m; +} + +void +pcmunmap(int slotno, PCMmap* m) +{ + Slot *pp; + + pp = slot + slotno; + lock(&pp->mlock); + m->ref--; + unlock(&pp->mlock); +} + +static void +increfp(Slot *pp) +{ + lock(pp); + if(pp->ref++ == 0) + slotena(pp); + unlock(pp); +} + +static void +decrefp(Slot *pp) +{ + lock(pp); + if(pp->ref-- == 1) + slotdis(pp); + unlock(pp); +} + +/* + * look for a card whose version contains 'idstr' + */ +static int +pcmcia_pcmspecial(char *idstr, ISAConf *isa) +{ + Slot *pp; + extern char *strstr(char*, char*); + int enabled; + + i82365reset(); + for(pp = slot; pp < lastslot; pp++){ + if(pp->special) + continue; /* already taken */ + enabled = 0; + /* make sure we don't power on cards when we already know what's + * in them. We'll reread every two minutes if necessary + */ + if (pp->verstr[0] == '\0') { + increfp(pp); + enabled++; + } + + if(pp->occupied) { + if(strstr(pp->verstr, idstr)) { + if (!enabled) + increfp(pp); + if(isa == 0 || pcmio(pp->slotno, isa) == 0){ + pp->special = 1; + return pp->slotno; + } + } + } else + pp->special = 1; + if (enabled) + decrefp(pp); + } + return -1; +} + +static void +pcmcia_pcmspecialclose(int slotno) +{ + Slot *pp; + + print("pcmspecialclose called\n"); + if(slotno >= nslot) + panic("pcmspecialclose"); + pp = slot + slotno; + pp->special = 0; + decrefp(pp); +} + +static char *chipname[] = +{ +[Ti82365] "Intel 82365SL", +[Tpd6710] "Cirrus Logic PD6710", +[Tpd6720] "Cirrus Logic PD6720", +[Tvg46x] "Vadem VG-46x", +}; + +static I82365* +i82365probe(int x, int d, int dev) +{ + uchar c, id; + I82365 *cp; + ISAConf isa; + int i, nslot; + + outb(x, Rid + (dev<<7)); + id = inb(d); + if((id & 0xf0) != 0x80) + return 0; /* not this family */ + + cp = xalloc(sizeof(I82365)); + cp->xreg = x; + cp->dreg = d; + cp->dev = dev; + cp->type = Ti82365; + cp->nslot = 2; + + switch(id){ + case 0x82: + case 0x83: + case 0x84: + /* could be a cirrus */ + outb(x, Rchipinfo + (dev<<7)); + outb(d, 0); + c = inb(d); + if((c & 0xc0) != 0xc0) + break; + c = inb(d); + if((c & 0xc0) != 0x00) + break; + if(c & 0x20){ + cp->type = Tpd6720; + } else { + cp->type = Tpd6710; + cp->nslot = 1; + } + + /* low power mode */ + outb(x, Rmisc2 + (dev<<7)); + c = inb(d); + outb(d, c & ~Flowpow); + break; + } + + if(cp->type == Ti82365){ + outb(x, 0x0E + (dev<<7)); + outb(x, 0x37 + (dev<<7)); + outb(x, 0x3A + (dev<<7)); + c = inb(d); + outb(d, c|0xC0); + outb(x, Rid + (dev<<7)); + c = inb(d); + if(c != id && !(c & 0x08)) + print("#y%d: id %uX changed to %uX\n", ncontroller, id, c); + if(c & 0x08) + cp->type = Tvg46x; + outb(x, 0x3A + (dev<<7)); + c = inb(d); + outb(d, c & ~0xC0); + } + + memset(&isa, 0, sizeof(ISAConf)); + if(isaconfig("pcmcia", ncontroller, &isa) && isa.irq) + cp->irq = isa.irq; + else + cp->irq = VectorPCMCIA - VectorPIC; + + for(i = 0; i < isa.nopt; i++){ + if(cistrncmp(isa.opt[i], "nslot=", 6)) + continue; + nslot = strtol(&isa.opt[i][6], nil, 0); + if(nslot > 0 && nslot <= 2) + cp->nslot = nslot; + } + + controller[ncontroller++] = cp; + return cp; +} + +static void +i82365dump(Slot *pp) +{ + int i; + + for(i = 0; i < 0x40; i++){ + if((i&0x0F) == 0) + print("\n%2.2uX: ", i); + if(((i+1) & 0x0F) == 0x08) + print(" - "); + print("%2.2uX ", rdreg(pp, i)); + } + print("\n"); +} + +/* + * set up for slot cards + */ +static void +i82365reset(void) +{ + static int already; + int i, j; + I82365 *cp; + Slot *pp; + + if(already) + return; + already = 1; + + + /* look for controllers */ + i82365probe(0x3E0, 0x3E1, 0); + i82365probe(0x3E0, 0x3E1, 1); + i82365probe(0x3E2, 0x3E3, 0); + i82365probe(0x3E2, 0x3E3, 1); + + for(i = 0; i < ncontroller; i++) + nslot += controller[i]->nslot; + slot = xalloc(nslot * sizeof(Slot)); + + /* if the card is there turn on 5V power to keep its battery alive */ + lastslot = slot; + for(i = 0; i < ncontroller; i++){ + cp = controller[i]; + print("#y%d: %d slot %s: port 0x%uX irq %d\n", + i, cp->nslot, chipname[cp->type], cp->xreg, cp->irq); + for(j = 0; j < cp->nslot; j++){ + pp = lastslot++; + pp->slotno = pp - slot; + pp->memlen = 64*MB; + pp->base = (cp->dev<<7) | (j<<6); + pp->cp = cp; + slotdis(pp); + + /* interrupt on status change */ + wrreg(pp, Rcscic, (cp->irq<<4) | Fchangeena); + rdreg(pp, Rcsc); + } + + /* for card management interrupts */ + setvec(cp->irq+VectorPIC, i82365intr, 0); + } +} + +/* + * configure the Slot for IO. We assume very heavily that we can read + * configuration info from the CIS. If not, we won't set up correctly. + */ +static int +pcmio(int slotno, ISAConf *isa) +{ + uchar we, x, *p; + Slot *pp; + Conftab *ct, *et, *t; + PCMmap *m; + int i, index, irq; + char *cp; + + irq = isa->irq; + if(irq == 2) + irq = 9; + + if(slotno > nslot) + return -1; + pp = slot + slotno; + + if(!pp->occupied) + return -1; + + et = &pp->ctab[pp->nctab]; + + ct = 0; + for(i = 0; i < isa->nopt; i++){ + if(strncmp(isa->opt[i], "index=", 6)) + continue; + index = strtol(&isa->opt[i][6], &cp, 0); + if(cp == &isa->opt[i][6] || index >= pp->nctab) + return -1; + ct = &pp->ctab[index]; + } + if(ct == 0){ + + /* assume default is right */ + if(pp->def) + ct = pp->def; + else + ct = pp->ctab; + + /* try for best match */ + if(ct->nio == 0 + || ct->io[0].start != isa->port || ((1<irqs) == 0){ + for(t = pp->ctab; t < et; t++) + if(t->nio + && t->io[0].start == isa->port + && ((1<irqs)){ + ct = t; + break; + } + } + if(ct->nio == 0 || ((1<irqs) == 0){ + for(t = pp->ctab; t < et; t++) + if(t->nio && ((1<irqs)){ + ct = t; + break; + } + } + if(ct->nio == 0){ + for(t = pp->ctab; t < et; t++) + if(t->nio){ + ct = t; + break; + } + } + } + + if(ct == et || ct->nio == 0) + return -1; + if(isa->port == 0 && ct->io[0].start == 0) + return -1; + + /* route interrupts */ + isa->irq = irq; + wrreg(pp, Rigc, irq | Fnotreset | Fiocard); + + /* set power and enable device */ + x = vcode(ct->vpp1); + wrreg(pp, Rpc, x|Fautopower|Foutena|Fcardena); + + /* 16-bit data path */ + if(ct->bit16) + x = Ftiming|Fiocs16|Fwidth16; + else + x = Ftiming; + if(ct->nio == 2 && ct->io[1].start) + x |= x<<4; + wrreg(pp, Rio, x); + + /* enable io port map 0 */ + if(isa->port == 0) + isa->port = ct->io[0].start; + we = rdreg(pp, Rwe); + wrreg(pp, Riobtm0lo, isa->port); + wrreg(pp, Riobtm0hi, isa->port>>8); + i = isa->port+ct->io[0].len-1; + wrreg(pp, Riotop0lo, i); + wrreg(pp, Riotop0hi, i>>8); + we |= 1<<6; + if(ct->nio == 2 && ct->io[1].start){ + wrreg(pp, Riobtm1lo, ct->io[1].start); + wrreg(pp, Riobtm1hi, ct->io[1].start>>8); + i = ct->io[1].start+ct->io[1].len-1; + wrreg(pp, Riotop1lo, i); + wrreg(pp, Riotop1hi, i>>8); + we |= 1<<7; + } + wrreg(pp, Rwe, we); + + /* only touch Rconfig if it is present */ + if(pp->cpresent & (1<caddr + Rconfig, 1, 1); + p = KADDR(m->isa + pp->caddr + Rconfig - m->ca); + + /* set configuration and interrupt type */ + x = ct->index; + if((ct->irqtype & 0x20) && ((ct->irqtype & 0x40)==0 || isa->irq>7)) + x |= Clevel; + *p = x; + delay(5); + + pcmunmap(slotno, m); + } + return 0; +} + +/* + * read and crack the card information structure enough to set + * important parameters like power + */ +static void tcfig(Slot*, Cisdat*, int); +static void tentry(Slot*, Cisdat*, int); +static void tvers1(Slot*, Cisdat*, int); + +struct { + int n; + void (*parse)(Slot*, Cisdat*, int); +} cistab[] = { + 0x15, tvers1, + 0x1A, tcfig, + 0x1B, tentry, +}; + +static int +readc(Cisdat *pp, uchar *x) +{ + if(pp->cispos >= pp->cislen) + return 0; + *x = pp->cisbase[pp->cisskip*pp->cispos]; + pp->cispos++; + return 1; +} + +static int +xcistuple(int slotno, int tuple, void *v, int nv, int attr) +{ + PCMmap *m; + Cisdat cis; + int i, l; + uchar *p; + uchar type, link; + int this; + + m = pcmmap(slotno, 0, 0, attr); + if(m == 0) { +if(debug) print("could not map\n"); + return -1; + } + + cis.cisbase = KADDR(m->isa); + cis.cispos = 0; + cis.cisskip = attr ? 2 : 1; + cis.cislen = Mchunk; + +if(debug) print("cis %d %d #%lux srch %x...", attr, cis.cisskip, cis.cisbase, tuple); + /* loop through all the tuples */ + for(i = 0; i < 1000; i++){ + this = cis.cispos; + if(readc(&cis, &type) != 1) + break; +if(debug) print("%2ux...", type); + if(type == 0xFF) + break; + if(readc(&cis, &link) != 1) + break; + if(link == 0xFF) + break; + if(type == tuple) { + p = v; + for(l=0; l= 0) + return n; + return xcistuple(slotno, tuple, v, nv, 0); +} + +static void +cisread(Slot *pp) +{ + uchar v[256]; + int i, nv; + Cisdat cis; + + memset(pp->ctab, 0, sizeof(pp->ctab)); + pp->caddr = 0; + pp->cpresent = 0; + pp->configed = 0; + pp->nctab = 0; + + for(i = 0; i < nelem(cistab); i++) { + if((nv = pcmcistuple(pp->slotno, cistab[i].n, v, sizeof(v))) >= 0) { + cis.cisbase = v; + cis.cispos = 0; + cis.cisskip = 1; + cis.cislen = nv; + + (*cistab[i].parse)(pp, &cis, cistab[i].n); + } + } +} + +static ulong +getlong(Cisdat *cis, int size) +{ + uchar c; + int i; + ulong x; + + x = 0; + for(i = 0; i < size; i++){ + if(readc(cis, &c) != 1) + break; + x |= c<<(i*8); + } + return x; +} + +static void +tcfig(Slot *pp, Cisdat *cis, int ) +{ + uchar size, rasize, rmsize; + uchar last; + + if(readc(cis, &size) != 1) + return; + rasize = (size&0x3) + 1; + rmsize = ((size>>2)&0xf) + 1; + if(readc(cis, &last) != 1) + return; + pp->caddr = getlong(cis, rasize); + pp->cpresent = getlong(cis, rmsize); +} + +static ulong vexp[8] = +{ + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000 +}; +static ulong vmant[16] = +{ + 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 90, +}; + +static ulong +microvolt(Cisdat *cis) +{ + uchar c; + ulong microvolts; + ulong exp; + + if(readc(cis, &c) != 1) + return 0; + exp = vexp[c&0x7]; + microvolts = vmant[(c>>3)&0xf]*exp; + while(c & 0x80){ + if(readc(cis, &c) != 1) + return 0; + switch(c){ + case 0x7d: + break; /* high impedence when sleeping */ + case 0x7e: + case 0x7f: + microvolts = 0; /* no connection */ + break; + default: + exp /= 10; + microvolts += exp*(c&0x7f); + } + } + return microvolts; +} + +static ulong +nanoamps(Cisdat *cis) +{ + uchar c; + ulong nanoamps; + + if(readc(cis, &c) != 1) + return 0; + nanoamps = vexp[c&0x7]*vmant[(c>>3)&0xf]; + while(c & 0x80){ + if(readc(cis, &c) != 1) + return 0; + if(c == 0x7d || c == 0x7e || c == 0x7f) + nanoamps = 0; + } + return nanoamps; +} + +/* + * only nominal voltage is important for config + */ +static ulong +power(Cisdat *cis) +{ + uchar feature; + ulong mv; + + mv = 0; + if(readc(cis, &feature) != 1) + return 0; + if(feature & 1) + mv = microvolt(cis); + if(feature & 2) + microvolt(cis); + if(feature & 4) + microvolt(cis); + if(feature & 8) + nanoamps(cis); + if(feature & 0x10) + nanoamps(cis); + if(feature & 0x20) + nanoamps(cis); + if(feature & 0x40) + nanoamps(cis); + return mv/1000000; +} + +static ulong mantissa[16] = +{ 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, }; + +static ulong exponent[8] = +{ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, }; + +static ulong +ttiming(Cisdat *cis, int scale) +{ + uchar unscaled; + ulong nanosecs; + + if(readc(cis, &unscaled) != 1) + return 0; + nanosecs = (mantissa[(unscaled>>3)&0xf]*exponent[unscaled&7])/10; + nanosecs = nanosecs * vexp[scale]; + return nanosecs; +} + +static void +timing(Cisdat *cis, Conftab *ct) +{ + uchar c, i; + + if(readc(cis, &c) != 1) + return; + i = c&0x3; + if(i != 3) + ct->maxwait = ttiming(cis, i); /* max wait */ + i = (c>>2)&0x7; + if(i != 7) + ct->readywait = ttiming(cis, i); /* max ready/busy wait */ + i = (c>>5)&0x7; + if(i != 7) + ct->otherwait = ttiming(cis, i); /* reserved wait */ +} + +static void +iospaces(Cisdat *cis, Conftab *ct) +{ + uchar c; + int i, nio; + + ct->nio = 0; + if(readc(cis, &c) != 1) + return; + + ct->bit16 = ((c>>5)&3) >= 2; + if(!(c & 0x80)){ + ct->io[0].start = 0; + ct->io[0].len = 1<<(c&0x1f); + ct->nio = 1; + return; + } + + if(readc(cis, &c) != 1) + return; + + nio = (c&0xf)+1; + for(i = 0; i < nio; i++){ + ct->io[i].start = getlong(cis, (c>>4)&0x3); + ct->io[i].len = getlong(cis, (c>>6)&0x3)+1; + } + ct->nio = nio; +} + +static void +irq(Cisdat *cis, Conftab *ct) +{ + uchar c; + + if(readc(cis, &c) != 1) + return; + ct->irqtype = c & 0xe0; + if(c & 0x10) + ct->irqs = getlong(cis, 2); + else + ct->irqs = 1<<(c&0xf); + ct->irqs &= 0xDEB8; /* levels available to card */ +} + +static void +memspace(Cisdat *cis, int asize, int lsize, int host) +{ + ulong haddress, address, len; + + len = getlong(cis, lsize)*256; + address = getlong(cis, asize)*256; + USED(len, address); + if(host){ + haddress = getlong(cis, asize)*256; + USED(haddress); + } +} + +static void +tentry(Slot *pp, Cisdat *cis, int ) +{ + uchar c, i, feature; + Conftab *ct; + + if(pp->nctab >= Maxctab) + return; + if(readc(cis, &c) != 1) + return; + ct = &pp->ctab[pp->nctab++]; + + /* copy from last default config */ + if(pp->def) + *ct = *pp->def; + + ct->index = c & 0x3f; + + /* is this the new default? */ + if(c & 0x40) + pp->def = ct; + + /* memory wait specified? */ + if(c & 0x80){ + if(readc(cis, &i) != 1) + return; + if(i&0x80) + ct->memwait = 1; + } + + if(readc(cis, &feature) != 1) + return; + switch(feature&0x3){ + case 1: + ct->vpp1 = ct->vpp2 = power(cis); + break; + case 2: + power(cis); + ct->vpp1 = ct->vpp2 = power(cis); + break; + case 3: + power(cis); + ct->vpp1 = power(cis); + ct->vpp2 = power(cis); + break; + default: + break; + } + if(feature&0x4) + timing(cis, ct); + if(feature&0x8) + iospaces(cis, ct); + if(feature&0x10) + irq(cis, ct); + switch((feature>>5)&0x3){ + case 1: + memspace(cis, 0, 2, 0); + break; + case 2: + memspace(cis, 2, 2, 0); + break; + case 3: + if(readc(cis, &c) != 1) + return; + for(i = 0; i <= (c&0x7); i++) + memspace(cis, (c>>5)&0x3, (c>>3)&0x3, c&0x80); + break; + } + pp->configed++; +} + +static void +tvers1(Slot *pp, Cisdat *cis, int ) +{ + uchar c, major, minor; + int i; + + if(readc(cis, &major) != 1) + return; + if(readc(cis, &minor) != 1) + return; + for(i = 0; i < sizeof(pp->verstr)-1; i++){ + if(readc(cis, &c) != 1) + return; + if(c == 0) + c = '\n'; + if(c == 0xff) + break; + pp->verstr[i] = c; + } + pp->verstr[i] = 0; +} diff --git a/os/boot/pc/devpccard.c b/os/boot/pc/devpccard.c new file mode 100644 index 00000000..d5c96aa9 --- /dev/null +++ b/os/boot/pc/devpccard.c @@ -0,0 +1,1957 @@ +/* + cardbus and pcmcia (grmph) support. +*/ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "io.h" + +#define ioalloc(addr, len, align, name) (addr) +#define iofree(addr) +extern int pciscan(int, Pcidev **); +extern ulong pcibarsize(Pcidev *, int); + +int (*_pcmspecial)(char *, ISAConf *); +void (*_pcmspecialclose)(int); + +int +pcmspecial(char *idstr, ISAConf *isa) +{ + return (_pcmspecial != nil)? _pcmspecial(idstr, isa): -1; +} + +void +pcmspecialclose(int a) +{ + if (_pcmspecialclose != nil) + _pcmspecialclose(a); +} + +static ulong +ioreserve(ulong, int size, int align, char *) +{ + static ulong isaend = 0xfd00; + ulong ioaddr; + + if (align) + isaend = ((isaend + align - 1) / align) * align; + ioaddr = isaend; + isaend += size; + return ioaddr; +} + +#define MAP(x,o) (Rmap + (x)*0x8 + o) + +enum { + TI_vid = 0x104c, + TI_1131_did = 0xAC15, + TI_1250_did = 0xAC16, + TI_1450_did = 0xAC1B, + TI_1251A_did = 0xAC1D, + + Ricoh_vid = 0x1180, + Ricoh_476_did = 0x0476, + Ricoh_478_did = 0x0478, + + Nslots = 4, /* Maximum number of CardBus slots to use */ + + K = 1024, + M = K * K, + + LegacyAddr = 0x3e0, + NUMEVENTS = 10, + + TI1131xSC = 0x80, // system control + TI122X_SC_INTRTIE = 1 << 29, + TI12xxIM = 0x8c, // + TI1131xCC = 0x91, // card control + TI113X_CC_RIENB = 1 << 7, + TI113X_CC_ZVENABLE = 1 << 6, + TI113X_CC_PCI_IRQ_ENA = 1 << 5, + TI113X_CC_PCI_IREQ = 1 << 4, + TI113X_CC_PCI_CSC = 1 << 3, + TI113X_CC_SPKROUTEN = 1 << 1, + TI113X_CC_IFG = 1 << 0, + TI1131xDC = 0x92, // device control +}; + +typedef struct { + ushort r_vid; + ushort r_did; + char *r_name; +} variant_t; + +static variant_t variant[] = { +{ Ricoh_vid, Ricoh_476_did, "Ricoh 476 PCI/Cardbus bridge", }, +{ Ricoh_vid, Ricoh_478_did, "Ricoh 478 PCI/Cardbus bridge", }, +{ TI_vid, TI_1131_did, "TI PCI-1131 Cardbus Controller", }, +{ TI_vid, TI_1250_did, "TI PCI-1250 Cardbus Controller", }, +{ TI_vid, TI_1450_did, "TI PCI-1450 Cardbus Controller", }, +{ TI_vid, TI_1251A_did, "TI PCI-1251A Cardbus Controller", }, +}; + +/* Cardbus registers */ +enum { + SocketEvent = 0, + SE_CCD = 3 << 1, + SE_POWER = 1 << 3, + SocketMask = 1, + SocketState = 2, + SS_CCD = 3 << 1, + SS_POWER = 1 << 3, + SS_PC16 = 1 << 4, + SS_CBC = 1 << 5, + SS_NOTCARD = 1 << 7, + SS_BADVCC = 1 << 9, + SS_5V = 1 << 10, + SS_3V = 1 << 11, + SocketForce = 3, + SocketControl = 4, + SC_5V = 0x22, + SC_3V = 0x33, +}; + +enum { + PciPCR_IO = 1 << 0, + PciPCR_MEM = 1 << 1, + PciPCR_Master = 1 << 2, + + Nbars = 6, + Ncmd = 10, + CBIRQ = 9, + + PC16, + PC32, +}; + +enum { + Ti82365, + Tpd6710, + Tpd6720, + Tvg46x, +}; + +static char *chipname[] = { +[Ti82365] "Intel 82365SL", +[Tpd6710] "Cirrus Logic PD6710", +[Tpd6720] "Cirrus Logic PD6720", +[Tvg46x] "Vadem VG-46x", +}; + +/* + * Intel 82365SL PCIC controller for the PCMCIA or + * Cirrus Logic PD6710/PD6720 which is mostly register compatible + */ +enum +{ + /* + * registers indices + */ + Rid= 0x0, /* identification and revision */ + Ris= 0x1, /* interface status */ + Rpc= 0x2, /* power control */ + Foutena= (1<<7), /* output enable */ + Fautopower= (1<<5), /* automatic power switching */ + Fcardena= (1<<4), /* PC card enable */ + Rigc= 0x3, /* interrupt and general control */ + Fiocard= (1<<5), /* I/O card (vs memory) */ + Fnotreset= (1<<6), /* reset if not set */ + FSMIena= (1<<4), /* enable change interrupt on SMI */ + Rcsc= 0x4, /* card status change */ + Rcscic= 0x5, /* card status change interrupt config */ + Fchangeena= (1<<3), /* card changed */ + Fbwarnena= (1<<1), /* card battery warning */ + Fbdeadena= (1<<0), /* card battery dead */ + Rwe= 0x6, /* address window enable */ + Fmem16= (1<<5), /* use A23-A12 to decode address */ + Rio= 0x7, /* I/O control */ + Fwidth16= (1<<0), /* 16 bit data width */ + Fiocs16= (1<<1), /* IOCS16 determines data width */ + Fzerows= (1<<2), /* zero wait state */ + Ftiming= (1<<3), /* timing register to use */ + Riobtm0lo= 0x8, /* I/O address 0 start low byte */ + Riobtm0hi= 0x9, /* I/O address 0 start high byte */ + Riotop0lo= 0xa, /* I/O address 0 stop low byte */ + Riotop0hi= 0xb, /* I/O address 0 stop high byte */ + Riobtm1lo= 0xc, /* I/O address 1 start low byte */ + Riobtm1hi= 0xd, /* I/O address 1 start high byte */ + Riotop1lo= 0xe, /* I/O address 1 stop low byte */ + Riotop1hi= 0xf, /* I/O address 1 stop high byte */ + Rmap= 0x10, /* map 0 */ + + /* + * CL-PD67xx extension registers + */ + Rmisc1= 0x16, /* misc control 1 */ + F5Vdetect= (1<<0), + Fvcc3V= (1<<1), + Fpmint= (1<<2), + Fpsirq= (1<<3), + Fspeaker= (1<<4), + Finpack= (1<<7), + Rfifo= 0x17, /* fifo control */ + Fflush= (1<<7), /* flush fifo */ + Rmisc2= 0x1E, /* misc control 2 */ + Flowpow= (1<<1), /* low power mode */ + Rchipinfo= 0x1F, /* chip information */ + Ratactl= 0x26, /* ATA control */ + + /* + * offsets into the system memory address maps + */ + Mbtmlo= 0x0, /* System mem addr mapping start low byte */ + Mbtmhi= 0x1, /* System mem addr mapping start high byte */ + F16bit= (1<<7), /* 16-bit wide data path */ + Mtoplo= 0x2, /* System mem addr mapping stop low byte */ + Mtophi= 0x3, /* System mem addr mapping stop high byte */ + Ftimer1= (1<<6), /* timer set 1 */ + Mofflo= 0x4, /* Card memory offset address low byte */ + Moffhi= 0x5, /* Card memory offset address high byte */ + Fregactive= (1<<6), /* attribute memory */ + + /* + * configuration registers - they start at an offset in attribute + * memory found in the CIS. + */ + Rconfig= 0, + Creset= (1<<7), /* reset device */ + Clevel= (1<<6), /* level sensitive interrupt line */ +}; + +/* + * read and crack the card information structure enough to set + * important parameters like power + */ +/* cis memory walking */ +typedef struct Cisdat { + uchar *cisbase; + int cispos; + int cisskip; + int cislen; +} Cisdat; + +/* configuration table entry */ +typedef struct PCMconftab PCMconftab; +struct PCMconftab +{ + int index; + ushort irqs; /* legal irqs */ + uchar irqtype; + uchar bit16; /* true for 16 bit access */ + struct { + ulong start; + ulong len; + } io[16]; + int nio; + uchar vpp1; + uchar vpp2; + uchar memwait; + ulong maxwait; + ulong readywait; + ulong otherwait; +}; + +typedef struct { + char pi_verstr[512]; /* Version string */ + PCMmap pi_mmap[4]; /* maps, last is always for the kernel */ + ulong pi_conf_addr; /* Config address */ + uchar pi_conf_present; /* Config register present */ + int pi_nctab; /* In use configuration tables */ + PCMconftab pi_ctab[8]; /* Configuration tables */ + PCMconftab *pi_defctab; /* Default conftab */ + + int pi_port; /* Actual port usage */ + int pi_irq; /* Actual IRQ usage */ +} pcminfo_t; + +#define qlock(i) {/* nothing to do */;} +#define qunlock(i) {/* nothing to do */;} +typedef struct QLock { int r; } QLock; + +typedef struct { + QLock; + variant_t *cb_variant; /* Which CardBus chipset */ + Pcidev *cb_pci; /* The bridge itself */ + ulong *cb_regs; /* Cardbus registers */ + int cb_ltype; /* Legacy type */ + int cb_lindex; /* Legacy port index address */ + int cb_ldata; /* Legacy port data address */ + int cb_lbase; /* Base register for this socket */ + + int cb_state; /* Current state of card */ + int cb_type; /* Type of card */ + pcminfo_t cb_linfo; /* PCMCIA slot info */ + + int cb_refs; /* Number of refs to slot */ + QLock cb_refslock; /* inc/dev ref lock */ +} cb_t; + +static int managerstarted; + +enum { + Mshift= 12, + Mgran= (1<cb_state], messages[message]); + switch (cb->cb_state) { + case SlotEmpty: + + switch (message) { + case CardDetected: + cb->cb_state = SlotFull; + powerup(cb); + break; + case CardEjected: + break; + default: + print("#Y%d: Invalid message %s in SlotEmpty state\n", + (int)(cb - cbslots), messages[message]); + break; + } + break; + + case SlotFull: + + switch (message) { + case CardPowered: + cb->cb_state = SlotPowered; + configure(cb); + break; + case CardEjected: + cb->cb_state = SlotEmpty; + powerdown(cb); + break; + default: + //print("#Y%d: Invalid message %s in SlotFull state\n", + // (int)(cb - cbslots), messages[message]); + break; + } + break; + + case SlotPowered: + + switch (message) { + case CardConfigured: + cb->cb_state = SlotConfigured; + break; + case CardEjected: + cb->cb_state = SlotEmpty; + unconfigure(cb); + powerdown(cb); + break; + default: + print("#Y%d: Invalid message %s in SlotPowered state\n", + (int)(cb - cbslots), messages[message]); + break; + } + break; + + case SlotConfigured: + + switch (message) { + case CardEjected: + cb->cb_state = SlotEmpty; + unconfigure(cb); + powerdown(cb); + break; + default: + print("#Y%d: Invalid message %s in SlotConfigured state\n", + (int)(cb - cbslots), messages[message]); + break; + } + break; + } +} + +static void +qengine(cb_t *cb, int message) +{ + qlock(cb); + engine(cb, message); + qunlock(cb); +} + +typedef struct { + cb_t *e_cb; + int e_message; +} events_t; + +static Lock levents; +static events_t events[NUMEVENTS]; +// static Rendez revents; +static int nevents; + +//static void +//iengine(cb_t *cb, int message) +//{ +// if (nevents >= NUMEVENTS) { +// print("#Y: Too many events queued, discarding request\n"); +// return; +// } +// ilock(&levents); +// events[nevents].e_cb = cb; +// events[nevents].e_message = message; +// nevents++; +// iunlock(&levents); +// wakeup(&revents); +//} + +static int +eventoccured(void) +{ + return nevents > 0; +} + +// static void +// processevents(void *) +// { +// while (1) { +// int message; +// cb_t *cb; +// +// sleep(&revents, (int (*)(void *))eventoccured, nil); +// +// cb = nil; +// message = 0; +// ilock(&levents); +// if (nevents > 0) { +// cb = events[0].e_cb; +// message = events[0].e_message; +// nevents--; +// if (nevents > 0) +// memmove(events, &events[1], nevents * sizeof(events_t)); +// } +// iunlock(&levents); +// +// if (cb) +// qengine(cb, message); +// } +// } + +// static void +// interrupt(Ureg *, void *) +// { +// int i; +// +// for (i = 0; i != nslots; i++) { +// cb_t *cb = &cbslots[i]; +// ulong event, state; +// +// event= cb->cb_regs[SocketEvent]; +// state = cb->cb_regs[SocketState]; +// rdreg(cb, Rcsc); /* Ack the interrupt */ +// +// print("interrupt: slot %d, event %.8lX, state %.8lX, (%s)\n", +// (int)(cb - cbslots), event, state, states[cb->cb_state]); +// +// if (event & SE_CCD) { +// cb->cb_regs[SocketEvent] |= SE_CCD; /* Ack interrupt */ +// if (state & SE_CCD) { +// if (cb->cb_state != SlotEmpty) { +// print("#Y: take cardejected interrupt\n"); +// iengine(cb, CardEjected); +// } +// } +// else +// iengine(cb, CardDetected); +// } +// +// if (event & SE_POWER) { +// cb->cb_regs[SocketEvent] |= SE_POWER; /* Ack interrupt */ +// iengine(cb, CardPowered); +// } +// } +// } + +void +devpccardlink(void) +{ + static int initialized; + Pcidev *pci; + int i; +// uchar intl; + + if (initialized) + return; + initialized = 1; + + if (!getconf("pccard0")) + return; + + if (_pcmspecial) { + print("#Y: CardBus and PCMCIA at the same time?\n"); + return; + } + + _pcmspecial = pccard_pcmspecial; + _pcmspecialclose = pccard_pcmspecialclose; + + + /* Allocate legacy space */ + if (ioalloc(LegacyAddr, 2, 0, "i82365.0") < 0) + print("#Y: WARNING: Cannot allocate legacy ports\n"); + + /* Find all CardBus controllers */ + pci = nil; +// intl = (uchar)-1; + while ((pci = pcimatch(pci, 0, 0)) != nil) { + ulong baddr; + uchar pin; + cb_t *cb; + int slot; + + for (i = 0; i != nelem(variant); i++) + if (pci->vid == variant[i].r_vid && pci->did == variant[i].r_did) + break; + if (i == nelem(variant)) + continue; + + /* initialize this slot */ + slot = nslots++; + cb = &cbslots[slot]; + + cb->cb_pci = pci; + cb->cb_variant = &variant[i]; + + // Don't you love standards! + if (pci->vid == TI_vid) { + if (pci->did <= TI_1131_did) { + uchar cc; + + cc = pcicfgr8(pci, TI1131xCC); + cc &= ~(TI113X_CC_PCI_IRQ_ENA | + TI113X_CC_PCI_IREQ | + TI113X_CC_PCI_CSC | + TI113X_CC_ZVENABLE); + cc |= TI113X_CC_PCI_IRQ_ENA | + TI113X_CC_PCI_IREQ | + TI113X_CC_SPKROUTEN; + pcicfgw8(pci, TI1131xCC, cc); + + // PCI interrupts only + pcicfgw8(pci, TI1131xDC, + pcicfgr8(pci, TI1131xDC) & ~6); + + // CSC ints to PCI bus. + wrreg(cb, Rigc, rdreg(cb, Rigc) | 0x10); + } + else if (pci->did == TI_1250_did) { + print("No support yet for the TI_1250_did, prod pb\n"); + } + } + +// if (intl != -1 && intl != pci->intl) +// intrenable(pci->intl, interrupt, cb, pci->tbdf, "cardbus"); +// intl = pci->intl; + + // Set up PCI bus numbers if needed. + if (pcicfgr8(pci, PciSBN) == 0) { + static int busbase = 0x20; + + pcicfgw8(pci, PciSBN, busbase); + pcicfgw8(pci, PciUBN, busbase + 2); + busbase += 3; + } + + // Patch up intl if needed. + if ((pin = pcicfgr8(pci, PciINTP)) != 0 && + (pci->intl == 0xff || pci->intl == 0)) { + pci->intl = pciipin(nil, pin); + pcicfgw8(pci, PciINTL, pci->intl); + + if (pci->intl == 0xff || pci->intl == 0) + print("#Y%d: No interrupt?\n", (int)(cb - cbslots)); + } + + if ((baddr = pcicfgr32(cb->cb_pci, PciBAR0)) == 0) { + int align = (pci->did == Ricoh_478_did)? 0x10000: 0x1000; + + baddr = upamalloc(baddr, align, align); + pcicfgw32(cb->cb_pci, PciBAR0, baddr); + cb->cb_regs = (ulong *)KADDR(baddr); + } + else + cb->cb_regs = (ulong *)KADDR(upamalloc(baddr, 4096, 0)); + cb->cb_state = SlotEmpty; + + /* Don't really know what to do with this... */ + i82365probe(cb, LegacyAddr, LegacyAddr + 1); + + print("#Y%ld: %s, %.8ulX intl %d\n", cb - cbslots, + variant[i].r_name, baddr, pci->intl); + } + + if (nslots == 0) + return; + + for (i = 0; i != nslots; i++) { + cb_t *cb = &cbslots[i]; + + if ((cb->cb_regs[SocketState] & SE_CCD) == 0) + engine(cb, CardDetected); + } + + delay(500); /* Allow time for power up */ + + for (i = 0; i != nslots; i++) { + cb_t *cb = &cbslots[i]; + + if (cb->cb_regs[SocketState] & SE_POWER) + engine(cb, CardPowered); + + /* Enable interrupt on all events */ +// cb->cb_regs[SocketMask] |= 0xF; +// wrreg(cb, Rcscic, 0xC); + } +} + +static int +powerup(cb_t *cb) +{ + ulong state; + ushort bcr; + + if ((state = cb->cb_regs[SocketState]) & SS_PC16) { + + // print("#Y%ld: Probed a PC16 card, powering up card\n", cb - cbslots); + cb->cb_type = PC16; + memset(&cb->cb_linfo, 0, sizeof(pcminfo_t)); + + /* power up and unreset, wait's are empirical (???) */ + wrreg(cb, Rpc, Fautopower|Foutena|Fcardena); + delay(300); + wrreg(cb, Rigc, 0); + delay(100); + wrreg(cb, Rigc, Fnotreset); + + return 1; + } + + if (cb->cb_regs[SocketState] & SS_CCD) + return 0; + + if ((state & SS_CBC) == 0 || (state & SS_NOTCARD)) { + print("#Y%ld: No cardbus card inserted\n", cb - cbslots); + return 0; + } + + if (state & SS_BADVCC) { + print("#Y%ld: Bad VCC request to card, powering down card!\n", + cb - cbslots); + cb->cb_regs[SocketControl] = 0; + return 0; + } + + if ((state & SS_3V) == 0 && (state & SS_5V) == 0) { + print("#Y%ld: Unsupported voltage, powering down card!\n", + cb - cbslots); + cb->cb_regs[SocketControl] = 0; + return 0; + } + + print("#Y%ld: card %spowered at %d volt\n", cb - cbslots, + (state & SS_POWER)? "": "not ", + (state & SS_3V)? 3: (state & SS_5V)? 5: -1); + + /* Power up the card + * and make sure the secondary bus is not in reset. + */ + cb->cb_regs[SocketControl] = (state & SS_5V)? SC_5V: SC_3V; + delay(50); + bcr = pcicfgr16(cb->cb_pci, PciBCR); + bcr &= ~0x40; + pcicfgw16(cb->cb_pci, PciBCR, bcr); + delay(100); + + cb->cb_type = PC32; + + return 1; +} + +static void +powerdown(cb_t *cb) +{ + ushort bcr; + + if (cb->cb_type == PC16) { + + wrreg(cb, Rpc, 0); /* turn off card power */ + wrreg(cb, Rwe, 0); /* no windows */ + + cb->cb_type = -1; + return; + } + + bcr = pcicfgr16(cb->cb_pci, PciBCR); + bcr |= 0x40; + pcicfgw16(cb->cb_pci, PciBCR, bcr); + cb->cb_regs[SocketControl] = 0; + cb->cb_type = -1; +} + +static void +configure(cb_t *cb) +{ + int i; + Pcidev *pci; + + // print("configuring slot %d (%s)\n", (int)(cb - cbslots), states[cb->cb_state]); + if (cb->cb_state == SlotConfigured) + return; + engine(cb, CardConfigured); + + delay(50); /* Emperically established */ + + if (cb->cb_type == PC16) { + i82365configure(cb); + return; + } + + /* Scan the CardBus for new PCI devices */ + pciscan(pcicfgr8(cb->cb_pci, PciSBN), &cb->cb_pci->bridge); + pci = cb->cb_pci->bridge; + while (pci) { + ulong size, bar; + int memindex, ioindex; + + /* Treat the found device as an ordinary PCI card. It seems that the + CIS is not always present in CardBus cards. XXX, need to support + multifunction cards */ + memindex = ioindex = 0; + for (i = 0; i != Nbars; i++) { + + if (pci->mem[i].size == 0) continue; + if (pci->mem[i].bar & 1) { + + // Allocate I/O space + if (ioindex > 1) { + print("#Y%ld: WARNING: Can only configure 2 I/O slots\n", cb - cbslots); + continue; + } + bar = ioreserve(-1, pci->mem[i].size, 0, "cardbus"); + pci->mem[i].bar = bar | 1; + pcicfgw32(pci, PciBAR0 + i * sizeof(ulong), + pci->mem[i].bar); + pcicfgw16(cb->cb_pci, PciCBIBR0 + ioindex * 8, bar); + pcicfgw16(cb->cb_pci, PciCBILR0 + ioindex * 8, + bar + pci->mem[i].size - 1); + //print("ioindex[%d] %.8uX (%d)\n", + // ioindex, bar, pci->mem[i].size); + ioindex++; + continue; + } + + // Allocating memory space + if (memindex > 1) { + print("#Y%ld: WARNING: Can only configure 2 memory slots\n", cb - cbslots); + continue; + } + + bar = upamalloc(0, pci->mem[i].size, BY2PG); + pci->mem[i].bar = bar | (pci->mem[i].bar & 0x80); + pcicfgw32(pci, PciBAR0 + i * sizeof(ulong), pci->mem[i].bar); + pcicfgw32(cb->cb_pci, PciCBMBR0 + memindex * 8, bar); + pcicfgw32(cb->cb_pci, PciCBMLR0 + memindex * 8, + bar + pci->mem[i].size - 1); + + if (pci->mem[i].bar & 0x80) + /* Enable prefetch */ + pcicfgw16(cb->cb_pci, PciBCR, + pcicfgr16(cb->cb_pci, PciBCR) | + (1 << (8 + memindex))); + + //print("memindex[%d] %.8uX (%d)\n", + // memindex, bar, pci->mem[i].size); + memindex++; + } + + if ((size = pcibarsize(pci, PciEBAR0)) > 0) { + + if (memindex > 1) + print("#Y%ld: WARNING: Too many memory spaces, not mapping ROM space\n", + cb - cbslots); + else { + pci->rom.bar = upamalloc(0, size, BY2PG); + pci->rom.size = size; + + pcicfgw32(pci, PciEBAR0, pci->rom.bar); + pcicfgw32(cb->cb_pci, PciCBMBR0 + memindex * 8, + pci->rom.bar); + pcicfgw32(cb->cb_pci, PciCBMLR0 + memindex * 8, + pci->rom.bar + pci->rom.size - 1); + } + } + + /* Set the basic PCI registers for the device */ + pcicfgw16(pci, PciPCR, + pcicfgr16(pci, PciPCR) | + PciPCR_IO|PciPCR_MEM|PciPCR_Master); + pcicfgw8(pci, PciCLS, 8); + pcicfgw8(pci, PciLTR, 64); + + if (pcicfgr8(pci, PciINTP)) { + pci->intl = pcicfgr8(cb->cb_pci, PciINTL); + pcicfgw8(pci, PciINTL, pci->intl); + + /* Route interrupts to INTA#/B# */ + pcicfgw16(cb->cb_pci, PciBCR, + pcicfgr16(cb->cb_pci, PciBCR) & ~(1 << 7)); + } + + pci = pci->list; + } +} + +static void +unconfigure(cb_t *cb) +{ + Pcidev *pci; + int i, ioindex, memindex; + + if (cb->cb_type == PC16) { + print("#Y%d: Don't know how to unconfigure a PC16 card\n", + (int)(cb - cbslots)); + + memset(&cb->cb_linfo, 0, sizeof(pcminfo_t)); + return; + } + + pci = cb->cb_pci->bridge; + if (pci == nil) + return; /* Not configured */ + cb->cb_pci->bridge = nil; + + memindex = ioindex = 0; + while (pci) { + Pcidev *_pci; + + for (i = 0; i != Nbars; i++) { + if (pci->mem[i].size == 0) continue; + if (pci->mem[i].bar & 1) { + iofree(pci->mem[i].bar & ~1); + pcicfgw16(cb->cb_pci, PciCBIBR0 + ioindex * 8, + (ushort)-1); + pcicfgw16(cb->cb_pci, PciCBILR0 + ioindex * 8, 0); + ioindex++; + continue; + } + + upafree(pci->mem[i].bar & ~0xF, pci->mem[i].size); + pcicfgw32(cb->cb_pci, PciCBMBR0 + memindex * 8, + (ulong)-1); + pcicfgw32(cb->cb_pci, PciCBMLR0 + memindex * 8, 0); + pcicfgw16(cb->cb_pci, PciBCR, + pcicfgr16(cb->cb_pci, PciBCR) & + ~(1 << (8 + memindex))); + memindex++; + } + + if (pci->rom.bar && memindex < 2) { + upafree(pci->rom.bar & ~0xF, pci->rom.size); + pcicfgw32(cb->cb_pci, PciCBMBR0 + memindex * 8, + (ulong)-1); + pcicfgw32(cb->cb_pci, PciCBMLR0 + memindex * 8, 0); + memindex++; + } + + _pci = pci->list; + free(_pci); + pci = _pci; + } +} + +static void +i82365configure(cb_t *cb) +{ + int this; + Cisdat cis; + PCMmap *m; + uchar type, link; + + /* + * Read all tuples in attribute space. + */ + m = isamap(cb, 0, 0, 1); + if(m == 0) + return; + + cis.cisbase = KADDR(m->isa); + cis.cispos = 0; + cis.cisskip = 2; + cis.cislen = m->len; + + /* loop through all the tuples */ + for(;;){ + this = cis.cispos; + if(readc(&cis, &type) != 1) + break; + if(type == 0xFF) + break; + if(readc(&cis, &link) != 1) + break; + + switch(type){ + default: + break; + case 0x15: + tvers1(cb, &cis, type); + break; + case 0x1A: + tcfig(cb, &cis, type); + break; + case 0x1B: + tentry(cb, &cis, type); + break; + } + + if(link == 0xFF) + break; + cis.cispos = this + (2+link); + } + isaunmap(m); +} + +/* + * look for a card whose version contains 'idstr' + */ +static int +pccard_pcmspecial(char *idstr, ISAConf *isa) +{ + int i, irq; + PCMconftab *ct, *et; + pcminfo_t *pi; + cb_t *cb; + uchar x, we, *p; + + cb = nil; + for (i = 0; i != nslots; i++) { + cb = &cbslots[i]; + + qlock(cb); + if (cb->cb_state == SlotConfigured && + cb->cb_type == PC16 && + strstr(cb->cb_linfo.pi_verstr, idstr)) + break; + qunlock(cb); + } + + if (i == nslots) { + // print("#Y: %s not found\n", idstr); + return -1; + } + + pi = &cb->cb_linfo; + + /* + * configure the PCMslot for IO. We assume very heavily that we can read + * configuration info from the CIS. If not, we won't set up correctly. + */ + irq = isa->irq; + if(irq == 2) + irq = 9; + + et = &pi->pi_ctab[pi->pi_nctab]; + ct = nil; + for(i = 0; i < isa->nopt; i++){ + int index; + char *cp; + + if(strncmp(isa->opt[i], "index=", 6)) + continue; + index = strtol(&isa->opt[i][6], &cp, 0); + if(cp == &isa->opt[i][6] || index >= pi->pi_nctab) { + qunlock(cb); + print("#Y%d: Cannot find index %d in conf table\n", + (int)(cb - cbslots), index); + return -1; + } + ct = &pi->pi_ctab[index]; + } + + if(ct == nil){ + PCMconftab *t; + + /* assume default is right */ + if(pi->pi_defctab) + ct = pi->pi_defctab; + else + ct = pi->pi_ctab; + + /* try for best match */ + if(ct->nio == 0 + || ct->io[0].start != isa->port || ((1<irqs) == 0){ + for(t = pi->pi_ctab; t < et; t++) + if(t->nio + && t->io[0].start == isa->port + && ((1<irqs)){ + ct = t; + break; + } + } + if(ct->nio == 0 || ((1<irqs) == 0){ + for(t = pi->pi_ctab; t < et; t++) + if(t->nio && ((1<irqs)){ + ct = t; + break; + } + } + if(ct->nio == 0){ + for(t = pi->pi_ctab; t < et; t++) + if(t->nio){ + ct = t; + break; + } + } + } + + if(ct == et || ct->nio == 0) { + qunlock(cb); + print("#Y%d: No configuration?\n", (int)(cb - cbslots)); + return -1; + } + if(isa->port == 0 && ct->io[0].start == 0) { + qunlock(cb); + print("#Y%d: No part or start address\n", (int)(cb - cbslots)); + return -1; + } + + /* route interrupts */ + isa->irq = irq; + wrreg(cb, Rigc, irq | Fnotreset | Fiocard); + + /* set power and enable device */ + x = vcode(ct->vpp1); + wrreg(cb, Rpc, x|Fautopower|Foutena|Fcardena); + + /* 16-bit data path */ + if(ct->bit16) + x = Ftiming|Fiocs16|Fwidth16; + else + x = Ftiming; + if(ct->nio == 2 && ct->io[1].start) + x |= x<<4; + wrreg(cb, Rio, x); + + /* + * enable io port map 0 + * the 'top' register value includes the last valid address + */ + if(isa->port == 0) + isa->port = ct->io[0].start; + we = rdreg(cb, Rwe); + wrreg(cb, Riobtm0lo, isa->port); + wrreg(cb, Riobtm0hi, isa->port>>8); + i = isa->port+ct->io[0].len-1; + wrreg(cb, Riotop0lo, i); + wrreg(cb, Riotop0hi, i>>8); + we |= 1<<6; + if(ct->nio == 2 && ct->io[1].start){ + wrreg(cb, Riobtm1lo, ct->io[1].start); + wrreg(cb, Riobtm1hi, ct->io[1].start>>8); + i = ct->io[1].start+ct->io[1].len-1; + wrreg(cb, Riotop1lo, i); + wrreg(cb, Riotop1hi, i>>8); + we |= 1<<7; + } + wrreg(cb, Rwe, we); + + /* only touch Rconfig if it is present */ + if(pi->pi_conf_present & (1<pi_conf_addr + Rconfig, 1, 1); + p = KADDR(m->isa + pi->pi_conf_addr + Rconfig - m->ca); + + /* set configuration and interrupt type */ + x = ct->index; + if((ct->irqtype & 0x20) && ((ct->irqtype & 0x40)==0 || isa->irq>7)) + x |= Clevel; + *p = x; + delay(5); + + isaunmap(m); + } + + pi->pi_port = isa->port; + pi->pi_irq = isa->irq; + qunlock(cb); + + print("#Y%d: %s irq %ld, port %lX\n", (int)(cb - cbslots), pi->pi_verstr, isa->irq, isa->port); + return (int)(cb - cbslots); +} + +static void +pccard_pcmspecialclose(int slotno) +{ + cb_t *cb = &cbslots[slotno]; + + wrreg(cb, Rwe, 0); /* no windows */ +} + +static int +xcistuple(int slotno, int tuple, int subtuple, void *v, int nv, int attr) +{ + PCMmap *m; + Cisdat cis; + int i, l; + uchar *p; + uchar type, link, n, c; + int this, subtype; + cb_t *cb = &cbslots[slotno]; + + m = isamap(cb, 0, 0, attr); + if(m == 0) + return -1; + + cis.cisbase = KADDR(m->isa); + cis.cispos = 0; + cis.cisskip = attr ? 2 : 1; + cis.cislen = m->len; + + /* loop through all the tuples */ + for(i = 0; i < 1000; i++){ + this = cis.cispos; + if(readc(&cis, &type) != 1) + break; + if(type == 0xFF) + break; + if(readc(&cis, &link) != 1) + break; + if(link == 0xFF) + break; + + n = link; + if (link > 1 && subtuple != -1) { + if (readc(&cis, &c) != 1) + break; + subtype = c; + n--; + } else + subtype = -1; + + if(type == tuple && subtype == subtuple) { + p = v; + for(l=0; lqid.path>>8)&0xff)) +//#define TYPE(c) ((ulong)(c->qid.path&0xff)) +//#define QID(s,t) (((s)<<8)|(t)) +// +//static int +//pccardgen(Chan *c, char*, Dirtab *, int , int i, Dir *dp) +//{ +// int slotno; +// Qid qid; +// long len; +// int entry; +// +// if(i == DEVDOTDOT){ +// mkqid(&qid, Qdir, 0, QTDIR); +// devdir(c, qid, "#Y", 0, eve, 0555, dp); +// return 1; +// } +// +// len = 0; +// if(i >= Nents * nslots) return -1; +// slotno = i / Nents; +// entry = i % Nents; +// if (entry == 0) { +// qid.path = QID(slotno, Qctl); +// snprint(up->genbuf, sizeof up->genbuf, "cb%dctl", slotno); +// } +// else { +// /* Entries for memory regions. I'll implement them when +// needed. (pb) */ +// } +// qid.vers = 0; +// qid.type = QTFILE; +// devdir(c, qid, up->genbuf, len, eve, 0660, dp); +// return 1; +//} +// +//static Walkqid* +//pccardwalk(Chan *c, Chan *nc, char **name, int nname) +//{ +// return devwalk(c, nc, name, nname, 0, 0, pccardgen); +//} +// +//static int +//pccardstat(Chan *c, uchar *db, int n) +//{ +// return devstat(c, db, n, 0, 0, pccardgen); +//} +// +//static void +//increfp(cb_t *cb) +//{ +// qlock(&cb->cb_refslock); +// cb->cb_refs++; +// qunlock(&cb->cb_refslock); +//} +// +//static void +//decrefp(cb_t *cb) +//{ +// qlock(&cb->cb_refslock); +// cb->cb_refs--; +// qunlock(&cb->cb_refslock); +//} +// +//static Chan* +//pccardopen(Chan *c, int omode) +//{ +// if (c->qid.type & QTDIR){ +// if(omode != OREAD) +// error(Eperm); +// } else +// increfp(&cbslots[SLOTNO(c)]); +// c->mode = openmode(omode); +// c->flag |= COPEN; +// c->offset = 0; +// return c; +//} +// +//static void +//pccardclose(Chan *c) +//{ +// if(c->flag & COPEN) +// if((c->qid.type & QTDIR) == 0) +// decrefp(&cbslots[SLOTNO(c)]); +//} +// +//static long +//pccardread(Chan *c, void *a, long n, vlong offset) +//{ +// cb_t *cb; +// char *buf, *p, *e; +// +// switch(TYPE(c)){ +// case Qdir: +// return devdirread(c, a, n, 0, 0, pccardgen); +// +// case Qctl: +// buf = p = malloc(READSTR); +// buf[0] = 0; +// e = p + READSTR; +// +// cb = &cbslots[SLOTNO(c)]; +// qlock(cb); +// p = seprint(p, e, "slot %ld: %s; ", cb - cbslots, states[cb->cb_state]); +// +// switch (cb->cb_type) { +// case -1: +// seprint(p, e, "\n"); +// break; +// +// case PC32: +// if (cb->cb_pci->bridge) { +// Pcidev *pci = cb->cb_pci->bridge; +// int i; +// +// while (pci) { +// p = seprint(p, e, "%.4uX %.4uX; irq %d\n", +// pci->vid, pci->did, pci->intl); +// for (i = 0; i != Nbars; i++) +// if (pci->mem[i].size) +// p = seprint(p, e, +// "\tmem[%d] %.8uX (%.8uX)\n", +// i, pci->mem[i].bar, +// pci->mem[i].size); +// if (pci->rom.size) +// p = seprint(p, e, "\tROM %.8uX (%.8uX)\n", i, +// pci->rom.bar, pci->rom.size); +// pci = pci->list; +// } +// } +// break; +// +// case PC16: +// if (cb->cb_state == SlotConfigured) { +// pcminfo_t *pi = &cb->cb_linfo; +// +// p = seprint(p, e, "%s port %X; irq %d;\n", +// pi->pi_verstr, pi->pi_port, +// pi->pi_irq); +// for (n = 0; n != pi->pi_nctab; n++) { +// PCMconftab *ct; +// int i; +// +// ct = &pi->pi_ctab[n]; +// p = seprint(p, e, +// "\tconfiguration[%d] irqs %.4X; vpp %d, %d; %s\n", +// n, ct->irqs, ct->vpp1, ct->vpp2, +// (ct == pi->pi_defctab)? "(default);": ""); +// for (i = 0; i != ct->nio; i++) +// if (ct->io[i].len > 0) +// p = seprint(p, e, "\t\tio[%d] %.8lX %d\n", +// i, ct->io[i].start, ct->io[i].len); +// } +// } +// break; +// } +// qunlock(cb); +// +// n = readstr(offset, a, n, buf); +// free(buf); +// return n; +// } +// return 0; +//} +// +//static long +//pccardwrite(Chan *c, void *v, long n, vlong) +//{ +// Rune r; +// ulong n0; +// int i, nf; +// char buf[255], *field[Ncmd], *device; +// cb_t *cb; +// +// n0 = n; +// switch(TYPE(c)){ +// case Qctl: +// cb = &cbslots[SLOTNO(c)]; +// if(n > sizeof(buf)-1) n = sizeof(buf)-1; +// memmove(buf, v, n); +// buf[n] = '\0'; +// +// nf = getfields(buf, field, Ncmd, 1, " \t\n"); +// for (i = 0; i != nf; i++) { +// if (!strcmp(field[i], "down")) { +// +// if (i + 1 < nf && *field[i + 1] == '#') { +// device = field[++i]; +// device += chartorune(&r, device); +// if ((n = devno(r, 1)) >= 0 && devtab[n]->config) +// devtab[n]->config(0, device, nil); +// } +// qengine(cb, CardEjected); +// } +// else if (!strcmp(field[i], "power")) { +// if ((cb->cb_regs[SocketState] & SS_CCD) == 0) +// qengine(cb, CardDetected); +// } +// else +// error(Ebadarg); +// } +// break; +// } +// return n0 - n; +//} +// +//Dev pccarddevtab = { +// 'Y', +// "cardbus", +// +// devreset, +// devinit, +// pccardattach, +// pccardwalk, +// pccardstat, +// pccardopen, +// devcreate, +// pccardclose, +// pccardread, +// devbread, +// pccardwrite, +// devbwrite, +// devremove, +// devwstat, +//}; + +static PCMmap * +isamap(cb_t *cb, ulong offset, int len, int attr) +{ + uchar we, bit; + PCMmap *m, *nm; + pcminfo_t *pi; + int i; + ulong e; + + pi = &cb->cb_linfo; + + /* convert offset to granularity */ + if(len <= 0) + len = 1; + e = ROUND(offset+len, Mgran); + offset &= Mmask; + len = e - offset; + + /* look for a map that covers the right area */ + we = rdreg(cb, Rwe); + bit = 1; + nm = 0; + for(m = pi->pi_mmap; m < &pi->pi_mmap[nelem(pi->pi_mmap)]; m++){ + if((we & bit)) + if(m->attr == attr) + if(offset >= m->ca && e <= m->cea){ + + m->ref++; + return m; + } + bit <<= 1; + if(nm == 0 && m->ref == 0) + nm = m; + } + m = nm; + if(m == 0) + return 0; + + /* if isa space isn't big enough, free it and get more */ + if(m->len < len){ + if(m->isa){ + umbfree(m->isa, m->len); + m->len = 0; + } + m->isa = PADDR(umbmalloc(0, len, Mgran)); + if(m->isa == 0){ + print("isamap: out of isa space\n"); + return 0; + } + m->len = len; + } + + /* set up new map */ + m->ca = offset; + m->cea = m->ca + m->len; + m->attr = attr; + i = m - pi->pi_mmap; + bit = 1<isa>>Mshift); + wrreg(cb, MAP(i, Mbtmhi), (m->isa>>(Mshift+8)) | F16bit); + wrreg(cb, MAP(i, Mtoplo), (m->isa+m->len-1)>>Mshift); + wrreg(cb, MAP(i, Mtophi), ((m->isa+m->len-1)>>(Mshift+8))); + offset -= m->isa; + offset &= (1<<25)-1; + offset >>= Mshift; + wrreg(cb, MAP(i, Mofflo), offset); + wrreg(cb, MAP(i, Moffhi), (offset>>8) | (attr ? Fregactive : 0)); + wrreg(cb, Rwe, we | bit); /* enable map */ + m->ref = 1; + + return m; +} + +static void +isaunmap(PCMmap* m) +{ + m->ref--; +} + +/* + * reading and writing card registers + */ +static uchar +rdreg(cb_t *cb, int index) +{ + outb(cb->cb_lindex, cb->cb_lbase + index); + return inb(cb->cb_ldata); +} + +static void +wrreg(cb_t *cb, int index, uchar val) +{ + outb(cb->cb_lindex, cb->cb_lbase + index); + outb(cb->cb_ldata, val); +} + +static int +readc(Cisdat *cis, uchar *x) +{ + if(cis->cispos >= cis->cislen) + return 0; + *x = cis->cisbase[cis->cisskip*cis->cispos]; + cis->cispos++; + return 1; +} + +static ulong +getlong(Cisdat *cis, int size) +{ + uchar c; + int i; + ulong x; + + x = 0; + for(i = 0; i < size; i++){ + if(readc(cis, &c) != 1) + break; + x |= c<<(i*8); + } + return x; +} + +static void +tcfig(cb_t *cb, Cisdat *cis, int ) +{ + uchar size, rasize, rmsize; + uchar last; + pcminfo_t *pi; + + if(readc(cis, &size) != 1) + return; + rasize = (size&0x3) + 1; + rmsize = ((size>>2)&0xf) + 1; + if(readc(cis, &last) != 1) + return; + + pi = &cb->cb_linfo; + pi->pi_conf_addr = getlong(cis, rasize); + pi->pi_conf_present = getlong(cis, rmsize); +} + +static void +tvers1(cb_t *cb, Cisdat *cis, int ) +{ + uchar c, major, minor, last; + int i; + pcminfo_t *pi; + + pi = &cb->cb_linfo; + if(readc(cis, &major) != 1) + return; + if(readc(cis, &minor) != 1) + return; + last = 0; + for(i = 0; i < sizeof(pi->pi_verstr) - 1; i++){ + if(readc(cis, &c) != 1) + return; + if(c == 0) + c = ';'; + if(c == '\n') + c = ';'; + if(c == 0xff) + break; + if(c == ';' && last == ';') + continue; + pi->pi_verstr[i] = c; + last = c; + } + pi->pi_verstr[i] = 0; +} + +static ulong +microvolt(Cisdat *cis) +{ + uchar c; + ulong microvolts; + ulong exp; + + if(readc(cis, &c) != 1) + return 0; + exp = exponent[c&0x7]; + microvolts = vmant[(c>>3)&0xf]*exp; + while(c & 0x80){ + if(readc(cis, &c) != 1) + return 0; + switch(c){ + case 0x7d: + break; /* high impedence when sleeping */ + case 0x7e: + case 0x7f: + microvolts = 0; /* no connection */ + break; + default: + exp /= 10; + microvolts += exp*(c&0x7f); + } + } + return microvolts; +} + +static ulong +nanoamps(Cisdat *cis) +{ + uchar c; + ulong nanoamps; + + if(readc(cis, &c) != 1) + return 0; + nanoamps = exponent[c&0x7]*vmant[(c>>3)&0xf]; + while(c & 0x80){ + if(readc(cis, &c) != 1) + return 0; + if(c == 0x7d || c == 0x7e || c == 0x7f) + nanoamps = 0; + } + return nanoamps; +} + +/* + * only nominal voltage (feature 1) is important for config, + * other features must read card to stay in sync. + */ +static ulong +power(Cisdat *cis) +{ + uchar feature; + ulong mv; + + mv = 0; + if(readc(cis, &feature) != 1) + return 0; + if(feature & 1) + mv = microvolt(cis); + if(feature & 2) + microvolt(cis); + if(feature & 4) + microvolt(cis); + if(feature & 8) + nanoamps(cis); + if(feature & 0x10) + nanoamps(cis); + if(feature & 0x20) + nanoamps(cis); + if(feature & 0x40) + nanoamps(cis); + return mv/1000000; +} + +static ulong +ttiming(Cisdat *cis, int scale) +{ + uchar unscaled; + ulong nanosecs; + + if(readc(cis, &unscaled) != 1) + return 0; + nanosecs = (mantissa[(unscaled>>3)&0xf]*exponent[unscaled&7])/10; + nanosecs = nanosecs * exponent[scale]; + return nanosecs; +} + +static void +timing(Cisdat *cis, PCMconftab *ct) +{ + uchar c, i; + + if(readc(cis, &c) != 1) + return; + i = c&0x3; + if(i != 3) + ct->maxwait = ttiming(cis, i); /* max wait */ + i = (c>>2)&0x7; + if(i != 7) + ct->readywait = ttiming(cis, i); /* max ready/busy wait */ + i = (c>>5)&0x7; + if(i != 7) + ct->otherwait = ttiming(cis, i); /* reserved wait */ +} + +static void +iospaces(Cisdat *cis, PCMconftab *ct) +{ + uchar c; + int i, nio; + + ct->nio = 0; + if(readc(cis, &c) != 1) + return; + + ct->bit16 = ((c>>5)&3) >= 2; + if(!(c & 0x80)){ + ct->io[0].start = 0; + ct->io[0].len = 1<<(c&0x1f); + ct->nio = 1; + return; + } + + if(readc(cis, &c) != 1) + return; + + /* + * For each of the range descriptions read the + * start address and the length (value is length-1). + */ + nio = (c&0xf)+1; + for(i = 0; i < nio; i++){ + ct->io[i].start = getlong(cis, (c>>4)&0x3); + ct->io[i].len = getlong(cis, (c>>6)&0x3)+1; + } + ct->nio = nio; +} + +static void +irq(Cisdat *cis, PCMconftab *ct) +{ + uchar c; + + if(readc(cis, &c) != 1) + return; + ct->irqtype = c & 0xe0; + if(c & 0x10) + ct->irqs = getlong(cis, 2); + else + ct->irqs = 1<<(c&0xf); + ct->irqs &= 0xDEB8; /* levels available to card */ +} + +static void +memspace(Cisdat *cis, int asize, int lsize, int host) +{ + ulong haddress, address, len; + + len = getlong(cis, lsize)*256; + address = getlong(cis, asize)*256; + USED(len, address); + if(host){ + haddress = getlong(cis, asize)*256; + USED(haddress); + } +} + +static void +tentry(cb_t *cb, Cisdat *cis, int ) +{ + uchar c, i, feature; + PCMconftab *ct; + pcminfo_t *pi; + + pi = &cb->cb_linfo; + if(pi->pi_nctab >= nelem(pi->pi_ctab)) + return; + if(readc(cis, &c) != 1) + return; + ct = &pi->pi_ctab[pi->pi_nctab++]; + + /* copy from last default config */ + if(pi->pi_defctab) + *ct = *pi->pi_defctab; + + ct->index = c & 0x3f; + + /* is this the new default? */ + if(c & 0x40) + pi->pi_defctab = ct; + + /* memory wait specified? */ + if(c & 0x80){ + if(readc(cis, &i) != 1) + return; + if(i&0x80) + ct->memwait = 1; + } + + if(readc(cis, &feature) != 1) + return; + switch(feature&0x3){ + case 1: + ct->vpp1 = ct->vpp2 = power(cis); + break; + case 2: + power(cis); + ct->vpp1 = ct->vpp2 = power(cis); + break; + case 3: + power(cis); + ct->vpp1 = power(cis); + ct->vpp2 = power(cis); + break; + default: + break; + } + if(feature&0x4) + timing(cis, ct); + if(feature&0x8) + iospaces(cis, ct); + if(feature&0x10) + irq(cis, ct); + switch((feature>>5)&0x3){ + case 1: + memspace(cis, 0, 2, 0); + break; + case 2: + memspace(cis, 2, 2, 0); + break; + case 3: + if(readc(cis, &c) != 1) + return; + for(i = 0; i <= (c&0x7); i++) + memspace(cis, (c>>5)&0x3, (c>>3)&0x3, c&0x80); + break; + } +} + +static void +i82365probe(cb_t *cb, int lindex, int ldata) +{ + uchar c, id; + int dev = 0; /* According to the Ricoh spec 00->3F _and_ 80->BF seem + to be the same socket A (ditto for B). */ + + outb(lindex, Rid + (dev<<7)); + id = inb(ldata); + if((id & 0xf0) != 0x80) + return; /* not a memory & I/O card */ + if((id & 0x0f) == 0x00) + return; /* no revision number, not possible */ + + cb->cb_lindex = lindex; + cb->cb_ldata = ldata; + cb->cb_ltype = Ti82365; + cb->cb_lbase = (int)(cb - cbslots) * 0x40; + + switch(id){ + case 0x82: + case 0x83: + case 0x84: + /* could be a cirrus */ + outb(cb->cb_lindex, Rchipinfo + (dev<<7)); + outb(cb->cb_ldata, 0); + c = inb(cb->cb_ldata); + if((c & 0xc0) != 0xc0) + break; + c = inb(cb->cb_ldata); + if((c & 0xc0) != 0x00) + break; + if(c & 0x20){ + cb->cb_ltype = Tpd6720; + } else { + cb->cb_ltype = Tpd6710; + } + break; + } + + /* if it's not a Cirrus, it could be a Vadem... */ + if(cb->cb_ltype == Ti82365){ + /* unlock the Vadem extended regs */ + outb(cb->cb_lindex, 0x0E + (dev<<7)); + outb(cb->cb_lindex, 0x37 + (dev<<7)); + + /* make the id register show the Vadem id */ + outb(cb->cb_lindex, 0x3A + (dev<<7)); + c = inb(cb->cb_ldata); + outb(cb->cb_ldata, c|0xC0); + outb(cb->cb_lindex, Rid + (dev<<7)); + c = inb(cb->cb_ldata); + if(c & 0x08) + cb->cb_ltype = Tvg46x; + + /* go back to Intel compatible id */ + outb(cb->cb_lindex, 0x3A + (dev<<7)); + c = inb(cb->cb_ldata); + outb(cb->cb_ldata, c & ~0xC0); + } +} + +static int +vcode(int volt) +{ + switch(volt){ + case 5: + return 1; + case 12: + return 2; + default: + return 0; + } +} + diff --git a/os/boot/pc/devsd.c b/os/boot/pc/devsd.c new file mode 100644 index 00000000..54bfd835 --- /dev/null +++ b/os/boot/pc/devsd.c @@ -0,0 +1,617 @@ +/* + * Storage Device. + */ +#include "u.h" +#include "mem.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "error.h" + +#include "sd.h" +#include "fs.h" + +#define parttrace 0 + + +extern SDifc* sdifc[]; + +static SDev* sdlist; +static SDunit** sdunit; +static int sdnunit; +static int _sdmask; +static int cdmask; +static int sdmask; + +enum { + Rawcmd, + Rawdata, + Rawstatus, +}; + +void +sdaddpart(SDunit* unit, char* name, ulong start, ulong end) +{ + SDpart *pp; + int i, partno; + + if(parttrace) + print("add %d %s %s %ld %ld\n", unit->npart, unit->name, name, start, end); + /* + * Check name not already used + * and look for a free slot. + */ + if(unit->part != nil){ + partno = -1; + for(i = 0; i < SDnpart; i++){ + pp = &unit->part[i]; + if(!pp->valid){ + if(partno == -1) + partno = i; + break; + } + if(strcmp(name, pp->name) == 0){ + if(pp->start == start && pp->end == end){ + if(parttrace) + print("already present\n"); + return; + } + } + } + }else{ + if((unit->part = malloc(sizeof(SDpart)*SDnpart)) == nil){ + if(parttrace) + print("malloc failed\n"); + return; + } + partno = 0; + } + + /* + * Check there is a free slot and size and extent are valid. + */ + if(partno == -1 || start > end || end > unit->sectors){ + print("cannot add %s!%s [%lud,%lud) to disk [0,%lud): %s\n", + unit->name, name, start, end, unit->sectors, + partno==-1 ? "no free partitions" : "partition boundaries out of range"); + return; + } + pp = &unit->part[partno]; + pp->start = start; + pp->end = end; + strncpy(pp->name, name, NAMELEN); + pp->valid = 1; + unit->npart++; +} + +void +sddelpart(SDunit* unit, char* name) +{ + int i; + SDpart *pp; + + if(parttrace) + print("del %d %s %s\n", unit->npart, unit->name, name); + /* + * Look for the partition to delete. + * Can't delete if someone still has it open. + * If it's the last valid partition zap the + * whole table. + */ + pp = unit->part; + for(i = 0; i < SDnpart; i++){ + if(strncmp(name, pp->name, NAMELEN) == 0) + break; + pp++; + } + if(i >= SDnpart) + return; + pp->valid = 0; + + unit->npart--; + if(unit->npart == 0){ + free(unit->part); + unit->part = nil; + } +} + +static int +sdinitpart(SDunit* unit) +{ + unit->sectors = unit->secsize = 0; + unit->npart = 0; + if(unit->part){ + free(unit->part); + unit->part = nil; + } + + if(unit->inquiry[0] & 0xC0) + return 0; + switch(unit->inquiry[0] & 0x1F){ + case 0x00: /* DA */ + case 0x04: /* WORM */ + case 0x05: /* CD-ROM */ + case 0x07: /* MO */ + break; + default: + return 0; + } + + if(unit->dev->ifc->online == nil || unit->dev->ifc->online(unit) == 0) + return 0; + sdaddpart(unit, "data", 0, unit->sectors); + return 1; +} + +static SDunit* +sdgetunit(SDev* sdev, int subno) +{ + int index; + SDunit *unit; + + /* + * Associate a unit with a given device and sub-unit + * number on that device. + * The device will be probed if it has not already been + * successfully accessed. + */ + qlock(&sdqlock); + index = sdev->index+subno; + unit = sdunit[index]; + if(unit == nil){ + if((unit = malloc(sizeof(SDunit))) == nil){ + qunlock(&sdqlock); + return nil; + } + + if(sdev->enabled == 0 && sdev->ifc->enable) + sdev->ifc->enable(sdev); + sdev->enabled = 1; + + snprint(unit->name, NAMELEN, "sd%c%d", sdev->idno, subno); + unit->subno = subno; + unit->dev = sdev; + + /* + * No need to lock anything here as this is only + * called before the unit is made available in the + * sdunit[] array. + */ + if(unit->dev->ifc->verify(unit) == 0){ + qunlock(&sdqlock); + free(unit); + return nil; + } + sdunit[index] = unit; + } + qunlock(&sdqlock); + + return unit; +} + +static SDunit* +sdindex2unit(int index) +{ + SDev *sdev; + + /* + * Associate a unit with a given index into the top-level + * device directory. + * The device will be probed if it has not already been + * successfully accessed. + */ + for(sdev = sdlist; sdev != nil; sdev = sdev->next){ + if(index >= sdev->index && index < sdev->index+sdev->nunit) + return sdgetunit(sdev, index-sdev->index); + } + + return nil; +} + +static void +_sddetach(void) +{ + SDev *sdev; + + for(sdev = sdlist; sdev != nil; sdev = sdev->next){ + if(sdev->enabled == 0) + continue; + if(sdev->ifc->disable) + sdev->ifc->disable(sdev); + sdev->enabled = 0; + } +} + +static int +_sdinit(void) +{ + ulong m; + int i; + SDev *sdev, *tail; + SDunit *unit; + + /* + * Probe all configured controllers and make a list + * of devices found, accumulating a possible maximum number + * of units attached and marking each device with an index + * into the linear top-level directory array of units. + */ + tail = nil; + for(i = 0; sdifc[i] != nil; i++){ + if((sdev = sdifc[i]->pnp()) == nil) + continue; + if(sdlist != nil) + tail->next = sdev; + else + sdlist = sdev; + for(tail = sdev; tail->next != nil; tail = tail->next){ + sdev->index = sdnunit; + sdnunit += tail->nunit; + } + tail->index = sdnunit; + sdnunit += tail->nunit; + } + + /* + * Legacy and option code goes here. This will be hard... + */ + + /* + * The maximum number of possible units is known, allocate + * placeholders for their datastructures; the units will be + * probed and structures allocated when attached. + * Allocate controller names for the different types. + */ + if(sdnunit == 0) + return 0; + if((sdunit = malloc(sdnunit*sizeof(SDunit*))) == nil) + return 0; + sddetach = _sddetach; + + for(i = 0; sdifc[i] != nil; i++){ + if(sdifc[i]->id) + sdifc[i]->id(sdlist); + } + + m = 0; + cdmask = sdmask = 0; + for(i=0; inpart > 0){ /* BUG */ + if((unit->inquiry[0] & 0x1F) == 0x05) + cdmask |= (1<name); +} + +void +sdprintdevs(int i) +{ + char *s; + SDunit *unit; + + unit = sdindex2unit(i); + for(i=0; inpart; i++){ + s = unit->part[i].name; + if(strncmp(s, "dos", 3) == 0 + || strncmp(s, "9fat", 4) == 0 + || strncmp(s, "fs", 2) == 0) + print(" %s!%s", unit->name, s); + } +} + +SDpart* +sdfindpart(SDunit *unit, char *name) +{ + int i; + + if(parttrace) + print("findpart %d %s %s\t\n", unit->npart, unit->name, name); + for(i=0; inpart; i++) { + if(parttrace) + print("%s...", unit->part[i].name); + if(strcmp(unit->part[i].name, name) == 0){ + if(parttrace) + print("\n"); + return &unit->part[i]; + } + } + if(parttrace) + print("not found\n"); + return nil; +} + +typedef struct Scsicrud Scsicrud; +struct Scsicrud { + Fs fs; + vlong offset; + SDunit *unit; + SDpart *part; +}; + +long +sdread(Fs *vcrud, void *v, long n) +{ + Scsicrud *crud; + long x; + + crud = (Scsicrud*)vcrud; + x = sdbio(crud->unit, crud->part, v, n, crud->offset); + if(x > 0) + crud->offset += x; + return x; +} + +vlong +sdseek(Fs *vcrud, vlong seek) +{ + ((Scsicrud*)vcrud)->offset = seek; + return seek; +} + +void* +sdgetfspart(int i, char *s, int chatty) +{ + SDunit *unit; + SDpart *p; + Scsicrud *crud; + + if(cdmask&(1<name, s); + return nil; + } + if(p->crud == nil) { + crud = malloc(sizeof(Scsicrud)); + crud->fs.dev = i; + crud->fs.diskread = sdread; + crud->fs.diskseek = sdseek; + // crud->start = 0; + crud->unit = unit; + crud->part = p; + if(dosinit(&crud->fs) < 0 && dosinit(&crud->fs) < 0 && kfsinit(&crud->fs) < 0){ + if(chatty) + print("partition %s!%s does not contain a DOS or KFS file system\n", + unit->name, s); + return nil; + } + p->crud = crud; + } + return p->crud; +} + +/* + * Leave partitions around for devsd to pick up. + * (Needed by boot process; more extensive + * partitioning is done by termrc or cpurc). + */ +void +sdaddconf(int i) +{ + SDunit *unit; + SDpart *pp; + + unit = sdindex2unit(i); + + /* + * If there were no partitions (just data and partition), don't bother. + */ + if(unit->npart<= 1 || (unit->npart==2 && strcmp(unit->part[1].name, "partition")==0)) + return; + + addconf("%spart=", unit->name); + for(i=1, pp=&unit->part[i]; inpart; i++, pp++) /* skip 0, which is "data" */ + addconf("%s%s %ld %ld", i==1 ? "" : "/", pp->name, + pp->start, pp->end); + addconf("\n"); +} + +int +sdboot(int dev, char *pname, Boot *b) +{ + char *file; + Fs *fs; + + if((file = strchr(pname, '!')) == nil) { + print("syntax is sdC0!partition!file\n"); + return -1; + } + *file++ = '\0'; + + fs = sdgetfspart(dev, pname, 1); + if(fs == nil) + return -1; + + return fsboot(fs, file, b); +} + +long +sdbio(SDunit *unit, SDpart *pp, void* va, long len, vlong off) +{ + long l; + ulong bno, max, nb, offset; + static uchar *b; + char *a; + static ulong bsz; + + a = va; +memset(a, 0xDA, len); + qlock(&unit->ctl); + if(unit->changed){ + qunlock(&unit->ctl); + return 0; + } + + /* + * Check the request is within bounds. + * Removeable drives are locked throughout the I/O + * in case the media changes unexpectedly. + * Non-removeable drives are not locked during the I/O + * to allow the hardware to optimise if it can; this is + * a little fast and loose. + * It's assumed that non-removable media parameters + * (sectors, secsize) can't change once the drive has + * been brought online. + */ + bno = (off/unit->secsize) + pp->start; + nb = ((off+len+unit->secsize-1)/unit->secsize) + pp->start - bno; + max = SDmaxio/unit->secsize; + if(nb > max) + nb = max; + if(bno+nb > pp->end) + nb = pp->end - bno; + if(bno >= pp->end || nb == 0){ + qunlock(&unit->ctl); + return 0; + } + if(!(unit->inquiry[1] & 0x80)) + qunlock(&unit->ctl); + + if(bsz < nb*unit->secsize){ + b = malloc(nb*unit->secsize); + bsz = nb*unit->secsize; + } +// b = sdmalloc(nb*unit->secsize); +// if(b == nil) +// return 0; + + offset = off%unit->secsize; + if((l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno)) < 0) { +// sdfree(b); + return 0; + } + + if(l < offset) + len = 0; + else if(len > l - offset) + len = l - offset; + if(len) + memmove(a, b+offset, len); +// sdfree(b); + + if(unit->inquiry[1] & 0x80) + qunlock(&unit->ctl); + + return len; +} + +#ifdef DMA +long +sdrio(SDreq *r, void* a, long n) +{ + if(n >= SDmaxio || n < 0) + return 0; + + r->data = nil; + if(n){ + if((r->data = malloc(n)) == nil) + return 0; + if(r->write) + memmove(r->data, a, n); + } + r->dlen = n; + + if(r->unit->dev->ifc->rio(r) != SDok){ +// cgascreenputs("1", 1); + if(r->data != nil){ + sdfree(r->data); + r->data = nil; + } + return 0; + } +// cgascreenputs("2", 1); + + if(!r->write && r->rlen > 0) + memmove(a, r->data, r->rlen); +// cgascreenputs("3", 1); + if(r->data != nil){ + sdfree(r->data); + r->data = nil; + } + +// cgascreenputs("4", 1); + return r->rlen; +} +#endif /* DMA */ + +void +sleep(void*, int (*fn)(void*), void *v) +{ + int x; + x = spllo(); + while(!fn(v)) + ; + splx(x); + return; +} + +void +tsleep(void*, int (*fn)(void*), void *v, int msec) +{ + int x; + ulong start; + + x = spllo(); + for(start = m->ticks; TK2MS(m->ticks - start) < msec + && !fn(v); ) + ; + splx(x); + return; +} + +void* +sdmalloc(void *p, ulong sz) +{ + if(p != nil) { + memset(p, 0, sz); + return p; + } + return malloc(sz); +} diff --git a/os/boot/pc/dma.c b/os/boot/pc/dma.c new file mode 100644 index 00000000..edaaa2fe --- /dev/null +++ b/os/boot/pc/dma.c @@ -0,0 +1,245 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +typedef struct DMAport DMAport; +typedef struct DMA DMA; +typedef struct DMAxfer DMAxfer; + +enum +{ + /* + * the byte registers for DMA0 are all one byte apart + */ + Dma0= 0x00, + Dma0status= Dma0+0x8, /* status port */ + Dma0reset= Dma0+0xD, /* reset port */ + + /* + * the byte registers for DMA1 are all two bytes apart (why?) + */ + Dma1= 0xC0, + Dma1status= Dma1+2*0x8, /* status port */ + Dma1reset= Dma1+2*0xD, /* reset port */ +}; + +/* + * state of a dma transfer + */ +struct DMAxfer +{ + ulong bpa; /* bounce buffer physical address */ + void* bva; /* bounce buffer virtual address */ + void* va; /* virtual address destination/src */ + long len; /* bytes to be transferred */ + int isread; +}; + +/* + * the dma controllers. the first half of this structure specifies + * the I/O ports used by the DMA controllers. + */ +struct DMAport +{ + uchar addr[4]; /* current address (4 channels) */ + uchar count[4]; /* current count (4 channels) */ + uchar page[4]; /* page registers (4 channels) */ + uchar cmd; /* command status register */ + uchar req; /* request registers */ + uchar sbm; /* single bit mask register */ + uchar mode; /* mode register */ + uchar cbp; /* clear byte pointer */ + uchar mc; /* master clear */ + uchar cmask; /* clear mask register */ + uchar wam; /* write all mask register bit */ +}; + +struct DMA +{ + DMAport; + int shift; + Lock; + DMAxfer x[4]; +}; + +DMA dma[2] = { + { 0x00, 0x02, 0x04, 0x06, + 0x01, 0x03, 0x05, 0x07, + 0x87, 0x83, 0x81, 0x82, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0 }, + + { 0xc0, 0xc4, 0xc8, 0xcc, + 0xc2, 0xc6, 0xca, 0xce, + 0x8f, 0x8b, 0x89, 0x8a, + 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde, + 1 }, +}; + +/* + * DMA must be in the first 16MB. This gets called early by the + * initialisation routines of any devices which require DMA to ensure + * the allocated bounce buffers are below the 16MB limit. + */ +void +dmainit(int chan) +{ + DMA *dp; + DMAxfer *xp; + ulong v; + static int once; + + if(once == 0){ +// if(ioalloc(0x00, 0x10, 0, "dma") < 0 +// || ioalloc(0x80, 0x10, 0, "dma") < 0 +// || ioalloc(0xd0, 0x10, 0, "dma") < 0) +// panic("dmainit"); + outb(dma[0].mc, 0); + outb(dma[1].mc, 0); + outb(dma[0].cmask, 0); + outb(dma[1].cmask, 0); + outb(dma[1].mode, 0xC0); + once = 1; + } + + dp = &dma[(chan>>2)&1]; + chan = chan & 3; + xp = &dp->x[chan]; + if(xp->bva != nil) + return; + + v = (ulong)xalloc(BY2PG+BY2PG); + if(v == 0 || PADDR(v) >= 16*MB){ + print("dmainit: chan %d: 0x%luX out of range\n", chan, v); + xfree((void*)v); + v = 0; + } + xp->bva = (void*)ROUND(v, BY2PG); + xp->bpa = PADDR(xp->bva); + xp->len = 0; + xp->isread = 0; +} + +/* + * setup a dma transfer. if the destination is not in kernel + * memory, allocate a page for the transfer. + * + * we assume BIOS has set up the command register before we + * are booted. + * + * return the updated transfer length (we can't transfer across 64k + * boundaries) + */ +long +dmasetup(int chan, void *va, long len, int isread) +{ + DMA *dp; + ulong pa; + uchar mode; + DMAxfer *xp; + + dp = &dma[(chan>>2)&1]; + chan = chan & 3; + xp = &dp->x[chan]; + + /* + * if this isn't kernel memory or crossing 64k boundary or above 16 meg + * use the allocated low memory page. + */ + pa = PADDR(va); + if((((ulong)va)&0xF0000000) != KZERO + || (pa&0xFFFF0000) != ((pa+len)&0xFFFF0000) + || pa > 16*MB) { + if(xp->bva == nil) + return -1; + if(len > BY2PG) + len = BY2PG; + if(!isread) + memmove(xp->bva, va, len); + xp->va = va; + xp->len = len; + xp->isread = isread; + pa = xp->bpa; + } + else + xp->len = 0; + + /* + * this setup must be atomic + */ + ilock(dp); + mode = (isread ? 0x44 : 0x48) | chan; + outb(dp->mode, mode); /* single mode dma (give CPU a chance at mem) */ + outb(dp->page[chan], pa>>16); + outb(dp->cbp, 0); /* set count & address to their first byte */ + outb(dp->addr[chan], pa>>dp->shift); /* set address */ + outb(dp->addr[chan], pa>>(8+dp->shift)); + outb(dp->count[chan], (len>>dp->shift)-1); /* set count */ + outb(dp->count[chan], ((len>>dp->shift)-1)>>8); + outb(dp->sbm, chan); /* enable the channel */ + iunlock(dp); + + return len; +} + +int +dmadone(int chan) +{ + DMA *dp; + + dp = &dma[(chan>>2)&1]; + chan = chan & 3; + + return inb(dp->cmd) & (1<>2)&1]; + chan = chan & 3; + + /* + * disable the channel + */ + ilock(dp); + outb(dp->sbm, 4|chan); + iunlock(dp); + + xp = &dp->x[chan]; + if(xp->len == 0 || !xp->isread) + return; + + /* + * copy out of temporary page + */ + memmove(xp->va, xp->bva, xp->len); + xp->len = 0; +} + +/* +int +dmacount(int chan) +{ + int retval; + DMA *dp; + + dp = &dma[(chan>>2)&1]; + outb(dp->cbp, 0); + retval = inb(dp->count[chan]); + retval |= inb(dp->count[chan]) << 8; + return((retval<shift)+1); +} + */ diff --git a/os/boot/pc/dosboot.c b/os/boot/pc/dosboot.c new file mode 100644 index 00000000..9192a5c8 --- /dev/null +++ b/os/boot/pc/dosboot.c @@ -0,0 +1,582 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "fs.h" + +struct Dosboot{ + uchar magic[3]; + uchar version[8]; + uchar sectsize[2]; + uchar clustsize; + uchar nresrv[2]; + uchar nfats; + uchar rootsize[2]; + uchar volsize[2]; + uchar mediadesc; + uchar fatsize[2]; + uchar trksize[2]; + uchar nheads[2]; + uchar nhidden[4]; + uchar bigvolsize[4]; +/* fat 32 */ + uchar bigfatsize[4]; + uchar extflags[2]; + uchar fsversion[2]; + uchar rootdirstartclust[4]; + uchar fsinfosect[2]; + uchar backupbootsect[2]; +/* ??? + uchar driveno; + uchar reserved0; + uchar bootsig; + uchar volid[4]; + uchar label[11]; + uchar reserved1[8]; +*/ +}; + +struct Dosdir{ + uchar name[8]; + uchar ext[3]; + uchar attr; + uchar lowercase; + uchar hundredth; + uchar ctime[2]; + uchar cdate[2]; + uchar adate[2]; + uchar highstart[2]; + uchar mtime[2]; + uchar mdate[2]; + uchar start[2]; + uchar length[4]; +}; + +#define DOSRONLY 0x01 +#define DOSHIDDEN 0x02 +#define DOSSYSTEM 0x04 +#define DOSVLABEL 0x08 +#define DOSDIR 0x10 +#define DOSARCH 0x20 + +/* + * predeclared + */ +static void bootdump(Dosboot*); +static void setname(Dosfile*, char*); + +/* + * debugging + */ +#define chatty 0 +#define chat if(chatty)print + +/* + * block io buffers + */ +enum +{ + Nbio= 16, +}; +typedef struct Clustbuf Clustbuf; +struct Clustbuf +{ + int age; + long sector; + uchar *iobuf; + Dos *dos; + int size; +}; +Clustbuf bio[Nbio]; + +/* + * get an io block from an io buffer + */ +Clustbuf* +getclust(Dos *dos, long sector) +{ + Fs *fs; + Clustbuf *p, *oldest; + int size; + + chat("getclust @ %ld\n", sector); + + /* + * if we have it, just return it + */ + for(p = bio; p < &bio[Nbio]; p++){ + if(sector == p->sector && dos == p->dos){ + p->age = m->ticks; + chat("getclust %ld in cache\n", sector); + return p; + } + } + + /* + * otherwise, reuse the oldest entry + */ + oldest = bio; + for(p = &bio[1]; p < &bio[Nbio]; p++){ + if(p->age <= oldest->age) + oldest = p; + } + p = oldest; + + /* + * make sure the buffer is big enough + */ + size = dos->clustsize*dos->sectsize; + if(p->iobuf==0 || p->size < size) + p->iobuf = ialloc(size, 0); + p->size = size; + + /* + * read in the cluster + */ + fs = (Fs*)dos; + chat("getclust addr %lud %p %p %p\n", (ulong)((sector+dos->start)*(vlong)dos->sectsize), + fs, fs->diskseek, fs->diskread); + if(fs->diskseek(fs, (sector+dos->start)*(vlong)dos->sectsize) < 0){ + chat("can't seek block\n"); + return 0; + } + if(fs->diskread(fs, p->iobuf, size) != size){ + chat("can't read block\n"); + return 0; + } + + p->age = m->ticks; + p->dos = dos; + p->sector = sector; + chat("getclust %ld read\n", sector); + return p; +} + +/* + * walk the fat one level ( n is a current cluster number ). + * return the new cluster number or -1 if no more. + */ +static long +fatwalk(Dos *dos, int n) +{ + ulong k, sect; + Clustbuf *p; + int o; + + chat("fatwalk %d\n", n); + + if(n < 2 || n >= dos->fatclusters) + return -1; + + switch(dos->fatbits){ + case 12: + k = (3*n)/2; break; + case 16: + k = 2*n; break; + case 32: + k = 4*n; break; + default: + return -1; + } + if(k >= dos->fatsize*dos->sectsize) + panic("getfat"); + + sect = (k/(dos->sectsize*dos->clustsize))*dos->clustsize + dos->fataddr; + o = k%(dos->sectsize*dos->clustsize); + p = getclust(dos, sect); + k = p->iobuf[o++]; + if(o >= dos->sectsize*dos->clustsize){ + p = getclust(dos, sect+dos->clustsize); + o = 0; + } + k |= p->iobuf[o++]<<8; + if(dos->fatbits == 12){ + if(n&1) + k >>= 4; + else + k &= 0xfff; + if(k >= 0xff8) + k = -1; + } + else if (dos->fatbits == 32){ + if(o >= dos->sectsize*dos->clustsize){ + p = getclust(dos, sect+dos->clustsize); + o = 0; + } + k |= p->iobuf[o++]<<16; + k |= p->iobuf[o]<<24; + if (k >= 0xfffffff8) + k = -1; + } + else + k = k < 0xfff8 ? k : -1; + chat("fatwalk %d -> %lud\n", n, k); + return k; +} + +/* + * map a file's logical cluster address to a physical sector address + */ +static long +fileaddr(Dosfile *fp, long ltarget) +{ + Dos *dos = fp->dos; + long l; + long p; + + chat("fileaddr %8.8s %ld\n", fp->name, ltarget); + /* + * root directory is contiguous and easy (unless FAT32) + */ + if(fp->pstart == 0 && dos->rootsize != 0) { + if(ltarget*dos->sectsize*dos->clustsize >= dos->rootsize*sizeof(Dosdir)) + return -1; + l = dos->rootaddr + ltarget*dos->clustsize; + chat("fileaddr %ld -> %ld\n", ltarget, l); + return l; + } + + /* + * anything else requires a walk through the fat + */ + if(ltarget >= fp->lcurrent && fp->pcurrent){ + /* start at the currrent point */ + l = fp->lcurrent; + p = fp->pcurrent; + } else { + /* go back to the beginning */ + l = 0; + p = fp->pstart; + } + while(l != ltarget){ + /* walk the fat */ + p = fatwalk(dos, p); + if(p < 0) + return -1; + l++; + } + fp->lcurrent = l; + fp->pcurrent = p; + + /* + * clusters start at 2 instead of 0 (why? - presotto) + */ + l = dos->dataaddr + (p-2)*dos->clustsize; + chat("fileaddr %ld -> %ld\n", ltarget, l); + return l; +} + +/* + * read from a dos file + */ +long +dosread(Dosfile *fp, void *a, long n) +{ + long addr; + long rv; + int i; + int off; + Clustbuf *p; + uchar *from, *to; + + if((fp->attr & DOSDIR) == 0){ + if(fp->offset >= fp->length) + return 0; + if(fp->offset+n > fp->length) + n = fp->length - fp->offset; + } + + to = a; + for(rv = 0; rv < n; rv+=i){ + /* + * read the cluster + */ + addr = fileaddr(fp, fp->offset/fp->dos->clustbytes); + if(addr < 0) + return -1; + p = getclust(fp->dos, addr); + if(p == 0) + return -1; + + /* + * copy the bytes we need + */ + off = fp->offset % fp->dos->clustbytes; + from = &p->iobuf[off]; + i = n - rv; + if(i > fp->dos->clustbytes - off) + i = fp->dos->clustbytes - off; + memmove(to, from, i); + to += i; + fp->offset += i; + } + + return rv; +} + +/* + * walk a directory returns + * -1 if something went wrong + * 0 if not found + * 1 if found + */ +int +doswalk(File *f, char *name) +{ + Dosdir d; + long n; + Dosfile *file; + + chat("doswalk %s\n", name); + + file = &f->dos; + + if((file->attr & DOSDIR) == 0){ + chat("walking non-directory!\n"); + return -1; + } + + setname(file, name); + + file->offset = 0; /* start at the beginning */ + while((n = dosread(file, &d, sizeof(d))) == sizeof(d)){ + chat("comparing to %8.8s.%3.3s\n", (char*)d.name, (char*)d.ext); + if(memcmp(file->name, d.name, sizeof(d.name)) != 0) + continue; + if(memcmp(file->ext, d.ext, sizeof(d.ext)) != 0) + continue; + if(d.attr & DOSVLABEL){ + chat("%8.8s.%3.3s is a LABEL\n", (char*)d.name, (char*)d.ext); + continue; + } + file->attr = d.attr; + file->pstart = GSHORT(d.start); + if (file->dos->fatbits == 32) + file->pstart |= GSHORT(d.highstart) << 16; + file->length = GLONG(d.length); + file->pcurrent = 0; + file->lcurrent = 0; + file->offset = 0; + return 1; + } + return n >= 0 ? 0 : -1; +} + +/* + * instructions that boot blocks can start with + */ +#define JMPSHORT 0xeb +#define JMPNEAR 0xe9 + +/* + * read in a segment + */ +long +dosreadseg(File *f, void *va, long len) +{ + char *a; + long n, sofar; + Dosfile *fp; + + fp = &f->dos; + a = va; + for(sofar = 0; sofar < len; sofar += n){ + n = 8*1024; + if(len - sofar < n) + n = len - sofar; + n = dosread(fp, a + sofar, n); + if(n <= 0) + break; + print("."); + } + return sofar; +} + +int +dosinit(Fs *fs) +{ + Clustbuf *p; + Dosboot *b; + int i; + Dos *dos; + Dosfile *root; + +chat("dosinit0 %p %p %p\n", fs, fs->diskseek, fs->diskread); + + dos = &fs->dos; + /* defaults till we know better */ + dos->sectsize = 512; + dos->clustsize = 1; + + /* get first sector */ + p = getclust(dos, 0); + if(p == 0){ + chat("can't read boot block\n"); + return -1; + } + +chat("dosinit0a\n"); + + p->dos = 0; + b = (Dosboot *)p->iobuf; + if(b->magic[0] != JMPNEAR && (b->magic[0] != JMPSHORT || b->magic[2] != 0x90)){ + chat("no dos file system %x %x %x %x\n", + b->magic[0], b->magic[1], b->magic[2], b->magic[3]); + return -1; + } + + if(chatty) + bootdump(b); + + if(b->clustsize == 0) { +unreasonable: + if(chatty){ + print("unreasonable FAT BPB: "); + for(i=0; i<3+8+2+1; i++) + print(" %.2ux", p->iobuf[i]); + print("\n"); + } + return -1; + } + +chat("dosinit1\n"); + + /* + * Determine the systems' wondrous properties. + * There are heuristics here, but there's no real way + * of knowing if this is a reasonable FAT. + */ + dos->fatbits = 0; + dos->sectsize = GSHORT(b->sectsize); + if(dos->sectsize & 0xFF) + goto unreasonable; + dos->clustsize = b->clustsize; + dos->clustbytes = dos->sectsize*dos->clustsize; + dos->nresrv = GSHORT(b->nresrv); + dos->nfats = b->nfats; + dos->fatsize = GSHORT(b->fatsize); + dos->rootsize = GSHORT(b->rootsize); + dos->volsize = GSHORT(b->volsize); + if(dos->volsize == 0) + dos->volsize = GLONG(b->bigvolsize); + dos->mediadesc = b->mediadesc; + if(dos->fatsize == 0) { + chat("fat32\n"); + dos->rootsize = 0; + dos->fatsize = GLONG(b->bigfatsize); + dos->fatbits = 32; + } + dos->fataddr = dos->nresrv; + if (dos->rootsize == 0) { + dos->rootaddr = 0; + dos->rootclust = GLONG(b->rootdirstartclust); + dos->dataaddr = dos->fataddr + dos->nfats*dos->fatsize; + } else { + dos->rootaddr = dos->fataddr + dos->nfats*dos->fatsize; + i = dos->rootsize*sizeof(Dosdir) + dos->sectsize - 1; + i = i/dos->sectsize; + dos->dataaddr = dos->rootaddr + i; + } + dos->fatclusters = 2+(dos->volsize - dos->dataaddr)/dos->clustsize; + if(dos->fatbits != 32) { + if(dos->fatclusters < 4087) + dos->fatbits = 12; + else + dos->fatbits = 16; + } + dos->freeptr = 2; + + if(dos->clustbytes < 512 || dos->clustbytes > 64*1024) + goto unreasonable; + +chat("dosinit2\n"); + + /* + * set up the root + */ + + fs->root.fs = fs; + root = &fs->root.dos; + root->dos = dos; + root->pstart = dos->rootsize == 0 ? dos->rootclust : 0; + root->pcurrent = root->lcurrent = 0; + root->offset = 0; + root->attr = DOSDIR; + root->length = dos->rootsize*sizeof(Dosdir); + +chat("dosinit3\n"); + + fs->read = dosreadseg; + fs->walk = doswalk; + return 0; +} + +static void +bootdump(Dosboot *b) +{ + if(chatty == 0) + return; + print("magic: 0x%2.2x 0x%2.2x 0x%2.2x ", + b->magic[0], b->magic[1], b->magic[2]); + print("version: \"%8.8s\"\n", (char*)b->version); + print("sectsize: %d ", GSHORT(b->sectsize)); + print("allocsize: %d ", b->clustsize); + print("nresrv: %d ", GSHORT(b->nresrv)); + print("nfats: %d\n", b->nfats); + print("rootsize: %d ", GSHORT(b->rootsize)); + print("volsize: %d ", GSHORT(b->volsize)); + print("mediadesc: 0x%2.2x\n", b->mediadesc); + print("fatsize: %d ", GSHORT(b->fatsize)); + print("trksize: %d ", GSHORT(b->trksize)); + print("nheads: %d ", GSHORT(b->nheads)); + print("nhidden: %d ", GLONG(b->nhidden)); + print("bigvolsize: %d\n", GLONG(b->bigvolsize)); +/* + print("driveno: %d\n", b->driveno); + print("reserved0: 0x%2.2x\n", b->reserved0); + print("bootsig: 0x%2.2x\n", b->bootsig); + print("volid: 0x%8.8x\n", GLONG(b->volid)); + print("label: \"%11.11s\"\n", b->label); +*/ +} + + +/* + * set up a dos file name + */ +static void +setname(Dosfile *fp, char *from) +{ + char *to; + + to = fp->name; + for(; *from && to-fp->name < 8; from++, to++){ + if(*from == '.'){ + from++; + break; + } + if(*from >= 'a' && *from <= 'z') + *to = *from + 'A' - 'a'; + else + *to = *from; + } + while(to - fp->name < 8) + *to++ = ' '; + + /* from might be 12345678.123: don't save the '.' in ext */ + if(*from == '.') + from++; + + to = fp->ext; + for(; *from && to-fp->ext < 3; from++, to++){ + if(*from >= 'a' && *from <= 'z') + *to = *from + 'A' - 'a'; + else + *to = *from; + } + while(to-fp->ext < 3) + *to++ = ' '; + + chat("name is %8.8s.%3.3s\n", fp->name, fp->ext); +} diff --git a/os/boot/pc/dosfs.h b/os/boot/pc/dosfs.h new file mode 100644 index 00000000..1107c4ce --- /dev/null +++ b/os/boot/pc/dosfs.h @@ -0,0 +1,62 @@ +typedef struct Dosboot Dosboot; +typedef struct Dos Dos; +typedef struct Dosdir Dosdir; +typedef struct Dosfile Dosfile; +typedef struct Dospart Dospart; + +struct Dospart +{ + uchar flag; /* active flag */ + uchar shead; /* starting head */ + uchar scs[2]; /* starting cylinder/sector */ + uchar type; /* partition type */ + uchar ehead; /* ending head */ + uchar ecs[2]; /* ending cylinder/sector */ + uchar start[4]; /* starting sector */ + uchar len[4]; /* length in sectors */ +}; + +#define FAT12 0x01 +#define FAT16 0x04 +#define EXTEND 0x05 +#define FATHUGE 0x06 +#define FAT32 0x0b +#define FAT32X 0x0c +#define EXTHUGE 0x0f +#define DMDDO 0x54 +#define PLAN9 0x39 +#define LEXTEND 0x85 + +struct Dosfile{ + Dos *dos; /* owning dos file system */ + char name[8]; + char ext[3]; + uchar attr; + long length; + long pstart; /* physical start cluster address */ + long pcurrent; /* physical current cluster address */ + long lcurrent; /* logical current cluster address */ + long offset; +}; + +struct Dos{ + long start; /* start of file system */ + int sectsize; /* in bytes */ + int clustsize; /* in sectors */ + int clustbytes; /* in bytes */ + int nresrv; /* sectors */ + int nfats; /* usually 2 */ + int rootsize; /* number of entries */ + int volsize; /* in sectors */ + int mediadesc; + int fatsize; /* in sectors */ + int fatclusters; + int fatbits; /* 12 or 16 */ + long fataddr; /* sector number */ + long rootaddr; + long rootclust; + long dataaddr; + long freeptr; +}; + +extern int dosinit(Fs*); diff --git a/os/boot/pc/eipfmt.c b/os/boot/pc/eipfmt.c new file mode 100644 index 00000000..9d3b38fb --- /dev/null +++ b/os/boot/pc/eipfmt.c @@ -0,0 +1,145 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ip.h" + + +enum +{ + IPaddrlen= 16, + IPv4addrlen= 4, + IPv4off= 12, + IPllen= 4, +}; +extern int fmtstrcpy(Fmt*, char*); + + +/* + * prefix of all v4 addresses + */ +uchar v4prefix[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0xff, 0xff, + 0, 0, 0, 0 +}; + +enum +{ + Isprefix= 16, +}; + +uchar prefixvals[256] = +{ +[0x00] 0 | Isprefix, +[0x80] 1 | Isprefix, +[0xC0] 2 | Isprefix, +[0xE0] 3 | Isprefix, +[0xF0] 4 | Isprefix, +[0xF8] 5 | Isprefix, +[0xFC] 6 | Isprefix, +[0xFE] 7 | Isprefix, +[0xFF] 8 | Isprefix, +}; + +void +hnputl(void *p, uint v) +{ + uchar *a; + + a = p; + a[0] = v>>24; + a[1] = v>>16; + a[2] = v>>8; + a[3] = v; +} + +int +eipfmt(Fmt *f) +{ + char buf[5*8]; + static char *efmt = "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux"; + static char *ifmt = "%d.%d.%d.%d"; + uchar *p, ip[16]; + ulong *lp; + ushort s; + int i, j, n, eln, eli; + + switch(f->r) { + case 'E': /* Ethernet address */ + p = va_arg(f->args, uchar*); + snprint(buf, sizeof buf, efmt, p[0], p[1], p[2], p[3], p[4], p[5]); + return fmtstrcpy(f, buf); + + case 'I': /* Ip address */ + p = va_arg(f->args, uchar*); +common: + if(memcmp(p, v4prefix, 12) == 0){ + snprint(buf, sizeof buf, ifmt, p[12], p[13], p[14], p[15]); + return fmtstrcpy(f, buf); + } + + /* find longest elision */ + eln = eli = -1; + for(i = 0; i < 16; i += 2){ + for(j = i; j < 16; j += 2) + if(p[j] != 0 || p[j+1] != 0) + break; + if(j > i && j - i > eln){ + eli = i; + eln = j - i; + } + } + + /* print with possible elision */ + n = 0; + for(i = 0; i < 16; i += 2){ + if(i == eli){ + n += sprint(buf+n, "::"); + i += eln; + if(i >= 16) + break; + } else if(i != 0) + n += sprint(buf+n, ":"); + s = (p[i]<<8) + p[i+1]; + n += sprint(buf+n, "%ux", s); + } + return fmtstrcpy(f, buf); + + case 'i': /* v6 address as 4 longs */ + lp = va_arg(f->args, ulong*); + for(i = 0; i < 4; i++) + hnputl(ip+4*i, *lp++); + p = ip; + goto common; + + case 'V': /* v4 ip address */ + p = va_arg(f->args, uchar*); + snprint(buf, sizeof buf, ifmt, p[0], p[1], p[2], p[3]); + return fmtstrcpy(f, buf); + + case 'M': /* ip mask */ + p = va_arg(f->args, uchar*); + + /* look for a prefix mask */ + for(i = 0; i < 16; i++) + if(p[i] != 0xff) + break; + if(i < 16){ + if((prefixvals[p[i]] & Isprefix) == 0) + goto common; + for(j = i+1; j < 16; j++) + if(p[j] != 0) + goto common; + n = 8*i + (prefixvals[p[i]] & ~Isprefix); + } else + n = 8*16; + + /* got one, use /xx format */ + snprint(buf, sizeof buf, "/%d", n); + return fmtstrcpy(f, buf); + } + return fmtstrcpy(f, "(eipfmt)"); +} diff --git a/os/boot/pc/error.h b/os/boot/pc/error.h new file mode 100644 index 00000000..98277d63 --- /dev/null +++ b/os/boot/pc/error.h @@ -0,0 +1,58 @@ +extern char Enoerror[]; /* no error */ +extern char Emount[]; /* inconsistent mount */ +extern char Eunmount[]; /* not mounted */ +extern char Eunion[]; /* not in union */ +extern char Emountrpc[]; /* mount rpc error */ +extern char Eshutdown[]; /* mounted device shut down */ +extern char Enocreate[]; /* mounted directory forbids creation */ +extern char Enonexist[]; /* file does not exist */ +extern char Eexist[]; /* file already exists */ +extern char Ebadsharp[]; /* unknown device in # filename */ +extern char Enotdir[]; /* not a directory */ +extern char Eisdir[]; /* file is a directory */ +extern char Ebadchar[]; /* bad character in file name */ +extern char Efilename[]; /* file name syntax */ +extern char Eperm[]; /* permission denied */ +extern char Ebadusefd[]; /* inappropriate use of fd */ +extern char Ebadarg[]; /* bad arg in system call */ +extern char Einuse[]; /* device or object already in use */ +extern char Eio[]; /* i/o error */ +extern char Etoobig[]; /* read or write too large */ +extern char Etoosmall[]; /* read or write too small */ +extern char Enetaddr[]; /* bad network address */ +extern char Emsgsize[]; /* message is too big for protocol */ +extern char Enetbusy[]; /* network device is busy or allocated */ +extern char Enoproto[]; /* network protocol not supported */ +extern char Enoport[]; /* network port not available */ +extern char Enoifc[]; /* bad interface or no free interface slots */ +extern char Enolisten[]; /* not announced */ +extern char Ehungup[]; /* write to hungup channel */ +extern char Ebadctl[]; /* bad process or channel control request */ +extern char Enodev[]; /* no free devices */ +extern char Enoenv[]; /* no free environment resources */ +extern char Emuxshutdown[]; /* mux server shut down */ +extern char Emuxbusy[]; /* all mux channels busy */ +extern char Emuxmsg[]; /* bad mux message format or mismatch */ +extern char Eprocdied[]; /* process exited */ +extern char Enochild[]; /* no living children */ +extern char Eioload[]; /* i/o error in demand load */ +extern char Enovmem[]; /* virtual memory allocation failed */ +extern char Ebadld[]; /* illegal line discipline */ +extern char Ebadfd[]; /* fd out of range or not open */ +extern char Eisstream[]; /* seek on a stream */ +extern char Ebadexec[]; /* exec header invalid */ +extern char Etimedout[]; /* connection timed out */ +extern char Econrefused[]; /* connection refused */ +extern char Enetunreach[]; /* network unreachable */ +extern char Eintr[]; /* interrupted */ +extern char Eneedservice[]; /* service required for tcp/udp/il calls */ +extern char Enomem[]; /* kernel allocate failed */ +extern char Enoswap[]; /* swap space full */ +extern char Esfnotcached[]; /* subfont not cached */ +extern char Esoverlap[]; /* segments overlap */ +extern char Emouseset[]; /* mouse type already set */ +extern char Erecover[]; /* failed to recover fd */ +extern char Eshort[]; /* i/o count too small */ +extern char Egreg[]; /* ken scheduled it */ +extern char Ebadspec[]; /* bad attach specifier */ +extern char Enoreg[]; /* process has no saved registers */ diff --git a/os/boot/pc/ether.c b/os/boot/pc/ether.c new file mode 100644 index 00000000..c8a4b7de --- /dev/null +++ b/os/boot/pc/ether.c @@ -0,0 +1,282 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ip.h" + +#include "etherif.h" + +static Ether ether[MaxEther]; + +extern int ether2114xreset(Ether*); +extern int elnk3reset(Ether*); +extern int i82557reset(Ether*); +extern int igbepnp(Ether *); +extern int elnk3reset(Ether*); +extern int ether589reset(Ether*); +extern int ne2000reset(Ether*); +extern int wd8003reset(Ether*); +extern int ec2treset(Ether*); +extern int amd79c970reset(Ether*); +extern int rtl8139pnp(Ether*); +extern int rtl8169pnp(Ether*); +extern int ether83815reset(Ether*); +extern int rhinepnp(Ether*); + +struct { + char *type; + int (*reset)(Ether*); + int noprobe; +} ethercards[] = { + { "21140", ether2114xreset, 0, }, + { "2114x", ether2114xreset, 0, }, + { "i82557", i82557reset, 0, }, + { "igbe", igbepnp, 0, }, + { "elnk3", elnk3reset, 0, }, + { "3C509", elnk3reset, 0, }, + { "3C575", elnk3reset, 0, }, + { "3C589", ether589reset, 1, }, + { "3C562", ether589reset, 1, }, + { "589E", ether589reset, 1, }, + { "NE2000", ne2000reset, 0, }, + { "WD8003", wd8003reset, 1, }, + { "EC2T", ec2treset, 0, }, + { "AMD79C970", amd79c970reset, 0, }, + { "RTL8139", rtl8139pnp, 0, }, + { "RTL8169", rtl8169pnp, 0, }, + { "83815", ether83815reset, 0, }, + { "rhine", rhinepnp, 0, }, + + { 0, } +}; + +static void xetherdetach(void); + +int +etherinit(void) +{ + Ether *ctlr; + int ctlrno, i, mask, n, x; + + fmtinstall('E', eipfmt); + + etherdetach = xetherdetach; + mask = 0; + for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){ + ctlr = ðer[ctlrno]; + memset(ctlr, 0, sizeof(Ether)); + if(iniread && isaconfig("ether", ctlrno, ctlr) == 0) + continue; + + for(n = 0; ethercards[n].type; n++){ + if(!iniread){ + if(ethercards[n].noprobe) + continue; + memset(ctlr, 0, sizeof(Ether)); + strcpy(ctlr->type, ethercards[n].type); + } + else if(cistrcmp(ethercards[n].type, ctlr->type)) + continue; + ctlr->ctlrno = ctlrno; + + x = splhi(); + if((*ethercards[n].reset)(ctlr)){ + splx(x); + if(iniread) + break; + else + continue; + } + + ctlr->state = 1; + mask |= 1<irq == 2) + ctlr->irq = 9; + setvec(VectorPIC + ctlr->irq, ctlr->interrupt, ctlr); + + print("ether#%d: %s: port 0x%luX irq %lud", + ctlr->ctlrno, ctlr->type, ctlr->port, ctlr->irq); + if(ctlr->mem) + print(" addr 0x%luX", ctlr->mem & ~KZERO); + if(ctlr->size) + print(" size 0x%luX", ctlr->size); + print(": %E\n", ctlr->ea); + + if(ctlr->nrb == 0) + ctlr->nrb = Nrb; + ctlr->rb = ialloc(sizeof(RingBuf)*ctlr->nrb, 0); + if(ctlr->ntb == 0) + ctlr->ntb = Ntb; + ctlr->tb = ialloc(sizeof(RingBuf)*ctlr->ntb, 0); + + ctlr->rh = 0; + ctlr->ri = 0; + for(i = 0; i < ctlr->nrb; i++) + ctlr->rb[i].owner = Interface; + + ctlr->th = 0; + ctlr->ti = 0; + for(i = 0; i < ctlr->ntb; i++) + ctlr->tb[i].owner = Host; + + splx(x); + break; + } + } + + return mask; +} + +void +etherinitdev(int i, char *s) +{ + sprint(s, "ether%d", i); +} + +void +etherprintdevs(int i) +{ + print(" ether%d", i); +} + +static Ether* +attach(int ctlrno) +{ + Ether *ctlr; + + if(ctlrno >= MaxEther || ether[ctlrno].state == 0) + return 0; + + ctlr = ðer[ctlrno]; + if(ctlr->state == 1){ + ctlr->state = 2; + (*ctlr->attach)(ctlr); + } + + return ctlr; +} + +static void +xetherdetach(void) +{ + Ether *ctlr; + int ctlrno, x; + + x = splhi(); + for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){ + ctlr = ðer[ctlrno]; + if(ctlr->detach && ctlr->state != 0) + ctlr->detach(ctlr); + } + splx(x); +} + +uchar* +etheraddr(int ctlrno) +{ + Ether *ctlr; + + if((ctlr = attach(ctlrno)) == 0) + return 0; + + return ctlr->ea; +} + +static int +wait(RingBuf* ring, uchar owner, int timo) +{ + ulong start; + + start = m->ticks; + while(TK2MS(m->ticks - start) < timo){ + if(ring->owner != owner) + return 1; + } + + return 0; +} + +int +etherrxpkt(int ctlrno, Etherpkt* pkt, int timo) +{ + int n; + Ether *ctlr; + RingBuf *ring; + + if((ctlr = attach(ctlrno)) == 0) + return 0; + + ring = &ctlr->rb[ctlr->rh]; + if(wait(ring, Interface, timo) == 0){ + if(debug) + print("ether%d: rx timeout\n", ctlrno); + return 0; + } + + n = ring->len; + memmove(pkt, ring->pkt, n); + ring->owner = Interface; + ctlr->rh = NEXT(ctlr->rh, ctlr->nrb); + + return n; +} + +int +etherrxflush(int ctlrno) +{ + int n; + Ether *ctlr; + RingBuf *ring; + + if((ctlr = attach(ctlrno)) == 0) + return 0; + + n = 0; + for(;;){ + ring = &ctlr->rb[ctlr->rh]; + if(wait(ring, Interface, 100) == 0) + break; + + ring->owner = Interface; + ctlr->rh = NEXT(ctlr->rh, ctlr->nrb); + n++; + } + + return n; +} + +int +ethertxpkt(int ctlrno, Etherpkt* pkt, int len, int) +{ + Ether *ctlr; + RingBuf *ring; + int s; + + if((ctlr = attach(ctlrno)) == 0) + return 0; + + ring = &ctlr->tb[ctlr->th]; + if(wait(ring, Interface, 1000) == 0){ + print("ether%d: tx buffer timeout\n", ctlrno); + return 0; + } + + memmove(pkt->s, ctlr->ea, Eaddrlen); + if(debug) + print("%E to %E...\n", pkt->s, pkt->d); + memmove(ring->pkt, pkt, len); + if(len < ETHERMINTU){ + memset(ring->pkt+len, 0, ETHERMINTU-len); + len = ETHERMINTU; + } + ring->len = len; + ring->owner = Interface; + ctlr->th = NEXT(ctlr->th, ctlr->ntb); + s = splhi(); + (*ctlr->transmit)(ctlr); + splx(s); + + return 1; +} diff --git a/os/boot/pc/ether2000.c b/os/boot/pc/ether2000.c new file mode 100644 index 00000000..4c329978 --- /dev/null +++ b/os/boot/pc/ether2000.c @@ -0,0 +1,110 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "etherif.h" +#include "ether8390.h" + +/* + * Driver written for the 'Notebook Computer Ethernet LAN Adapter', + * a plug-in to the bus-slot on the rear of the Gateway NOMAD 425DXL + * laptop. The manual says NE2000 compatible. + * The interface appears to be pretty well described in the National + * Semiconductor Local Area Network Databook (1992) as one of the + * AT evaluation cards. + * + * The NE2000 is really just a DP8390[12] plus a data port + * and a reset port. + */ +enum { + Data = 0x10, /* offset from I/O base of data port */ + Reset = 0x1F, /* offset from I/O base of reset port */ +}; + +int +ne2000reset(Ether* ether) +{ + ushort buf[16]; + ulong port; + Dp8390 *ctlr; + int i; + uchar ea[Eaddrlen]; + + /* + * Set up the software configuration. + * Use defaults for port, irq, mem and size + * if not specified. + */ + if(ether->port == 0) + ether->port = 0x300; + if(ether->irq == 0) + ether->irq = 2; + if(ether->mem == 0) + ether->mem = 0x4000; + if(ether->size == 0) + ether->size = 16*1024; + port = ether->port; + + ether->ctlr = malloc(sizeof(Dp8390)); + ctlr = ether->ctlr; + ctlr->width = 2; + ctlr->ram = 0; + + ctlr->port = port; + ctlr->data = port+Data; + + ctlr->tstart = HOWMANY(ether->mem, Dp8390BufSz); + ctlr->pstart = ctlr->tstart + HOWMANY(sizeof(Etherpkt), Dp8390BufSz); + ctlr->pstop = ctlr->tstart + HOWMANY(ether->size, Dp8390BufSz); + + ctlr->dummyrr = 1; + for(i = 0; i < ether->nopt; i++){ + if(strcmp(ether->opt[i], "nodummyrr")) + continue; + ctlr->dummyrr = 0; + break; + } + + /* + * Reset the board. This is done by doing a read + * followed by a write to the Reset address. + */ + buf[0] = inb(port+Reset); + delay(2); + outb(port+Reset, buf[0]); + delay(2); + + /* + * Init the (possible) chip, then use the (possible) + * chip to read the (possible) PROM for ethernet address + * and a marker byte. + * Could just look at the DP8390 command register after + * initialisation has been tried, but that wouldn't be + * enough, there are other ethernet boards which could + * match. + */ + dp8390reset(ether); + memset(buf, 0, sizeof(buf)); + dp8390read(ctlr, buf, 0, sizeof(buf)); + if((buf[0x0E] & 0xFF) != 0x57 || (buf[0x0F] & 0xFF) != 0x57){ + free(ether->ctlr); + return -1; + } + + /* + * Stupid machine. Shorts were asked for, + * shorts were delivered, although the PROM is a byte array. + * Set the ethernet address. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + for(i = 0; i < sizeof(ether->ea); i++) + ether->ea[i] = buf[i]; + } + dp8390setea(ether); + + return 0; +} diff --git a/os/boot/pc/ether2114x.c b/os/boot/pc/ether2114x.c new file mode 100644 index 00000000..d6f12b52 --- /dev/null +++ b/os/boot/pc/ether2114x.c @@ -0,0 +1,1652 @@ +/* + * Digital Semiconductor DECchip 21140 PCI Fast Ethernet LAN Controller + * as found on the Digital Fast EtherWORKS PCI 10/100 adapter (DE-500-X). + * To do: + * thresholds; + * ring sizing; + * handle more error conditions; + * all the rest of it... + */ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "etherif.h" + +#define DEBUG (0) +#define debug if(DEBUG)print + +enum { + Nrde = 32, + Ntde = 4, +}; + +#define Rbsz ROUNDUP(sizeof(Etherpkt)+4, 4) + +enum { /* CRS0 - Bus Mode */ + Swr = 0x00000001, /* Software Reset */ + Bar = 0x00000002, /* Bus Arbitration */ + Dsl = 0x0000007C, /* Descriptor Skip Length (field) */ + Ble = 0x00000080, /* Big/Little Endian */ + Pbl = 0x00003F00, /* Programmable Burst Length (field) */ + Cal = 0x0000C000, /* Cache Alignment (field) */ + Cal8 = 0x00004000, /* 8 longword boundary alignment */ + Cal16 = 0x00008000, /* 16 longword boundary alignment */ + Cal32 = 0x0000C000, /* 32 longword boundary alignment */ + Tap = 0x000E0000, /* Transmit Automatic Polling (field) */ + Dbo = 0x00100000, /* Descriptor Byte Ordering Mode */ + Rml = 0x00200000, /* Read Multiple */ +}; + +enum { /* CSR[57] - Status and Interrupt Enable */ + Ti = 0x00000001, /* Transmit Interrupt */ + Tps = 0x00000002, /* Transmit Process Stopped */ + Tu = 0x00000004, /* Transmit buffer Unavailable */ + Tjt = 0x00000008, /* Transmit Jabber Timeout */ + Unf = 0x00000020, /* transmit UNderFlow */ + Ri = 0x00000040, /* Receive Interrupt */ + Ru = 0x00000080, /* Receive buffer Unavailable */ + Rps = 0x00000100, /* Receive Process Stopped */ + Rwt = 0x00000200, /* Receive Watchdog Timeout */ + Eti = 0x00000400, /* Early Transmit Interrupt */ + Gte = 0x00000800, /* General purpose Timer Expired */ + Fbe = 0x00002000, /* Fatal Bit Error */ + Ais = 0x00008000, /* Abnormal Interrupt Summary */ + Nis = 0x00010000, /* Normal Interrupt Summary */ + Rs = 0x000E0000, /* Receive process State (field) */ + Ts = 0x00700000, /* Transmit process State (field) */ + Eb = 0x03800000, /* Error bits */ +}; + +enum { /* CSR6 - Operating Mode */ + Hp = 0x00000001, /* Hash/Perfect receive filtering mode */ + Sr = 0x00000002, /* Start/stop Receive */ + Ho = 0x00000004, /* Hash-Only filtering mode */ + Pb = 0x00000008, /* Pass Bad frames */ + If = 0x00000010, /* Inverse Filtering */ + Sb = 0x00000020, /* Start/stop Backoff counter */ + Pr = 0x00000040, /* Promiscuous Mode */ + Pm = 0x00000080, /* Pass all Multicast */ + Fd = 0x00000200, /* Full Duplex mode */ + Om = 0x00000C00, /* Operating Mode (field) */ + Fc = 0x00001000, /* Force Collision */ + St = 0x00002000, /* Start/stop Transmission Command */ + Tr = 0x0000C000, /* ThReshold control bits (field) */ + Tr128 = 0x00000000, + Tr256 = 0x00004000, + Tr512 = 0x00008000, + Tr1024 = 0x0000C000, + Ca = 0x00020000, /* CApture effect enable */ + Ps = 0x00040000, /* Port Select */ + Hbd = 0x00080000, /* HeartBeat Disable */ + Imm = 0x00100000, /* IMMediate mode */ + Sf = 0x00200000, /* Store and Forward */ + Ttm = 0x00400000, /* Transmit Threshold Mode */ + Pcs = 0x00800000, /* PCS function */ + Scr = 0x01000000, /* SCRambler mode */ + Mbo = 0x02000000, /* Must Be One */ + Ra = 0x40000000, /* Receive All */ + Sc = 0x80000000, /* Special Capture effect enable */ + + TrMODE = Tr512, /* default transmission threshold */ +}; + +enum { /* CSR9 - ROM and MII Management */ + Scs = 0x00000001, /* serial ROM chip select */ + Sclk = 0x00000002, /* serial ROM clock */ + Sdi = 0x00000004, /* serial ROM data in */ + Sdo = 0x00000008, /* serial ROM data out */ + Ss = 0x00000800, /* serial ROM select */ + Wr = 0x00002000, /* write */ + Rd = 0x00004000, /* read */ + + Mdc = 0x00010000, /* MII management clock */ + Mdo = 0x00020000, /* MII management write data */ + Mii = 0x00040000, /* MII management operation mode (W) */ + Mdi = 0x00080000, /* MII management data in */ +}; + +enum { /* CSR12 - General-Purpose Port */ + Gpc = 0x00000100, /* General Purpose Control */ +}; + +typedef struct Des { + int status; + int control; + ulong addr; + void* bp; +} Des; + +enum { /* status */ + Of = 0x00000001, /* Rx: OverFlow */ + Ce = 0x00000002, /* Rx: CRC Error */ + Db = 0x00000004, /* Rx: Dribbling Bit */ + Re = 0x00000008, /* Rx: Report on MII Error */ + Rw = 0x00000010, /* Rx: Receive Watchdog */ + Ft = 0x00000020, /* Rx: Frame Type */ + Cs = 0x00000040, /* Rx: Collision Seen */ + Tl = 0x00000080, /* Rx: Frame too Long */ + Ls = 0x00000100, /* Rx: Last deScriptor */ + Fs = 0x00000200, /* Rx: First deScriptor */ + Mf = 0x00000400, /* Rx: Multicast Frame */ + Rf = 0x00000800, /* Rx: Runt Frame */ + Dt = 0x00003000, /* Rx: Data Type (field) */ + De = 0x00004000, /* Rx: Descriptor Error */ + Fl = 0x3FFF0000, /* Rx: Frame Length (field) */ + Ff = 0x40000000, /* Rx: Filtering Fail */ + + Def = 0x00000001, /* Tx: DEFerred */ + Uf = 0x00000002, /* Tx: UnderFlow error */ + Lf = 0x00000004, /* Tx: Link Fail report */ + Cc = 0x00000078, /* Tx: Collision Count (field) */ + Hf = 0x00000080, /* Tx: Heartbeat Fail */ + Ec = 0x00000100, /* Tx: Excessive Collisions */ + Lc = 0x00000200, /* Tx: Late Collision */ + Nc = 0x00000400, /* Tx: No Carrier */ + Lo = 0x00000800, /* Tx: LOss of carrier */ + To = 0x00004000, /* Tx: Transmission jabber timeOut */ + + Es = 0x00008000, /* [RT]x: Error Summary */ + Own = 0x80000000, /* [RT]x: OWN bit */ +}; + +enum { /* control */ + Bs1 = 0x000007FF, /* [RT]x: Buffer 1 Size */ + Bs2 = 0x003FF800, /* [RT]x: Buffer 2 Size */ + + Ch = 0x01000000, /* [RT]x: second address CHained */ + Er = 0x02000000, /* [RT]x: End of Ring */ + + Ft0 = 0x00400000, /* Tx: Filtering Type 0 */ + Dpd = 0x00800000, /* Tx: Disabled PaDding */ + Ac = 0x04000000, /* Tx: Add CRC disable */ + Set = 0x08000000, /* Tx: SETup packet */ + Ft1 = 0x10000000, /* Tx: Filtering Type 1 */ + Fseg = 0x20000000, /* Tx: First SEGment */ + Lseg = 0x40000000, /* Tx: Last SEGment */ + Ic = 0x80000000, /* Tx: Interrupt on Completion */ +}; + +enum { /* PHY registers */ + Bmcr = 0, /* Basic Mode Control */ + Bmsr = 1, /* Basic Mode Status */ + Phyidr1 = 2, /* PHY Identifier #1 */ + Phyidr2 = 3, /* PHY Identifier #2 */ + Anar = 4, /* Auto-Negotiation Advertisment */ + Anlpar = 5, /* Auto-Negotiation Link Partner Ability */ + Aner = 6, /* Auto-Negotiation Expansion */ +}; + +enum { /* Variants */ + Tulip0 = (0x0009<<16)|0x1011, + Tulip1 = (0x0014<<16)|0x1011, + Tulip3 = (0x0019<<16)|0x1011, + Pnic = (0x0002<<16)|0x11AD, + Pnic2 = (0xC115<<16)|0x11AD, +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; /* (pcidev->did<<16)|pcidev->vid */ + + uchar *srom; + int sromsz; + uchar* sromea; /* MAC address */ + uchar* leaf; + int sct; /* selected connection type */ + int k; /* info block count */ + uchar* infoblock[16]; + int sctk; /* sct block index */ + int curk; /* current block index */ + uchar* type5block; + + int phy[32]; /* logical to physical map */ + int phyreset; /* reset bitmap */ + int curphyad; + int fdx; + int ttm; + + uchar fd; /* option */ + int medium; /* option */ + + int csr6; /* CSR6 - operating mode */ + int mask; /* CSR[57] - interrupt mask */ + int mbps; + + Des* rdr; /* receive descriptor ring */ + int nrdr; /* size of rdr */ + int rdrx; /* index into rdr */ + + Des* tdr; /* transmit descriptor ring */ + int ntdr; /* size of tdr */ + int tdrh; /* host index into tdr */ + int tdri; /* interface index into tdr */ + int ntq; /* descriptors active */ + Block* setupbp; + + ulong of; /* receive statistics */ + ulong ce; + ulong cs; + ulong tl; + ulong rf; + ulong de; + + ulong uf; /* transmit statistics */ + ulong ec; + ulong lc; + ulong nc; + ulong lo; + ulong to; + +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +#define csr32r(c, r) (inl((c)->port+((r)*8))) +#define csr32w(c, r, l) (outl((c)->port+((r)*8), (ulong)(l))) + +static void +attach(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + if(!(ctlr->csr6 & Sr)){ + ctlr->csr6 |= Sr; + csr32w(ctlr, 6, ctlr->csr6); + } +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + Block *bp; + Des *des; + int control; + RingBuf *tb; + + ctlr = ether->ctlr; + while(ctlr->ntq < (ctlr->ntdr-1)){ + if(ctlr->setupbp){ + bp = ctlr->setupbp; + ctlr->setupbp = 0; + control = Ic|Set|BLEN(bp); + } + else{ + if(ether->ntb == 0) + break; + tb = ðer->tb[ether->ti]; + if(tb->owner != Interface) + break; + bp = allocb(tb->len); + memmove(bp->wp, tb->pkt, tb->len); + memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen); + bp->wp += tb->len; + + tb->owner = Host; + ether->ti = NEXT(ether->ti, ether->ntb); + + control = Ic|Lseg|Fseg|BLEN(bp); + } + + ctlr->tdr[PREV(ctlr->tdrh, ctlr->ntdr)].control &= ~Ic; + des = &ctlr->tdr[ctlr->tdrh]; + des->bp = bp; + des->addr = PADDR(bp->rp); + des->control |= control; + ctlr->ntq++; + //coherence(); + des->status = Own; + csr32w(ctlr, 1, 0); + ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr); + } +} + +static void +interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *ether; + int len, status; + Des *des; + RingBuf *ring; + + ether = arg; + ctlr = ether->ctlr; + + while((status = csr32r(ctlr, 5)) & (Nis|Ais)){ + /* + * Acknowledge the interrupts and mask-out + * the ones that are implicitly handled. + */ + csr32w(ctlr, 5, status); + status &= (ctlr->mask & ~(Nis|Ais|Ti)); + + /* + * Received packets. + */ + if(status & Ri){ + des = &ctlr->rdr[ctlr->rdrx]; + while((des->status & Own) == 0){ + len = ((des->status & Fl)>>16)-4; + if(des->status & Es){ + if(des->status & Of) + ctlr->of++; + if(des->status & Ce) + ctlr->ce++; + if(des->status & Cs) + ctlr->cs++; + if(des->status & Tl) + ctlr->tl++; + if(des->status & Rf) + ctlr->rf++; + if(des->status & De) + ctlr->de++; + } + else{ + ring = ðer->rb[ether->ri]; + if(ring->owner == Interface){ + ring->owner = Host; + ring->len = len; + memmove(ring->pkt, des->bp, len); + ether->ri = NEXT(ether->ri, ether->nrb); + } + } + + des->control &= Er; + des->control |= Rbsz; + des->status = Own; + + ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr); + des = &ctlr->rdr[ctlr->rdrx]; + } + status &= ~Ri; + } + + /* + * Check the transmit side: + * check for Transmit Underflow and Adjust + * the threshold upwards; + * free any transmitted buffers and try to + * top-up the ring. + */ + if(status & Unf){ + csr32w(ctlr, 6, ctlr->csr6 & ~St); + switch(ctlr->csr6 & Tr){ + case Tr128: + len = Tr256; + break; + case Tr256: + len = Tr512; + break; + case Tr512: + len = Tr1024; + break; + default: + case Tr1024: + len = Sf; + break; + } + ctlr->csr6 = (ctlr->csr6 & ~Tr)|len; + csr32w(ctlr, 6, ctlr->csr6); + csr32w(ctlr, 5, Tps); + status &= ~(Unf|Tps); + } + + while(ctlr->ntq){ + des = &ctlr->tdr[ctlr->tdri]; + if(des->status & Own) + break; + + if(des->status & Es){ + if(des->status & Uf) + ctlr->uf++; + if(des->status & Ec) + ctlr->ec++; + if(des->status & Lc) + ctlr->lc++; + if(des->status & Nc) + ctlr->nc++; + if(des->status & Lo) + ctlr->lo++; + if(des->status & To) + ctlr->to++; + } + + freeb(des->bp); + des->control &= Er; + + ctlr->ntq--; + ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr); + } + transmit(ether); + + /* + * Anything left not catered for? + */ + if(status) + panic("#l%d: status %8.8uX\n", ether->ctlrno, status); + } +} + +static void +ctlrinit(Ether* ether) +{ + Ctlr *ctlr; + Des *des; + Block *bp; + int i; + uchar bi[Eaddrlen*2]; + + ctlr = ether->ctlr; + + /* + * Allocate and initialise the receive ring; + * allocate and initialise the transmit ring; + * unmask interrupts and start the transmit side; + * create and post a setup packet to initialise + * the physical ethernet address. + */ + ctlr->rdr = malloc(ctlr->nrdr*sizeof(Des)); + for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){ + des->bp = malloc(Rbsz); + des->status = Own; + des->control = Rbsz; + des->addr = PADDR(des->bp); + } + ctlr->rdr[ctlr->nrdr-1].control |= Er; + ctlr->rdrx = 0; + csr32w(ctlr, 3, PADDR(ctlr->rdr)); + + ctlr->tdr = ialloc(ctlr->ntdr*sizeof(Des), 32); + ctlr->tdr[ctlr->ntdr-1].control |= Er; + ctlr->tdrh = 0; + ctlr->tdri = 0; + csr32w(ctlr, 4, PADDR(ctlr->tdr)); + + /* + * Clear any bits in the Status Register (CSR5) as + * the PNIC has a different reset value from a true 2114x. + */ + ctlr->mask = Nis|Ais|Fbe|Rwt|Rps|Ru|Ri|Unf|Tjt|Tps|Ti; + csr32w(ctlr, 5, ctlr->mask); + csr32w(ctlr, 7, ctlr->mask); + ctlr->csr6 |= St; + csr32w(ctlr, 6, ctlr->csr6); + + for(i = 0; i < Eaddrlen/2; i++){ + bi[i*4] = ether->ea[i*2]; + bi[i*4+1] = ether->ea[i*2+1]; + bi[i*4+2] = ether->ea[i*2+1]; + bi[i*4+3] = ether->ea[i*2]; + } + bp = allocb(Eaddrlen*2*16); + memset(bp->rp, 0xFF, sizeof(bi)); + for(i = sizeof(bi); i < sizeof(bi)*16; i += sizeof(bi)) + memmove(bp->rp+i, bi, sizeof(bi)); + bp->wp += sizeof(bi)*16; + + ctlr->setupbp = bp; + transmit(ether); +} + +static void +csr9w(Ctlr* ctlr, int data) +{ + csr32w(ctlr, 9, data); + microdelay(1); +} + +static int +miimdi(Ctlr* ctlr, int n) +{ + int data, i; + + /* + * Read n bits from the MII Management Register. + */ + data = 0; + for(i = n-1; i >= 0; i--){ + if(csr32r(ctlr, 9) & Mdi) + data |= (1<= 0; i--){ + if(bits & (1<id == Pnic){ + i = 1000; + csr32w(ctlr, 20, 0x60020000|(phyad<<23)|(regad<<18)); + do{ + microdelay(1); + data = csr32r(ctlr, 20); + }while((data & 0x80000000) && --i); + + if(i == 0) + return -1; + return data & 0xFFFF; + } + + /* + * Preamble; + * ST+OP+PHYAD+REGAD; + * TA + 16 data bits. + */ + miimdo(ctlr, 0xFFFFFFFF, 32); + miimdo(ctlr, 0x1800|(phyad<<5)|regad, 14); + data = miimdi(ctlr, 18); + + if(data & 0x10000) + return -1; + + return data & 0xFFFF; +} + +static void +miiw(Ctlr* ctlr, int phyad, int regad, int data) +{ + /* + * Preamble; + * ST+OP+PHYAD+REGAD+TA + 16 data bits; + * Z. + */ + miimdo(ctlr, 0xFFFFFFFF, 32); + data &= 0xFFFF; + data |= (0x05<<(5+5+2+16))|(phyad<<(5+2+16))|(regad<<(2+16))|(0x02<<16); + miimdo(ctlr, data, 32); + csr9w(ctlr, Mdc); + csr9w(ctlr, 0); +} + +static int +sromr(Ctlr* ctlr, int r) +{ + int i, op, data, size; + + if(ctlr->id == Pnic){ + i = 1000; + csr32w(ctlr, 19, 0x600|r); + do{ + microdelay(1); + data = csr32r(ctlr, 19); + }while((data & 0x80000000) && --i); + + if(ctlr->sromsz == 0) + ctlr->sromsz = 6; + + return csr32r(ctlr, 9) & 0xFFFF; + } + + /* + * This sequence for reading a 16-bit register 'r' + * in the EEPROM is taken (pretty much) straight from Section + * 7.4 of the 21140 Hardware Reference Manual. + */ +reread: + csr9w(ctlr, Rd|Ss); + csr9w(ctlr, Rd|Ss|Scs); + csr9w(ctlr, Rd|Ss|Sclk|Scs); + csr9w(ctlr, Rd|Ss); + + op = 0x06; + for(i = 3-1; i >= 0; i--){ + data = Rd|Ss|(((op>>i) & 0x01)<<2)|Scs; + csr9w(ctlr, data); + csr9w(ctlr, data|Sclk); + csr9w(ctlr, data); + } + + /* + * First time through must work out the EEPROM size. + * This doesn't seem to work on the 21041 as implemented + * in Virtual PC for the Mac, so wire any 21041 to 6, + * it's the only 21041 this code will ever likely see. + */ + if((size = ctlr->sromsz) == 0){ + if(ctlr->id == Tulip1) + ctlr->sromsz = size = 6; + else + size = 8; + } + + for(size = size-1; size >= 0; size--){ + data = Rd|Ss|(((r>>size) & 0x01)<<2)|Scs; + csr9w(ctlr, data); + csr9w(ctlr, data|Sclk); + csr9w(ctlr, data); + microdelay(1); + if(ctlr->sromsz == 0 && !(csr32r(ctlr, 9) & Sdo)) + break; + } + + data = 0; + for(i = 16-1; i >= 0; i--){ + csr9w(ctlr, Rd|Ss|Sclk|Scs); + if(csr32r(ctlr, 9) & Sdo) + data |= (1<sromsz == 0){ + ctlr->sromsz = 8-size; + goto reread; + } + + return data & 0xFFFF; +} + +static void +softreset(Ctlr* ctlr) +{ + /* + * Soft-reset the controller and initialise bus mode. + * Delay should be >= 50 PCI cycles (2×S @ 25MHz). + */ + csr32w(ctlr, 0, Swr); + microdelay(10); + csr32w(ctlr, 0, Rml|Cal16); + delay(1); +} + +static int +type5block(Ctlr* ctlr, uchar* block) +{ + int csr15, i, len; + + /* + * Reset or GPR sequence. Reset should be once only, + * before the GPR sequence. + * Note 'block' is not a pointer to the block head but + * a pointer to the data in the block starting at the + * reset length value so type5block can be used for the + * sequences contained in type 1 and type 3 blocks. + * The SROM docs state the 21140 type 5 block is the + * same as that for the 21143, but the two controllers + * use different registers and sequence-element lengths + * so the 21140 code here is a guess for a real type 5 + * sequence. + */ + len = *block++; + if(ctlr->id != Tulip3){ + for(i = 0; i < len; i++){ + csr32w(ctlr, 12, *block); + block++; + } + return len; + } + + for(i = 0; i < len; i++){ + csr15 = *block++<<16; + csr15 |= *block++<<24; + csr32w(ctlr, 15, csr15); + debug("%8.8uX ", csr15); + } + return 2*len; +} + +static int +typephylink(Ctlr* ctlr, uchar*) +{ + int an, bmcr, bmsr, csr6, x; + + /* + * Fail if + * auto-negotiataion enabled but not complete; + * no valid link established. + */ + bmcr = miir(ctlr, ctlr->curphyad, Bmcr); + miir(ctlr, ctlr->curphyad, Bmsr); + bmsr = miir(ctlr, ctlr->curphyad, Bmsr); + debug("bmcr 0x%2.2uX bmsr 0x%2.2uX\n", bmcr, bmsr); + if(((bmcr & 0x1000) && !(bmsr & 0x0020)) || !(bmsr & 0x0004)) + return 0; + + if(bmcr & 0x1000){ + an = miir(ctlr, ctlr->curphyad, Anar); + an &= miir(ctlr, ctlr->curphyad, Anlpar) & 0x3E0; + debug("an 0x%2.uX 0x%2.2uX 0x%2.2uX\n", + miir(ctlr, ctlr->curphyad, Anar), + miir(ctlr, ctlr->curphyad, Anlpar), + an); + + if(an & 0x0100) + x = 0x4000; + else if(an & 0x0080) + x = 0x2000; + else if(an & 0x0040) + x = 0x1000; + else if(an & 0x0020) + x = 0x0800; + else + x = 0; + } + else if((bmcr & 0x2100) == 0x2100) + x = 0x4000; + else if(bmcr & 0x2000){ + /* + * If FD capable, force it if necessary. + */ + if((bmsr & 0x4000) && ctlr->fd){ + miiw(ctlr, ctlr->curphyad, Bmcr, 0x2100); + x = 0x4000; + } + else + x = 0x2000; + } + else if(bmcr & 0x0100) + x = 0x1000; + else + x = 0x0800; + + csr6 = Sc|Mbo|Hbd|Ps|Ca|TrMODE|Sb; + if(ctlr->fdx & x) + csr6 |= Fd; + if(ctlr->ttm & x) + csr6 |= Ttm; + debug("csr6 0x%8.8uX 0x%8.8uX 0x%8.8luX\n", + csr6, ctlr->csr6, csr32r(ctlr, 6)); + if(csr6 != ctlr->csr6){ + ctlr->csr6 = csr6; + csr32w(ctlr, 6, csr6); + } + + return 1; +} + +static int +typephymode(Ctlr* ctlr, uchar* block, int wait) +{ + uchar *p; + int len, mc, nway, phyx, timeo; + + if(DEBUG){ + int i; + + len = (block[0] & ~0x80)+1; + for(i = 0; i < len; i++) + debug("%2.2uX ", block[i]); + debug("\n"); + } + + if(block[1] == 1) + len = 1; + else if(block[1] == 3) + len = 2; + else + return -1; + + /* + * Snarf the media capabilities, nway advertisment, + * FDX and TTM bitmaps. + */ + p = &block[5+len*block[3]+len*block[4+len*block[3]]]; + mc = *p++; + mc |= *p++<<8; + nway = *p++; + nway |= *p++<<8; + ctlr->fdx = *p++; + ctlr->fdx |= *p++<<8; + ctlr->ttm = *p++; + ctlr->ttm |= *p<<8; + debug("mc %4.4uX nway %4.4uX fdx %4.4uX ttm %4.4uX\n", + mc, nway, ctlr->fdx, ctlr->ttm); + USED(mc); + + phyx = block[2]; + ctlr->curphyad = ctlr->phy[phyx]; + + ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|TrMODE|Sb; + //csr32w(ctlr, 6, ctlr->csr6); + if(typephylink(ctlr, block)) + return 0; + + if(!(ctlr->phyreset & (1<type5block) + type5block(ctlr, &ctlr->type5block[2]); + else + type5block(ctlr, &block[4+len*block[3]]); + debug("\n"); + ctlr->phyreset |= (1<csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|TrMODE|Sb; + //csr32w(ctlr, 6, ctlr->csr6); + if(typephylink(ctlr, block)) + return 0; + + /* + * Turn off auto-negotiation, set the auto-negotiation + * advertisment register then start the auto-negotiation + * process again. + */ + miiw(ctlr, ctlr->curphyad, Bmcr, 0); + miiw(ctlr, ctlr->curphyad, Anar, nway|1); + miiw(ctlr, ctlr->curphyad, Bmcr, 0x1000); + + if(!wait) + return 0; + + for(timeo = 0; timeo < 30; timeo++){ + if(typephylink(ctlr, block)) + return 0; + delay(100); + } + + return -1; +} + +static int +typesymmode(Ctlr *ctlr, uchar *block, int wait) +{ + uint gpmode, gpdata, command; + + USED(wait); + gpmode = block[3] | ((uint) block[4] << 8); + gpdata = block[5] | ((uint) block[6] << 8); + command = (block[7] | ((uint) block[8] << 8)) & 0x71; + if (command & 0x8000) { + print("ether2114x.c: FIXME: handle type 4 mode blocks where cmd.active_invalid != 0\n"); + return -1; + } + csr32w(ctlr, 15, gpmode); + csr32w(ctlr, 15, gpdata); + ctlr->csr6 = (command & 0x71) << 18; + csr32w(ctlr, 6, ctlr->csr6); + return 0; +} + +static int +type2mode(Ctlr* ctlr, uchar* block, int) +{ + uchar *p; + int csr6, csr13, csr14, csr15, gpc, gpd; + + csr6 = Sc|Mbo|Ca|TrMODE|Sb; + debug("type2mode: medium 0x%2.2uX\n", block[2]); + + /* + * Don't attempt full-duplex + * unless explicitly requested. + */ + if((block[2] & 0x3F) == 0x04){ /* 10BASE-TFD */ + if(!ctlr->fd) + return -1; + csr6 |= Fd; + } + + /* + * Operating mode programming values from the datasheet + * unless media specific data is explicitly given. + */ + p = &block[3]; + if(block[2] & 0x40){ + csr13 = (block[4]<<8)|block[3]; + csr14 = (block[6]<<8)|block[5]; + csr15 = (block[8]<<8)|block[7]; + p += 6; + } + else switch(block[2] & 0x3F){ + default: + return -1; + case 0x00: /* 10BASE-T */ + csr13 = 0x00000001; + csr14 = 0x00007F3F; + csr15 = 0x00000008; + break; + case 0x01: /* 10BASE-2 */ + csr13 = 0x00000009; + csr14 = 0x00000705; + csr15 = 0x00000006; + break; + case 0x02: /* 10BASE-5 (AUI) */ + csr13 = 0x00000009; + csr14 = 0x00000705; + csr15 = 0x0000000E; + break; + case 0x04: /* 10BASE-TFD */ + csr13 = 0x00000001; + csr14 = 0x00007F3D; + csr15 = 0x00000008; + break; + } + gpc = *p++<<16; + gpc |= *p++<<24; + gpd = *p++<<16; + gpd |= *p<<24; + + csr32w(ctlr, 13, 0); + csr32w(ctlr, 14, csr14); + csr32w(ctlr, 15, gpc|csr15); + delay(10); + csr32w(ctlr, 15, gpd|csr15); + csr32w(ctlr, 13, csr13); + + ctlr->csr6 = csr6; + csr32w(ctlr, 6, ctlr->csr6); + + debug("type2mode: csr13 %8.8uX csr14 %8.8uX csr15 %8.8uX\n", + csr13, csr14, csr15); + debug("type2mode: gpc %8.8uX gpd %8.8uX csr6 %8.8uX\n", + gpc, gpd, csr6); + + return 0; +} + +static int +type0link(Ctlr* ctlr, uchar* block) +{ + int m, polarity, sense; + + m = (block[3]<<8)|block[2]; + sense = 1<<((m & 0x000E)>>1); + if(m & 0x0080) + polarity = sense; + else + polarity = 0; + + return (csr32r(ctlr, 12) & sense)^polarity; +} + +static int +type0mode(Ctlr* ctlr, uchar* block, int wait) +{ + int csr6, m, timeo; + + csr6 = Sc|Mbo|Hbd|Ca|TrMODE|Sb; +debug("type0: medium 0x%uX, fd %d: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n", + ctlr->medium, ctlr->fd, block[0], block[1], block[2], block[3]); + switch(block[0]){ + default: + break; + + case 0x04: /* 10BASE-TFD */ + case 0x05: /* 100BASE-TXFD */ + case 0x08: /* 100BASE-FXFD */ + /* + * Don't attempt full-duplex + * unless explicitly requested. + */ + if(!ctlr->fd) + return -1; + csr6 |= Fd; + break; + } + + m = (block[3]<<8)|block[2]; + if(m & 0x0001) + csr6 |= Ps; + if(m & 0x0010) + csr6 |= Ttm; + if(m & 0x0020) + csr6 |= Pcs; + if(m & 0x0040) + csr6 |= Scr; + + csr32w(ctlr, 12, block[1]); + microdelay(10); + csr32w(ctlr, 6, csr6); + ctlr->csr6 = csr6; + + if(!wait) + return 0; + + for(timeo = 0; timeo < 30; timeo++){ + if(type0link(ctlr, block)) + return 0; + delay(100); + } + + return -1; +} + +static int +media21041(Ether* ether, int wait) +{ + Ctlr* ctlr; + uchar *block; + int csr6, csr13, csr14, csr15, medium, timeo; + + ctlr = ether->ctlr; + block = ctlr->infoblock[ctlr->curk]; + debug("media21041: block[0] %2.2uX, medium %4.4uX sct %4.4uX\n", + block[0], ctlr->medium, ctlr->sct); + + medium = block[0] & 0x3F; + if(ctlr->medium >= 0 && medium != ctlr->medium) + return 0; + if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != medium) + return 0; + + csr6 = Sc|Mbo|Ca|TrMODE|Sb; + if(block[0] & 0x40){ + csr13 = (block[2]<<8)|block[1]; + csr14 = (block[4]<<8)|block[3]; + csr15 = (block[6]<<8)|block[5]; + } + else switch(medium){ + default: + return -1; + case 0x00: /* 10BASE-T */ + csr13 = 0xEF01; + csr14 = 0xFF3F; + csr15 = 0x0008; + break; + case 0x01: /* 10BASE-2 */ + csr13 = 0xEF09; + csr14 = 0xF73D; + csr15 = 0x0006; + break; + case 0x02: /* 10BASE-5 */ + csr13 = 0xEF09; + csr14 = 0xF73D; + csr15 = 0x000E; + break; + case 0x04: /* 10BASE-TFD */ + csr13 = 0xEF01; + csr14 = 0xFF3D; + csr15 = 0x0008; + break; + } + + csr32w(ctlr, 13, 0); + csr32w(ctlr, 14, csr14); + csr32w(ctlr, 15, csr15); + csr32w(ctlr, 13, csr13); + delay(10); + + if(medium == 0x04) + csr6 |= Fd; + ctlr->csr6 = csr6; + csr32w(ctlr, 6, ctlr->csr6); + + debug("media21041: csr6 %8.8uX csr13 %4.4uX csr14 %4.4uX csr15 %4.4uX\n", + csr6, csr13, csr14, csr15); + + if(!wait) + return 0; + + for(timeo = 0; timeo < 30; timeo++){ + if(!(csr32r(ctlr, 12) & 0x0002)){ + debug("media21041: ok: csr12 %4.4luX timeo %d\n", + csr32r(ctlr, 12), timeo); + return 10; + } + delay(100); + } + debug("media21041: !ok: csr12 %4.4luX\n", csr32r(ctlr, 12)); + + return -1; +} + +static int +mediaxx(Ether* ether, int wait) +{ + Ctlr* ctlr; + uchar *block; + + ctlr = ether->ctlr; + block = ctlr->infoblock[ctlr->curk]; + if(block[0] & 0x80){ + switch(block[1]){ + default: + return -1; + case 0: + if(ctlr->medium >= 0 && block[2] != ctlr->medium) + return 0; +/* need this test? */ if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[2]) + return 0; + if(type0mode(ctlr, block+2, wait)) + return 0; + break; + case 1: + if(typephymode(ctlr, block, wait)) + return 0; + break; + case 2: + debug("type2: medium %d block[2] %d\n", + ctlr->medium, block[2]); + if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium)) + return 0; + if(type2mode(ctlr, block, wait)) + return 0; + break; + case 3: + if(typephymode(ctlr, block, wait)) + return 0; + break; + case 4: + debug("type4: medium %d block[2] %d\n", + ctlr->medium, block[2]); + if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium)) + return 0; + if(typesymmode(ctlr, block, wait)) + return 0; + break; + } + } + else{ + if(ctlr->medium >= 0 && block[0] != ctlr->medium) + return 0; +/* need this test? */if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[0]) + return 0; + if(type0mode(ctlr, block, wait)) + return 0; + } + + if(ctlr->csr6){ + if(!(ctlr->csr6 & Ps) || (ctlr->csr6 & Ttm)) + return 10; + return 100; + } + + return 0; +} + +static int +media(Ether* ether, int wait) +{ + Ctlr* ctlr; + int k, mbps; + + ctlr = ether->ctlr; + for(k = 0; k < ctlr->k; k++){ + switch(ctlr->id){ + default: + mbps = mediaxx(ether, wait); + break; + case Tulip1: /* 21041 */ + mbps = media21041(ether, wait); + break; + } + if(mbps > 0) + return mbps; + if(ctlr->curk == 0) + ctlr->curk = ctlr->k-1; + else + ctlr->curk--; + } + + return 0; +} + +static char* mediatable[9] = { + "10BASE-T", /* TP */ + "10BASE-2", /* BNC */ + "10BASE-5", /* AUI */ + "100BASE-TX", + "10BASE-TFD", + "100BASE-TXFD", + "100BASE-T4", + "100BASE-FX", + "100BASE-FXFD", +}; + +static uchar en1207[] = { /* Accton EN1207-COMBO */ + 0x00, 0x00, 0xE8, /* [0] vendor ethernet code */ + 0x00, /* [3] spare */ + + 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */ + 0x1F, /* [6] general purpose control */ + 2, /* [7] block count */ + + 0x00, /* [8] media code (10BASE-TX) */ + 0x0B, /* [9] general purpose port data */ + 0x9E, 0x00, /* [10] command (LSB+MSB = 0x009E) */ + + 0x03, /* [8] media code (100BASE-TX) */ + 0x1B, /* [9] general purpose port data */ + 0x6D, 0x00, /* [10] command (LSB+MSB = 0x006D) */ + + /* There is 10BASE-2 as well, but... */ +}; + +static uchar ana6910fx[] = { /* Adaptec (Cogent) ANA-6910FX */ + 0x00, 0x00, 0x92, /* [0] vendor ethernet code */ + 0x00, /* [3] spare */ + + 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */ + 0x3F, /* [6] general purpose control */ + 1, /* [7] block count */ + + 0x07, /* [8] media code (100BASE-FX) */ + 0x03, /* [9] general purpose port data */ + 0x2D, 0x00 /* [10] command (LSB+MSB = 0x000D) */ +}; + +static uchar smc9332[] = { /* SMC 9332 */ + 0x00, 0x00, 0xC0, /* [0] vendor ethernet code */ + 0x00, /* [3] spare */ + + 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */ + 0x1F, /* [6] general purpose control */ + 2, /* [7] block count */ + + 0x00, /* [8] media code (10BASE-TX) */ + 0x00, /* [9] general purpose port data */ + 0x9E, 0x00, /* [10] command (LSB+MSB = 0x009E) */ + + 0x03, /* [8] media code (100BASE-TX) */ + 0x09, /* [9] general purpose port data */ + 0x6D, 0x00, /* [10] command (LSB+MSB = 0x006D) */ +}; + +static uchar* leaf21140[] = { + en1207, /* Accton EN1207-COMBO */ + ana6910fx, /* Adaptec (Cogent) ANA-6910FX */ + smc9332, /* SMC 9332 */ + 0, +}; + +/* + * Copied to ctlr->srom at offset 20. + */ +static uchar leafpnic[] = { + 0x00, 0x00, 0x00, 0x00, /* MAC address */ + 0x00, 0x00, + 0x00, /* controller 0 device number */ + 0x1E, 0x00, /* controller 0 info leaf offset */ + 0x00, /* reserved */ + 0x00, 0x08, /* selected connection type */ + 0x00, /* general purpose control */ + 0x01, /* block count */ + + 0x8C, /* format indicator and count */ + 0x01, /* block type */ + 0x00, /* PHY number */ + 0x00, /* GPR sequence length */ + 0x00, /* reset sequence length */ + 0x00, 0x78, /* media capabilities */ + 0xE0, 0x01, /* Nway advertisment */ + 0x00, 0x50, /* FDX bitmap */ + 0x00, 0x18, /* TTM bitmap */ +}; + +static int +srom(Ctlr* ctlr) +{ + int i, k, oui, phy, x; + uchar *p; + + /* + * This is a partial decoding of the SROM format described in + * 'Digital Semiconductor 21X4 Serial ROM Format, Version 4.05, + * 2-Mar-98'. Only the 2114[03] are handled, support for other + * controllers can be added as needed. + */ + sromr(ctlr, 0); + if(ctlr->srom == nil) + ctlr->srom = malloc((1<sromsz)*sizeof(ushort)); + for(i = 0; i < (1<sromsz); i++){ + x = sromr(ctlr, i); + ctlr->srom[2*i] = x; + ctlr->srom[2*i+1] = x>>8; + } + + if(DEBUG){ + print("srom:"); + for(i = 0; i < ((1<sromsz)*sizeof(ushort)); i++){ + if(i && ((i & 0x0F) == 0)) + print("\n "); + print(" %2.2uX", ctlr->srom[i]); + } + print("\n"); + } + + /* + * There are 2 SROM layouts: + * e.g. Digital EtherWORKS station address at offset 20; + * this complies with the 21140A SROM + * application note from Digital; + * e.g. SMC9332 station address at offset 0 followed by + * 2 additional bytes, repeated at offset + * 6; the 8 bytes are also repeated in + * reverse order at offset 8. + * To check which it is, read the SROM and check for the repeating + * patterns of the non-compliant cards; if that fails use the one at + * offset 20. + */ + ctlr->sromea = ctlr->srom; + for(i = 0; i < 8; i++){ + x = ctlr->srom[i]; + if(x != ctlr->srom[15-i] || x != ctlr->srom[16+i]){ + ctlr->sromea = &ctlr->srom[20]; + break; + } + } + + /* + * Fake up the SROM for the PNIC. + * It looks like a 21140 with a PHY. + * The MAC address is byte-swapped in the orginal SROM data. + */ + if(ctlr->id == Pnic){ + memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic)); + for(i = 0; i < Eaddrlen; i += 2){ + ctlr->srom[20+i] = ctlr->srom[i+1]; + ctlr->srom[20+i+1] = ctlr->srom[i]; + } + } + + /* + * Next, try to find the info leaf in the SROM for media detection. + * If it's a non-conforming card try to match the vendor ethernet code + * and point p at a fake info leaf with compact 21140 entries. + */ + if(ctlr->sromea == ctlr->srom){ + p = nil; + for(i = 0; leaf21140[i] != nil; i++){ + if(memcmp(leaf21140[i], ctlr->sromea, 3) == 0){ + p = &leaf21140[i][4]; + break; + } + } + if(p == nil) + return -1; + } + else + p = &ctlr->srom[(ctlr->srom[28]<<8)|ctlr->srom[27]]; + + /* + * Set up the info needed for later media detection. + * For the 21140, set the general-purpose mask in CSR12. + * The info block entries are stored in order of increasing + * precedence, so detection will work backwards through the + * stored indexes into ctlr->srom. + * If an entry is found which matches the selected connection + * type, save the index. Otherwise, start at the last entry. + * If any MII entries are found (type 1 and 3 blocks), scan + * for PHYs. + */ + ctlr->leaf = p; + ctlr->sct = *p++; + ctlr->sct |= *p++<<8; + if(ctlr->id != Tulip3 && ctlr->id != Tulip1){ + csr32w(ctlr, 12, Gpc|*p++); + delay(200); + } + ctlr->k = *p++; + if(ctlr->k >= nelem(ctlr->infoblock)) + ctlr->k = nelem(ctlr->infoblock)-1; + ctlr->sctk = ctlr->k-1; + phy = 0; + for(k = 0; k < ctlr->k; k++){ + ctlr->infoblock[k] = p; + if(ctlr->id == Tulip1){ + debug("type21041: 0x%2.2uX\n", p[0]); + if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF)) + ctlr->sctk = k; + if(*p & 0x40) + p += 7; + else + p += 1; + } + /* + * The RAMIX PMC665 has a badly-coded SROM, + * hence the test for 21143 and type 3. + */ + else if((*p & 0x80) || (ctlr->id == Tulip3 && *(p+1) == 3)){ + *p |= 0x80; + if(*(p+1) == 1 || *(p+1) == 3) + phy = 1; + if(*(p+1) == 5) + ctlr->type5block = p; + p += (*p & ~0x80)+1; + } + else{ + debug("type0: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n", + p[0], p[1], p[2], p[3]); + if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF)) + ctlr->sctk = k; + p += 4; + } + } + ctlr->curk = ctlr->sctk; + debug("sct 0x%uX medium 0x%uX k %d curk %d phy %d\n", + ctlr->sct, ctlr->medium, ctlr->k, ctlr->curk, phy); + + if(phy){ + x = 0; + for(k = 0; k < nelem(ctlr->phy); k++){ + if((oui = miir(ctlr, k, 2)) == -1 || oui == 0) + continue; + if(DEBUG){ + oui = (oui & 0x3FF)<<6; + oui |= miir(ctlr, k, 3)>>10; + miir(ctlr, k, 1); + debug("phy%d: index %d oui %uX reg1 %uX\n", + x, k, oui, miir(ctlr, k, 1)); + USED(oui); + } + ctlr->phy[x] = k; + } + } + + ctlr->fd = 0; + ctlr->medium = -1; + + return 0; +} + +static void +dec2114xpci(void) +{ + Ctlr *ctlr; + Pcidev *p; + int x; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + switch((p->did<<16)|p->vid){ + default: + continue; + + case Tulip3: /* 21143 */ + /* + * Exit sleep mode. + */ + x = pcicfgr32(p, 0x40); + x &= ~0xC0000000; + pcicfgw32(p, 0x40, x); + /*FALLTHROUGH*/ + + case Pnic: /* PNIC */ + case Pnic2: /* PNIC-II */ + case Tulip0: /* 21140 */ + case Tulip1: /* 21041 */ + break; + } + + /* + * bar[0] is the I/O port register address and + * bar[1] is the memory-mapped register address. + */ + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = p->mem[0].bar & ~0x01; + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + debug("2114x: type 0x%8.8uX rev 0x%4.4uX at port 0x%4.4uX\n", + ctlr->id, p->rid, ctlr->port); + + /* + * Some cards (e.g. ANA-6910FX) seem to need the Ps bit + * set or they don't always work right after a hardware + * reset. + */ + csr32w(ctlr, 6, Mbo|Ps); + softreset(ctlr); + + if(srom(ctlr)){ + free(ctlr); + break; + } + + switch(ctlr->id){ + default: + break; + + case Pnic: /* PNIC */ + /* + * Turn off the jabber timer. + */ + csr32w(ctlr, 15, 0x00000001); + break; + } + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + +static void +detach(Ether* ether) +{ + softreset(ether->ctlr); +} + +int +ether2114xreset(Ether* ether) +{ + Ctlr *ctlr; + int i, x; + uchar ea[Eaddrlen]; + static int scandone; + + if(scandone == 0){ + dec2114xpci(); + scandone = 1; + } + + /* + * Any adapter matches if no ether->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(ether->port == 0 || ether->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + ether->ctlr = ctlr; + ether->port = ctlr->port; + ether->irq = ctlr->pcidev->intl; + ether->tbdf = ctlr->pcidev->tbdf; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to + * loading the station address in the hardware. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0) + memmove(ether->ea, ctlr->sromea, Eaddrlen); + + /* + * Look for a medium override in case there's no autonegotiation + * (no MII) or the autonegotiation fails. + */ + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "FD") == 0){ + ctlr->fd = 1; + continue; + } + for(x = 0; x < nelem(mediatable); x++){ + debug("compare <%s> <%s>\n", mediatable[x], + ether->opt[i]); + if(cistrcmp(mediatable[x], ether->opt[i])) + continue; + ctlr->medium = x; + + switch(ctlr->medium){ + default: + ctlr->fd = 0; + break; + + case 0x04: /* 10BASE-TFD */ + case 0x05: /* 100BASE-TXFD */ + case 0x08: /* 100BASE-FXFD */ + ctlr->fd = 1; + break; + } + break; + } + } + + /* + * Determine media. + */ + ctlr->mbps = media(ether, 1); + + /* + * Initialise descriptor rings, ethernet address. + */ + ctlr->nrdr = Nrde; + ctlr->ntdr = Ntde; + pcisetbme(ctlr->pcidev); + ctlrinit(ether); + + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->detach = detach; + + return 0; +} diff --git a/os/boot/pc/ether589.c b/os/boot/pc/ether589.c new file mode 100644 index 00000000..fc86f463 --- /dev/null +++ b/os/boot/pc/ether589.c @@ -0,0 +1,211 @@ +/* + * 3C589 and 3C562. + * To do: + * check xcvr10Base2 still works (is GlobalReset necessary?). + */ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "error.h" + +#include "etherif.h" + +enum { /* all windows */ + CommandR = 0x000E, + IntStatusR = 0x000E, +}; + +enum { /* Commands */ + GlobalReset = 0x0000, + SelectRegisterWindow = 0x0001, + RxReset = 0x0005, + TxReset = 0x000B, + AcknowledgeInterrupt = 0x000D, +}; + +enum { /* IntStatus bits */ + commandInProgress = 0x1000, +}; + +#define COMMAND(port, cmd, a) outs((port)+CommandR, ((cmd)<<11)|(a)) +#define STATUS(port) ins((port)+IntStatusR) + +enum { /* Window 0 - setup */ + Wsetup = 0x0000, + /* registers */ + ManufacturerID = 0x0000, /* 3C5[08]*, 3C59[27] */ + ProductID = 0x0002, /* 3C5[08]*, 3C59[27] */ + ConfigControl = 0x0004, /* 3C5[08]*, 3C59[27] */ + AddressConfig = 0x0006, /* 3C5[08]*, 3C59[27] */ + ResourceConfig = 0x0008, /* 3C5[08]*, 3C59[27] */ + EepromCommand = 0x000A, + EepromData = 0x000C, + /* AddressConfig Bits */ + autoSelect9 = 0x0080, + xcvrMask9 = 0xC000, + /* ConfigControl bits */ + Ena = 0x0001, + base10TAvailable9 = 0x0200, + coaxAvailable9 = 0x1000, + auiAvailable9 = 0x2000, + /* EepromCommand bits */ + EepromReadRegister = 0x0080, + EepromBusy = 0x8000, +}; + +enum { /* Window 1 - operating set */ + Wop = 0x0001, +}; + +enum { /* Window 3 - FIFO management */ + Wfifo = 0x0003, + /* registers */ + InternalConfig = 0x0000, /* 3C509B, 3C589, 3C59[0257] */ + /* InternalConfig bits */ + xcvr10BaseT = 0x00000000, + xcvr10Base2 = 0x00300000, +}; + +enum { /* Window 4 - diagnostic */ + Wdiagnostic = 0x0004, + /* registers */ + MediaStatus = 0x000A, + /* MediaStatus bits */ + linkBeatDetect = 0x0800, +}; + +extern int elnk3reset(Ether*); + +static char *tcmpcmcia[] = { + "3C589", /* 3COM 589[ABCD] */ + "3C562", /* 3COM 562 */ + "589E", /* 3COM Megahertz 589E */ + nil, +}; + +static int +configASIC(Ether* ether, int port, int xcvr) +{ + int x; + + /* set Window 0 configuration registers */ + COMMAND(port, SelectRegisterWindow, Wsetup); + outs(port+ConfigControl, Ena); + + /* IRQ must be 3 on 3C589/3C562 */ + outs(port + ResourceConfig, 0x3F00); + + x = ins(port+AddressConfig) & ~xcvrMask9; + x |= (xcvr>>20)<<14; + outs(port+AddressConfig, x); + + COMMAND(port, TxReset, 0); + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, RxReset, 0); + while(STATUS(port) & commandInProgress) + ; + + return elnk3reset(ether); +} + +int +ether589reset(Ether* ether) +{ + int i, t, slot; + char *type; + int port; + enum { WantAny, Want10BT, Want10B2 }; + int want; + uchar ea[6]; + char *p; + + if(ether->irq == 0) + ether->irq = 10; + if(ether->port == 0) + ether->port = 0x240; + port = ether->port; + +// if(ioalloc(port, 0x10, 0, "3C589") < 0) +// return -1; + + type = nil; + slot = -1; + for(i = 0; tcmpcmcia[i] != nil; i++){ + type = tcmpcmcia[i]; +if(debug) print("try %s...", type); + if((slot = pcmspecial(type, ether)) >= 0) + break; + } + if(slot < 0){ +if(debug) print("none found\n"); +// iofree(port); + return -1; + } + + /* + * Read Ethernet address from card memory + * on 3C562, but only if the user has not + * overridden it. + */ + memset(ea, 0, sizeof ea); + if(memcmp(ea, ether->ea, 6) == 0 && strcmp(type, "3C562") == 0) { + if(debug) + print("read 562..."); + if(pcmcistuple(slot, 0x88, ea, 6) == 6) { + for(i = 0; i < 6; i += 2){ + t = ea[i]; + ea[i] = ea[i+1]; + ea[i+1] = t; + } + memmove(ether->ea, ea, 6); + if(debug) + print("ea %E", ea); + } + } + /* + * Allow user to specify desired media in plan9.ini + */ + want = WantAny; + for(i = 0; i < ether->nopt; i++){ + if(cistrncmp(ether->opt[i], "media=", 6) != 0) + continue; + p = ether->opt[i]+6; + if(cistrcmp(p, "10base2") == 0) + want = Want10B2; + else if(cistrcmp(p, "10baseT") == 0) + want = Want10BT; + } + + /* try configuring as a 10BaseT */ + if(want==WantAny || want==Want10BT){ + if(configASIC(ether, port, xcvr10BaseT) < 0){ + pcmspecialclose(slot); +// iofree(port); + return -1; + } + delay(100); + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + if((ins(port+MediaStatus)&linkBeatDetect) || want==Want10BT){ + COMMAND(port, SelectRegisterWindow, Wop); + print("#l%d: xcvr10BaseT %s\n", ether->ctlrno, type); + return 0; + } + } + + /* try configuring as a 10base2 */ + if(want==WantAny || want==Want10B2){ + COMMAND(port, GlobalReset, 0); + if(configASIC(ether, port, xcvr10Base2) < 0){ + pcmspecialclose(slot); +// iofree(port); + return -1; + } + print("#l%d: xcvr10Base2 %s\n", ether->ctlrno, type); + return 0; + } + return -1; /* not reached */ +} diff --git a/os/boot/pc/ether79c970.c b/os/boot/pc/ether79c970.c new file mode 100644 index 00000000..6a171b24 --- /dev/null +++ b/os/boot/pc/ether79c970.c @@ -0,0 +1,539 @@ +/* + * AMD79C970 + * PCnet-PCI Single-Chip Ethernet Controller for PCI Local Bus + * To do: + * finish this rewrite + */ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "etherif.h" + +enum { + Lognrdre = 6, + Nrdre = (1<= Rdp) + r = (r-Rdp)/2+Rdp; + return ins(c->port+r); +} + +static void +io16w(Ctlr* c, int r, int v) +{ + if(r >= Rdp) + r = (r-Rdp)/2+Rdp; + outs(c->port+r, v); +} + +static int +io32r(Ctlr* c, int r) +{ + return inl(c->port+r); +} + +static void +io32w(Ctlr* c, int r, int v) +{ + outl(c->port+r, v); +} + +static void +attach(Ether*) +{ +} + +static void +detach(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ctlr->iow(ctlr, Rdp, Iena|Stop); +} + +static void +ringinit(Ctlr* ctlr) +{ + Dre *dre; + + /* + * Initialise the receive and transmit buffer rings. + * The ring entries must be aligned on 16-byte boundaries. + * + * This routine is protected by ctlr->init. + */ + if(ctlr->rdr == 0){ + ctlr->rdr = ialloc(Nrdre*sizeof(Dre), 0x10); + for(dre = ctlr->rdr; dre < &ctlr->rdr[Nrdre]; dre++){ + dre->data = malloc(Rbsize); + dre->addr = PADDR(dre->data); + dre->md2 = 0; + dre->md1 = Own|(-Rbsize & 0xFFFF); + } + } + ctlr->rdrx = 0; + + if(ctlr->tdr == 0) + ctlr->tdr = ialloc(Ntdre*sizeof(Dre), 0x10); + memset(ctlr->tdr, 0, Ntdre*sizeof(Dre)); + ctlr->tdrh = ctlr->tdri = 0; +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + Block *bp; + Dre *dre; + RingBuf *tb; + + ctlr = ether->ctlr; + + if(ctlr->init) + return; + + while(ctlr->ntq < (Ntdre-1)){ + tb = ðer->tb[ether->ti]; + if(tb->owner != Interface) + break; + + bp = allocb(tb->len); + memmove(bp->wp, tb->pkt, tb->len); + memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen); + bp->wp += tb->len; + + /* + * Give ownership of the descriptor to the chip, + * increment the software ring descriptor pointer + * and tell the chip to poll. + * There's no need to pad to ETHERMINTU + * here as ApadXmt is set in CSR4. + */ + dre = &ctlr->tdr[ctlr->tdrh]; + dre->data = bp; + dre->addr = PADDR(bp->rp); + dre->md2 = 0; + dre->md1 = Own|Stp|Enp|Oflo|(-BLEN(bp) & 0xFFFF); + ctlr->ntq++; + ctlr->iow(ctlr, Rap, 0); + ctlr->iow(ctlr, Rdp, Iena|Tdmd); + ctlr->tdrh = NEXT(ctlr->tdrh, Ntdre); + + tb->owner = Host; + ether->ti = NEXT(ether->ti, ether->ntb); + } +} + +static void +interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *ether; + int csr0; + Dre *dre; + RingBuf *rb; + + ether = arg; + ctlr = ether->ctlr; + + /* + * Acknowledge all interrupts and whine about those that shouldn't + * happen. + */ +intrloop: + csr0 = ctlr->ior(ctlr, Rdp) & 0xFFFF; + ctlr->iow(ctlr, Rdp, Babl|Cerr|Miss|Merr|Rint|Tint|Iena); + if(csr0 & Merr) + ctlr->merr++; + if(csr0 & Miss) + ctlr->miss++; + if(csr0 & Babl) + ctlr->babl++; + //if(csr0 & (Babl|Miss|Merr)) + // print("#l%d: csr0 = 0x%uX\n", ether->ctlrno, csr0); + if(!(csr0 & (Rint|Tint))) + return; + + /* + * Receiver interrupt: run round the descriptor ring logging + * errors and passing valid receive data up to the higher levels + * until a descriptor is encountered still owned by the chip. + */ + if(csr0 & Rint){ + dre = &ctlr->rdr[ctlr->rdrx]; + while(!(dre->md1 & Own)){ + rb = ðer->rb[ether->ri]; + if(dre->md1 & RxErr){ + if(dre->md1 & RxBuff) + ctlr->rxbuff++; + if(dre->md1 & Crc) + ctlr->crc++; + if(dre->md1 & Oflo) + ctlr->oflo++; + if(dre->md1 & Fram) + ctlr->fram++; + } + else if(rb->owner == Interface){ + rb->owner = Host; + rb->len = (dre->md2 & 0x0FFF)-4; + memmove(rb->pkt, dre->data, rb->len); + ether->ri = NEXT(ether->ri, ether->nrb); + } + + /* + * Finished with this descriptor, reinitialise it, + * give it back to the chip, then on to the next... + */ + dre->md2 = 0; + dre->md1 = Own|(-Rbsize & 0xFFFF); + + ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre); + dre = &ctlr->rdr[ctlr->rdrx]; + } + } + + /* + * Transmitter interrupt: wakeup anyone waiting for a free descriptor. + */ + if(csr0 & Tint){ + lock(ctlr); + while(ctlr->ntq){ + dre = &ctlr->tdr[ctlr->tdri]; + if(dre->md1 & Own) + break; + + if(dre->md1 & TxErr){ + if(dre->md2 & Rtry) + ctlr->rtry++; + if(dre->md2 & Lcar) + ctlr->lcar++; + if(dre->md2 & Lcol) + ctlr->lcol++; + if(dre->md2 & Uflo) + ctlr->uflo++; + if(dre->md2 & TxBuff) + ctlr->txbuff++; + } + + freeb(dre->data); + + ctlr->ntq--; + ctlr->tdri = NEXT(ctlr->tdri, Ntdre); + } + transmit(ether); + unlock(ctlr); + } + goto intrloop; +} + +static void +amd79c970pci(void) +{ + Ctlr *ctlr; + Pcidev *p; + + p = nil; + while(p = pcimatch(p, 0x1022, 0x2000)){ + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = p->mem[0].bar & ~0x01; + ctlr->pcidev = p; + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + +int +amd79c970reset(Ether* ether) +{ + int x; + uchar ea[Eaddrlen]; + Ctlr *ctlr; + + if(ctlrhead == nil) + amd79c970pci(); + + /* + * Any adapter matches if no port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(ether->port == 0 || ether->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + /* + * Allocate a controller structure and start to initialise it. + */ + ether->ctlr = ctlr; + ether->port = ctlr->port; + ether->irq = ctlr->pcidev->intl; + ether->tbdf = ctlr->pcidev->tbdf; + pcisetbme(ctlr->pcidev); + ilock(ctlr); + ctlr->init = 1; + + io32r(ctlr, Sreset); + io16r(ctlr, Sreset); + + if(io16w(ctlr, Rap, 0), io16r(ctlr, Rdp) == 4){ + ctlr->ior = io16r; + ctlr->iow = io16w; + }else if(io32w(ctlr, Rap, 0), io32r(ctlr, Rdp) == 4){ + ctlr->ior = io32r; + ctlr->iow = io32w; + }else{ + print("#l%d: card doesn't talk right\n", ether->ctlrno); + iunlock(ctlr); + return -1; + } + + ctlr->iow(ctlr, Rap, 88); + x = ctlr->ior(ctlr, Rdp); + ctlr->iow(ctlr, Rap, 89); + x |= ctlr->ior(ctlr, Rdp)<<16; + + switch(x&0xFFFFFFF){ + case 0x2420003: /* PCnet/PCI 79C970 */ + case 0x2621003: /* PCnet/PCI II 79C970A */ + break; + default: + print("unknown PCnet card version %.7ux\n", x&0xFFFFFFF); + iunlock(ctlr); + return -1; + } + + /* + * Set the software style in BCR20 to be PCnet-PCI to ensure 32-bit access. + * Set the auto pad transmit in CSR4. + */ + ctlr->iow(ctlr, Rap, 20); + ctlr->iow(ctlr, Bdp, 0x0002); + + ctlr->iow(ctlr, Rap, 4); + x = ctlr->ior(ctlr, Rdp) & 0xFFFF; + ctlr->iow(ctlr, Rdp, ApadXmt|x); + + ctlr->iow(ctlr, Rap, 0); + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the I/O-space and set in ether->ea prior to + * loading the station address in the initialisation block. + */ + memset(ea, 0, Eaddrlen); + if(!memcmp(ea, ether->ea, Eaddrlen)){ + x = ctlr->ior(ctlr, Aprom); + ether->ea[0] = x; + ether->ea[1] = x>>8; + if(ctlr->ior == io16r) + x = ctlr->ior(ctlr, Aprom+2); + else + x >>= 16; + ether->ea[2] = x; + ether->ea[3] = x>>8; + x = ctlr->ior(ctlr, Aprom+4); + ether->ea[4] = x; + ether->ea[5] = x>>8; + } + + /* + * Start to fill in the initialisation block + * (must be DWORD aligned). + */ + ctlr->iblock.rlen = Lognrdre<<4; + ctlr->iblock.tlen = Logntdre<<4; + memmove(ctlr->iblock.padr, ether->ea, sizeof(ctlr->iblock.padr)); + + ringinit(ctlr); + ctlr->iblock.rdra = PADDR(ctlr->rdr); + ctlr->iblock.tdra = PADDR(ctlr->tdr); + + /* + * Point the chip at the initialisation block and tell it to go. + * Mask the Idon interrupt and poll for completion. Strt and interrupt + * enables will be set later when attaching to the network. + */ + x = PADDR(&ctlr->iblock); + ctlr->iow(ctlr, Rap, 1); + ctlr->iow(ctlr, Rdp, x & 0xFFFF); + ctlr->iow(ctlr, Rap, 2); + ctlr->iow(ctlr, Rdp, (x>>16) & 0xFFFF); + ctlr->iow(ctlr, Rap, 3); + ctlr->iow(ctlr, Rdp, Idon); + ctlr->iow(ctlr, Rap, 0); + ctlr->iow(ctlr, Rdp, Init); + + while(!(ctlr->ior(ctlr, Rdp) & Idon)) + ; + + /* + * We used to set CSR0 to Idon|Stop here, and then + * in attach change it to Iena|Strt. Apparently the simulated + * 79C970 in VMware never enables after a write of Idon|Stop, + * so we enable the device here now. + */ + ctlr->iow(ctlr, Rdp, Iena|Strt); + ctlr->init = 0; + iunlock(ctlr); + + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->detach = detach; + + return 0; +} diff --git a/os/boot/pc/ether8003.c b/os/boot/pc/ether8003.c new file mode 100644 index 00000000..1ea9be21 --- /dev/null +++ b/os/boot/pc/ether8003.c @@ -0,0 +1,258 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "etherif.h" +#include "ether8390.h" + +/* + * Western Digital/Standard Microsystems Corporation cards (WD80[01]3). + * Also handles 8216 cards (Elite Ultra). + * Configuration code based on that provided by SMC a long time ago. + */ +enum { /* 83C584 Bus Interface Controller */ + Msr = 0x00, /* Memory Select Register */ + Icr = 0x01, /* Interface Configuration Register */ + Iar = 0x02, /* I/O Address Register */ + Bio = 0x03, /* BIOS ROM Address Register */ + Ear = 0x03, /* EEROM Address Register (shared with Bio) */ + Irr = 0x04, /* Interrupt Request Register */ + Hcr = 0x04, /* 8216 hardware control */ + Laar = 0x05, /* LA Address Register */ + Ijr = 0x06, /* Initialisation Jumpers */ + Gp2 = 0x07, /* General Purpose Data Register */ + Lar = 0x08, /* LAN Address Registers */ + Id = 0x0E, /* Card ID byte */ + Cksum = 0x0F, /* Checksum */ +}; + +enum { /* Msr */ + Rst = 0x80, /* software reset */ + Menb = 0x40, /* memory enable */ +}; + +enum { /* Icr */ + Bit16 = 0x01, /* 16-bit bus */ + Other = 0x02, /* other register access */ + Ir2 = 0x04, /* IR2 */ + Msz = 0x08, /* SRAM size */ + Rla = 0x10, /* recall LAN address */ + Rx7 = 0x20, /* recall all but I/O and LAN address */ + Rio = 0x40, /* recall I/O address from EEROM */ + Sto = 0x80, /* non-volatile EEROM store */ +}; + +enum { /* Laar */ + ZeroWS16 = 0x20, /* zero wait states for 16-bit ops */ + L16en = 0x40, /* enable 16-bit LAN operation */ + M16en = 0x80, /* enable 16-bit memory access */ +}; + +enum { /* Ijr */ + Ienable = 0x01, /* 8216 interrupt enable */ +}; + +/* + * Mapping from configuration bits to interrupt level. + */ +static int irq8003[8] = { + 9, 3, 5, 7, 10, 11, 15, 4, +}; + +static int irq8216[8] = { + 0, 9, 3, 5, 7, 10, 11, 15, +}; + +static void +reset8003(Ether* ether, uchar ea[Eaddrlen], uchar ic[8]) +{ + Dp8390 *ctlr; + ulong port; + + ctlr = ether->ctlr; + port = ether->port; + + /* + * Check for old, dumb 8003E, which doesn't have an interface + * chip. Only Msr exists out of the 1st eight registers, reads + * of the others just alias the 2nd eight registers, the LAN + * address ROM. Can check Icr, Irr and Laar against the ethernet + * address read above and if they match it's an 8003E (or an + * 8003EBT, 8003S, 8003SH or 8003WT, doesn't matter), in which + * case the default irq gets used. + */ + if(memcmp(&ea[1], &ic[1], 5) == 0){ + memset(ic, 0, sizeof(ic)); + ic[Msr] = (((ulong)ether->mem)>>13) & 0x3F; + } + else{ + /* + * As a final sanity check for the 8013EBT, which doesn't have + * the 83C584 interface chip, but has 2 real registers, write Gp2 + * and if it reads back the same, it's not an 8013EBT. + */ + outb(port+Gp2, 0xAA); + inb(port+Msr); /* wiggle bus */ + if(inb(port+Gp2) != 0xAA){ + memset(ic, 0, sizeof(ic)); + ic[Msr] = (((ulong)ether->mem)>>13) & 0x3F; + } + else + ether->irq = irq8003[((ic[Irr]>>5) & 0x3)|(ic[Icr] & 0x4)]; + + /* + * Check if 16-bit card. + * If Bit16 is read/write, then it's an 8-bit card. + * If Bit16 is set, it's in a 16-bit slot. + */ + outb(port+Icr, ic[Icr]^Bit16); + inb(port+Msr); /* wiggle bus */ + if((inb(port+Icr) & Bit16) == (ic[Icr] & Bit16)){ + ctlr->width = 2; + ic[Icr] &= ~Bit16; + } + outb(port+Icr, ic[Icr]); + + if(ctlr->width == 2 && (inb(port+Icr) & Bit16) == 0) + ctlr->width = 1; + } + + ether->mem = (ulong)KADDR((ic[Msr] & 0x3F)<<13); + if(ctlr->width == 2) + ether->mem |= (ic[Laar] & 0x1F)<<19; + else + ether->mem |= 0x80000; + + if(ic[Icr] & (1<<3)) + ether->size = 32*1024; + if(ctlr->width == 2) + ether->size <<= 1; + + /* + * Enable interface RAM, set interface width. + */ + outb(port+Msr, ic[Msr]|Menb); + if(ctlr->width == 2) + outb(port+Laar, ic[Laar]|L16en|M16en|ZeroWS16); +} + +static void +reset8216(Ether* ether, uchar[8]) +{ + uchar hcr, irq, x; + ulong addr, port; + Dp8390 *ctlr; + + ctlr = ether->ctlr; + port = ether->port; + + ctlr->width = 2; + + /* + * Switch to the alternate register set and retrieve the memory + * and irq information. + */ + hcr = inb(port+Hcr); + outb(port+Hcr, 0x80|hcr); + addr = inb(port+0x0B) & 0xFF; + irq = inb(port+0x0D); + outb(port+Hcr, hcr); + + ether->mem = (ulong)KADDR(0xC0000+((((addr>>2) & 0x30)|(addr & 0x0F))<<13)); + ether->size = 8192*(1<<((addr>>4) & 0x03)); + ether->irq = irq8216[((irq>>4) & 0x04)|((irq>>2) & 0x03)]; + + /* + * Enable interface RAM, set interface width, and enable interrupts. + */ + x = inb(port+Msr) & ~Rst; + outb(port+Msr, Menb|x); + x = inb(port+Laar); + outb(port+Laar, M16en|x); + outb(port+Ijr, Ienable); +} + +/* + * Get configuration parameters, enable memory. + * There are opportunities here for buckets of code, try to resist. + */ +int +wd8003reset(Ether* ether) +{ + int i; + uchar ea[Eaddrlen], ic[8], id, nullea[Eaddrlen], sum; + ulong port; + Dp8390 *ctlr; + + /* + * Set up the software configuration. + * Use defaults for port, irq, mem and size if not specified. + * Defaults are set for the dumb 8003E which can't be + * autoconfigured. + */ + if(ether->port == 0) + ether->port = 0x280; + if(ether->irq == 0) + ether->irq = 3; + if(ether->mem == 0) + ether->mem = 0xD0000; + if(ether->size == 0) + ether->size = 8*1024; + + /* + * Look for the interface. Read the LAN address ROM + * and validate the checksum - the sum of all 8 bytes + * should be 0xFF. + * At the same time, get the (possible) interface chip + * registers, they'll be used later to check for aliasing. + */ + port = ether->port; + sum = 0; + for(i = 0; i < sizeof(ea); i++){ + ea[i] = inb(port+Lar+i); + sum += ea[i]; + ic[i] = inb(port+i); + } + id = inb(port+Id); + sum += id; + sum += inb(port+Cksum); + if(sum != 0xFF) + return -1; + + ether->ctlr = malloc(sizeof(Dp8390)); + ctlr = ether->ctlr; + ctlr->ram = 1; + + if((id & 0xFE) == 0x2A) + reset8216(ether, ic); + else + reset8003(ether, ea, ic); + + /* + * Set the DP8390 ring addresses. + */ + ctlr->port = port+0x10; + ctlr->tstart = 0; + ctlr->pstart = HOWMANY(sizeof(Etherpkt), Dp8390BufSz); + ctlr->pstop = HOWMANY(ether->size, Dp8390BufSz); + + /* + * Finally, init the 8390, set the ethernet address + * and claim the memory used. + */ + dp8390reset(ether); + memset(nullea, 0, Eaddrlen); + if(memcmp(nullea, ether->ea, Eaddrlen) == 0){ + for(i = 0; i < sizeof(ether->ea); i++) + ether->ea[i] = ea[i]; + } + dp8390setea(ether); + + if(umbrwmalloc(PADDR(ether->mem), ether->size, 0) == 0) + print("ether8003: warning - 0x%luX unavailable", PADDR(ether->mem)); + + return 0; +} diff --git a/os/boot/pc/ether8139.c b/os/boot/pc/ether8139.c new file mode 100644 index 00000000..650df1b3 --- /dev/null +++ b/os/boot/pc/ether8139.c @@ -0,0 +1,614 @@ +/* + * Realtek 8139 (but not the 8129). + * Error recovery for the various over/under -flow conditions + * may need work. + */ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "etherif.h" + +enum { /* registers */ + Idr0 = 0x0000, /* MAC address */ + Mar0 = 0x0008, /* Multicast address */ + Tsd0 = 0x0010, /* Transmit Status Descriptor0 */ + Tsad0 = 0x0020, /* Transmit Start Address Descriptor0 */ + Rbstart = 0x0030, /* Receive Buffer Start Address */ + Erbcr = 0x0034, /* Early Receive Byte Count */ + Ersr = 0x0036, /* Early Receive Status */ + Cr = 0x0037, /* Command Register */ + Capr = 0x0038, /* Current Address of Packet Read */ + Cbr = 0x003A, /* Current Buffer Address */ + Imr = 0x003C, /* Interrupt Mask */ + Isr = 0x003E, /* Interrupt Status */ + Tcr = 0x0040, /* Transmit Configuration */ + Rcr = 0x0044, /* Receive Configuration */ + Tctr = 0x0048, /* Timer Count */ + Mpc = 0x004C, /* Missed Packet Counter */ + Cr9346 = 0x0050, /* 9346 Command Register */ + Config0 = 0x0051, /* Configuration Register 0 */ + Config1 = 0x0052, /* Configuration Register 1 */ + TimerInt = 0x0054, /* Timer Interrupt */ + Msr = 0x0058, /* Media Status */ + Config3 = 0x0059, /* Configuration Register 3 */ + Config4 = 0x005A, /* Configuration Register 4 */ + Mulint = 0x005C, /* Multiple Interrupt Select */ + RerID = 0x005E, /* PCI Revision ID */ + Tsad = 0x0060, /* Transmit Status of all Descriptors */ + + Bmcr = 0x0062, /* Basic Mode Control */ + Bmsr = 0x0064, /* Basic Mode Status */ + Anar = 0x0066, /* Auto-Negotiation Advertisment */ + Anlpar = 0x0068, /* Auto-Negotiation Link Partner */ + Aner = 0x006A, /* Auto-Negotiation Expansion */ + Dis = 0x006C, /* Disconnect Counter */ + Fcsc = 0x006E, /* False Carrier Sense Counter */ + Nwaytr = 0x0070, /* N-way Test */ + Rec = 0x0072, /* RX_ER Counter */ + Cscr = 0x0074, /* CS Configuration */ + Phy1parm = 0x0078, /* PHY Parameter 1 */ + Twparm = 0x007C, /* Twister Parameter */ + Phy2parm = 0x0080, /* PHY Parameter 2 */ +}; + +enum { /* Cr */ + Bufe = 0x01, /* Rx Buffer Empty */ + Te = 0x04, /* Transmitter Enable */ + Re = 0x08, /* Receiver Enable */ + Rst = 0x10, /* Software Reset */ +}; + +enum { /* Imr/Isr */ + Rok = 0x0001, /* Receive OK */ + Rer = 0x0002, /* Receive Error */ + Tok = 0x0004, /* Transmit OK */ + Ter = 0x0008, /* Transmit Error */ + Rxovw = 0x0010, /* Receive Buffer Overflow */ + PunLc = 0x0020, /* Packet Underrun or Link Change */ + Fovw = 0x0040, /* Receive FIFO Overflow */ + Clc = 0x2000, /* Cable Length Change */ + Timer = 0x4000, /* Timer */ + Serr = 0x8000, /* System Error */ +}; + +enum { /* Tcr */ + Clrabt = 0x00000001, /* Clear Abort */ + TxrrSHIFT = 4, /* Transmit Retry Count */ + TxrrMASK = 0x000000F0, + MtxdmaSHIFT = 8, /* Max. DMA Burst Size */ + MtxdmaMASK = 0x00000700, + Mtxdma2048 = 0x00000700, + Acrc = 0x00010000, /* Append CRC (not) */ + LbkSHIFT = 17, /* Loopback Test */ + LbkMASK = 0x00060000, + IfgSHIFT = 24, /* Interframe Gap */ + IfgMASK = 0x03000000, + HwveridSHIFT = 22, /* Hardware Version ID */ + HwveridMASK = 0x7CC00000, +}; + +enum { /* Rcr */ + Aap = 0x00000001, /* Accept All Packets */ + Apm = 0x00000002, /* Accept Physical Match */ + Am = 0x00000004, /* Accept Multicast */ + Ab = 0x00000008, /* Accept Broadcast */ + Ar = 0x00000010, /* Accept Runt */ + Aer = 0x00000020, /* Accept Error */ + Sel9356 = 0x00000040, /* 9356 EEPROM used */ + Wrap = 0x00000080, /* Rx Buffer Wrap Control */ + MrxdmaSHIFT = 8, /* Max. DMA Burst Size */ + MrxdmaMASK = 0x00000700, + Mrxdmaunlimited = 0x00000700, + RblenSHIFT = 11, /* Receive Buffer Length */ + RblenMASK = 0x00001800, + Rblen8K = 0x00000000, /* 8KB+16 */ + Rblen16K = 0x00000800, /* 16KB+16 */ + Rblen32K = 0x00001000, /* 32KB+16 */ + Rblen64K = 0x00001800, /* 64KB+16 */ + RxfthSHIFT = 13, /* Receive Buffer Length */ + RxfthMASK = 0x0000E000, + Rxfth256 = 0x00008000, + Rxfthnone = 0x0000E000, + Rer8 = 0x00010000, /* Accept Error Packets > 8 bytes */ + MulERINT = 0x00020000, /* Multiple Early Interrupt Select */ + ErxthSHIFT = 24, /* Early Rx Threshold */ + ErxthMASK = 0x0F000000, + Erxthnone = 0x00000000, +}; + +enum { /* Received Packet Status */ + Rcok = 0x0001, /* Receive Completed OK */ + Fae = 0x0002, /* Frame Alignment Error */ + Crc = 0x0004, /* CRC Error */ + Long = 0x0008, /* Long Packet */ + Runt = 0x0010, /* Runt Packet Received */ + Ise = 0x0020, /* Invalid Symbol Error */ + Bar = 0x2000, /* Broadcast Address Received */ + Pam = 0x4000, /* Physical Address Matched */ + Mar = 0x8000, /* Multicast Address Received */ +}; + +enum { /* Media Status Register */ + Rxpf = 0x01, /* Pause Flag */ + Txpf = 0x02, /* Pause Flag */ + Linkb = 0x04, /* Inverse of Link Status */ + Speed10 = 0x08, /* 10Mbps */ + Auxstatus = 0x10, /* Aux. Power Present Status */ + Rxfce = 0x40, /* Receive Flow Control Enable */ + Txfce = 0x80, /* Transmit Flow Control Enable */ +}; + +typedef struct { /* Soft Transmit Descriptor */ + int tsd; + int tsad; + uchar* data; +} Td; + +enum { /* Tsd0 */ + SizeSHIFT = 0, /* Descriptor Size */ + SizeMASK = 0x00001FFF, + Own = 0x00002000, + Tun = 0x00004000, /* Transmit FIFO Underrun */ + Tcok = 0x00008000, /* Transmit COmpleted OK */ + EtxthSHIFT = 16, /* Early Tx Threshold */ + EtxthMASK = 0x001F0000, + NccSHIFT = 24, /* Number of Collisions Count */ + NccMASK = 0x0F000000, + Cdh = 0x10000000, /* CD Heartbeat */ + Owc = 0x20000000, /* Out of Window Collision */ + Tabt = 0x40000000, /* Transmit Abort */ + Crs = 0x80000000, /* Carrier Sense Lost */ +}; + +enum { + Rblen = Rblen64K, /* Receive Buffer Length */ + Ntd = 4, /* Number of Transmit Descriptors */ + Tdbsz = ROUNDUP(sizeof(Etherpkt), 4), +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; + + Lock ilock; /* init */ + void* alloc; /* base of per-Ctlr allocated data */ + + int rcr; /* receive configuration register */ + uchar* rbstart; /* receive buffer */ + int rblen; /* receive buffer length */ + int ierrs; /* receive errors */ + + Lock tlock; /* transmit */ + Td td[Ntd]; + int ntd; /* descriptors active */ + int tdh; /* host index into td */ + int tdi; /* interface index into td */ + int etxth; /* early transmit threshold */ + int taligned; /* packet required no alignment */ + int tunaligned; /* packet required alignment */ + + int dis; /* disconnect counter */ + int fcsc; /* false carrier sense counter */ + int rec; /* RX_ER counter */ +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +#define csr8r(c, r) (inb((c)->port+(r))) +#define csr16r(c, r) (ins((c)->port+(r))) +#define csr32r(c, r) (inl((c)->port+(r))) +#define csr8w(c, r, b) (outb((c)->port+(r), (int)(b))) +#define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w))) +#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l))) + +static int +rtl8139reset(Ctlr* ctlr) +{ + /* + * Soft reset the controller. + */ + csr8w(ctlr, Cr, Rst); + while(csr8r(ctlr, Cr) & Rst) + ; + + return 0; +} + +static void +rtl8139detach(Ether* edev) +{ + rtl8139reset(edev->ctlr); +} + +static void +rtl8139halt(Ctlr* ctlr) +{ + csr8w(ctlr, Cr, 0); + csr16w(ctlr, Imr, 0); + csr16w(ctlr, Isr, ~0); +} + +static void +rtl8139init(Ether* edev) +{ + int i; + ulong r; + Ctlr *ctlr; + uchar *alloc; + + ctlr = edev->ctlr; + ilock(&ctlr->ilock); + + rtl8139halt(ctlr); + + /* + * MAC Address. + */ + r = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0]; + csr32w(ctlr, Idr0, r); + r = (edev->ea[5]<<8)|edev->ea[4]; + csr32w(ctlr, Idr0+4, r); + + /* + * Receiver + */ + alloc = (uchar*)ROUNDUP((ulong)ctlr->alloc, 32); + ctlr->rbstart = alloc; + alloc += ctlr->rblen+16; + memset(ctlr->rbstart, 0, ctlr->rblen+16); + csr32w(ctlr, Rbstart, PADDR(ctlr->rbstart)); + ctlr->rcr = Rxfth256|Rblen|Mrxdmaunlimited|Ab|Apm; + + /* + * Transmitter. + */ + for(i = 0; i < Ntd; i++){ + ctlr->td[i].tsd = Tsd0+i*4; + ctlr->td[i].tsad = Tsad0+i*4; + ctlr->td[i].data = alloc; + alloc += Tdbsz; + } + ctlr->ntd = ctlr->tdh = ctlr->tdi = 0; + ctlr->etxth = 128/32; + + /* + * Interrupts. + */ + csr32w(ctlr, TimerInt, 0); + csr16w(ctlr, Imr, Serr|Timer|Fovw|PunLc|Rxovw|Ter|Tok|Rer|Rok); + csr32w(ctlr, Mpc, 0); + + /* + * Enable receiver/transmitter. + * Need to enable before writing the Rcr or it won't take. + */ + csr8w(ctlr, Cr, Te|Re); + csr32w(ctlr, Tcr, Mtxdma2048); + csr32w(ctlr, Rcr, ctlr->rcr); + + iunlock(&ctlr->ilock); +} + +static void +rtl8139attach(Ether* edev) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + if(ctlr->alloc == nil){ + ctlr->rblen = 1<<((Rblen>>RblenSHIFT)+13); + ctlr->alloc = mallocz(ctlr->rblen+16 + Ntd*Tdbsz + 32, 0); + rtl8139init(edev); + } +} + +static void +rtl8139txstart(Ether* edev) +{ + Td *td; + Ctlr *ctlr; + RingBuf *tb; + + ctlr = edev->ctlr; + while(ctlr->ntd < Ntd){ + tb = &edev->tb[edev->ti]; + if(tb->owner != Interface) + break; + + td = &ctlr->td[ctlr->tdh]; + memmove(td->data, tb->pkt, tb->len); + csr32w(ctlr, td->tsad, PADDR(tb->pkt)); + csr32w(ctlr, td->tsd, (ctlr->etxth<len); + + ctlr->ntd++; + ctlr->tdh = NEXT(ctlr->tdh, Ntd); + tb->owner = Host; + edev->ti = NEXT(edev->ti, edev->ntb); + } +} + +static void +rtl8139transmit(Ether* edev) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + ilock(&ctlr->tlock); + rtl8139txstart(edev); + iunlock(&ctlr->tlock); +} + +static void +rtl8139receive(Ether* edev) +{ + Ctlr *ctlr; + RingBuf *rb; + ushort capr; + uchar cr, *p; + int l, length, status; + + ctlr = edev->ctlr; + + /* + * Capr is where the host is reading from, + * Cbr is where the NIC is currently writing. + */ + capr = (csr16r(ctlr, Capr)+16) % ctlr->rblen; + while(!(csr8r(ctlr, Cr) & Bufe)){ + p = ctlr->rbstart+capr; + + /* + * Apparently the packet length may be 0xFFF0 if + * the NIC is still copying the packet into memory. + */ + length = (*(p+3)<<8)|*(p+2); + if(length == 0xFFF0) + break; + status = (*(p+1)<<8)|*p; + + if(!(status & Rcok)){ + /* + * Reset the receiver. + * Also may have to restore the multicast list + * here too if it ever gets used. + */ + cr = csr8r(ctlr, Cr); + csr8w(ctlr, Cr, cr & ~Re); + csr32w(ctlr, Rbstart, PADDR(ctlr->rbstart)); + csr8w(ctlr, Cr, cr); + csr32w(ctlr, Rcr, ctlr->rcr); + + continue; + } + + /* + * Receive Completed OK. + * Very simplistic; there are ways this could be done + * without copying, but the juice probably isn't worth + * the squeeze. + * The packet length includes a 4 byte CRC on the end. + */ + capr = (capr+4) % ctlr->rblen; + p = ctlr->rbstart+capr; + capr = (capr+length) % ctlr->rblen; + + rb = &edev->rb[edev->ri]; + l = 0; + if(p+length >= ctlr->rbstart+ctlr->rblen){ + l = ctlr->rbstart+ctlr->rblen - p; + if(rb->owner == Interface) + memmove(rb->pkt, p, l); + length -= l; + p = ctlr->rbstart; + } + if(length > 0 && rb->owner == Interface){ + memmove(rb->pkt+l, p, length); + l += length; + } + if(rb->owner == Interface){ + rb->owner = Host; + rb->len = l-4; + edev->ri = NEXT(edev->ri, edev->nrb); + } + + capr = ROUNDUP(capr, 4); + csr16w(ctlr, Capr, capr-16); + } +} + +static void +rtl8139interrupt(Ureg*, void* arg) +{ + Td *td; + Ctlr *ctlr; + Ether *edev; + int isr, tsd; + + edev = arg; + ctlr = edev->ctlr; + + while((isr = csr16r(ctlr, Isr)) != 0){ + csr16w(ctlr, Isr, isr); + if(isr & (Fovw|PunLc|Rxovw|Rer|Rok)){ + rtl8139receive(edev); + if(!(isr & Rok)) + ctlr->ierrs++; + isr &= ~(Fovw|Rxovw|Rer|Rok); + } + + if(isr & (Ter|Tok)){ + ilock(&ctlr->tlock); + while(ctlr->ntd){ + td = &ctlr->td[ctlr->tdi]; + tsd = csr32r(ctlr, td->tsd); + if(!(tsd & (Tabt|Tun|Tcok))) + break; + + if(!(tsd & Tcok)){ + if(tsd & Tun){ + if(ctlr->etxth < ETHERMAXTU/32) + ctlr->etxth++; + } + } + + ctlr->ntd--; + ctlr->tdi = NEXT(ctlr->tdi, Ntd); + } + rtl8139txstart(edev); + iunlock(&ctlr->tlock); + isr &= ~(Ter|Tok); + } + + if(isr & PunLc) + isr &= ~(Clc|PunLc); + + /* + * Only Serr|Timer should be left by now. + * Should anything be done to tidy up? TimerInt isn't + * used so that can be cleared. A PCI bus error is indicated + * by Serr, that's pretty serious; is there anyhing to do + * other than try to reinitialise the chip? + */ + if((isr & (Serr|Timer)) != 0){ + print("rtl8139interrupt: imr %4.4uX isr %4.4uX\n", + csr16r(ctlr, Imr), isr); + if(isr & Timer) + csr32w(ctlr, TimerInt, 0); + if(isr & Serr) + rtl8139init(edev); + } + } +} + +static Ctlr* +rtl8139match(Ether* edev, int id) +{ + int port; + Pcidev *p; + Ctlr *ctlr; + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + p = ctlr->pcidev; + if(((p->did<<16)|p->vid) != id) + continue; + port = p->mem[0].bar & ~0x01; + if(edev->port != 0 && edev->port != port) + continue; + + ctlr->port = port; + if(rtl8139reset(ctlr)) + continue; + pcisetbme(p); + + ctlr->active = 1; + return ctlr; + } + return nil; +} + +static struct { + char* name; + int id; +} rtl8139pci[] = { + { "rtl8139", (0x8139<<16)|0x10EC, }, /* generic */ + { "smc1211", (0x1211<<16)|0x1113, }, /* SMC EZ-Card */ + { "dfe-538tx", (0x1300<<16)|0x1186, }, /* D-Link DFE-538TX */ + { "dfe-560txd", (0x1340<<16)|0x1186, }, /* D-Link DFE-560TXD */ + { nil }, +}; + +int +rtl8139pnp(Ether* edev) +{ + int i, id; + Pcidev *p; + Ctlr *ctlr; + uchar ea[Eaddrlen]; + + /* + * Make a list of all ethernet controllers + * if not already done. + */ + if(ctlrhead == nil){ + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + ctlr = malloc(sizeof(Ctlr)); + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } + } + + /* + * Is it an RTL8139 under a different name? + * Normally a search is made through all the found controllers + * for one which matches any of the known vid+did pairs. + * If a vid+did pair is specified a search is made for that + * specific controller only. + */ + id = 0; + for(i = 0; i < edev->nopt; i++){ + if(cistrncmp(edev->opt[i], "id=", 3) == 0) + id = strtol(&edev->opt[i][3], nil, 0); + } + + ctlr = nil; + if(id != 0) + ctlr = rtl8139match(edev, id); + else for(i = 0; rtl8139pci[i].name; i++){ + if((ctlr = rtl8139match(edev, rtl8139pci[i].id)) != nil) + break; + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pcidev->intl; + edev->tbdf = ctlr->pcidev->tbdf; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the device and set in edev->ea. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, edev->ea, Eaddrlen) == 0){ + i = csr32r(ctlr, Idr0); + edev->ea[0] = i; + edev->ea[1] = i>>8; + edev->ea[2] = i>>16; + edev->ea[3] = i>>24; + i = csr32r(ctlr, Idr0+4); + edev->ea[4] = i; + edev->ea[5] = i>>8; + } + + edev->attach = rtl8139attach; + edev->transmit = rtl8139transmit; + edev->interrupt = rtl8139interrupt; + edev->detach = rtl8139detach; + + return 0; +} diff --git a/os/boot/pc/ether8169.c b/os/boot/pc/ether8169.c new file mode 100644 index 00000000..5b36f7c3 --- /dev/null +++ b/os/boot/pc/ether8169.c @@ -0,0 +1,847 @@ +/* + * Realtek RTL8110S/8169S. + * Mostly there. There are some magic register values used + * which are not described in any datasheet or driver but seem + * to be necessary. + * Why is the Fovf descriptor bit set for every received packet? + * Occasionally the hardware indicates an input TCP checksum error + * although the higher-level software seems to check the packet OK? + * No tuning has been done. Only tested on an RTL8110S, there + * are slight differences between the chips in the series so some + * tweaks may be needed. + */ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + + +typedef struct QLock { int r; } QLock; +#define qlock(i) while(0) +#define qunlock(i) while(0) +#define iallocb allocb +extern void mb386(void); +#define coherence() mb386() +#define iprint print +#define mallocalign(n, a, o, s) ialloc((n), (a)) + +#include "etherif.h" +#include "ethermii.h" + +enum { /* registers */ + Idr0 = 0x00, /* MAC address */ + Mar0 = 0x08, /* Multicast address */ + Dtccr = 0x10, /* Dump Tally Counter Command */ + Tnpds = 0x20, /* Transmit Normal Priority Descriptors */ + Thpds = 0x28, /* Transmit High Priority Descriptors */ + Flash = 0x30, /* Flash Memory Read/Write */ + Erbcr = 0x34, /* Early Receive Byte Count */ + Ersr = 0x36, /* Early Receive Status */ + Cr = 0x37, /* Command Register */ + Tppoll = 0x38, /* Transmit Priority Polling */ + Imr = 0x3C, /* Interrupt Mask */ + Isr = 0x3E, /* Interrupt Status */ + Tcr = 0x40, /* Transmit Configuration */ + Rcr = 0x44, /* Receive Configuration */ + Tctr = 0x48, /* Timer Count */ + Mpc = 0x4C, /* Missed Packet Counter */ + Cr9346 = 0x50, /* 9346 Command Register */ + Config0 = 0x51, /* Configuration Register 0 */ + Config1 = 0x52, /* Configuration Register 1 */ + Config2 = 0x53, /* Configuration Register 2 */ + Config3 = 0x54, /* Configuration Register 3 */ + Config4 = 0x55, /* Configuration Register 4 */ + Config5 = 0x56, /* Configuration Register 5 */ + Timerint = 0x58, /* Timer Interrupt */ + Mulint = 0x5C, /* Multiple Interrupt Select */ + Phyar = 0x60, /* PHY Access */ + Tbicsr0 = 0x64, /* TBI Control and Status */ + Tbianar = 0x68, /* TBI Auto-Negotiation Advertisment */ + Tbilpar = 0x6A, /* TBI Auto-Negotiation Link Partner */ + Phystatus = 0x6C, /* PHY Status */ + + Rms = 0xDA, /* Receive Packet Maximum Size */ + Cplusc = 0xE0, /* C+ Command */ + Rdsar = 0xE4, /* Receive Descriptor Start Address */ + Mtps = 0xEC, /* Max. Transmit Packet Size */ +}; + +enum { /* Dtccr */ + Cmd = 0x00000008, /* Command */ +}; + +enum { /* Cr */ + Te = 0x04, /* Transmitter Enable */ + Re = 0x08, /* Receiver Enable */ + Rst = 0x10, /* Software Reset */ +}; + +enum { /* Tppoll */ + Fswint = 0x01, /* Forced Software Interrupt */ + Npq = 0x40, /* Normal Priority Queue polling */ + Hpq = 0x80, /* High Priority Queue polling */ +}; + +enum { /* Imr/Isr */ + Rok = 0x0001, /* Receive OK */ + Rer = 0x0002, /* Receive Error */ + Tok = 0x0004, /* Transmit OK */ + Ter = 0x0008, /* Transmit Error */ + Rdu = 0x0010, /* Receive Descriptor Unavailable */ + Punlc = 0x0020, /* Packet Underrun or Link Change */ + Fovw = 0x0040, /* Receive FIFO Overflow */ + Tdu = 0x0080, /* Transmit Descriptor Unavailable */ + Swint = 0x0100, /* Software Interrupt */ + Timeout = 0x4000, /* Timer */ + Serr = 0x8000, /* System Error */ +}; + +enum { /* Tcr */ + MtxdmaSHIFT = 8, /* Max. DMA Burst Size */ + MtxdmaMASK = 0x00000700, + Mtxdmaunlimited = 0x00000700, + Acrc = 0x00010000, /* Append CRC (not) */ + Lbk0 = 0x00020000, /* Loopback Test 0 */ + Lbk1 = 0x00040000, /* Loopback Test 1 */ + Ifg2 = 0x00080000, /* Interframe Gap 2 */ + HwveridSHIFT = 23, /* Hardware Version ID */ + HwveridMASK = 0x7C800000, + Ifg0 = 0x01000000, /* Interframe Gap 0 */ + Ifg1 = 0x02000000, /* Interframe Gap 1 */ +}; + +enum { /* Rcr */ + Aap = 0x00000001, /* Accept All Packets */ + Apm = 0x00000002, /* Accept Physical Match */ + Am = 0x00000004, /* Accept Multicast */ + Ab = 0x00000008, /* Accept Broadcast */ + Ar = 0x00000010, /* Accept Runt */ + Aer = 0x00000020, /* Accept Error */ + Sel9356 = 0x00000040, /* 9356 EEPROM used */ + MrxdmaSHIFT = 8, /* Max. DMA Burst Size */ + MrxdmaMASK = 0x00000700, + Mrxdmaunlimited = 0x00000700, + RxfthSHIFT = 13, /* Receive Buffer Length */ + RxfthMASK = 0x0000E000, + Rxfth256 = 0x00008000, + Rxfthnone = 0x0000E000, + Rer8 = 0x00010000, /* Accept Error Packets > 8 bytes */ + MulERINT = 0x01000000, /* Multiple Early Interrupt Select */ +}; + +enum { /* Cr9346 */ + Eedo = 0x01, /* */ + Eedi = 0x02, /* */ + Eesk = 0x04, /* */ + Eecs = 0x08, /* */ + Eem0 = 0x40, /* Operating Mode */ + Eem1 = 0x80, +}; + +enum { /* Phyar */ + DataMASK = 0x0000FFFF, /* 16-bit GMII/MII Register Data */ + DataSHIFT = 0, + RegaddrMASK = 0x001F0000, /* 5-bit GMII/MII Register Address */ + RegaddrSHIFT = 16, + Flag = 0x80000000, /* */ +}; + +enum { /* Phystatus */ + Fd = 0x01, /* Full Duplex */ + Linksts = 0x02, /* Link Status */ + Speed10 = 0x04, /* */ + Speed100 = 0x08, /* */ + Speed1000 = 0x10, /* */ + Rxflow = 0x20, /* */ + Txflow = 0x40, /* */ + Entbi = 0x80, /* */ +}; + +enum { /* Cplusc */ + Mulrw = 0x0008, /* PCI Multiple R/W Enable */ + Dac = 0x0010, /* PCI Dual Address Cycle Enable */ + Rxchksum = 0x0020, /* Receive Checksum Offload Enable */ + Rxvlan = 0x0040, /* Receive VLAN De-tagging Enable */ + Endian = 0x0200, /* Endian Mode */ +}; + +typedef struct D D; /* Transmit/Receive Descriptor */ +struct D { + u32int control; + u32int vlan; + u32int addrlo; + u32int addrhi; +}; + +enum { /* Transmit Descriptor control */ + TxflMASK = 0x0000FFFF, /* Transmit Frame Length */ + TxflSHIFT = 0, + Tcps = 0x00010000, /* TCP Checksum Offload */ + Udpcs = 0x00020000, /* UDP Checksum Offload */ + Ipcs = 0x00040000, /* IP Checksum Offload */ + Lgsen = 0x08000000, /* Large Send */ +}; + +enum { /* Receive Descriptor control */ + RxflMASK = 0x00003FFF, /* Receive Frame Length */ + RxflSHIFT = 0, + Tcpf = 0x00004000, /* TCP Checksum Failure */ + Udpf = 0x00008000, /* UDP Checksum Failure */ + Ipf = 0x00010000, /* IP Checksum Failure */ + Pid0 = 0x00020000, /* Protocol ID0 */ + Pid1 = 0x00040000, /* Protocol ID1 */ + Crce = 0x00080000, /* CRC Error */ + Runt = 0x00100000, /* Runt Packet */ + Res = 0x00200000, /* Receive Error Summary */ + Rwt = 0x00400000, /* Receive Watchdog Timer Expired */ + Fovf = 0x00800000, /* FIFO Overflow */ + Bovf = 0x01000000, /* Buffer Overflow */ + Bar = 0x02000000, /* Broadcast Address Received */ + Pam = 0x04000000, /* Physical Address Matched */ + Mar = 0x08000000, /* Multicast Address Received */ +}; + +enum { /* General Descriptor control */ + Ls = 0x10000000, /* Last Segment Descriptor */ + Fs = 0x20000000, /* First Segment Descriptor */ + Eor = 0x40000000, /* End of Descriptor Ring */ + Own = 0x80000000, /* Ownership */ +}; + +/* + */ +enum { /* Ring sizes (<= 1024) */ + Ntd = 8, /* Transmit Ring */ + Nrd = 32, /* Receive Ring */ + + Mps = ROUNDUP(ETHERMAXTU+4, 128), +}; + +typedef struct Dtcc Dtcc; +struct Dtcc { + u64int txok; + u64int rxok; + u64int txer; + u32int rxer; + u16int misspkt; + u16int fae; + u32int tx1col; + u32int txmcol; + u64int rxokph; + u64int rxokbrd; + u32int rxokmu; + u16int txabt; + u16int txundrn; +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + uint id; + + QLock alock; /* attach */ + Lock ilock; /* init */ + int init; /* */ + + Mii* mii; + + Lock tlock; /* transmit */ + D* td; /* descriptor ring */ + Block** tb; /* transmit buffers */ + int ntd; + + int tdh; /* head - producer index (host) */ + int tdt; /* tail - consumer index (NIC) */ + int ntdfree; + int ntq; + + int mtps; /* Max. Transmit Packet Size */ + + Lock rlock; /* receive */ + D* rd; /* descriptor ring */ + void** rb; /* receive buffers */ + int nrd; + + int rdh; /* head - producer index (NIC) */ + int rdt; /* tail - consumer index (host) */ + int nrdfree; + + int rcr; /* receive configuration register */ + + QLock slock; /* statistics */ + Dtcc* dtcc; + uint txdu; + uint tcpf; + uint udpf; + uint ipf; + uint fovf; + uint ierrs; + uint rer; + uint rdu; + uint punlc; + uint fovw; +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +#define csr8r(c, r) (inb((c)->port+(r))) +#define csr16r(c, r) (ins((c)->port+(r))) +#define csr32r(c, r) (inl((c)->port+(r))) +#define csr8w(c, r, b) (outb((c)->port+(r), (int)(b))) +#define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w))) +#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l))) + +static int +rtl8169miimir(Mii* mii, int pa, int ra) +{ + uint r; + int timeo; + Ctlr *ctlr; + + if(pa != 1) + return -1; + ctlr = mii->ctlr; + + r = (ra<<16) & RegaddrMASK; + csr32w(ctlr, Phyar, r); + delay(1); + for(timeo = 0; timeo < 2000; timeo++){ + if((r = csr32r(ctlr, Phyar)) & Flag) + break; + microdelay(100); + } + if(!(r & Flag)) + return -1; + + return (r & DataMASK)>>DataSHIFT; +} + +static int +rtl8169miimiw(Mii* mii, int pa, int ra, int data) +{ + uint r; + int timeo; + Ctlr *ctlr; + + if(pa != 1) + return -1; + ctlr = mii->ctlr; + + r = Flag|((ra<<16) & RegaddrMASK)|((data<mii = malloc(sizeof(Mii))) == nil) + return -1; + ctlr->mii->mir = rtl8169miimir; + ctlr->mii->miw = rtl8169miimiw; + ctlr->mii->ctlr = ctlr; + rtl8169miimiw(ctlr->mii, 1, 0x0B, 0x0000); /* magic */ + + if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){ + free(ctlr->mii); + ctlr->mii = nil; + return -1; + } + print("oui %X phyno %d\n", phy->oui, phy->phyno); + + miiane(ctlr->mii, ~0, ~0, ~0); + + return 0; +} + +static int +rtl8169reset(Ctlr* ctlr) +{ + int timeo; + + /* + * Soft reset the controller. + */ + csr8w(ctlr, Cr, Rst); + for(timeo = 0; timeo < 1000; timeo++){ + if(!(csr8r(ctlr, Cr) & Rst)) + return 0; + delay(1); + } + + return -1; +} + +static void +rtl8169detach(Ether* edev) +{ + rtl8169reset(edev->ctlr); +} + +static void +rtl8169halt(Ctlr* ctlr) +{ + csr8w(ctlr, Cr, 0); + csr16w(ctlr, Imr, 0); + csr16w(ctlr, Isr, ~0); +} + +static void +rtl8169replenish(Ctlr* ctlr) +{ + D *d; + int rdt; + void *bp; + + rdt = ctlr->rdt; + while(NEXT(rdt, ctlr->nrd) != ctlr->rdh){ + d = &ctlr->rd[rdt]; + if(ctlr->rb[rdt] == nil){ + /* + * simple allocation for now + */ + bp = malloc(Mps); + ctlr->rb[rdt] = bp; + d->addrlo = PCIWADDR(bp); + d->addrhi = 0; + } + coherence(); + d->control |= Own|Mps; + rdt = NEXT(rdt, ctlr->nrd); + ctlr->nrdfree++; + } + ctlr->rdt = rdt; +} + +static void +rtl8169init(Ether* edev) +{ + uint r; + Ctlr *ctlr; + + ctlr = edev->ctlr; + ilock(&ctlr->ilock); + + rtl8169halt(ctlr); + + /* + * Setting Mulrw in Cplusc disables the Tx/Rx DMA burst settings + * in Tcr/Rcr. + */ + csr16w(ctlr, Cplusc, (1<<14)|Rxchksum|Mulrw); /* magic (1<<14) */ + + /* + * MAC Address. + * Must put chip into config register write enable mode. + */ + csr8w(ctlr, Cr9346, Eem1|Eem0); + r = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0]; + csr32w(ctlr, Idr0, r); + r = (edev->ea[5]<<8)|edev->ea[4]; + csr32w(ctlr, Idr0+4, r); + + /* + * Enable receiver/transmitter. + * Need to do this first or some of the settings below + * won't take. + */ + csr8w(ctlr, Cr, Te|Re); + + /* + * Transmitter. + * Mtps is in units of 128. + */ + memset(ctlr->td, 0, sizeof(D)*ctlr->ntd); + ctlr->tdh = ctlr->tdt = 0; + ctlr->td[ctlr->ntd-1].control = Eor; + ctlr->mtps = HOWMANY(Mps, 128); + + /* + * Receiver. + */ + memset(ctlr->rd, 0, sizeof(D)*ctlr->nrd); + ctlr->rdh = ctlr->rdt = 0; + ctlr->rd[ctlr->nrd-1].control = Eor; + + //for(i = 0; i < ctlr->nrd; i++){ + // if((bp = ctlr->rb[i]) != nil){ + // ctlr->rb[i] = nil; + // freeb(bp); + // } + //} + rtl8169replenish(ctlr); + ctlr->rcr = Rxfth256|Mrxdmaunlimited|Ab|Apm; + + /* + * Interrupts. + * Disable Tdu|Tok for now, the transmit routine will tidy. + * Tdu means the NIC ran out of descritors to send, so it + * doesn't really need to ever be on. + */ + csr32w(ctlr, Timerint, 0); + csr16w(ctlr, Imr, Serr|Timeout/*|Tdu*/|Fovw|Punlc|Rdu|Ter/*|Tok*/|Rer|Rok); + + /* + * Clear missed-packet counter; + * initial early transmit threshold value; + * set the descriptor ring base addresses; + * set the maximum receive packet size; + * no early-receive interrupts. + */ + csr32w(ctlr, Mpc, 0); + csr8w(ctlr, Mtps, ctlr->mtps); + csr32w(ctlr, Tnpds+4, 0); + csr32w(ctlr, Tnpds, PCIWADDR(ctlr->td)); + csr32w(ctlr, Rdsar+4, 0); + csr32w(ctlr, Rdsar, PCIWADDR(ctlr->rd)); + csr16w(ctlr, Rms, Mps); + csr16w(ctlr, Mulint, 0); + + /* + * Set configuration. + */ + csr32w(ctlr, Tcr, Ifg1|Ifg0|Mtxdmaunlimited); + csr32w(ctlr, Rcr, ctlr->rcr); + csr16w(ctlr, 0xE2, 0); /* magic */ + + csr8w(ctlr, Cr9346, 0); + + iunlock(&ctlr->ilock); + + //rtl8169mii(ctlr); +} + +static void +rtl8169attach(Ether* edev) +{ + int timeo; + Ctlr *ctlr; + + ctlr = edev->ctlr; + qlock(&ctlr->alock); + if(ctlr->init == 0){ + /* + * Handle allocation/init errors here. + */ + ctlr->td = xspanalloc(sizeof(D)*Ntd, 256, 0); + ctlr->tb = malloc(Ntd*sizeof(Block*)); + ctlr->ntd = Ntd; + ctlr->rd = xspanalloc(sizeof(D)*Nrd, 256, 0); + ctlr->rb = malloc(Nrd*sizeof(Block*)); + ctlr->nrd = Nrd; + ctlr->dtcc = xspanalloc(sizeof(Dtcc), 64, 0); + rtl8169init(edev); + ctlr->init = 1; + } + qunlock(&ctlr->alock); + + for(timeo = 0; timeo < 3500; timeo++){ + if(miistatus(ctlr->mii) == 0) + break; + delay(10); + } +} + +static void +rtl8169transmit(Ether* edev) +{ + D *d; + Block *bp; + Ctlr *ctlr; + int control, x; + RingBuf *tb; + + ctlr = edev->ctlr; + + ilock(&ctlr->tlock); + for(x = ctlr->tdh; ctlr->ntq > 0; x = NEXT(x, ctlr->ntd)){ + d = &ctlr->td[x]; + if((control = d->control) & Own) + break; + + /* + * Check errors and log here. + */ + USED(control); + + /* + * Free it up. + * Need to clean the descriptor here? Not really. + * Simple freeb for now (no chain and freeblist). + * Use ntq count for now. + */ + freeb(ctlr->tb[x]); + ctlr->tb[x] = nil; + d->control &= Eor; + + ctlr->ntq--; + } + ctlr->tdh = x; + + x = ctlr->tdt; + while(ctlr->ntq < (ctlr->ntd-1)){ + tb = &edev->tb[edev->ti]; + if(tb->owner != Interface) + break; + + bp = allocb(tb->len); + memmove(bp->wp, tb->pkt, tb->len); + memmove(bp->wp+Eaddrlen, edev->ea, Eaddrlen); + bp->wp += tb->len; + + tb->owner = Host; + edev->ti = NEXT(edev->ti, edev->ntb); + + d = &ctlr->td[x]; + d->addrlo = PCIWADDR(bp->rp); + d->addrhi = 0; + ctlr->tb[x] = bp; + coherence(); + d->control |= Own|Fs|Ls|((BLEN(bp)<ntd); + ctlr->ntq++; + } + if(x != ctlr->tdt){ + ctlr->tdt = x; + csr8w(ctlr, Tppoll, Npq); + } + else if(ctlr->ntq >= (ctlr->ntd-1)) + ctlr->txdu++; + + iunlock(&ctlr->tlock); +} + +static void +rtl8169receive(Ether* edev) +{ + D *d; + int len, rdh; + Ctlr *ctlr; + u32int control; + RingBuf *ring; + + ctlr = edev->ctlr; + + rdh = ctlr->rdh; + for(;;){ + d = &ctlr->rd[rdh]; + + if(d->control & Own) + break; + + control = d->control; + if((control & (Fs|Ls|Res)) == (Fs|Ls)){ + len = ((control & RxflMASK)>>RxflSHIFT) - 4; + + ring = &edev->rb[edev->ri]; + if(ring->owner == Interface){ + ring->owner = Host; + ring->len = len; + memmove(ring->pkt, ctlr->rb[rdh], len); + edev->ri = NEXT(edev->ri, edev->nrb); + } + } + else{ + /* + * Error stuff here. + print("control %8.8uX\n", control); + */ + } + d->control &= Eor; + ctlr->nrdfree--; + rdh = NEXT(rdh, ctlr->nrd); + } + ctlr->rdh = rdh; + + if(ctlr->nrdfree < ctlr->nrd/2) + rtl8169replenish(ctlr); +} + +static void +rtl8169interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *edev; + u32int isr; + + edev = arg; + ctlr = edev->ctlr; + + while((isr = csr16r(ctlr, Isr)) != 0){ + csr16w(ctlr, Isr, isr); + if(isr & (Fovw|Punlc|Rdu|Rer|Rok)){ + rtl8169receive(edev); + if(!(isr & (Punlc|Rok))) + ctlr->ierrs++; + if(isr & Rer) + ctlr->rer++; + if(isr & Rdu) + ctlr->rdu++; + if(isr & Punlc) + ctlr->punlc++; + if(isr & Fovw) + ctlr->fovw++; + isr &= ~(Fovw|Rdu|Rer|Rok); + } + + if(isr & (Tdu|Ter|Tok)){ + rtl8169transmit(edev); + isr &= ~(Tdu|Ter|Tok); + } + + if(isr & Punlc){ + //rtl8169link(edev); + isr &= ~Punlc; + } + + /* + * Some of the reserved bits get set sometimes... + */ + if(isr & (Serr|Timeout|Tdu|Fovw|Punlc|Rdu|Ter|Tok|Rer|Rok)) + panic("rtl8169interrupt: imr %4.4uX isr %4.4uX\n", + csr16r(ctlr, Imr), isr); + } +} + +static Ctlr* +rtl8169match(Ether* edev, int id) +{ + Pcidev *p; + Ctlr *ctlr; + int port; + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + p = ctlr->pcidev; + if(((p->did<<16)|p->vid) != id) + continue; + port = p->mem[0].bar & ~0x01; + if(edev->port != 0 && edev->port != port) + continue; + + ctlr->port = port; + if(rtl8169reset(ctlr)) + continue; + + csr8w(ctlr, 0x82, 1); /* magic */ + + rtl8169mii(ctlr); + + pcisetbme(p); + ctlr->active = 1; + return ctlr; + } + return nil; +} + +static struct { + char* name; + int id; +} rtl8169pci[] = { + { "rtl8169", (0x8169<<16)|0x10EC, }, /* generic */ + { "CG-LAPCIGT", (0xC107<<16)|0x1259, }, /* Corega CG-LAPCIGT */ + { nil }, +}; + +int +rtl8169pnp(Ether* edev) +{ + Pcidev *p; + Ctlr *ctlr; + int i, id; + uchar ea[Eaddrlen]; + + /* + * Make a list of all ethernet controllers + * if not already done. + */ + if(ctlrhead == nil){ + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + ctlr = malloc(sizeof(Ctlr)); + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } + } + + /* + * Is it an RTL8169 under a different name? + * Normally a search is made through all the found controllers + * for one which matches any of the known vid+did pairs. + * If a vid+did pair is specified a search is made for that + * specific controller only. + */ + id = 0; + for(i = 0; i < edev->nopt; i++){ + if(cistrncmp(edev->opt[i], "id=", 3) == 0) + id = strtol(&edev->opt[i][3], nil, 0); + } + + ctlr = nil; + if(id != 0) + ctlr = rtl8169match(edev, id); + else for(i = 0; rtl8169pci[i].name; i++){ + if((ctlr = rtl8169match(edev, rtl8169pci[i].id)) != nil) + break; + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pcidev->intl; + edev->tbdf = ctlr->pcidev->tbdf; + + /* + */ + memset(ea, 0, Eaddrlen); + i = csr32r(ctlr, Idr0); + edev->ea[0] = i; + edev->ea[1] = i>>8; + edev->ea[2] = i>>16; + edev->ea[3] = i>>24; + i = csr32r(ctlr, Idr0+4); + edev->ea[4] = i; + edev->ea[5] = i>>8; + + edev->attach = rtl8169attach; + edev->transmit = rtl8169transmit; + edev->interrupt = rtl8169interrupt; + edev->detach = rtl8169detach; + + return 0; +} diff --git a/os/boot/pc/ether82557.c b/os/boot/pc/ether82557.c new file mode 100644 index 00000000..1876692c --- /dev/null +++ b/os/boot/pc/ether82557.c @@ -0,0 +1,881 @@ +/* + * Intel 82557 Fast Ethernet PCI Bus LAN Controller + * as found on the Intel EtherExpress PRO/100B. This chip is full + * of smarts, unfortunately none of them are in the right place. + * To do: + * the PCI scanning code could be made common to other adapters; + * PCI code needs rewritten to handle byte, word, dword accesses + * and using the devno as a bus+dev+function triplet. + */ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "etherif.h" + +enum { + Nrfd = 4, /* receive frame area */ + + NullPointer = 0xFFFFFFFF, /* 82557 NULL pointer */ +}; + +enum { /* CSR */ + Status = 0x00, /* byte or word (word includes Ack) */ + Ack = 0x01, /* byte */ + CommandR = 0x02, /* byte or word (word includes Interrupt) */ + Interrupt = 0x03, /* byte */ + Pointer = 0x04, /* dword */ + Port = 0x08, /* dword */ + Fcr = 0x0C, /* Flash control register */ + Ecr = 0x0E, /* EEPROM control register */ + Mcr = 0x10, /* MDI control register */ +}; + +enum { /* Status */ + RUidle = 0x0000, + RUsuspended = 0x0004, + RUnoresources = 0x0008, + RUready = 0x0010, + RUrbd = 0x0020, /* bit */ + RUstatus = 0x003F, /* mask */ + + CUidle = 0x0000, + CUsuspended = 0x0040, + CUactive = 0x0080, + CUstatus = 0x00C0, /* mask */ + + StatSWI = 0x0400, /* SoftWare generated Interrupt */ + StatMDI = 0x0800, /* MDI r/w done */ + StatRNR = 0x1000, /* Receive unit Not Ready */ + StatCNA = 0x2000, /* Command unit Not Active (Active->Idle) */ + StatFR = 0x4000, /* Finished Receiving */ + StatCX = 0x8000, /* Command eXecuted */ + StatTNO = 0x8000, /* Transmit NOT OK */ +}; + +enum { /* Command (byte) */ + CUnop = 0x00, + CUstart = 0x10, + CUresume = 0x20, + LoadDCA = 0x40, /* Load Dump Counters Address */ + DumpSC = 0x50, /* Dump Statistical Counters */ + LoadCUB = 0x60, /* Load CU Base */ + ResetSA = 0x70, /* Dump and Reset Statistical Counters */ + + RUstart = 0x01, + RUresume = 0x02, + RUabort = 0x04, + LoadHDS = 0x05, /* Load Header Data Size */ + LoadRUB = 0x06, /* Load RU Base */ + RBDresume = 0x07, /* Resume frame reception */ +}; + +enum { /* Interrupt (byte) */ + InterruptM = 0x01, /* interrupt Mask */ + InterruptSI = 0x02, /* Software generated Interrupt */ +}; + +enum { /* Ecr */ + EEsk = 0x01, /* serial clock */ + EEcs = 0x02, /* chip select */ + EEdi = 0x04, /* serial data in */ + EEdo = 0x08, /* serial data out */ + + EEstart = 0x04, /* start bit */ + EEread = 0x02, /* read opcode */ +}; + +enum { /* Mcr */ + MDIread = 0x08000000, /* read opcode */ + MDIwrite = 0x04000000, /* write opcode */ + MDIready = 0x10000000, /* ready bit */ + MDIie = 0x20000000, /* interrupt enable */ +}; + +typedef struct Rfd { + int field; + ulong link; + ulong rbd; + ushort count; + ushort size; + + Etherpkt; +} Rfd; + +enum { /* field */ + RfdCollision = 0x00000001, + RfdIA = 0x00000002, /* IA match */ + RfdRxerr = 0x00000010, /* PHY character error */ + RfdType = 0x00000020, /* Type frame */ + RfdRunt = 0x00000080, + RfdOverrun = 0x00000100, + RfdBuffer = 0x00000200, + RfdAlignment = 0x00000400, + RfdCRC = 0x00000800, + + RfdOK = 0x00002000, /* frame received OK */ + RfdC = 0x00008000, /* reception Complete */ + RfdSF = 0x00080000, /* Simplified or Flexible (1) Rfd */ + RfdH = 0x00100000, /* Header RFD */ + + RfdI = 0x20000000, /* Interrupt after completion */ + RfdS = 0x40000000, /* Suspend after completion */ + RfdEL = 0x80000000, /* End of List */ +}; + +enum { /* count */ + RfdF = 0x00004000, + RfdEOF = 0x00008000, +}; + +typedef struct Cb { + int command; + ulong link; + uchar data[24]; /* CbIAS + CbConfigure */ +} Cb; + +typedef struct TxCB { + int command; + ulong link; + ulong tbd; + ushort count; + uchar threshold; + uchar number; +} TxCB; + +enum { /* action command */ + CbOK = 0x00002000, /* DMA completed OK */ + CbC = 0x00008000, /* execution Complete */ + + CbNOP = 0x00000000, + CbIAS = 0x00010000, /* Indvidual Address Setup */ + CbConfigure = 0x00020000, + CbMAS = 0x00030000, /* Multicast Address Setup */ + CbTransmit = 0x00040000, + CbDump = 0x00060000, + CbDiagnose = 0x00070000, + CbCommand = 0x00070000, /* mask */ + + CbSF = 0x00080000, /* CbTransmit */ + + CbI = 0x20000000, /* Interrupt after completion */ + CbS = 0x40000000, /* Suspend after completion */ + CbEL = 0x80000000, /* End of List */ +}; + +enum { /* CbTransmit count */ + CbEOF = 0x00008000, +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + + int eepromsz; /* address size in bits */ + ushort* eeprom; + + int ctlrno; + char* type; + + uchar configdata[24]; + + Rfd rfd[Nrfd]; + int rfdl; + int rfdx; + + Block* cbqhead; + Block* cbqtail; + int cbqbusy; +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +static uchar configdata[24] = { + 0x16, /* byte count */ + 0x44, /* Rx/Tx FIFO limit */ + 0x00, /* adaptive IFS */ + 0x00, + 0x04, /* Rx DMA maximum byte count */ + 0x84, /* Tx DMA maximum byte count */ + 0x33, /* late SCB, CNA interrupts */ + 0x01, /* discard short Rx frames */ + 0x00, /* 503/MII */ + + 0x00, + 0x2E, /* normal operation, NSAI */ + 0x00, /* linear priority */ + 0x60, /* inter-frame spacing */ + 0x00, + 0xF2, + 0x48, /* promiscuous mode off */ + 0x00, + 0x40, + 0xF2, /* transmit padding enable */ + 0x80, /* full duplex pin enable */ + 0x3F, /* no Multi IA */ + 0x05, /* no Multi Cast ALL */ +}; + +#define csr8r(c, r) (inb((c)->port+(r))) +#define csr16r(c, r) (ins((c)->port+(r))) +#define csr32r(c, r) (inl((c)->port+(r))) +#define csr8w(c, r, b) (outb((c)->port+(r), (int)(b))) +#define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w))) +#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l))) + +static void +custart(Ctlr* ctlr) +{ + if(ctlr->cbqhead == 0){ + ctlr->cbqbusy = 0; + return; + } + ctlr->cbqbusy = 1; + + csr32w(ctlr, Pointer, PADDR(ctlr->cbqhead->rp)); + while(csr8r(ctlr, CommandR)) + ; + csr8w(ctlr, CommandR, CUstart); +} + +static void +action(Ctlr* ctlr, Block* bp) +{ + Cb *cb; + + cb = (Cb*)bp->rp; + cb->command |= CbEL; + + if(ctlr->cbqhead){ + ctlr->cbqtail->next = bp; + cb = (Cb*)ctlr->cbqtail->rp; + cb->link = PADDR(bp->rp); + cb->command &= ~CbEL; + } + else + ctlr->cbqhead = bp; + ctlr->cbqtail = bp; + + if(ctlr->cbqbusy == 0) + custart(ctlr); +} + +static void +attach(Ether* ether) +{ + int status; + Ctlr *ctlr; + + ctlr = ether->ctlr; + status = csr16r(ctlr, Status); + if((status & RUstatus) == RUidle){ + csr32w(ctlr, Pointer, PADDR(&ctlr->rfd[ctlr->rfdx])); + while(csr8r(ctlr, CommandR)) + ; + csr8w(ctlr, CommandR, RUstart); + } +} + +static void +configure(void* arg, int promiscuous) +{ + Ctlr *ctlr; + Block *bp; + Cb *cb; + + ctlr = ((Ether*)arg)->ctlr; + + bp = allocb(sizeof(Cb)); + cb = (Cb*)bp->rp; + bp->wp += sizeof(Cb); + + cb->command = CbConfigure; + cb->link = NullPointer; + memmove(cb->data, ctlr->configdata, sizeof(ctlr->configdata)); + if(promiscuous) + cb->data[15] |= 0x01; + action(ctlr, bp); +} + +static void +transmit(Ether* ether) +{ + Block *bp; + TxCB *txcb; + RingBuf *tb; + + for(tb = ðer->tb[ether->ti]; tb->owner == Interface; tb = ðer->tb[ether->ti]){ + bp = allocb(tb->len+sizeof(TxCB)); + txcb = (TxCB*)bp->wp; + bp->wp += sizeof(TxCB); + + txcb->command = CbTransmit; + txcb->link = NullPointer; + txcb->tbd = NullPointer; + txcb->count = CbEOF|tb->len; + txcb->threshold = 2; + txcb->number = 0; + + memmove(bp->wp, tb->pkt, tb->len); + memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen); + bp->wp += tb->len; + + action(ether->ctlr, bp); + + tb->owner = Host; + ether->ti = NEXT(ether->ti, ether->ntb); + } +} + +static void +interrupt(Ureg*, void* arg) +{ + Rfd *rfd; + Block *bp; + Ctlr *ctlr; + Ether *ether; + int status; + RingBuf *rb; + + ether = arg; + ctlr = ether->ctlr; + + for(;;){ + status = csr16r(ctlr, Status); + csr8w(ctlr, Ack, (status>>8) & 0xFF); + + if((status & (StatCX|StatFR|StatCNA|StatRNR)) == 0) + return; + + if(status & StatFR){ + rfd = &ctlr->rfd[ctlr->rfdx]; + while(rfd->field & RfdC){ + rb = ðer->rb[ether->ri]; + if(rb->owner == Interface){ + rb->owner = Host; + rb->len = rfd->count & 0x3FFF; + memmove(rb->pkt, rfd->d, rfd->count & 0x3FFF); + ether->ri = NEXT(ether->ri, ether->nrb); + } + + /* + * Reinitialise the frame for reception and bump + * the receive frame processing index; + * bump the sentinel index, mark the new sentinel + * and clear the old sentinel suspend bit; + * set bp and rfd for the next receive frame to + * process. + */ + rfd->field = 0; + rfd->count = 0; + ctlr->rfdx = NEXT(ctlr->rfdx, Nrfd); + + rfd = &ctlr->rfd[ctlr->rfdl]; + ctlr->rfdl = NEXT(ctlr->rfdl, Nrfd); + ctlr->rfd[ctlr->rfdl].field |= RfdS; + rfd->field &= ~RfdS; + + rfd = &ctlr->rfd[ctlr->rfdx]; + } + status &= ~StatFR; + } + + if(status & StatRNR){ + while(csr8r(ctlr, CommandR)) + ; + csr8w(ctlr, CommandR, RUresume); + + status &= ~StatRNR; + } + + if(status & StatCNA){ + while(bp = ctlr->cbqhead){ + if((((Cb*)bp->rp)->command & CbC) == 0) + break; + ctlr->cbqhead = bp->next; + freeb(bp); + } + custart(ctlr); + + status &= ~StatCNA; + } + + if(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI)) + panic("%s#%d: status %uX\n", ctlr->type, ctlr->ctlrno, status); + } +} + +static void +ctlrinit(Ctlr* ctlr) +{ + int i; + Rfd *rfd; + ulong link; + + link = NullPointer; + for(i = Nrfd-1; i >= 0; i--){ + rfd = &ctlr->rfd[i]; + + rfd->field = 0; + rfd->link = link; + link = PADDR(rfd); + rfd->rbd = NullPointer; + rfd->count = 0; + rfd->size = sizeof(Etherpkt); + } + ctlr->rfd[Nrfd-1].link = PADDR(&ctlr->rfd[0]); + + ctlr->rfdl = 0; + ctlr->rfd[0].field |= RfdS; + ctlr->rfdx = 2; + + memmove(ctlr->configdata, configdata, sizeof(configdata)); +} + +static int +miir(Ctlr* ctlr, int phyadd, int regadd) +{ + int mcr, timo; + + csr32w(ctlr, Mcr, MDIread|(phyadd<<21)|(regadd<<16)); + mcr = 0; + for(timo = 64; timo; timo--){ + mcr = csr32r(ctlr, Mcr); + if(mcr & MDIready) + break; + microdelay(1); + } + + if(mcr & MDIready) + return mcr & 0xFFFF; + + return -1; +} + +static int +miiw(Ctlr* ctlr, int phyadd, int regadd, int data) +{ + int mcr, timo; + + csr32w(ctlr, Mcr, MDIwrite|(phyadd<<21)|(regadd<<16)|(data & 0xFFFF)); + mcr = 0; + for(timo = 64; timo; timo--){ + mcr = csr32r(ctlr, Mcr); + if(mcr & MDIready) + break; + microdelay(1); + } + + if(mcr & MDIready) + return 0; + + return -1; +} + +static int +hy93c46r(Ctlr* ctlr, int r) +{ + int data, i, op, size; + + /* + * Hyundai HY93C46 or equivalent serial EEPROM. + * This sequence for reading a 16-bit register 'r' + * in the EEPROM is taken straight from Section + * 3.3.4.2 of the Intel 82557 User's Guide. + */ +reread: + csr16w(ctlr, Ecr, EEcs); + op = EEstart|EEread; + for(i = 2; i >= 0; i--){ + data = (((op>>i) & 0x01)<<2)|EEcs; + csr16w(ctlr, Ecr, data); + csr16w(ctlr, Ecr, data|EEsk); + microdelay(1); + csr16w(ctlr, Ecr, data); + microdelay(1); + } + + /* + * First time through must work out the EEPROM size. + */ + if((size = ctlr->eepromsz) == 0) + size = 8; + + for(size = size-1; size >= 0; size--){ + data = (((r>>size) & 0x01)<<2)|EEcs; + csr16w(ctlr, Ecr, data); + csr16w(ctlr, Ecr, data|EEsk); + delay(1); + csr16w(ctlr, Ecr, data); + microdelay(1); + if(!(csr16r(ctlr, Ecr) & EEdo)) + break; + } + + data = 0; + for(i = 15; i >= 0; i--){ + csr16w(ctlr, Ecr, EEcs|EEsk); + microdelay(1); + if(csr16r(ctlr, Ecr) & EEdo) + data |= (1<eepromsz == 0){ + ctlr->eepromsz = 8-size; + ctlr->eeprom = malloc((1<eepromsz)*sizeof(ushort)); + goto reread; + } + + return data; +} + +static void +i82557pci(void) +{ + Pcidev *p; + Ctlr *ctlr; + + p = nil; + while(p = pcimatch(p, 0x8086, 0)){ + switch(p->did){ + default: + continue; + case 0x1031: /* Intel 82562EM */ + case 0x1050: /* Intel 82562EZ */ + case 0x2449: /* Intel 82562ET */ + case 0x1209: /* Intel 82559ER */ + case 0x1229: /* Intel 8255[789] */ + case 0x1030: /* Intel 82559 InBusiness 10/100 */ + case 0x1039: /* Intel 82801BD PRO/100 VE */ + case 0x103A: /* Intel 82562 PRO/100 VE */ + break; + } + + /* + * bar[0] is the memory-mapped register address (4KB), + * bar[1] is the I/O port register address (32 bytes) and + * bar[2] is for the flash ROM (1MB). + */ + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = p->mem[1].bar & ~0x01; + ctlr->pcidev = p; + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + + pcisetbme(p); + } +} + +static void +detach(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + + csr32w(ctlr, Port, 0); + delay(1); + + while(csr8r(ctlr, CommandR)) + ; +} + +static int +scanphy(Ctlr* ctlr) +{ + int i, oui, x; + + for(i = 0; i < 32; i++){ + if((oui = miir(ctlr, i, 2)) == -1 || oui == 0 || oui == 0xFFFF) + continue; + oui <<= 6; + x = miir(ctlr, i, 3); + oui |= x>>10; + //print("phy%d: oui %uX reg1 %uX\n", i, oui, miir(ctlr, i, 1)); + + if(oui == 0xAA00) + ctlr->eeprom[6] = 0x07<<8; + else if(oui == 0x80017){ + if(x & 0x01) + ctlr->eeprom[6] = 0x0A<<8; + else + ctlr->eeprom[6] = 0x04<<8; + } + return i; + } + return -1; +} + +int +i82557reset(Ether* ether) +{ + int anar, anlpar, bmcr, bmsr, force, i, phyaddr, x; + unsigned short sum; + Block *bp; + uchar ea[Eaddrlen]; + Ctlr *ctlr; + Cb *cb; + + + if(ctlrhead == nil) + i82557pci(); + + /* + * Any adapter matches if no ether->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(ether->port == 0 || ether->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + /* + * Initialise the Ctlr structure. + * Perform a software reset after which need to ensure busmastering + * is still enabled. The EtherExpress PRO/100B appears to leave + * the PCI configuration alone (see the 'To do' list above) so punt + * for now. + * Load the RUB and CUB registers for linear addressing (0). + */ + ether->ctlr = ctlr; + ether->port = ctlr->port; + ether->irq = ctlr->pcidev->intl; + ether->tbdf = ctlr->pcidev->tbdf; + ctlr->ctlrno = ether->ctlrno; + ctlr->type = ether->type; + + csr32w(ctlr, Port, 0); + delay(1); + + while(csr8r(ctlr, CommandR)) + ; + csr32w(ctlr, Pointer, 0); + csr8w(ctlr, CommandR, LoadRUB); + while(csr8r(ctlr, CommandR)) + ; + csr8w(ctlr, CommandR, LoadCUB); + + /* + * Initialise the action and receive frame areas. + */ + ctlrinit(ctlr); + + /* + * Read the EEPROM. + * Do a dummy read first to get the size + * and allocate ctlr->eeprom. + */ + hy93c46r(ctlr, 0); + sum = 0; + for(i = 0; i < (1<eepromsz); i++){ + x = hy93c46r(ctlr, i); + ctlr->eeprom[i] = x; + sum += x; + } + if(sum != 0xBABA) + print("#l%d: EEPROM checksum - 0x%4.4uX\n", ether->ctlrno, sum); + + /* + * Eeprom[6] indicates whether there is a PHY and whether + * it's not 10Mb-only, in which case use the given PHY address + * to set any PHY specific options and determine the speed. + * Unfortunately, sometimes the EEPROM is blank except for + * the ether address and checksum; in this case look at the + * controller type and if it's am 82558 or 82559 it has an + * embedded PHY so scan for that. + * If no PHY, assume 82503 (serial) operation. + */ + if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000)) + phyaddr = ctlr->eeprom[6] & 0x00FF; + else + switch(ctlr->pcidev->rid){ + case 0x01: /* 82557 A-step */ + case 0x02: /* 82557 B-step */ + case 0x03: /* 82557 C-step */ + default: + phyaddr = -1; + break; + case 0x04: /* 82558 A-step */ + case 0x05: /* 82558 B-step */ + case 0x06: /* 82559 A-step */ + case 0x07: /* 82559 B-step */ + case 0x08: /* 82559 C-step */ + case 0x09: /* 82559ER A-step */ + phyaddr = scanphy(ctlr); + break; + } + if(phyaddr >= 0){ + /* + * Resolve the highest common ability of the two + * link partners. In descending order: + * 0x0100 100BASE-TX Full Duplex + * 0x0200 100BASE-T4 + * 0x0080 100BASE-TX + * 0x0040 10BASE-T Full Duplex + * 0x0020 10BASE-T + */ + anar = miir(ctlr, phyaddr, 0x04); + anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0; + anar &= anlpar; + bmcr = 0; + if(anar & 0x380) + bmcr = 0x2000; + if(anar & 0x0140) + bmcr |= 0x0100; + + switch((ctlr->eeprom[6]>>8) & 0x001F){ + + case 0x04: /* DP83840 */ + case 0x0A: /* DP83840A */ + /* + * The DP83840[A] requires some tweaking for + * reliable operation. + * The manual says bit 10 should be unconditionally + * set although it supposedly only affects full-duplex + * operation (an & 0x0140). + */ + x = miir(ctlr, phyaddr, 0x17) & ~0x0520; + x |= 0x0420; + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "congestioncontrol")) + continue; + x |= 0x0100; + break; + } + miiw(ctlr, phyaddr, 0x17, x); + + /* + * If the link partner can't autonegotiate, determine + * the speed from elsewhere. + */ + if(anlpar == 0){ + miir(ctlr, phyaddr, 0x01); + bmsr = miir(ctlr, phyaddr, 0x01); + x = miir(ctlr, phyaddr, 0x19); + if((bmsr & 0x0004) && !(x & 0x0040)) + bmcr = 0x2000; + } + break; + + case 0x07: /* Intel 82555 */ + /* + * Auto-negotiation may fail if the other end is + * a DP83840A and the cable is short. + */ + bmsr = miir(ctlr, phyaddr, 0x01); + if((miir(ctlr, phyaddr, 0) & 0x1000) && !(bmsr & 0x0020)){ + miiw(ctlr, phyaddr, 0x1A, 0x2010); + x = miir(ctlr, phyaddr, 0); + miiw(ctlr, phyaddr, 0, 0x0200|x); + for(i = 0; i < 3000; i++){ + delay(1); + if(miir(ctlr, phyaddr, 0x01) & 0x0020) + break; + } + miiw(ctlr, phyaddr, 0x1A, 0x2000); + + anar = miir(ctlr, phyaddr, 0x04); + anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0; + anar &= anlpar; + bmcr = 0; + if(anar & 0x380) + bmcr = 0x2000; + if(anar & 0x0140) + bmcr |= 0x0100; + } + break; + } + + /* + * Force speed and duplex if no auto-negotiation. + */ + if(anlpar == 0){ + force = 0; + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "fullduplex") == 0){ + force = 1; + bmcr |= 0x0100; + ctlr->configdata[19] |= 0x40; + } + else if(cistrcmp(ether->opt[i], "speed") == 0){ + force = 1; + x = strtol(ðer->opt[i][6], 0, 0); + if(x == 10) + bmcr &= ~0x2000; + else if(x == 100) + bmcr |= 0x2000; + else + force = 0; + } + } + if(force) + miiw(ctlr, phyaddr, 0x00, bmcr); + } + + ctlr->configdata[8] = 1; + ctlr->configdata[15] &= ~0x80; + } + else{ + ctlr->configdata[8] = 0; + ctlr->configdata[15] |= 0x80; + } + + /* + * Load the chip configuration + */ + configure(ether, 0); + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to loading + * the station address with the Individual Address Setup command. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + for(i = 0; i < Eaddrlen/2; i++){ + x = ctlr->eeprom[i]; + ether->ea[2*i] = x & 0xFF; + ether->ea[2*i+1] = (x>>8) & 0xFF; + } + } + + bp = allocb(sizeof(Cb)); + cb = (Cb*)bp->rp; + bp->wp += sizeof(Cb); + + cb->command = CbIAS; + cb->link = NullPointer; + memmove(cb->data, ether->ea, Eaddrlen); + action(ctlr, bp); + + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->detach = detach; + + return 0; +} diff --git a/os/boot/pc/ether83815.c b/os/boot/pc/ether83815.c new file mode 100644 index 00000000..71a1c24d --- /dev/null +++ b/os/boot/pc/ether83815.c @@ -0,0 +1,825 @@ +/* + * National Semiconductor DP83815 + * + * Supports only internal PHY and has been tested on: + * Netgear FA311TX (using Netgear DS108 10/100 hub) + * To do: + * check Ethernet address; + * test autonegotiation on 10 Mbit, and 100 Mbit full duplex; + * external PHY via MII (should be common code for MII); + * thresholds; + * ring sizing; + * physical link changes/disconnect; + * push initialisation back to attach. + * + * C H Forsyth, forsyth@vitanuova.com, 18th June 2001. + */ + +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "etherif.h" + +#define DEBUG (1) +#define debug if(DEBUG)print + +enum { + Nrde = 8, + Ntde = 8, +}; + +#define Rbsz ROUNDUP(sizeof(Etherpkt)+4, 4) + +typedef struct Des { + ulong next; + int cmdsts; + ulong addr; + Block* bp; +} Des; + +enum { /* cmdsts */ + Own = 1<<31, /* set by data producer to hand to consumer */ + More = 1<<30, /* more of packet in next descriptor */ + Intr = 1<<29, /* interrupt when device is done with it */ + Supcrc = 1<<28, /* suppress crc on transmit */ + Inccrc = 1<<28, /* crc included on receive (always) */ + Ok = 1<<27, /* packet ok */ + Size = 0xFFF, /* packet size in bytes */ + + /* transmit */ + Txa = 1<<26, /* transmission aborted */ + Tfu = 1<<25, /* transmit fifo underrun */ + Crs = 1<<24, /* carrier sense lost */ + Td = 1<<23, /* transmission deferred */ + Ed = 1<<22, /* excessive deferral */ + Owc = 1<<21, /* out of window collision */ + Ec = 1<<20, /* excessive collisions */ + /* 19-16 collision count */ + + /* receive */ + Rxa = 1<<26, /* receive aborted (same as Rxo) */ + Rxo = 1<<25, /* receive overrun */ + Dest = 3<<23, /* destination class */ + Drej= 0<<23, /* packet was rejected */ + Duni= 1<<23, /* unicast */ + Dmulti= 2<<23, /* multicast */ + Dbroad= 3<<23, /* broadcast */ + Long = 1<<22, /* too long packet received */ + Runt = 1<<21, /* packet less than 64 bytes */ + Ise = 1<<20, /* invalid symbol */ + Crce = 1<<19, /* invalid crc */ + Fae = 1<<18, /* frame alignment error */ + Lbp = 1<<17, /* loopback packet */ + Col = 1<<16, /* collision during receive */ +}; + +enum { /* Variants */ + Nat83815 = (0x0020<<16)|0x100B, +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; /* (pcidev->did<<16)|pcidev->vid */ + + ushort srom[0xB+1]; + uchar sromea[Eaddrlen]; /* MAC address */ + + uchar fd; /* option or auto negotiation */ + + int mbps; + + Lock ilock; + + Des* rdr; /* receive descriptor ring */ + int nrdr; /* size of rdr */ + int rdrx; /* index into rdr */ + + Lock tlock; + Des* tdr; /* transmit descriptor ring */ + int ntdr; /* size of tdr */ + int tdrh; /* host index into tdr */ + int tdri; /* interface index into tdr */ + int ntq; /* descriptors active */ + int ntqmax; + Block* bqhead; /* transmission queue */ + Block* bqtail; + + ulong rxa; /* receive statistics */ + ulong rxo; + ulong rlong; + ulong runt; + ulong ise; + ulong crce; + ulong fae; + ulong lbp; + ulong col; + ulong rxsovr; + ulong rxorn; + + ulong txa; /* transmit statistics */ + ulong tfu; + ulong crs; + ulong td; + ulong ed; + ulong owc; + ulong ec; + ulong txurn; + + ulong dperr; /* system errors */ + ulong rmabt; + ulong rtabt; + ulong sserr; + ulong rxsover; +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +enum { + /* registers (could memory map) */ + Rcr= 0x00, /* command register */ + Rst= 1<<8, + Rxr= 1<<5, /* receiver reset */ + Txr= 1<<4, /* transmitter reset */ + Rxd= 1<<3, /* receiver disable */ + Rxe= 1<<2, /* receiver enable */ + Txd= 1<<1, /* transmitter disable */ + Txe= 1<<0, /* transmitter enable */ + Rcfg= 0x04, /* configuration */ + Lnksts= 1<<31, /* link good */ + Speed100= 1<<30, /* 100 Mb/s link */ + Fdup= 1<<29, /* full duplex */ + Pol= 1<<28, /* polarity reversal (10baseT) */ + Aneg_dn= 1<<27, /* autonegotiation done */ + Pint_acen= 1<<17, /* PHY interrupt auto clear enable */ + Pause_adv= 1<<16, /* advertise pause during auto neg */ + Paneg_ena= 1<<13, /* auto negotiation enable */ + Paneg_all= 7<<13, /* auto negotiation enable 10/100 half & full */ + Ext_phy= 1<<12, /* enable MII for external PHY */ + Phy_rst= 1<<10, /* reset internal PHY */ + Phy_dis= 1<<9, /* disable internal PHY (eg, low power) */ + Req_alg= 1<<7, /* PCI bus request: set means less aggressive */ + Sb= 1<<6, /* single slot back-off not random */ + Pow= 1<<5, /* out of window timer selection */ + Exd= 1<<4, /* disable excessive deferral timer */ + Pesel= 1<<3, /* parity error algorithm selection */ + Brom_dis= 1<<2, /* disable boot rom interface */ + Bem= 1<<0, /* big-endian mode */ + Rmear= 0x08, /* eeprom access */ + Mdc= 1<<6, /* MII mangement check */ + Mddir= 1<<5, /* MII management direction */ + Mdio= 1<<4, /* MII mangement data */ + Eesel= 1<<3, /* EEPROM chip select */ + Eeclk= 1<<2, /* EEPROM clock */ + Eedo= 1<<1, /* EEPROM data out (from chip) */ + Eedi= 1<<0, /* EEPROM data in (to chip) */ + Rptscr= 0x0C, /* pci test control */ + Risr= 0x10, /* interrupt status */ + Txrcmp= 1<<25, /* transmit reset complete */ + Rxrcmp= 1<<24, /* receiver reset complete */ + Dperr= 1<<23, /* detected parity error */ + Sserr= 1<<22, /* signalled system error */ + Rmabt= 1<<21, /* received master abort */ + Rtabt= 1<<20, /* received target abort */ + Rxsovr= 1<<16, /* RX status FIFO overrun */ + Hiberr= 1<<15, /* high bits error set (OR of 25-16) */ + Phy= 1<<14, /* PHY interrupt */ + Pme= 1<<13, /* power management event (wake online) */ + Swi= 1<<12, /* software interrupt */ + Mib= 1<<11, /* MIB service */ + Txurn= 1<<10, /* TX underrun */ + Txidle= 1<<9, /* TX idle */ + Txerr= 1<<8, /* TX packet error */ + Txdesc= 1<<7, /* TX descriptor (with Intr bit done) */ + Txok= 1<<6, /* TX ok */ + Rxorn= 1<<5, /* RX overrun */ + Rxidle= 1<<4, /* RX idle */ + Rxearly= 1<<3, /* RX early threshold */ + Rxerr= 1<<2, /* RX packet error */ + Rxdesc= 1<<1, /* RX descriptor (with Intr bit done) */ + Rxok= 1<<0, /* RX ok */ + Rimr= 0x14, /* interrupt mask */ + Rier= 0x18, /* interrupt enable */ + Ie= 1<<0, /* interrupt enable */ + Rtxdp= 0x20, /* transmit descriptor pointer */ + Rtxcfg= 0x24, /* transmit configuration */ + Csi= 1<<31, /* carrier sense ignore (needed for full duplex) */ + Hbi= 1<<30, /* heartbeat ignore (needed for full duplex) */ + Atp= 1<<28, /* automatic padding of runt packets */ + Mxdma= 7<<20, /* maximum dma transfer field */ + Mxdma32= 4<<20, /* 4x32-bit words (32 bytes) */ + Mxdma64= 5<<20, /* 8x32-bit words (64 bytes) */ + Flth= 0x3F<<8, /* Tx fill threshold, units of 32 bytes (must be > Mxdma) */ + Drth= 0x3F<<0, /* Tx drain threshold (units of 32 bytes) */ + Flth128= 4<<8, /* fill at 128 bytes */ + Drth512= 16<<0, /* drain at 512 bytes */ + Rrxdp= 0x30, /* receive descriptor pointer */ + Rrxcfg= 0x34, /* receive configuration */ + Atx= 1<<28, /* accept transmit packets (needed for full duplex) */ + Rdrth= 0x1F<<1, /* Rx drain threshold (units of 32 bytes) */ + Rdrth64= 2<<1, /* drain at 64 bytes */ + Rccsr= 0x3C, /* CLKRUN control/status */ + Pmests= 1<<15, /* PME status */ + Rwcsr= 0x40, /* wake on lan control/status */ + Rpcr= 0x44, /* pause control/status */ + Rrfcr= 0x48, /* receive filter/match control */ + Rfen= 1<<31, /* receive filter enable */ + Aab= 1<<30, /* accept all broadcast */ + Aam= 1<<29, /* accept all multicast */ + Aau= 1<<28, /* accept all unicast */ + Apm= 1<<27, /* accept on perfect match */ + Apat= 0xF<<23, /* accept on pattern match */ + Aarp= 1<<22, /* accept ARP */ + Mhen= 1<<21, /* multicast hash enable */ + Uhen= 1<<20, /* unicast hash enable */ + Ulm= 1<<19, /* U/L bit mask */ + /* bits 0-9 are rfaddr */ + Rrfdr= 0x4C, /* receive filter/match data */ + Rbrar= 0x50, /* boot rom address */ + Rbrdr= 0x54, /* boot rom data */ + Rsrr= 0x58, /* silicon revision */ + Rmibc= 0x5C, /* MIB control */ + /* 60-78 MIB data */ + + /* PHY registers */ + Rbmcr= 0x80, /* basic mode configuration */ + Reset= 1<<15, + Sel100= 1<<13, /* select 100Mb/sec if no auto neg */ + Anena= 1<<12, /* auto negotiation enable */ + Anrestart= 1<<9, /* restart auto negotiation */ + Selfdx= 1<<8, /* select full duplex if no auto neg */ + Rbmsr= 0x84, /* basic mode status */ + Ancomp= 1<<5, /* autonegotiation complete */ + Rphyidr1= 0x88, + Rphyidr2= 0x8C, + Ranar= 0x90, /* autonegotiation advertisement */ + Ranlpar= 0x94, /* autonegotiation link partner ability */ + Raner= 0x98, /* autonegotiation expansion */ + Rannptr= 0x9C, /* autonegotiation next page TX */ + Rphysts= 0xC0, /* PHY status */ + Rmicr= 0xC4, /* MII control */ + Inten= 1<<1, /* PHY interrupt enable */ + Rmisr= 0xC8, /* MII status */ + Rfcscr= 0xD0, /* false carrier sense counter */ + Rrecr= 0xD4, /* receive error counter */ + Rpcsr= 0xD8, /* 100Mb config/status */ + Rphycr= 0xE4, /* PHY control */ + Rtbscr= 0xE8, /* 10BaseT status/control */ +}; + +/* + * eeprom addresses + * 7 to 9 (16 bit words): mac address, shifted and reversed + */ + +#define csr32r(c, r) (inl((c)->port+(r))) +#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l))) +#define csr16r(c, r) (ins((c)->port+(r))) +#define csr16w(c, r, l) (outs((c)->port+(r), (ulong)(l))) + +static void +coherence(void) +{ +} + +static void +dumpcregs(Ctlr *ctlr) +{ + int i; + + for(i=0; i<=0x5C; i+=4) + print("%2.2ux %8.8lux\n", i, csr32r(ctlr, i)); +} + +static void +attach(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->ilock); + if(0) + dumpcregs(ctlr); + csr32w(ctlr, Rcr, Rxe); + iunlock(&ctlr->ilock); +} + +static void +detach(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + csr32w(ctlr, Rcr, 0); + delay(1); +} + +static void +txstart(Ether* ether) +{ + Ctlr *ctlr; + Block *bp; + Des *des; + int started; + + ctlr = ether->ctlr; + started = 0; + while(ctlr->ntq < ctlr->ntdr-1){ + bp = ctlr->bqhead; + if(bp == nil) + break; + ctlr->bqhead = bp->next; + des = &ctlr->tdr[ctlr->tdrh]; + des->bp = bp; + des->addr = PADDR(bp->rp); + ctlr->ntq++; + coherence(); + des->cmdsts = Own | BLEN(bp); + ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr); + started = 1; + } + if(started){ + coherence(); + csr32w(ctlr, Rcr, Txe); /* prompt */ + } + + if(ctlr->ntq > ctlr->ntqmax) + ctlr->ntqmax = ctlr->ntq; +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + Block *bp; + RingBuf *tb; + + ctlr = ether->ctlr; + ilock(&ctlr->tlock); + while((tb = ðer->tb[ether->ti])->owner == Interface){ + bp = allocb(tb->len); + memmove(bp->wp, tb->pkt, tb->len); + memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen); + bp->wp += tb->len; + if(ctlr->bqhead) + ctlr->bqtail->next = bp; + else + ctlr->bqhead = bp; + ctlr->bqtail = bp; + txstart(ether); + tb->owner = Host; + ether->ti = NEXT(ether->ti, ether->ntb); + } + iunlock(&ctlr->tlock); +} + +static void +txrxcfg(Ctlr *ctlr, int txdrth) +{ + ulong rx, tx; + + rx = csr32r(ctlr, Rrxcfg); + tx = csr32r(ctlr, Rtxcfg); + if(ctlr->fd){ + rx |= Atx; + tx |= Csi | Hbi; + }else{ + rx &= ~Atx; + tx &= ~(Csi | Hbi); + } + tx &= ~(Mxdma|Drth|Flth); + tx |= Mxdma64 | Flth128 | txdrth; + csr32w(ctlr, Rtxcfg, tx); + rx &= ~(Mxdma|Rdrth); + rx |= Mxdma64 | Rdrth64; + csr32w(ctlr, Rrxcfg, rx); +} + +static void +interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *ether; + int status, cmdsts; + Des *des; + RingBuf *rb; + + ether = arg; + ctlr = ether->ctlr; + + while((status = csr32r(ctlr, Risr)) != 0){ + + status &= ~(Pme|Mib); + status &= ~(Hiberr|Txrcmp|Rxrcmp|Rxsovr|Dperr|Sserr|Rmabt|Rtabt); + + /* + * Received packets. + */ + if(status & (Rxdesc|Rxok|Rxerr|Rxearly|Rxorn)){ + des = &ctlr->rdr[ctlr->rdrx]; + while((cmdsts = des->cmdsts) & Own){ + rb = ðer->rb[ether->ri]; + if(rb->owner == Interface && (cmdsts&Ok)){ + rb->len = (cmdsts&Size)-4; + memmove(rb->pkt, des->bp->rp, rb->len); + rb->owner = Host; + ether->ri = NEXT(ether->ri, ether->nrb); + } + + des->cmdsts = Rbsz; + coherence(); + + ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr); + des = &ctlr->rdr[ctlr->rdrx]; + } + status &= ~(Rxdesc|Rxok|Rxerr|Rxearly|Rxorn); + } + + /* + * Check the transmit side: + * check for Transmit Underflow and Adjust + * the threshold upwards; + * free any transmitted buffers and try to + * top-up the ring. + */ + if(status & Txurn){ + ctlr->txurn++; + ilock(&ctlr->ilock); + /* change threshold */ + iunlock(&ctlr->ilock); + status &= ~(Txurn); + } + + ilock(&ctlr->tlock); + while(ctlr->ntq){ + des = &ctlr->tdr[ctlr->tdri]; + cmdsts = des->cmdsts; + if(cmdsts & Own) + break; + + freeb(des->bp); + des->bp = nil; + des->cmdsts = 0; + + ctlr->ntq--; + ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr); + } + txstart(ether); + iunlock(&ctlr->tlock); + + status &= ~(Txurn|Txidle|Txerr|Txdesc|Txok); + + /* + * Anything left not catered for? + */ + if(status) + print("#l%d: status %8.8uX\n", ether->ctlrno, status); + } +} + +static void +ctlrinit(Ether* ether) +{ + Ctlr *ctlr; + Des *des, *last; + + ctlr = ether->ctlr; + + /* + * Allocate and initialise the receive ring; + * allocate and initialise the transmit ring; + * unmask interrupts and start the transmit side + */ + ctlr->rdr = malloc(ctlr->nrdr*sizeof(Des)); + last = nil; + for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){ + des->bp = allocb(Rbsz); + des->cmdsts = Rbsz; + des->addr = PADDR(des->bp->rp); + if(last != nil) + last->next = PADDR(des); + last = des; + } + ctlr->rdr[ctlr->nrdr-1].next = PADDR(ctlr->rdr); + ctlr->rdrx = 0; + csr32w(ctlr, Rrxdp, PADDR(ctlr->rdr)); + + ctlr->tdr = xspanalloc(ctlr->ntdr*sizeof(Des), 8*sizeof(ulong), 0); + last = nil; + for(des = ctlr->tdr; des < &ctlr->tdr[ctlr->ntdr]; des++){ + des->cmdsts = 0; + des->bp = nil; + des->addr = ~0; + if(last != nil) + last->next = PADDR(des); + last = des; + } + ctlr->tdr[ctlr->ntdr-1].next = PADDR(ctlr->tdr); + ctlr->tdrh = 0; + ctlr->tdri = 0; + csr32w(ctlr, Rtxdp, PADDR(ctlr->tdr)); + + txrxcfg(ctlr, Drth512); + + csr32w(ctlr, Rimr, Dperr|Sserr|Rmabt|Rtabt|Rxsovr|Hiberr|Txurn|Txerr|Txdesc|Txok|Rxorn|Rxerr|Rxdesc|Rxok); /* Phy|Pme|Mib */ + csr32r(ctlr, Risr); /* clear status */ + csr32w(ctlr, Rier, Ie); +} + +static void +eeclk(Ctlr *ctlr, int clk) +{ + csr32w(ctlr, Rmear, Eesel | clk); + microdelay(2); +} + +static void +eeidle(Ctlr *ctlr) +{ + int i; + + eeclk(ctlr, 0); + eeclk(ctlr, Eeclk); + for(i=0; i<25; i++){ + eeclk(ctlr, 0); + eeclk(ctlr, Eeclk); + } + eeclk(ctlr, 0); + csr32w(ctlr, Rmear, 0); + microdelay(2); +} + +static int +eegetw(Ctlr *ctlr, int a) +{ + int d, i, w, v; + + eeidle(ctlr); + eeclk(ctlr, 0); + eeclk(ctlr, Eeclk); + d = 0x180 | a; + for(i=0x400; i; i>>=1){ + v = (d & i) ? Eedi : 0; + eeclk(ctlr, v); + eeclk(ctlr, Eeclk|v); + } + eeclk(ctlr, 0); + + w = 0; + for(i=0x8000; i; i >>= 1){ + eeclk(ctlr, Eeclk); + if(csr32r(ctlr, Rmear) & Eedo) + w |= i; + microdelay(2); + eeclk(ctlr, 0); + } + eeidle(ctlr); + return w; +} + +static void +softreset(Ctlr* ctlr, int resetphys) +{ + int i, w; + + /* + * Soft-reset the controller + */ + csr32w(ctlr, Rcr, Rst); + for(i=0;; i++){ + if(i > 100) + panic("ns83815: soft reset did not complete"); + microdelay(250); + if((csr32r(ctlr, Rcr) & Rst) == 0) + break; + delay(1); + } + + csr32w(ctlr, Rccsr, Pmests); + csr32w(ctlr, Rccsr, 0); + csr32w(ctlr, Rcfg, csr32r(ctlr, Rcfg) | Pint_acen); + + if(resetphys){ + /* + * Soft-reset the PHY + */ + csr32w(ctlr, Rbmcr, Reset); + for(i=0;; i++){ + if(i > 100) + panic("ns83815: PHY soft reset time out"); + if((csr32r(ctlr, Rbmcr) & Reset) == 0) + break; + delay(1); + } + } + + /* + * Initialisation values, in sequence (see 4.4 Recommended Registers Configuration) + */ + csr16w(ctlr, 0xCC, 0x0001); /* PGSEL */ + csr16w(ctlr, 0xE4, 0x189C); /* PMCCSR */ + csr16w(ctlr, 0xFC, 0x0000); /* TSTDAT */ + csr16w(ctlr, 0xF4, 0x5040); /* DSPCFG */ + csr16w(ctlr, 0xF8, 0x008C); /* SDCFG */ + + /* + * Auto negotiate + */ + w = csr16r(ctlr, Rbmsr); /* clear latched bits */ + debug("anar: %4.4ux\n", csr16r(ctlr, Ranar)); + csr16w(ctlr, Rbmcr, Anena); + if(csr16r(ctlr, Ranar) == 0 || (csr32r(ctlr, Rcfg) & Aneg_dn) == 0){ + csr16w(ctlr, Rbmcr, Anena|Anrestart); + for(i=0;; i++){ + if(i > 6000){ + print("ns83815: auto neg timed out\n"); + break; + } + if((w = csr16r(ctlr, Rbmsr)) & Ancomp) + break; + delay(1); + } + debug("%d ms\n", i); + w &= 0xFFFF; + debug("bmsr: %4.4ux\n", w); + } + debug("anar: %4.4ux\n", csr16r(ctlr, Ranar)); + debug("anlpar: %4.4ux\n", csr16r(ctlr, Ranlpar)); + debug("aner: %4.4ux\n", csr16r(ctlr, Raner)); + debug("physts: %4.4ux\n", csr16r(ctlr, Rphysts)); + debug("tbscr: %4.4ux\n", csr16r(ctlr, Rtbscr)); +} + +static char* mediatable[9] = { + "10BASE-T", /* TP */ + "10BASE-2", /* BNC */ + "10BASE-5", /* AUI */ + "100BASE-TX", + "10BASE-TFD", + "100BASE-TXFD", + "100BASE-T4", + "100BASE-FX", + "100BASE-FXFD", +}; + +static void +srom(Ctlr* ctlr) +{ + int i, j; + + for(i = 0; i < nelem(ctlr->srom); i++) + ctlr->srom[i] = eegetw(ctlr, i); + + /* + * the MAC address is reversed, straddling word boundaries + */ + memset(ctlr->sromea, 0, sizeof(ctlr->sromea)); + j = 6*16 + 15; + for(i=0; i<48; i++){ + ctlr->sromea[i>>3] |= ((ctlr->srom[j>>4] >> (15-(j&0xF))) & 1) << (i&7); + j++; + } +} + +static void +scanpci83815(void) +{ + Ctlr *ctlr; + Pcidev *p; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + switch((p->did<<16)|p->vid){ + default: + continue; + + case Nat83815: + break; + } + + /* + * bar[0] is the I/O port register address and + * bar[1] is the memory-mapped register address. + */ + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = p->mem[0].bar & ~0x01; + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + + softreset(ctlr, 0); + srom(ctlr); + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + +int +ether83815reset(Ether* ether) +{ + Ctlr *ctlr; + int i, x; + uchar ea[Eaddrlen]; + static int scandone; + + if(scandone == 0){ + scanpci83815(); + scandone = 1; + } + + /* + * Any adapter matches if no ether->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(ether->port == 0 || ether->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + ether->ctlr = ctlr; + ether->port = ctlr->port; + ether->irq = ctlr->pcidev->intl; + ether->tbdf = ctlr->pcidev->tbdf; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to + * loading the station address in the hardware. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0) + memmove(ether->ea, ctlr->sromea, Eaddrlen); + for(i=0; iea[i] | (ether->ea[i+1]<<8); + csr32w(ctlr, Rrfcr, i); + csr32w(ctlr, Rrfdr, x); + } + csr32w(ctlr, Rrfcr, Rfen|Apm|Aab|Aam); + + /* + * Look for a medium override in case there's no autonegotiation + * the autonegotiation fails. + */ + + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "FD") == 0){ + ctlr->fd = 1; + continue; + } + for(x = 0; x < nelem(mediatable); x++){ + debug("compare <%s> <%s>\n", mediatable[x], + ether->opt[i]); + if(cistrcmp(mediatable[x], ether->opt[i]) == 0){ + switch(x){ + default: + ctlr->fd = 0; + break; + + case 0x04: /* 10BASE-TFD */ + case 0x05: /* 100BASE-TXFD */ + case 0x08: /* 100BASE-FXFD */ + ctlr->fd = 1; + break; + } + break; + } + } + } + + /* + * Initialise descriptor rings, ethernet address. + */ + ctlr->nrdr = Nrde; + ctlr->ntdr = Ntde; + pcisetbme(ctlr->pcidev); + ctlrinit(ether); + + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->detach = detach; + + return 0; +} diff --git a/os/boot/pc/ether8390.c b/os/boot/pc/ether8390.c new file mode 100644 index 00000000..ea4fedab --- /dev/null +++ b/os/boot/pc/ether8390.c @@ -0,0 +1,715 @@ +/* + * National Semiconductor DP8390 and clone + * Network Interface Controller. + */ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "etherif.h" +#include "ether8390.h" + +enum { /* NIC core registers */ + Cr = 0x00, /* command register, all pages */ + + /* Page 0, read */ + Clda0 = 0x01, /* current local DMA address 0 */ + Clda1 = 0x02, /* current local DMA address 1 */ + Bnry = 0x03, /* boundary pointer (R/W) */ + Tsr = 0x04, /* transmit status register */ + Ncr = 0x05, /* number of collisions register */ + Fifo = 0x06, /* FIFO */ + Isr = 0x07, /* interrupt status register (R/W) */ + Crda0 = 0x08, /* current remote DMA address 0 */ + Crda1 = 0x09, /* current remote DMA address 1 */ + Rsr = 0x0C, /* receive status register */ + Cntr0 = 0x0D, /* frame alignment errors */ + Cntr1 = 0x0E, /* CRC errors */ + Cntr2 = 0x0F, /* missed packet errors */ + + /* Page 0, write */ + Pstart = 0x01, /* page start register */ + Pstop = 0x02, /* page stop register */ + Tpsr = 0x04, /* transmit page start address */ + Tbcr0 = 0x05, /* transmit byte count register 0 */ + Tbcr1 = 0x06, /* transmit byte count register 1 */ + Rsar0 = 0x08, /* remote start address register 0 */ + Rsar1 = 0x09, /* remote start address register 1 */ + Rbcr0 = 0x0A, /* remote byte count register 0 */ + Rbcr1 = 0x0B, /* remote byte count register 1 */ + Rcr = 0x0C, /* receive configuration register */ + Tcr = 0x0D, /* transmit configuration register */ + Dcr = 0x0E, /* data configuration register */ + Imr = 0x0F, /* interrupt mask */ + + /* Page 1, read/write */ + Par0 = 0x01, /* physical address register 0 */ + Curr = 0x07, /* current page register */ + Mar0 = 0x08, /* multicast address register 0 */ +}; + +enum { /* Cr */ + Stp = 0x01, /* stop */ + Sta = 0x02, /* start */ + Txp = 0x04, /* transmit packet */ + Rd0 = 0x08, /* remote DMA command */ + Rd1 = 0x10, + Rd2 = 0x20, + RdREAD = Rd0, /* remote read */ + RdWRITE = Rd1, /* remote write */ + RdSEND = Rd1|Rd0, /* send packet */ + RdABORT = Rd2, /* abort/complete remote DMA */ + Ps0 = 0x40, /* page select */ + Ps1 = 0x80, + Page0 = 0x00, + Page1 = Ps0, + Page2 = Ps1, +}; + +enum { /* Isr/Imr */ + Prx = 0x01, /* packet received */ + Ptx = 0x02, /* packet transmitted */ + Rxe = 0x04, /* receive error */ + Txe = 0x08, /* transmit error */ + Ovw = 0x10, /* overwrite warning */ + Cnt = 0x20, /* counter overflow */ + Rdc = 0x40, /* remote DMA complete */ + Rst = 0x80, /* reset status */ +}; + +enum { /* Dcr */ + Wts = 0x01, /* word transfer select */ + Bos = 0x02, /* byte order select */ + Las = 0x04, /* long address select */ + Ls = 0x08, /* loopback select */ + Arm = 0x10, /* auto-initialise remote */ + Ft0 = 0x20, /* FIFO threshold select */ + Ft1 = 0x40, + Ft1WORD = 0x00, + Ft2WORD = Ft0, + Ft4WORD = Ft1, + Ft6WORD = Ft1|Ft0, +}; + +enum { /* Tcr */ + Crc = 0x01, /* inhibit CRC */ + Lb0 = 0x02, /* encoded loopback control */ + Lb1 = 0x04, + LpbkNORMAL = 0x00, /* normal operation */ + LpbkNIC = Lb0, /* internal NIC module loopback */ + LpbkENDEC = Lb1, /* internal ENDEC module loopback */ + LpbkEXTERNAL = Lb1|Lb0, /* external loopback */ + Atd = 0x08, /* auto transmit disable */ + Ofst = 0x10, /* collision offset enable */ +}; + +enum { /* Tsr */ + Ptxok = 0x01, /* packet transmitted */ + Col = 0x04, /* transmit collided */ + Abt = 0x08, /* tranmit aborted */ + Crs = 0x10, /* carrier sense lost */ + Fu = 0x20, /* FIFO underrun */ + Cdh = 0x40, /* CD heartbeat */ + Owc = 0x80, /* out of window collision */ +}; + +enum { /* Rcr */ + Sep = 0x01, /* save errored packets */ + Ar = 0x02, /* accept runt packets */ + Ab = 0x04, /* accept broadcast */ + Am = 0x08, /* accept multicast */ + Pro = 0x10, /* promiscuous physical */ + Mon = 0x20, /* monitor mode */ +}; + +enum { /* Rsr */ + Prxok = 0x01, /* packet received intact */ + Crce = 0x02, /* CRC error */ + Fae = 0x04, /* frame alignment error */ + Fo = 0x08, /* FIFO overrun */ + Mpa = 0x10, /* missed packet */ + Phy = 0x20, /* physical/multicast address */ + Dis = 0x40, /* receiver disabled */ + Dfr = 0x80, /* deferring */ +}; + +typedef struct { + uchar status; + uchar next; + uchar len0; + uchar len1; +} Hdr; + +void +dp8390getea(Ether* ether, uchar* ea) +{ + Dp8390 *ctlr; + uchar cr; + int i; + + ctlr = ether->ctlr; + + /* + * Get the ethernet address from the chip. + * Take care to restore the command register + * afterwards. + */ + ilock(ctlr); + cr = regr(ctlr, Cr) & ~Txp; + regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr)); + for(i = 0; i < Eaddrlen; i++) + ea[i] = regr(ctlr, Par0+i); + regw(ctlr, Cr, cr); + iunlock(ctlr); +} + +void +dp8390setea(Ether* ether) +{ + int i; + uchar cr; + Dp8390 *ctlr; + + ctlr = ether->ctlr; + + /* + * Set the ethernet address into the chip. + * Take care to restore the command register + * afterwards. Don't care about multicast + * addresses as multicast is never enabled + * (currently). + */ + ilock(ctlr); + cr = regr(ctlr, Cr) & ~Txp; + regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr)); + for(i = 0; i < Eaddrlen; i++) + regw(ctlr, Par0+i, ether->ea[i]); + regw(ctlr, Cr, cr); + iunlock(ctlr); +} + +static void* +_dp8390read(Dp8390* ctlr, void* to, ulong from, ulong len) +{ + uchar cr; + int timo; + + /* + * Read some data at offset 'from' in the card's memory + * using the DP8390 remote DMA facility, and place it at + * 'to' in main memory, via the I/O data port. + */ + cr = regr(ctlr, Cr) & ~Txp; + regw(ctlr, Cr, Page0|RdABORT|Sta); + regw(ctlr, Isr, Rdc); + + /* + * Set up the remote DMA address and count. + */ + len = ROUNDUP(len, ctlr->width); + regw(ctlr, Rbcr0, len & 0xFF); + regw(ctlr, Rbcr1, (len>>8) & 0xFF); + regw(ctlr, Rsar0, from & 0xFF); + regw(ctlr, Rsar1, (from>>8) & 0xFF); + + /* + * Start the remote DMA read and suck the data + * out of the I/O port. + */ + regw(ctlr, Cr, Page0|RdREAD|Sta); + rdread(ctlr, to, len); + + /* + * Wait for the remote DMA to complete. The timeout + * is necessary because this routine may be called on + * a non-existent chip during initialisation and, due + * to the miracles of the bus, it's possible to get this + * far and still be talking to a slot full of nothing. + */ + for(timo = 10000; (regr(ctlr, Isr) & Rdc) == 0 && timo; timo--) + ; + + regw(ctlr, Isr, Rdc); + regw(ctlr, Cr, cr); + + return to; +} + +void* +dp8390read(Dp8390* ctlr, void* to, ulong from, ulong len) +{ + void *v; + + ilock(ctlr); + v = _dp8390read(ctlr, to, from, len); + iunlock(ctlr); + + return v; +} + +static void* +dp8390write(Dp8390* ctlr, ulong to, void* from, ulong len) +{ + ulong crda; + uchar cr; + int timo, width; + +top: + /* + * Write some data to offset 'to' in the card's memory + * using the DP8390 remote DMA facility, reading it at + * 'from' in main memory, via the I/O data port. + */ + cr = regr(ctlr, Cr) & ~Txp; + regw(ctlr, Cr, Page0|RdABORT|Sta); + regw(ctlr, Isr, Rdc); + + len = ROUNDUP(len, ctlr->width); + + /* + * Set up the remote DMA address and count. + * This is straight from the DP8390[12D] datasheet, + * hence the initial set up for read. + * Assumption here that the A7000 EtherV card will + * never need a dummyrr. + */ + if(ctlr->dummyrr && (ctlr->width == 1 || ctlr->width == 2)){ + if(ctlr->width == 2) + width = 1; + else + width = 0; + crda = to-1-width; + regw(ctlr, Rbcr0, (len+1+width) & 0xFF); + regw(ctlr, Rbcr1, ((len+1+width)>>8) & 0xFF); + regw(ctlr, Rsar0, crda & 0xFF); + regw(ctlr, Rsar1, (crda>>8) & 0xFF); + regw(ctlr, Cr, Page0|RdREAD|Sta); + + for(timo=0;; timo++){ + if(timo > 10000){ + print("ether8390: dummyrr timeout; assuming nodummyrr\n"); + ctlr->dummyrr = 0; + goto top; + } + crda = regr(ctlr, Crda0); + crda |= regr(ctlr, Crda1)<<8; + if(crda == to){ + /* + * Start the remote DMA write and make sure + * the registers are correct. + */ + regw(ctlr, Cr, Page0|RdWRITE|Sta); + + crda = regr(ctlr, Crda0); + crda |= regr(ctlr, Crda1)<<8; + if(crda != to) + panic("crda write %d to %d\n", crda, to); + + break; + } + } + } + else{ + regw(ctlr, Rsar0, to & 0xFF); + regw(ctlr, Rsar1, (to>>8) & 0xFF); + regw(ctlr, Rbcr0, len & 0xFF); + regw(ctlr, Rbcr1, (len>>8) & 0xFF); + regw(ctlr, Cr, Page0|RdWRITE|Sta); + } + + /* + * Pump the data into the I/O port + * then wait for the remote DMA to finish. + */ + rdwrite(ctlr, from, len); + for(timo = 10000; (regr(ctlr, Isr) & Rdc) == 0 && timo; timo--) + ; + + regw(ctlr, Isr, Rdc); + regw(ctlr, Cr, cr); + + return (void*)to; +} + +static void +ringinit(Dp8390* ctlr) +{ + regw(ctlr, Pstart, ctlr->pstart); + regw(ctlr, Pstop, ctlr->pstop); + regw(ctlr, Bnry, ctlr->pstop-1); + + regw(ctlr, Cr, Page1|RdABORT|Stp); + regw(ctlr, Curr, ctlr->pstart); + regw(ctlr, Cr, Page0|RdABORT|Stp); + + ctlr->nxtpkt = ctlr->pstart; +} + +static uchar +getcurr(Dp8390* ctlr) +{ + uchar cr, curr; + + cr = regr(ctlr, Cr) & ~Txp; + regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr)); + curr = regr(ctlr, Curr); + regw(ctlr, Cr, cr); + + return curr; +} + +static void +receive(Ether* ether) +{ + Dp8390 *ctlr; + uchar curr, *p; + Hdr hdr; + ulong count, data, len; + RingBuf *ring; + + ctlr = ether->ctlr; + for(curr = getcurr(ctlr); ctlr->nxtpkt != curr; curr = getcurr(ctlr)){ + data = ctlr->nxtpkt*Dp8390BufSz; + if(ctlr->ram) + memmove(&hdr, (void*)(ether->mem+data), sizeof(Hdr)); + else + _dp8390read(ctlr, &hdr, data, sizeof(Hdr)); + + /* + * Don't believe the upper byte count, work it + * out from the software next-page pointer and + * the current next-page pointer. + */ + if(hdr.next > ctlr->nxtpkt) + len = hdr.next - ctlr->nxtpkt - 1; + else + len = (ctlr->pstop-ctlr->nxtpkt) + (hdr.next-ctlr->pstart) - 1; + if(hdr.len0 > (Dp8390BufSz-sizeof(Hdr))) + len--; + + len = ((len<<8)|hdr.len0)-4; + + /* + * Chip is badly scrogged, reinitialise the ring. + */ + if(hdr.next < ctlr->pstart || hdr.next >= ctlr->pstop + || len < 60 || len > sizeof(Etherpkt)){ + print("dp8390: H#%2.2ux#%2.2ux#%2.2ux#%2.2ux,%lud\n", + hdr.status, hdr.next, hdr.len0, hdr.len1, len); + regw(ctlr, Cr, Page0|RdABORT|Stp); + ringinit(ctlr); + regw(ctlr, Cr, Page0|RdABORT|Sta); + + return; + } + + /* + * If it's a good packet read it in to the software buffer. + * If the packet wraps round the hardware ring, read it in + * two pieces. + */ + ring = ðer->rb[ether->ri]; + if((hdr.status & (Fo|Fae|Crce|Prxok)) == Prxok && ring->owner == Interface){ + p = ring->pkt; + ring->len = len; + data += sizeof(Hdr); + + if((data+len) >= ctlr->pstop*Dp8390BufSz){ + count = ctlr->pstop*Dp8390BufSz - data; + if(ctlr->ram) + memmove(p, (void*)(ether->mem+data), count); + else + _dp8390read(ctlr, p, data, count); + p += count; + data = ctlr->pstart*Dp8390BufSz; + len -= count; + } + if(len){ + if(ctlr->ram) + memmove(p, (void*)(ether->mem+data), len); + else + _dp8390read(ctlr, p, data, len); + } + + /* + * Copy the packet to whoever wants it. + */ + ring->owner = Host; + ether->ri = NEXT(ether->ri, ether->nrb); + } + + /* + * Finished with this packet, update the + * hardware and software ring pointers. + */ + ctlr->nxtpkt = hdr.next; + + hdr.next--; + if(hdr.next < ctlr->pstart) + hdr.next = ctlr->pstop-1; + regw(ctlr, Bnry, hdr.next); + } +} + +static void +txstart(Ether* ether) +{ + int len; + Dp8390 *ctlr; + RingBuf *ring; + uchar minpkt[ETHERMINTU], *rp; + + ctlr = ether->ctlr; + + /* + * This routine is called both from the top level and from interrupt + * level and expects to be called with ctlr already locked. + */ + if(ether->tbusy) + return; + ring = ðer->tb[ether->ti]; + if(ring->owner != Interface) + return; + + /* + * Make sure the packet is of minimum length; + * copy it to the card's memory by the appropriate means; + * start the transmission. + */ + len = ring->len; + rp = ring->pkt; + if(len < ETHERMINTU){ + rp = minpkt; + memmove(rp, ring->pkt, len); + memset(rp+len, 0, ETHERMINTU-len); + len = ETHERMINTU; + } + + if(ctlr->ram) + memmove((void*)(ether->mem+ctlr->tstart*Dp8390BufSz), rp, len); + else + dp8390write(ctlr, ctlr->tstart*Dp8390BufSz, rp, len); + + regw(ctlr, Tbcr0, len & 0xFF); + regw(ctlr, Tbcr1, (len>>8) & 0xFF); + regw(ctlr, Cr, Page0|RdABORT|Txp|Sta); + + ether->tbusy = 1; +} + +static void +transmit(Ether* ether) +{ + Dp8390 *ctlr; + + ctlr = ether->ctlr; + + ilock(ctlr); + txstart(ether); + iunlock(ctlr); +} + +static void +overflow(Ether *ether) +{ + Dp8390 *ctlr; + uchar txp; + int resend; + + ctlr = ether->ctlr; + + /* + * The following procedure is taken from the DP8390[12D] datasheet, + * it seems pretty adamant that this is what has to be done. + */ + txp = regr(ctlr, Cr) & Txp; + regw(ctlr, Cr, Page0|RdABORT|Stp); + delay(2); + regw(ctlr, Rbcr0, 0); + regw(ctlr, Rbcr1, 0); + + resend = 0; + if(txp && (regr(ctlr, Isr) & (Txe|Ptx)) == 0) + resend = 1; + + regw(ctlr, Tcr, LpbkNIC); + regw(ctlr, Cr, Page0|RdABORT|Sta); + receive(ether); + regw(ctlr, Isr, Ovw); + regw(ctlr, Tcr, LpbkNORMAL); + + if(resend) + regw(ctlr, Cr, Page0|RdABORT|Txp|Sta); +} + +static void +interrupt(Ureg*, void* arg) +{ + Ether *ether; + Dp8390 *ctlr; + RingBuf *ring; + uchar isr, r; + + ether = arg; + ctlr = ether->ctlr; + + /* + * While there is something of interest, + * clear all the interrupts and process. + */ + ilock(ctlr); + regw(ctlr, Imr, 0x00); + while(isr = (regr(ctlr, Isr) & (Cnt|Ovw|Txe|Rxe|Ptx|Prx))){ + if(isr & Ovw){ + overflow(ether); + regw(ctlr, Isr, Ovw); + } + + /* + * Packets have been received. + * Take a spin round the ring. + */ + if(isr & (Rxe|Prx)){ + receive(ether); + regw(ctlr, Isr, Rxe|Prx); + } + + /* + * A packet completed transmission, successfully or + * not. Start transmission on the next buffered packet, + * and wake the output routine. + */ + if(isr & (Txe|Ptx)){ + r = regr(ctlr, Tsr); + if((isr & Txe) && (r & (Cdh|Fu|Crs|Abt))){ + print("dp8390: Tsr#%2.2ux|", r); + } + + regw(ctlr, Isr, Txe|Ptx); + + ring = ðer->tb[ether->ti]; + ring->owner = Host; + ether->ti = NEXT(ether->ti, ether->ntb); + ether->tbusy = 0; + txstart(ether); + } + + if(isr & Cnt){ + regr(ctlr, Cntr0); + regr(ctlr, Cntr1); + regr(ctlr, Cntr2); + regw(ctlr, Isr, Cnt); + } + } + regw(ctlr, Imr, Cnt|Ovw|Txe|Rxe|Ptx|Prx); + iunlock(ctlr); +} + +static void +attach(Ether* ether) +{ + Dp8390 *ctlr; + uchar r; + + ctlr = ether->ctlr; + + /* + * Enable the chip for transmit/receive. + * The init routine leaves the chip in monitor + * mode. Clear the missed-packet counter, it + * increments while in monitor mode. + * Sometimes there's an interrupt pending at this + * point but there's nothing in the Isr, so + * any pending interrupts are cleared and the + * mask of acceptable interrupts is enabled here. + */ + r = Ab; + ilock(ctlr); + regw(ctlr, Isr, 0xFF); + regw(ctlr, Imr, Cnt|Ovw|Txe|Rxe|Ptx|Prx); + regw(ctlr, Rcr, r); + r = regr(ctlr, Cntr2); + regw(ctlr, Tcr, LpbkNORMAL); + iunlock(ctlr); + USED(r); +} + +static void +detach(Ether* ether) +{ + int timo; + Dp8390 *ctlr; + + /* + * Stop the chip. Set the Stp bit and wait for the chip + * to finish whatever was on its tiny mind before it sets + * the Rst bit. + * The timeout is needed because there may not be a real + * chip there if this is called when probing for a device + * at boot. + */ + ctlr = ether->ctlr; + regw(ctlr, Cr, Page0|RdABORT|Stp); + regw(ctlr, Rbcr0, 0); + regw(ctlr, Rbcr1, 0); + for(timo = 10000; (regr(ctlr, Isr) & Rst) == 0 && timo; timo--) + ; +} + +int +dp8390reset(Ether* ether) +{ + Dp8390 *ctlr; + + ctlr = ether->ctlr; + + /* + * This is the initialisation procedure described + * as 'mandatory' in the datasheet, with references + * to the 3C503 technical reference manual. + */ + detach(ether); + if(ctlr->width != 1) + regw(ctlr, Dcr, Ft4WORD|Ls|Wts); + else + regw(ctlr, Dcr, Ft4WORD|Ls); + + regw(ctlr, Rbcr0, 0); + regw(ctlr, Rbcr1, 0); + + regw(ctlr, Tcr, LpbkNIC); + regw(ctlr, Rcr, Mon); + + /* + * Init the ring hardware and software ring pointers. + * Can't initialise ethernet address as it may not be + * known yet. + */ + ringinit(ctlr); + regw(ctlr, Tpsr, ctlr->tstart); + + /* + * Clear any pending interrupts and mask then all off. + */ + regw(ctlr, Isr, 0xFF); + regw(ctlr, Imr, 0); + + /* + * Leave the chip initialised, + * but in monitor mode. + */ + regw(ctlr, Cr, Page0|RdABORT|Sta); + + /* + * Set up the software configuration. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->detach = detach; + + return 0; +} diff --git a/os/boot/pc/ether8390.h b/os/boot/pc/ether8390.h new file mode 100644 index 00000000..1c464110 --- /dev/null +++ b/os/boot/pc/ether8390.h @@ -0,0 +1,71 @@ +/* + * Ctlr for the boards using the National Semiconductor DP8390 + * and SMC 83C90 Network Interface Controller. + * Common code is in ether8390.c. + */ +typedef struct { + Lock; + + ulong port; /* I/O address of 8390 */ + ulong data; /* I/O data port if no shared memory */ + + uchar width; /* data transfer width in bytes */ + uchar ram; /* true if card has shared memory */ + uchar dummyrr; /* do dummy remote read */ + + uchar nxtpkt; /* receive: software bndry */ + uchar pstart; + uchar pstop; + + int txbusy; /* transmit */ + uchar tstart; /* 8390 ring addresses */ +} Dp8390; + +#define Dp8390BufSz 256 + +extern int dp8390reset(Ether*); +extern void *dp8390read(Dp8390*, void*, ulong, ulong); +extern void dp8390getea(Ether*, uchar*); +extern void dp8390setea(Ether*); + +/* + * x86-specific code. + */ +#define regr(c, r) inb((c)->port+(r)) +#define regw(c, r, v) outb((c)->port+(r), (v)) + +static void +rdread(Dp8390* ctlr, void* to, int len) +{ + switch(ctlr->width){ + default: + panic("dp8390 rdread: width %d\n", ctlr->width); + break; + + case 2: + inss(ctlr->data, to, len/2); + break; + + case 1: + insb(ctlr->data, to, len); + break; + } +} + +static void +rdwrite(Dp8390* ctlr, void* from, int len) +{ + switch(ctlr->width){ + default: + panic("dp8390 rdwrite: width %d\n", ctlr->width); + break; + + case 2: + outss(ctlr->data, from, len/2); + break; + + case 1: + outsb(ctlr->data, from, len); + break; + } +} diff --git a/os/boot/pc/etherec2t.c b/os/boot/pc/etherec2t.c new file mode 100644 index 00000000..4d91cd25 --- /dev/null +++ b/os/boot/pc/etherec2t.c @@ -0,0 +1,155 @@ +/* + * Supposed NE2000 PCMCIA clones, see the comments in ether2000.c + */ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "etherif.h" +#include "ether8390.h" + +enum { + Data = 0x10, /* offset from I/O base of data port */ + Reset = 0x1F, /* offset from I/O base of reset port */ +}; + +static char* ec2tpcmcia[] = { + "EC2T", /* Linksys Combo PCMCIA EthernetCard */ + "PCMPC100", /* EtherFast 10/100 PC Card */ + "EN2216", /* Accton EtherPair-PCMCIA */ + "FA410TX", /* Netgear FA410TX */ + "Network Everywhere", /* Linksys NP10T 10BaseT Card */ + nil, +}; + +int +ec2treset(Ether* ether) +{ + ushort buf[16]; + ulong port; + Dp8390 *ctlr; + int i, slot; + uchar ea[Eaddrlen], sum, x; + char *type; + + /* + * Set up the software configuration. + * Use defaults for port, irq, mem and size + * if not specified. + * The manual says 16KB memory, the box + * says 32KB. The manual seems to be correct. + */ + if(ether->port == 0) + ether->port = 0x300; + if(ether->irq == 0) + ether->irq = 9; + if(ether->mem == 0) + ether->mem = 0x4000; + if(ether->size == 0) + ether->size = 16*1024; + port = ether->port; + + //if(ioalloc(ether->port, 0x20, 0, "ec2t") < 0) + // return -1; + slot = -1; + type = nil; + for(i = 0; ec2tpcmcia[i] != nil; i++){ + type = ec2tpcmcia[i]; + if((slot = pcmspecial(type, ether)) >= 0) + break; + } + if(ec2tpcmcia[i] == nil){ + for(i = 0; i < ether->nopt; i++){ + if(cistrncmp(ether->opt[i], "id=", 3)) + continue; + type = ðer->opt[i][3]; + if((slot = pcmspecial(type, ether)) >= 0) + break; + } + } + if(slot < 0){ + // iofree(port); + return -1; + } + + ether->ctlr = malloc(sizeof(Dp8390)); + ctlr = ether->ctlr; + ctlr->width = 2; + ctlr->ram = 0; + + ctlr->port = port; + ctlr->data = port+Data; + + ctlr->tstart = HOWMANY(ether->mem, Dp8390BufSz); + ctlr->pstart = ctlr->tstart + HOWMANY(sizeof(Etherpkt), Dp8390BufSz); + ctlr->pstop = ctlr->tstart + HOWMANY(ether->size, Dp8390BufSz); + + ctlr->dummyrr = 0; + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "nodummyrr") == 0) + ctlr->dummyrr = 0; + else if(cistrncmp(ether->opt[i], "dummyrr=", 8) == 0) + ctlr->dummyrr = strtol(ðer->opt[i][8], nil, 0); + } + + /* + * Reset the board. This is done by doing a read + * followed by a write to the Reset address. + */ + buf[0] = inb(port+Reset); + delay(2); + outb(port+Reset, buf[0]); + delay(2); + + /* + * Init the (possible) chip, then use the (possible) + * chip to read the (possible) PROM for ethernet address + * and a marker byte. + * Could just look at the DP8390 command register after + * initialisation has been tried, but that wouldn't be + * enough, there are other ethernet boards which could + * match. + */ + dp8390reset(ether); + sum = 0; + if(cistrcmp(type, "PCMPC100") == 0 || cistrcmp(type, "FA410TX") == 0){ + /* + * The PCMPC100 has the ethernet address in I/O space. + * There's a checksum over 8 bytes which sums to 0xFF. + */ + for(i = 0; i < 8; i++){ + x = inb(port+0x14+i); + sum += x; + buf[i] = (x<<8)|x; + } + } + else{ + memset(buf, 0, sizeof(buf)); + dp8390read(ctlr, buf, 0, sizeof(buf)); + if((buf[0x0E] & 0xFF) == 0x57 && (buf[0x0F] & 0xFF) == 0x57) + sum = 0xFF; + } + if(sum != 0xFF){ + pcmspecialclose(slot); + //iofree(ether->port); + free(ether->ctlr); + return -1; + } + + /* + * Stupid machine. Shorts were asked for, + * shorts were delivered, although the PROM is a byte array. + * Set the ethernet address. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + for(i = 0; i < sizeof(ether->ea); i++) + ether->ea[i] = buf[i]; + } + dp8390setea(ether); + + return 0; +} diff --git a/os/boot/pc/etherelnk3.c b/os/boot/pc/etherelnk3.c new file mode 100644 index 00000000..618f7a1b --- /dev/null +++ b/os/boot/pc/etherelnk3.c @@ -0,0 +1,1898 @@ +/* + * Etherlink III, Fast EtherLink and Fast EtherLink XL adapters. + * To do: + * check robustness in the face of errors (e.g. busmaster & rxUnderrun); + * RxEarly and busmaster; + * autoSelect; + * PCI latency timer and master enable; + * errata list; + * rewrite all initialisation. + * + * Product ID: + * 9150 ISA 3C509[B] + * 9050 ISA 3C509[B]-TP + * 9450 ISA 3C509[B]-COMBO + * 9550 ISA 3C509[B]-TPO + * + * 9350 EISA 3C579 + * 9250 EISA 3C579-TP + * + * 5920 EISA 3C592-[TP|COMBO|TPO] + * 5970 EISA 3C597-TX Fast Etherlink 10BASE-T/100BASE-TX + * 5971 EISA 3C597-T4 Fast Etherlink 10BASE-T/100BASE-T4 + * 5972 EISA 3C597-MII Fast Etherlink 10BASE-T/MII + * + * 5900 PCI 3C590-[TP|COMBO|TPO] + * 5950 PCI 3C595-TX Fast Etherlink Shared 10BASE-T/100BASE-TX + * 5951 PCI 3C595-T4 Fast Etherlink Shared 10BASE-T/100BASE-T4 + * 5952 PCI 3C595-MII Fast Etherlink 10BASE-T/MII + * + * 9000 PCI 3C900-TPO Etherlink III XL PCI 10BASE-T + * 9001 PCI 3C900-COMBO Etherlink III XL PCI 10BASE-T/10BASE-2/AUI + * 9005 PCI 3C900B-COMBO Etherlink III XL PCI 10BASE-T/10BASE-2/AUI + * 9050 PCI 3C905-TX Fast Etherlink XL Shared 10BASE-T/100BASE-TX + * 9051 PCI 3C905-T4 Fast Etherlink Shared 10BASE-T/100BASE-T4 + * 9055 PCI 3C905B-TX Fast Etherlink Shared 10BASE-T/100BASE-TX + * 9200 PCI 3C905C-TX Fast Etherlink Shared 10BASE-T/100BASE-TX + * + * 9058 PCMCIA 3C589[B]-[TP|COMBO] + * + * 627C MCA 3C529 + * 627D MCA 3C529-TP + */ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "etherif.h" + +#define coherence() +#define XCVRDEBUG if(0)print + +enum { + IDport = 0x0110, /* anywhere between 0x0100 and 0x01F0 */ +}; + +enum { /* all windows */ + CommandR = 0x000E, + IntStatusR = 0x000E, +}; + +enum { /* Commands */ + GlobalReset = 0x0000, + SelectRegisterWindow = 0x0001, + EnableDcConverter = 0x0002, + RxDisable = 0x0003, + RxEnable = 0x0004, + RxReset = 0x0005, + Stall = 0x0006, /* 3C90x */ + TxDone = 0x0007, + RxDiscard = 0x0008, + TxEnable = 0x0009, + TxDisable = 0x000A, + TxReset = 0x000B, + RequestInterrupt = 0x000C, + AcknowledgeInterrupt = 0x000D, + SetInterruptEnable = 0x000E, + SetIndicationEnable = 0x000F, /* SetReadZeroMask */ + SetRxFilter = 0x0010, + SetRxEarlyThresh = 0x0011, + SetTxAvailableThresh = 0x0012, + SetTxStartThresh = 0x0013, + StartDma = 0x0014, /* initiate busmaster operation */ + StatisticsEnable = 0x0015, + StatisticsDisable = 0x0016, + DisableDcConverter = 0x0017, + SetTxReclaimThresh = 0x0018, /* PIO-only adapters */ + PowerUp = 0x001B, /* not all adapters */ + PowerDownFull = 0x001C, /* not all adapters */ + PowerAuto = 0x001D, /* not all adapters */ +}; + +enum { /* (Global|Rx|Tx)Reset command bits */ + tpAuiReset = 0x0001, /* 10BaseT and AUI transceivers */ + endecReset = 0x0002, /* internal Ethernet encoder/decoder */ + networkReset = 0x0004, /* network interface logic */ + fifoReset = 0x0008, /* FIFO control logic */ + aismReset = 0x0010, /* autoinitialise state-machine logic */ + hostReset = 0x0020, /* bus interface logic */ + dmaReset = 0x0040, /* bus master logic */ + vcoReset = 0x0080, /* on-board 10Mbps VCO */ + updnReset = 0x0100, /* upload/download (Rx/TX) logic */ + + resetMask = 0x01FF, +}; + +enum { /* Stall command bits */ + upStall = 0x0000, + upUnStall = 0x0001, + dnStall = 0x0002, + dnUnStall = 0x0003, +}; + +enum { /* SetRxFilter command bits */ + receiveIndividual = 0x0001, /* match station address */ + receiveMulticast = 0x0002, + receiveBroadcast = 0x0004, + receiveAllFrames = 0x0008, /* promiscuous */ +}; + +enum { /* StartDma command bits */ + Upload = 0x0000, /* transfer data from adapter to memory */ + Download = 0x0001, /* transfer data from memory to adapter */ +}; + +enum { /* IntStatus bits */ + interruptLatch = 0x0001, + hostError = 0x0002, /* Adapter Failure */ + txComplete = 0x0004, + txAvailable = 0x0008, + rxComplete = 0x0010, + rxEarly = 0x0020, + intRequested = 0x0040, + updateStats = 0x0080, + transferInt = 0x0100, /* Bus Master Transfer Complete */ + dnComplete = 0x0200, + upComplete = 0x0400, + busMasterInProgress = 0x0800, + commandInProgress = 0x1000, + + interruptMask = 0x07FE, +}; + +#define COMMAND(port, cmd, a) outs((port)+CommandR, ((cmd)<<11)|(a)) +#define STATUS(port) ins((port)+IntStatusR) + +enum { /* Window 0 - setup */ + Wsetup = 0x0000, + /* registers */ + ManufacturerID = 0x0000, /* 3C5[08]*, 3C59[27] */ + ProductID = 0x0002, /* 3C5[08]*, 3C59[27] */ + ConfigControl = 0x0004, /* 3C5[08]*, 3C59[27] */ + AddressConfig = 0x0006, /* 3C5[08]*, 3C59[27] */ + ResourceConfig = 0x0008, /* 3C5[08]*, 3C59[27] */ + EepromCommand = 0x000A, + EepromData = 0x000C, + /* AddressConfig Bits */ + autoSelect9 = 0x0080, + xcvrMask9 = 0xC000, + /* ConfigControl bits */ + Ena = 0x0001, + base10TAvailable9 = 0x0200, + coaxAvailable9 = 0x1000, + auiAvailable9 = 0x2000, + /* EepromCommand bits */ + _EepromReadRegister = 0x0080, + _EepromRead8bRegister = 0x0230, + EepromBusy = 0x8000, +}; + +static int EepromReadRegister = _EepromReadRegister; + +#define EEPROMCMD(port, cmd, a) outs((port)+EepromCommand, (cmd)|(a)) +#define EEPROMBUSY(port) (ins((port)+EepromCommand) & EepromBusy) +#define EEPROMDATA(port) ins((port)+EepromData) + +enum { /* Window 1 - operating set */ + Wop = 0x0001, + /* registers */ + Fifo = 0x0000, + RxError = 0x0004, /* 3C59[0257] only */ + RxStatus = 0x0008, + Timer = 0x000A, + TxStatus = 0x000B, + TxFree = 0x000C, + /* RxError bits */ + rxOverrun = 0x0001, + runtFrame = 0x0002, + alignmentError = 0x0004, /* Framing */ + crcError = 0x0008, + oversizedFrame = 0x0010, + dribbleBits = 0x0080, + /* RxStatus bits */ + rxBytes = 0x1FFF, /* 3C59[0257] mask */ + rxBytes9 = 0x07FF, /* 3C5[078]9 mask */ + rxError9 = 0x3800, /* 3C5[078]9 error mask */ + rxOverrun9 = 0x0000, + oversizedFrame9 = 0x0800, + dribbleBits9 = 0x1000, + runtFrame9 = 0x1800, + alignmentError9 = 0x2000, /* Framing */ + crcError9 = 0x2800, + rxError = 0x4000, + rxIncomplete = 0x8000, + /* TxStatus Bits */ + txStatusOverflow = 0x0004, + maxCollisions = 0x0008, + txUnderrun = 0x0010, + txJabber = 0x0020, + interruptRequested = 0x0040, + txStatusComplete = 0x0080, +}; + +enum { /* Window 2 - station address */ + Wstation = 0x0002, + + ResetOp905B = 0x000C, +}; + +enum { /* Window 3 - FIFO management */ + Wfifo = 0x0003, + /* registers */ + InternalConfig = 0x0000, /* 3C509B, 3C589, 3C59[0257] */ + OtherInt = 0x0004, /* 3C59[0257] */ + RomControl = 0x0006, /* 3C509B, 3C59[27] */ + MacControl = 0x0006, /* 3C59[0257] */ + ResetOptions = 0x0008, /* 3C59[0257] */ + MediaOptions = 0x0008, /* 3C905B */ + RxFree = 0x000A, + /* InternalConfig bits */ + disableBadSsdDetect = 0x00000100, + ramLocation = 0x00000200, /* 0 external, 1 internal */ + ramPartition5to3 = 0x00000000, + ramPartition3to1 = 0x00010000, + ramPartition1to1 = 0x00020000, + ramPartition3to5 = 0x00030000, + ramPartitionMask = 0x00030000, + xcvr10BaseT = 0x00000000, + xcvrAui = 0x00100000, /* 10BASE5 */ + xcvr10Base2 = 0x00300000, + xcvr100BaseTX = 0x00400000, + xcvr100BaseFX = 0x00500000, + xcvrMii = 0x00600000, + xcvrMask = 0x00700000, + autoSelect = 0x01000000, + /* MacControl bits */ + deferExtendEnable = 0x0001, + deferTimerSelect = 0x001E, /* mask */ + fullDuplexEnable = 0x0020, + allowLargePackets = 0x0040, + extendAfterCollision = 0x0080, /* 3C90xB */ + flowControlEnable = 0x0100, /* 3C90xB */ + vltEnable = 0x0200, /* 3C90xB */ + /* ResetOptions bits */ + baseT4Available = 0x0001, + baseTXAvailable = 0x0002, + baseFXAvailable = 0x0004, + base10TAvailable = 0x0008, + coaxAvailable = 0x0010, + auiAvailable = 0x0020, + miiConnector = 0x0040, +}; + +enum { /* Window 4 - diagnostic */ + Wdiagnostic = 0x0004, + /* registers */ + VcoDiagnostic = 0x0002, + FifoDiagnostic = 0x0004, + NetworkDiagnostic = 0x0006, + PhysicalMgmt = 0x0008, + MediaStatus = 0x000A, + BadSSD = 0x000C, + UpperBytesOk = 0x000D, + /* FifoDiagnostic bits */ + txOverrun = 0x0400, + rxUnderrun = 0x2000, + receiving = 0x8000, + /* PhysicalMgmt bits */ + mgmtClk = 0x0001, + mgmtData = 0x0002, + mgmtDir = 0x0004, + cat5LinkTestDefeat = 0x8000, + /* MediaStatus bits */ + dataRate100 = 0x0002, + crcStripDisable = 0x0004, + enableSqeStats = 0x0008, + collisionDetect = 0x0010, + carrierSense = 0x0020, + jabberGuardEnable = 0x0040, + linkBeatEnable = 0x0080, + jabberDetect = 0x0200, + polarityReversed = 0x0400, + linkBeatDetect = 0x0800, + txInProg = 0x1000, + dcConverterEnabled = 0x4000, + auiDisable = 0x8000, /* 10BaseT transceiver selected */ +}; + +enum { /* Window 5 - internal state */ + Wstate = 0x0005, + /* registers */ + TxStartThresh = 0x0000, + TxAvailableThresh = 0x0002, + RxEarlyThresh = 0x0006, + RxFilter = 0x0008, + InterruptEnable = 0x000A, + IndicationEnable = 0x000C, +}; + +enum { /* Window 6 - statistics */ + Wstatistics = 0x0006, + /* registers */ + CarrierLost = 0x0000, + SqeErrors = 0x0001, + MultipleColls = 0x0002, + SingleCollFrames = 0x0003, + LateCollisions = 0x0004, + RxOverruns = 0x0005, + FramesXmittedOk = 0x0006, + FramesRcvdOk = 0x0007, + FramesDeferred = 0x0008, + UpperFramesOk = 0x0009, + BytesRcvdOk = 0x000A, + BytesXmittedOk = 0x000C, +}; + +enum { /* Window 7 - bus master operations */ + Wmaster = 0x0007, + /* registers */ + MasterAddress = 0x0000, + MasterLen = 0x0006, + MasterStatus = 0x000C, + /* MasterStatus bits */ + masterAbort = 0x0001, + targetAbort = 0x0002, + targetRetry = 0x0004, + targetDisc = 0x0008, + masterDownload = 0x1000, + masterUpload = 0x4000, + masterInProgress = 0x8000, + + masterMask = 0xD00F, +}; + +enum { /* 3C90x extended register set */ + Timer905 = 0x001A, /* 8-bits */ + TxStatus905 = 0x001B, /* 8-bits */ + PktStatus = 0x0020, /* 32-bits */ + DnListPtr = 0x0024, /* 32-bits, 8-byte aligned */ + FragAddr = 0x0028, /* 32-bits */ + FragLen = 0x002C, /* 16-bits */ + ListOffset = 0x002E, /* 8-bits */ + TxFreeThresh = 0x002F, /* 8-bits */ + UpPktStatus = 0x0030, /* 32-bits */ + FreeTimer = 0x0034, /* 16-bits */ + UpListPtr = 0x0038, /* 32-bits, 8-byte aligned */ + + /* PktStatus bits */ + fragLast = 0x00000001, + dnCmplReq = 0x00000002, + dnStalled = 0x00000004, + upCompleteX = 0x00000008, + dnCompleteX = 0x00000010, + upRxEarlyEnable = 0x00000020, + armCountdown = 0x00000040, + dnInProg = 0x00000080, + counterSpeed = 0x00000010, /* 0 3.2uS, 1 320nS */ + countdownMode = 0x00000020, + /* UpPktStatus bits (dpd->control) */ + upPktLenMask = 0x00001FFF, + upStalled = 0x00002000, + upError = 0x00004000, + upPktComplete = 0x00008000, + upOverrun = 0x00010000, /* RxError<<16 */ + upRuntFrame = 0x00020000, + upAlignmentError = 0x00040000, + upCRCError = 0x00080000, + upOversizedFrame = 0x00100000, + upDribbleBits = 0x00800000, + upOverflow = 0x01000000, + + dnIndicate = 0x80000000, /* FrameStartHeader (dpd->control) */ + + updnLastFrag = 0x80000000, /* (dpd->len) */ + + Nup = 32, + Ndn = 64, +}; + +/* + * Up/Dn Packet Descriptors. + * The hardware info (np, control, addr, len) must be 8-byte aligned + * and this structure size must be a multiple of 8. + */ +typedef struct Pd Pd; +typedef struct Pd { + ulong np; /* next pointer */ + ulong control; /* FSH or UpPktStatus */ + ulong addr; + ulong len; + + Pd* next; + void *vaddr; +} Pd; + +typedef struct { + Lock wlock; /* window access */ + + int attached; + int busmaster; + Block* rbp; /* receive buffer */ + + Block* txbp; /* FIFO -based transmission */ + int txthreshold; + int txbusy; + + int nup; /* full-busmaster -based reception */ + void* upbase; + Pd* upr; + Pd* uphead; + + int ndn; /* full-busmaster -based transmission */ + void* dnbase; + Pd* dnr; + Pd* dnhead; + Pd* dntail; + int dnq; + + long interrupts; /* statistics */ + long timer[2]; + long stats[BytesRcvdOk+3]; + + int upqmax; + int upqmaxhw; + ulong upinterrupts; + ulong upqueued; + ulong upstalls; + int dnqmax; + int dnqmaxhw; + ulong dninterrupts; + ulong dnqueued; + + int xcvr; /* transceiver type */ + int rxstatus9; /* old-style RxStatus register */ + int rxearly; /* RxEarlyThreshold */ + int ts; /* threshold shift */ + int upenabled; + int dnenabled; +} Ctlr; + +static void +init905(Ctlr* ctlr) +{ + Pd *pd, *prev; + uchar *vaddr; + + /* + * Create rings for the receive and transmit sides. + * Take care with alignment: + * make sure ring base is 8-byte aligned; + * make sure each entry is 8-byte aligned. + */ + ctlr->upbase = malloc((ctlr->nup+1)*sizeof(Pd)); + ctlr->upr = (Pd*)ROUNDUP((ulong)ctlr->upbase, 8); + vaddr = ialloc((ctlr->nup+1)*ROUNDUP(sizeof(Etherpkt)+4, 8), 8); + + prev = ctlr->upr; + for(pd = &ctlr->upr[ctlr->nup-1]; pd >= ctlr->upr; pd--){ + pd->np = PADDR(&prev->np); + pd->control = 0; + pd->vaddr = vaddr; + pd->addr = PADDR(vaddr); + vaddr += ROUNDUP(sizeof(Etherpkt)+4, 8); + pd->len = updnLastFrag|sizeof(Etherpkt); + + pd->next = prev; + prev = pd; + } + ctlr->uphead = ctlr->upr; + + ctlr->dnbase = malloc((ctlr->ndn+1)*sizeof(Pd)); + ctlr->dnr = (Pd*)ROUNDUP((ulong)ctlr->dnbase, 8); + vaddr = ialloc((ctlr->ndn+1)*ROUNDUP(sizeof(Etherpkt)+4, 8), 8); + + prev = ctlr->dnr; + for(pd = &ctlr->dnr[ctlr->ndn-1]; pd >= ctlr->dnr; pd--){ + pd->next = prev; + pd->vaddr = vaddr; + pd->addr = PADDR(vaddr); + vaddr += ROUNDUP(sizeof(Etherpkt)+4, 8); + prev = pd; + } + ctlr->dnhead = ctlr->dnr; + ctlr->dntail = ctlr->dnr; + ctlr->dnq = 0; +} + +static Block* +rbpalloc(Block* (*f)(int)) +{ + Block *bp; + ulong addr; + + /* + * The receive buffers must be on a 32-byte + * boundary for EISA busmastering. + */ + if(bp = f(ROUNDUP(sizeof(Etherpkt), 4) + 31)){ + addr = (ulong)bp->base; + addr = ROUNDUP(addr, 32); + bp->rp = (uchar*)addr; + } + + return bp; +} + +static uchar* +startdma(Ether* ether, ulong address) +{ + int port, status, w; + uchar *wp; + + port = ether->port; + + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wmaster); + + wp = KADDR(inl(port+MasterAddress)); + status = ins(port+MasterStatus); + if(status & (masterInProgress|targetAbort|masterAbort)) + print("#l%d: BM status 0x%uX\n", ether->ctlrno, status); + outs(port+MasterStatus, masterMask); + outl(port+MasterAddress, address); + outs(port+MasterLen, sizeof(Etherpkt)); + COMMAND(port, StartDma, Upload); + + COMMAND(port, SelectRegisterWindow, w); + return wp; +} + +/* On the 575B and C, interrupts need to be acknowledged in CardBus memory space */ +static void +intrack3c575(ulong *cbfns) +{ + cbfns[1] = 0x8000; +} + +static void +attach(Ether* ether) +{ + int port, x; + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->wlock); + if(ctlr->attached){ + iunlock(&ctlr->wlock); + return; + } + + port = ether->port; + + COMMAND(port, SetRxFilter, receiveIndividual|receiveBroadcast); + x = interruptMask; + if(ctlr->busmaster == 1) + x &= ~(rxEarly|rxComplete); + else{ + if(ctlr->dnenabled) + x &= ~transferInt; + if(ctlr->upenabled) + x &= ~(rxEarly|rxComplete); + } + COMMAND(port, SetIndicationEnable, x); + COMMAND(port, SetInterruptEnable, x); + COMMAND(port, RxEnable, 0); + COMMAND(port, TxEnable, 0); + + if (ether->mem) + /* This must be a cardbus card. Acknowledge the interrupt */ + intrack3c575(KADDR(ether->mem)); + + /* + * Prime the busmaster channel for receiving directly into a + * receive packet buffer if necessary. + */ + if(ctlr->busmaster == 1) + startdma(ether, PADDR(ctlr->rbp->rp)); + else{ + if(ctlr->upenabled) + outl(port+UpListPtr, PADDR(&ctlr->uphead->np)); + } + + ctlr->attached = 1; + iunlock(&ctlr->wlock); + +} + +static void +statistics(Ether* ether) +{ + int port, i, u, w; + Ctlr *ctlr; + + port = ether->port; + ctlr = ether->ctlr; + + /* + * 3C59[27] require a read between a PIO write and + * reading a statistics register. + */ + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wstatistics); + STATUS(port); + + for(i = 0; i < UpperFramesOk; i++) + ctlr->stats[i] += inb(port+i) & 0xFF; + u = inb(port+UpperFramesOk) & 0xFF; + ctlr->stats[FramesXmittedOk] += (u & 0x30)<<4; + ctlr->stats[FramesRcvdOk] += (u & 0x03)<<8; + ctlr->stats[BytesRcvdOk] += ins(port+BytesRcvdOk) & 0xFFFF; + ctlr->stats[BytesRcvdOk+1] += ins(port+BytesXmittedOk) & 0xFFFF; + + switch(ctlr->xcvr){ + + case xcvrMii: + case xcvr100BaseTX: + case xcvr100BaseFX: + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + STATUS(port); + ctlr->stats[BytesRcvdOk+2] += inb(port+BadSSD); + break; + } + + COMMAND(port, SelectRegisterWindow, w); +} + +static void +txstart(Ether* ether) +{ + int port, len; + Ctlr *ctlr; + RingBuf *tb; + + port = ether->port; + ctlr = ether->ctlr; + + /* + * Attempt to top-up the transmit FIFO. If there's room simply + * stuff in the packet length (unpadded to a dword boundary), the + * packet data (padded) and remove the packet from the queue. + * If there's no room post an interrupt for when there is. + * This routine is called both from the top level and from interrupt + * level and expects to be called with ctlr->wlock already locked + * and the correct register window (Wop) in place. + */ + for(tb = ðer->tb[ether->ti]; tb->owner == Interface; tb = ðer->tb[ether->ti]){ + len = ROUNDUP(tb->len, 4); + if(len+4 <= ins(port+TxFree)){ + outl(port+Fifo, tb->len); + outsl(port+Fifo, tb->pkt, len/4); + tb->owner = Host; + ether->ti = NEXT(ether->ti, ether->ntb); + } + else{ + if(ctlr->txbusy == 0){ + ctlr->txbusy = 1; + COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts); + } + break; + } + } +} + +static void +txstart905(Ether* ether) +{ + Ctlr *ctlr; + int port, stalled, timeo; + RingBuf *tb; + Pd *pd; + + ctlr = ether->ctlr; + port = ether->port; + + /* + * Free any completed packets. + */ + pd = ctlr->dntail; + while(ctlr->dnq){ + if(PADDR(&pd->np) == inl(port+DnListPtr)) + break; + ctlr->dnq--; + pd = pd->next; + } + ctlr->dntail = pd; + + stalled = 0; + while(ctlr->dnq < (ctlr->ndn-1)){ + tb = ðer->tb[ether->ti]; + if(tb->owner != Interface) + break; + + pd = ctlr->dnhead->next; + pd->np = 0; + pd->control = dnIndicate|tb->len; + memmove(pd->vaddr, tb->pkt, tb->len); + pd->len = updnLastFrag|tb->len; + + tb->owner = Host; + ether->ti = NEXT(ether->ti, ether->ntb); + + if(stalled == 0 && ctlr->dnq && inl(port+DnListPtr)){ + COMMAND(port, Stall, dnStall); + for(timeo = 100; (STATUS(port) & commandInProgress) && timeo; timeo--) + ; + if(timeo == 0) + print("#l%d: dnstall %d\n", ether->ctlrno, timeo); + stalled = 1; + } + + coherence(); + ctlr->dnhead->np = PADDR(&pd->np); + ctlr->dnhead->control &= ~dnIndicate; + ctlr->dnhead = pd; + if(ctlr->dnq == 0) + ctlr->dntail = pd; + ctlr->dnq++; + + ctlr->dnqueued++; + } + + if(ctlr->dnq > ctlr->dnqmax) + ctlr->dnqmax = ctlr->dnq; + + /* + * If the adapter is not currently processing anything + * and there is something on the queue, start it processing. + */ + if(inl(port+DnListPtr) == 0 && ctlr->dnq) + outl(port+DnListPtr, PADDR(&ctlr->dnhead->np)); + if(stalled) + COMMAND(port, Stall, dnUnStall); +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + int port, w; + + port = ether->port; + ctlr = ether->ctlr; + + ilock(&ctlr->wlock); + if(ctlr->dnenabled) + txstart905(ether); + else{ + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wop); + txstart(ether); + COMMAND(port, SelectRegisterWindow, w); + } + iunlock(&ctlr->wlock); +} + +static void +receive905(Ether* ether) +{ + Ctlr *ctlr; + int len, port, q; + Pd *pd; + RingBuf *rb; + + ctlr = ether->ctlr; + port = ether->port; + + if(inl(port+UpPktStatus) & upStalled) + ctlr->upstalls++; + q = 0; + for(pd = ctlr->uphead; pd->control & upPktComplete; pd = pd->next){ + if(!(pd->control & upError)){ + rb = ðer->rb[ether->ri]; + if (rb->owner == Interface) { + len = pd->control & rxBytes; + rb->len = len; + memmove(rb->pkt, pd->vaddr, len); + rb->owner = Host; + ether->ri = NEXT(ether->ri, ether->nrb); + } + } + + pd->control = 0; + COMMAND(port, Stall, upUnStall); + + q++; + } + ctlr->uphead = pd; + + ctlr->upqueued += q; + if(q > ctlr->upqmax) + ctlr->upqmax = q; +} + +static void +receive(Ether* ether) +{ + int len, port, rxstatus; + RingBuf *rb; + Ctlr *ctlr; + + port = ether->port; + ctlr = ether->ctlr; + + while(((rxstatus = ins(port+RxStatus)) & rxIncomplete) == 0){ + if(ctlr->busmaster == 1 && (STATUS(port) & busMasterInProgress)) + break; + + /* + * If there was an error, log it and continue. + * Unfortunately the 3C5[078]9 has the error info in the status register + * and the 3C59[0257] implement a separate RxError register. + */ + if((rxstatus & rxError) == 0){ + /* + * Packet received. Read it into the next free + * ring buffer, if any. Must read len bytes padded + * to a doubleword, can be picked out 32-bits at + * a time. The CRC is already stripped off. + */ + rb = ðer->rb[ether->ri]; + if(rb->owner == Interface){ + len = (rxstatus & rxBytes9); + rb->len = len; + insl(port+Fifo, rb->pkt, HOWMANY(len, 4)); + + rb->owner = Host; + ether->ri = NEXT(ether->ri, ether->nrb); + }else +if(debug) print("toss..."); + } +else +if(debug) print("error..."); + + /* + * All done, discard the packet. + */ + COMMAND(port, RxDiscard, 0); + while(STATUS(port) & commandInProgress) + ; + } +} + +static void +interrupt(Ureg*, void* arg) +{ + Ether *ether; + int port, status, s, txstatus, w, x; + Ctlr *ctlr; + + ether = arg; + port = ether->port; + ctlr = ether->ctlr; + + ilock(&ctlr->wlock); + status = STATUS(port); + if(!(status & (interruptMask|interruptLatch))){ + iunlock(&ctlr->wlock); + return; + } + w = (status>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wop); + + ctlr->interrupts++; + if(ctlr->busmaster == 2) + ctlr->timer[0] += inb(port+Timer905) & 0xFF; + else + ctlr->timer[0] += inb(port+Timer) & 0xFF; + + do{ + if(status & hostError){ + /* + * Adapter failure, try to find out why, reset if + * necessary. What happens if Tx is active and a reset + * occurs, need to retransmit? This probably isn't right. + */ + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+FifoDiagnostic); + COMMAND(port, SelectRegisterWindow, Wop); + print("#l%d: status 0x%uX, diag 0x%uX\n", + ether->ctlrno, status, x); + + if(x & txOverrun){ + if(ctlr->busmaster == 0) + COMMAND(port, TxReset, 0); + else + COMMAND(port, TxReset, (updnReset|dmaReset)); + COMMAND(port, TxEnable, 0); + } + + if(x & rxUnderrun){ + /* + * This shouldn't happen... + * Reset the receiver and restore the filter and RxEarly + * threshold before re-enabling. + * Need to restart any busmastering? + */ + COMMAND(port, SelectRegisterWindow, Wstate); + s = (port+RxFilter) & 0x000F; + COMMAND(port, SelectRegisterWindow, Wop); + COMMAND(port, RxReset, 0); + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, SetRxFilter, s); + COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts); + COMMAND(port, RxEnable, 0); + } + + status &= ~hostError; + } + + if(status & (transferInt|rxComplete)){ + receive(ether); + status &= ~(transferInt|rxComplete); + } + + if(status & (upComplete)){ + COMMAND(port, AcknowledgeInterrupt, upComplete); + receive905(ether); + status &= ~upComplete; + ctlr->upinterrupts++; + } + + if(status & txComplete){ + /* + * Pop the TxStatus stack, accumulating errors. + * Adjust the TX start threshold if there was an underrun. + * If there was a Jabber or Underrun error, reset + * the transmitter, taking care not to reset the dma logic + * as a busmaster receive may be in progress. + * For all conditions enable the transmitter. + */ + if(ctlr->busmaster == 2) + txstatus = port+TxStatus905; + else + txstatus = port+TxStatus; + s = 0; + do{ + if(x = inb(txstatus)) + outb(txstatus, 0); + s |= x; + }while(STATUS(port) & txComplete); + + if(s & txUnderrun){ + if(ctlr->dnenabled){ + while(inl(port+PktStatus) & dnInProg) + ; + } + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + while(ins(port+MediaStatus) & txInProg) + ; + COMMAND(port, SelectRegisterWindow, Wop); + if(ctlr->txthreshold < ETHERMAXTU) + ctlr->txthreshold += ETHERMINTU; + } + + /* + * According to the manual, maxCollisions does not require + * a TxReset, merely a TxEnable. However, evidence points to + * it being necessary on the 3C905. The jury is still out. + * On busy or badly configured networks maxCollisions can + * happen frequently enough for messages to be annoying so + * keep quiet about them by popular request. + */ + if(s & (txJabber|txUnderrun|maxCollisions)){ + if(ctlr->busmaster == 0) + COMMAND(port, TxReset, 0); + else + COMMAND(port, TxReset, (updnReset|dmaReset)); + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts); + if(ctlr->busmaster == 2) + outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256)); + if(ctlr->dnenabled) + status |= dnComplete; + } + + if(s & ~(txStatusComplete|maxCollisions)) + print("#l%d: txstatus 0x%uX, threshold %d\n", + ether->ctlrno, s, ctlr->txthreshold); + COMMAND(port, TxEnable, 0); + status &= ~txComplete; + status |= txAvailable; + } + + if(status & txAvailable){ + COMMAND(port, AcknowledgeInterrupt, txAvailable); + ctlr->txbusy = 0; + txstart(ether); + status &= ~txAvailable; + } + + if(status & dnComplete){ + COMMAND(port, AcknowledgeInterrupt, dnComplete); + txstart905(ether); + status &= ~dnComplete; + ctlr->dninterrupts++; + } + + if(status & updateStats){ + statistics(ether); + status &= ~updateStats; + } + + /* + * Currently, this shouldn't happen. + */ + if(status & rxEarly){ + COMMAND(port, AcknowledgeInterrupt, rxEarly); + status &= ~rxEarly; + } + + /* + * Panic if there are any interrupts not dealt with. + */ + if(status & interruptMask) + panic("#l%d: interrupt mask 0x%uX\n", ether->ctlrno, status); + + COMMAND(port, AcknowledgeInterrupt, interruptLatch); + if (ether->mem) + intrack3c575((ulong *)KADDR(ether->mem)); + + }while((status = STATUS(port)) & (interruptMask|interruptLatch)); + + if(ctlr->busmaster == 2) + ctlr->timer[1] += inb(port+Timer905) & 0xFF; + else + ctlr->timer[1] += inb(port+Timer) & 0xFF; + + COMMAND(port, SelectRegisterWindow, w); + iunlock(&ctlr->wlock); +} + +static void +txrxreset(int port) +{ + COMMAND(port, TxReset, 0); + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, RxReset, 0); + while(STATUS(port) & commandInProgress) + ; +} + +typedef struct Adapter { + int port; + int irq; + int tbdf; + ulong cbfns; +} Adapter; +static Block* adapter; + +static void +tcmadapter(int port, int irq, int tbdf, ulong cbfns) +{ + Block *bp; + Adapter *ap; + + bp = allocb(sizeof(Adapter)); + ap = (Adapter*)bp->rp; + ap->port = port; + ap->irq = irq; + ap->tbdf = tbdf; + ap->cbfns = cbfns; + + bp->next = adapter; + adapter = bp; +} + +/* + * Write two 0 bytes to identify the IDport and then reset the + * ID sequence. Then send the ID sequence to the card to get + * the card into command state. + */ +static void +idseq(void) +{ + int i; + uchar al; + static int reset, untag; + + /* + * One time only: + * reset any adapters listening + */ + if(reset == 0){ + outb(IDport, 0); + outb(IDport, 0); + outb(IDport, 0xC0); + delay(20); + reset = 1; + } + + outb(IDport, 0); + outb(IDport, 0); + for(al = 0xFF, i = 0; i < 255; i++){ + outb(IDport, al); + if(al & 0x80){ + al <<= 1; + al ^= 0xCF; + } + else + al <<= 1; + } + + /* + * One time only: + * write ID sequence to get the attention of all adapters; + * untag all adapters. + * If a global reset is done here on all adapters it will confuse + * any ISA cards configured for EISA mode. + */ + if(untag == 0){ + outb(IDport, 0xD0); + untag = 1; + } +} + +static ulong +activate(void) +{ + int i; + ushort x, acr; + + /* + * Do the little configuration dance: + * + * 2. write the ID sequence to get to command state. + */ + idseq(); + + /* + * 3. Read the Manufacturer ID from the EEPROM. + * This is done by writing the IDPort with 0x87 (0x80 + * is the 'read EEPROM' command, 0x07 is the offset of + * the Manufacturer ID field in the EEPROM). + * The data comes back 1 bit at a time. + * A delay seems necessary between reading the bits. + * + * If the ID doesn't match, there are no more adapters. + */ + outb(IDport, 0x87); + delay(20); + for(x = 0, i = 0; i < 16; i++){ + delay(20); + x <<= 1; + x |= inb(IDport) & 0x01; + } + if(x != 0x6D50) + return 0; + + /* + * 3. Read the Address Configuration from the EEPROM. + * The Address Configuration field is at offset 0x08 in the EEPROM). + */ + outb(IDport, 0x88); + for(acr = 0, i = 0; i < 16; i++){ + delay(20); + acr <<= 1; + acr |= inb(IDport) & 0x01; + } + + return (acr & 0x1F)*0x10 + 0x200; +} + +static void +tcm509isa(void) +{ + int irq, port; + + /* + * Attempt to activate all adapters. If adapter is set for + * EISA mode (0x3F0), tag it and ignore. Otherwise, activate + * it fully. + */ + while(port = activate()){ + /* + * 6. Tag the adapter so it won't respond in future. + */ + outb(IDport, 0xD1); + if(port == 0x3F0) + continue; + + /* + * 6. Activate the adapter by writing the Activate command + * (0xFF). + */ + outb(IDport, 0xFF); + delay(20); + + /* + * 8. Can now talk to the adapter's I/O base addresses. + * Use the I/O base address from the acr just read. + * + * Enable the adapter and clear out any lingering status + * and interrupts. + */ + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, SelectRegisterWindow, Wsetup); + outs(port+ConfigControl, Ena); + + txrxreset(port); + COMMAND(port, AcknowledgeInterrupt, 0xFF); + + irq = (ins(port+ResourceConfig)>>12) & 0x0F; + tcmadapter(port, irq, BUSUNKNOWN, 0); + } +} + +static void +tcm5XXeisa(void) +{ + ushort x; + int irq, port, slot; + + /* + * Check if this is an EISA machine. + * If not, nothing to do. + */ + if(strncmp((char*)KADDR(0xFFFD9), "EISA", 4)) + return; + + /* + * Continue through the EISA slots looking for a match on both + * 3COM as the manufacturer and 3C579-* or 3C59[27]-* as the product. + * If an adapter is found, select window 0, enable it and clear + * out any lingering status and interrupts. + */ + for(slot = 1; slot < MaxEISA; slot++){ + port = slot*0x1000; + if(ins(port+0xC80+ManufacturerID) != 0x6D50) + continue; + x = ins(port+0xC80+ProductID); + if((x & 0xF0FF) != 0x9050 && (x & 0xFF00) != 0x5900) + continue; + + COMMAND(port, SelectRegisterWindow, Wsetup); + outs(port+ConfigControl, Ena); + + txrxreset(port); + COMMAND(port, AcknowledgeInterrupt, 0xFF); + + irq = (ins(port+ResourceConfig)>>12) & 0x0F; + tcmadapter(port, irq, BUSUNKNOWN, 0); + } +} + +static void +tcm59Xpci(Ether *ether) +{ + Pcidev *p; + int irq, port; + ulong bar; + + p = nil; + while(p = pcimatch(p, 0x10B7, 0)){ + if (p->did == 0x5157) { + EepromReadRegister = _EepromRead8bRegister; + + /* Map the CardBus functions */ + bar = pcicfgr32(p, PciBAR2); + print("ether#%d: CardBus functions at %.8luX\n", ether->ctlrno, bar & ~KZERO); + } + else + bar = 0; + + /* + * Not prepared to deal with memory-mapped + * devices yet. + */ + if(!(p->mem[0].bar & 0x01)) + continue; + port = p->mem[0].bar & ~0x01; + irq = p->intl; + COMMAND(port, GlobalReset, 0); + while(STATUS(port) & commandInProgress) + ; + + tcmadapter(port, irq, p->tbdf, bar); + pcisetbme(p); + } +} + +static char* tcmpcmcia[] = { + "3C589", /* 3COM 589[ABCD] */ + "3C562", /* 3COM 562 */ + "589E", /* 3COM Megahertz 589E */ + nil, +}; + +static int +tcm5XXpcmcia(Ether* ether) +{ + int i; + + for(i = 0; tcmpcmcia[i] != nil; i++){ + if(!cistrcmp(ether->type, tcmpcmcia[i])){ + /* + * No need for an ioalloc here, the 589 reset + * code deals with it. + if(ioalloc(ether->port, 0x10, 0, "tcm5XXpcmcia") < 0) + return 0; + */ + return ether->port; + } + } + + return 0; +} + +static void +setxcvr(int port, int xcvr, int is9) +{ + int x; + + if(is9){ + COMMAND(port, SelectRegisterWindow, Wsetup); + x = ins(port+AddressConfig) & ~xcvrMask9; + x |= (xcvr>>20)<<14; + outs(port+AddressConfig, x); + } + else{ + COMMAND(port, SelectRegisterWindow, Wfifo); + x = inl(port+InternalConfig) & ~xcvrMask; + x |= xcvr; + outl(port+InternalConfig, x); + } + + txrxreset(port); +} + +static void +setfullduplex(int port) +{ + int x; + + COMMAND(port, SelectRegisterWindow, Wfifo); + x = ins(port+MacControl); + outs(port+MacControl, fullDuplexEnable|x); + + txrxreset(port); +} + +static int +miimdi(int port, int n) +{ + int data, i; + + /* + * Read n bits from the MII Management Register. + */ + data = 0; + for(i = n-1; i >= 0; i--){ + if(ins(port) & mgmtData) + data |= (1<= 0; i--){ + if(bits & (1<>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + port += PhysicalMgmt; + + /* + * Preamble; + * ST+OP+PHYAD+REGAD; + * TA + 16 data bits. + */ + miimdo(port, 0xFFFFFFFF, 32); + miimdo(port, 0x1800|(phyad<<5)|regad, 14); + data = miimdi(port, 18); + + port -= PhysicalMgmt; + COMMAND(port, SelectRegisterWindow, w); + + if(data & 0x10000) + return -1; + + return data & 0xFFFF; +} + +static void +scanphy(int port) +{ + int i, x; + + for(i = 0; i < 32; i++){ + if((x = miir(port, i, 2)) == -1 || x == 0) + continue; + x <<= 6; + x |= miir(port, i, 3)>>10; + XCVRDEBUG("phy%d: oui %uX reg1 %uX\n", i, x, miir(port, i, 1)); + USED(x); + } +} + +#ifdef notdef +static struct xxx { + int available; + int next; +} xxx[8] = { + { base10TAvailable, 1, }, /* xcvr10BaseT -> xcvrAui */ + { auiAvailable, 3, }, /* xcvrAui -> xcvr10Base2 */ + { 0, -1, }, + { coaxAvailable, -1, }, /* xcvr10Base2 -> nowhere */ + { baseTXAvailable, 5, }, /* xcvr100BaseTX-> xcvr100BaseFX */ + { baseFXAvailable, -1, }, /* xcvr100BaseFX-> nowhere */ + { miiConnector, -1, }, /* xcvrMii -> nowhere */ + { 0, -1, }, +}; +#endif /* notdef */ + +static struct { + char *name; + int avail; + int xcvr; +} media[] = { + "10BaseT", base10TAvailable, xcvr10BaseT, + "10Base2", coaxAvailable, xcvr10Base2, + "100BaseTX", baseTXAvailable, xcvr100BaseTX, + "100BaseFX", baseFXAvailable, xcvr100BaseFX, + "aui", auiAvailable, xcvrAui, + "mii", miiConnector, xcvrMii +}; + +static int +autoselect(int port, int xcvr, int is9) +{ + int media, x; + USED(xcvr); + + /* + * Pathetic attempt at automatic media selection. + * Really just to get the Fast Etherlink 10BASE-T/100BASE-TX + * cards operational. + * It's a bonus if it works for anything else. + */ + if(is9){ + COMMAND(port, SelectRegisterWindow, Wsetup); + x = ins(port+ConfigControl); + media = 0; + if(x & base10TAvailable9) + media |= base10TAvailable; + if(x & coaxAvailable9) + media |= coaxAvailable; + if(x & auiAvailable9) + media |= auiAvailable; + } + else{ + COMMAND(port, SelectRegisterWindow, Wfifo); + media = ins(port+ResetOptions); + } + XCVRDEBUG("autoselect: media %uX\n", media); + + if(media & miiConnector) + return xcvrMii; + + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + XCVRDEBUG("autoselect: media status %uX\n", ins(port+MediaStatus)); + + if(media & baseTXAvailable){ + /* + * Must have InternalConfig register. + */ + setxcvr(port, xcvr100BaseTX, is9); + + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable); + outs(port+MediaStatus, linkBeatEnable|x); + delay(10); + + if(ins(port+MediaStatus) & linkBeatDetect) + return xcvr100BaseTX; + outs(port+MediaStatus, x); + } + + if(media & base10TAvailable){ + setxcvr(port, xcvr10BaseT, is9); + + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~dcConverterEnabled; + outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x); + delay(100); + + XCVRDEBUG("autoselect: 10BaseT media status %uX\n", ins(port+MediaStatus)); + if(ins(port+MediaStatus) & linkBeatDetect) + return xcvr10BaseT; + outs(port+MediaStatus, x); + } + + /* + * Botch. + */ + return autoSelect; +} + +static int +eepromdata(int port, int offset) +{ + COMMAND(port, SelectRegisterWindow, Wsetup); + while(EEPROMBUSY(port)) + ; + EEPROMCMD(port, EepromReadRegister, offset); + while(EEPROMBUSY(port)) + ; + return EEPROMDATA(port); +} + +int +elnk3reset(Ether* ether) +{ + int anar, anlpar, phyaddr, phystat, timeo, xcvr; + int busmaster, did, i, j, port, rxearly, rxstatus9, x; + Block *bp, **bpp; + Adapter *ap; + uchar ea[Eaddrlen]; + Ctlr *ctlr; + static int scandone; + char *p; + + /* + * Scan for adapter on PCI, EISA and finally + * using the little ISA configuration dance. + */ + if(scandone == 0){ + tcm59Xpci(ether); + tcm5XXeisa(); + tcm509isa(); + scandone = 1; + } + + /* + * Any adapter matches if no ether->port is supplied, + * otherwise the ports must match. + */ + port = 0; + bpp = &adapter; + for(bp = *bpp; bp; bp = bp->next){ + ap = (Adapter*)bp->rp; + if(ether->port == 0 || ether->port == ap->port){ + port = ap->port; + ether->irq = ap->irq; + ether->tbdf = ap->tbdf; + ether->mem = ap->cbfns; /* Misuse the mem ref for the cardbus functions */ + *bpp = bp->next; + freeb(bp); + break; + } + bpp = &bp->next; + } + if(port == 0 && (port = tcm5XXpcmcia(ether)) == 0) + return -1; + + /* + * Read the DeviceID from the EEPROM, it's at offset 0x03, + * and do something depending on capabilities. + */ + switch(did = eepromdata(port, 0x03)){ + + case 0x5157: /* 3C575 Cyclone */ + case 0x4500: /* 3C450 HomePNA Tornado */ + case 0x6056: + case 0x7646: /* 3CSOHO100-TX */ + case 0x9055: /* 3C905B-TX */ + case 0x9200: /* 3C905C-TX */ + /*FALLTHROUGH*/ + case 0x9000: /* 3C900-TPO */ + case 0x9001: /* 3C900-COMBO */ + case 0x9005: /* 3C900B-COMBO */ + case 0x9050: /* 3C905-TX */ + case 0x9051: /* 3C905-T4 */ + if(BUSTYPE(ether->tbdf) != BusPCI) + goto buggery; + busmaster = 2; + goto vortex; + + case 0x5900: /* 3C590-[TP|COMBO|TPO] */ + case 0x5920: /* 3C592-[TP|COMBO|TPO] */ + case 0x5950: /* 3C595-TX */ + case 0x5951: /* 3C595-T4 */ + case 0x5952: /* 3C595-MII */ + case 0x5970: /* 3C597-TX */ + case 0x5971: /* 3C597-T4 */ + case 0x5972: /* 3C597-MII */ + busmaster = 1; + vortex: + COMMAND(port, SelectRegisterWindow, Wfifo); + xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask); + rxearly = 8188; + rxstatus9 = 0; + break; + + buggery: + default: + busmaster = 0; + COMMAND(port, SelectRegisterWindow, Wsetup); + x = ins(port+AddressConfig); + xcvr = ((x & xcvrMask9)>>14)<<20; + if(x & autoSelect9) + xcvr |= autoSelect; + rxearly = 2044; + rxstatus9 = 1; + break; + } + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to + * loading the station address in Wstation. + * The EEPROM returns 16-bits at a time. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + for(i = 0; i < Eaddrlen/2; i++){ + x = eepromdata(port, i); + ether->ea[2*i] = x>>8; + ether->ea[2*i+1] = x; + } + } + + COMMAND(port, SelectRegisterWindow, Wstation); + for(i = 0; i < Eaddrlen; i++) + outb(port+i, ether->ea[i]); + + /* + * Enable the transceiver if necessary and determine whether + * busmastering can be used. Due to bugs in the first revision + * of the 3C59[05], don't use busmastering at 10Mbps. + */ + XCVRDEBUG("reset: xcvr %uX\n", xcvr); + + /* + * Allow user to specify desired media in plan9.ini + */ + for(i = 0; i < ether->nopt; i++){ + if(cistrncmp(ether->opt[i], "media=", 6) != 0) + continue; + p = ether->opt[i]+6; + for(j = 0; j < nelem(media); j++) + if(cistrcmp(p, media[j].name) == 0) + xcvr = media[j].xcvr; + } + + /* + * forgive me, but i am weak + */ + switch(did){ + default: + if(xcvr & autoSelect) + xcvr = autoselect(port, xcvr, rxstatus9); + break; + case 0x4500: + case 0x5157: + case 0x6056: + case 0x7646: + case 0x9055: + case 0x9200: + xcvr = xcvrMii; + txrxreset(port); + XCVRDEBUG("905[BC] reset ops 0x%uX\n", ins(port+ResetOp905B)); + + if (did == 0x5157) { + ushort reset_opts; + + COMMAND(port, SelectRegisterWindow, Wstation); + reset_opts = ins(port + ResetOp905B); + reset_opts |= 0x0010; /* Invert LED */ + outs(port + ResetOp905B, reset_opts); + } + break; + } + XCVRDEBUG("autoselect returns: xcvr %uX, did 0x%uX\n", xcvr, did); + + switch(xcvr){ + + case xcvrMii: + /* + * Quick hack. + scanphy(port); + */ + phyaddr = (did == 0x5157)? 0: 24; + for(i = 0; i < 7; i++) + XCVRDEBUG(" %2.2uX", miir(port, phyaddr, i)); + XCVRDEBUG("\n"); + + for(timeo = 0; timeo < 30; timeo++){ + phystat = miir(port, phyaddr, 0x01); + if(phystat & 0x20) + break; + XCVRDEBUG(" %2.2uX", phystat); + delay(100); + } + XCVRDEBUG(" %2.2uX", miir(port, phyaddr, 0x01)); + XCVRDEBUG("\n"); + + anar = miir(port, phyaddr, 0x04); + anlpar = miir(port, phyaddr, 0x05) & 0x03E0; + anar &= anlpar; + miir(port, phyaddr, 0x00); + XCVRDEBUG("mii an: %uX anlp: %uX r0:%uX r1:%uX\n", + anar, anlpar, miir(port, phyaddr, 0x00), + miir(port, phyaddr, 0x01)); + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "fullduplex") == 0) + anar |= 0x0100; + else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0) + anar |= 0x0100; + else if(cistrcmp(ether->opt[i], "force100") == 0) + anar |= 0x0080; + } + XCVRDEBUG("mii anar: %uX\n", anar); + if(anar & 0x0100){ /* 100BASE-TXFD */ + setfullduplex(port); + } + else if(anar & 0x0200){ /* 100BASE-T4 */ + /* nothing to do */ + } + else if(anar & 0x0080){ /* 100BASE-TX */ + /* nothing to do */; + } + else if(anar & 0x0040) /* 10BASE-TFD */ + setfullduplex(port); + else{ /* 10BASE-T */ + /* nothing to do */; + } + break; + + case xcvr100BaseTX: + case xcvr100BaseFX: + COMMAND(port, SelectRegisterWindow, Wfifo); + x = inl(port+InternalConfig) & ~ramPartitionMask; + outl(port+InternalConfig, x|ramPartition1to1); + + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable); + x |= linkBeatEnable; + outs(port+MediaStatus, x); + break; + + case xcvr10BaseT: + /* + * Enable Link Beat and Jabber to start the + * transceiver. + */ + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~dcConverterEnabled; + x |= linkBeatEnable|jabberGuardEnable; + outs(port+MediaStatus, x); + + if((did & 0xFF00) == 0x5900) + busmaster = 0; + break; + + case xcvr10Base2: + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable); + outs(port+MediaStatus, x); + + /* + * Start the DC-DC converter. + * Wait > 800 microseconds. + */ + COMMAND(port, EnableDcConverter, 0); + delay(1); + break; + } + + /* + * Wop is the normal operating register set. + * The 3C59[0257] adapters allow access to more than one register window + * at a time, but there are situations where switching still needs to be + * done, so just do it. + * Clear out any lingering Tx status. + */ + COMMAND(port, SelectRegisterWindow, Wop); + if(busmaster == 2) + x = port+TxStatus905; + else + x = port+TxStatus; + while(inb(x)) + outb(x, 0); + + /* + * Allocate a controller structure, clear out the + * adapter statistics, clear the statistics logged into ctlr + * and enable statistics collection. Xcvr is needed in order + * to collect the BadSSD statistics. + */ + ether->ctlr = malloc(sizeof(Ctlr)); + ctlr = ether->ctlr; + + ilock(&ctlr->wlock); + ctlr->xcvr = xcvr; + statistics(ether); + memset(ctlr->stats, 0, sizeof(ctlr->stats)); + + ctlr->busmaster = busmaster; + ctlr->xcvr = xcvr; + ctlr->rxstatus9 = rxstatus9; + ctlr->rxearly = rxearly; + if(rxearly >= 2048) + ctlr->ts = 2; + + COMMAND(port, StatisticsEnable, 0); + + /* + * Allocate any receive buffers. + */ + if (ctlr->busmaster == 2) { + ctlr->dnenabled = 1; + + /* + * 10MUpldBug. + * Disabling is too severe, can use receive busmastering at + * 100Mbps OK, but how to tell which rate is actually being used - + * the 3c905 always seems to have dataRate100 set? + * Believe the bug doesn't apply if upRxEarlyEnable is set + * and the threshold is set such that uploads won't start + * until the whole packet has been received. + */ + ctlr->upenabled = 1; + x = eepromdata(port, 0x0F); + if(!(x & 0x01)) + outl(port+PktStatus, upRxEarlyEnable); + + ctlr->nup = Nup; + ctlr->ndn = Ndn; + init905(ctlr); + outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256)); + } + + /* + * Set a base TxStartThresh which will be incremented + * if any txUnderrun errors occur and ensure no RxEarly + * interrupts happen. + */ + ctlr->txthreshold = ETHERMAXTU/2; + COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts); + COMMAND(port, SetRxEarlyThresh, rxearly>>ctlr->ts); + + iunlock(&ctlr->wlock); + + /* + * Linkage to the generic ethernet driver. + */ + ether->port = port; + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + + return 0; +} diff --git a/os/boot/pc/etherif.h b/os/boot/pc/etherif.h new file mode 100644 index 00000000..522fd9b0 --- /dev/null +++ b/os/boot/pc/etherif.h @@ -0,0 +1,46 @@ +typedef struct RingBuf { + uchar owner; + uchar unused; + ushort len; + uchar pkt[sizeof(Etherpkt)]; +} RingBuf; + +enum { + Host = 0, /* buffer owned by host */ + Interface = 1, /* buffer owned by card */ + + Nrb = 32, /* default number of receive buffers */ + Ntb = 8, /* default number of transmit buffers */ +}; + +typedef struct Ether Ether; +struct Ether { + ISAConf; /* hardware info */ + int ctlrno; + int state; + int tbdf; + + void (*attach)(Ether*); /* filled in by reset routine */ + void (*transmit)(Ether*); + void (*interrupt)(Ureg*, void*); + void (*detach)(Ether*); + void *ctlr; + + ushort nrb; /* number of software receive buffers */ + ushort ntb; /* number of software transmit buffers */ + RingBuf *rb; /* software receive buffers */ + RingBuf *tb; /* software transmit buffers */ + + ushort rh; /* first receive buffer belonging to host */ + ushort ri; /* first receive buffer belonging to card */ + + ushort th; /* first transmit buffer belonging to host */ + ushort ti; /* first transmit buffer belonging to card */ + int tbusy; /* transmitter is busy */ +}; + +extern void etherrloop(Ether*, Etherpkt*, long); +extern void addethercard(char*, int(*)(Ether*)); + +#define NEXT(x, l) (((x)+1)%(l)) +#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1) diff --git a/os/boot/pc/etherigbe.c b/os/boot/pc/etherigbe.c new file mode 100644 index 00000000..f65ea7ba --- /dev/null +++ b/os/boot/pc/etherigbe.c @@ -0,0 +1,1706 @@ +/* + * bootstrap driver for + * Intel RS-82543GC Gigabit Ethernet Controller + * as found on the Intel PRO/1000[FT] Server Adapter. + * The older non-[FT] cards use the 82542 (LSI L2A1157) chip; no attempt + * is made to handle the older chip although it should be possible. + * + * updated just enough to cope with the + * Intel 8254[0347]NN Gigabit Ethernet Controller + * as found on the Intel PRO/1000 series of adapters: + * 82540EM Intel PRO/1000 MT + * 82543GC Intel PRO/1000 T + * 82544EI Intel PRO/1000 XT + * 82547EI built-in + * + * The datasheet is not very clear about running on a big-endian system + * and this driver assumes little-endian throughout. + * To do: + * GMII/MII + * check recovery from receive no buffers condition + * automatic ett adjustment + */ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "etherif.h" +#include "ethermii.h" + +enum { + i82542 = (0x1000<<16)|0x8086, + i82543gc = (0x1004<<16)|0x8086, + i82544ei = (0x1008<<16)|0x8086, + i82547ei = (0x1019<<16)|0x8086, + i82540em = (0x100E<<16)|0x8086, + i82540eplp = (0x101E<<16)|0x8086, + i82547gi = (0x1075<<16)|0x8086, + i82541gi = (0x1076<<16)|0x8086, + i82546gb = (0x1079<<16)|0x8086, + i82541pi = (0x107c<<16)|0x8086, + i82546eb = (0x1010<<16)|0x8086, +}; + +/* compatibility with cpu kernels */ +#define iallocb allocb +#ifndef CACHELINESZ +#define CACHELINESZ 32 /* pentium & later */ +#endif + +/* from pci.c */ +enum +{ /* command register (pcidev->pcr) */ + IOen = (1<<0), + MEMen = (1<<1), + MASen = (1<<2), + MemWrInv = (1<<4), + PErrEn = (1<<6), + SErrEn = (1<<8), +}; +enum { + Ctrl = 0x00000000, /* Device Control */ + Status = 0x00000008, /* Device Status */ + Eecd = 0x00000010, /* EEPROM/Flash Control/Data */ + Ctrlext = 0x00000018, /* Extended Device Control */ + Mdic = 0x00000020, /* MDI Control */ + Fcal = 0x00000028, /* Flow Control Address Low */ + Fcah = 0x0000002C, /* Flow Control Address High */ + Fct = 0x00000030, /* Flow Control Type */ + Icr = 0x000000C0, /* Interrupt Cause Read */ + Ics = 0x000000C8, /* Interrupt Cause Set */ + Ims = 0x000000D0, /* Interrupt Mask Set/Read */ + Imc = 0x000000D8, /* Interrupt mask Clear */ + Rctl = 0x00000100, /* Receive Control */ + Fcttv = 0x00000170, /* Flow Control Transmit Timer Value */ + Txcw = 0x00000178, /* Transmit Configuration Word */ + Tctl = 0x00000400, /* Transmit Control */ + Tipg = 0x00000410, /* Transmit IPG */ + Tbt = 0x00000448, /* Transmit Burst Timer */ + Ait = 0x00000458, /* Adaptive IFS Throttle */ + Fcrtl = 0x00002160, /* Flow Control RX Threshold Low */ + Fcrth = 0x00002168, /* Flow Control Rx Threshold High */ + Rdbal = 0x00002800, /* Rdesc Base Address Low */ + Rdbah = 0x00002804, /* Rdesc Base Address High */ + Rdlen = 0x00002808, /* Receive Descriptor Length */ + Rdh = 0x00002810, /* Receive Descriptor Head */ + Rdt = 0x00002818, /* Receive Descriptor Tail */ + Rdtr = 0x00002820, /* Receive Descriptor Timer Ring */ + Rxdctl = 0x00002828, /* Receive Descriptor Control */ + Radv = 0x0000282C, /* Receive Interrupt Absolute Delay Timer */ + Txdmac = 0x00003000, /* Transfer DMA Control */ + Ett = 0x00003008, /* Early Transmit Control */ + Tdbal = 0x00003800, /* Tdesc Base Address Low */ + Tdbah = 0x00003804, /* Tdesc Base Address High */ + Tdlen = 0x00003808, /* Transmit Descriptor Length */ + Tdh = 0x00003810, /* Transmit Descriptor Head */ + Tdt = 0x00003818, /* Transmit Descriptor Tail */ + Tidv = 0x00003820, /* Transmit Interrupt Delay Value */ + Txdctl = 0x00003828, /* Transmit Descriptor Control */ + Tadv = 0x0000382C, /* Transmit Interrupt Absolute Delay Timer */ + + Statistics = 0x00004000, /* Start of Statistics Area */ + Gorcl = 0x88/4, /* Good Octets Received Count */ + Gotcl = 0x90/4, /* Good Octets Transmitted Count */ + Torl = 0xC0/4, /* Total Octets Received */ + Totl = 0xC8/4, /* Total Octets Transmitted */ + Nstatistics = 64, + + Rxcsum = 0x00005000, /* Receive Checksum Control */ + Mta = 0x00005200, /* Multicast Table Array */ + Ral = 0x00005400, /* Receive Address Low */ + Rah = 0x00005404, /* Receive Address High */ + Manc = 0x00005820, /* Management Control */ +}; + +enum { /* Ctrl */ + Bem = 0x00000002, /* Big Endian Mode */ + Prior = 0x00000004, /* Priority on the PCI bus */ + Lrst = 0x00000008, /* Link Reset */ + Asde = 0x00000020, /* Auto-Speed Detection Enable */ + Slu = 0x00000040, /* Set Link Up */ + Ilos = 0x00000080, /* Invert Loss of Signal (LOS) */ + SspeedMASK = 0x00000300, /* Speed Selection */ + SspeedSHIFT = 8, + Sspeed10 = 0x00000000, /* 10Mb/s */ + Sspeed100 = 0x00000100, /* 100Mb/s */ + Sspeed1000 = 0x00000200, /* 1000Mb/s */ + Frcspd = 0x00000800, /* Force Speed */ + Frcdplx = 0x00001000, /* Force Duplex */ + SwdpinsloMASK = 0x003C0000, /* Software Defined Pins - lo nibble */ + SwdpinsloSHIFT = 18, + SwdpioloMASK = 0x03C00000, /* Software Defined Pins - I or O */ + SwdpioloSHIFT = 22, + Devrst = 0x04000000, /* Device Reset */ + Rfce = 0x08000000, /* Receive Flow Control Enable */ + Tfce = 0x10000000, /* Transmit Flow Control Enable */ + Vme = 0x40000000, /* VLAN Mode Enable */ +}; + +enum { /* Status */ + Lu = 0x00000002, /* Link Up */ + Tckok = 0x00000004, /* Transmit clock is running */ + Rbcok = 0x00000008, /* Receive clock is running */ + Txoff = 0x00000010, /* Transmission Paused */ + Tbimode = 0x00000020, /* TBI Mode Indication */ + SpeedMASK = 0x000000C0, + Speed10 = 0x00000000, /* 10Mb/s */ + Speed100 = 0x00000040, /* 100Mb/s */ + Speed1000 = 0x00000080, /* 1000Mb/s */ + Mtxckok = 0x00000400, /* MTX clock is running */ + Pci66 = 0x00000800, /* PCI Bus speed indication */ + Bus64 = 0x00001000, /* PCI Bus width indication */ +}; + +enum { /* Ctrl and Status */ + Fd = 0x00000001, /* Full-Duplex */ + AsdvMASK = 0x00000300, + Asdv10 = 0x00000000, /* 10Mb/s */ + Asdv100 = 0x00000100, /* 100Mb/s */ + Asdv1000 = 0x00000200, /* 1000Mb/s */ +}; + +enum { /* Eecd */ + Sk = 0x00000001, /* Clock input to the EEPROM */ + Cs = 0x00000002, /* Chip Select */ + Di = 0x00000004, /* Data Input to the EEPROM */ + Do = 0x00000008, /* Data Output from the EEPROM */ + Areq = 0x00000040, /* EEPROM Access Request */ + Agnt = 0x00000080, /* EEPROM Access Grant */ + Eesz256 = 0x00000200, /* EEPROM is 256 words not 64 */ + Spi = 0x00002000, /* EEPROM is SPI not Microwire */ +}; + +enum { /* Ctrlext */ + Gpien = 0x0000000F, /* General Purpose Interrupt Enables */ + SwdpinshiMASK = 0x000000F0, /* Software Defined Pins - hi nibble */ + SwdpinshiSHIFT = 4, + SwdpiohiMASK = 0x00000F00, /* Software Defined Pins - I or O */ + SwdpiohiSHIFT = 8, + Asdchk = 0x00001000, /* ASD Check */ + Eerst = 0x00002000, /* EEPROM Reset */ + Ips = 0x00004000, /* Invert Power State */ + Spdbyps = 0x00008000, /* Speed Select Bypass */ +}; + +enum { /* EEPROM content offsets */ + Ea = 0x00, /* Ethernet Address */ + Cf = 0x03, /* Compatibility Field */ + Pba = 0x08, /* Printed Board Assembly number */ + Icw1 = 0x0A, /* Initialization Control Word 1 */ + Sid = 0x0B, /* Subsystem ID */ + Svid = 0x0C, /* Subsystem Vendor ID */ + Did = 0x0D, /* Device ID */ + Vid = 0x0E, /* Vendor ID */ + Icw2 = 0x0F, /* Initialization Control Word 2 */ +}; + +enum { /* Mdic */ + MDIdMASK = 0x0000FFFF, /* Data */ + MDIdSHIFT = 0, + MDIrMASK = 0x001F0000, /* PHY Register Address */ + MDIrSHIFT = 16, + MDIpMASK = 0x03E00000, /* PHY Address */ + MDIpSHIFT = 21, + MDIwop = 0x04000000, /* Write Operation */ + MDIrop = 0x08000000, /* Read Operation */ + MDIready = 0x10000000, /* End of Transaction */ + MDIie = 0x20000000, /* Interrupt Enable */ + MDIe = 0x40000000, /* Error */ +}; + +enum { /* Icr, Ics, Ims, Imc */ + Txdw = 0x00000001, /* Transmit Descriptor Written Back */ + Txqe = 0x00000002, /* Transmit Queue Empty */ + Lsc = 0x00000004, /* Link Status Change */ + Rxseq = 0x00000008, /* Receive Sequence Error */ + Rxdmt0 = 0x00000010, /* Rdesc Minimum Threshold Reached */ + Rxo = 0x00000040, /* Receiver Overrun */ + Rxt0 = 0x00000080, /* Receiver Timer Interrupt */ + Mdac = 0x00000200, /* MDIO Access Completed */ + Rxcfg = 0x00000400, /* Receiving /C/ ordered sets */ + Gpi0 = 0x00000800, /* General Purpose Interrupts */ + Gpi1 = 0x00001000, + Gpi2 = 0x00002000, + Gpi3 = 0x00004000, +}; + +/* + * The Mdic register isn't implemented on the 82543GC, + * the software defined pins are used instead. + * These definitions work for the Intel PRO/1000 T Server Adapter. + * The direction pin bits are read from the EEPROM. + */ +enum { + Mdd = ((1<<2)<nic+((r)/4))) +#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v)) + +static void +igbeim(Ctlr* ctlr, int im) +{ + ilock(&ctlr->imlock); + ctlr->im |= im; + csr32w(ctlr, Ims, ctlr->im); + iunlock(&ctlr->imlock); +} + +static void +igbeattach(Ether* edev) +{ + int ctl; + Ctlr *ctlr; + + /* + * To do here: + * one-time stuff; + * start off a kproc for link status change: + * adjust queue length depending on speed; + * flow control. + * more needed here... + */ + ctlr = edev->ctlr; + igbeim(ctlr, 0); + ctl = csr32r(ctlr, Rctl)|Ren; + csr32w(ctlr, Rctl, ctl); + ctl = csr32r(ctlr, Tctl)|Ten; + csr32w(ctlr, Tctl, ctl); +} + +static char* statistics[Nstatistics] = { + "CRC Error", + "Alignment Error", + "Symbol Error", + "RX Error", + "Missed Packets", + "Single Collision", + "Excessive Collisions", + "Multiple Collision", + "Late Collisions", + nil, + "Collision", + "Transmit Underrun", + "Defer", + "Transmit - No CRS", + "Sequence Error", + "Carrier Extension Error", + "Receive Error Length", + nil, + "XON Received", + "XON Transmitted", + "XOFF Received", + "XOFF Transmitted", + "FC Received Unsupported", + "Packets Received (64 Bytes)", + "Packets Received (65-127 Bytes)", + "Packets Received (128-255 Bytes)", + "Packets Received (256-511 Bytes)", + "Packets Received (512-1023 Bytes)", + "Packets Received (1024-1522 Bytes)", + "Good Packets Received", + "Broadcast Packets Received", + "Multicast Packets Received", + "Good Packets Transmitted", + nil, + "Good Octets Received", + nil, + "Good Octets Transmitted", + nil, + nil, + nil, + "Receive No Buffers", + "Receive Undersize", + "Receive Fragment", + "Receive Oversize", + "Receive Jabber", + nil, + nil, + nil, + "Total Octets Received", + nil, + "Total Octets Transmitted", + nil, + "Total Packets Received", + "Total Packets Transmitted", + "Packets Transmitted (64 Bytes)", + "Packets Transmitted (65-127 Bytes)", + "Packets Transmitted (128-255 Bytes)", + "Packets Transmitted (256-511 Bytes)", + "Packets Transmitted (512-1023 Bytes)", + "Packets Transmitted (1024-1522 Bytes)", + "Multicast Packets Transmitted", + "Broadcast Packets Transmitted", + "TCP Segmentation Context Transmitted", + "TCP Segmentation Context Fail", +}; + +static void +txstart(Ether *edev) +{ + int tdh, tdt, len, olen; + Ctlr *ctlr = edev->ctlr; + Block *bp; + Tdesc *tdesc; + + /* + * Try to fill the ring back up, moving buffers from the transmit q. + */ + tdh = PREV(ctlr->tdh, Ntdesc); + for(tdt = ctlr->tdt; tdt != tdh; tdt = NEXT(tdt, Ntdesc)){ + /* pull off the head of the transmission queue */ + if((bp = ctlr->bqhead) == nil) /* was qget(edev->oq) */ + break; + ctlr->bqhead = bp->next; + if (ctlr->bqtail == bp) + ctlr->bqtail = nil; + len = olen = BLEN(bp); + + /* + * if packet is too short, make it longer rather than relying + * on ethernet interface to pad it and complain so the caller + * will get fixed. I don't think Psp is working right, or it's + * getting cleared. + */ + if (len < ETHERMINTU) { + if (bp->rp + ETHERMINTU <= bp->lim) + bp->wp = bp->rp + ETHERMINTU; + else + bp->wp = bp->lim; + len = BLEN(bp); + print("txstart: extended short pkt %d -> %d bytes\n", + olen, len); + } + + /* set up a descriptor for it */ + tdesc = &ctlr->tdba[tdt]; + tdesc->addr[0] = PCIWADDR(bp->rp); + tdesc->addr[1] = 0; + tdesc->control = /* Ide| */ Rs|Dext|Ifcs|Teop|DtypeDD|len; + tdesc->status = 0; + + ctlr->tb[tdt] = bp; + } + ctlr->tdt = tdt; + csr32w(ctlr, Tdt, tdt); + igbeim(ctlr, Txdw); +} + +static Block * +fromringbuf(Ether *ether) +{ + RingBuf *tb = ðer->tb[ether->ti]; + Block *bp = allocb(tb->len); + + memmove(bp->wp, tb->pkt, tb->len); + memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen); + bp->wp += tb->len; + return bp; +} + +static void +igbetransmit(Ether* edev) +{ + Block *bp; + Ctlr *ctlr; + Tdesc *tdesc; + RingBuf *tb; + int tdh; + + /* + * For now there are no smarts here. Tuning comes later. + */ + ctlr = edev->ctlr; + ilock(&ctlr->tdlock); + + /* + * Free any completed packets + * - try to get the soft tdh to catch the tdt; + * - if the packet had an underrun bump the threshold + * - the Tu bit doesn't seem to ever be set, perhaps + * because Rs mode is used? + */ + tdh = ctlr->tdh; + for(;;){ + tdesc = &ctlr->tdba[tdh]; + if(!(tdesc->status & Tdd)) + break; + if(tdesc->status & Tu){ + ctlr->ett++; + csr32w(ctlr, Ett, ctlr->ett); + } + tdesc->status = 0; + if(ctlr->tb[tdh] != nil){ + freeb(ctlr->tb[tdh]); + ctlr->tb[tdh] = nil; + } + tdh = NEXT(tdh, Ntdesc); + } + ctlr->tdh = tdh; + + /* copy packets from the software RingBuf to the transmission q */ + /* from boot ether83815.c */ + while((tb = &edev->tb[edev->ti])->owner == Interface){ + bp = fromringbuf(edev); + + /* put the buffer on the transmit queue */ + if(ctlr->bqhead) + ctlr->bqtail->next = bp; + else + ctlr->bqhead = bp; + ctlr->bqtail = bp; + + txstart(edev); /* kick transmitter */ + tb->owner = Host; /* give descriptor back */ + + edev->ti = NEXT(edev->ti, edev->ntb); + } + + iunlock(&ctlr->tdlock); +} + +static void +igbereplenish(Ctlr* ctlr) +{ + int rdt; + Block *bp; + Rdesc *rdesc; + + rdt = ctlr->rdt; + while(NEXT(rdt, Nrdesc) != ctlr->rdh){ + rdesc = &ctlr->rdba[rdt]; + if(ctlr->rb[rdt] != nil){ + /* nothing to do */ + } + else if((bp = iallocb(2048)) != nil){ + ctlr->rb[rdt] = bp; + rdesc->addr[0] = PCIWADDR(bp->rp); + rdesc->addr[1] = 0; + } + else + break; + rdesc->status = 0; + + rdt = NEXT(rdt, Nrdesc); + } + ctlr->rdt = rdt; + csr32w(ctlr, Rdt, rdt); +} + +static void +toringbuf(Ether *ether, Block *bp) +{ + RingBuf *rb = ðer->rb[ether->ri]; + + if (rb->owner == Interface) { + rb->len = BLEN(bp); + memmove(rb->pkt, bp->rp, rb->len); + rb->owner = Host; + ether->ri = NEXT(ether->ri, ether->nrb); + } + /* else no one is expecting packets from the network */ +} + +static void +igbeinterrupt(Ureg*, void* arg) +{ + Block *bp; + Ctlr *ctlr; + Ether *edev; + Rdesc *rdesc; + int icr, im, rdh, txdw = 0; + + edev = arg; + ctlr = edev->ctlr; + + ilock(&ctlr->imlock); + csr32w(ctlr, Imc, ~0); + im = ctlr->im; + + for(icr = csr32r(ctlr, Icr); icr & ctlr->im; icr = csr32r(ctlr, Icr)){ + /* + * Link status changed. + */ + if(icr & (Rxseq|Lsc)){ + /* + * More here... + */ + } + + /* + * Process any received packets. + */ + rdh = ctlr->rdh; + for(;;){ + rdesc = &ctlr->rdba[rdh]; + if(!(rdesc->status & Rdd)) + break; + if ((rdesc->status & Reop) && rdesc->errors == 0) { + bp = ctlr->rb[rdh]; + ctlr->rb[rdh] = nil; + /* + * it appears that the original 82543 needed + * to have the Ethernet CRC excluded, but that + * the newer chips do not? + */ + bp->wp += rdesc->length /* -4 */; + toringbuf(edev, bp); + freeb(bp); + } else if ((rdesc->status & Reop) && rdesc->errors) + print("igbe: input packet error 0x%ux\n", + rdesc->errors); + rdesc->status = 0; + rdh = NEXT(rdh, Nrdesc); + } + ctlr->rdh = rdh; + + if(icr & Rxdmt0) + igbereplenish(ctlr); + if(icr & Txdw){ + im &= ~Txdw; + txdw++; + } + } + + ctlr->im = im; + csr32w(ctlr, Ims, im); + iunlock(&ctlr->imlock); + + if(txdw) + igbetransmit(edev); +} + +static int +igbeinit(Ether* edev) +{ + int csr, i, r, ctrl; + MiiPhy *phy; + Ctlr *ctlr; + + ctlr = edev->ctlr; + + /* + * Set up the receive addresses. + * There are 16 addresses. The first should be the MAC address. + * The others are cleared and not marked valid (MS bit of Rah). + */ + csr = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0]; + csr32w(ctlr, Ral, csr); + csr = 0x80000000|(edev->ea[5]<<8)|edev->ea[4]; + csr32w(ctlr, Rah, csr); + for(i = 1; i < 16; i++){ + csr32w(ctlr, Ral+i*8, 0); + csr32w(ctlr, Rah+i*8, 0); + } + + /* + * Clear the Multicast Table Array. + * It's a 4096 bit vector accessed as 128 32-bit registers. + */ + for(i = 0; i < 128; i++) + csr32w(ctlr, Mta+i*4, 0); + + /* + * Receive initialisation. + * Mostly defaults from the datasheet, will + * need some tuning for performance: + * Rctl descriptor mimimum threshold size + * discard pause frames + * strip CRC + * Rdtr interrupt delay + * Rxdctl all the thresholds + */ + csr32w(ctlr, Rctl, 0); + + /* + * Allocate the descriptor ring and load its + * address and length into the NIC. + */ + ctlr->rdba = xspanalloc(Nrdesc*sizeof(Rdesc), 128 /* was 16 */, 0); + csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba)); + csr32w(ctlr, Rdbah, 0); + csr32w(ctlr, Rdlen, Nrdesc*sizeof(Rdesc)); + + /* + * Initialise the ring head and tail pointers and + * populate the ring with Blocks. + * The datasheet says the tail pointer is set to beyond the last + * descriptor hardware can process, which implies the initial + * condition is Rdh == Rdt. However, experience shows Rdt must + * always be 'behind' Rdh; the replenish routine ensures this. + */ + ctlr->rdh = 0; + csr32w(ctlr, Rdh, ctlr->rdh); + ctlr->rdt = 0; + csr32w(ctlr, Rdt, ctlr->rdt); + ctlr->rb = malloc(sizeof(Block*)*Nrdesc); + igbereplenish(ctlr); + + /* + * Set up Rctl but don't enable receiver (yet). + */ + csr32w(ctlr, Rdtr, 0); + switch(ctlr->id){ + case i82540em: + case i82540eplp: + case i82541gi: + case i82546gb: + case i82546eb: + case i82547gi: + case i82541pi: + csr32w(ctlr, Radv, 64); + break; + } + csr32w(ctlr, Rxdctl, (8<id){ + default: + r = 6; + break; + case i82543gc: + case i82544ei: + case i82547ei: + case i82540em: + case i82540eplp: + case i82541gi: + case i82541pi: + case i82546gb: + case i82546eb: + case i82547gi: + r = 8; + break; + } + csr32w(ctlr, Tipg, (6<<20)|(8<<10)|r); + csr32w(ctlr, Ait, 0); + csr32w(ctlr, Txdmac, 0); + csr32w(ctlr, Tidv, 128); + + /* + * Allocate the descriptor ring and load its + * address and length into the NIC. + */ + ctlr->tdba = xspanalloc(Ntdesc*sizeof(Tdesc), 128 /* was 16 */, 0); + csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba)); + csr32w(ctlr, Tdbah, 0); + csr32w(ctlr, Tdlen, Ntdesc*sizeof(Tdesc)); + + /* + * Initialise the ring head and tail pointers. + */ + ctlr->tdh = 0; + csr32w(ctlr, Tdh, ctlr->tdh); + ctlr->tdt = 0; + csr32w(ctlr, Tdt, ctlr->tdt); + ctlr->tb = malloc(sizeof(Block*)*Ntdesc); +// ctlr->im |= Txqe|Txdw; + + r = (4<id){ + default: + break; + case i82540em: + case i82540eplp: + case i82547gi: + case i82546gb: + case i82546eb: + case i82541gi: + case i82541pi: + r = csr32r(ctlr, Txdctl); + r &= ~WthreshMASK; + r |= Gran|(4<mii == nil || ctlr->mii->curphy == nil) { + print("igbe: no mii (yet)\n"); + return 0; + } + /* wait for the link to come up */ + if (miistatus(ctlr->mii) < 0) + return -1; + print("igbe: phy: "); + phy = ctlr->mii->curphy; + if (phy->fd) + print("full duplex"); + else + print("half duplex"); + print(", %d Mb/s\n", phy->speed); + + /* + * Flow control. + */ + ctrl = csr32r(ctlr, Ctrl); + if(phy->rfc) + ctrl |= Rfce; + if(phy->tfc) + ctrl |= Tfce; + csr32w(ctlr, Ctrl, ctrl); + + return 0; +} + +static int +i82543mdior(Ctlr* ctlr, int n) +{ + int ctrl, data, i, r; + + /* + * Read n bits from the Management Data I/O Interface. + */ + ctrl = csr32r(ctlr, Ctrl); + r = (ctrl & ~Mddo)|Mdco; + data = 0; + for(i = n-1; i >= 0; i--){ + if(csr32r(ctlr, Ctrl) & Mdd) + data |= (1<= 0; i--){ + if(bits & (1<ctlr; + + /* + * MII Management Interface Read. + * + * Preamble; + * ST+OP+PHYAD+REGAD; + * TA + 16 data bits. + */ + i82543mdiow(ctlr, 0xFFFFFFFF, 32); + i82543mdiow(ctlr, 0x1800|(pa<<5)|ra, 14); + data = i82543mdior(ctlr, 18); + + if(data & 0x10000) + return -1; + + return data & 0xFFFF; +} + +static int +i82543miimiw(Mii* mii, int pa, int ra, int data) +{ + Ctlr *ctlr; + + ctlr = mii->ctlr; + + /* + * MII Management Interface Write. + * + * Preamble; + * ST+OP+PHYAD+REGAD+TA + 16 data bits; + * Z. + */ + i82543mdiow(ctlr, 0xFFFFFFFF, 32); + data &= 0xFFFF; + data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16); + i82543mdiow(ctlr, data, 32); + + return 0; +} + +static int +igbemiimir(Mii* mii, int pa, int ra) +{ + Ctlr *ctlr; + int mdic, timo; + + ctlr = mii->ctlr; + + csr32w(ctlr, Mdic, MDIrop|(pa<ctlr; + + data &= MDIdMASK; + csr32w(ctlr, Mdic, MDIwop|(pa<mii = malloc(sizeof(Mii))) == nil) + return -1; + ctlr->mii->ctlr = ctlr; + + ctrl = csr32r(ctlr, Ctrl); + ctrl |= Slu; + + switch(ctlr->id){ + case i82543gc: + ctrl |= Frcdplx|Frcspd; + csr32w(ctlr, Ctrl, ctrl); + + /* + * The reset pin direction (Mdro) should already + * be set from the EEPROM load. + * If it's not set this configuration is unexpected + * so bail. + */ + r = csr32r(ctlr, Ctrlext); + if(!(r & Mdro)) + return -1; + csr32w(ctlr, Ctrlext, r); + delay(20); + r = csr32r(ctlr, Ctrlext); + r &= ~Mdr; + csr32w(ctlr, Ctrlext, r); + delay(20); + r = csr32r(ctlr, Ctrlext); + r |= Mdr; + csr32w(ctlr, Ctrlext, r); + delay(20); + + ctlr->mii->mir = i82543miimir; + ctlr->mii->miw = i82543miimiw; + break; + case i82544ei: + case i82547ei: + case i82540em: + case i82540eplp: + case i82547gi: + case i82541gi: + case i82541pi: + case i82546gb: + case i82546eb: + ctrl &= ~(Frcdplx|Frcspd); + csr32w(ctlr, Ctrl, ctrl); + ctlr->mii->mir = igbemiimir; + ctlr->mii->miw = igbemiimiw; + break; + default: + free(ctlr->mii); + ctlr->mii = nil; + return -1; + } + + if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){ + if (0) + print("phy trouble: phy = 0x%lux\n", (ulong)phy); + free(ctlr->mii); + ctlr->mii = nil; + return -1; + } + print("oui %X phyno %d\n", phy->oui, phy->phyno); + + /* + * 8254X-specific PHY registers not in 802.3: + * 0x10 PHY specific control + * 0x14 extended PHY specific control + * Set appropriate values then reset the PHY to have + * changes noted. + */ + switch(ctlr->id){ + case i82547gi: + case i82541gi: + case i82541pi: + case i82546gb: + case i82546eb: + break; + default: + r = miimir(ctlr->mii, 16); + r |= 0x0800; /* assert CRS on Tx */ + r |= 0x0060; /* auto-crossover all speeds */ + r |= 0x0002; /* polarity reversal enabled */ + miimiw(ctlr->mii, 16, r); + + r = miimir(ctlr->mii, 20); + r |= 0x0070; /* +25MHz clock */ + r &= ~0x0F00; + r |= 0x0100; /* 1x downshift */ + miimiw(ctlr->mii, 20, r); + + miireset(ctlr->mii); + break; + } + p = 0; + if(ctlr->txcw & TxcwPs) + p |= AnaP; + if(ctlr->txcw & TxcwAs) + p |= AnaAP; + miiane(ctlr->mii, ~0, p, ~0); + + return 0; +} + +static int +at93c46io(Ctlr* ctlr, char* op, int data) +{ + char *lp, *p; + int i, loop, eecd, r; + + eecd = csr32r(ctlr, Eecd); + + r = 0; + loop = -1; + lp = nil; + for(p = op; *p != '\0'; p++){ + switch(*p){ + default: + return -1; + case ' ': + continue; + case ':': /* start of loop */ + loop = strtol(p+1, &lp, 0)-1; + lp--; + if(p == lp) + loop = 7; + p = lp; + continue; + case ';': /* end of loop */ + if(lp == nil) + return -1; + loop--; + if(loop >= 0) + p = lp; + else + lp = nil; + continue; + case 'C': /* assert clock */ + eecd |= Sk; + break; + case 'c': /* deassert clock */ + eecd &= ~Sk; + break; + case 'D': /* next bit in 'data' byte */ + if(loop < 0) + return -1; + if(data & (1<= 0) + r |= (i<= 0) + return -1; + return r; +} + +static int +at93c46r(Ctlr* ctlr) +{ + ushort sum; + char rop[20]; + int addr, areq, bits, data, eecd, i; + + eecd = csr32r(ctlr, Eecd); + if(eecd & Spi){ + print("igbe: SPI EEPROM access not implemented\n"); + return 0; + } + if(eecd & Eesz256) + bits = 8; + else + bits = 6; + snprint(rop, sizeof(rop), "S :%dDCc;", bits+3); + + sum = 0; + + switch(ctlr->id){ + default: + areq = 0; + break; + case i82540em: + case i82540eplp: + case i82541gi: + case i82541pi: + case i82547gi: + case i82546gb: + case i82546eb: + areq = 1; + csr32w(ctlr, Eecd, eecd|Areq); + for(i = 0; i < 1000; i++){ + if((eecd = csr32r(ctlr, Eecd)) & Agnt) + break; + microdelay(5); + } + if(!(eecd & Agnt)){ + print("igbe: not granted EEPROM access\n"); + goto release; + } + break; + } + + for(addr = 0; addr < 0x40; addr++){ + /* + * Read a word at address 'addr' from the Atmel AT93C46 + * 3-Wire Serial EEPROM or compatible. The EEPROM access is + * controlled by 4 bits in Eecd. See the AT93C46 datasheet + * for protocol details. + */ + if(at93c46io(ctlr, rop, (0x06<eeprom[addr] = data; + sum += data; + } + +release: + if(areq) + csr32w(ctlr, Eecd, eecd & ~Areq); + return sum; +} + +static void +detach(Ctlr *ctlr) +{ + int r; + + /* + * Perform a device reset to get the chip back to the + * power-on state, followed by an EEPROM reset to read + * the defaults for some internal registers. + */ + csr32w(ctlr, Imc, ~0); + csr32w(ctlr, Rctl, 0); + csr32w(ctlr, Tctl, 0); + + delay(10); + + csr32w(ctlr, Ctrl, Devrst); + /* apparently needed on multi-GHz processors to avoid infinite loops */ + delay(1); + while(csr32r(ctlr, Ctrl) & Devrst) + ; + + csr32w(ctlr, Ctrlext, Eerst | csr32r(ctlr, Ctrlext)); + delay(1); + while(csr32r(ctlr, Ctrlext) & Eerst) + ; + + switch(ctlr->id){ + default: + break; + case i82540em: + case i82540eplp: + case i82541gi: + case i82541pi: + case i82547gi: + case i82546gb: + case i82546eb: + r = csr32r(ctlr, Manc); + r &= ~Arpen; + csr32w(ctlr, Manc, r); + break; + } + + csr32w(ctlr, Imc, ~0); + delay(1); + while(csr32r(ctlr, Icr)) + ; +} + +static void +igbedetach(Ether *edev) +{ + detach(edev->ctlr); +} + +static void +igbeshutdown(Ether* ether) +{ +print("igbeshutdown\n"); + igbedetach(ether); +} + +static int +igbereset(Ctlr* ctlr) +{ + int ctrl, i, pause, r, swdpio, txcw; + + detach(ctlr); + + /* + * Read the EEPROM, validate the checksum + * then get the device back to a power-on state. + */ + r = at93c46r(ctlr); + /* zero return means no SPI EEPROM access */ + if (r != 0 && r != 0xBABA){ + print("igbe: bad EEPROM checksum - 0x%4.4uX\n", r); + return -1; + } + + /* + * Snarf and set up the receive addresses. + * There are 16 addresses. The first should be the MAC address. + * The others are cleared and not marked valid (MS bit of Rah). + */ + if ((ctlr->id == i82546gb || ctlr->id == i82546eb) && BUSFNO(ctlr->pcidev->tbdf) == 1) + ctlr->eeprom[Ea+2] += 0x100; // second interface + for(i = Ea; i < Eaddrlen/2; i++){ + ctlr->ra[2*i] = ctlr->eeprom[i]; + ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8; + } + r = (ctlr->ra[3]<<24)|(ctlr->ra[2]<<16)|(ctlr->ra[1]<<8)|ctlr->ra[0]; + csr32w(ctlr, Ral, r); + r = 0x80000000|(ctlr->ra[5]<<8)|ctlr->ra[4]; + csr32w(ctlr, Rah, r); + for(i = 1; i < 16; i++){ + csr32w(ctlr, Ral+i*8, 0); + csr32w(ctlr, Rah+i*8, 0); + } + + /* + * Clear the Multicast Table Array. + * It's a 4096 bit vector accessed as 128 32-bit registers. + */ + memset(ctlr->mta, 0, sizeof(ctlr->mta)); + for(i = 0; i < 128; i++) + csr32w(ctlr, Mta+i*4, 0); + + /* + * Just in case the Eerst didn't load the defaults + * (doesn't appear to fully on the 8243GC), do it manually. + */ + if (ctlr->id == i82543gc) { + txcw = csr32r(ctlr, Txcw); + txcw &= ~(TxcwAne|TxcwPauseMASK|TxcwFd); + ctrl = csr32r(ctlr, Ctrl); + ctrl &= ~(SwdpioloMASK|Frcspd|Ilos|Lrst|Fd); + + if(ctlr->eeprom[Icw1] & 0x0400){ + ctrl |= Fd; + txcw |= TxcwFd; + } + if(ctlr->eeprom[Icw1] & 0x0200) + ctrl |= Lrst; + if(ctlr->eeprom[Icw1] & 0x0010) + ctrl |= Ilos; + if(ctlr->eeprom[Icw1] & 0x0800) + ctrl |= Frcspd; + swdpio = (ctlr->eeprom[Icw1] & 0x01E0)>>5; + ctrl |= swdpio<eeprom[Icw2] & 0x00F0)>>4; + if(ctlr->eeprom[Icw1] & 0x1000) + ctrl |= Ips; + ctrl |= swdpio<eeprom[Icw2] & 0x0800) + txcw |= TxcwAne; + pause = (ctlr->eeprom[Icw2] & 0x3000)>>12; + txcw |= pause<fcrtl = 0x00002000; + ctlr->fcrth = 0x00004000; + txcw |= TxcwAs|TxcwPs; + break; + case 0: + ctlr->fcrtl = 0x00002000; + ctlr->fcrth = 0x00004000; + break; + case 2: + ctlr->fcrtl = 0; + ctlr->fcrth = 0; + txcw |= TxcwAs; + break; + } + ctlr->txcw = txcw; + csr32w(ctlr, Txcw, txcw); + } + /* + * Flow control - values from the datasheet. + */ + csr32w(ctlr, Fcal, 0x00C28001); + csr32w(ctlr, Fcah, 0x00000100); + csr32w(ctlr, Fct, 0x00008808); + csr32w(ctlr, Fcttv, 0x00000100); + + csr32w(ctlr, Fcrtl, ctlr->fcrtl); + csr32w(ctlr, Fcrth, ctlr->fcrth); + + ilock(&ctlr->imlock); + csr32w(ctlr, Imc, ~0); + ctlr->im = Lsc; + csr32w(ctlr, Ims, ctlr->im); + iunlock(&ctlr->imlock); + + if(!(csr32r(ctlr, Status) & Tbimode) && igbemii(ctlr) < 0) { + print("igbe: igbemii failed\n"); + return -1; + } + + return 0; +} + +static void +igbepci(void) +{ + int port, cls; + Pcidev *p; + Ctlr *ctlr; + static int first = 1; + + if (first) + first = 0; + else + return; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + + switch((p->did<<16)|p->vid){ + case i82542: + default: + continue; + + case (0x1001<<16)|0x8086: /* Intel PRO/1000 F */ + break; + case i82543gc: + case i82544ei: + case i82547ei: + case i82540em: + case i82540eplp: + case i82547gi: + case i82541gi: + case i82541pi: + case i82546gb: + case i82546eb: + break; + } + + /* the 82547EI is on the CSA bus, whatever that is */ + port = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0); + if(port == 0){ + print("igbe: can't map %d @ 0x%8.8luX\n", + p->mem[0].size, p->mem[0].bar); + continue; + } + + /* + * from etherga620.c: + * If PCI Write-and-Invalidate is enabled set the max write DMA + * value to the host cache-line size (32 on Pentium or later). + */ + if(p->pcr & MemWrInv){ + cls = pcicfgr8(p, PciCLS) * 4; + if(cls != CACHELINESZ) + pcicfgw8(p, PciCLS, CACHELINESZ/4); + } + + cls = pcicfgr8(p, PciCLS); + switch(cls){ + default: + print("igbe: unexpected CLS - %d bytes\n", + cls*sizeof(long)); + break; + case 0x00: + case 0xFF: + /* alphapc 164lx returns 0 */ + print("igbe: unusable PciCLS: %d, using %d longs\n", + cls, CACHELINESZ/sizeof(long)); + cls = CACHELINESZ/sizeof(long); + pcicfgw8(p, PciCLS, cls); + break; + case 0x08: + case 0x10: + break; + } + + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = port; + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + ctlr->cls = cls*4; + ctlr->nic = KADDR(ctlr->port); +print("status0 %8.8uX\n", csr32r(ctlr, Status)); + if(igbereset(ctlr)){ + free(ctlr); + continue; + } +print("status1 %8.8uX\n", csr32r(ctlr, Status)); + pcisetbme(p); + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + +int +igbepnp(Ether* edev) +{ + int i; + Ctlr *ctlr; + uchar ea[Eaddrlen]; + + if(ctlrhead == nil) + igbepci(); + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(edev->port == 0 || edev->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pcidev->intl; + edev->tbdf = ctlr->pcidev->tbdf; +// edev->mbps = 1000; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to + * loading the station address in the hardware. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, edev->ea, Eaddrlen) == 0){ + for(i = 0; i < Eaddrlen/2; i++){ + edev->ea[2*i] = ctlr->eeprom[i]; + edev->ea[2*i+1] = ctlr->eeprom[i]>>8; + } + } + igbeinit(edev); + + /* + * Linkage to the generic ethernet driver. + */ + edev->attach = igbeattach; + edev->transmit = igbetransmit; + edev->interrupt = igbeinterrupt; + edev->detach = igbedetach; + + return 0; +} diff --git a/os/boot/pc/ethermii.c b/os/boot/pc/ethermii.c new file mode 100644 index 00000000..160e4fe0 --- /dev/null +++ b/os/boot/pc/ethermii.c @@ -0,0 +1,224 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "etherif.h" +#include "ethermii.h" + +int +mii(Mii* mii, int mask) +{ + MiiPhy *miiphy; + int bit, phyno, r, rmask; + + /* + * Probe through mii for PHYs in mask; + * return the mask of those found in the current probe. + * If the PHY has not already been probed, update + * the Mii information. + */ + rmask = 0; + for(phyno = 0; phyno < NMiiPhy; phyno++){ + bit = 1<mask & bit){ + rmask |= bit; + continue; + } + if(mii->mir(mii, phyno, Bmsr) == -1) + continue; + if((miiphy = malloc(sizeof(MiiPhy))) == nil) + continue; + + miiphy->mii = mii; + r = mii->mir(mii, phyno, Phyidr1); + miiphy->oui = (r & 0x3FFF)<<6; + r = mii->mir(mii, phyno, Phyidr2); + miiphy->oui |= r>>10; + miiphy->phyno = phyno; + + miiphy->anar = ~0; + miiphy->fc = ~0; + miiphy->mscr = ~0; + + mii->phy[phyno] = miiphy; + if(mii->curphy == nil) + mii->curphy = miiphy; + mii->mask |= bit; + mii->nphy++; + + rmask |= bit; + } + return rmask; +} + +int +miimir(Mii* mii, int r) +{ + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + return mii->mir(mii, mii->curphy->phyno, r); +} + +int +miimiw(Mii* mii, int r, int data) +{ + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + return mii->miw(mii, mii->curphy->phyno, r, data); +} + +int +miireset(Mii* mii) +{ + int bmcr; + + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + bmcr = mii->mir(mii, mii->curphy->phyno, Bmcr); + bmcr |= BmcrR; + mii->miw(mii, mii->curphy->phyno, Bmcr, bmcr); + microdelay(1); + + return 0; +} + +int +miiane(Mii* mii, int a, int p, int e) +{ + int anar, bmsr, mscr, r, phyno; + + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + phyno = mii->curphy->phyno; + + bmsr = mii->mir(mii, phyno, Bmsr); + if(!(bmsr & BmsrAna)) + return -1; + + if(a != ~0) + anar = (AnaTXFD|AnaTXHD|Ana10FD|Ana10HD) & a; + else if(mii->curphy->anar != ~0) + anar = mii->curphy->anar; + else{ + anar = mii->mir(mii, phyno, Anar); + anar &= ~(AnaAP|AnaP|AnaT4|AnaTXFD|AnaTXHD|Ana10FD|Ana10HD); + if(bmsr & Bmsr10THD) + anar |= Ana10HD; + if(bmsr & Bmsr10TFD) + anar |= Ana10FD; + if(bmsr & Bmsr100TXHD) + anar |= AnaTXHD; + if(bmsr & Bmsr100TXFD) + anar |= AnaTXFD; + } + mii->curphy->anar = anar; + + if(p != ~0) + anar |= (AnaAP|AnaP) & p; + else if(mii->curphy->fc != ~0) + anar |= mii->curphy->fc; + mii->curphy->fc = (AnaAP|AnaP) & anar; + + if(bmsr & BmsrEs){ + mscr = mii->mir(mii, phyno, Mscr); + mscr &= ~(Mscr1000TFD|Mscr1000THD); + if(e != ~0) + mscr |= (Mscr1000TFD|Mscr1000THD) & e; + else if(mii->curphy->mscr != ~0) + mscr = mii->curphy->mscr; + else{ + r = mii->mir(mii, phyno, Esr); + if(r & Esr1000THD) + mscr |= Mscr1000THD; + if(r & Esr1000TFD) + mscr |= Mscr1000TFD; + } + mii->curphy->mscr = mscr; + mii->miw(mii, phyno, Mscr, mscr); + } + mii->miw(mii, phyno, Anar, anar); + + r = mii->mir(mii, phyno, Bmcr); + if(!(r & BmcrR)){ + r |= BmcrAne|BmcrRan; + mii->miw(mii, phyno, Bmcr, r); + } + + return 0; +} + +int +miistatus(Mii* mii) +{ + MiiPhy *phy; + int anlpar, bmsr, p, r, phyno; + + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + phy = mii->curphy; + phyno = phy->phyno; + + /* + * Check Auto-Negotiation is complete and link is up. + * (Read status twice as the Ls bit is sticky). + */ + bmsr = mii->mir(mii, phyno, Bmsr); + if(!(bmsr & (BmsrAnc|BmsrAna))) + return -1; + + bmsr = mii->mir(mii, phyno, Bmsr); + if(!(bmsr & BmsrLs)){ + phy->link = 0; + return -1; + } + + phy->speed = phy->fd = phy->rfc = phy->tfc = 0; + if(phy->mscr){ + r = mii->mir(mii, phyno, Mssr); + if((phy->mscr & Mscr1000TFD) && (r & Mssr1000TFD)){ + phy->speed = 1000; + phy->fd = 1; + } + else if((phy->mscr & Mscr1000THD) && (r & Mssr1000THD)) + phy->speed = 1000; + } + + anlpar = mii->mir(mii, phyno, Anlpar); + if(phy->speed == 0){ + r = phy->anar & anlpar; + if(r & AnaTXFD){ + phy->speed = 100; + phy->fd = 1; + } + else if(r & AnaTXHD) + phy->speed = 100; + else if(r & Ana10FD){ + phy->speed = 10; + phy->fd = 1; + } + else if(r & Ana10HD) + phy->speed = 10; + } + if(phy->speed == 0) + return -1; + + if(phy->fd){ + p = phy->fc; + r = anlpar & (AnaAP|AnaP); + if(p == AnaAP && r == (AnaAP|AnaP)) + phy->tfc = 1; + else if(p == (AnaAP|AnaP) && r == AnaAP) + phy->rfc = 1; + else if((p & AnaP) && (r & AnaP)) + phy->rfc = phy->tfc = 1; + } + + phy->link = 1; + + return 0; +} diff --git a/os/boot/pc/ethermii.h b/os/boot/pc/ethermii.h new file mode 100644 index 00000000..80c4ed06 --- /dev/null +++ b/os/boot/pc/ethermii.h @@ -0,0 +1,116 @@ +typedef struct Mii Mii; +typedef struct MiiPhy MiiPhy; + +enum { /* registers */ + Bmcr = 0x00, /* Basic Mode Control */ + Bmsr = 0x01, /* Basic Mode Status */ + Phyidr1 = 0x02, /* PHY Identifier #1 */ + Phyidr2 = 0x03, /* PHY Identifier #2 */ + Anar = 0x04, /* Auto-Negotiation Advertisement */ + Anlpar = 0x05, /* AN Link Partner Ability */ + Aner = 0x06, /* AN Expansion */ + Annptr = 0x07, /* AN Next Page TX */ + Annprr = 0x08, /* AN Next Page RX */ + Mscr = 0x09, /* MASTER-SLAVE Control */ + Mssr = 0x0A, /* MASTER-SLAVE Status */ + Esr = 0x0F, /* Extended Status */ + + NMiiPhyr = 32, + NMiiPhy = 32, +}; + +enum { /* Bmcr */ + BmcrSs1 = 0x0040, /* Speed Select[1] */ + BmcrCte = 0x0080, /* Collision Test Enable */ + BmcrDm = 0x0100, /* Duplex Mode */ + BmcrRan = 0x0200, /* Restart Auto-Negotiation */ + BmcrI = 0x0400, /* Isolate */ + BmcrPd = 0x0800, /* Power Down */ + BmcrAne = 0x1000, /* Auto-Negotiation Enable */ + BmcrSs0 = 0x2000, /* Speed Select[0] */ + BmcrLe = 0x4000, /* Loopback Enable */ + BmcrR = 0x8000, /* Reset */ +}; + +enum { /* Bmsr */ + BmsrEc = 0x0001, /* Extended Capability */ + BmsrJd = 0x0002, /* Jabber Detect */ + BmsrLs = 0x0004, /* Link Status */ + BmsrAna = 0x0008, /* Auto-Negotiation Ability */ + BmsrRf = 0x0010, /* Remote Fault */ + BmsrAnc = 0x0020, /* Auto-Negotiation Complete */ + BmsrPs = 0x0040, /* Preamble Suppression Capable */ + BmsrEs = 0x0100, /* Extended Status */ + Bmsr100T2HD = 0x0200, /* 100BASE-T2 HD Capable */ + Bmsr100T2FD = 0x0400, /* 100BASE-T2 FD Capable */ + Bmsr10THD = 0x0800, /* 100BASE-T HD Capable */ + Bmsr10TFD = 0x1000, /* 10BASE-T FD Capable */ + Bmsr100TXHD = 0x2000, /* 100BASE-TX HD Capable */ + Bmsr100TXFD = 0x4000, /* 100BASE-TX FD Capable */ + Bmsr100T4 = 0x8000, /* 100BASE-T4 Capable */ +}; + +enum { /* Anar/Anlpar */ + Ana10HD = 0x0020, /* Advertise 10BASE-T */ + Ana10FD = 0x0040, /* Advertise 10BASE-T FD */ + AnaTXHD = 0x0080, /* Advertise 100BASE-TX */ + AnaTXFD = 0x0100, /* Advertise 100BASE-TX FD */ + AnaT4 = 0x0200, /* Advertise 100BASE-T4 */ + AnaP = 0x0400, /* Pause */ + AnaAP = 0x0800, /* Asymmetrical Pause */ + AnaRf = 0x2000, /* Remote Fault */ + AnaAck = 0x4000, /* Acknowledge */ + AnaNp = 0x8000, /* Next Page Indication */ +}; + +enum { /* Mscr */ + Mscr1000THD = 0x0100, /* Advertise 1000BASE-T HD */ + Mscr1000TFD = 0x0200, /* Advertise 1000BASE-T FD */ +}; + +enum { /* Mssr */ + Mssr1000THD = 0x0400, /* Link Partner 1000BASE-T HD able */ + Mssr1000TFD = 0x0800, /* Link Partner 1000BASE-T FD able */ +}; + +enum { /* Esr */ + Esr1000THD = 0x1000, /* 1000BASE-T HD Capable */ + Esr1000TFD = 0x2000, /* 1000BASE-T FD Capable */ + Esr1000XHD = 0x4000, /* 1000BASE-X HD Capable */ + Esr1000XFD = 0x8000, /* 1000BASE-X FD Capable */ +}; + +typedef struct Mii { + Lock; + int nphy; + int mask; + MiiPhy* phy[NMiiPhy]; + MiiPhy* curphy; + + void* ctlr; + int (*mir)(Mii*, int, int); + int (*miw)(Mii*, int, int, int); +} Mii; + +typedef struct MiiPhy { + Mii* mii; + int oui; + int phyno; + + int anar; + int fc; + int mscr; + + int link; + int speed; + int fd; + int rfc; + int tfc; +}; + +extern int mii(Mii*, int); +extern int miiane(Mii*, int, int, int); +extern int miimir(Mii*, int); +extern int miimiw(Mii*, int, int); +extern int miireset(Mii*); +extern int miistatus(Mii*); diff --git a/os/boot/pc/etherrhine.c b/os/boot/pc/etherrhine.c new file mode 100644 index 00000000..b4d37bf5 --- /dev/null +++ b/os/boot/pc/etherrhine.c @@ -0,0 +1,676 @@ + /* + Via Rhine driver, written for VT6102. + Uses the ethermii to control PHY. + + Currently always copies on both, tx and rx. + rx side could be copy-free, and tx-side might be made + (almost) copy-free by using (possibly) two descriptors (if it allows + arbitrary tx lengths, which it should..): first for alignment and + second for rest of the frame. Rx-part should be worth doing. +*/ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +typedef struct QLock { int r; } QLock; +#define qlock(i) while(0) +#define qunlock(i) while(0) +#define coherence() +#define iprint print + +#include "etherif.h" +#include "ethermii.h" + +typedef struct Desc Desc; +typedef struct Ctlr Ctlr; + +enum { + Ntxd = 4, + Nrxd = 4, + Nwait = 50, + Ntxstats = 9, + Nrxstats = 8, + BIGSTR = 8192, +}; + +struct Desc { + ulong stat; + ulong size; + ulong addr; + ulong next; + char *buf; + ulong pad[3]; +}; + +struct Ctlr { + Pcidev *pci; + int attached; + int txused; + int txhead; + int txtail; + int rxtail; + ulong port; + + Mii mii; + + ulong txstats[Ntxstats]; + ulong rxstats[Nrxstats]; + + Desc *txd; /* wants to be aligned on 16-byte boundary */ + Desc *rxd; + + QLock attachlck; + Lock tlock; +}; + +#define ior8(c, r) (inb((c)->port+(r))) +#define ior16(c, r) (ins((c)->port+(r))) +#define ior32(c, r) (inl((c)->port+(r))) +#define iow8(c, r, b) (outb((c)->port+(r), (int)(b))) +#define iow16(c, r, w) (outs((c)->port+(r), (ushort)(w))) +#define iow32(c, r, l) (outl((c)->port+(r), (ulong)(l))) + +enum Regs { + Eaddr = 0x0, + Rcr = 0x6, + Tcr = 0x7, + Cr = 0x8, + Isr = 0xc, + Imr = 0xe, + McastAddr = 0x10, + RxdAddr = 0x18, + TxdAddr = 0x1C, + Bcr = 0x6e, + RhineMiiPhy = 0x6C, + RhineMiiSr = 0x6D, + RhineMiiCr = 0x70, + RhineMiiAddr = 0x71, + RhineMiiData = 0x72, + Eecsr = 0x74, + ConfigB = 0x79, + ConfigD = 0x7B, + MiscCr = 0x80, + HwSticky = 0x83, + MiscIsr = 0x84, + MiscImr = 0x86, + WolCrSet = 0xA0, + WolCfgSet = 0xA1, + WolCgSet = 0xA3, + WolCrClr = 0xA4, + PwrCfgClr = 0xA5, + WolCgClr = 0xA7, +}; + +enum Rcrbits { + RxErrX = 1<<0, + RxSmall = 1<<1, + RxMcast = 1<<2, + RxBcast = 1<<3, + RxProm = 1<<4, + RxFifo64 = 0<<5, RxFifo32 = 1<<5, RxFifo128 = 2<<5, RxFifo256 = 3<<5, + RxFifo512 = 4<<5, RxFifo768 = 5<<5, RxFifo1024 = 6<<5, + RxFifoStoreForward = 7<<5, +}; + +enum Tcrbits { + TxLoopback0 = 1<<1, + TxLoopback1 = 1<<2, + TxBackoff = 1<<3, + TxFifo128 = 0<<5, TxFifo256 = 1<<5, TxFifo512 = 2<<5, TxFifo1024 = 3<<5, + TxFifoStoreForward = 7<<5, +}; + +enum Crbits { + Init = 1<<0, + Start = 1<<1, + Stop = 1<<2, + RxOn = 1<<3, + TxOn = 1<<4, + Tdmd = 1<<5, + Rdmd = 1<<6, + EarlyRx = 1<<8, + Reserved0 = 1<<9, + FullDuplex = 1<<10, + NoAutoPoll = 1<<11, + Reserved1 = 1<<12, + Tdmd1 = 1<<13, + Rdmd1 = 1<<14, + Reset = 1<<15, +}; + +enum Isrbits { + RxOk = 1<<0, + TxOk = 1<<1, + RxErr = 1<<2, + TxErr = 1<<3, + TxBufUdf = 1<<4, + RxBufLinkErr = 1<<5, + BusErr = 1<<6, + CrcOvf = 1<<7, + EarlyRxInt = 1<<8, + TxFifoUdf = 1<<9, + RxFifoOvf = 1<<10, + TxPktRace = 1<<11, + NoRxbuf = 1<<12, + TxCollision = 1<<13, + PortCh = 1<<14, + GPInt = 1<<15 +}; + +enum Bcrbits { + Dma32 = 0<<0, Dma64 = 1<<0, Dma128 = 2<<0, + Dma256 = 3<<0, Dma512 = 4<<0, Dma1024 = 5<<0, + DmaStoreForward = 7<<0, + DupRxFifo0 = 1<<3, DupRxFifo1 = 1<<4, DupRxFifo2 = 1<<5, + ExtraLed = 1<<6, + MediumSelect = 1<<7, + PollTimer0 = 1<<8, PollTimer1 = 1<<9, PollTimer2 = 1<<10, + DupTxFifo0 = 1<<11, DupTxFifo1 = 1<<12, DupTxFifo2 = 1<<13, +}; + +enum Eecsrbits { + EeAutoLoad = 1<<5, +}; + +enum MiscCrbits { + Timer0Enable= 1<<0, + Timer0Suspend = 1<<1, + HalfDuplexFlowControl = 1<<2, + FullDuplexFlowControl = 1<<3, + Timer1Enable = 1<<8, + ForceSoftReset = 1<<14, +}; + +enum HwStickybits { + StickyDS0 = 1<<0, + StickyDS1 = 1<<1, + WOLEna = 1<<2, + WOLStat = 1<<3, +}; + +enum WolCgbits { + PmeOvr = 1<<7, +}; + +enum Descbits { + OwnNic = 1<<31, /* stat */ + TxAbort = 1<<8, /* stat */ + TxError = 1<<15, /* stat */ + RxChainbuf = 1<<10, /* stat */ + RxChainStart = 1<<9, /* stat */ + RxChainEnd = 1<<8, /* stat */ + Chainbuf = 1<<15, /* size rx & tx*/ + TxDisableCrc = 1<<16, /* size */ + TxChainStart = 1<<21, /* size */ + TxChainEnd = 1<<22, /* size */ + TxInt = 1<<23, /* size */ +}; + +enum ConfigDbits { + BackoffOptional = 1<<0, + BackoffAMD = 1<<1, + BackoffDEC = 1<<2, + BackoffRandom = 1<<3, + PmccTestMode = 1<<4, + PciReadlineCap = 1<<5, + DiagMode = 1<<6, + MmioEnable = 1<<7, +}; + +enum ConfigBbits { + LatencyTimer = 1<<0, + WriteWaitState = 1<<1, + ReadWaitState = 1<<2, + RxArbit = 1<<3, + TxArbit = 1<<4, + NoMemReadline = 1<<5, + NoParity = 1<<6, + NoTxQueuing = 1<<7, +}; + +enum RhineMiiCrbits { + Mdc = 1<<0, + Mdi = 1<<1, + Mdo = 1<<2, + Mdout = 1<<3, + Mdpm = 1<<4, + Wcmd = 1<<5, + Rcmd = 1<<6, + Mauto = 1<<7, +}; + +enum RhineMiiSrbits { + Speed10M = 1<<0, + LinkFail = 1<<1, + PhyError = 1<<3, + DefaultPhy = 1<<4, + ResetPhy = 1<<7, +}; + +enum RhineMiiAddrbits { + Mdone = 1<<5, + Msrcen = 1<<6, + Midle = 1<<7, +}; + +static char * +txstatnames[Ntxstats] = { + "aborts (excess collisions)", + "out of window collisions", + "carrier sense losses", + "fifo underflows", + "invalid descriptor format or underflows", + "system errors", + "reserved", + "transmit errors", + "collisions", +}; + +static char * +rxstatnames[Nrxstats] = { + "receiver errors", + "crc errors", + "frame alignment errors", + "fifo overflows", + "long packets", + "run packets", + "system errors", + "buffer underflows", +}; + +static void +attach(Ether *edev) +{ + Ctlr *ctlr; + Desc *txd, *rxd, *td, *rd; + Mii *mi; + MiiPhy *phy; + int i, s; + + ctlr = edev->ctlr; + qlock(&ctlr->attachlck); + if (ctlr->attached == 0) { + txd = ctlr->txd; + rxd = ctlr->rxd; + for (i = 0; i < Ntxd; ++i) { + td = &txd[i]; + td->next = PCIWADDR(&txd[(i+1) % Ntxd]); + td->buf = xspanalloc(sizeof(Etherpkt)+4, 4, 0); + td->addr = PCIWADDR(td->buf); + td->size = 0; + coherence(); + td->stat = 0; + } + for (i = 0; i < Nrxd; ++i) { + rd = &rxd[i]; + rd->next = PCIWADDR(&rxd[(i+1) % Nrxd]); + rd->buf = xspanalloc(sizeof(Etherpkt)+4, 4, 0); + rd->addr = PCIWADDR(rd->buf); + rd->size = sizeof(Etherpkt)+4; + coherence(); + rd->stat = OwnNic; + } + + ctlr->txhead = ctlr->txtail = ctlr->rxtail = 0; + mi = &ctlr->mii; + miistatus(mi); + phy = mi->curphy; + s = splhi(); + iow32(ctlr, TxdAddr, PCIWADDR(&txd[0])); + iow32(ctlr, RxdAddr, PCIWADDR(&rxd[0])); + iow16(ctlr, Cr, (phy->fd ? FullDuplex : 0) | NoAutoPoll | TxOn | RxOn | Start | Rdmd); + iow16(ctlr, Isr, 0xFFFF); + iow16(ctlr, Imr, 0xFFFF); + iow8(ctlr, MiscIsr, 0xFF); + iow8(ctlr, MiscImr, ~(3<<5)); + splx(s); + } + ctlr->attached++; + qunlock(&ctlr->attachlck); +} + +static void +txstart(Ether *edev) +{ + Ctlr *ctlr; + Desc *txd, *td; + int i, txused, n; + RingBuf *tb; + + ctlr = edev->ctlr; + + txd = ctlr->txd; + i = ctlr->txhead; + txused = ctlr->txused; + n = 0; + while (txused < Ntxd) { + tb = &edev->tb[edev->ti]; + if(tb->owner != Interface) + break; + + td = &txd[i]; + memmove(td->buf, tb->pkt, tb->len); + td->size = tb->len | TxChainStart | TxChainEnd | TxInt; /* could reduce number of ints here */ + coherence(); + td->stat = OwnNic; + i = (i + 1) % Ntxd; + txused++; + n++; + + tb->owner = Host; + edev->ti = NEXT(edev->ti, edev->ntb); + } + if (n) + iow16(ctlr, Cr, ior16(ctlr, Cr) | Tdmd); + + ctlr->txhead = i; + ctlr->txused = txused; +} + +static void +transmit(Ether *edev) +{ + Ctlr *ctlr; + ctlr = edev->ctlr; + ilock(&ctlr->tlock); + txstart(edev); + iunlock(&ctlr->tlock); +} + +static void +txcomplete(Ether *edev) +{ + Ctlr *ctlr; + Desc *txd, *td; + int i, txused, j; + ulong stat; + + ctlr = edev->ctlr; + txd = ctlr->txd; + txused = ctlr->txused; + i = ctlr->txtail; + while (txused > 0) { + td = &txd[i]; + stat = td->stat; + + if (stat & OwnNic) + break; + + ctlr->txstats[Ntxstats-1] += stat & 0xF; + for (j = 0; j < Ntxstats-1; ++j) + if (stat & (1<<(j+8))) + ctlr->txstats[j]++; + + i = (i + 1) % Ntxd; + txused--; + } + ctlr->txused = txused; + ctlr->txtail = i; + + if (txused <= Ntxd/2) + txstart(edev); +} + +static void +interrupt(Ureg *, void *arg) +{ + Ether *edev; + Ctlr *ctlr; + RingBuf *rb; + ushort isr, misr; + ulong stat; + Desc *rxd, *rd; + int i, n, j, size; + + edev = (Ether*)arg; + ctlr = edev->ctlr; + iow16(ctlr, Imr, 0); + isr = ior16(ctlr, Isr); + iow16(ctlr, Isr, 0xFFFF); + misr = ior16(ctlr, MiscIsr) & ~(3<<5); /* don't care about used defined ints */ + + if (isr & RxOk) { + rxd = ctlr->rxd; + i = ctlr->rxtail; + + n = 0; + while ((rxd[i].stat & OwnNic) == 0) { + rd = &rxd[i]; + stat = rd->stat; + for (j = 0; j < Nrxstats; ++j) + if (stat & (1<rxstats[j]++; + + if (stat & 0xFF) + iprint("rx: %lux\n", stat & 0xFF); + + size = ((rd->stat>>16) & 2047) - 4; + + rb = &edev->rb[edev->ri]; + if(rb->owner == Interface){ + rb->owner = Host; + rb->len = size; + memmove(rb->pkt, rd->buf, size); + edev->ri = NEXT(edev->ri, edev->nrb); + } + + rd->size = sizeof(Etherpkt)+4; + coherence(); + rd->stat = OwnNic; + i = (i + 1) % Nrxd; + n++; + } + if (n) + iow16(ctlr, Cr, ior16(ctlr, Cr) | Rdmd); + ctlr->rxtail = i; + isr &= ~RxOk; + } + if (isr & TxOk) { + txcomplete(edev); + isr &= ~TxOk; + } + if (isr | misr) + iprint("etherrhine: unhandled irq(s). isr:%x misr:%x\n", isr, misr); + + iow16(ctlr, Imr, 0xFFFF); +} + +static void +promiscuous(void *arg, int enable) +{ + Ether *edev; + Ctlr *ctlr; + + edev = arg; + ctlr = edev->ctlr; + ilock(&ctlr->tlock); + iow8(ctlr, Rcr, ior8(ctlr, Rcr) | (enable ? RxProm : RxBcast)); + iunlock(&ctlr->tlock); +} + +static int +miiread(Mii *mii, int phy, int reg) +{ + Ctlr *ctlr; + int n; + + ctlr = mii->ctlr; + + n = Nwait; + while (n-- && ior8(ctlr, RhineMiiCr) & (Rcmd | Wcmd)) + microdelay(1); + if (n == Nwait) + iprint("etherrhine: miiread: timeout\n"); + + iow8(ctlr, RhineMiiCr, 0); + iow8(ctlr, RhineMiiPhy, phy); + iow8(ctlr, RhineMiiAddr, reg); + iow8(ctlr, RhineMiiCr, Rcmd); + + n = Nwait; + while (n-- && ior8(ctlr, RhineMiiCr) & Rcmd) + microdelay(1); + if (n == Nwait) + iprint("etherrhine: miiread: timeout\n"); + + n = ior16(ctlr, RhineMiiData); + + return n; +} + +static int +miiwrite(Mii *mii, int phy, int reg, int data) +{ + int n; + Ctlr *ctlr; + + ctlr = mii->ctlr; + + n = Nwait; + while (n-- && ior8(ctlr, RhineMiiCr) & (Rcmd | Wcmd)) + microdelay(1); + if (n == Nwait) + iprint("etherrhine: miiwrite: timeout\n"); + + iow8(ctlr, RhineMiiCr, 0); + iow8(ctlr, RhineMiiPhy, phy); + iow8(ctlr, RhineMiiAddr, reg); + iow16(ctlr, RhineMiiData, data); + iow8(ctlr, RhineMiiCr, Wcmd); + + n = Nwait; + while (n-- && ior8(ctlr, RhineMiiCr) & Wcmd) + microdelay(1); + if (n == Nwait) + iprint("etherrhine: miiwrite: timeout\n"); + + return 0; +} + +static void +reset(Ctlr* ctlr) +{ + int i; + + iow16(ctlr, Cr, ior16(ctlr, Cr) | Stop); + iow16(ctlr, Cr, ior16(ctlr, Cr) | Reset); + + for (i = 0; i < Nwait; ++i) { + if ((ior16(ctlr, Cr) & Reset) == 0) + return; + delay(5); + } + iprint("etherrhine: reset timeout\n"); +} + +static void +detach(Ether* edev) +{ + reset(edev->ctlr); +} + +static void +init(Ether *edev) +{ + Ctlr *ctlr; + int i; + + ctlr = edev->ctlr; + + ilock(&ctlr->tlock); + + pcisetbme(ctlr->pci); + + reset(ctlr); + + iow8(ctlr, Eecsr, ior8(ctlr, Eecsr) | EeAutoLoad); + for (i = 0; i < Nwait; ++i) { + if ((ior8(ctlr, Eecsr) & EeAutoLoad) == 0) + break; + delay(5); + } + if (i == Nwait) + iprint("etherrhine: eeprom autoload timeout\n"); + + for (i = 0; i < Eaddrlen; ++i) + edev->ea[i] = ior8(ctlr, Eaddr + i); + + ctlr->mii.mir = miiread; + ctlr->mii.miw = miiwrite; + ctlr->mii.ctlr = ctlr; + + if(mii(&ctlr->mii, ~0) == 0 || ctlr->mii.curphy == nil){ + iprint("etherrhine: init mii failure\n"); + return; + } + for (i = 0; i < NMiiPhy; ++i) + if (ctlr->mii.phy[i]) + if (ctlr->mii.phy[i]->oui != 0xFFFFF) + ctlr->mii.curphy = ctlr->mii.phy[i]; + + miistatus(&ctlr->mii); + + iow16(ctlr, Imr, 0); + iow16(ctlr, Cr, ior16(ctlr, Cr) | Stop); + + iunlock(&ctlr->tlock); +} + +static Pcidev * +rhinematch(ulong) +{ + static int nrhines = 0; + int nfound = 0; + Pcidev *p = nil; + + while (p = pcimatch(p, 0x1106, 0)) + if (p->did == 0x3065) + if (++nfound > nrhines) { + nrhines++; + break; + } + return p; +} + +int +rhinepnp(Ether *edev) +{ + Pcidev *p; + Ctlr *ctlr; + ulong port; + + p = rhinematch(edev->port); + if (p == nil) + return -1; + + port = p->mem[0].bar & ~1; + + if ((ctlr = malloc(sizeof(Ctlr))) == nil) { + print("etherrhine: couldn't allocate memory for ctlr\n"); + return -1; + } + memset(ctlr, 0, sizeof(Ctlr)); + ctlr->txd = xspanalloc(sizeof(Desc) * Ntxd, 16, 0); + ctlr->rxd = xspanalloc(sizeof(Desc) * Nrxd, 16, 0); + + ctlr->pci = p; + ctlr->port = port; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = p->intl; + edev->tbdf = p->tbdf; + + init(edev); + + + edev->attach = attach; + edev->transmit = transmit; + edev->interrupt = interrupt; + edev->detach = detach; + + return 0; +} diff --git a/os/boot/pc/fns.h b/os/boot/pc/fns.h new file mode 100644 index 00000000..794fe592 --- /dev/null +++ b/os/boot/pc/fns.h @@ -0,0 +1,155 @@ +void aamloop(int); +void addconf(char*, ...); +Alarm* alarm(int, void (*)(Alarm*), void*); +void alarminit(void); +Block* allocb(int); +void apminit(void); +int bootpboot(int, char*, Boot*); +int bootpass(Boot*, void*, int); +void cancel(Alarm*); +int cdinit(void); +void check(char*); +void cgascreenputs(char*, int); +int cistrcmp(char*, char*); +int cistrncmp(char*, char*, int); +void changeconf(char*, ...); +void checkalarms(void); +void clockinit(void); +void consdrain(void); +void consinit(char*, char*); +void consputs(char*, int); +void delay(int); +uchar* etheraddr(int); +int etherinit(void); +void etherinitdev(int, char*); +void etherprintdevs(int); +int etherrxflush(int); +int etherrxpkt(int, Etherpkt*, int); +int ethertxpkt(int, Etherpkt*, int, int); +#define evenaddr(x) /* 386 doesn't care */ +int floppyboot(int, char*, Boot*); +int floppyinit(void); +void floppyinitdev(int, char*); +void floppyprintdevs(int); +void* floppygetfspart(int, char*, int); +void freeb(Block*); +char* getconf(char*); +ulong getcr0(void); +ulong getcr2(void); +ulong getcr3(void); +ulong getcr4(void); +int getfields(char*, char**, int, char); +int getstr(char*, char*, int, char*, int); +int gunzip(uchar*, int, uchar*, int); +void i8042a20(void); +void i8042init(void); +void i8042reset(void); +void* ialloc(ulong, int); +void idle(void); +void ilock(Lock*); +int inb(int); +ushort ins(int); +ulong inl(int); +void insb(int, void*, int); +void inss(int, void*, int); +void insl(int, void*, int); +void iunlock(Lock*); +int isaconfig(char*, int, ISAConf*); +void kbdinit(void); +void kbdchar(int); +void machinit(void); +void meminit(ulong); +void microdelay(int); +void mmuinit(void); +#define nelem(x) (sizeof(x)/sizeof(x[0])) +char* nextelem(char*, char*); +uchar nvramread(int); +void outb(int, int); +void outs(int, ushort); +void outl(int, ulong); +void outsb(int, void*, int); +void outss(int, void*, int); +void outsl(int, void*, int); +void panic(char*, ...); +int pcicfgr8(Pcidev*, int); +int pcicfgr16(Pcidev*, int); +int pcicfgr32(Pcidev*, int); +void pcicfgw8(Pcidev*, int, int); +void pcicfgw16(Pcidev*, int, int); +void pcicfgw32(Pcidev*, int, int); +void pcihinv(Pcidev*); +Pcidev* pcimatch(Pcidev*, int, int); +uchar pciintl(Pcidev *); +uchar pciipin(Pcidev *, uchar); +void pcireset(void); +void pcisetbme(Pcidev*); +int pcmcistuple(int, int, void*, int); +int pcmspecial(char*, ISAConf*); +void pcmspecialclose(int); +void pcmunmap(int, PCMmap*); +void ptcheck(char*); +void putcr3(ulong); +void putidt(Segdesc*, int); +void* pxegetfspart(int, char*, int); +void qinit(IOQ*); +void readlsconf(void); +void sdaddconf(int); +int sdboot(int, char*, Boot*); +void sdcheck(char*); +void* sdgetfspart(int, char*, int); +int sdinit(void); +void sdinitdev(int, char*); +void sdprintdevs(int); +int sdsetpart(int, char*); +void setvec(int, void (*)(Ureg*, void*), void*); +int splhi(void); +int spllo(void); +void splx(int); +void trapinit(void); +void trapdisable(void); +void trapenable(void); +void uartdrain(void); +void uartspecial(int, void (*)(int), int (*)(void), int); +void uartputs(IOQ*, char*, int); +ulong umbmalloc(ulong, int, int); +void umbfree(ulong, int); +ulong umbrwmalloc(ulong, int, int); +void upafree(ulong, int); +ulong upamalloc(ulong, int, int); +void warp86(char*, ulong); +void warp9(ulong); +int x86cpuid(int*, int*); +void* xspanalloc(ulong, int, ulong); + +#define malloc(n) ialloc(n, 0) +#define mallocz(n, c) ialloc(n, 0) +#define free(v) while(0) + +#define GSHORT(p) (((p)[1]<<8)|(p)[0]) +#define GLONG(p) ((GSHORT(p+2)<<16)|GSHORT(p)) +#define GLSHORT(p) (((p)[0]<<8)|(p)[1]) +#define GLLONG(p) (((ulong)GLSHORT(p)<<16)|GLSHORT(p+2)) +#define PLLONG(p,v) (p)[3]=(v);(p)[2]=(v)>>8;(p)[1]=(v)>>16;(p)[0]=(v)>>24 + +#define KADDR(a) ((void*)((ulong)(a)|KZERO)) +#define PADDR(a) ((ulong)(a)&~0xF0000000) + +#define HOWMANY(x, y) (((x)+((y)-1))/(y)) +#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) + + +#define xalloc(n) ialloc(n, 0) +#define xfree(v) while(0) +#define lock(l) if(l){/* nothing to do */;}else{/* nothing to do */;} +#define unlock(l) if(l){/* nothing to do */;}else{/* nothing to do */;} + +int dmacount(int); +int dmadone(int); +void dmaend(int); +void dmainit(int); +long dmasetup(int, void*, long, int); + +extern int (*_pcmspecial)(char *, ISAConf *); +extern void (*_pcmspecialclose)(int); +extern void devi82365link(void); +extern void devpccardlink(void); diff --git a/os/boot/pc/fs.c b/os/boot/pc/fs.c new file mode 100644 index 00000000..24b33c09 --- /dev/null +++ b/os/boot/pc/fs.c @@ -0,0 +1,94 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "fs.h" + +/* + * grab next element from a path, return the pointer to unprocessed portion of + * path. + */ +char * +nextelem(char *path, char *elem) +{ + int i; + + while(*path == '/') + path++; + if(*path==0 || *path==' ') + return 0; + for(i=0; *path!='\0' && *path!='/' && *path!=' '; i++){ + if(i==NAMELEN){ + print("name component too long\n"); + return 0; + } + *elem++ = *path++; + } + *elem = '\0'; + return path; +} + +int +fswalk(Fs *fs, char *path, File *f) +{ + char element[NAMELEN]; + + *f = fs->root; + if(BADPTR(fs->walk)) + panic("fswalk bad pointer fs->walk"); + + f->path = path; + while(path = nextelem(path, element)){ + switch(fs->walk(f, element)){ + case -1: + return -1; + case 0: + return 0; + } + } + return 1; +} + +/* + * boot + */ +int +fsboot(Fs *fs, char *path, Boot *b) +{ + File file; + long n; + static char buf[8192]; + + switch(fswalk(fs, path, &file)){ + case -1: + print("error walking to %s\n", path); + return -1; + case 0: + print("%s not found\n", path); + return -1; + case 1: + print("found %s\n", path); + break; + } + + while((n = fsread(&file, buf, sizeof buf)) > 0) { + if(bootpass(b, buf, n) != MORE) + break; + } + + bootpass(b, nil, 0); /* tries boot */ + return -1; +} + +int +fsread(File *file, void *a, long n) +{ + if(BADPTR(file->fs)) + panic("bad pointer file->fs in fsread"); + if(BADPTR(file->fs->read)) + panic("bad pointer file->fs->read in fsread"); + return file->fs->read(file, a, n); +} diff --git a/os/boot/pc/fs.h b/os/boot/pc/fs.h new file mode 100644 index 00000000..1b53e929 --- /dev/null +++ b/os/boot/pc/fs.h @@ -0,0 +1,36 @@ +typedef struct File File; +typedef struct Fs Fs; + +#include "dosfs.h" +#include "kfs.h" + +struct File{ + union{ + Dosfile dos; + Kfsfile kfs; + int walked; + }; + Fs *fs; + char *path; +}; + +struct Fs{ + union { + Dos dos; + Kfs kfs; + }; + int dev; /* device id */ + long (*diskread)(Fs*, void*, long); /* disk read routine */ + vlong (*diskseek)(Fs*, vlong); /* disk seek routine */ + long (*read)(File*, void*, long); + int (*walk)(File*, char*); + File root; +}; + +extern int chatty; +extern int dotini(Fs*); +extern int fswalk(Fs*, char*, File*); +extern int fsread(File*, void*, long); +extern int fsboot(Fs*, char*, Boot*); + +#define BADPTR(x) ((ulong)x < 0x80000000) diff --git a/os/boot/pc/getcallerpc.c b/os/boot/pc/getcallerpc.c new file mode 100644 index 00000000..c14a2449 --- /dev/null +++ b/os/boot/pc/getcallerpc.c @@ -0,0 +1,8 @@ +#include "u.h" +#include "lib.h" + +ulong +getcallerpc(void *x) +{ + return (((ulong*)(x))[-1]); +} diff --git a/os/boot/pc/ilock.c b/os/boot/pc/ilock.c new file mode 100644 index 00000000..977b9ad2 --- /dev/null +++ b/os/boot/pc/ilock.c @@ -0,0 +1,24 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +void +ilock(Lock *lk) +{ + if(lk->locked != 0) + panic("ilock"); + lk->spl = splhi(); + lk->locked = 1; +} + +void +iunlock(Lock *lk) +{ + if(lk->locked != 1) + panic("iunlock"); + lk->locked = 0; + splx(lk->spl); +} diff --git a/os/boot/pc/inflate.c b/os/boot/pc/inflate.c new file mode 100644 index 00000000..262511a0 --- /dev/null +++ b/os/boot/pc/inflate.c @@ -0,0 +1,199 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include + +typedef struct Biobuf Biobuf; + +struct Biobuf +{ + uchar *bp; + uchar *p; + uchar *ep; +}; + +static int header(Biobuf*); +static int trailer(Biobuf*, Biobuf*); +static int getc(void*); +static ulong offset(Biobuf*); +static int crcwrite(void *out, void *buf, int n); +static ulong get4(Biobuf *b); +static ulong Boffset(Biobuf *bp); + +/* GZIP flags */ +enum { + Ftext= (1<<0), + Fhcrc= (1<<1), + Fextra= (1<<2), + Fname= (1<<3), + Fcomment= (1<<4), + + GZCRCPOLY = 0xedb88320UL, +}; + +static ulong *crctab; +static ulong crc; + +extern void diff(char*); //XXX +int +gunzip(uchar *out, int outn, uchar *in, int inn) +{ + Biobuf bin, bout; + int err; + + crc = 0; + crctab = mkcrctab(GZCRCPOLY); + err = inflateinit(); + if(err != FlateOk) + print("inflateinit failed: %s\n", flateerr(err)); + + bin.bp = bin.p = in; + bin.ep = in+inn; + bout.bp = bout.p = out; + bout.ep = out+outn; + + err = header(&bin); + if(err != FlateOk) + return err; + + err = inflate(&bout, crcwrite, &bin, getc); + if(err != FlateOk) + print("inflate failed: %s\n", flateerr(err)); + + err = trailer(&bout, &bin); + if(err != FlateOk) + return err; + + return Boffset(&bout); +} + +static int +header(Biobuf *bin) +{ + int i, flag; + + if(getc(bin) != 0x1f || getc(bin) != 0x8b){ + print("bad magic\n"); + return FlateCorrupted; + } + if(getc(bin) != 8){ + print("unknown compression type\n"); + return FlateCorrupted; + } + + flag = getc(bin); + + /* mod time */ + get4(bin); + + /* extra flags */ + getc(bin); + + /* OS type */ + getc(bin); + + if(flag & Fextra) + for(i=getc(bin); i>0; i--) + getc(bin); + + /* name */ + if(flag&Fname) + while(getc(bin) != 0) + ; + + /* comment */ + if(flag&Fcomment) + while(getc(bin) != 0) + ; + + /* crc16 */ + if(flag&Fhcrc) { + getc(bin); + getc(bin); + } + + return FlateOk; +} + +static int +trailer(Biobuf *bout, Biobuf *bin) +{ + /* crc32 */ + if(crc != get4(bin)){ + print("crc mismatch\n"); + return FlateCorrupted; + } + + /* length */ + if(get4(bin) != Boffset(bout)){ + print("bad output len\n"); + return FlateCorrupted; + } + return FlateOk; +} + +static ulong +get4(Biobuf *b) +{ + ulong v; + int i, c; + + v = 0; + for(i = 0; i < 4; i++){ + c = getc(b); + v |= c << (i * 8); + } + return v; +} + +static int +getc(void *in) +{ + Biobuf *bp = in; + +// if((bp->p - bp->bp) % 10000 == 0) +// print("."); + if(bp->p >= bp->ep) + return -1; + return *bp->p++; +} + +static ulong +Boffset(Biobuf *bp) +{ + return bp->p - bp->bp; +} + +static int +crcwrite(void *out, void *buf, int n) +{ + Biobuf *bp; + int nn; + + crc = blockcrc(crctab, crc, buf, n); + bp = out; + nn = n; + if(nn > bp->ep-bp->p) + nn = bp->ep-bp->p; + if(nn > 0) + memmove(bp->p, buf, nn); + bp->p += n; + return n; +} + +#undef malloc +#undef free + +void * +malloc(ulong n) +{ + return ialloc(n, 8); +} + +void +free(void *) +{ +} diff --git a/os/boot/pc/io.h b/os/boot/pc/io.h new file mode 100644 index 00000000..67219ddd --- /dev/null +++ b/os/boot/pc/io.h @@ -0,0 +1,201 @@ +/* + * programmable interrupt vectors (for the 8259's) + */ +enum +{ + Bptvec= 3, /* breakpoints */ + Mathemuvec= 7, /* math coprocessor emulation interrupt */ + Mathovervec= 9, /* math coprocessor overrun interrupt */ + Matherr1vec= 16, /* math coprocessor error interrupt */ + Faultvec= 14, /* page fault */ + + Syscallvec= 64, + + VectorPIC = 24, /* external [A]PIC interrupts */ + VectorCLOCK = VectorPIC+0, + VectorKBD = VectorPIC+1, + VectorUART1 = VectorPIC+3, + VectorUART0 = VectorPIC+4, + VectorPCMCIA = VectorPIC+5, + VectorFLOPPY = VectorPIC+6, + VectorLPT = VectorPIC+7, + VectorIRQ7 = VectorPIC+7, + VectorAUX = VectorPIC+12, /* PS/2 port */ + VectorIRQ13 = VectorPIC+13, /* coprocessor on x386 */ + VectorATA0 = VectorPIC+14, + VectorATA1 = VectorPIC+15, + MaxVectorPIC = VectorPIC+15, +}; + +enum { + BusCBUS = 0, /* Corollary CBUS */ + BusCBUSII, /* Corollary CBUS II */ + BusEISA, /* Extended ISA */ + BusFUTURE, /* IEEE Futurebus */ + BusINTERN, /* Internal bus */ + BusISA, /* Industry Standard Architecture */ + BusMBI, /* Multibus I */ + BusMBII, /* Multibus II */ + BusMCA, /* Micro Channel Architecture */ + BusMPI, /* MPI */ + BusMPSA, /* MPSA */ + BusNUBUS, /* Apple Macintosh NuBus */ + BusPCI, /* Peripheral Component Interconnect */ + BusPCMCIA, /* PC Memory Card International Association */ + BusTC, /* DEC TurboChannel */ + BusVL, /* VESA Local bus */ + BusVME, /* VMEbus */ + BusXPRESS, /* Express System Bus */ +}; + +#define MKBUS(t,b,d,f) (((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8)) +#define BUSFNO(tbdf) (((tbdf)>>8)&0x07) +#define BUSDNO(tbdf) (((tbdf)>>11)&0x1F) +#define BUSBNO(tbdf) (((tbdf)>>16)&0xFF) +#define BUSTYPE(tbdf) ((tbdf)>>24) +#define BUSBDF(tbdf) ((tbdf)&0x00FFFF00) +#define BUSUNKNOWN (-1) + +enum { + MaxEISA = 16, + CfgEISA = 0xC80, +}; + +/* + * PCI support code. + */ +enum { /* type 0 and type 1 pre-defined header */ + PciVID = 0x00, /* vendor ID */ + PciDID = 0x02, /* device ID */ + PciPCR = 0x04, /* command */ + PciPSR = 0x06, /* status */ + PciRID = 0x08, /* revision ID */ + PciCCRp = 0x09, /* programming interface class code */ + PciCCRu = 0x0A, /* sub-class code */ + PciCCRb = 0x0B, /* base class code */ + PciCLS = 0x0C, /* cache line size */ + PciLTR = 0x0D, /* latency timer */ + PciHDT = 0x0E, /* header type */ + PciBST = 0x0F, /* BIST */ + + PciBAR0 = 0x10, /* base address */ + PciBAR1 = 0x14, + + PciINTL = 0x3C, /* interrupt line */ + PciINTP = 0x3D, /* interrupt pin */ +}; + +enum { /* type 0 pre-defined header */ + PciBAR2 = 0x18, + PciBAR3 = 0x1C, + PciBAR4 = 0x20, + PciBAR5 = 0x24, + PciCIS = 0x28, /* cardbus CIS pointer */ + PciSVID = 0x2C, /* subsystem vendor ID */ + PciSID = 0x2E, /* cardbus CIS pointer */ + PciEBAR0 = 0x30, /* expansion ROM base address */ + PciMGNT = 0x3E, /* burst period length */ + PciMLT = 0x3F, /* maximum latency between bursts */ +}; + +enum { /* type 1 pre-defined header */ + PciPBN = 0x18, /* primary bus number */ + PciSBN = 0x19, /* secondary bus number */ + PciUBN = 0x1A, /* subordinate bus number */ + PciSLTR = 0x1B, /* secondary latency timer */ + PciIBR = 0x1C, /* I/O base */ + PciILR = 0x1D, /* I/O limit */ + PciSPSR = 0x1E, /* secondary status */ + PciMBR = 0x20, /* memory base */ + PciMLR = 0x22, /* memory limit */ + PciPMBR = 0x24, /* prefetchable memory base */ + PciPMLR = 0x26, /* prefetchable memory limit */ + PciPUBR = 0x28, /* prefetchable base upper 32 bits */ + PciPULR = 0x2C, /* prefetchable limit upper 32 bits */ + PciIUBR = 0x30, /* I/O base upper 16 bits */ + PciIULR = 0x32, /* I/O limit upper 16 bits */ + PciEBAR1 = 0x28, /* expansion ROM base address */ + PciBCR = 0x3E, /* bridge control register */ +}; + +enum { /* type 2 pre-defined header */ + PciCBExCA = 0x10, + PciCBSPSR = 0x16, + PciCBPBN = 0x18, /* primary bus number */ + PciCBSBN = 0x19, /* secondary bus number */ + PciCBUBN = 0x1A, /* subordinate bus number */ + PciCBSLTR = 0x1B, /* secondary latency timer */ + PciCBMBR0 = 0x1C, + PciCBMLR0 = 0x20, + PciCBMBR1 = 0x24, + PciCBMLR1 = 0x28, + PciCBIBR0 = 0x2C, /* I/O base */ + PciCBILR0 = 0x30, /* I/O limit */ + PciCBIBR1 = 0x34, /* I/O base */ + PciCBILR1 = 0x38, /* I/O limit */ + PciCBBCTL = 0x3E, /* Bridhe control */ + PciCBSVID = 0x40, /* subsystem vendor ID */ + PciCBSID = 0x42, /* subsystem ID */ + PciCBLMBAR = 0x44, /* legacy mode base address */ +}; + +typedef struct Pcisiz Pcisiz; +struct Pcisiz +{ + Pcidev* dev; + int siz; + int bar; +}; + +typedef struct Pcidev Pcidev; +typedef struct Pcidev { + int tbdf; /* type+bus+device+function */ + ushort vid; /* vendor ID */ + ushort did; /* device ID */ + + uchar rid; + uchar ccrp; + uchar ccru; + uchar ccrb; + + struct { + ulong bar; /* base address */ + int size; + } mem[6]; + + struct { + ulong bar; + int size; + } rom; + uchar intl; /* interrupt line */ + + Pcidev* list; + Pcidev* bridge; /* down a bus */ + Pcidev* link; /* next device on this bno */ + struct { + ulong bar; + int size; + } ioa, mema; + + ulong pcr; +}; + +#define PCIWINDOW 0 +#define PCIWADDR(va) (PADDR(va)+PCIWINDOW) +#define ISAWINDOW 0 +#define ISAWADDR(va) (PADDR(va)+ISAWINDOW) + +/* + * PCMCIA support code. + */ +/* + * Map between ISA memory space and PCMCIA card memory space. + */ +struct PCMmap { + ulong ca; /* card address */ + ulong cea; /* card end address */ + ulong isa; /* ISA address */ + int len; /* length of the ISA area */ + int attr; /* attribute memory */ + int ref; +}; diff --git a/os/boot/pc/ip.h b/os/boot/pc/ip.h new file mode 100644 index 00000000..6dfb7cdd --- /dev/null +++ b/os/boot/pc/ip.h @@ -0,0 +1,100 @@ +typedef struct Udphdr Udphdr; +struct Udphdr +{ + uchar d[6]; /* Ethernet destination */ + uchar s[6]; /* Ethernet source */ + uchar type[2]; /* Ethernet packet type */ + + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + + /* Udp pseudo ip really starts here */ + uchar ttl; + uchar udpproto; /* Protocol */ + uchar udpplen[2]; /* Header plus data length */ + uchar udpsrc[4]; /* Ip source */ + uchar udpdst[4]; /* Ip destination */ + uchar udpsport[2]; /* Source port */ + uchar udpdport[2]; /* Destination port */ + uchar udplen[2]; /* data length */ + uchar udpcksum[2]; /* Checksum */ +}; + +typedef struct Etherhdr Etherhdr; +struct Etherhdr +{ + uchar d[6]; + uchar s[6]; + uchar type[2]; + + /* Now we have the ip fields */ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar ttl; /* Time to live */ + uchar proto; /* Protocol */ + uchar cksum[2]; /* Header checksum */ + uchar src[4]; /* Ip source */ + uchar dst[4]; /* Ip destination */ +}; + +enum +{ + IP_VER = 0x40, + IP_HLEN = 0x05, + UDP_EHSIZE = 22, + UDP_PHDRSIZE = 12, + UDP_HDRSIZE = 20, + ETHER_HDR = 14, + IP_UDPPROTO = 17, + ET_IP = 0x800, + Bcastip = 0xffffffff, + BPportsrc = 68, + BPportdst = 67, + TFTPport = 69, + Timeout = 5000, /* milliseconds */ + Bootrequest = 1, + Bootreply = 2, + Tftp_READ = 1, + Tftp_WRITE = 2, + Tftp_DATA = 3, + Tftp_ACK = 4, + Tftp_ERROR = 5, + Segsize = 512, + TFTPSZ = Segsize+10, +}; + +typedef struct Bootp Bootp; +struct Bootp +{ + uchar op; /* opcode */ + uchar htype; /* hardware type */ + uchar hlen; /* hardware address len */ + uchar hops; /* hops */ + uchar xid[4]; /* a random number */ + uchar secs[2]; /* elapsed since client started booting */ + uchar pad[2]; + uchar ciaddr[4]; /* client IP address (client tells server) */ + uchar yiaddr[4]; /* client IP address (server tells client) */ + uchar siaddr[4]; /* server IP address */ + uchar giaddr[4]; /* gateway IP address */ + uchar chaddr[16]; /* client hardware address */ + char sname[64]; /* server host name (optional) */ + char file[128]; /* boot file name */ + char vend[128]; /* vendor-specific goo */ +}; + +typedef struct Netaddr Netaddr; +struct Netaddr +{ + ulong ip; + ushort port; + char ea[Eaddrlen]; +}; + +extern int eipfmt(Fmt*); diff --git a/os/boot/pc/kbd.c b/os/boot/pc/kbd.c new file mode 100644 index 00000000..a572866f --- /dev/null +++ b/os/boot/pc/kbd.c @@ -0,0 +1,489 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +enum { + Data= 0x60, /* data port */ + + Status= 0x64, /* status port */ + Inready= 0x01, /* input character ready */ + Outbusy= 0x02, /* output busy */ + Sysflag= 0x04, /* system flag */ + Cmddata= 0x08, /* cmd==0, data==1 */ + Inhibit= 0x10, /* keyboard/mouse inhibited */ + Minready= 0x20, /* mouse character ready */ + Rtimeout= 0x40, /* general timeout */ + Parity= 0x80, + + Cmd= 0x64, /* command port (write only) */ + + Spec= 0x80, + + PF= Spec|0x20, /* num pad function key */ + View= Spec|0x00, /* view (shift window up) */ + KF= Spec|0x40, /* function key */ + Shift= Spec|0x60, + Break= Spec|0x61, + Ctrl= Spec|0x62, + Latin= Spec|0x63, + Caps= Spec|0x64, + Num= Spec|0x65, + No= Spec|0x7F, /* no mapping */ + + Home= KF|13, + Up= KF|14, + Pgup= KF|15, + Print= KF|16, + Left= View, + Right= View, + End= '\r', + Down= View, + Pgdown= View, + Ins= KF|20, + Del= 0x7F, +}; + +uchar kbtab[] = +{ +[0x00] No, 0x1b, '1', '2', '3', '4', '5', '6', +[0x08] '7', '8', '9', '0', '-', '=', '\b', '\t', +[0x10] 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', +[0x18] 'o', 'p', '[', ']', '\n', Ctrl, 'a', 's', +[0x20] 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', +[0x28] '\'', '`', Shift, '\\', 'z', 'x', 'c', 'v', +[0x30] 'b', 'n', 'm', ',', '.', '/', Shift, No, +[0x38] Latin, ' ', Caps, KF|1, KF|2, KF|3, KF|4, KF|5, +[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, KF|12, Home, +[0x48] No, No, No, No, No, No, No, No, +[0x50] No, No, No, No, No, No, No, KF|11, +[0x58] KF|12, No, No, No, No, No, No, No, +}; + +uchar kbtabshift[] = +{ +[0x00] No, 0x1b, '!', '@', '#', '$', '%', '^', +[0x08] '&', '*', '(', ')', '_', '+', '\b', '\t', +[0x10] 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', +[0x18] 'O', 'P', '{', '}', '\n', Ctrl, 'A', 'S', +[0x20] 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', +[0x28] '"', '~', Shift, '|', 'Z', 'X', 'C', 'V', +[0x30] 'B', 'N', 'M', '<', '>', '?', Shift, No, +[0x38] Latin, ' ', Caps, KF|1, KF|2, KF|3, KF|4, KF|5, +[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, KF|12, Home, +[0x48] No, No, No, No, No, No, No, No, +[0x50] No, No, No, No, No, No, No, KF|11, +[0x58] KF|12, No, No, No, No, No, No, No, +}; + +uchar kbtabesc1[] = +{ +[0x00] No, No, No, No, No, No, No, No, +[0x08] No, No, No, No, No, No, No, No, +[0x10] No, No, No, No, No, No, No, No, +[0x18] No, No, No, No, No, Ctrl, No, No, +[0x20] No, No, No, No, No, No, No, No, +[0x28] No, No, No, No, No, No, No, No, +[0x30] No, No, No, No, No, No, No, Print, +[0x38] Latin, No, No, No, No, No, No, No, +[0x40] No, No, No, No, No, No, Break, Home, +[0x48] Up, Pgup, No, Down, No, Right, No, End, +[0x50] Left, Pgdown, Ins, Del, No, No, No, No, +[0x58] No, No, No, No, No, No, No, No, +}; + +struct latin +{ + uchar l; + char c[2]; +}latintab[] = { + L'¡', "!!", /* spanish initial ! */ + L'¢', "c|", /* cent */ + L'¢', "c$", /* cent */ + L'£', "l$", /* pound sterling */ + L'¤', "g$", /* general currency */ + L'¥', "y$", /* yen */ + L'¥', "j$", /* yen */ + L'¦', "||", /* broken vertical bar */ + L'§', "SS", /* section symbol */ + L'¨', "\"\"", /* dieresis */ + L'©', "cr", /* copyright */ + L'©', "cO", /* copyright */ + L'ª', "sa", /* super a, feminine ordinal */ + L'«', "<<", /* left angle quotation */ + L'¬', "no", /* not sign, hooked overbar */ + L'­', "--", /* soft hyphen */ + L'®', "rg", /* registered trademark */ + L'¯', "__", /* macron */ + L'°', "s0", /* degree (sup o) */ + L'±', "+-", /* plus-minus */ + L'²', "s2", /* sup 2 */ + L'³', "s3", /* sup 3 */ + L'´', "''", /* grave accent */ + L'µ', "mu", /* mu */ + L'¶', "pg", /* paragraph (pilcrow) */ + L'·', "..", /* centered . */ + L'¸', ",,", /* cedilla */ + L'¹', "s1", /* sup 1 */ + L'º', "so", /* sup o */ + L'»', ">>", /* right angle quotation */ + L'¼', "14", /* 1/4 */ + L'½', "12", /* 1/2 */ + L'¾', "34", /* 3/4 */ + L'¿', "??", /* spanish initial ? */ + L'À', "A`", /* A grave */ + L'Á', "A'", /* A acute */ + L'Â', "A^", /* A circumflex */ + L'Ã', "A~", /* A tilde */ + L'Ä', "A\"", /* A dieresis */ + L'Ä', "A:", /* A dieresis */ + L'Å', "Ao", /* A circle */ + L'Å', "AO", /* A circle */ + L'Æ', "Ae", /* AE ligature */ + L'Æ', "AE", /* AE ligature */ + L'Ç', "C,", /* C cedilla */ + L'È', "E`", /* E grave */ + L'É', "E'", /* E acute */ + L'Ê', "E^", /* E circumflex */ + L'Ë', "E\"", /* E dieresis */ + L'Ë', "E:", /* E dieresis */ + L'Ì', "I`", /* I grave */ + L'Í', "I'", /* I acute */ + L'Î', "I^", /* I circumflex */ + L'Ï', "I\"", /* I dieresis */ + L'Ï', "I:", /* I dieresis */ + L'Ð', "D-", /* Eth */ + L'Ñ', "N~", /* N tilde */ + L'Ò', "O`", /* O grave */ + L'Ó', "O'", /* O acute */ + L'Ô', "O^", /* O circumflex */ + L'Õ', "O~", /* O tilde */ + L'Ö', "O\"", /* O dieresis */ + L'Ö', "O:", /* O dieresis */ + L'Ö', "OE", /* O dieresis */ + L'Ö', "Oe", /* O dieresis */ + L'×', "xx", /* times sign */ + L'Ø', "O/", /* O slash */ + L'Ù', "U`", /* U grave */ + L'Ú', "U'", /* U acute */ + L'Û', "U^", /* U circumflex */ + L'Ü', "U\"", /* U dieresis */ + L'Ü', "U:", /* U dieresis */ + L'Ü', "UE", /* U dieresis */ + L'Ü', "Ue", /* U dieresis */ + L'Ý', "Y'", /* Y acute */ + L'Þ', "P|", /* Thorn */ + L'Þ', "Th", /* Thorn */ + L'Þ', "TH", /* Thorn */ + L'ß', "ss", /* sharp s */ + L'à', "a`", /* a grave */ + L'á', "a'", /* a acute */ + L'â', "a^", /* a circumflex */ + L'ã', "a~", /* a tilde */ + L'ä', "a\"", /* a dieresis */ + L'ä', "a:", /* a dieresis */ + L'å', "ao", /* a circle */ + L'æ', "ae", /* ae ligature */ + L'ç', "c,", /* c cedilla */ + L'è', "e`", /* e grave */ + L'é', "e'", /* e acute */ + L'ê', "e^", /* e circumflex */ + L'ë', "e\"", /* e dieresis */ + L'ë', "e:", /* e dieresis */ + L'ì', "i`", /* i grave */ + L'í', "i'", /* i acute */ + L'î', "i^", /* i circumflex */ + L'ï', "i\"", /* i dieresis */ + L'ï', "i:", /* i dieresis */ + L'ð', "d-", /* eth */ + L'ñ', "n~", /* n tilde */ + L'ò', "o`", /* o grave */ + L'ó', "o'", /* o acute */ + L'ô', "o^", /* o circumflex */ + L'õ', "o~", /* o tilde */ + L'ö', "o\"", /* o dieresis */ + L'ö', "o:", /* o dieresis */ + L'ö', "oe", /* o dieresis */ + L'÷', "-:", /* divide sign */ + L'ø', "o/", /* o slash */ + L'ù', "u`", /* u grave */ + L'ú', "u'", /* u acute */ + L'û', "u^", /* u circumflex */ + L'ü', "u\"", /* u dieresis */ + L'ü', "u:", /* u dieresis */ + L'ü', "ue", /* u dieresis */ + L'ý', "y'", /* y acute */ + L'þ', "th", /* thorn */ + L'þ', "p|", /* thorn */ + L'ÿ', "y\"", /* y dieresis */ + L'ÿ', "y:", /* y dieresis */ + 0, 0, +}; + +enum +{ + /* controller command byte */ + Cscs1= (1<<6), /* scan code set 1 */ + Cmousedis= (1<<5), /* mouse disable */ + Ckbddis= (1<<4), /* kbd disable */ + Csf= (1<<2), /* system flag */ + Cmouseint= (1<<1), /* mouse interrupt enable */ + Ckbdint= (1<<0), /* kbd interrupt enable */ +}; + +static uchar ccc; + +int +latin1(int k1, int k2) +{ + struct latin *l; + + for(l=latintab; l->l; l++) + if(k1==l->c[0] && k2==l->c[1]) + return l->l; + return 0; +} + +/* + * wait for output no longer busy + */ +static int +outready(void) +{ + int tries; + + for(tries = 0; (inb(Status) & Outbusy); tries++){ + if(tries > 500) + return -1; + delay(2); + } + return 0; +} + +/* + * wait for input + */ +static int +inready(void) +{ + int tries; + + for(tries = 0; !(inb(Status) & Inready); tries++){ + if(tries > 500) + return -1; + delay(2); + } + return 0; +} + +/* + * ask 8042 to enable the use of address bit 20 + */ +void +i8042a20(void) +{ + outready(); + outb(Cmd, 0xD1); + outready(); + outb(Data, 0xDF); + outready(); +} + +/* + * ask 8042 to reset the machine + */ +void +i8042reset(void) +{ + int i, x; +#ifdef notdef + ushort *s = (ushort*)(KZERO|0x472); + + *s = 0x1234; /* BIOS warm-boot flag */ +#endif /* notdef */ + + outready(); + outb(Cmd, 0xFE); /* pulse reset line (means resend on AT&T machines) */ + outready(); + + /* + * Pulse it by hand (old somewhat reliable) + */ + x = 0xDF; + for(i = 0; i < 5; i++){ + x ^= 1; + outready(); + outb(Cmd, 0xD1); + outready(); + outb(Data, x); /* toggle reset */ + delay(100); + } +} + +/* + * keyboard interrupt + */ +static void +i8042intr(Ureg*, void*) +{ + int s, c; + static int esc1, esc2; + static int alt, caps, ctl, num, shift; + static int lstate, k1, k2; + int keyup; + + /* + * get status + */ + s = inb(Status); + if(!(s&Inready)) + return; + + /* + * get the character + */ + c = inb(Data); + + /* + * if it's the aux port... + */ + if(s & Minready) + return; + + /* + * e0's is the first of a 2 character sequence + */ + if(c == 0xe0){ + esc1 = 1; + return; + } else if(c == 0xe1){ + esc2 = 2; + return; + } + + keyup = c&0x80; + c &= 0x7f; + if(c > sizeof kbtab){ + c |= keyup; + if(c != 0xFF) /* these come fairly often: CAPSLOCK U Y */ + print("unknown key %ux\n", c); + return; + } + + if(esc1){ + c = kbtabesc1[c]; + esc1 = 0; + } else if(esc2){ + esc2--; + return; + } else if(shift) + c = kbtabshift[c]; + else + c = kbtab[c]; + + if(caps && c<='z' && c>='a') + c += 'A' - 'a'; + + /* + * keyup only important for shifts + */ + if(keyup){ + switch(c){ + case Latin: + alt = 0; + break; + case Shift: + shift = 0; + break; + case Ctrl: + ctl = 0; + break; + } + return; + } + + /* + * normal character + */ + if(!(c & Spec)){ + if(ctl){ + if(alt && c == Del) + warp86("\nCtrl-Alt-Del\n", 0); + c &= 0x1f; + } + switch(lstate){ + case 1: + k1 = c; + lstate = 2; + return; + case 2: + k2 = c; + lstate = 0; + c = latin1(k1, k2); + if(c == 0){ + kbdchar(k1); + c = k2; + } + /* fall through */ + default: + break; + } + } else { + switch(c){ + case Caps: + caps ^= 1; + return; + case Num: + num ^= 1; + return; + case Shift: + shift = 1; + return; + case Latin: + alt = 1; + lstate = 1; + return; + case Ctrl: + ctl = 1; + return; + } + } + kbdchar(c); +} + +static char *initfailed = "kbd init failed\n"; + +void +i8042init(void) +{ + int c; + + /* wait for a quiescent controller */ + while((c = inb(Status)) & (Outbusy | Inready)) + if(c & Inready) + inb(Data); + + /* get current controller command byte */ + outb(Cmd, 0x20); + if(inready() < 0){ + print("kbdinit: can't read ccc\n"); + ccc = 0; + } else + ccc = inb(Data); + + /* enable kbd xfers and interrupts */ + ccc &= ~Ckbddis; + ccc |= Csf | Ckbdint | Cscs1; + if(outready() < 0) + print(initfailed); + outb(Cmd, 0x60); + if(outready() < 0) + print(initfailed); + outb(Data, ccc); + if(outready() < 0) + print(initfailed); + + setvec(VectorKBD, i8042intr, 0); +} diff --git a/os/boot/pc/kfs.h b/os/boot/pc/kfs.h new file mode 100644 index 00000000..a08ad37a --- /dev/null +++ b/os/boot/pc/kfs.h @@ -0,0 +1,57 @@ +typedef struct Qid9p1 Qid9p1; +typedef struct Dentry Dentry; +typedef struct Kfsfile Kfsfile; +typedef struct Kfs Kfs; + +/* DONT TOUCH, this is the disk structure */ +struct Qid9p1 +{ + long path; + long version; +}; + +//#define NAMELEN 28 /* size of names */ +#define NDBLOCK 6 /* number of direct blocks in Dentry */ + +/* DONT TOUCH, this is the disk structure */ +struct Dentry +{ + char name[NAMELEN]; + short uid; + short gid; + ushort mode; +/* + #define DALLOC 0x8000 + #define DDIR 0x4000 + #define DAPND 0x2000 + #define DLOCK 0x1000 + #define DREAD 0x4 + #define DWRITE 0x2 + #define DEXEC 0x1 +*/ + Qid9p1 qid; + long size; + long dblock[NDBLOCK]; + long iblock; + long diblock; + long atime; + long mtime; +}; + +struct Kfsfile +{ + Dentry; + long off; +}; + +struct Kfs +{ + int RBUFSIZE; + int BUFSIZE; + int DIRPERBUF; + int INDPERBUF; + int INDPERBUF2; +}; + +extern int kfsinit(Fs*); + diff --git a/os/boot/pc/kfsboot.c b/os/boot/pc/kfsboot.c new file mode 100644 index 00000000..a99d226d --- /dev/null +++ b/os/boot/pc/kfsboot.c @@ -0,0 +1,256 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "fs.h" + +typedef struct Tag Tag; + +/* + * tags on block + */ +enum +{ + Tnone = 0, + Tsuper, /* the super block */ + Tdir, /* directory contents */ + Tind1, /* points to blocks */ + Tind2, /* points to Tind1 */ + Tfile, /* file contents */ + Tfree, /* in free list */ + Tbuck, /* cache fs bucket */ + Tvirgo, /* fake worm virgin bits */ + Tcache, /* cw cache things */ + MAXTAG +}; + +#define QPDIR 0x80000000L +#define QPNONE 0 +#define QPROOT 1 +#define QPSUPER 2 + +/* DONT TOUCH, this is the disk structure */ +struct Tag +{ + short pad; + short tag; + long path; +}; + +static int thisblock = -1; +static Fs *thisfs; +static uchar *block; + +/* + * we end up reading 2x or 3x the number of blocks we need to read. + * this is okay because we need to read so few. if it wasn't okay, we could + * have getblock return a pointer to a block, and keep a cache of the last + * three read blocks. that would get us down to the minimum. + * but this is fine. + */ +static int +getblock(Fs *fs, ulong n) +{ + if(!block) + block = malloc(16384); + + if(thisblock == n && thisfs == fs) + return 0; + thisblock = -1; + if(fs->diskseek(fs, (vlong)n*fs->kfs.RBUFSIZE) < 0) + return -1; + if(fs->diskread(fs, block, fs->kfs.RBUFSIZE) != fs->kfs.RBUFSIZE) + return -1; + thisblock = n; + thisfs = fs; + + return 1; +} + +static int +checktag(Fs *fs, uchar *block, int tag, long qpath) +{ + Tag *t; + + t = (Tag*)(block+fs->kfs.BUFSIZE); + if(t->tag != tag) + return -1; + if(qpath != QPNONE && (qpath&~QPDIR) != t->path) + return -1; + return 1; +} + +static int +getblocktag(Fs *fs, ulong n, int tag, long qpath) +{ + if(getblock(fs, n) < 0 || checktag(fs, block, tag, qpath) < 0) + return -1; + return 1; +} + +static int +readinfo(Fs *fs) +{ + fs->kfs.RBUFSIZE = 512; + if(getblock(fs, 0) < 0) + return -1; + + if(memcmp(block+256, "kfs wren device\n", 16) != 0) + return -1; + + fs->kfs.RBUFSIZE = atoi((char*)block+256+16); + if(!fs->kfs.RBUFSIZE || (fs->kfs.RBUFSIZE&(fs->kfs.RBUFSIZE-1))) + return -1; + + fs->kfs.BUFSIZE = fs->kfs.RBUFSIZE - sizeof(Tag); + fs->kfs.DIRPERBUF = fs->kfs.BUFSIZE / sizeof(Dentry); + fs->kfs.INDPERBUF = fs->kfs.BUFSIZE / sizeof(long); + fs->kfs.INDPERBUF2 = fs->kfs.INDPERBUF * fs->kfs.INDPERBUF; + + return 1; +} + +static int +readroot(Fs *fs, Dentry *d) +{ + Dentry *d2; + + if(getblocktag(fs, 2, Tdir, QPROOT) < 0) + return -1; + d2 = (Dentry*)block; + if(strcmp(d2->name, "/") != 0) + return -1; + *d = *(Dentry*)block; + return 1; +} + +static long +indfetch(Fs *fs, long addr, long off, int tag, long path) +{ + if(getblocktag(fs, addr, tag, path) < 0) + return -1; + return ((long*)block)[off]; +} + +static long +rel2abs(Fs *fs, Dentry *d, long a) +{ + long addr; + + if(a < NDBLOCK) + return d->dblock[a]; + a -= NDBLOCK; + if(a < fs->kfs.INDPERBUF){ + if(d->iblock == 0) + return 0; + addr = indfetch(fs, d->iblock, a, Tind1, d->qid.path); + if(addr == 0) + print("rel2abs indfetch 0 %s %ld\n", d->name, a); + return addr; + } + a -= fs->kfs.INDPERBUF; + if(a < fs->kfs.INDPERBUF2){ + if(d->diblock == 0) + return 0; + addr = indfetch(fs, d->diblock, a/fs->kfs.INDPERBUF, Tind2, d->qid.path); + if(addr == 0){ + print("rel2abs indfetch 0 %s %ld\n", d->name, a/fs->kfs.INDPERBUF); + return 0; + } + addr = indfetch(fs, addr, a%fs->kfs.INDPERBUF, Tind1, d->qid.path); + return addr; + } + print("rel2abs trip ind %s %ld\n", d->name, a); + return -1; +} + +static int +readdentry(Fs *fs, Dentry *d, int n, Dentry *e) +{ + long addr, m; + + m = n/fs->kfs.DIRPERBUF; + if((addr = rel2abs(fs, d, m)) <= 0) + return addr; + if(getblocktag(fs, addr, Tdir, d->qid.path) < 0) + return -1; + *e = *(Dentry*)(block+(n%fs->kfs.DIRPERBUF)*sizeof(Dentry)); + return 1; +} + +static int +getdatablock(Fs *fs, Dentry *d, long a) +{ + long addr; + + if((addr = rel2abs(fs, d, a)) == 0) + return -1; + return getblocktag(fs, addr, Tfile, QPNONE); +} + +static int +walk(Fs *fs, Dentry *d, char *name, Dentry *e) +{ + int i, n; + Dentry x; + + for(i=0;; i++){ + if((n=readdentry(fs, d, i, &x)) <= 0) + return n; + if(strcmp(x.name, name) == 0){ + *e = x; + return 1; + } + } +} + +static long +kfsread(File *f, void *va, long len) +{ + uchar *a; + long tot, off, o, n; + Fs *fs; + + a = va; + fs = f->fs; + off = f->kfs.off; + tot = 0; + while(tot < len){ + if(getdatablock(fs, &f->kfs, off/fs->kfs.BUFSIZE) < 0) + return -1; + o = off%fs->kfs.BUFSIZE; + n = fs->kfs.BUFSIZE - o; + if(n > len-tot) + n = len-tot; + memmove(a+tot, block+o, n); + off += n; + tot += n; + } + f->kfs.off = off; + return tot; +} + +static int +kfswalk(File *f, char *name) +{ + int n; + + n = walk(f->fs, &f->kfs, name, &f->kfs); + if(n < 0) + return -1; + f->kfs.off = 0; + return 1; +} + +int +kfsinit(Fs *fs) +{ + if(readinfo(fs) < 0 || readroot(fs, &fs->root.kfs) < 0) + return -1; + + fs->root.fs = fs; + fs->read = kfsread; + fs->walk = kfswalk; + return 0; +} diff --git a/os/boot/pc/l.s b/os/boot/pc/l.s new file mode 100644 index 00000000..0269e92e --- /dev/null +++ b/os/boot/pc/l.s @@ -0,0 +1,1079 @@ +#include "x16.h" +#include "mem.h" + +#define WRMSR BYTE $0x0F; BYTE $0x30 /* WRMSR, argument in AX/DX (lo/hi) */ +#define RDTSC BYTE $0x0F; BYTE $0x31 /* RDTSC, result in AX/DX (lo/hi) */ +#define RDMSR BYTE $0x0F; BYTE $0x32 /* RDMSR, result in AX/DX (lo/hi) */ + +#ifdef PXE +#define PDB 0x90000 /* temporary page tables (24KB) */ +#else +#define PDB 0x08000 +#endif PXE + +#define NoScreenBlank 1 +/*#define ResetDiscs 1*/ + +TEXT origin(SB), $0 + /* + * This part of l.s is used only in the boot kernel. + * It assumes that we are in real address mode, i.e., + * that we look like an 8086. + * + * Make sure the segments are reasonable. + * If we were started directly from the BIOS + * (i.e. no MS-DOS) then DS may not be + * right. + */ + MOVW CS, AX + MOVW AX, DS + +#ifdef NoScreenBlank + /* + * Get the current video mode. If it isn't mode 3, + * set text mode 3. + * Well, no. Windows95 won't co-operate here so we have + * to explicitly set mode 3. + */ + XORL AX, AX + MOVB $0x0F, AH + INT $0x10 /* get current video mode in AL */ + CMPB AL, $03 + JEQ sayhello +#endif /* NoScreenBlank */ + XORL AX, AX + MOVB $0x03, AL + INT $0x10 /* set video mode in AL */ + +sayhello: + LWI(hello(SB), rSI) + CALL16(biosputs(SB)) + +#ifdef ResetDiscs + XORL AX, AX /* reset disc system */ + XORL DX, DX + MOVB $0x80, DL + INT $0x13 +#endif /* ResetDiscs */ + +#ifdef DOTCOM +/* + * relocate everything to a half meg and jump there + * - looks weird because it is being assembled by a 32 bit + * assembler for a 16 bit world + * + * only b.com does this - not 9load + */ + MOVL $0,BX + INCL BX + SHLL $15,BX + MOVL BX,CX + MOVW BX,ES + MOVL $0,SI + MOVL SI,DI + CLD + REP + MOVSL + + /* + * Jump to the copied image; + * fix up the DS for the new location. + */ + FARJUMP16(0x8000, _start8000(SB)) + +TEXT _start8000(SB), $0 + MFSR(rCS, rAX) /* fix up DS, ES (0x8000) */ + MTSR(rAX, rDS) + MTSR(rAX, rES) + + /* + * If we are already in protected mode, have to get back + * to real mode before trying any privileged operations + * (like going into protected mode...). + * Try to reset with a restart vector. + */ + MFCR(rCR0, rAX) /* are we in protected mode? */ + ANDI(0x0001, rAX) + JEQ _real + + CLR(rBX) + MTSR(rBX, rES) + + LWI(0x0467, rBX) /* reset entry point */ + LWI(_start8000(SB), rAX) /* offset within segment */ + BYTE $0x26 + BYTE $0x89 + BYTE $0x07 /* MOVW AX, ES:[BX] */ + LBI(0x69, rBL) + MFSR(rCS, rAX) /* segment */ + BYTE $0x26 + BYTE $0x89 + BYTE $0x07 /* MOVW AX, ES:[BX] */ + + CLR(rDX) + OUTPORTB(0x70, 0x8F) + OUTPORTB(0x71, 0x0A) + + FARJUMP16(0xFFFF, 0x0000) /* reset */ +#endif /* DOTCOM */ + +_real: + +/* + * do things that need to be done in real mode. + * the results get written to CONFADDR (0x1200) + * in a series of <4-byte-magic-number> + * the data length is dependent on the magic number. + * + * this gets parsed by conf.c:/^readlsconf + * + * N.B. CALL16 kills rDI, so we can't call anything. + */ + LWI(0x0000, rAX) + MTSR(rAX, rES) + LWI(0x1200, rDI) + +/* + * turn off interrupts + */ + CLI + +/* + * detect APM1.2 bios support + */ + /* save DI */ + SW(rDI, rock(SB)) + + /* disconnect anyone else */ + LWI(0x5304, rAX) + LWI(0x0000, rBX) + INT $0x15 + + /* connect */ + CLC + LWI(0x5303, rAX) + LWI(0x0000, rBX) + INT $0x15 + CLI /* apm put interrupts back? */ + + JC noapm + + OPSIZE; PUSHR(rSI) + OPSIZE; PUSHR(rBX) + PUSHR(rDI) + PUSHR(rDX) + PUSHR(rCX) + PUSHR(rAX) + + /* put DI, ES back */ + LW(rock(SB), rDI) + LWI(0x0000, rAX) + MTSR(rAX, rES) + + /* + * write APM data. first four bytes are APM\0. + */ + LWI(0x5041, rAX) + STOSW + + LWI(0x004d, rAX) + STOSW + + LWI(8, rCX) +apmmove: + POPR(rAX) + STOSW + LOOP apmmove + +noapm: + +/* + * end of real mode hacks: write terminator, put ES back. + */ + LWI(0x0000, rAX) + STOSW + STOSW + + MFSR(rCS, rAX) /* fix up ES (0x8000) */ + MTSR(rAX, rES) + +/* + * goto protected mode + */ +/* MOVL tgdtptr(SB),GDTR /**/ + BYTE $0x0f + BYTE $0x01 + BYTE $0x16 + WORD $tgdtptr(SB) + + LWI(1, rAX) + /* MOV AX,MSW */ + BYTE $0x0F; BYTE $0x01; BYTE $0xF0 + +/* + * clear prefetch queue (weird code to avoid optimizations) + */ + /* JMP .+2 */ + BYTE $0xEB + BYTE $0x00 + +/* + * set all segs + */ +/* MOVW $SELECTOR(1, SELGDT, 0),AX /**/ + BYTE $0xc7 + BYTE $0xc0 + WORD $SELECTOR(1, SELGDT, 0) + MOVW AX,DS + MOVW AX,SS + MOVW AX,ES + MOVW AX,FS + MOVW AX,GS + +/* JMPFAR SELECTOR(2, SELGDT, 0):$mode32bit(SB) /**/ + BYTE $0x66 + BYTE $0xEA + LONG $mode32bit-KZERO(SB) + WORD $SELECTOR(2, SELGDT, 0) + +TEXT mode32bit(SB),$0 + /* + * make a bottom level page table page that maps the first + * 16 meg of physical memory + */ + MOVL $PDB, DI /* clear 6 pages for the tables etc. */ + XORL AX, AX + MOVL $(6*BY2PG), CX + SHRL $2, CX + + CLD + REP; STOSL + + MOVL $PDB, AX /* phys addr of temporary page table */ + MOVL $(4*1024),CX /* pte's per page */ + MOVL $((((4*1024)-1)<>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+0)(AX) + ADDL $BY2PG,BX + MOVL BX,4(AX) + MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+4)(AX) + ADDL $BY2PG,BX + MOVL BX,8(AX) + MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+8)(AX) + ADDL $BY2PG,BX + MOVL BX,12(AX) + MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+12)(AX) + + /* + * point processor to top level page & turn on paging + * + * this produces the apparently harmless "VMX|F(125):468 Dis 0x0:0x0" + * message in the VMware log. + */ + MOVL AX,CR3 + MOVL CR0,AX + ORL $0X80000000,AX + MOVL AX,CR0 + + /* + * use a jump to an absolute location to get the PC into + * KZERO. + */ + LEAL tokzero(SB),AX + JMP* AX + +/* + * When we load 9load from DOS, the bootstrap jumps + * to the instruction right after `JUMP', which gets + * us into kzero. + * + * The name prevents it from being optimized away. + */ +TEXT jumplabel(SB), $0 + BYTE $'J'; BYTE $'U'; BYTE $'M'; BYTE $'P' + + LEAL tokzero(SB),AX + JMP* AX + +TEXT tokzero(SB),$0 + /* + * Clear BSS + */ + LEAL edata(SB),SI + MOVL SI,DI + ADDL $4,DI + MOVL $0,AX + MOVL AX,(SI) + LEAL end(SB),CX + SUBL DI,CX + SHRL $2,CX + CLD + REP + MOVSL + + /* + * stack and mach + */ + MOVL $mach0(SB),SP + MOVL SP,m(SB) + MOVL $0,0(SP) + ADDL $(MACHSIZE-4),SP /* start stack above machine struct */ + + CALL main(SB) + +loop: + JMP loop + +GLOBL mach0+0(SB), $MACHSIZE +GLOBL m(SB), $4 + +/* + * gdt to get us to 32-bit/segmented/unpaged mode + */ +TEXT tgdt(SB),$0 + + /* null descriptor */ + LONG $0 + LONG $0 + + /* data segment descriptor for 4 gigabytes (PL 0) */ + LONG $(0xFFFF) + LONG $(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW) + + /* exec segment descriptor for 4 gigabytes (PL 0) */ + LONG $(0xFFFF) + LONG $(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR) + + /* exec segment descriptor for 4 gigabytes (PL 0) 16-bit */ + LONG $(0xFFFF) + LONG $(SEGG|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR) + +/* + * pointer to initial gdt + */ +TEXT tgdtptr(SB),$0 + WORD $(4*8) + LONG $tgdt-KZERO(SB) + +/* + * Output a string to the display. + * String argument is in rSI. + */ +TEXT biosputs(SB), $0 + PUSHA + CLR(rBX) +_BIOSputs: + LODSB + ORB(rAL, rAL) + JEQ _BIOSputsret + + LBI(0x0E, rAH) + BIOSCALL(0x10) + JMP _BIOSputs + +_BIOSputsret: + POPA + RET + +/* + * input a byte + */ +TEXT inb(SB),$0 + + MOVL p+0(FP),DX + XORL AX,AX + INB + RET + +/* + * input a short from a port + */ +TEXT ins(SB), $0 + + MOVL p+0(FP), DX + XORL AX, AX + OPSIZE; INL + RET + +/* + * input a long from a port + */ +TEXT inl(SB), $0 + + MOVL p+0(FP), DX + XORL AX, AX + INL + RET + +/* + * output a byte + */ +TEXT outb(SB),$0 + + MOVL p+0(FP),DX + MOVL b+4(FP),AX + OUTB + RET + +/* + * output a short to a port + */ +TEXT outs(SB), $0 + MOVL p+0(FP), DX + MOVL s+4(FP), AX + OPSIZE; OUTL + RET + +/* + * output a long to a port + */ +TEXT outl(SB), $0 + MOVL p+0(FP), DX + MOVL s+4(FP), AX + OUTL + RET + +/* + * input a string of bytes from a port + */ +TEXT insb(SB),$0 + + MOVL p+0(FP),DX + MOVL a+4(FP),DI + MOVL c+8(FP),CX + CLD; REP; INSB + RET + +/* + * input a string of shorts from a port + */ +TEXT inss(SB),$0 + MOVL p+0(FP),DX + MOVL a+4(FP),DI + MOVL c+8(FP),CX + CLD + REP; OPSIZE; INSL + RET + +/* + * output a string of bytes to a port + */ +TEXT outsb(SB),$0 + + MOVL p+0(FP),DX + MOVL a+4(FP),SI + MOVL c+8(FP),CX + CLD; REP; OUTSB + RET + +/* + * output a string of shorts to a port + */ +TEXT outss(SB),$0 + MOVL p+0(FP),DX + MOVL a+4(FP),SI + MOVL c+8(FP),CX + CLD + REP; OPSIZE; OUTSL + RET + +/* + * input a string of longs from a port + */ +TEXT insl(SB),$0 + + MOVL p+0(FP),DX + MOVL a+4(FP),DI + MOVL c+8(FP),CX + CLD; REP; INSL + RET + +/* + * output a string of longs to a port + */ +TEXT outsl(SB),$0 + + MOVL p+0(FP),DX + MOVL a+4(FP),SI + MOVL c+8(FP),CX + CLD; REP; OUTSL + RET + +/* + * routines to load/read various system registers + */ +GLOBL idtptr(SB),$6 +TEXT putidt(SB),$0 /* interrupt descriptor table */ + MOVL t+0(FP),AX + MOVL AX,idtptr+2(SB) + MOVL l+4(FP),AX + MOVW AX,idtptr(SB) + MOVL idtptr(SB),IDTR + RET + +TEXT putcr3(SB),$0 /* top level page table pointer */ + MOVL t+0(FP),AX + MOVL AX,CR3 + RET + +TEXT getcr0(SB),$0 /* coprocessor bits */ + MOVL CR0,AX + RET + +TEXT getcr2(SB),$0 /* fault address */ + MOVL CR2,AX + RET + +TEXT getcr3(SB),$0 /* page directory base */ + MOVL CR3,AX + RET + +TEXT getcr4(SB), $0 /* CR4 - extensions */ + MOVL CR4, AX + RET + +TEXT _cycles(SB), $0 /* time stamp counter */ + RDTSC + MOVL vlong+0(FP), CX /* &vlong */ + MOVL AX, 0(CX) /* lo */ + MOVL DX, 4(CX) /* hi */ + RET + +TEXT rdmsr(SB), $0 /* model-specific register */ + MOVL index+0(FP), CX + RDMSR + MOVL vlong+4(FP), CX /* &vlong */ + MOVL AX, 0(CX) /* lo */ + MOVL DX, 4(CX) /* hi */ + RET + +TEXT wrmsr(SB), $0 + MOVL index+0(FP), CX + MOVL lo+4(FP), AX + MOVL hi+8(FP), DX + WRMSR + RET + +TEXT mb386(SB), $0 + POPL AX /* return PC */ + PUSHFL + PUSHL CS + PUSHL AX + IRETL + +/* + * special traps + */ +TEXT intr0(SB),$0 + PUSHL $0 + PUSHL $0 + JMP intrcommon +TEXT intr1(SB),$0 + PUSHL $0 + PUSHL $1 + JMP intrcommon +TEXT intr2(SB),$0 + PUSHL $0 + PUSHL $2 + JMP intrcommon +TEXT intr3(SB),$0 + PUSHL $0 + PUSHL $3 + JMP intrcommon +TEXT intr4(SB),$0 + PUSHL $0 + PUSHL $4 + JMP intrcommon +TEXT intr5(SB),$0 + PUSHL $0 + PUSHL $5 + JMP intrcommon +TEXT intr6(SB),$0 + PUSHL $0 + PUSHL $6 + JMP intrcommon +TEXT intr7(SB),$0 + PUSHL $0 + PUSHL $7 + JMP intrcommon +TEXT intr8(SB),$0 + PUSHL $8 + JMP intrcommon +TEXT intr9(SB),$0 + PUSHL $0 + PUSHL $9 + JMP intrcommon +TEXT intr10(SB),$0 + PUSHL $10 + JMP intrcommon +TEXT intr11(SB),$0 + PUSHL $11 + JMP intrcommon +TEXT intr12(SB),$0 + PUSHL $12 + JMP intrcommon +TEXT intr13(SB),$0 + PUSHL $13 + JMP intrcommon +TEXT intr14(SB),$0 + PUSHL $14 + JMP intrcommon +TEXT intr15(SB),$0 + PUSHL $0 + PUSHL $15 + JMP intrcommon +TEXT intr16(SB),$0 + PUSHL $0 + PUSHL $16 + JMP intrcommon +TEXT intr24(SB),$0 + PUSHL $0 + PUSHL $24 + JMP intrcommon +TEXT intr25(SB),$0 + PUSHL $0 + PUSHL $25 + JMP intrcommon +TEXT intr26(SB),$0 + PUSHL $0 + PUSHL $26 + JMP intrcommon +TEXT intr27(SB),$0 + PUSHL $0 + PUSHL $27 + JMP intrcommon +TEXT intr28(SB),$0 + PUSHL $0 + PUSHL $28 + JMP intrcommon +TEXT intr29(SB),$0 + PUSHL $0 + PUSHL $29 + JMP intrcommon +TEXT intr30(SB),$0 + PUSHL $0 + PUSHL $30 + JMP intrcommon +TEXT intr31(SB),$0 + PUSHL $0 + PUSHL $31 + JMP intrcommon +TEXT intr32(SB),$0 + PUSHL $0 + PUSHL $32 + JMP intrcommon +TEXT intr33(SB),$0 + PUSHL $0 + PUSHL $33 + JMP intrcommon +TEXT intr34(SB),$0 + PUSHL $0 + PUSHL $34 + JMP intrcommon +TEXT intr35(SB),$0 + PUSHL $0 + PUSHL $35 + JMP intrcommon +TEXT intr36(SB),$0 + PUSHL $0 + PUSHL $36 + JMP intrcommon +TEXT intr37(SB),$0 + PUSHL $0 + PUSHL $37 + JMP intrcommon +TEXT intr38(SB),$0 + PUSHL $0 + PUSHL $38 + JMP intrcommon +TEXT intr39(SB),$0 + PUSHL $0 + PUSHL $39 + JMP intrcommon +TEXT intr64(SB),$0 + PUSHL $0 + PUSHL $64 + JMP intrcommon +TEXT intrbad(SB),$0 + PUSHL $0 + PUSHL $0x1ff + JMP intrcommon + +intrcommon: + PUSHL DS + PUSHL ES + PUSHL FS + PUSHL GS + PUSHAL + MOVL $(KDSEL),AX + MOVW AX,DS + MOVW AX,ES + LEAL 0(SP),AX + PUSHL AX + CALL trap(SB) + POPL AX + POPAL + POPL GS + POPL FS + POPL ES + POPL DS + ADDL $8,SP /* error code and trap type */ + IRETL + + +/* + * interrupt level is interrupts on or off + */ +TEXT spllo(SB),$0 + PUSHFL + POPL AX + STI + RET + +TEXT splhi(SB),$0 + PUSHFL + POPL AX + CLI + RET + +TEXT splx(SB),$0 + MOVL s+0(FP),AX + PUSHL AX + POPFL + RET + +/* + * do nothing whatsoever till interrupt happens + */ +TEXT idle(SB),$0 + HLT + RET + +/* + * Try to determine the CPU type which requires fiddling with EFLAGS. + * If the Id bit can be toggled then the CPUID instruciton can be used + * to determine CPU identity and features. First have to check if it's + * a 386 (Ac bit can't be set). If it's not a 386 and the Id bit can't be + * toggled then it's an older 486 of some kind. + * + * cpuid(id[], &ax, &dx); + */ +#define CPUID BYTE $0x0F; BYTE $0xA2 /* CPUID, argument in AX */ +TEXT cpuid(SB), $0 + MOVL $0x240000, AX + PUSHL AX + POPFL /* set Id|Ac */ + + PUSHFL + POPL BX /* retrieve value */ + + MOVL $0, AX + PUSHL AX + POPFL /* clear Id|Ac, EFLAGS initialised */ + + PUSHFL + POPL AX /* retrieve value */ + XORL BX, AX + TESTL $0x040000, AX /* Ac */ + JZ _cpu386 /* can't set this bit on 386 */ + TESTL $0x200000, AX /* Id */ + JZ _cpu486 /* can't toggle this bit on some 486 */ + + MOVL $0, AX + CPUID + MOVL id+0(FP), BP + MOVL BX, 0(BP) /* "Genu" "Auth" "Cyri" */ + MOVL DX, 4(BP) /* "ineI" "enti" "xIns" */ + MOVL CX, 8(BP) /* "ntel" "cAMD" "tead" */ + + MOVL $1, AX + CPUID + JMP _cpuid + +_cpu486: + MOVL $0x400, AX + MOVL $0, DX + JMP _cpuid + +_cpu386: + MOVL $0x300, AX + MOVL $0, DX + +_cpuid: + MOVL ax+4(FP), BP + MOVL AX, 0(BP) + MOVL dx+8(FP), BP + MOVL DX, 0(BP) + RET + + +/* + * basic timing loop to determine CPU frequency + */ +TEXT aamloop(SB),$0 + + MOVL c+0(FP),CX +aaml1: + AAM + LOOP aaml1 + RET + +TEXT hello(SB), $0 + BYTE $'P'; BYTE $'l'; BYTE $'a'; BYTE $'n'; + BYTE $' '; BYTE $'9'; BYTE $' '; BYTE $'f'; + BYTE $'r'; BYTE $'o'; BYTE $'m'; BYTE $' '; + BYTE $'B'; BYTE $'e'; BYTE $'l'; BYTE $'l'; + BYTE $' '; BYTE $'L'; BYTE $'a'; BYTE $'b'; + BYTE $'s'; + BYTE $'\r'; + BYTE $'\n'; + BYTE $'\z'; + +TEXT rock(SB), $0 + BYTE $0; BYTE $0; BYTE $0; BYTE $0; + +GLOBL pxe(SB), $4 +#ifdef PXE +DATA pxe+0(SB)/4, $1 +#else +DATA pxe+0(SB)/4, $0 +#endif /* PXE */ + +/* + * Save registers. + */ +TEXT saveregs(SB), $0 + /* appease 8l */ + SUBL $32, SP + POPL AX + POPL AX + POPL AX + POPL AX + POPL AX + POPL AX + POPL AX + POPL AX + + PUSHL AX + PUSHL BX + PUSHL CX + PUSHL DX + PUSHL BP + PUSHL DI + PUSHL SI + PUSHFL + + XCHGL 32(SP), AX /* swap return PC and saved flags */ + XCHGL 0(SP), AX + XCHGL 32(SP), AX + RET + +TEXT restoreregs(SB), $0 + /* appease 8l */ + PUSHL AX + PUSHL AX + PUSHL AX + PUSHL AX + PUSHL AX + PUSHL AX + PUSHL AX + PUSHL AX + ADDL $32, SP + + XCHGL 32(SP), AX /* swap return PC and saved flags */ + XCHGL 0(SP), AX + XCHGL 32(SP), AX + + POPFL + POPL SI + POPL DI + POPL BP + POPL DX + POPL CX + POPL BX + POPL AX + RET + +/* + * Assumed to be in protected mode at time of call. + * Switch to real mode, execute an interrupt, and + * then switch back to protected mode. + * + * Assumes: + * + * - no device interrupts are going to come in + * - 0-16MB is identity mapped in page tables + * - can use code segment 0x1000 in real mode + * to get at l.s code + */ +TEXT realmodeidtptr(SB), $0 + WORD $(4*256-1) + LONG $0 + +TEXT realmode0(SB), $0 + CALL saveregs(SB) + + /* switch to low code address */ + LEAL physcode-KZERO(SB), AX + JMP *AX + +TEXT physcode(SB), $0 + + /* switch to low stack */ + MOVL SP, AX + MOVL $0x7C00, SP + PUSHL AX + + /* load IDT with real-mode version; GDT already fine */ + MOVL realmodeidtptr(SB), IDTR + + /* edit INT $0x00 instruction below */ + MOVL realmodeintr(SB), AX + MOVB AX, realmodeintrinst+1(SB) + + /* disable paging */ + MOVL CR0, AX + ANDL $0x7FFFFFFF, AX + MOVL AX, CR0 + /* JMP .+2 to clear prefetch queue*/ + BYTE $0xEB; BYTE $0x00 + + /* jump to 16-bit code segment */ +/* JMPFAR SELECTOR(3, SELGDT, 0):$again16bit(SB) /**/ + BYTE $0xEA + LONG $again16bit-KZERO(SB) + WORD $SELECTOR(3, SELGDT, 0) + +TEXT again16bit(SB), $0 + /* + * Now in 16-bit compatibility mode. + * These are 32-bit instructions being interpreted + * as 16-bit instructions. I'm being lazy and + * not using the macros because I know when + * the 16- and 32-bit instructions look the same + * or close enough. + */ + + /* disable protected mode and jump to real mode cs */ + OPSIZE; MOVL CR0, AX + OPSIZE; XORL BX, BX + OPSIZE; INCL BX + OPSIZE; XORL BX, AX + OPSIZE; MOVL AX, CR0 + + /* JMPFAR 0x1000:now16real */ + BYTE $0xEA + WORD $now16real-KZERO(SB) + WORD $0x1000 + +TEXT now16real(SB), $0 + /* copy the registers for the bios call */ + LWI(0x1000, rAX) + MOVW AX,SS + LWI(realmoderegs(SB), rBP) + + /* offsets are in Ureg */ + LXW(44, xBP, rAX) + MOVW AX, DS + LXW(40, xBP, rAX) + MOVW AX, ES + + OPSIZE; LXW(0, xBP, rDI) + OPSIZE; LXW(4, xBP, rSI) + OPSIZE; LXW(16, xBP, rBX) + OPSIZE; LXW(20, xBP, rDX) + OPSIZE; LXW(24, xBP, rCX) + OPSIZE; LXW(28, xBP, rAX) + + CLC + +TEXT realmodeintrinst(SB), $0 + INT $0x00 + + /* save the registers after the call */ + + LWI(0x7bfc, rSP) + OPSIZE; PUSHFL + OPSIZE; PUSHL AX + + LWI(0x1000, rAX) + MOVW AX,SS + LWI(realmoderegs(SB), rBP) + + OPSIZE; SXW(rDI, 0, xBP) + OPSIZE; SXW(rSI, 4, xBP) + OPSIZE; SXW(rBX, 16, xBP) + OPSIZE; SXW(rDX, 20, xBP) + OPSIZE; SXW(rCX, 24, xBP) + OPSIZE; POPL AX + OPSIZE; SXW(rAX, 28, xBP) + + MOVW DS, AX + OPSIZE; SXW(rAX, 44, xBP) + MOVW ES, AX + OPSIZE; SXW(rAX, 40, xBP) + + OPSIZE; POPL AX + OPSIZE; SXW(rAX, 64, xBP) /* flags */ + + /* re-enter protected mode and jump to 32-bit code */ + OPSIZE; MOVL $1, AX + OPSIZE; MOVL AX, CR0 + +/* JMPFAR SELECTOR(2, SELGDT, 0):$again32bit(SB) /**/ + OPSIZE + BYTE $0xEA + LONG $again32bit-KZERO(SB) + WORD $SELECTOR(2, SELGDT, 0) + +TEXT again32bit(SB), $0 + MOVW $SELECTOR(1, SELGDT, 0),AX + MOVW AX,DS + MOVW AX,SS + MOVW AX,ES + MOVW AX,FS + MOVW AX,GS + + /* enable paging and jump to kzero-address code */ + MOVL CR0, AX + ORL $0x80000000, AX + MOVL AX, CR0 + LEAL again32kzero(SB), AX + JMP* AX + +TEXT again32kzero(SB), $0 + /* breathe a sigh of relief - back in 32-bit protected mode */ + + /* switch to old stack */ + PUSHL AX /* match popl below for 8l */ + MOVL $0x7BFC, SP + POPL SP + + /* restore idt */ + MOVL idtptr(SB),IDTR + + CALL restoreregs(SB) + RET + +TEXT realmoderegs(SB), $0 + LONG $0; LONG $0; LONG $0; LONG $0 + LONG $0; LONG $0; LONG $0; LONG $0 + LONG $0; LONG $0; LONG $0; LONG $0 + LONG $0; LONG $0; LONG $0; LONG $0 + LONG $0; LONG $0; LONG $0; LONG $0 + +TEXT realmodeintr(SB), $0 + LONG $0 + diff --git a/os/boot/pc/lib.h b/os/boot/pc/lib.h new file mode 100644 index 00000000..a011b5e4 --- /dev/null +++ b/os/boot/pc/lib.h @@ -0,0 +1,102 @@ +/* + * functions (possibly) linked in, complete, from libc. + */ + +/* + * mem routines + */ +extern void* memccpy(void*, void*, int, long); +extern void* memset(void*, int, long); +extern int memcmp(void*, void*, long); +extern void* memmove(void*, void*, long); +extern void* memchr(void*, int, long); + +/* + * string routines + */ +extern char* strcat(char*, char*); +extern char* strchr(char*, char); +extern int strcmp(char*, char*); +extern char* strcpy(char*, char*); +extern char* strncat(char*, char*, long); +extern char* strncpy(char*, char*, long); +extern int strncmp(char*, char*, long); +extern long strlen(char*); +extern char* strrchr(char*, char); +extern char* strstr(char*, char*); + + +/* + * print routines + */ +typedef struct Fmt Fmt; +typedef int (*Fmts)(Fmt*); +struct Fmt{ + uchar runes; /* output buffer is runes or chars? */ + void *start; /* of buffer */ + void *to; /* current place in the buffer */ + void *stop; /* end of the buffer; overwritten if flush fails */ + int (*flush)(Fmt *); /* called when to == stop */ + void *farg; /* to make flush a closure */ + int nfmt; /* num chars formatted so far */ + va_list args; /* args passed to dofmt */ + int r; /* % format Rune */ + int width; + int prec; + ulong flags; +}; +extern int print(char*, ...); +extern char* vseprint(char*, char*, char*, va_list); +extern int sprint(char*, char*, ...); +extern int snprint(char*, int, char*, ...); +extern int fmtinstall(int, int (*)(Fmt*)); + +#pragma varargck argpos fmtprint 2 +#pragma varargck argpos print 1 +#pragma varargck argpos seprint 3 +#pragma varargck argpos snprint 3 +#pragma varargck argpos sprint 2 +#pragma varargck type "H" void* + +#pragma varargck type "lld" vlong +#pragma varargck type "llx" vlong +#pragma varargck type "lld" uvlong +#pragma varargck type "llx" uvlong +#pragma varargck type "ld" long +#pragma varargck type "lx" long +#pragma varargck type "ld" ulong +#pragma varargck type "lx" ulong +#pragma varargck type "d" int +#pragma varargck type "x" int +#pragma varargck type "c" int +#pragma varargck type "C" int +#pragma varargck type "d" uint +#pragma varargck type "x" uint +#pragma varargck type "c" uint +#pragma varargck type "C" uint +#pragma varargck type "f" double +#pragma varargck type "e" double +#pragma varargck type "g" double +#pragma varargck type "s" char* +#pragma varargck type "q" char* +#pragma varargck type "S" Rune* +#pragma varargck type "Q" Rune* +#pragma varargck type "r" void +#pragma varargck type "%" void +#pragma varargck type "|" int +#pragma varargck type "p" void* +#pragma varargck type "lux" void* +#pragma varargck type "E" uchar* + +#define PRINTSIZE 256 + +/* + * one-of-a-kind + */ +extern int atoi(char*); +extern ulong getcallerpc(void*); +extern long strtol(char*, char**, int); +extern ulong strtoul(char*, char**, int); +extern long end; + +#define NAMELEN 28 diff --git a/os/boot/pc/load.c b/os/boot/pc/load.c new file mode 100644 index 00000000..575c1205 --- /dev/null +++ b/os/boot/pc/load.c @@ -0,0 +1,563 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "fs.h" + +static char *diskparts[] = { "dos", "9fat", "fs", "data", "cdboot", 0 }; +static char *etherparts[] = { "*", 0 }; + +static char *diskinis[] = { + "plan9/plan9.ini", + "plan9.ini", + 0 +}; +static char *etherinis[] = { + "/cfg/pxe/%E", + 0 +}; + +Type types[] = { + { Tfloppy, + Fini|Ffs, + floppyinit, floppyinitdev, + floppygetfspart, 0, floppyboot, + floppyprintdevs, + diskparts, + diskinis, + }, + { Tcd, + Fini|Ffs, + cdinit, sdinitdev, + sdgetfspart, sdaddconf, sdboot, + sdprintdevs, + diskparts, + diskinis, + }, + { Tether, + Fini|Fbootp, + etherinit, etherinitdev, + pxegetfspart, 0, bootpboot, + etherprintdevs, + etherparts, + etherinis, + }, + { Tsd, + Fini|Ffs, + sdinit, sdinitdev, + sdgetfspart, sdaddconf, sdboot, + sdprintdevs, + diskparts, + diskinis, + }, + { Tnil, + 0, + nil, nil, nil, nil, nil, nil, + nil, + nil, + 0, + nil, + }, +}; + +#include "sd.h" + +extern SDifc sdataifc; + +#ifdef NOSCSI + +SDifc* sdifc[] = { + &sdataifc, + nil, +}; + +#else + +extern SDifc sdmylexifc; +extern SDifc sd53c8xxifc; +SDifc* sdifc[] = { + &sdataifc, + &sdmylexifc, + &sd53c8xxifc, + nil, +}; + +#endif NOSCSI + +typedef struct Mode Mode; + +enum { + Maxdev = 7, + Dany = -1, + Nmedia = 16, + Nini = 10, +}; + +enum { /* mode */ + Mauto = 0x00, + Mlocal = 0x01, + Manual = 0x02, + NMode = 0x03, +}; + +typedef struct Medium Medium; +struct Medium { + Type* type; + int flag; + int dev; + char name[NAMELEN]; + + Fs *inifs; + char *part; + char *ini; + + Medium* next; +}; + +typedef struct Mode { + char* name; + int mode; +} Mode; + +static Medium media[Nmedia]; +static Medium *curmedium = media; + +static Mode modes[NMode+1] = { + [Mauto] { "auto", Mauto, }, + [Mlocal] { "local", Mlocal, }, + [Manual] { "manual", Manual, }, +}; + +char **ini; + +int scsi0port; +char *defaultpartition; +int iniread; + +static Medium* +parse(char *line, char **file) +{ + char *p; + Type *tp; + Medium *mp; + + if(p = strchr(line, '!')) { + *p++ = 0; + *file = p; + } else + *file = ""; + + for(tp = types; tp->type != Tnil; tp++) + for(mp = tp->media; mp; mp = mp->next) + if(strcmp(mp->name, line) == 0) + return mp; + if(p) + *--p = '!'; + return nil; +} + +static int +boot(Medium *mp, char *file) +{ + Type *tp; + Medium *xmp; + static int didaddconf; + Boot b; + + memset(&b, 0, sizeof b); + b.state = INITKERNEL; + + if(didaddconf == 0) { + didaddconf = 1; + for(tp = types; tp->type != Tnil; tp++) + if(tp->addconf) + for(xmp = tp->media; xmp; xmp = xmp->next) + (*tp->addconf)(xmp->dev); + } + + sprint(BOOTLINE, "%s!%s", mp->name, file); + return (*mp->type->boot)(mp->dev, file, &b); +} + +static Medium* +allocm(Type *tp) +{ + Medium **l; + + if(curmedium >= &media[Nmedia]) + return 0; + + for(l = &tp->media; *l; l = &(*l)->next) + ; + *l = curmedium++; + return *l; +} + +Medium* +probe(int type, int flag, int dev) +{ + Type *tp; + int i; + Medium *mp; + File f; + Fs *fs; + char **partp; + + for(tp = types; tp->type != Tnil; tp++){ + if(type != Tany && type != tp->type) + continue; + + if(flag != Fnone){ + for(mp = tp->media; mp; mp = mp->next){ + if((flag & mp->flag) && (dev == Dany || dev == mp->dev)) + return mp; + } + } + + if((tp->flag & Fprobe) == 0){ + tp->flag |= Fprobe; + tp->mask = (*tp->init)(); + } + + for(i = 0; tp->mask; i++){ + if((tp->mask & (1<mask &= ~(1<dev = i; + mp->flag = tp->flag; + mp->type = tp; + (*tp->initdev)(i, mp->name); + + if(mp->flag & Fini){ + mp->flag &= ~Fini; + for(partp = tp->parts; *partp; partp++){ + if((fs = (*tp->getfspart)(i, *partp, 0)) == nil) + continue; + + for(ini = tp->inis; *ini; ini++){ + if(fswalk(fs, *ini, &f) > 0){ + mp->inifs = fs; + mp->part = *partp; + mp->ini = f.path; + mp->flag |= Fini; + goto Break2; + } + } + } + } + Break2: + if((flag & mp->flag) && (dev == Dany || dev == i)) + return mp; + } + } + + return 0; +} + +void +main(void) +{ + Medium *mp; + int flag, i, mode, tried; + char def[2*NAMELEN], line[80], *p, *file; + Type *tp; + + i8042a20(); + memset(m, 0, sizeof(Mach)); + trapinit(); + clockinit(); + alarminit(); + meminit(0); + spllo(); + kbdinit(); + + if((ulong)&end > (KZERO|(640*1024))) + panic("i'm too big\n"); + + readlsconf(); + for(tp = types; tp->type != Tnil; tp++){ + //if(tp->type == Tether) + // continue; + if((mp = probe(tp->type, Fini, Dany)) && (mp->flag & Fini)){ + print("using %s!%s!%s\n", mp->name, mp->part, mp->ini); + iniread = !dotini(mp->inifs); + break; + } + } + apminit(); + + if((p = getconf("console")) != nil) + consinit(p, getconf("baud")); + devpccardlink(); + devi82365link(); + + /* + * Even after we find the ini file, we keep probing disks, + * because we have to collect the partition tables and + * have boot devices for parse. + */ + probe(Tany, Fnone, Dany); + tried = 0; + mode = Mauto; + + p = getconf("bootfile"); + + if(p != 0) { + mode = Manual; + for(i = 0; i < NMode; i++){ + if(strcmp(p, modes[i].name) == 0){ + mode = modes[i].mode; + goto done; + } + } + if((mp = parse(p, &file)) == nil) { + print("Unknown boot device: %s\n", p); + goto done; + } + tried = boot(mp, file); + } +done: + if(tried == 0 && mode != Manual){ + flag = Fany; + if(mode == Mlocal) + flag &= ~Fbootp; + if((mp = probe(Tany, flag, Dany)) && mp->type->type != Tfloppy) + boot(mp, ""); + } + + def[0] = 0; + probe(Tany, Fnone, Dany); + if(p = getconf("bootdef")) + strcpy(def, p); + + flag = 0; + for(tp = types; tp->type != Tnil; tp++){ + for(mp = tp->media; mp; mp = mp->next){ + if(flag == 0){ + flag = 1; + print("Boot devices:"); + } + (*tp->printdevs)(mp->dev); + } + } + if(flag) + print("\n"); + + for(;;){ + if(getstr("boot from", line, sizeof(line), def, (mode != Manual)*15) >= 0) + if(mp = parse(line, &file)) + boot(mp, file); + def[0] = 0; + } +} + +int +getfields(char *lp, char **fields, int n, char sep) +{ + int i; + + for(i = 0; lp && *lp && i < n; i++){ + while(*lp == sep) + *lp++ = 0; + if(*lp == 0) + break; + fields[i] = lp; + while(*lp && *lp != sep){ + if(*lp == '\\' && *(lp+1) == '\n') + *lp++ = ' '; + lp++; + } + } + return i; +} + +int +cistrcmp(char *a, char *b) +{ + int ac, bc; + + for(;;){ + ac = *a++; + bc = *b++; + + if(ac >= 'A' && ac <= 'Z') + ac = 'a' + (ac - 'A'); + if(bc >= 'A' && bc <= 'Z') + bc = 'a' + (bc - 'A'); + ac -= bc; + if(ac) + return ac; + if(bc == 0) + break; + } + return 0; +} + +int +cistrncmp(char *a, char *b, int n) +{ + unsigned ac, bc; + + while(n > 0){ + ac = *a++; + bc = *b++; + n--; + + if(ac >= 'A' && ac <= 'Z') + ac = 'a' + (ac - 'A'); + if(bc >= 'A' && bc <= 'Z') + bc = 'a' + (bc - 'A'); + + ac -= bc; + if(ac) + return ac; + if(bc == 0) + break; + } + + return 0; +} + +#define PSTART (12*1024*1024) +#define PEND (16*1024*1024) + +ulong palloc = PSTART; + +void* +ialloc(ulong n, int align) +{ + ulong p; + int a; + + p = palloc; + if(align <= 0) + align = 4; + if(a = n % align) + n += align - a; + if(a = p % align) + p += align - a; + + + palloc = p+n; + if(palloc > PEND) + panic("ialloc(%lud, %d) called from 0x%lux\n", + n, align, getcallerpc(&n)); + return memset((void*)(p|KZERO), 0, n); +} + +void* +xspanalloc(ulong size, int align, ulong span) +{ + ulong a, v; + + if((palloc + (size+align+span)) > PEND) + panic("xspanalloc(%lud, %d, 0x%lux) called from 0x%lux\n", + size, align, span, getcallerpc(&size)); + + a = (ulong)ialloc(size+align+span, 0); + + if(span > 2) + v = (a + span) & ~(span-1); + else + v = a; + + if(align > 1) + v = (v + align) & ~(align-1); + + return (void*)v; +} + +static Block *allocbp; + +Block* +allocb(int size) +{ + Block *bp, **lbp; + ulong addr; + + lbp = &allocbp; + for(bp = *lbp; bp; bp = bp->next){ + if((bp->lim - bp->base) >= size){ + *lbp = bp->next; + break; + } + lbp = &bp->next; + } + if(bp == 0){ + if((palloc + (sizeof(Block)+size+64)) > PEND) + panic("allocb(%d) called from 0x%lux\n", + size, getcallerpc(&size)); + bp = ialloc(sizeof(Block)+size+64, 0); + addr = (ulong)bp; + addr = ROUNDUP(addr + sizeof(Block), 8); + bp->base = (uchar*)addr; + bp->lim = ((uchar*)bp) + sizeof(Block)+size+64; + } + + if(bp->flag) + panic("allocb reuse\n"); + + bp->rp = bp->base; + bp->wp = bp->rp; + bp->next = 0; + bp->flag = 1; + + return bp; +} + +void +freeb(Block* bp) +{ + bp->next = allocbp; + allocbp = bp; + + bp->flag = 0; +} + +enum { + Paddr= 0x70, /* address port */ + Pdata= 0x71, /* data port */ +}; + +uchar +nvramread(int offset) +{ + outb(Paddr, offset); + return inb(Pdata); +} + +void (*etherdetach)(void); +void (*floppydetach)(void); +void (*sddetach)(void); + +void +warp9(ulong entry) +{ + if(etherdetach) + etherdetach(); + if(floppydetach) + floppydetach(); + if(sddetach) + sddetach(); + + consdrain(); + + splhi(); + trapdisable(); + + /* + * This is where to push things on the stack to + * boot *BSD systems, e.g. + (*(void(*)(void*, void*, void*, void*, ulong, ulong))(PADDR(entry)))(0, 0, 0, 0, 8196, 640); + * will enable NetBSD boot (the real memory size needs to + * go in the 5th argument). + */ + (*(void(*)(void))(PADDR(entry)))(); +} diff --git a/os/boot/pc/mbr.s b/os/boot/pc/mbr.s new file mode 100644 index 00000000..fb299584 --- /dev/null +++ b/os/boot/pc/mbr.s @@ -0,0 +1,259 @@ +/* + * Hard disc boot block. Loaded at 0x7C00, relocates to 0x0600: + * 8a mbr.s; 8l -o mbr -l -H3 -T0x0600 mbr.8 + */ +#include "x16.h" +#include "mem.h" + +/*#define FLOPPY 1 /* test on a floppy */ +#define TRACE(C) PUSHA;\ + CLR(rBX);\ + MOVB $C, AL;\ + LBI(0x0E, rAH);\ + BIOSCALL(0x10);\ + POPA + +/* + * We keep data on the stack, indexed by BP. + */ +#define Xdap 0x00 /* disc address packet */ +#define Xtable 0x10 /* partition table entry */ +#define Xdrive 0x12 /* starting disc */ +#define Xtotal 0x14 /* sum of allocated data above */ + +/* + * Start: loaded at 0000:7C00, relocate to 0000:0600. + * Boot drive is in rDL. + */ +TEXT _start(SB), $0 + CLI + CLR(rAX) + MTSR(rAX, rSS) /* 0000 -> rSS */ + LWI((0x7C00-Xtotal), rSP) /* 7Bxx -> rSP */ + MW(rSP, rBP) /* set the indexed-data pointer */ + + MTSR(rAX, rDS) /* 0000 -> rDS, source segment */ + LWI(0x7C00, rSI) /* 7C00 -> rSI, source offset */ + MTSR(rAX, rES) /* 0000 -> rES, destination segment */ + LWI(0x600, rDI) /* 0600 -> rDI, destination offset */ + LWI(0x100, rCX) /* 0100 -> rCX, loop count (words) */ + + CLD + REP; MOVSL /* MOV DS:[(E)SI] -> ES:[(E)DI] */ + + FARJUMP16(0x0000, _start0600(SB)) + +TEXT _start0600(SB), $0 +#ifdef FLOPPY + LBI(0x80, rDL) +#else + CLRB(rAL) /* some systems pass 0 */ + CMPBR(rAL, rDL) + JNE _save + LBI(0x80, rDL) +#endif /* FLOPPY */ +_save: + SXB(rDL, Xdrive, xBP) /* save disc */ + + LWI(confidence(SB), rSI) /* for that warm, fuzzy feeling */ + CALL16(BIOSputs(SB)) + + LWI(_start+0x01BE(SB), rSI) /* address of partition table */ + LWI(0x04, rCX) /* 4 entries in table */ + LBI(0x80, rAH) /* active entry value */ + CLRB(rAL) /* inactive entry value */ + +_activeloop0: + LXB(0x00, xSI, rBL) /* get active entry from table */ + CMPBR(rBL, rAH) /* is this an active entry? */ + JEQ _active + + CMPBR(rBL, rAL) /* if not active it should be 0 */ + JNE _invalidMBR + + ADDI(0x10, rSI) /* next table entry */ + DEC(rCX) + JNE _activeloop0 + + LWI(noentry(SB), rSI) + CALL16(buggery(SB)) + +_active: + MW(rSI, rDI) /* save table address */ + +_activeloop1: + ADDI(0x10, rSI) /* next table entry */ + DEC(rCX) + JEQ _readsector + + LXB(0x00, xSI, rBL) /* get active entry from table */ + CMPBR(rBL, rAH) /* is this an active entry? */ + JNE _activeloop1 /* should only be one active */ + +_invalidMBR: + LWI(invalidMBR(SB), rSI) + CALL16(buggery(SB)) + +_readsector: + LBI(0x41, rAH) /* check extensions present */ + LWI(0x55AA, rBX) + LXB(Xdrive, xBP, rDL) /* drive */ + BIOSCALL(0x13) /* CF set on failure */ + JCS _readsector2 + CMPI(0xAA55, rBX) + JNE _readsector2 + ANDI(0x0001, rCX) + JEQ _readsector2 + +_readsector42: + SBPBI(0x10, Xdap+0) /* packet size */ + SBPBI(0x00, Xdap+1) /* reserved */ + SBPBI(0x01, Xdap+2) /* number of blocks to transfer */ + SBPBI(0x00, Xdap+3) /* reserved */ + SBPWI(0x7C00, Xdap+4) /* transfer buffer :offset */ + SBPWI(0x0000, Xdap+6) /* transfer buffer seg: */ + LXW(0x08, xDI, rAX) /* LBA (64-bits) */ + SBPW(rAX, Xdap+8) + LXW(0x0A, xDI, rAX) + SBPW(rAX, Xdap+10) + SBPWI(0x0000, Xdap+12) + SBPWI(0x0000, Xdap+14) + + MW(rBP, rSI) /* disk address packet */ + LBI(0x42, rAH) /* extended read */ + BIOSCALL(0x13) /* CF set on failure */ + JCC _readsectorok + + LWI(ioerror(SB), rSI) + CALL16(buggery(SB)) + +/* + * Read a sector from a disc using the traditional BIOS call. + * For BIOSCALL(0x13/AH=0x02): + * rAH 0x02 + * rAL number of sectors to read (1) + * rCH low 8 bits of cylinder + * rCL high 2 bits of cylinder (7-6), sector (5-0) + * rDH head + * rDL drive + * rES:rBX buffer address + */ +_readsector2: + LXB(0x01, xDI, rDH) /* head */ + LXW(0x02, xDI, rCX) /* save active cylinder/sector */ + + LWI(0x0201, rAX) /* read one sector */ + LXB(Xdrive, xBP, rDL) /* drive */ + LWI(0x7C00, rBX) /* buffer address (rES already OK) */ + BIOSCALL(0x13) /* CF set on failure */ + JCC _readsectorok + + LWI(ioerror(SB), rSI) + CALL16(buggery(SB)) + +_readsectorok: + LWI(0x7C00, rBX) /* buffer address (rES already OK) */ + LXW(0x1FE, xBX, rAX) + CMPI(0xAA55, rAX) + JNE _bbnotok + + /* + * Jump to the loaded PBS. + * rDL and rSI should still contain the drive + * and partition table pointer respectively. + */ + MW(rDI, rSI) + FARJUMP16(0x0000, 0x7C00) + +_bbnotok: + LWI(invalidPBS(SB), rSI) + +TEXT buggery(SB), $0 + CALL16(BIOSputs(SB)) + LWI(reboot(SB), rSI) + CALL16(BIOSputs(SB)) + +_wait: + CLR(rAX) /* wait for any key */ + BIOSCALL(0x16) + +_reset: + CLR(rBX) /* set ES segment for BIOS area */ + MTSR(rBX, rES) + + LWI(0x0472, rBX) /* warm-start code address */ + LWI(0x1234, rAX) /* warm-start code */ + POKEW /* MOVW AX, ES:[BX] */ + + FARJUMP16(0xFFFF, 0x0000) /* reset */ + +/* + * Output a string to the display. + * String argument is in rSI. + */ +TEXT BIOSputs(SB), $0 + PUSHA + CLR(rBX) +_BIOSputs: + LODSB + ORB(rAL, rAL) + JEQ _BIOSputsret + + LBI(0x0E, rAH) + BIOSCALL(0x10) + JMP _BIOSputs + +_BIOSputsret: + POPA + RET + +/* "No active entry in MBR" */ +TEXT noentry(SB), $0 + BYTE $'N'; BYTE $'o'; BYTE $' '; BYTE $'a'; + BYTE $'c'; BYTE $'t'; BYTE $'i'; BYTE $'v'; + BYTE $'e'; BYTE $' '; BYTE $'e'; BYTE $'n'; + BYTE $'t'; BYTE $'r'; BYTE $'y'; BYTE $' '; + BYTE $'i'; BYTE $'n'; BYTE $' '; BYTE $'M'; + BYTE $'B'; BYTE $'R'; + BYTE $'\z'; + +/* "Invalid MBR" */ +TEXT invalidMBR(SB), $0 + BYTE $'I'; BYTE $'n'; BYTE $'v'; BYTE $'a'; + BYTE $'l'; BYTE $'i'; BYTE $'d'; BYTE $' '; + BYTE $'M'; BYTE $'B'; BYTE $'R'; + BYTE $'\z'; + +/* "I/O error" */ +TEXT ioerror(SB), $0 + BYTE $'I'; BYTE $'/'; BYTE $'O'; BYTE $' '; + BYTE $'e'; BYTE $'r'; BYTE $'r'; BYTE $'o'; + BYTE $'r'; + BYTE $'\z'; + +/* "Invalid PBS" */ +TEXT invalidPBS(SB), $0 + BYTE $'I'; BYTE $'n'; BYTE $'v'; BYTE $'a'; + BYTE $'l'; BYTE $'i'; BYTE $'d'; BYTE $' '; + BYTE $'P'; BYTE $'B'; BYTE $'S'; + BYTE $'\z'; + +/* "\r\nPress almost any key to reboot..." */ +TEXT reboot(SB), $0 + BYTE $'\r';BYTE $'\n'; + BYTE $'P'; BYTE $'r'; BYTE $'e'; BYTE $'s'; + BYTE $'s'; BYTE $' '; BYTE $'a'; BYTE $'l'; + BYTE $'m'; BYTE $'o'; BYTE $'s'; BYTE $'t'; + BYTE $' '; BYTE $'a'; BYTE $'n'; BYTE $'y'; + BYTE $' '; BYTE $'k'; BYTE $'e'; BYTE $'y'; + BYTE $' '; BYTE $'t'; BYTE $'o'; BYTE $' '; + BYTE $'r'; BYTE $'e'; BYTE $'b'; BYTE $'o'; + BYTE $'o'; BYTE $'t'; BYTE $'.'; BYTE $'.'; + BYTE $'.'; + BYTE $'\z'; + +/* "MBR..." */ +TEXT confidence(SB), $0 + BYTE $'M'; BYTE $'B'; BYTE $'R'; BYTE $'.'; + BYTE $'.'; BYTE $'.'; + BYTE $'\z'; diff --git a/os/boot/pc/mem.h b/os/boot/pc/mem.h new file mode 100644 index 00000000..d33fee0e --- /dev/null +++ b/os/boot/pc/mem.h @@ -0,0 +1,114 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ + +/* + * Sizes + */ +#define BI2BY 8 /* bits per byte */ +#define BI2WD 32 /* bits per word */ +#define BY2WD 4 /* bytes per word */ +#define BY2PG 4096 /* bytes per page */ +#define WD2PG (BY2PG/BY2WD) /* words per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define PGROUND(s) (((s)+(BY2PG-1))&~(BY2PG-1)) + +#define MAXMACH 1 /* max # cpus system can run */ + +/* + * Time + */ +#define HZ (100) /* clock frequency */ +#define MS2HZ (1000/HZ) /* millisec per clock tick */ +#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ +#define TK2MS(x) ((x)*(1000/HZ)) +#define MS2TK(t) ((((ulong)(t))*HZ)/1000) /* milliseconds to ticks */ + +/* + * Fundamental addresses + */ +#define IDTADDR 0x80000800 /* idt */ +#define APBOOTSTRAP 0x80001000 /* AP bootstrap code */ +#define CONFADDR 0x80001200 /* info passed from boot loader */ +#define CPU0PDB 0x80002000 /* bootstrap processor PDB */ +#define CPU0PTE 0x80003000 /* bootstrap processor PTE's for 0-4MB */ +#define MACHADDR 0x80004000 /* as seen by current processor */ +#define CPU0MACH 0x80005000 /* Mach for bootstrap processor */ +#define MACHSIZE (BY2PG*8) /* stack size */ + + +/* + * Address spaces + * + * Kernel is at 2GB-4GB + */ +#define KZERO 0x80000000 /* base of kernel address space */ +#define KTZERO KZERO /* first address in kernel text */ +#define ROMBIOS (KZERO|0xF0000) + +/* + * known 80386 segments (in GDT) and their selectors + */ +#define NULLSEG 0 /* null segment */ +#define KDSEG 1 /* kernel data/stack */ +#define KESEG 2 /* kernel executable */ +#define UDSEG 3 /* user data/stack */ +#define UESEG 4 /* user executable */ +#define SYSGATE 5 /* system call gate */ +#define TSSSEG 6 /* task segment */ + +#define SELGDT (0<<3) /* selector is in gdt */ +#define SELLDT (1<<3) /* selector is in ldt */ + +#define SELECTOR(i, t, p) (((i)<<3) | (t) | (p)) + +#define NULLSEL SELECTOR(NULLSEG, SELGDT, 0) +#define KESEL SELECTOR(KESEG, SELGDT, 0) +#define KDSEL SELECTOR(KDSEG, SELGDT, 0) +#define UESEL SELECTOR(UESEG, SELGDT, 3) +#define UDSEL SELECTOR(UDSEG, SELGDT, 3) +#define TSSSEL SELECTOR(TSSSEG, SELGDT, 0) + +/* + * fields in segment descriptors + */ +#define SEGDATA (0x10<<8) /* data/stack segment */ +#define SEGEXEC (0x18<<8) /* executable segment */ +#define SEGTSS (0x9<<8) /* TSS segment */ +#define SEGCG (0x0C<<8) /* call gate */ +#define SEGIG (0x0E<<8) /* interrupt gate */ +#define SEGTG (0x0F<<8) /* task gate */ +#define SEGTYPE (0x1F<<8) + +#define SEGP (1<<15) /* segment present */ +#define SEGPL(x) ((x)<<13) /* priority level */ +#define SEGB (1<<22) /* granularity 1==4k (for expand-down) */ +#define SEGG (1<<23) /* granularity 1==4k (for other) */ +#define SEGE (1<<10) /* expand down */ +#define SEGW (1<<9) /* writable (for data/stack) */ +#define SEGR (1<<9) /* readable (for code) */ +#define SEGD (1<<22) /* default 1==32bit (for code) */ + +/* + * virtual MMU + */ +#define PTEMAPMEM (1024*1024) /* ??? */ +#define SEGMAPSIZE 16 /* ??? */ +#define PTEPERTAB (PTEMAPMEM/BY2PG) /* ??? */ +#define PPN(x) ((x)&~(BY2PG-1)) + +/* + * physical MMU + */ +#define PTEVALID (1<<0) +#define PTEUNCACHED 0 /* everything is uncached */ +#define PTEWRITE (1<<1) +#define PTERONLY (0<<1) +#define PTEKERNEL (0<<2) +#define PTEUSER (1<<2) +#define PTESIZE (1<<7) + +/* + * flag register bits that we care about + */ +#define IFLAG 0x200 diff --git a/os/boot/pc/memory.c b/os/boot/pc/memory.c new file mode 100644 index 00000000..82da8b2e --- /dev/null +++ b/os/boot/pc/memory.c @@ -0,0 +1,504 @@ +/* + * Size memory and create the kernel page-tables on the fly while doing so. + * Called from main(), this code should only be run by the bootstrap processor. + */ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#define MEMDEBUG 0 + +#define PDX(va) ((((ulong)(va))>>22) & 0x03FF) +#define PTX(va) ((((ulong)(va))>>12) & 0x03FF) + +enum { + MemUPA = 0, /* unbacked physical address */ + MemRAM = 1, /* physical memory */ + MemUMB = 2, /* upper memory block (<16MB) */ + NMemType = 3, + + KB = 1024, + + MemMinMB = 4, /* minimum physical memory (<=4MB) */ + MemMaxMB = 768, /* maximum physical memory to check */ + + NMemBase = 10, +}; + +typedef struct { + int size; + ulong addr; +} Map; + +typedef struct { + char* name; + Map* map; + Map* mapend; + + Lock; +} RMap; + +static Map mapupa[8]; +static RMap rmapupa = { + "unallocated unbacked physical memory", + mapupa, + &mapupa[7], +}; + +static Map xmapupa[8]; +static RMap xrmapupa = { + "unbacked physical memory", + xmapupa, + &xmapupa[7], +}; + +static Map mapram[8]; +static RMap rmapram = { + "physical memory", + mapram, + &mapram[7], +}; + +static Map mapumb[64]; +static RMap rmapumb = { + "upper memory block", + mapumb, + &mapumb[63], +}; + +static Map mapumbrw[8]; +static RMap rmapumbrw = { + "UMB device memory", + mapumbrw, + &mapumbrw[7], +}; + +void +memdebug(void) +{ + Map *mp; + ulong maxpa, maxpa1, maxpa2; + + if(MEMDEBUG == 0) + return; + + maxpa = (nvramread(0x18)<<8)|nvramread(0x17); + maxpa1 = (nvramread(0x31)<<8)|nvramread(0x30); + maxpa2 = (nvramread(0x16)<<8)|nvramread(0x15); + print("maxpa = %luX -> %luX, maxpa1 = %luX maxpa2 = %luX\n", + maxpa, MB+maxpa*KB, maxpa1, maxpa2); + + for(mp = rmapram.map; mp->size; mp++) + print("%8.8luX %8.8luX %8.8luX\n", mp->addr, (ulong)mp->size, mp->addr+mp->size); + for(mp = rmapumb.map; mp->size; mp++) + print("%8.8luX %8.8luX %8.8luX\n", mp->addr, (ulong)mp->size, mp->addr+mp->size); + for(mp = rmapumbrw.map; mp->size; mp++) + print("%8.8luX %8.8luX %8.8luX\n", mp->addr, (ulong)mp->size, mp->addr+mp->size); + for(mp = rmapupa.map; mp->size; mp++) + print("%8.8luX %8.8luX %8.8luX\n", mp->addr, (ulong)mp->size, mp->addr+mp->size); +} + +void +mapfree(RMap* rmap, ulong addr, ulong size) +{ + Map *mp; + ulong t; + + if(size == 0) + return; + + lock(rmap); + for(mp = rmap->map; mp->addr <= addr && mp->size; mp++) + ; + + if(mp > rmap->map && (mp-1)->addr+(mp-1)->size == addr){ + (mp-1)->size += size; + if(addr+size == mp->addr){ + (mp-1)->size += mp->size; + while(mp->size){ + mp++; + (mp-1)->addr = mp->addr; + (mp-1)->size = mp->size; + } + } + } + else{ + if(addr+size == mp->addr && mp->size){ + mp->addr -= size; + mp->size += size; + } + else do{ + if(mp >= rmap->mapend){ + print("mapfree: %s: losing 0x%luX, %lud\n", + rmap->name, addr, size); + break; + } + t = mp->addr; + mp->addr = addr; + addr = t; + t = mp->size; + mp->size = size; + mp++; + }while(size = t); + } + unlock(rmap); +} + +ulong +mapalloc(RMap* rmap, ulong addr, int size, int align) +{ + Map *mp; + ulong maddr, oaddr; + + lock(rmap); + for(mp = rmap->map; mp->size; mp++){ + maddr = mp->addr; + + if(addr){ + /* + * A specific address range has been given: + * if the current map entry is greater then + * the address is not in the map; + * if the current map entry does not overlap + * the beginning of the requested range then + * continue on to the next map entry; + * if the current map entry does not entirely + * contain the requested range then the range + * is not in the map. + */ + if(maddr > addr) + break; + if(mp->size < addr - maddr) /* maddr+mp->size < addr, but no overflow */ + continue; + if(addr - maddr > mp->size - size) /* addr+size > maddr+mp->size, but no overflow */ + break; + maddr = addr; + } + + if(align > 0) + maddr = ((maddr+align-1)/align)*align; + if(mp->addr+mp->size-maddr < size) + continue; + + oaddr = mp->addr; + mp->addr = maddr+size; + mp->size -= maddr-oaddr+size; + if(mp->size == 0){ + do{ + mp++; + (mp-1)->addr = mp->addr; + }while((mp-1)->size = mp->size); + } + + unlock(rmap); + if(oaddr != maddr) + mapfree(rmap, oaddr, maddr-oaddr); + + return maddr; + } + unlock(rmap); + + return 0; +} + +static void +umbscan(void) +{ + uchar *p; + + /* + * Scan the Upper Memory Blocks (0xA0000->0xF0000) for pieces + * which aren't used; they can be used later for devices which + * want to allocate some virtual address space. + * Check for two things: + * 1) device BIOS ROM. This should start with a two-byte header + * of 0x55 0xAA, followed by a byte giving the size of the ROM + * in 512-byte chunks. These ROM's must start on a 2KB boundary. + * 2) device memory. This is read-write. + * There are some assumptions: there's VGA memory at 0xA0000 and + * the VGA BIOS ROM is at 0xC0000. Also, if there's no ROM signature + * at 0xE0000 then the whole 64KB up to 0xF0000 is theoretically up + * for grabs; check anyway. + */ + p = KADDR(0xD0000); /*RSC: changed from 0xC0000 */ + while(p < (uchar*)KADDR(0xE0000)){ + if (p[0] == 0x55 && p[1] == 0xAA) { + /* Skip p[2] chunks of 512 bytes. Test for 0x55 AA before + poking obtrusively, or else the Thinkpad X20 dies when + setting up the cardbus (PB) */ + p += p[2] * 512; + continue; + } + + p[0] = 0xCC; + p[2*KB-1] = 0xCC; + if(p[0] != 0xCC || p[2*KB-1] != 0xCC){ + p[0] = 0x55; + p[1] = 0xAA; + p[2] = 4; + if(p[0] == 0x55 && p[1] == 0xAA){ + p += p[2]*512; + continue; + } + if(p[0] == 0xFF && p[1] == 0xFF) + mapfree(&rmapumb, PADDR(p), 2*KB); + } + else + mapfree(&rmapumbrw, PADDR(p), 2*KB); + p += 2*KB; + } + + p = KADDR(0xE0000); + if(p[0] != 0x55 || p[1] != 0xAA){ + p[0] = 0xCC; + p[64*KB-1] = 0xCC; + if(p[0] != 0xCC && p[64*KB-1] != 0xCC) + mapfree(&rmapumb, PADDR(p), 64*KB); + } +} + + +void +meminit(ulong) +{ + /* A hack to initialize unbacked physical memory. It's assumed PCI space is assigned by + the BIOS in the 0xF0000000 range and 9load never needs more than 0x2000... to run. These + values leave ample space for memory allocations for uninitialized PCI cards (e.g. cardbus + cards). (pb) */ + ulong maxmem = 0x40000000; + + umbscan(); + mapfree(&rmapupa, maxmem, 0x00000000-maxmem); + if(MEMDEBUG) + memdebug(); +} + +ulong +umbmalloc(ulong addr, int size, int align) +{ + ulong a; + + if(a = mapalloc(&rmapumb, addr, size, align)) + return (ulong)KADDR(a); + + return 0; +} + +void +umbfree(ulong addr, int size) +{ + mapfree(&rmapumb, PADDR(addr), size); +} + +ulong +umbrwmalloc(ulong addr, int size, int align) +{ + ulong a; + uchar *p; + + if(a = mapalloc(&rmapumbrw, addr, size, align)) + return(ulong)KADDR(a); + + /* + * Perhaps the memory wasn't visible before + * the interface is initialised, so try again. + */ + if((a = umbmalloc(addr, size, align)) == 0) + return 0; + p = (uchar*)a; + p[0] = 0xCC; + p[size-1] = 0xCC; + if(p[0] == 0xCC && p[size-1] == 0xCC) + return a; + umbfree(a, size); + + return 0; +} + +void +umbrwfree(ulong addr, int size) +{ + mapfree(&rmapumbrw, PADDR(addr), size); +} + +ulong* +mmuwalk(ulong* pdb, ulong va, int level, int create) +{ + ulong pa, *table; + + /* + * Walk the page-table pointed to by pdb and return a pointer + * to the entry for virtual address va at the requested level. + * If the entry is invalid and create isn't requested then bail + * out early. Otherwise, for the 2nd level walk, allocate a new + * page-table page and register it in the 1st level. + */ + table = &pdb[PDX(va)]; + if(!(*table & PTEVALID) && create == 0) + return 0; + + switch(level){ + + default: + return 0; + + case 1: + return table; + + case 2: + if(*table & PTESIZE) + panic("mmuwalk2: va 0x%ux entry 0x%ux\n", va, *table); + if(!(*table & PTEVALID)){ + pa = PADDR(ialloc(BY2PG, BY2PG)); + *table = pa|PTEWRITE|PTEVALID; + } + table = KADDR(PPN(*table)); + + return &table[PTX(va)]; + } +} + +static Lock mmukmaplock; + +ulong +mmukmap(ulong pa, ulong va, int size) +{ + ulong pae, *table, *pdb, pgsz, *pte, x; + int pse, sync; + extern int cpuidax, cpuiddx; + + pdb = KADDR(getcr3()); + if((cpuiddx & 0x08) && (getcr4() & 0x10)) + pse = 1; + else + pse = 0; + sync = 0; + + pa = PPN(pa); + if(va == 0) + va = (ulong)KADDR(pa); + else + va = PPN(va); + + pae = pa + size; + lock(&mmukmaplock); + while(pa < pae){ + table = &pdb[PDX(va)]; + /* + * Possibly already mapped. + */ + if(*table & PTEVALID){ + if(*table & PTESIZE){ + /* + * Big page. Does it fit within? + * If it does, adjust pgsz so the correct end can be + * returned and get out. + * If not, adjust pgsz up to the next 4MB boundary + * and continue. + */ + x = PPN(*table); + if(x != pa) + panic("mmukmap1: pa 0x%ux entry 0x%ux\n", + pa, *table); + x += 4*MB; + if(pae <= x){ + pa = pae; + break; + } + pgsz = x - pa; + pa += pgsz; + va += pgsz; + + continue; + } + else{ + /* + * Little page. Walk to the entry. + * If the entry is valid, set pgsz and continue. + * If not, make it so, set pgsz, sync and continue. + */ + pte = mmuwalk(pdb, va, 2, 0); + if(pte && *pte & PTEVALID){ + x = PPN(*pte); + if(x != pa) + panic("mmukmap2: pa 0x%ux entry 0x%ux\n", + pa, *pte); + pgsz = BY2PG; + pa += pgsz; + va += pgsz; + sync++; + + continue; + } + } + } + + /* + * Not mapped. Check if it can be mapped using a big page - + * starts on a 4MB boundary, size >= 4MB and processor can do it. + * If not a big page, walk the walk, talk the talk. + * Sync is set. + */ + if(pse && (pa % (4*MB)) == 0 && (pae >= pa+4*MB)){ + *table = pa|PTESIZE|PTEWRITE|PTEUNCACHED|PTEVALID; + pgsz = 4*MB; + } + else{ + pte = mmuwalk(pdb, va, 2, 1); + *pte = pa|PTEWRITE|PTEUNCACHED|PTEVALID; + pgsz = BY2PG; + } + pa += pgsz; + va += pgsz; + sync++; + } + unlock(&mmukmaplock); + + /* + * If something was added + * then need to sync up. + */ + if(sync) + putcr3(PADDR(pdb)); + + return pa; +} + +ulong +upamalloc(ulong addr, int size, int align) +{ + ulong ae, a; + + USED(align); + + if((a = mapalloc(&rmapupa, addr, size, align)) == 0){ + memdebug(); + return 0; + } + + /* + * This is a travesty, but they all are. + */ + ae = mmukmap(a, 0, size); + + /* + * Should check here that it was all delivered + * and put it back and barf if not. + */ + USED(ae); + + /* + * Be very careful this returns a PHYSICAL address. + */ + return a; +} + +void +upafree(ulong pa, int size) +{ + USED(pa, size); +} + diff --git a/os/boot/pc/mkfile b/os/boot/pc/mkfile new file mode 100644 index 00000000..57eef41e --- /dev/null +++ b/os/boot/pc/mkfile @@ -0,0 +1,241 @@ +<../../../mkconfig +objtype=386 +SYSTARG=$OSTARG +OBJTYPE=386 +BIN=$ROOT/Inferno/$OBJTYPE +LIBDIR=$ROOT/Inferno/$OBJTYPE/lib +LIBDIRS=../libflate $ROOT/libkern +LIBS=\ + libflate\ + libkern\ + +LIBFILES=${LIBS:%=$LIBDIR/%.a} +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE + +BIN=$ROOT/Inferno/$OBJTYPE + +TARG=\ + 9load\ + 9pxeload\ + 9loadlite\ + 9loaddebug\ + 9loadlitedebug\ + ld.com\ + mbr\ + pbs\ + pbslba\ + +CORE=\ + alarm.$O\ + cga.$O\ + clock.$O\ + console.$O\ + dosboot.$O\ + devfloppy.$O\ + dma.$O\ + fs.$O\ + ilock.$O\ + kbd.$O\ + kfsboot.$O\ + print.$O\ + queue.$O\ + trap.$O\ + getcallerpc.$O\ + +LOAD=\ + 8250.$O\ + apm.$O\ + boot.$O\ + devpccard.$O\ + conf.$O\ + devi82365.$O\ + devsd.$O\ + inflate.$O\ + load.$O\ + memory.$O\ + part.$O\ + pci.$O\ + sdata.$O\ + sdmylex.$O\ + sd53c8xx.$O\ + sdscsi.$O\ + +ETHER=\ + bootp.$O\ + eipfmt.$O\ + ether.$O\ + ether2114x.$O\ + ether2000.$O\ + ether589.$O\ + ether79c970.$O\ + ether8003.$O\ + ether8139.$O\ + ether8169.$O\ + ether82557.$O\ + ether83815.$O\ + ether8390.$O\ + etherec2t.$O\ + etherelnk3.$O\ + etherigbe.$O\ + ethermii.$O\ + etherrhine.$O\ + +BCOM=\ + bcom.$O\ + bootld.$O\ + devsd.$O\ + memory.$O\ + part.$O\ + pci.$O\ + sdata.$O\ + sdscsi.$O\ + +HFILES=\ + lib.h\ + mem.h\ + dat.h\ + fns.h\ + io.h\ + +CFLAGS=-FVw -I. -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include + +all:V: $TARG + +9load: l.$O $CORE $LOAD $ETHER $LIBFILES + $LD -o $target -H3 -T0x80010000 -l $prereq + ls -l $target + +9pxeload: l.$O $CORE $LOAD $ETHER $LIBFILES + $LD -o $target -H3 -T0x80007C00 -l $prereq + ls -l $target + +9loaddebug: l.$O $CORE $LOAD $ETHER $LIBFILES + $LD -o $target -T0x80010000 -l $prereq + ls -l $target + # acid $target + # map({"text", 0x80010000, 0x80090000, 0x00000020}) + +9loadlite: l.$O $CORE $LOAD noether.$O $LIBFILES + $LD -o $target -H3 -T0x80010000 -l $prereq + ls -l $target + +9loadlitedebug: l.$O $CORE $LOAD noether.$O $LIBFILES + $LD -o $target -T0x80010000 -l $prereq + ls -l $target + # acid $target + # map({"text", 0x80010000, 0x80090000, 0x00000020}) + +ld.com: ld.$O $CORE $BCOM $LIBFILES + $LD -o $target -H3 -T0x80080100 -l $prereq + ls -l $target + +lddebug: ld.$O $CORE $BCOM $LIBFILES + $LD -o $target -T0x80080100 -l $prereq + ls -l $target + # acid $target + # map({"text", 0x80080100, 0x800B0000, 0x00000020}) + +ld.$O: l.s + $AS -DDOTCOM -o $target l.s + +lpxe.$O: l.s + $AS -DPXE -o $target l.s + +%.$O: %.s + $AS $stem.s + +%.$O: %.c + $CC $CFLAGS $stem.c + +%.$O: $HFILES + +l.$O pbs.$O pbslba.$O mbr.$O: x16.h + +clock.$O floppy.$O trap.$O: ureg.h +bcom.$O conf.$O devfloppy.$O devsd.$O dosboot.$O fs.$O \ + kfsboot.$O load.$O part.$O: dosfs.h fs.h kfs.h +ether.$O etherelnk3.$O: etherif.h +devsd.$O part.$O sdata.$O sdscsi.$O: sd.h +bootp.$O: ip.h + +mbr: mbr.$O + $LD -o $target -H3 -T0x0600 -l $prereq + ls -l $target + +pbs&: pbs%.$O + $LD -o $target -H3 -T0x7C00 -l $prereq + ls -l $target + +pbs&.debug: pbs%.$O + $LD -o $target -T0x7C00 -l $prereq + ls -l $target + # acid $target + # map({"text", 0x7C00, 0x7E00, 0x00000020}) + +# added to cause libflate to be made automatically: + +$ROOT/Inferno/$OBJTYPE/lib/lib%.a:Q: all-$SHELLTYPE + # + +rc-lib%.a nt-lib%.a:VQ: + echo '@{builtin cd ' $ROOT/lib$stem ';mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install}' + @{builtin cd $ROOT/lib$stem ;mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install} + +sh-lib%.a:VQ: + echo "(cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install)" + (cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install) + +clean: + rm -f *.[$OS] [$OS].out y.tab.? y.debug y.output $TARG 9loaddebug lddebug + +install:V: + for (i in $TARG) + mk $MKFLAGS $i.install + +%.install:V: $BIN/% + +$BIN/%: % + cp $stem $BIN/$stem + +UPDATE=\ + mkfile\ + ${CORE:%.$O=%.c}\ + ${LOAD:%.$O=%.c}\ + ${BCOM:%.$O=%.c}\ + ${ETHER:%.$O=%.c}\ + $HFILES\ + l.s\ + noether.c\ + pbs.s\ + pbslba.s\ + mbr.s\ + x16.h\ + ureg.h\ + dosfs.h\ + fs.h\ + kfs.h\ + etherif.h\ + sd.h\ + ip.h\ + devfloppy.h\ + ${TARG:%=/386/%}\ + +update:V: + update $UPDATEFLAGS $UPDATE + + +%-sh:QV: + for i in $LIBDIRS + do + echo "(cd $i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE $stem)" + (cd $i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE $stem) + done + +%-rc %-nt:QV: + for (i in $LIBDIRS) + { + echo '@{cd $i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE $stem}' + @{cd $i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE $stem} + } + +nuke:V: clean nuke-$SHELLTYPE diff --git a/os/boot/pc/noether.c b/os/boot/pc/noether.c new file mode 100644 index 00000000..2162d1d4 --- /dev/null +++ b/os/boot/pc/noether.c @@ -0,0 +1,40 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +int +etherinit(void) +{ + return -1; +} + +void +etherinitdev(int, char*) +{ +} + +void +etherprintdevs(int) +{ +} + +int +etherrxpkt(int, Etherpkt*, int) +{ + return -1; +} + +int +bootpboot(int, char*, Boot*) +{ + return -1; +} + +void* +pxegetfspart(int, char*, int) +{ + return nil; +} diff --git a/os/boot/pc/part.c b/os/boot/pc/part.c new file mode 100644 index 00000000..8c703ae8 --- /dev/null +++ b/os/boot/pc/part.c @@ -0,0 +1,344 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +#include "sd.h" +#include "fs.h" + +enum { + Npart = 32 +}; + +uchar *mbrbuf, *partbuf; +int nbuf; +#define trace 0 + +int +tsdbio(SDunit *unit, SDpart *part, void *a, vlong off, int mbr) +{ + uchar *b; + + if(sdbio(unit, part, a, unit->secsize, off) != unit->secsize){ + if(trace) + print("%s: read %lud at %lld failed\n", unit->name, + unit->secsize, (vlong)part->start*unit->secsize+off); + return -1; + } + b = a; + if(mbr && (b[0x1FE] != 0x55 || b[0x1FF] != 0xAA)){ + if(trace) + print("%s: bad magic %.2ux %.2ux at %lld\n", + unit->name, b[0x1FE], b[0x1FF], + (vlong)part->start*unit->secsize+off); + return -1; + } + return 0; +} + +/* + * read partition table. The partition table is just ascii strings. + */ +#define MAGIC "plan9 partitions" +static void +oldp9part(SDunit *unit) +{ + SDpart *pp; + char *field[3], *line[Npart+1]; + ulong n, start, end; + int i; + + /* + * We have some partitions already. + */ + pp = &unit->part[unit->npart]; + + /* + * We prefer partition tables on the second to last sector, + * but some old disks use the last sector instead. + */ + strcpy(pp->name, "partition"); + pp->start = unit->sectors - 2; + pp->end = unit->sectors - 1; + + if(tsdbio(unit, pp, partbuf, 0, 0) < 0) + return; + + if(strncmp((char*)partbuf, MAGIC, sizeof(MAGIC)-1) != 0) { + /* not found on 2nd last sector; look on last sector */ + pp->start++; + pp->end++; + if(tsdbio(unit, pp, partbuf, 0, 0) < 0) + return; + if(strncmp((char*)partbuf, MAGIC, sizeof(MAGIC)-1) != 0) + return; + print("%s: using old plan9 partition table on last sector\n", unit->name); + }else + print("%s: using old plan9 partition table on 2nd-to-last sector\n", unit->name); + + /* we found a partition table, so add a partition partition */ + unit->npart++; + partbuf[unit->secsize-1] = '\0'; + + /* + * parse partition table + */ + n = getfields((char*)partbuf, line, Npart+1, '\n'); + if(n && strncmp(line[0], MAGIC, sizeof(MAGIC)-1) == 0){ + for(i = 1; i < n && unit->npart < SDnpart; i++){ + if(getfields(line[i], field, 3, ' ') != 3) + break; + start = strtoul(field[1], 0, 0); + end = strtoul(field[2], 0, 0); + if(start >= end || end > unit->sectors) + break; + sdaddpart(unit, field[0], start, end); + } + } +} + +static void +p9part(SDunit *unit, char *name) +{ + SDpart *p; + char *field[4], *line[Npart+1]; + ulong start, end; + int i, n; + + p = sdfindpart(unit, name); + if(p == nil) + return; + + if(tsdbio(unit, p, partbuf, unit->secsize, 0) < 0) + return; + partbuf[unit->secsize-1] = '\0'; + + if(strncmp((char*)partbuf, "part ", 5) != 0) + return; + + n = getfields((char*)partbuf, line, Npart+1, '\n'); + if(n == 0) + return; + for(i = 0; i < n && unit->npart < SDnpart; i++){ + if(strncmp(line[i], "part ", 5) != 0) + break; + if(getfields(line[i], field, 4, ' ') != 4) + break; + start = strtoul(field[2], 0, 0); + end = strtoul(field[3], 0, 0); + if(start >= end || end > unit->sectors) + break; + sdaddpart(unit, field[1], p->start+start, p->start+end); + } +} + +int +isdos(int t) +{ + return t==FAT12 || t==FAT16 || t==FATHUGE || t==FAT32 || t==FAT32X; +} + +int +isextend(int t) +{ + return t==EXTEND || t==EXTHUGE || t==LEXTEND; +} + +/* + * Fetch the first dos and all plan9 partitions out of the MBR partition table. + * We return -1 if we did not find a plan9 partition. + */ +static int +mbrpart(SDunit *unit) +{ + Dospart *dp; + ulong taboffset, start, end; + ulong firstxpart, nxtxpart; + int havedos, i, nplan9; + char name[10]; + + taboffset = 0; + dp = (Dospart*)&mbrbuf[0x1BE]; + if(1) { + /* get the MBR (allowing for DMDDO) */ + if(tsdbio(unit, &unit->part[0], mbrbuf, (vlong)taboffset*unit->secsize, 1) < 0) + return -1; + for(i=0; i<4; i++) + if(dp[i].type == DMDDO) { + if(trace) + print("DMDDO partition found\n"); + taboffset = 63; + if(tsdbio(unit, &unit->part[0], mbrbuf, (vlong)taboffset*unit->secsize, 1) < 0) + return -1; + i = -1; /* start over */ + } + } + + /* + * Read the partitions, first from the MBR and then + * from successive extended partition tables. + */ + nplan9 = 0; + havedos = 0; + firstxpart = 0; + for(;;) { + if(tsdbio(unit, &unit->part[0], mbrbuf, (vlong)taboffset*unit->secsize, 1) < 0) + return -1; + if(trace) { + if(firstxpart) + print("%s ext %lud ", unit->name, taboffset); + else + print("%s mbr ", unit->name); + } + nxtxpart = 0; + for(i=0; i<4; i++) { + if(trace) + print("dp %d...", dp[i].type); + start = taboffset+GLONG(dp[i].start); + end = start+GLONG(dp[i].len); + + if(dp[i].type == PLAN9) { + if(nplan9 == 0) + strcpy(name, "plan9"); + else + sprint(name, "plan9.%d", nplan9); + sdaddpart(unit, name, start, end); + p9part(unit, name); + nplan9++; + } + + /* + * We used to take the active partition (and then the first + * when none are active). We have to take the first here, + * so that the partition we call ``dos'' agrees with the + * partition disk/fdisk calls ``dos''. + */ + if(havedos==0 && isdos(dp[i].type)){ + havedos = 1; + sdaddpart(unit, "dos", start, end); + } + + /* nxtxpart is relative to firstxpart (or 0), not taboffset */ + if(isextend(dp[i].type)){ + nxtxpart = start-taboffset+firstxpart; + if(trace) + print("link %lud...", nxtxpart); + } + } + if(trace) + print("\n"); + + if(!nxtxpart) + break; + if(!firstxpart) + firstxpart = nxtxpart; + taboffset = nxtxpart; + } + return nplan9 ? 0 : -1; +} + +/* + * To facilitate booting from CDs, we create a partition for + * the boot floppy image embedded in a bootable CD. + */ +static int +part9660(SDunit *unit) +{ + uchar buf[2048]; + ulong a, n; + uchar *p; + + if(unit->secsize != 2048) + return -1; + + if(sdbio(unit, &unit->part[0], buf, 2048, 17*2048) < 0) + return -1; + + if(buf[0] || strcmp((char*)buf+1, "CD001\x01EL TORITO SPECIFICATION") != 0) + return -1; + + + p = buf+0x47; + a = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); + + if(sdbio(unit, &unit->part[0], buf, 2048, a*2048) < 0) + return -1; + + if(memcmp(buf, "\x01\x00\x00\x00", 4) != 0 + || memcmp(buf+30, "\x55\xAA", 2) != 0 + || buf[0x20] != 0x88) + return -1; + + p = buf+0x28; + a = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); + + switch(buf[0x21]){ + case 0x01: + n = 1200*1024; + break; + case 0x02: + n = 1440*1024; + break; + case 0x03: + n = 2880*1024; + break; + default: + return -1; + } + n /= 2048; + + print("found partition %s!cdboot; %lud+%lud\n", unit->name, a, n); + sdaddpart(unit, "cdboot", a, a+n); + return 0; +} + +enum { + NEW = 1<<0, + OLD = 1<<1 +}; + +void +partition(SDunit *unit) +{ + int type; + char *p; + + if(unit->part == 0) + return; + + if(part9660(unit) == 0) + return; + + p = getconf("partition"); + if(p == nil) + p = defaultpartition; + + if(p != nil && strncmp(p, "new", 3) == 0) + type = NEW; + else if(p != nil && strncmp(p, "old", 3) == 0) + type = OLD; + else + type = NEW|OLD; + + if(nbuf < unit->secsize) { + free(mbrbuf); + free(partbuf); + mbrbuf = malloc(unit->secsize); + partbuf = malloc(unit->secsize); + if(mbrbuf==nil || partbuf==nil) { + free(mbrbuf); + free(partbuf); + partbuf = mbrbuf = nil; + nbuf = 0; + return; + } + nbuf = unit->secsize; + } + + if((type & NEW) && mbrpart(unit) >= 0){ + /* nothing to do */; + } + else if(type & OLD) + oldp9part(unit); +} diff --git a/os/boot/pc/pbs.s b/os/boot/pc/pbs.s new file mode 100644 index 00000000..fde5ec39 --- /dev/null +++ b/os/boot/pc/pbs.s @@ -0,0 +1,372 @@ +/* + * FAT Partition Boot Sector. Loaded at 0x7C00: + * 8a pbs.s; 8l -o pbs -l -H3 -T0x7C00 pbs.8 + * Will load the target at LOADSEG*16+LOADOFF, so the target + * should be probably be loaded with LOADOFF added to the + * -Taddress. + * If LOADSEG is a multiple of 64KB and LOADOFF is 0 then + * targets larger than 64KB can be loaded. + * + * This code uses the traditional INT13 BIOS interface and can + * therefore only access the first 8.4GB of the disc. + * + * It relies on the _volid field in the FAT header containing + * the LBA of the root directory. + */ +#include "x16.h" +#include "mem.h" + +#define LOADSEG (0x10000/16) /* where to load code (64KB) */ +#define LOADOFF 0 +#define DIROFF 0x0200 /* where to read the root directory */ + +/* + * FAT directory entry. + */ +#define Dname 0x00 +#define Dext 0x08 +#define Dattr 0x0B +#define Dtime 0x16 +#define Ddate 0x18 +#define Dstart 0x1A +#define Dlengthlo 0x1C +#define Dlengthhi 0x1E + +#define Dirsz 0x20 + +/* + * Data is kept on the stack, indexed by rBP. + */ +#define Xdap 0x00 /* disc address packet */ +#define Xrootsz 0x10 /* file data area */ +#define Xdrive 0x12 /* boot drive, passed by BIOS or MBR */ +#define Xtotal 0x14 /* sum of allocated data above */ + +TEXT _magic(SB), $0 + BYTE $0xEB; BYTE $0x3C; /* jmp .+ 0x3C (_start0x3E) */ + BYTE $0x90 /* nop */ +TEXT _version(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00 +TEXT _sectsize(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _clustsize(SB), $0 + BYTE $0x00 +TEXT _nresrv(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _nfats(SB), $0 + BYTE $0x00 +TEXT _rootsize(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _volsize(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _mediadesc(SB), $0 + BYTE $0x00 +TEXT _fatsize(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _trksize(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _nheads(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _nhiddenlo(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _nhiddenhi(SB), $0 + BYTE $0x00; BYTE $0x00; +TEXT _bigvolsize(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; +TEXT _driveno(SB), $0 + BYTE $0x00 +TEXT _reserved0(SB), $0 + BYTE $0x00 +TEXT _bootsig(SB), $0 + BYTE $0x00 +TEXT _volid(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; +TEXT _label(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00 + BYTE $0x00; BYTE $0x00; BYTE $0x00 +TEXT _type(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; + +_start0x3E: + CLI + CLR(rAX) + MTSR(rAX, rSS) /* 0000 -> rSS */ + MTSR(rAX, rDS) /* 0000 -> rDS, source segment */ + MTSR(rAX, rES) + LWI(_magic-Xtotal(SB), rSP) + MW(rSP, rBP) /* set the indexed-data pointer */ + + SBPB(rDL, Xdrive) /* save the boot drive */ + + /* booting from a CD starts us at 7C0:0. Move to 0:7C00 */ + PUSHR(rAX) + LWI(_nxt(SB), rAX) + PUSHR(rAX) + BYTE $0xCB /* FAR RET */ + +TEXT _nxt(SB), $0 + STI + + LWI(confidence(SB), rSI) /* for that warm, fuzzy feeling */ + CALL16(BIOSputs(SB)) + + CALL16(dreset(SB)) + +_jmp00: + LW(_volid(SB), rAX) /* Xrootlo */ + LW(_volid+2(SB), rDX) /* Xroothi */ + + LWI(_magic+DIROFF(SB), rBX) + CALL16(BIOSread(SB)) /* read the root directory */ + + LWI((512/Dirsz), rBX) + + LWI(_magic+DIROFF(SB), rDI) /* compare first directory entry */ + +_cmp00: + PUSHR(rDI) /* save for later if it matches */ + LWI(bootfile(SB), rSI) + LWI(Dattr, rCX) + REP + CMPSB + POPR(rDI) + JEQ _jmp02 + + DEC(rBX) + JEQ _jmp01 + + ADDI(Dirsz, rDI) + JMP _cmp00 +_jmp01: + CALL16(buggery(SB)) + +_jmp02: + CLR(rBX) /* a handy value */ + LW(_rootsize(SB), rAX) /* calculate and save Xrootsz */ + LWI(Dirsz, rCX) + MUL(rCX) + LW(_sectsize(SB), rCX) + PUSHR(rCX) + DEC(rCX) + ADD(rCX, rAX) + ADC(rBX, rDX) + POPR(rCX) /* _sectsize(SB) */ + DIV(rCX) + PUSHR(rAX) /* Xrootsz */ + + /* + * rDI points to the matching directory entry. + */ + LXW(Dstart, xDI, rAX) /* starting sector address */ + DEC(rAX) /* that's just the way it is */ + DEC(rAX) + LB(_clustsize(SB), rCL) + CLRB(rCH) + MUL(rCX) + LW(_volid(SB), rCX) /* Xrootlo */ + ADD(rCX, rAX) + LW(_volid+2(SB), rCX) /* Xroothi */ + ADC(rCX, rDX) + POPR(rCX) /* Xrootsz */ + ADD(rCX, rAX) + ADC(rBX, rDX) + + PUSHR(rAX) /* calculate how many sectors to read */ + PUSHR(rDX) + LXW(Dlengthlo, xDI, rAX) + LXW(Dlengthhi, xDI, rDX) + LW(_sectsize(SB), rCX) + PUSHR(rCX) + DEC(rCX) + ADD(rCX, rAX) + ADC(rBX, rDX) + POPR(rCX) /* _sectsize(SB) */ + DIV(rCX) + MW(rAX, rCX) + POPR(rDX) + POPR(rAX) + + LWI(LOADSEG, rBX) /* address to load into (seg+offset) */ + MTSR(rBX, rES) /* seg */ + LWI(LOADOFF, rBX) /* offset */ + +_readboot: + CALL16(BIOSread(SB)) /* read the sector */ + + LW(_sectsize(SB), rDI) /* bump addresses/counts */ + ADD(rDI, rBX) + JCC _incsecno + + MFSR(rES, rDI) /* next 64KB segment */ + ADDI(0x1000, rDI) + MTSR(rDI, rES) + +_incsecno: + CLR(rDI) + INC(rAX) + ADC(rDI, rDX) + LOOP _readboot + + LWI(LOADSEG, rDI) /* set rDS for loaded code */ + MTSR(rDI, rDS) + FARJUMP16(LOADSEG, LOADOFF) /* no deposit, no return */ + +TEXT buggery(SB), $0 + LWI(error(SB), rSI) + CALL16(BIOSputs(SB)) + +_wait: + CLR(rAX) /* wait for almost any key */ + BIOSCALL(0x16) + +_reset: + CLR(rBX) /* set ES segment for BIOS area */ + MTSR(rBX, rES) + + LWI(0x0472, rBX) /* warm-start code address */ + LWI(0x1234, rAX) /* warm-start code */ + POKEW /* MOVW AX, ES:[BX] */ + + FARJUMP16(0xFFFF, 0x0000) /* reset */ + +/* + * Read a sector from a disc. On entry: + * rDX:rAX sector number + * rES:rBX buffer address + * For BIOSCALL(0x13): + * rAH 0x02 + * rAL number of sectors to read (1) + * rCH low 8 bits of cylinder + * rCL high 2 bits of cylinder (7-6), sector (5-0) + * rDH head + * rDL drive + * rES:rBX buffer address + */ +TEXT BIOSread(SB), $0 + LWI(5, rDI) /* retry count (ATAPI ZIPs suck) */ +_retry: + PUSHA /* may be trashed by BIOSCALL */ + PUSHR(rBX) + + LW(_trksize(SB), rBX) + LW(_nheads(SB), rDI) + IMUL(rDI, rBX) + OR(rBX, rBX) + JZ _ioerror + +_okay: + DIV(rBX) /* cylinder -> rAX, track,sector -> rDX */ + + MW(rAX, rCX) /* save cylinder */ + ROLI(0x08, rCX) /* swap rC[HL] */ + SHLBI(0x06, rCL) /* move high bits up */ + + MW(rDX, rAX) + CLR(rDX) + LW(_trksize(SB), rBX) + + DIV(rBX) /* head -> rAX, sector -> rDX */ + + INC(rDX) /* sector numbers are 1-based */ + ANDI(0x003F, rDX) /* should not be necessary */ + OR(rDX, rCX) + + MW(rAX, rDX) + SHLI(0x08, rDX) /* form head */ + LBPB(Xdrive, rDL) /* form drive */ + + POPR(rBX) + LWI(0x0201, rAX) /* form command and sectors */ + BIOSCALL(0x13) /* CF set on failure */ + JCC _BIOSreadret + + POPA + DEC(rDI) /* too many retries? */ + JEQ _ioerror + + CALL16(dreset(SB)) + JMP _retry + +_ioerror: + LWI(ioerror(SB), rSI) + CALL16(BIOSputs(SB)) + JMP _wait + +_BIOSreadret: + POPA + RET + +TEXT dreset(SB), $0 + PUSHA + CLR(rAX) /* rAH == 0 == reset disc system */ + LBPB(Xdrive, rDL) + BIOSCALL(0x13) + ORB(rAH, rAH) /* status (0 == success) */ + POPA + JNE _ioerror + RET + +/* + * Output a string to the display. + * String argument is in rSI. + */ +TEXT BIOSputs(SB), $0 + PUSHA + CLR(rBX) +_BIOSputs: + LODSB + ORB(rAL, rAL) + JEQ _BIOSputsret + + LBI(0x0E, rAH) + BIOSCALL(0x10) + JMP _BIOSputs + +_BIOSputsret: + POPA + RET + +/* "Bad format or I/O error\r\nPress almost any key to reboot..."*/ +TEXT error(SB), $0 + BYTE $'B'; BYTE $'a'; BYTE $'d'; BYTE $' '; + BYTE $'f'; BYTE $'o'; BYTE $'r'; BYTE $'m'; + BYTE $'a'; BYTE $'t'; BYTE $' '; BYTE $'o'; + BYTE $'r'; BYTE $' '; +/* "I/O error\r\nPress almost any key to reboot..." */ +TEXT ioerror(SB), $0 + BYTE $'I'; BYTE $'/'; BYTE $'O'; BYTE $' '; + BYTE $'e'; BYTE $'r'; BYTE $'r'; BYTE $'o'; + BYTE $'r'; BYTE $'\r';BYTE $'\n'; + BYTE $'P'; BYTE $'r'; BYTE $'e'; BYTE $'s'; + BYTE $'s'; BYTE $' '; BYTE $'a'; BYTE $' '; + BYTE $'k'; BYTE $'e'; BYTE $'y'; + BYTE $' '; BYTE $'t'; BYTE $'o'; BYTE $' '; + BYTE $'r'; BYTE $'e'; BYTE $'b'; BYTE $'o'; + BYTE $'o'; BYTE $'t'; + BYTE $'.'; BYTE $'.'; BYTE $'.'; + BYTE $'\z'; + +#ifdef USEBCOM +/* "B COM" */ +TEXT bootfile(SB), $0 + BYTE $'B'; BYTE $' '; BYTE $' '; BYTE $' '; + BYTE $' '; BYTE $' '; BYTE $' '; BYTE $' '; + BYTE $'C'; BYTE $'O'; BYTE $'M'; + BYTE $'\z'; +#else +/* "9LOAD " */ +TEXT bootfile(SB), $0 + BYTE $'9'; BYTE $'L'; BYTE $'O'; BYTE $'A'; + BYTE $'D'; BYTE $' '; BYTE $' '; BYTE $' '; + BYTE $' '; BYTE $' '; BYTE $' '; + BYTE $'\z'; +#endif /* USEBCOM */ + +/* "PBS..." */ +TEXT confidence(SB), $0 + BYTE $'P'; BYTE $'B'; BYTE $'S'; BYTE $'.'; + BYTE $'.'; BYTE $'.'; + BYTE $'\z'; diff --git a/os/boot/pc/pbsdisk b/os/boot/pc/pbsdisk new file mode 100755 index 00000000..cae49ba7 Binary files /dev/null and b/os/boot/pc/pbsdisk differ diff --git a/os/boot/pc/pbsdisk.s b/os/boot/pc/pbsdisk.s new file mode 100644 index 00000000..f879e79e --- /dev/null +++ b/os/boot/pc/pbsdisk.s @@ -0,0 +1,344 @@ +/* + * Debugging boot sector. Reads the first directory + * sector from disk and displays it. + * + * It relies on the _volid field in the FAT header containing + * the LBA of the root directory. + */ +#include "x16.h" + +#define DIROFF 0x00200 /* where to read the root directory (offset) */ +#define LOADSEG (0x10000/16) /* where to load code (64KB) */ +#define LOADOFF 0 + +/* + * FAT directory entry. + */ +#define Dname 0x00 +#define Dext 0x08 +#define Dattr 0x0B +#define Dtime 0x16 +#define Ddate 0x18 +#define Dstart 0x1A +#define Dlengthlo 0x1C +#define Dlengthhi 0x1E + +#define Dirsz 0x20 + +/* + * We keep data on the stack, indexed by rBP. + */ +#define Xdrive 0x00 /* boot drive, passed by BIOS in rDL */ +#define Xrootlo 0x02 /* offset of root directory */ +#define Xroothi 0x04 +#define Xrootsz 0x06 /* file data area */ +#define Xtotal 0x08 /* sum of allocated data above */ +#define Xdap 0x00 /* disc address packet */ + +TEXT _magic(SB), $0 + BYTE $0xEB; BYTE $0x3C; /* jmp .+ 0x3C (_start0x3E) */ + BYTE $0x90 /* nop */ +TEXT _version(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00 +TEXT _sectsize(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _clustsize(SB), $0 + BYTE $0x00 +TEXT _nresrv(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _nfats(SB), $0 + BYTE $0x00 +TEXT _rootsize(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _volsize(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _mediadesc(SB), $0 + BYTE $0x00 +TEXT _fatsize(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _trksize(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _nheads(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _nhiddenlo(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _nhiddenhi(SB), $0 + BYTE $0x00; BYTE $0x00; +TEXT _bigvolsize(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; +TEXT _driveno(SB), $0 + BYTE $0x00 +TEXT _reserved0(SB), $0 + BYTE $0x00 +TEXT _bootsig(SB), $0 + BYTE $0x00 +TEXT _volid(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; +TEXT _label(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00 + BYTE $0x00; BYTE $0x00; BYTE $0x00 +TEXT _type(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; + +_start0x3E: + CLI + CLR(rAX) + MTSR(rAX, rSS) /* 0000 -> rSS */ + MTSR(rAX, rDS) /* 0000 -> rDS, source segment */ + MTSR(rAX, rES) + LWI(_magic-Xtotal(SB), rSP) + MW(rSP, rBP) /* set the indexed-data pointer */ + SBPB(rDL, Xdrive) /* save the boot drive */ + + /* VMware starts us at 7C0:0. Move to 0:7C00 */ + PUSHR(rAX) + LWI(_nxt(SB), rAX) + PUSHR(rAX) + BYTE $0xCB /* FAR RET */ + +TEXT _nxt(SB), $0 + STI + LWI(confidence(SB), rSI) /* for that warm, fuzzy feeling */ + CALL16(BIOSputs(SB)) + + CALL16(dreset(SB)) + +_jmp00: + LW(_volid(SB), rAX) /* Xrootlo */ + LW(_volid+2(SB), rDX) /* Xroothi */ + + LWI(_magic+DIROFF(SB), rBX) + CALL16(BIOSread(SB)) /* read the root directory */ + + CALL16(printnl(SB)) + LWI(_magic+DIROFF(SB), rBX) + LWI((512/2), rCX) + CALL16(printbuf(SB)) + +xloop: + JMP xloop + + +TEXT buggery(SB), $0 + LWI(error(SB), rSI) + CALL16(BIOSputs(SB)) + +TEXT quietbuggery(SB), $0 +xbuggery: + JMP xbuggery + +/* + * Read a sector from a disc. On entry: + * rDX:rAX sector number + * rES:rBX buffer address + * For BIOSCALL(0x13): + * rAH 0x02 + * rAL number of sectors to read (1) + * rCH low 8 bits of cylinder + * rCL high 2 bits of cylinder (7-6), sector (5-0) + * rDH head + * rDL drive + * rES:rBX buffer address + */ +TEXT BIOSread(SB), $0 + LWI(5, rDI) /* retry count (ATAPI ZIPs suck) */ +_retry: + PUSHA /* may be trashed by BIOSCALL */ + PUSHR(rBX) + + LW(_trksize(SB), rBX) + LW(_nheads(SB), rDI) + IMUL(rDI, rBX) + OR(rBX, rBX) + JZ _ioerror + +_okay: + DIV(rBX) /* cylinder -> rAX, track,sector -> rDX */ + + MW(rAX, rCX) /* save cylinder */ + ROLI(0x08, rCX) /* swap rC[HL] */ + SHLBI(0x06, rCL) /* move high bits up */ + + MW(rDX, rAX) + CLR(rDX) + LW(_trksize(SB), rBX) + + DIV(rBX) /* head -> rAX, sector -> rDX */ + + INC(rDX) /* sector numbers are 1-based */ + ANDI(0x003F, rDX) /* should not be necessary */ + OR(rDX, rCX) + + MW(rAX, rDX) + SHLI(0x08, rDX) /* form head */ + LBPB(Xdrive, rDL) /* form drive */ + + POPR(rBX) + LWI(0x0201, rAX) /* form command and sectors */ + BIOSCALL(0x13) /* CF set on failure */ + JCC _BIOSreadret + + POPA + DEC(rDI) /* too many retries? */ + JEQ _ioerror + + CALL16(dreset(SB)) + JMP _retry + +_ioerror: + LWI(ioerror(SB), rSI) + CALL16(BIOSputs(SB)) + JMP xbuggery + +_BIOSreadret: + POPA + RET + +TEXT dreset(SB), $0 + PUSHA + CLR(rAX) /* rAH == 0 == reset disc system */ + LBPB(Xdrive, rDL) + BIOSCALL(0x13) + ORB(rAH, rAH) /* status (0 == success) */ + POPA + JNE _ioerror + RET + +TEXT printsharp(SB), $0 + LWI(sharp(SB), rSI) +_doprint: + CALL16(BIOSputs(SB)) + RET + +TEXT printspace(SB), $0 + LWI(space(SB), rSI) + JMP _doprint + +TEXT printnl(SB), $0 + LWI(nl(SB), rSI) + JMP _doprint + +/* + * Output a string to the display. + * String argument is in rSI. + */ +TEXT BIOSputs(SB), $0 + PUSHA + CLR(rBX) +_BIOSputs: + LODSB + ORB(rAL, rAL) + JEQ _BIOSputsret + + LBI(0x0E, rAH) + BIOSCALL(0x10) + JMP _BIOSputs + +_BIOSputsret: + POPA + RET + +/* + * Output a register to the display. + */ +TEXT printAX(SB), $0 + PUSHW(rAX) + PUSHW(rBX) + PUSHW(rCX) + PUSHW(rDI) + + LWI(4, rCX) + LWI(numbuf+4(SB), rSI) + +_nextchar: + DEC(rSI) + MW(rAX, rBX) + ANDI(0x000F, rBX) + ADDI(0x30, rBX) /* 0x30 = '0' */ + CMPI(0x39, rBX) /* 0x39 = '9' */ + JLE _dowrite + ADDI(0x07, rBX) /* 0x07 = 'A'-(1+'9')*/ + +_dowrite: + SXB(rBL, 0, xSI) + SHRI(4, rAX) + + DEC(rCX) + JNE _nextchar + + LWI(numbuf(SB), rSI) + CALL16(BIOSputs(SB)) + + POPW(rDI) + POPW(rCX) + POPW(rBX) + POPW(rAX) + + CALL16(printspace(SB)) + RET + +TEXT printDXAX(SB), $0 + PUSHW(rAX) + MW(rDX, rAX) + CALL16(printAX(SB)) + POPW(rAX) + CALL16(printAX(SB)) + RET + +TEXT printBX(SB), $0 + PUSHW(rAX) + MW(rBX, rAX) + CALL16(printAX(SB)) + POPW(rAX) + RET + +/* + * Output some number of words to the display + * rDS:rDI - buffer + * rCX: number of words + */ +TEXT printbuf(SB), $0 + PUSHW(rAX) + PUSHW(rBX) + PUSHW(rCX) + +_nextword: + LXW(0, xBX, rAX) + CALL16(printAX(SB)) + INC(rBX) + INC(rBX) + DEC(rCX) + JNE _nextword + + POPW(rCX) + POPW(rBX) + POPW(rAX) + RET + +TEXT error(SB), $0 + BYTE $'E'; + +TEXT ioerror(SB), $0 + BYTE $'I'; + +TEXT nl(SB), $0 + BYTE $'\r'; + BYTE $'\n'; + BYTE $'\z'; + +TEXT numbuf(SB), $0 + BYTE $'X'; BYTE $'X'; BYTE $'X'; BYTE $'X'; + BYTE $'\z'; + +TEXT space(SB), $0 + BYTE $' '; + BYTE $'\z'; + +TEXT sharp(SB), $0 + BYTE $'#'; BYTE $'\z'; + +TEXT confidence(SB), $0 + BYTE $'P'; BYTE $'\z' diff --git a/os/boot/pc/pbsdisklba b/os/boot/pc/pbsdisklba new file mode 100755 index 00000000..45c2ab81 Binary files /dev/null and b/os/boot/pc/pbsdisklba differ diff --git a/os/boot/pc/pbsdisklba.s b/os/boot/pc/pbsdisklba.s new file mode 100644 index 00000000..357abd00 --- /dev/null +++ b/os/boot/pc/pbsdisklba.s @@ -0,0 +1,327 @@ +/* + * Debugging boot sector. Reads the first directory + * sector from disk and displays it. + * + * It relies on the _volid field in the FAT header containing + * the LBA of the root directory. + */ +#include "x16.h" + +#define DIROFF 0x00200 /* where to read the root directory (offset) */ +#define LOADSEG (0x10000/16) /* where to load code (64KB) */ +#define LOADOFF 0 + +/* + * FAT directory entry. + */ +#define Dname 0x00 +#define Dext 0x08 +#define Dattr 0x0B +#define Dtime 0x16 +#define Ddate 0x18 +#define Dstart 0x1A +#define Dlengthlo 0x1C +#define Dlengthhi 0x1E + +#define Dirsz 0x20 + +/* + * We keep data on the stack, indexed by rBP. + */ +#define Xdrive 0x00 /* boot drive, passed by BIOS in rDL */ +#define Xrootlo 0x02 /* offset of root directory */ +#define Xroothi 0x04 +#define Xrootsz 0x06 /* file data area */ +#define Xtotal 0x08 /* sum of allocated data above */ +#define Xdap 0x00 /* disc address packet */ + +TEXT _magic(SB), $0 + BYTE $0xEB; BYTE $0x3C; /* jmp .+ 0x3C (_start0x3E) */ + BYTE $0x90 /* nop */ +TEXT _version(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00 +TEXT _sectsize(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _clustsize(SB), $0 + BYTE $0x00 +TEXT _nresrv(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _nfats(SB), $0 + BYTE $0x00 +TEXT _rootsize(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _volsize(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _mediadesc(SB), $0 + BYTE $0x00 +TEXT _fatsize(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _trksize(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _nheads(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _nhiddenlo(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _nhiddenhi(SB), $0 + BYTE $0x00; BYTE $0x00; +TEXT _bigvolsize(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; +TEXT _driveno(SB), $0 + BYTE $0x00 +TEXT _reserved0(SB), $0 + BYTE $0x00 +TEXT _bootsig(SB), $0 + BYTE $0x00 +TEXT _volid(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; +TEXT _label(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00 + BYTE $0x00; BYTE $0x00; BYTE $0x00 +TEXT _type(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; + +_start0x3E: + CLI + CLR(rAX) + MTSR(rAX, rSS) /* 0000 -> rSS */ + MTSR(rAX, rDS) /* 0000 -> rDS, source segment */ + MTSR(rAX, rES) + LWI(_magic-Xtotal(SB), rSP) + MW(rSP, rBP) /* set the indexed-data pointer */ + + SBPB(rDL, Xdrive) /* save the boot drive */ + + STI + + LWI(confidence(SB), rSI) /* for that warm, fuzzy feeling */ + CALL(BIOSputs(SB)) + + LBI(0x41, rAH) /* check extensions present */ + LWI(0x55AA, rBX) + LXB(Xdrive, xBP, rDL) /* drive */ + SYSCALL(0x13) /* CF set on failure */ + JCS _jmp01 + CMPI(0xAA55, rBX) + JNE _jmp01 + ANDI(0x0001, rCX) + JEQ _jmp01 + + /* rCX contains 0x0001 */ + SBPWI(0x0010, Xdap+0) /* reserved + packet size */ + SBPWI(rCX, Xdap+2) /* reserved + # of blocks to transfer */ + + DEC(rCX) + SBPW(rCX, Xdap+12) + SBPW(rCX, Xdap+14) + +/* BIOSread will do this CALL(dreset(SB)) */ + +_jmp00: + LW(_volid(SB), rAX) /* Xrootlo */ + LW(_volid+2(SB), rDX) /* Xroothi */ + + LWI(_magic+DIROFF(SB), rBX) + CALL(BIOSread(SB)) /* read the root directory */ + + CALL(printnl(SB)) + LWI(_magic+DIROFF(SB), rBX) + LWI((512/2), rCX) + CALL(printbuf(SB)) + +xloop: + JMP xloop + + +_jmp01: + +TEXT buggery(SB), $0 + LWI(error(SB), rSI) + CALL(BIOSputs(SB)) + +xbuggery: + JMP xbuggery + +/* + * Read a sector from a disc. On entry: + * rDX:rAX sector number + * rES:rBX buffer address + */ +TEXT BIOSread(SB), $0 + LWI(5, rDI) /* retry count (ATAPI ZIPs suck) */ +_retry: + PUSHA /* may be trashed by SYSCALL */ + + SBPW(rBX, Xdap+4) /* transfer buffer :offset */ + MFSR(rES, rDI) /* transfer buffer seg: */ + SBPW(rDI, Xdap+6) + SBPW(rAX, Xdap+8) /* LBA (64-bits) */ + SBPW(rDX, Xdap+10) + + MW(rBP, rSI) /* disk address packet */ + LBI(0x42, rAH) /* extended read */ + LBPB(Xdrive, rDL) /* form drive */ + SYSCALL(0x13) /* CF set on failure */ + JCC _BIOSreadret + + POPA + DEC(rDI) /* too many retries? */ + JEQ _ioerror + + CALL(dreset(SB)) + JMP _retry + +_ioerror: + LWI(ioerror(SB), rSI) + CALL(BIOSputs(SB)) + JMP xbuggery + +_BIOSreadret: + POPA + RET + +TEXT dreset(SB), $0 + PUSHA + CLR(rAX) /* rAH == 0 == reset disc system */ + LBPB(Xdrive, rDL) + SYSCALL(0x13) + ORB(rAH, rAH) /* status (0 == success) */ + POPA + JNE _ioerror + RET + + +TEXT printsharp(SB), $0 + LWI(sharp(SB), rSI) +_doprint: + CALL(BIOSputs(SB)) + RET + +TEXT printspace(SB), $0 + LWI(space(SB), rSI) + JMP _doprint + +TEXT printnl(SB), $0 + LWI(nl(SB), rSI) + JMP _doprint + +/* + * Output a string to the display. + * String argument is in rSI. + */ +TEXT BIOSputs(SB), $0 + PUSHA + CLR(rBX) +_BIOSputs: + LODSB + ORB(rAL, rAL) + JEQ _BIOSputsret + + LBI(0x0E, rAH) + SYSCALL(0x10) + JMP _BIOSputs + +_BIOSputsret: + POPA + RET + +/* + * Output a register to the display. + */ +TEXT printAX(SB), $0 + PUSHW(rAX) + PUSHW(rBX) + PUSHW(rCX) + PUSHW(rDI) + + LWI(4, rCX) + LWI(numbuf+4(SB), rSI) + +_nextchar: + DEC(rSI) + MW(rAX, rBX) + ANDI(0x000F, rBX) + ADDI(0x30, rBX) /* 0x30 = '0' */ + CMPI(0x39, rBX) /* 0x39 = '9' */ + JLE _dowrite + ADDI(0x07, rBX) /* 0x07 = 'A'-(1+'9')*/ + +_dowrite: + SXB(rBL, 0, xSI) + SHRI(4, rAX) + + DEC(rCX) + JNE _nextchar + + LWI(numbuf(SB), rSI) + CALL(BIOSputs(SB)) + + POPW(rDI) + POPW(rCX) + POPW(rBX) + POPW(rAX) + + CALL(printspace(SB)) + RET + +TEXT printDXAX(SB), $0 + PUSHW(rAX) + MW(rDX, rAX) + CALL(printAX(SB)) + POPW(rAX) + CALL(printAX(SB)) + RET + +TEXT printBX(SB), $0 + PUSHW(rAX) + MW(rBX, rAX) + CALL(printAX(SB)) + POPW(rAX) + RET + +/* + * Output some number of words to the display + * rDS:rDI - buffer + * rCX: number of words + */ +TEXT printbuf(SB), $0 + PUSHW(rAX) + PUSHW(rBX) + PUSHW(rCX) + +_nextword: + LXW(0, xBX, rAX) + CALL(printAX(SB)) + INC(rBX) + INC(rBX) + DEC(rCX) + JNE _nextword + + POPW(rCX) + POPW(rBX) + POPW(rAX) + RET + +TEXT error(SB), $0 + BYTE $'E'; + +TEXT ioerror(SB), $0 + BYTE $'I'; + +TEXT nl(SB), $0 + BYTE $'\r'; + BYTE $'\n'; + BYTE $'\z'; + +TEXT numbuf(SB), $0 + BYTE $'X'; BYTE $'X'; BYTE $'X'; BYTE $'X'; + BYTE $'\z'; + +TEXT space(SB), $0 + BYTE $' '; + BYTE $'\z'; + +TEXT sharp(SB), $0 + BYTE $'#'; BYTE $'\z'; diff --git a/os/boot/pc/pbslba.s b/os/boot/pc/pbslba.s new file mode 100644 index 00000000..56388868 --- /dev/null +++ b/os/boot/pc/pbslba.s @@ -0,0 +1,362 @@ +/* + * FAT Partition Boot Sector. Loaded at 0x7C00: + * 8a pbslba.s; 8l -o pbslba -l -H3 -T0x7C00 pbslba.8 + * Will load the target at LOADSEG*16+LOADOFF, so the target + * should be probably be loaded with LOADOFF added to the + * -Taddress. + * If LOADSEG is a multiple of 64KB and LOADOFF is 0 then + * targets larger than 64KB can be loaded. + * + * This code is uses Enhanced BIOS Services for Disc Drives and + * can be used with discs up to 137GB in capacity. + * + * It relies on the _volid field in the FAT header containing + * the LBA of the root directory. + */ +#include "x16.h" +#include "mem.h" + +#define LOADSEG (0x10000/16) /* where to load code (64KB) */ +#define LOADOFF 0 +#define DIROFF 0x0200 /* where to read the root directory */ + +/* + * FAT directory entry. + */ +#define Dname 0x00 +#define Dext 0x08 +#define Dattr 0x0B +#define Dtime 0x16 +#define Ddate 0x18 +#define Dstart 0x1A +#define Dlengthlo 0x1C +#define Dlengthhi 0x1E + +#define Dirsz 0x20 + +/* + * Data is kept on the stack, indexed by rBP. + */ +#define Xdap 0x00 /* disc address packet */ +#define Xrootsz 0x10 /* file data area */ +#define Xdrive 0x12 /* boot drive, passed by BIOS or MBR */ +#define Xtotal 0x14 /* sum of allocated data above */ + +TEXT _magic(SB), $0 + BYTE $0xEB; BYTE $0x3C; /* jmp .+ 0x3C (_start0x3E) */ + BYTE $0x90 /* nop */ +TEXT _version(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00 +TEXT _sectsize(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _clustsize(SB), $0 + BYTE $0x00 +TEXT _nresrv(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _nfats(SB), $0 + BYTE $0x00 +TEXT _rootsize(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _volsize(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _mediadesc(SB), $0 + BYTE $0x00 +TEXT _fatsize(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _trksize(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _nheads(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _nhiddenlo(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _nhiddenhi(SB), $0 + BYTE $0x00; BYTE $0x00; +TEXT _bigvolsize(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; +TEXT _driveno(SB), $0 + BYTE $0x00 +TEXT _reserved0(SB), $0 + BYTE $0x00 +TEXT _bootsig(SB), $0 + BYTE $0x00 +TEXT _volid(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; +TEXT _label(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00 + BYTE $0x00; BYTE $0x00; BYTE $0x00 +TEXT _type(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; + +_start0x3E: + CLI + CLR(rAX) + MTSR(rAX, rSS) /* 0000 -> rSS */ + MTSR(rAX, rDS) /* 0000 -> rDS, source segment */ + MTSR(rAX, rES) + LWI(_magic-Xtotal(SB), rSP) + MW(rSP, rBP) /* set the indexed-data pointer */ + + SBPB(rDL, Xdrive) /* save the boot drive */ + + /* booting from a CD starts us at 7C0:0. Move to 0:7C00 */ + PUSHR(rAX) + LWI(_nxt(SB), rAX) + PUSHR(rAX) + BYTE $0xCB /* FAR RET */ + +TEXT _nxt(SB), $0 + STI + + LWI(confidence(SB), rSI) /* for that warm, fuzzy feeling */ + CALL16(BIOSputs(SB)) + + LBI(0x41, rAH) /* check extensions present */ + LWI(0x55AA, rBX) + LXB(Xdrive, xBP, rDL) /* drive */ + BIOSCALL(0x13) /* CF set on failure */ + JCS _jmp01 + CMPI(0xAA55, rBX) + JNE _jmp01 + ANDI(0x0001, rCX) + JEQ _jmp01 + + /* rCX contains 0x0001 */ + SBPWI(0x0010, Xdap+0) /* reserved + packet size */ + SBPWI(rCX, Xdap+2) /* reserved + # of blocks to transfer */ + + DEC(rCX) + SBPW(rCX, Xdap+12) + SBPW(rCX, Xdap+14) + + CALL16(dreset(SB)) + +_jmp00: + LW(_volid(SB), rAX) /* Xrootlo */ + LW(_volid+2(SB), rDX) /* Xroothi */ + + LWI(_magic+DIROFF(SB), rBX) + CALL16(BIOSread(SB)) /* read the root directory */ + + LWI((512/Dirsz), rBX) + + LWI(_magic+DIROFF(SB), rDI) /* compare first directory entry */ + +_cmp00: + PUSHR(rDI) /* save for later if it matches */ + LWI(bootfile(SB), rSI) + LWI(Dattr, rCX) + REP + CMPSB + POPR(rDI) + JEQ _jmp02 + + DEC(rBX) + JEQ _jmp01 + + ADDI(Dirsz, rDI) + JMP _cmp00 +_jmp01: + CALL16(buggery(SB)) + +_jmp02: + CLR(rBX) /* a handy value */ + LW(_rootsize(SB), rAX) /* calculate and save Xrootsz */ + LWI(Dirsz, rCX) + MUL(rCX) + LW(_sectsize(SB), rCX) + PUSHR(rCX) + DEC(rCX) + ADD(rCX, rAX) + ADC(rBX, rDX) + POPR(rCX) /* _sectsize(SB) */ + DIV(rCX) + PUSHR(rAX) /* Xrootsz */ + + /* + * rDI points to the matching directory entry. + */ + LXW(Dstart, xDI, rAX) /* starting sector address */ + DEC(rAX) /* that's just the way it is */ + DEC(rAX) + LB(_clustsize(SB), rCL) + CLRB(rCH) + MUL(rCX) + LW(_volid(SB), rCX) /* Xrootlo */ + ADD(rCX, rAX) + LW(_volid+2(SB), rCX) /* Xroothi */ + ADC(rCX, rDX) + POPR(rCX) /* Xrootsz */ + ADD(rCX, rAX) + ADC(rBX, rDX) + + PUSHR(rAX) /* calculate how many sectors to read */ + PUSHR(rDX) + LXW(Dlengthlo, xDI, rAX) + LXW(Dlengthhi, xDI, rDX) + LW(_sectsize(SB), rCX) + PUSHR(rCX) + DEC(rCX) + ADD(rCX, rAX) + ADC(rBX, rDX) + POPR(rCX) /* _sectsize(SB) */ + DIV(rCX) + MW(rAX, rCX) + POPR(rDX) + POPR(rAX) + + LWI(LOADSEG, rBX) /* address to load into (seg+offset) */ + MTSR(rBX, rES) /* seg */ + LWI(LOADOFF, rBX) /* offset */ + +_readboot: + CALL16(BIOSread(SB)) /* read the sector */ + + LW(_sectsize(SB), rDI) /* bump addresses/counts */ + ADD(rDI, rBX) + JCC _incsecno + + MFSR(rES, rDI) /* next 64KB segment */ + ADDI(0x1000, rDI) + MTSR(rDI, rES) + +_incsecno: + CLR(rDI) + INC(rAX) + ADC(rDI, rDX) + LOOP _readboot + + LWI(LOADSEG, rDI) /* set rDS for loaded code */ + MTSR(rDI, rDS) + FARJUMP16(LOADSEG, LOADOFF) /* no deposit, no return */ + +TEXT buggery(SB), $0 + LWI(error(SB), rSI) + CALL16(BIOSputs(SB)) + +_wait: + CLR(rAX) /* wait for almost any key */ + BIOSCALL(0x16) + +_reset: + CLR(rBX) /* set ES segment for BIOS area */ + MTSR(rBX, rES) + + LWI(0x0472, rBX) /* warm-start code address */ + LWI(0x1234, rAX) /* warm-start code */ + POKEW /* MOVW AX, ES:[BX] */ + + FARJUMP16(0xFFFF, 0x0000) /* reset */ + + +/* + * Read a sector from a disc. On entry: + * rDX:rAX sector number + * rES:rBX buffer address + */ +TEXT BIOSread(SB), $0 + LWI(5, rDI) /* retry count (ATAPI ZIPs suck) */ +_retry: + PUSHA /* may be trashed by BIOSCALL */ + + SBPW(rBX, Xdap+4) /* transfer buffer :offset */ + MFSR(rES, rDI) /* transfer buffer seg: */ + SBPW(rDI, Xdap+6) + SBPW(rAX, Xdap+8) /* LBA (64-bits) */ + SBPW(rDX, Xdap+10) + + MW(rBP, rSI) /* disk address packet */ + LBI(0x42, rAH) /* extended read */ + LBPB(Xdrive, rDL) /* form drive */ + BIOSCALL(0x13) /* CF set on failure */ + JCC _BIOSreadret + + POPA + DEC(rDI) /* too many retries? */ + JEQ _ioerror + + CALL16(dreset(SB)) + JMP _retry + +_ioerror: + LWI(ioerror(SB), rSI) + CALL16(BIOSputs(SB)) + JMP _wait + +_BIOSreadret: + POPA + RET + +TEXT dreset(SB), $0 + PUSHA + CLR(rAX) /* rAH == 0 == reset disc system */ + LBPB(Xdrive, rDL) + BIOSCALL(0x13) + ORB(rAH, rAH) /* status (0 == success) */ + POPA + JNE _ioerror + RET + +/* + * Output a string to the display. + * String argument is in rSI. + */ +TEXT BIOSputs(SB), $0 + PUSHA + CLR(rBX) +_BIOSputs: + LODSB + ORB(rAL, rAL) + JEQ _BIOSputsret + + LBI(0x0E, rAH) + BIOSCALL(0x10) + JMP _BIOSputs + +_BIOSputsret: + POPA + RET + +/* "Bad format or I/O error\r\nPress almost any key to reboot..." */ +TEXT error(SB), $0 + BYTE $'B'; BYTE $'a'; BYTE $'d'; BYTE $' '; + BYTE $'f'; BYTE $'o'; BYTE $'r'; BYTE $'m'; + BYTE $'a'; BYTE $'t'; BYTE $' '; BYTE $'o'; + BYTE $'r'; BYTE $' '; +/* "I/O error\r\nPress almost any key to reboot..." */ +TEXT ioerror(SB), $0 + BYTE $'I'; BYTE $'/'; BYTE $'O'; BYTE $' '; + BYTE $'e'; BYTE $'r'; BYTE $'r'; BYTE $'o'; + BYTE $'r'; BYTE $'\r';BYTE $'\n'; + BYTE $'P'; BYTE $'r'; BYTE $'e'; BYTE $'s'; + BYTE $'s'; BYTE $' '; BYTE $'a'; BYTE $' '; + BYTE $'k'; BYTE $'e'; BYTE $'y'; + BYTE $' '; BYTE $'t'; BYTE $'o'; BYTE $' '; + BYTE $'r'; BYTE $'e'; BYTE $'b'; BYTE $'o'; + BYTE $'o'; BYTE $'t'; + BYTE $'.'; BYTE $'.'; BYTE $'.'; + BYTE $'\z'; + +#ifdef USEBCOM +/* "B COM" */ +TEXT bootfile(SB), $0 + BYTE $'B'; BYTE $' '; BYTE $' '; BYTE $' '; + BYTE $' '; BYTE $' '; BYTE $' '; BYTE $' '; + BYTE $'C'; BYTE $'O'; BYTE $'M'; + BYTE $'\z'; +#else +/* "9LOAD " */ +TEXT bootfile(SB), $0 + BYTE $'9'; BYTE $'L'; BYTE $'O'; BYTE $'A'; + BYTE $'D'; BYTE $' '; BYTE $' '; BYTE $' '; + BYTE $' '; BYTE $' '; BYTE $' '; + BYTE $'\z'; +#endif /* USEBCOM */ + +/* "PBS..." */ +TEXT confidence(SB), $0 + BYTE $'P'; BYTE $'B'; BYTE $'S'; BYTE $'.'; + BYTE $'.'; BYTE $'.'; + BYTE $'\z'; diff --git a/os/boot/pc/pci.c b/os/boot/pc/pci.c new file mode 100644 index 00000000..c7b9eda5 --- /dev/null +++ b/os/boot/pc/pci.c @@ -0,0 +1,852 @@ +/* + * PCI support code. + * To do: + * initialise bridge mappings if the PCI BIOS didn't. + */ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "error.h" + +enum { /* configuration mechanism #1 */ + PciADDR = 0xCF8, /* CONFIG_ADDRESS */ + PciDATA = 0xCFC, /* CONFIG_DATA */ + + /* configuration mechanism #2 */ + PciCSE = 0xCF8, /* configuration space enable */ + PciFORWARD = 0xCFA, /* which bus */ + + MaxFNO = 7, + MaxUBN = 255, +}; + +static Lock pcicfglock; +static Lock pcicfginitlock; +static int pcicfgmode = -1; +static int pcimaxbno = 7; +static int pcimaxdno; +static Pcidev* pciroot; +static Pcidev* pcilist; +static Pcidev* pcitail; + +static int pcicfgrw32(int, int, int, int); +static int pcicfgrw8(int, int, int, int); + +ulong +pcibarsize(Pcidev *p, int rno) +{ + ulong v, size; + + v = pcicfgrw32(p->tbdf, rno, 0, 1); + pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0); + size = pcicfgrw32(p->tbdf, rno, 0, 1); + if(v & 1) + size |= 0xFFFF0000; + pcicfgrw32(p->tbdf, rno, v, 0); + + return -(size & ~0x0F); +} + +int +pciscan(int bno, Pcidev** list) +{ + Pcidev *p, *head, *tail; + int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn; + + maxubn = bno; + head = nil; + tail = nil; + for(dno = 0; dno <= pcimaxdno; dno++){ + maxfno = 0; + for(fno = 0; fno <= maxfno; fno++){ + /* + * For this possible device, form the + * bus+device+function triplet needed to address it + * and try to read the vendor and device ID. + * If successful, allocate a device struct and + * start to fill it in with some useful information + * from the device's configuration space. + */ + tbdf = MKBUS(BusPCI, bno, dno, fno); + l = pcicfgrw32(tbdf, PciVID, 0, 1); + if(l == 0xFFFFFFFF || l == 0) + continue; + p = malloc(sizeof(*p)); + p->tbdf = tbdf; + p->vid = l; + p->did = l>>16; + + if(pcilist != nil) + pcitail->list = p; + else + pcilist = p; + pcitail = p; + + p->rid = pcicfgr8(p, PciRID); + p->ccrp = pcicfgr8(p, PciCCRp); + p->ccru = pcicfgr8(p, PciCCRu); + p->ccrb = pcicfgr8(p, PciCCRb); + p->pcr = pcicfgr32(p, PciPCR); + + p->intl = pcicfgr8(p, PciINTL); + + /* + * If the device is a multi-function device adjust the + * loop count so all possible functions are checked. + */ + hdt = pcicfgr8(p, PciHDT); + if(hdt & 0x80) + maxfno = MaxFNO; + + /* + * If appropriate, read the base address registers + * and work out the sizes. + */ + switch(p->ccrb){ + + case 0x01: /* mass storage controller */ + case 0x02: /* network controller */ + case 0x03: /* display controller */ + case 0x04: /* multimedia device */ + case 0x07: /* simple comm. controllers */ + case 0x08: /* base system peripherals */ + case 0x09: /* input devices */ + case 0x0A: /* docking stations */ + case 0x0B: /* processors */ + case 0x0C: /* serial bus controllers */ + if((hdt & 0x7F) != 0) + break; + rno = PciBAR0 - 4; + for(i = 0; i < nelem(p->mem); i++){ + rno += 4; + p->mem[i].bar = pcicfgr32(p, rno); + p->mem[i].size = pcibarsize(p, rno); + } + break; + + case 0x00: + case 0x05: /* memory controller */ + case 0x06: /* bridge device */ + default: + break; + } + + if(head != nil) + tail->link = p; + else + head = p; + tail = p; + } + } + + *list = head; + for(p = head; p != nil; p = p->link){ + /* + * Find PCI-PCI and PCI-Cardbus bridges and recursively descend the tree. + */ + if(p->ccrb != 0x06 || p->ccru != 0x04) + continue; + + /* + * If the secondary or subordinate bus number is not + * initialised try to do what the PCI BIOS should have + * done and fill in the numbers as the tree is descended. + * On the way down the subordinate bus number is set to + * the maximum as it's not known how many buses are behind + * this one; the final value is set on the way back up. + */ + ubn = pcicfgr8(p, PciUBN); + sbn = pcicfgr8(p, PciSBN); + + if(sbn == 0 || ubn == 0){ + sbn = maxubn+1; + /* + * Make sure memory, I/O and master enables are + * off, set the primary, secondary and subordinate + * bus numbers and clear the secondary status before + * attempting to scan the secondary bus. + * + * Initialisation of the bridge should be done here. + */ + pcicfgw32(p, PciPCR, 0xFFFF0000); + l = (MaxUBN<<16)|(sbn<<8)|bno; + pcicfgw32(p, PciPBN, l); + pcicfgw16(p, PciSPSR, 0xFFFF); + maxubn = pciscan(sbn, &p->bridge); + l = (maxubn<<16)|(sbn<<8)|bno; + + pcicfgw32(p, PciPBN, l); + } + else{ + maxubn = ubn; + pciscan(sbn, &p->bridge); + } + } + + return maxubn; +} + +static uchar +pIIx_link(Pcidev *router, uchar link) +{ + uchar pirq; + + /* link should be 0x60, 0x61, 0x62, 0x63 */ + pirq = pcicfgr8(router, link); + return (pirq < 16)? pirq: 0; +} + +static void +pIIx_init(Pcidev *router, uchar link, uchar irq) +{ + pcicfgw8(router, link, irq); +} + +static uchar +via_link(Pcidev *router, uchar link) +{ + uchar pirq; + + /* link should be 1, 2, 3, 5 */ + pirq = (link < 6)? pcicfgr8(router, 0x55 + (link>>1)): 0; + + return (link & 1)? (pirq >> 4): (pirq & 15); +} + +static void +via_init(Pcidev *router, uchar link, uchar irq) +{ + uchar pirq; + + pirq = pcicfgr8(router, 0x55 + (link >> 1)); + pirq &= (link & 1)? 0x0f: 0xf0; + pirq |= (link & 1)? (irq << 4): (irq & 15); + pcicfgw8(router, 0x55 + (link>>1), pirq); +} + +static uchar +opti_link(Pcidev *router, uchar link) +{ + uchar pirq = 0; + + /* link should be 0x02, 0x12, 0x22, 0x32 */ + if ((link & 0xcf) == 0x02) + pirq = pcicfgr8(router, 0xb8 + (link >> 5)); + return (link & 0x10)? (pirq >> 4): (pirq & 15); +} + +static void +opti_init(Pcidev *router, uchar link, uchar irq) +{ + uchar pirq; + + pirq = pcicfgr8(router, 0xb8 + (link >> 5)); + pirq &= (link & 0x10)? 0x0f : 0xf0; + pirq |= (link & 0x10)? (irq << 4): (irq & 15); + pcicfgw8(router, 0xb8 + (link >> 5), pirq); +} + +static uchar +ali_link(Pcidev *router, uchar link) +{ + /* No, you're not dreaming */ + static const uchar map[] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 }; + uchar pirq; + + /* link should be 0x01..0x08 */ + pirq = pcicfgr8(router, 0x48 + ((link-1)>>1)); + return (link & 1)? map[pirq&15]: map[pirq>>4]; +} + +static void +ali_init(Pcidev *router, uchar link, uchar irq) +{ + /* Inverse of map in ali_link */ + static const uchar map[] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 }; + uchar pirq; + + pirq = pcicfgr8(router, 0x48 + ((link-1)>>1)); + pirq &= (link & 1)? 0x0f: 0xf0; + pirq |= (link & 1)? (map[irq] << 4): (map[irq] & 15); + pcicfgw8(router, 0x48 + ((link-1)>>1), pirq); +} + +static uchar +cyrix_link(Pcidev *router, uchar link) +{ + uchar pirq; + + /* link should be 1, 2, 3, 4 */ + pirq = pcicfgr8(router, 0x5c + ((link-1)>>1)); + return ((link & 1)? pirq >> 4: pirq & 15); +} + +static void +cyrix_init(Pcidev *router, uchar link, uchar irq) +{ + uchar pirq; + + pirq = pcicfgr8(router, 0x5c + (link>>1)); + pirq &= (link & 1)? 0x0f: 0xf0; + pirq |= (link & 1)? (irq << 4): (irq & 15); + pcicfgw8(router, 0x5c + (link>>1), pirq); +} + +enum { + Intel = 0x8086, + Intel_82371FB_0 = 0x122e, + Intel_82371MX_0 = 0x1234, + Intel_82371SB_0 = 0x7000, + Intel_82371AB_0 = 0x7110, + Intel_82443MX_1 = 0x7198, + Intel_82801AA_0 = 0x2410, + Intel_82801AB_0 = 0x2420, + Intel_82801BA_0 = 0x2440, + Intel_82801BAM_0 = 0x244c, + Intel_82801CAM_0 = 0x248c, + Intel_82801DBM_0 = 0x24cc, + Intel_82801EB_0 = 0x24d0, + Intel_82801FB_0 = 0x2640, + Viatech = 0x1106, + Via_82C586_0 = 0x0586, + Via_82C596 = 0x0596, + Via_82C686 = 0x0686, + Opti = 0x1045, + Opti_82C700 = 0xc700, + Al = 0x10b9, + Al_M1533 = 0x1533, + SI = 0x1039, + SI_503 = 0x0008, + SI_496 = 0x0496, + Cyrix = 0x1078, + Cyrix_5530_Legacy = 0x0100, +}; + +typedef struct { + ushort sb_vid, sb_did; + uchar (*sb_translate)(Pcidev *, uchar); + void (*sb_initialize)(Pcidev *, uchar, uchar); +} bridge_t; + +static bridge_t southbridges[] = { +{ Intel, Intel_82371FB_0, pIIx_link, pIIx_init }, +{ Intel, Intel_82371MX_0, pIIx_link, pIIx_init }, +{ Intel, Intel_82371SB_0, pIIx_link, pIIx_init }, +{ Intel, Intel_82371AB_0, pIIx_link, pIIx_init }, +{ Intel, Intel_82443MX_1, pIIx_link, pIIx_init }, +{ Intel, Intel_82801AA_0, pIIx_link, pIIx_init }, +{ Intel, Intel_82801AB_0, pIIx_link, pIIx_init }, +{ Intel, Intel_82801BA_0, pIIx_link, pIIx_init }, +{ Intel, Intel_82801BAM_0, pIIx_link, pIIx_init }, +{ Intel, Intel_82801CAM_0, pIIx_link, pIIx_init }, +{ Intel, Intel_82801DBM_0, pIIx_link, pIIx_init }, +{ Intel, Intel_82801EB_0, pIIx_link, pIIx_init }, +{ Intel, Intel_82801FB_0, pIIx_link, pIIx_init }, +{ Viatech, Via_82C586_0, via_link, via_init }, +{ Viatech, Via_82C596, via_link, via_init }, +{ Viatech, Via_82C686, via_link, via_init }, +{ Opti, Opti_82C700, opti_link, opti_init }, +{ Al, Al_M1533, ali_link, ali_init }, +{ SI, SI_503, pIIx_link, pIIx_init }, +{ SI, SI_496, pIIx_link, pIIx_init }, +{ Cyrix, Cyrix_5530_Legacy, cyrix_link, cyrix_init } +}; + +typedef struct { + uchar e_bus; // Pci bus number + uchar e_dev; // Pci device number + uchar e_maps[12]; // Avoid structs! Link and mask. + uchar e_slot; // Add-in/built-in slot + uchar e_reserved; +} slot_t; + +typedef struct { + uchar rt_signature[4]; // Routing table signature + uchar rt_version[2]; // Version number + uchar rt_size[2]; // Total table size + uchar rt_bus; // Interrupt router bus number + uchar rt_devfn; // Router's devfunc + uchar rt_pciirqs[2]; // Exclusive PCI irqs + uchar rt_compat[4]; // Compatible PCI interrupt router + uchar rt_miniport[4]; // Miniport data + uchar rt_reserved[11]; + uchar rt_checksum; +} router_t; + +static ushort pciirqs; // Exclusive PCI irqs +static bridge_t *southbridge; // Which southbridge to use. + +static void +pcirouting(void) +{ + uchar *p, pin, irq; + ulong tbdf, vdid; + ushort vid, did; + router_t *r; + slot_t *e; + int size, i, fn; + Pcidev *sbpci, *pci; + + // Peek in the BIOS + for (p = (uchar *)KADDR(0xf0000); p < (uchar *)KADDR(0xfffff); p += 16) + if (p[0] == '$' && p[1] == 'P' && p[2] == 'I' && p[3] == 'R') + break; + + if (p >= (uchar *)KADDR(0xfffff)) + return; + + r = (router_t *)p; + + // print("PCI interrupt routing table version %d.%d at %.6uX\n", + // r->rt_version[0], r->rt_version[1], (ulong)r & 0xfffff); + + tbdf = (BusPCI << 24)|(r->rt_bus << 16)|(r->rt_devfn << 8); + vdid = pcicfgrw32(tbdf, PciVID, 0, 1); + vid = vdid; + did = vdid >> 16; + + for (i = 0; i != nelem(southbridges); i++) + if (vid == southbridges[i].sb_vid && did == southbridges[i].sb_did) + break; + + if (i == nelem(southbridges)) { + print("pcirouting: South bridge %.4uX, %.4uX not found\n", vid, did); + return; + } + southbridge = &southbridges[i]; + + if ((sbpci = pcimatch(nil, vid, did)) == nil) { + print("pcirouting: Cannot match south bridge %.4uX, %.4uX\n", + vid, did); + return; + } + + pciirqs = (r->rt_pciirqs[1] << 8)|r->rt_pciirqs[0]; + + size = (r->rt_size[1] << 8)|r->rt_size[0]; + for (e = (slot_t *)&r[1]; (uchar *)e < p + size; e++) { + // print("%.2uX/%.2uX %.2uX: ", e->e_bus, e->e_dev, e->e_slot); + // for (i = 0; i != 4; i++) { + // uchar *m = &e->e_maps[i * 3]; + // print("[%d] %.2uX %.4uX ", + // i, m[0], (m[2] << 8)|m[1]); + // } + // print("\n"); + + for (fn = 0; fn != 8; fn++) { + uchar *m; + + // Retrieve the did and vid through the devfn before + // obtaining the Pcidev structure. + tbdf = (BusPCI << 24)|(e->e_bus << 16)|((e->e_dev | fn) << 8); + vdid = pcicfgrw32(tbdf, PciVID, 0, 1); + if (vdid == 0xFFFFFFFF || vdid == 0) + continue; + + vid = vdid; + did = vdid >> 16; + + pci = nil; + while ((pci = pcimatch(pci, vid, did)) != nil) { + + if (pci->intl != 0 && pci->intl != 0xFF) + continue; + + pin = pcicfgr8(pci, PciINTP); + if (pin == 0 || pin == 0xff) + continue; + + m = &e->e_maps[(pin - 1) * 3]; + irq = southbridge->sb_translate(sbpci, m[0]); + if (irq) { + print("pcirouting: %.4uX/%.4uX at pin %d irq %d\n", + vid, did, pin, irq); + pcicfgw8(pci, PciINTL, irq); + pci->intl = irq; + } + } + } + } +} + +static void +pcicfginit(void) +{ + char *p; + int bno, n; + Pcidev **list; + + lock(&pcicfginitlock); + if(pcicfgmode != -1) + goto out; + + /* + * Try to determine which PCI configuration mode is implemented. + * Mode2 uses a byte at 0xCF8 and another at 0xCFA; Mode1 uses + * a DWORD at 0xCF8 and another at 0xCFC and will pass through + * any non-DWORD accesses as normal I/O cycles. There shouldn't be + * a device behind these addresses so if Mode1 accesses fail try + * for Mode2 (Mode2 is deprecated). + */ + + /* + * Bits [30:24] of PciADDR must be 0, + * according to the spec. + */ + n = inl(PciADDR); + if(!(n & 0x7FF00000)){ + outl(PciADDR, 0x80000000); + outb(PciADDR+3, 0); + if(inl(PciADDR) & 0x80000000){ + pcicfgmode = 1; + pcimaxdno = 31; + } + } + outl(PciADDR, n); + + if(pcicfgmode < 0){ + /* + * The 'key' part of PciCSE should be 0. + */ + n = inb(PciCSE); + if(!(n & 0xF0)){ + outb(PciCSE, 0x0E); + if(inb(PciCSE) == 0x0E){ + pcicfgmode = 2; + pcimaxdno = 15; + } + } + outb(PciCSE, n); + } + + if(pcicfgmode < 0) + goto out; + + + if(p = getconf("*pcimaxbno")) + pcimaxbno = strtoul(p, 0, 0); + if(p = getconf("*pcimaxdno")) + pcimaxdno = strtoul(p, 0, 0); + + list = &pciroot; + for(bno = 0; bno <= pcimaxbno; bno++) { + bno = pciscan(bno, list); + while(*list) + list = &(*list)->link; + } + + pcirouting(); + +out: + unlock(&pcicfginitlock); + + if(getconf("*pcihinv")) + pcihinv(nil); +} + + +static int +pcicfgrw8(int tbdf, int rno, int data, int read) +{ + int o, type, x; + + if(pcicfgmode == -1) + pcicfginit(); + + if(BUSBNO(tbdf)) + type = 0x01; + else + type = 0x00; + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + switch(pcicfgmode){ + + case 1: + o = rno & 0x03; + rno &= ~0x03; + outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type); + if(read) + x = inb(PciDATA+o); + else + outb(PciDATA+o, data); + outl(PciADDR, 0); + break; + + case 2: + outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1)); + outb(PciFORWARD, BUSBNO(tbdf)); + if(read) + x = inb((0xC000|(BUSDNO(tbdf)<<8)) + rno); + else + outb((0xC000|(BUSDNO(tbdf)<<8)) + rno, data); + outb(PciCSE, 0); + break; + } + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr8(Pcidev* pcidev, int rno) +{ + return pcicfgrw8(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw8(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw8(pcidev->tbdf, rno, data, 0); +} + +static int +pcicfgrw16(int tbdf, int rno, int data, int read) +{ + int o, type, x; + + if(pcicfgmode == -1) + pcicfginit(); + + if(BUSBNO(tbdf)) + type = 0x01; + else + type = 0x00; + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + switch(pcicfgmode){ + + case 1: + o = rno & 0x02; + rno &= ~0x03; + outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type); + if(read) + x = ins(PciDATA+o); + else + outs(PciDATA+o, data); + outl(PciADDR, 0); + break; + + case 2: + outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1)); + outb(PciFORWARD, BUSBNO(tbdf)); + if(read) + x = ins((0xC000|(BUSDNO(tbdf)<<8)) + rno); + else + outs((0xC000|(BUSDNO(tbdf)<<8)) + rno, data); + outb(PciCSE, 0); + break; + } + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr16(Pcidev* pcidev, int rno) +{ + return pcicfgrw16(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw16(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw16(pcidev->tbdf, rno, data, 0); +} + +static int +pcicfgrw32(int tbdf, int rno, int data, int read) +{ + int type, x; + + if(pcicfgmode == -1) + pcicfginit(); + + if(BUSBNO(tbdf)) + type = 0x01; + else + type = 0x00; + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + switch(pcicfgmode){ + + case 1: + rno &= ~0x03; + outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type); + if(read) + x = inl(PciDATA); + else + outl(PciDATA, data); + outl(PciADDR, 0); + break; + + case 2: + outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1)); + outb(PciFORWARD, BUSBNO(tbdf)); + if(read) + x = inl((0xC000|(BUSDNO(tbdf)<<8)) + rno); + else + outl((0xC000|(BUSDNO(tbdf)<<8)) + rno, data); + outb(PciCSE, 0); + break; + } + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr32(Pcidev* pcidev, int rno) +{ + return pcicfgrw32(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw32(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw32(pcidev->tbdf, rno, data, 0); +} + +Pcidev* +pcimatch(Pcidev* prev, int vid, int did) +{ + if(pcicfgmode == -1) + pcicfginit(); + + if(prev == nil) + prev = pcilist; + else + prev = prev->list; + + while(prev != nil) { + if((vid == 0 || prev->vid == vid) + && (did == 0 || prev->did == did)) + break; + prev = prev->list; + } + return prev; +} + +uchar +pciipin(Pcidev *pci, uchar pin) +{ + if (pci == nil) + pci = pcilist; + + while (pci) { + uchar intl; + + if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff) + return pci->intl; + + if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0) + return intl; + + pci = pci->list; + } + return 0; +} + +static ushort +pciimask(Pcidev *pci) +{ + ushort imask; + + imask = 0; + while (pci) { + if (pcicfgr8(pci, PciINTP) && pci->intl < 16) + imask |= 1 << pci->intl; + + if (pci->bridge) + imask |= pciimask(pci->bridge); + + pci = pci->list; + } + return imask; +} + +uchar +pciintl(Pcidev *pci) +{ + ushort imask; + int i; + + if (pci == nil) + pci = pcilist; + + imask = pciimask(pci) | 1; + for (i = 0; i != 16; i++) + if ((imask & (1 << i)) == 0) + return i; + return 0; +} + +void +pcihinv(Pcidev* p) +{ + int i; + Pcidev *t; + + if(pcicfgmode == -1) + pcicfginit(); + + if(p == nil) { + p = pciroot; + print("bus dev type vid did intl memory\n"); + } + for(t = p; t != nil; t = t->link) { + print("%d %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d ", + BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf), + t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl); + + for(i = 0; i < nelem(p->mem); i++) { + if(t->mem[i].size == 0) + continue; + print("%d:%.8lux %d ", i, + t->mem[i].bar, t->mem[i].size); + } + print("\n"); + } + while(p != nil) { + if(p->bridge != nil) + pcihinv(p->bridge); + p = p->link; + } +} + +void +pcireset(void) +{ + Pcidev *p; + int pcr; + + if(pcicfgmode == -1) + pcicfginit(); + + for(p = pcilist; p != nil; p = p->list){ + pcr = pcicfgr16(p, PciPSR); + pcicfgw16(p, PciPSR, pcr & ~0x04); + } +} + +void +pcisetbme(Pcidev* p) +{ + int pcr; + + pcr = pcicfgr16(p, PciPCR); + pcr |= 0x0004; + pcicfgw16(p, PciPCR, pcr); +} + diff --git a/os/boot/pc/print.c b/os/boot/pc/print.c new file mode 100644 index 00000000..f3137f55 --- /dev/null +++ b/os/boot/pc/print.c @@ -0,0 +1,31 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +static Lock fmtl; + +void +_fmtlock(void) +{ + lock(&fmtl); +} + +void +_fmtunlock(void) +{ + unlock(&fmtl); +} + +int +_efgfmt(Fmt*) +{ + return -1; +} + +int +errfmt(Fmt*) +{ + return -1; +} diff --git a/os/boot/pc/queue.c b/os/boot/pc/queue.c new file mode 100644 index 00000000..d2a529cd --- /dev/null +++ b/os/boot/pc/queue.c @@ -0,0 +1,44 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +int +qgetc(IOQ *q) +{ + int c; + + if(q->in == q->out) + return -1; + c = *q->out; + if(q->out == q->buf+sizeof(q->buf)-1) + q->out = q->buf; + else + q->out++; + return c; +} + +static int +qputc(IOQ *q, int c) +{ + uchar *nextin; + if(q->in >= &q->buf[sizeof(q->buf)-1]) + nextin = q->buf; + else + nextin = q->in+1; + if(nextin == q->out) + return -1; + *q->in = c; + q->in = nextin; + return 0; +} + +void +qinit(IOQ *q) +{ + q->in = q->out = q->buf; + q->getc = qgetc; + q->putc = qputc; +} diff --git a/os/boot/pc/sd.h b/os/boot/pc/sd.h new file mode 100644 index 00000000..a19ac689 --- /dev/null +++ b/os/boot/pc/sd.h @@ -0,0 +1,127 @@ +/* + * Storage Device. + */ +typedef struct SDev SDev; +typedef struct SDifc SDifc; +typedef struct SDpart SDpart; +typedef struct SDreq SDreq; +typedef struct SDunit SDunit; + +typedef struct SDpart { + ulong start; + ulong end; + char name[NAMELEN]; + char user[NAMELEN]; + ulong perm; + int valid; + void *crud; +} SDpart; + +typedef struct SDunit { + SDev* dev; + int subno; + uchar inquiry[256]; /* format follows SCSI spec */ + char name[NAMELEN]; +// Rendez rendez; + +// QLock ctl; + ulong sectors; + ulong secsize; + SDpart* part; + int npart; /* of valid partitions */ + int changed; + +// QLock raw; + int state; + ulong pid; + SDreq* req; +} SDunit; + +typedef struct SDev { + SDifc* ifc; /* pnp/legacy */ + void *ctlr; + int idno; + int index; /* into unit space */ + int nunit; + SDev* next; + +// QLock; /* enable/disable */ + int enabled; +} SDev; + +typedef struct SDifc { + char* name; + + SDev* (*pnp)(void); + SDev* (*legacy)(int, int); + SDev* (*id)(SDev*); + int (*enable)(SDev*); + int (*disable)(SDev*); + + int (*verify)(SDunit*); + int (*online)(SDunit*); + int (*rio)(SDreq*); + int (*rctl)(SDunit*, char*, int); + int (*wctl)(SDunit*, void*); + + long (*bio)(SDunit*, int, int, void*, long, long); +} SDifc; + +typedef struct SDreq { + SDunit* unit; + int lun; + int write; + uchar cmd[16]; + int clen; + void* data; + int dlen; + + int flags; + + int status; + long rlen; + uchar sense[256]; +} SDreq; + +enum { + SDnosense = 0x00000001, + SDvalidsense = 0x00010000, +}; + +enum { + SDmalloc = -4, + SDeio = -3, + SDtimeout = -2, + SDnostatus = -1, + + SDok = 0, + + SDcheck = 0x02, /* check condition */ + SDbusy = 0x08, /* busy */ + + SDmaxio = 2048*1024, + SDnpart = 16, +}; + +/* sdscsi.c */ +extern int scsiverify(SDunit*); +extern int scsionline(SDunit*); +extern long scsibio(SDunit*, int, int, void*, long, long); +extern SDev* scsiid(SDev*, SDifc*); + +#define IrqATA0 14 +#define IrqATA1 15 +#define qlock(i) while(0) +#define qunlock(i) while(0) + +#define putstrn consputs + +void sleep(void*, int(*)(void*), void*); +void tsleep(void*, int(*)(void*), void*, int); +#define wakeup(x) while(0) +extern long sdbio(SDunit *unit, SDpart *pp, void *a, long len, vlong off); +void partition(SDunit*); +void addpartconf(SDunit*); +SDpart* sdfindpart(SDunit*, char*); +void sdaddpart(SDunit*, char*, ulong, ulong); +void* sdmalloc(void*, ulong); diff --git a/os/boot/pc/sd53c8xx.c b/os/boot/pc/sd53c8xx.c new file mode 100644 index 00000000..b9dc3e9a --- /dev/null +++ b/os/boot/pc/sd53c8xx.c @@ -0,0 +1,2120 @@ +/* + * NCR 53c8xx driver for Plan 9 + * Nigel Roles (ngr@cotswold.demon.co.uk) + * + * 08/07/99 Ultra2 fixed. Brazil #ifdefs added. Fixed script error 6 diagnostics. + * + * 09/06/99 Enhancements to support 895 and 896 correctly. Attempt at Ultra 2 negotiation, + * though no device to test with yet. + * Variant now contains the number of valid chip registers to assist + * dumpncrregs() + * + * 06/10/98 Various bug fixes and Brazil compiler inspired changes from jmk + * + * 05/10/98 Small fix to handle command length being greater than expected by device + * + * 04/08/98 Added missing locks to interrupt handler. Marked places where + * multiple controller extensions could go + * + * 18/05/97 Fixed overestimate in size of local SCRIPT RAM + * + * 17/05/97 Bug fix to return status + * + * 06/10/96 Enhanced list of chip IDs. 875 revision 1 has no clock doubler, so assume it + * is shipped with 80MHz crystal. Use bit 3 of the GPREG to recognise differential + * boards. This is Symbios specific, but since they are about the only suppliers of + * differential cards. + * + * 23/9/96 Wide and Ultra supported. 825A and 860 added to variants. Dual compiling + * version for fileserver and cpu. 80MHz default clock for 860 + * + * 5/8/96 Waits for an Inquiry message before initiating synchronous negotiation + * in case capabilities byte [7] indicates device does not support it. Devices + * which do target initiated negotiation will typically get in first; a few + * bugs in handling this have been fixed + * + * 3/8/96 Added differential support (put scsi0=diff in plan9.ini) + * Split exec() into exec() and io(). Exec() is small, and Io() does not + * use any Plan 9 specific data structures, so alternate exec() functions + * may be done for other environments, such as the fileserver + * + * GENERAL + * + * Works on 810 and 875 + * Should work on 815, 825, 810A, 825A, 860A + * Uses local RAM, large FIFO, prefetch, burst opcode fetch, and 16 byte synch. offset + * where applicable + * Supports multi-target, wide, Ultra + * Differential mode can be enabled by putting scsi0=diff in plan9.ini + * NO SUPPORT FOR tagged queuing (yet) + * + * Known problems + */ + +#define MAXTARGET 16 /* can be 8 or 16 */ + +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "error.h" + +#include "sd.h" +extern SDifc sd53c8xxifc; + +#define waserror() (0) +#define poperror() +typedef struct QLock{ int r; } QLock; +typedef struct Rendez{ int r; } Rendez; +#define intrenable(irq, f, c, tbdf, name) setvec(VectorPIC+(irq), f, c); + +/**********************************/ +/* Portable configuration macros */ +/**********************************/ + +//#define BOOTDEBUG +//#define ASYNC_ONLY +//#define INTERNAL_SCLK +//#define ALWAYS_DO_WDTR +#define WMR_DEBUG + +/**********************************/ +/* CPU specific macros */ +/**********************************/ + +#ifdef BOOTDEBUG + +#define KPRINT oprint +#define IPRINT intrprint +#define DEBUG(n) 0 +#define IFLUSH() iflush() + +#else + +#define KPRINT if(0)print +#define IPRINT if(0)print +#define DEBUG(n) (0) +#define IFLUSH() + +#endif /* BOOTDEBUG */ + +/*******************************/ +/* General */ +/*******************************/ + +#ifndef DMASEG +#define DMASEG(x) PADDR(x) +#define legetl(x) (*(ulong*)(x)) +#define lesetl(x,v) (*(ulong*)(x) = (v)) +#define swabl(a,b,c) +#else +#endif /*DMASEG */ +#define DMASEG_TO_KADDR(x) KADDR(PADDR(x)) +#define KPTR(x) ((x) == 0 ? 0 : DMASEG_TO_KADDR(x)) + +#define MEGA 1000000L +#ifdef INTERNAL_SCLK +#define SCLK (33 * MEGA) +#else +#define SCLK (40 * MEGA) +#endif /* INTERNAL_SCLK */ +#define ULTRA_NOCLOCKDOUBLE_SCLK (80 * MEGA) + +#define MAXSYNCSCSIRATE (5 * MEGA) +#define MAXFASTSYNCSCSIRATE (10 * MEGA) +#define MAXULTRASYNCSCSIRATE (20 * MEGA) +#define MAXULTRA2SYNCSCSIRATE (40 * MEGA) +#define MAXASYNCCORERATE (25 * MEGA) +#define MAXSYNCCORERATE (25 * MEGA) +#define MAXFASTSYNCCORERATE (50 * MEGA) +#define MAXULTRASYNCCORERATE (80 * MEGA) +#define MAXULTRA2SYNCCORERATE (160 * MEGA) + + +#define X_MSG 1 +#define X_MSG_SDTR 1 +#define X_MSG_WDTR 3 + +struct na_patch { + unsigned lwoff; + unsigned char type; +}; + +typedef struct Ncr { + uchar scntl0; /* 00 */ + uchar scntl1; + uchar scntl2; + uchar scntl3; + + uchar scid; /* 04 */ + uchar sxfer; + uchar sdid; + uchar gpreg; + + uchar sfbr; /* 08 */ + uchar socl; + uchar ssid; + uchar sbcl; + + uchar dstat; /* 0c */ + uchar sstat0; + uchar sstat1; + uchar sstat2; + + uchar dsa[4]; /* 10 */ + + uchar istat; /* 14 */ + uchar istatpad[3]; + + uchar ctest0; /* 18 */ + uchar ctest1; + uchar ctest2; + uchar ctest3; + + uchar temp[4]; /* 1c */ + + uchar dfifo; /* 20 */ + uchar ctest4; + uchar ctest5; + uchar ctest6; + + uchar dbc[3]; /* 24 */ + uchar dcmd; /* 27 */ + + uchar dnad[4]; /* 28 */ + uchar dsp[4]; /* 2c */ + uchar dsps[4]; /* 30 */ + + uchar scratcha[4]; /* 34 */ + + uchar dmode; /* 38 */ + uchar dien; + uchar dwt; + uchar dcntl; + + uchar adder[4]; /* 3c */ + + uchar sien0; /* 40 */ + uchar sien1; + uchar sist0; + uchar sist1; + + uchar slpar; /* 44 */ + uchar slparpad0; + uchar macntl; + uchar gpcntl; + + uchar stime0; /* 48 */ + uchar stime1; + uchar respid; + uchar respidpad0; + + uchar stest0; /* 4c */ + uchar stest1; + uchar stest2; + uchar stest3; + + uchar sidl; /* 50 */ + uchar sidlpad[3]; + + uchar sodl; /* 54 */ + uchar sodlpad[3]; + + uchar sbdl; /* 58 */ + uchar sbdlpad[3]; + + uchar scratchb[4]; /* 5c */ +} Ncr; + +typedef struct Movedata { + uchar dbc[4]; + uchar pa[4]; +} Movedata; + +typedef enum NegoState { + NeitherDone, WideInit, WideResponse, WideDone, + SyncInit, SyncResponse, BothDone +} NegoState; + +typedef enum State { + Allocated, Queued, Active, Done +} State; + +typedef struct Dsa { + union { + uchar state[4]; + struct { + uchar stateb; + uchar result; + uchar dmablks; + uchar flag; /* setbyte(state,3,...) */ + }; + }; + + union { + ulong dmancr; /* For block transfer: NCR order (little-endian) */ + uchar dmaaddr[4]; + }; + + uchar target; /* Target */ + uchar pad0[3]; + + uchar lun; /* Logical Unit Number */ + uchar pad1[3]; + + uchar scntl3; + uchar sxfer; + uchar pad2[2]; + + uchar next[4]; /* chaining for SCRIPT (NCR byte order) */ + struct Dsa *freechain; /* chaining for freelist */ + Rendez; + uchar scsi_id_buf[4]; + Movedata msg_out_buf; + Movedata cmd_buf; + Movedata data_buf; + Movedata status_buf; + uchar msg_out[10]; /* enough to include SDTR */ + uchar status; + int p9status; + uchar parityerror; +} Dsa; + +typedef enum Feature { + BigFifo = 1, /* 536 byte fifo */ + BurstOpCodeFetch = 2, /* burst fetch opcodes */ + Prefetch = 4, /* prefetch 8 longwords */ + LocalRAM = 8, /* 4K longwords of local RAM */ + Differential = 16, /* Differential support */ + Wide = 32, /* Wide capable */ + Ultra = 64, /* Ultra capable */ + ClockDouble = 128, /* Has clock doubler */ + ClockQuad = 256, /* Has clock quadrupler (same as Ultra2) */ + Ultra2 = 256, +} Feature; + +typedef enum Burst { + Burst2 = 0, + Burst4 = 1, + Burst8 = 2, + Burst16 = 3, + Burst32 = 4, + Burst64 = 5, + Burst128 = 6 +} Burst; + +typedef struct Variant { + ushort did; + uchar maxrid; /* maximum allowed revision ID */ + char *name; + Burst burst; /* codings for max burst */ + uchar maxsyncoff; /* max synchronous offset */ + uchar registers; /* number of 32 bit registers */ + unsigned feature; +} Variant; + +static unsigned char cf2[] = { 6, 2, 3, 4, 6, 8, 12, 16 }; +#define NULTRA2SCF (sizeof(cf2)/sizeof(cf2[0])) +#define NULTRASCF (NULTRA2SCF - 2) +#define NSCF (NULTRASCF - 1) + +typedef struct Controller { + Lock; + struct { + uchar scntl3; + uchar stest2; + } bios; + uchar synctab[NULTRA2SCF - 1][8];/* table of legal tpfs */ + NegoState s[MAXTARGET]; + uchar scntl3[MAXTARGET]; + uchar sxfer[MAXTARGET]; + uchar cap[MAXTARGET]; /* capabilities byte from Identify */ + ushort capvalid; /* bit per target for validity of cap[] */ + ushort wide; /* bit per target set if wide negotiated */ + ulong sclk; /* clock speed of controller */ + uchar clockmult; /* set by synctabinit */ + uchar ccf; /* CCF bits */ + uchar tpf; /* best tpf value for this controller */ + uchar feature; /* requested features */ + int running; /* is the script processor running? */ + int ssm; /* single step mode */ + Ncr *n; /* pointer to registers */ + Variant *v; /* pointer to variant type */ + ulong *script; /* where the real script is */ + ulong scriptpa; /* where the real script is */ + Pcidev* pcidev; + SDev* sdev; + + struct { + Lock; + uchar head[4]; /* head of free list (NCR byte order) */ + Dsa *tail; + Dsa *freechain; + } dsalist; + + QLock q[MAXTARGET]; /* queues for each target */ +} Controller; + +static Controller controller; + +/* ISTAT */ +enum { Abrt = 0x80, Srst = 0x40, Sigp = 0x20, Sem = 0x10, Con = 0x08, Intf = 0x04, Sip = 0x02, Dip = 0x01 }; + +/* DSTAT */ +enum { Dfe = 0x80, Mdpe = 0x40, Bf = 0x20, Abrted = 0x10, Ssi = 0x08, Sir = 0x04, Iid = 0x01 }; + +/* SSTAT */ +enum { DataOut, DataIn, Cmd, Status, ReservedOut, ReservedIn, MessageOut, MessageIn }; + +static void setmovedata(Movedata*, ulong, ulong); +static void advancedata(Movedata*, long); +static int bios_set_differential(Controller *c); + +static char *phase[] = { + "data out", "data in", "command", "status", + "reserved out", "reserved in", "message out", "message in" +}; + +#ifdef BOOTDEBUG +#define DEBUGSIZE 10240 +char debugbuf[DEBUGSIZE]; +char *debuglast; + +static void +intrprint(char *format, ...) +{ + if (debuglast == 0) + debuglast = debugbuf; + debuglast = doprint(debuglast, debugbuf + (DEBUGSIZE - 1), format, (&format + 1)); +} + +static void +iflush() +{ + int s; + char *endp; + s = splhi(); + if (debuglast == 0) + debuglast = debugbuf; + if (debuglast == debugbuf) { + splx(s); + return; + } + endp = debuglast; + splx(s); + screenputs(debugbuf, endp - debugbuf); + s = splhi(); + memmove(debugbuf, endp, debuglast - endp); + debuglast -= endp - debugbuf; + splx(s); +} + +static void +oprint(char *format, ...) +{ + int s; + + iflush(); + s = splhi(); + if (debuglast == 0) + debuglast = debugbuf; + debuglast = doprint(debuglast, debugbuf + (DEBUGSIZE - 1), format, (&format + 1)); + splx(s); + iflush(); +} +#endif + +#include "sd53c8xx.i" + +static Dsa * +dsaalloc(Controller *c, int target, int lun) +{ + Dsa *d; + + ilock(&c->dsalist); + if ((d = c->dsalist.freechain) == 0) { + d = xalloc(sizeof(*d)); + if (DEBUG(1)) + KPRINT("sd53c8xx: %d/%d: allocated new dsa %lux\n", target, lun, d); + lesetl(d->next, 0); + lesetl(d->state, A_STATE_ALLOCATED); + if (legetl(c->dsalist.head) == 0) + lesetl(c->dsalist.head, DMASEG(d)); /* ATOMIC?!? */ + else + lesetl(c->dsalist.tail->next, DMASEG(d)); /* ATOMIC?!? */ + c->dsalist.tail = d; + } + else { + if (DEBUG(1)) + KPRINT("sd53c8xx: %d/%d: reused dsa %lux\n", target, lun, d); + c->dsalist.freechain = d->freechain; + lesetl(d->state, A_STATE_ALLOCATED); + } + iunlock(&c->dsalist); + d->target = target; + d->lun = lun; + return d; +} + +static void +dsafree(Controller *c, Dsa *d) +{ + ilock(&c->dsalist); + d->freechain = c->dsalist.freechain; + c->dsalist.freechain = d; + lesetl(d->state, A_STATE_FREE); + iunlock(&c->dsalist); +} + +static Dsa * +dsafind(Controller *c, uchar target, uchar lun, uchar state) +{ + Dsa *d; + for (d = KPTR(legetl(c->dsalist.head)); d; d = KPTR(legetl(d->next))) { + if (d->target != 0xff && d->target != target) + continue; + if (lun != 0xff && d->lun != lun) + continue; + if (state != 0xff && d->stateb != state) + continue; + break; + } + return d; +} + +static void +dumpncrregs(Controller *c, int intr) +{ + int i; + Ncr *n = c->n; + int depth = c->v->registers / 4; + + KPRINT("sa = %.8lux\n", c->scriptpa); + for (i = 0; i < depth; i++) { + int j; + for (j = 0; j < 4; j++) { + int k = j * depth + i; + uchar *p; + + /* display little-endian to make 32-bit values readable */ + p = (uchar*)n+k*4; + if (intr) + IPRINT(" %.2x%.2x%.2x%.2x %.2x %.2x", p[3], p[2], p[1], p[0], k * 4, (k * 4) + 0x80); + else + KPRINT(" %.2x%.2x%.2x%.2x %.2x %.2x", p[3], p[2], p[1], p[0], k * 4, (k * 4) + 0x80); + USED(p); + } + if (intr) + IPRINT("\n"); + else + KPRINT("\n"); + } +} + +static int +chooserate(Controller *c, int tpf, int *scfp, int *xferpp) +{ + /* find lowest entry >= tpf */ + int besttpf = 1000; + int bestscfi = 0; + int bestxferp = 0; + int scf, xferp; + int maxscf; + + if (c->v->feature & Ultra2) + maxscf = NULTRA2SCF; + else if (c->v->feature & Ultra) + maxscf = NULTRASCF; + else + maxscf = NSCF; + + /* + * search large clock factors first since this should + * result in more reliable transfers + */ + for (scf = maxscf; scf >= 1; scf--) { + for (xferp = 0; xferp < 8; xferp++) { + unsigned char v = c->synctab[scf - 1][xferp]; + if (v == 0) + continue; + if (v >= tpf && v < besttpf) { + besttpf = v; + bestscfi = scf; + bestxferp = xferp; + } + } + } + if (besttpf == 1000) + return 0; + if (scfp) + *scfp = bestscfi; + if (xferpp) + *xferpp = bestxferp; + return besttpf; +} + +static void +synctabinit(Controller *c) +{ + int scf; + unsigned long scsilimit; + int xferp; + unsigned long cr, sr; + int tpf; + int fast; + int maxscf; + + if (c->v->feature & Ultra2) + maxscf = NULTRA2SCF; + else if (c->v->feature & Ultra) + maxscf = NULTRASCF; + else + maxscf = NSCF; + + /* + * for chips with no clock doubler, but Ultra capable (e.g. 860, or interestingly the + * first spin of the 875), assume 80MHz + * otherwise use the internal (33 Mhz) or external (40MHz) default + */ + + if ((c->v->feature & Ultra) != 0 && (c->v->feature & (ClockDouble | ClockQuad)) == 0) + c->sclk = ULTRA_NOCLOCKDOUBLE_SCLK; + else + c->sclk = SCLK; + + /* + * otherwise, if the chip is Ultra capable, but has a slow(ish) clock, + * invoke the doubler + */ + + if (SCLK <= 40000000) { + if (c->v->feature & ClockDouble) { + c->sclk *= 2; + c->clockmult = 1; + } + else if (c->v->feature & ClockQuad) { + c->sclk *= 4; + c->clockmult = 1; + } + else + c->clockmult = 0; + } + else + c->clockmult = 0; + + /* derive CCF from sclk */ + /* woebetide anyone with SCLK < 16.7 or > 80MHz */ + if (c->sclk <= 25 * MEGA) + c->ccf = 1; + else if (c->sclk <= 3750000) + c->ccf = 2; + else if (c->sclk <= 50 * MEGA) + c->ccf = 3; + else if (c->sclk <= 75 * MEGA) + c->ccf = 4; + else if ((c->v->feature & ClockDouble) && c->sclk <= 80 * MEGA) + c->ccf = 5; + else if ((c->v->feature & ClockQuad) && c->sclk <= 120 * MEGA) + c->ccf = 6; + else if ((c->v->feature & ClockQuad) && c->sclk <= 160 * MEGA) + c->ccf = 7; + + for (scf = 1; scf < maxscf; scf++) { + /* check for legal core rate */ + /* round up so we run slower for safety */ + cr = (c->sclk * 2 + cf2[scf] - 1) / cf2[scf]; + if (cr <= MAXSYNCCORERATE) { + scsilimit = MAXSYNCSCSIRATE; + fast = 0; + } + else if (cr <= MAXFASTSYNCCORERATE) { + scsilimit = MAXFASTSYNCSCSIRATE; + fast = 1; + } + else if ((c->v->feature & Ultra) && cr <= MAXULTRASYNCCORERATE) { + scsilimit = MAXULTRASYNCSCSIRATE; + fast = 2; + } + else if ((c->v->feature & Ultra2) && cr <= MAXULTRA2SYNCCORERATE) { + scsilimit = MAXULTRA2SYNCSCSIRATE; + fast = 3; + } + else + continue; + for (xferp = 11; xferp >= 4; xferp--) { + int ok; + int tp; + /* calculate scsi rate - round up again */ + /* start from sclk for accuracy */ + int totaldivide = xferp * cf2[scf]; + sr = (c->sclk * 2 + totaldivide - 1) / totaldivide; + if (sr > scsilimit) + break; + /* + * now work out transfer period + * round down now so that period is pessimistic + */ + tp = (MEGA * 1000) / sr; + /* + * bounds check it + */ + if (tp < 25 || tp > 255 * 4) + continue; + /* + * spot stupid special case for Ultra or Ultra2 + * while working out factor + */ + if (tp == 25) + tpf = 10; + else if (tp == 50) + tpf = 12; + else if (tp < 52) + continue; + else + tpf = tp / 4; + /* + * now check tpf looks sensible + * given core rate + */ + switch (fast) { + case 0: + /* scf must be ccf for SCSI 1 */ + ok = tpf >= 50 && scf == c->ccf; + break; + case 1: + ok = tpf >= 25 && tpf < 50; + break; + case 2: + /* + * must use xferp of 4, or 5 at a pinch + * for an Ultra transfer + */ + ok = xferp <= 5 && tpf >= 12 && tpf < 25; + break; + case 3: + ok = xferp == 4 && (tpf == 10 || tpf == 11); + break; + default: + ok = 0; + } + if (!ok) + continue; + c->synctab[scf - 1][xferp - 4] = tpf; + } + } + +#ifndef NO_ULTRA2 + if (c->v->feature & Ultra2) + tpf = 10; + else +#endif + if (c->v->feature & Ultra) + tpf = 12; + else + tpf = 25; + for (; tpf < 256; tpf++) { + if (chooserate(c, tpf, &scf, &xferp) == tpf) { + unsigned tp = tpf == 10 ? 25 : (tpf == 12 ? 50 : tpf * 4); + unsigned long khz = (MEGA + tp - 1) / (tp); + KPRINT("sd53c8xx: tpf=%d scf=%d.%.1d xferp=%d mhz=%ld.%.3ld\n", + tpf, cf2[scf] / 2, (cf2[scf] & 1) ? 5 : 0, + xferp + 4, khz / 1000, khz % 1000); + USED(khz); + if (c->tpf == 0) + c->tpf = tpf; /* note lowest value for controller */ + } + } +} + +static void +synctodsa(Dsa *dsa, Controller *c) +{ +/* + KPRINT("synctodsa(dsa=%lux, target=%d, scntl3=%.2lx sxfer=%.2x)\n", + dsa, dsa->target, c->scntl3[dsa->target], c->sxfer[dsa->target]); +*/ + dsa->scntl3 = c->scntl3[dsa->target]; + dsa->sxfer = c->sxfer[dsa->target]; +} + +static void +setsync(Dsa *dsa, Controller *c, int target, uchar ultra, uchar scf, uchar xferp, uchar reqack) +{ + c->scntl3[target] = + (c->scntl3[target] & 0x08) | (((scf << 4) | c->ccf | (ultra << 7)) & ~0x08); + c->sxfer[target] = (xferp << 5) | reqack; + c->s[target] = BothDone; + if (dsa) { + synctodsa(dsa, c); + c->n->scntl3 = c->scntl3[target]; + c->n->sxfer = c->sxfer[target]; + } +} + +static void +setasync(Dsa *dsa, Controller *c, int target) +{ + setsync(dsa, c, target, 0, c->ccf, 0, 0); +} + +static void +setwide(Dsa *dsa, Controller *c, int target, uchar wide) +{ + c->scntl3[target] = wide ? (1 << 3) : 0; + setasync(dsa, c, target); + c->s[target] = WideDone; +} + +static int +buildsdtrmsg(uchar *buf, uchar tpf, uchar offset) +{ + *buf++ = X_MSG; + *buf++ = 3; + *buf++ = X_MSG_SDTR; + *buf++ = tpf; + *buf = offset; + return 5; +} + +static int +buildwdtrmsg(uchar *buf, uchar expo) +{ + *buf++ = X_MSG; + *buf++ = 2; + *buf++ = X_MSG_WDTR; + *buf = expo; + return 4; +} + +static void +start(Controller *c, long entry) +{ + ulong p; + + if (c->running) + panic("sd53c8xx: start called while running"); + c->running = 1; + p = c->scriptpa + entry; + lesetl(c->n->dsp, p); + if (c->ssm) + c->n->dcntl |= 0x4; /* start DMA in SSI mode */ +} + +static void +ncrcontinue(Controller *c) +{ + if (c->running) + panic("sd53c8xx: ncrcontinue called while running"); + /* set the start DMA bit to continue execution */ + c->running = 1; + c->n->dcntl |= 0x4; +} + +static void +softreset(Controller *c) +{ + Ncr *n = c->n; + + n->istat = Srst; /* software reset */ + n->istat = 0; + /* general initialisation */ + n->scid = (1 << 6) | 7; /* respond to reselect, ID 7 */ + n->respid = 1 << 7; /* response ID = 7 */ + +#ifdef INTERNAL_SCLK + n->stest1 = 0x80; /* disable external scsi clock */ +#else + n->stest1 = 0x00; +#endif + + n->stime0 = 0xdd; /* about 0.5 second timeout on each device */ + n->scntl0 |= 0x8; /* Enable parity checking */ + + /* continued setup */ + n->sien0 = 0x8f; + n->sien1 = 0x04; + n->dien = 0x7d; + n->stest3 = 0x80; /* TolerANT enable */ + c->running = 0; + + if (c->v->feature & BigFifo) + n->ctest5 = (1 << 5); + n->dmode = c->v->burst << 6; /* set burst length bits */ + if (c->v->burst & 4) + n->ctest5 |= (1 << 2); /* including overflow into ctest5 bit 2 */ + if (c->v->feature & Prefetch) + n->dcntl |= (1 << 5); /* prefetch enable */ + else if (c->v->feature & BurstOpCodeFetch) + n->dmode |= (1 << 1); /* burst opcode fetch */ + if (c->v->feature & Differential) { + /* chip capable */ + if ((c->feature & Differential) || bios_set_differential(c)) { + /* user enabled, or some evidence bios set differential */ + if (n->sstat2 & (1 << 2)) + print("sd53c8xx: can't go differential; wrong cable\n"); + else { + n->stest2 = (1 << 5); + print("sd53c8xx: differential mode set\n"); + } + } + } + if (c->clockmult) { + n->stest1 |= (1 << 3); /* power up doubler */ + delay(2); + n->stest3 |= (1 << 5); /* stop clock */ + n->stest1 |= (1 << 2); /* enable doubler */ + n->stest3 &= ~(1 << 5); /* start clock */ + /* pray */ + } +} + +static void +msgsm(Dsa *dsa, Controller *c, int msg, int *cont, int *wakeme) +{ + uchar histpf, hisreqack; + int tpf; + int scf, xferp; + int len; + + Ncr *n = c->n; + + switch (c->s[dsa->target]) { + case SyncInit: + switch (msg) { + case A_SIR_MSG_SDTR: + /* reply to my SDTR */ + histpf = n->scratcha[2]; + hisreqack = n->scratcha[3]; + KPRINT("sd53c8xx: %d: SDTN response %d %d\n", + dsa->target, histpf, hisreqack); + + if (hisreqack == 0) + setasync(dsa, c, dsa->target); + else { + /* hisreqack should be <= c->v->maxsyncoff */ + tpf = chooserate(c, histpf, &scf, &xferp); + KPRINT("sd53c8xx: %d: SDTN: using %d %d\n", + dsa->target, tpf, hisreqack); + setsync(dsa, c, dsa->target, tpf < 25, scf, xferp, hisreqack); + } + *cont = -2; + return; + case A_SIR_EV_PHASE_SWITCH_AFTER_ID: + /* target ignored ATN for message after IDENTIFY - not SCSI-II */ + KPRINT("sd53c8xx: %d: illegal phase switch after ID message - SCSI-1 device?\n", dsa->target); + KPRINT("sd53c8xx: %d: SDTN: async\n", dsa->target); + setasync(dsa, c, dsa->target); + *cont = E_to_decisions; + return; + case A_SIR_MSG_REJECT: + /* rejection of my SDTR */ + KPRINT("sd53c8xx: %d: SDTN: rejected SDTR\n", dsa->target); + //async: + KPRINT("sd53c8xx: %d: SDTN: async\n", dsa->target); + setasync(dsa, c, dsa->target); + *cont = -2; + return; + } + break; + case WideInit: + switch (msg) { + case A_SIR_MSG_WDTR: + /* reply to my WDTR */ + KPRINT("sd53c8xx: %d: WDTN: response %d\n", + dsa->target, n->scratcha[2]); + setwide(dsa, c, dsa->target, n->scratcha[2]); + *cont = -2; + return; + case A_SIR_EV_PHASE_SWITCH_AFTER_ID: + /* target ignored ATN for message after IDENTIFY - not SCSI-II */ + KPRINT("sd53c8xx: %d: illegal phase switch after ID message - SCSI-1 device?\n", dsa->target); + setwide(dsa, c, dsa->target, 0); + *cont = E_to_decisions; + return; + case A_SIR_MSG_REJECT: + /* rejection of my SDTR */ + KPRINT("sd53c8xx: %d: WDTN: rejected WDTR\n", dsa->target); + setwide(dsa, c, dsa->target, 0); + *cont = -2; + return; + } + break; + + case NeitherDone: + case WideDone: + case BothDone: + switch (msg) { + case A_SIR_MSG_WDTR: { + uchar hiswide, mywide; + hiswide = n->scratcha[2]; + mywide = (c->v->feature & Wide) != 0; + KPRINT("sd53c8xx: %d: WDTN: target init %d\n", + dsa->target, hiswide); + if (hiswide < mywide) + mywide = hiswide; + KPRINT("sd53c8xx: %d: WDTN: responding %d\n", + dsa->target, mywide); + setwide(dsa, c, dsa->target, mywide); + len = buildwdtrmsg(dsa->msg_out, mywide); + setmovedata(&dsa->msg_out_buf, DMASEG(dsa->msg_out), len); + *cont = E_response; + c->s[dsa->target] = WideResponse; + return; + } + case A_SIR_MSG_SDTR: +#ifdef ASYNC_ONLY + *cont = E_reject; + return; +#else + /* target decides to renegotiate */ + histpf = n->scratcha[2]; + hisreqack = n->scratcha[3]; + KPRINT("sd53c8xx: %d: SDTN: target init %d %d\n", + dsa->target, histpf, hisreqack); + if (hisreqack == 0) { + /* he wants asynchronous */ + setasync(dsa, c, dsa->target); + tpf = 0; + } + else { + /* he wants synchronous */ + tpf = chooserate(c, histpf, &scf, &xferp); + if (hisreqack > c->v->maxsyncoff) + hisreqack = c->v->maxsyncoff; + KPRINT("sd53c8xx: %d: using %d %d\n", + dsa->target, tpf, hisreqack); + setsync(dsa, c, dsa->target, tpf < 25, scf, xferp, hisreqack); + } + /* build my SDTR message */ + len = buildsdtrmsg(dsa->msg_out, tpf, hisreqack); + setmovedata(&dsa->msg_out_buf, DMASEG(dsa->msg_out), len); + *cont = E_response; + c->s[dsa->target] = SyncResponse; + return; +#endif + } + break; + case WideResponse: + switch (msg) { + case A_SIR_EV_RESPONSE_OK: + c->s[dsa->target] = WideDone; + KPRINT("sd53c8xx: %d: WDTN: response accepted\n", dsa->target); + *cont = -2; + return; + case A_SIR_MSG_REJECT: + setwide(dsa, c, dsa->target, 0); + KPRINT("sd53c8xx: %d: WDTN: response REJECTed\n", dsa->target); + *cont = -2; + return; + } + break; + case SyncResponse: + switch (msg) { + case A_SIR_EV_RESPONSE_OK: + c->s[dsa->target] = BothDone; + KPRINT("sd53c8xx: %d: SDTN: response accepted (%s)\n", + dsa->target, phase[n->sstat1 & 7]); + *cont = -2; + return; /* chf */ + case A_SIR_MSG_REJECT: + setasync(dsa, c, dsa->target); + KPRINT("sd53c8xx: %d: SDTN: response REJECTed\n", dsa->target); + *cont = -2; + return; + } + break; + } + KPRINT("sd53c8xx: %d: msgsm: state %d msg %d\n", + dsa->target, c->s[dsa->target], msg); + *wakeme = 1; + return; +} + +static void +calcblockdma(Dsa *d, ulong base, ulong count) +{ + ulong blocks; + if (DEBUG(3)) + blocks = 0; + else { + blocks = count / A_BSIZE; + if (blocks > 255) + blocks = 255; + } + d->dmablks = blocks; + d->dmaaddr[0] = base; + d->dmaaddr[1] = base >> 8; + d->dmaaddr[2] = base >> 16; + d->dmaaddr[3] = base >> 24; + setmovedata(&d->data_buf, base + blocks * A_BSIZE, count - blocks * A_BSIZE); + if (legetl(d->data_buf.dbc) == 0) + d->flag = 1; +} + +static ulong +read_mismatch_recover(Controller *c, Ncr *n, Dsa *dsa) +{ + ulong dbc; + uchar dfifo = n->dfifo; + int inchip; + + dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0]; + if (n->ctest5 & (1 << 5)) + inchip = ((dfifo | ((n->ctest5 & 3) << 8)) - (dbc & 0x3ff)) & 0x3ff; + else + inchip = ((dfifo & 0x7f) - (dbc & 0x7f)) & 0x7f; + if (inchip) { + IPRINT("sd53c8xx: %d/%d: read_mismatch_recover: DMA FIFO = %d\n", + dsa->target, dsa->lun, inchip); + } + if (n->sxfer & 0xf) { + /* SCSI FIFO */ + uchar fifo = n->sstat1 >> 4; + if (c->v->maxsyncoff > 8) + fifo |= (n->sstat2 & (1 << 4)); + if (fifo) { + inchip += fifo; + IPRINT("sd53c8xx: %d/%d: read_mismatch_recover: SCSI FIFO = %d\n", + dsa->target, dsa->lun, fifo); + } + } + else { + if (n->sstat0 & (1 << 7)) { + inchip++; + IPRINT("sd53c8xx: %d/%d: read_mismatch_recover: SIDL full\n", + dsa->target, dsa->lun); + } + if (n->sstat2 & (1 << 7)) { + inchip++; + IPRINT("sd53c8xx: %d/%d: read_mismatch_recover: SIDL msb full\n", + dsa->target, dsa->lun); + } + } + USED(inchip); + return dbc; +} + +static ulong +write_mismatch_recover(Ncr *n, Dsa *dsa) +{ + ulong dbc; + uchar dfifo = n->dfifo; + int inchip; + + dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0]; + USED(dsa); + if (n->ctest5 & (1 << 5)) + inchip = ((dfifo | ((n->ctest5 & 3) << 8)) - (dbc & 0x3ff)) & 0x3ff; + else + inchip = ((dfifo & 0x7f) - (dbc & 0x7f)) & 0x7f; +#ifdef WMR_DEBUG + if (inchip) { + IPRINT("sd53c8xx: %d/%d: write_mismatch_recover: DMA FIFO = %d\n", + dsa->target, dsa->lun, inchip); + } +#endif + if (n->sstat0 & (1 << 5)) { + inchip++; +#ifdef WMR_DEBUG + IPRINT("sd53c8xx: %d/%d: write_mismatch_recover: SODL full\n", dsa->target, dsa->lun); +#endif + } + if (n->sstat2 & (1 << 5)) { + inchip++; +#ifdef WMR_DEBUG + IPRINT("sd53c8xx: %d/%d: write_mismatch_recover: SODL msb full\n", dsa->target, dsa->lun); +#endif + } + if (n->sxfer & 0xf) { + /* synchronous SODR */ + if (n->sstat0 & (1 << 6)) { + inchip++; +#ifdef WMR_DEBUG + IPRINT("sd53c8xx: %d/%d: write_mismatch_recover: SODR full\n", + dsa->target, dsa->lun); +#endif + } + if (n->sstat2 & (1 << 6)) { + inchip++; +#ifdef WMR_DEBUG + IPRINT("sd53c8xx: %d/%d: write_mismatch_recover: SODR msb full\n", + dsa->target, dsa->lun); +#endif + } + } + /* clear the dma fifo */ + n->ctest3 |= (1 << 2); + /* wait till done */ + while ((n->dstat & Dfe) == 0) + ; + return dbc + inchip; +} + +static void +interrupt(Ureg *ur, void *a) +{ + uchar istat; + ushort sist; + uchar dstat; + int wakeme = 0; + int cont = -1; + Dsa *dsa; + Controller *c = a; + Ncr *n = c->n; + + USED(ur); + if (DEBUG(1)) + IPRINT("sd53c8xx: int\n"); + ilock(c); + istat = n->istat; + if (istat & Intf) { + Dsa *d; + int wokesomething = 0; + if (DEBUG(1)) + IPRINT("sd53c8xx: Intfly\n"); + n->istat = Intf; + /* search for structures in A_STATE_DONE */ + for (d = KPTR(legetl(c->dsalist.head)); d; d = KPTR(legetl(d->next))) { + if (d->stateb == A_STATE_DONE) { + d->p9status = d->status; + if (DEBUG(1)) + IPRINT("sd53c8xx: waking up dsa %lux\n", d); + wakeup(d); + wokesomething = 1; + } + } + if (!wokesomething) + IPRINT("sd53c8xx: nothing to wake up\n"); + } + + if ((istat & (Sip | Dip)) == 0) { + if (DEBUG(1)) + IPRINT("sd53c8xx: int end %x\n", istat); + iunlock(c); + return; + } + + sist = (n->sist1<<8)|n->sist0; /* BUG? can two-byte read be inconsistent? */ + dstat = n->dstat; + dsa = (Dsa *)DMASEG_TO_KADDR(legetl(n->dsa)); + c->running = 0; + if (istat & Sip) { + if (DEBUG(1)) + IPRINT("sist = %.4x\n", sist); + if (sist & 0x80) { + ulong addr; + ulong sa; + ulong dbc; + ulong tbc; + int dmablks; + ulong dmaaddr; + + addr = legetl(n->dsp); + sa = addr - c->scriptpa; + if (DEBUG(1) || DEBUG(2)) + IPRINT("sd53c8xx: %d/%d: Phase Mismatch sa=%.8lux\n", + dsa->target, dsa->lun, sa); + /* + * now recover + */ + if (sa == E_data_in_mismatch) { + dbc = read_mismatch_recover(c, n, dsa); + tbc = legetl(dsa->data_buf.dbc) - dbc; + advancedata(&dsa->data_buf, tbc); + if (DEBUG(1) || DEBUG(2)) + IPRINT("sd53c8xx: %d/%d: transferred = %ld residue = %ld\n", + dsa->target, dsa->lun, tbc, legetl(dsa->data_buf.dbc)); + cont = E_to_decisions; + } + else if (sa == E_data_in_block_mismatch) { + dbc = read_mismatch_recover(c, n, dsa); + tbc = A_BSIZE - dbc; + /* recover current state from registers */ + dmablks = n->scratcha[2]; + dmaaddr = legetl(n->scratchb); + /* we have got to dmaaddr + tbc */ + /* we have dmablks * A_BSIZE - tbc + residue left to do */ + /* so remaining transfer is */ + IPRINT("in_block_mismatch: dmaaddr = 0x%lux tbc=%lud dmablks=%d\n", + dmaaddr, tbc, dmablks); + calcblockdma(dsa, dmaaddr + tbc, + dmablks * A_BSIZE - tbc + legetl(dsa->data_buf.dbc)); + /* copy changes into scratch registers */ + IPRINT("recalc: dmablks %d dmaaddr 0x%lx pa 0x%lx dbc %ld\n", + dsa->dmablks, legetl(dsa->dmaaddr), + legetl(dsa->data_buf.pa), legetl(dsa->data_buf.dbc)); + n->scratcha[2] = dsa->dmablks; + lesetl(n->scratchb, dsa->dmancr); + cont = E_data_block_mismatch_recover; + } + else if (sa == E_data_out_mismatch) { + dbc = write_mismatch_recover(n, dsa); + tbc = legetl(dsa->data_buf.dbc) - dbc; + advancedata(&dsa->data_buf, tbc); + if (DEBUG(1) || DEBUG(2)) + IPRINT("sd53c8xx: %d/%d: transferred = %ld residue = %ld\n", + dsa->target, dsa->lun, tbc, legetl(dsa->data_buf.dbc)); + cont = E_to_decisions; + } + else if (sa == E_data_out_block_mismatch) { + dbc = write_mismatch_recover(n, dsa); + tbc = legetl(dsa->data_buf.dbc) - dbc; + /* recover current state from registers */ + dmablks = n->scratcha[2]; + dmaaddr = legetl(n->scratchb); + /* we have got to dmaaddr + tbc */ + /* we have dmablks blocks - tbc + residue left to do */ + /* so remaining transfer is */ + IPRINT("out_block_mismatch: dmaaddr = %lux tbc=%lud dmablks=%d\n", + dmaaddr, tbc, dmablks); + calcblockdma(dsa, dmaaddr + tbc, + dmablks * A_BSIZE - tbc + legetl(dsa->data_buf.dbc)); + /* copy changes into scratch registers */ + n->scratcha[2] = dsa->dmablks; + lesetl(n->scratchb, dsa->dmancr); + cont = E_data_block_mismatch_recover; + } + else if (sa == E_id_out_mismatch) { + /* + * target switched phases while attention held during + * message out. The possibilities are: + * 1. It didn't like the last message. This is indicated + * by the new phase being message_in. Use script to recover + * + * 2. It's not SCSI-II compliant. The new phase will be other + * than message_in. We should also indicate that the device + * is asynchronous, if it's the SDTR that got ignored + * + * For now, if the phase switch is not to message_in, and + * and it happens after IDENTIFY and before SDTR, we + * notify the negotiation state machine. + */ + ulong lim = legetl(dsa->msg_out_buf.dbc); + uchar p = n->sstat1 & 7; + dbc = write_mismatch_recover(n, dsa); + tbc = lim - dbc; + IPRINT("sd53c8xx: %d/%d: msg_out_mismatch: %lud/%lud sent, phase %s\n", + dsa->target, dsa->lun, tbc, lim, phase[p]); + if (p != MessageIn && tbc == 1) { + msgsm(dsa, c, A_SIR_EV_PHASE_SWITCH_AFTER_ID, &cont, &wakeme); + } + else + cont = E_id_out_mismatch_recover; + } + else if (sa == E_cmd_out_mismatch) { + /* + * probably the command count is longer than the device wants ... + */ + ulong lim = legetl(dsa->cmd_buf.dbc); + uchar p = n->sstat1 & 7; + dbc = write_mismatch_recover(n, dsa); + tbc = lim - dbc; + IPRINT("sd53c8xx: %d/%d: cmd_out_mismatch: %lud/%lud sent, phase %s\n", + dsa->target, dsa->lun, tbc, lim, phase[p]); + USED(p, tbc); + cont = E_to_decisions; + } + else { + IPRINT("sd53c8xx: %d/%d: ma sa=%.8lux wanted=%s got=%s\n", + dsa->target, dsa->lun, sa, + phase[n->dcmd & 7], + phase[n->sstat1 & 7]); + dumpncrregs(c, 1); + dsa->p9status = SDeio; /* chf */ + wakeme = 1; + } + } + /*else*/ if (sist & 0x400) { + if (DEBUG(0)) + IPRINT("sd53c8xx: %d/%d Sto\n", dsa->target, dsa->lun); + dsa->p9status = SDtimeout; + dsa->stateb = A_STATE_DONE; + softreset(c); + cont = E_issue_check; + wakeme = 1; + } + if (sist & 0x1) { + IPRINT("sd53c8xx: %d/%d: parity error\n", dsa->target, dsa->lun); + dsa->parityerror = 1; + } + if (sist & 0x4) { + IPRINT("sd53c8xx: %d/%d: unexpected disconnect\n", + dsa->target, dsa->lun); + dumpncrregs(c, 1); + //wakeme = 1; + dsa->p9status = SDeio; + } + } + if (istat & Dip) { + if (DEBUG(1)) + IPRINT("dstat = %.2x\n", dstat); + /*else*/ if (dstat & Ssi) { + ulong *p = DMASEG_TO_KADDR(legetl(n->dsp)); + ulong w = (uchar *)p - (uchar *)c->script; + IPRINT("[%lux]", w); + USED(w); + cont = -2; /* restart */ + } + if (dstat & Sir) { + switch (legetl(n->dsps)) { + case A_SIR_MSG_IO_COMPLETE: + dsa->p9status = dsa->status; + wakeme = 1; + break; + case A_SIR_MSG_SDTR: + case A_SIR_MSG_WDTR: + case A_SIR_MSG_REJECT: + case A_SIR_EV_RESPONSE_OK: + msgsm(dsa, c, legetl(n->dsps), &cont, &wakeme); + break; + case A_SIR_MSG_IGNORE_WIDE_RESIDUE: + /* back up one in the data transfer */ + IPRINT("sd53c8xx: %d/%d: ignore wide residue %d, WSR = %d\n", + dsa->target, dsa->lun, n->scratcha[1], n->scntl2 & 1); + if (dsa->dmablks == 0 && dsa->flag) + IPRINT("sd53c8xx: %d/%d: transfer over; residue ignored\n", + dsa->target, dsa->lun); + else + calcblockdma(dsa, legetl(dsa->dmaaddr) - 1, + dsa->dmablks * A_BSIZE + legetl(dsa->data_buf.dbc) + 1); + cont = -2; + break; + case A_SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT: + IPRINT("sd53c8xx: %d: not msg_in after reselect (%s)", + n->ssid & 7, phase[n->sstat1 & 7]); + dsa = dsafind(c, n->ssid & 7, -1, A_STATE_DISCONNECTED); + dumpncrregs(c, 1); + wakeme = 1; + break; + case A_SIR_NOTIFY_MSG_IN: + IPRINT("sd53c8xx: %d/%d: msg_in %d\n", + dsa->target, dsa->lun, n->sfbr); + cont = -2; + break; + case A_SIR_NOTIFY_DISC: + IPRINT("sd53c8xx: %d/%d: disconnect:", dsa->target, dsa->lun); + goto dsadump; + case A_SIR_NOTIFY_STATUS: + IPRINT("sd53c8xx: %d/%d: status\n", dsa->target, dsa->lun); + cont = -2; + break; + case A_SIR_NOTIFY_COMMAND: + IPRINT("sd53c8xx: %d/%d: commands\n", dsa->target, dsa->lun); + cont = -2; + break; + case A_SIR_NOTIFY_DATA_IN: + IPRINT("sd53c8xx: %d/%d: data in a %lx b %lx\n", + dsa->target, dsa->lun, legetl(n->scratcha), legetl(n->scratchb)); + cont = -2; + break; + case A_SIR_NOTIFY_BLOCK_DATA_IN: + IPRINT("sd53c8xx: %d/%d: block data in: a2 %x b %lx\n", + dsa->target, dsa->lun, n->scratcha[2], legetl(n->scratchb)); + cont = -2; + break; + case A_SIR_NOTIFY_DATA_OUT: + IPRINT("sd53c8xx: %d/%d: data out\n", dsa->target, dsa->lun); + cont = -2; + break; + case A_SIR_NOTIFY_DUMP: + IPRINT("sd53c8xx: %d/%d: dump\n", dsa->target, dsa->lun); + dumpncrregs(c, 1); + cont = -2; + break; + case A_SIR_NOTIFY_DUMP2: + IPRINT("sd53c8xx: %d/%d: dump2:", dsa->target, dsa->lun); + IPRINT(" sa %lux", legetl(n->dsp) - c->scriptpa); + IPRINT(" dsa %lux", legetl(n->dsa)); + IPRINT(" sfbr %ux", n->sfbr); + IPRINT(" a %lux", n->scratcha); + IPRINT(" b %lux", legetl(n->scratchb)); + IPRINT(" ssid %ux", n->ssid); + IPRINT("\n"); + cont = -2; + break; + case A_SIR_NOTIFY_WAIT_RESELECT: + IPRINT("sd53c8xx: wait reselect\n"); + cont = -2; + break; + case A_SIR_NOTIFY_RESELECT: + IPRINT("sd53c8xx: reselect: ssid %.2x sfbr %.2x at %ld\n", + n->ssid, n->sfbr, TK2MS(m->ticks)); + cont = -2; + break; + case A_SIR_NOTIFY_ISSUE: + IPRINT("sd53c8xx: %d/%d: issue:", dsa->target, dsa->lun); + dsadump: + IPRINT(" tgt=%d", dsa->target); + IPRINT(" time=%ld", TK2MS(m->ticks)); + IPRINT("\n"); + cont = -2; + break; + case A_SIR_NOTIFY_ISSUE_CHECK: + IPRINT("sd53c8xx: issue check\n"); + cont = -2; + break; + case A_SIR_NOTIFY_SIGP: + IPRINT("sd53c8xx: responded to SIGP\n"); + cont = -2; + break; + case A_SIR_NOTIFY_DUMP_NEXT_CODE: { + ulong *dsp = DMASEG_TO_KADDR(legetl(n->dsp)); + int x; + IPRINT("sd53c8xx: code at %lux", dsp - c->script); + for (x = 0; x < 6; x++) + IPRINT(" %.8lux", dsp[x]); + IPRINT("\n"); + USED(dsp); + cont = -2; + break; + } + case A_SIR_NOTIFY_WSR: + IPRINT("sd53c8xx: %d/%d: WSR set\n", dsa->target, dsa->lun); + cont = -2; + break; + case A_SIR_NOTIFY_LOAD_SYNC: + IPRINT("sd53c8xx: %d/%d: scntl=%.2x sxfer=%.2x\n", + dsa->target, dsa->lun, n->scntl3, n->sxfer); + cont = -2; + break; + case A_SIR_NOTIFY_RESELECTED_ON_SELECT: + IPRINT("sd53c8xx: %d/%d: reselected during select\n", + dsa->target, dsa->lun); + cont = -2; + break; + default: + IPRINT("sd53c8xx: %d/%d: script error %ld\n", + dsa->target, dsa->lun, legetl(n->dsps)); + dumpncrregs(c, 1); + wakeme = 1; + } + } + /*else*/ if (dstat & Iid) { + ulong addr = legetl(n->dsp); + ulong dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0]; + IPRINT("sd53c8xx: %d/%d: Iid pa=%.8lux sa=%.8lux dbc=%lux\n", + dsa->target, dsa->lun, + addr, addr - c->scriptpa, dbc); + addr = (ulong)DMASEG_TO_KADDR(addr); + IPRINT("%.8lux %.8lux %.8lux\n", + *(ulong *)(addr - 12), *(ulong *)(addr - 8), *(ulong *)(addr - 4)); + USED(addr, dbc); + dsa->p9status = SDeio; + wakeme = 1; + } + /*else*/ if (dstat & Bf) { + IPRINT("sd53c8xx: %d/%d: Bus Fault\n", dsa->target, dsa->lun); + dumpncrregs(c, 1); + dsa->p9status = SDeio; + wakeme = 1; + } + } + if (cont == -2) + ncrcontinue(c); + else if (cont >= 0) + start(c, cont); + if (wakeme){ + if(dsa->p9status == SDnostatus) + dsa->p9status = SDeio; + wakeup(dsa); + } + iunlock(c); + if (DEBUG(1)) { + IPRINT("sd53c8xx: int end 1\n"); + } +} + +static int +done(void *arg) +{ + return ((Dsa *)arg)->p9status != SDnostatus; +} + +static void +setmovedata(Movedata *d, ulong pa, ulong bc) +{ + d->pa[0] = pa; + d->pa[1] = pa>>8; + d->pa[2] = pa>>16; + d->pa[3] = pa>>24; + d->dbc[0] = bc; + d->dbc[1] = bc>>8; + d->dbc[2] = bc>>16; + d->dbc[3] = bc>>24; +} + +static void +advancedata(Movedata *d, long v) +{ + lesetl(d->pa, legetl(d->pa) + v); + lesetl(d->dbc, legetl(d->dbc) - v); +} + +static void +dumpwritedata(uchar *data, int datalen) +{ + int i; + uchar *bp; + if (!DEBUG(0)){ + USED(data, datalen); + return; + } + + if (datalen) { + KPRINT("sd53c8xx:write:"); + for (i = 0, bp = data; i < 50 && i < datalen; i++, bp++) + KPRINT("%.2ux", *bp); + if (i < datalen) { + KPRINT("..."); + } + KPRINT("\n"); + } +} + +static void +dumpreaddata(uchar *data, int datalen) +{ + int i; + uchar *bp; + if (!DEBUG(0)){ + USED(data, datalen); + return; + } + + if (datalen) { + KPRINT("sd53c8xx:read:"); + for (i = 0, bp = data; i < 50 && i < datalen; i++, bp++) + KPRINT("%.2ux", *bp); + if (i < datalen) { + KPRINT("..."); + } + KPRINT("\n"); + } +} + +static void +busreset(Controller *c) +{ + int x, ntarget; + + /* bus reset */ + c->n->scntl1 |= (1 << 3); + delay(500); + c->n->scntl1 &= ~(1 << 3); + if(!(c->v->feature & Wide)) + ntarget = 8; + else + ntarget = MAXTARGET; + for (x = 0; x < ntarget; x++) { + setwide(0, c, x, 0); +#ifndef ASYNC_ONLY + c->s[x] = NeitherDone; +#endif + } + c->capvalid = 0; +} + +static void +reset(Controller *c) +{ + /* should wakeup all pending tasks */ + softreset(c); + busreset(c); +} + +static int +symrio(SDreq* r) +{ + Dsa *d; + uchar *bp; + Controller *c; + uchar target_expo, my_expo; + int bc, check, status, target; + + if((target = r->unit->subno) == 0x07) + return r->status = SDtimeout; /* assign */ + c = r->unit->dev->ctlr; + + check = 0; + d = dsaalloc(c, target, r->lun); + + qlock(&c->q[target]); /* obtain access to target */ +docheck: + /* load the transfer control stuff */ + d->scsi_id_buf[0] = 0; + d->scsi_id_buf[1] = c->sxfer[target]; + d->scsi_id_buf[2] = target; + d->scsi_id_buf[3] = c->scntl3[target]; + synctodsa(d, c); + + bc = 0; + + d->msg_out[bc] = 0x80 | r->lun; + +#ifndef NO_DISCONNECT + d->msg_out[bc] |= (1 << 6); +#endif + bc++; + + /* work out what to do about negotiation */ + switch (c->s[target]) { + default: + KPRINT("sd53c8xx: %d: strange nego state %d\n", target, c->s[target]); + c->s[target] = NeitherDone; + /* fall through */ + case NeitherDone: + if ((c->capvalid & (1 << target)) == 0) + break; + target_expo = (c->cap[target] >> 5) & 3; + my_expo = (c->v->feature & Wide) != 0; + if (target_expo < my_expo) + my_expo = target_expo; +#ifdef ALWAYS_DO_WDTR + bc += buildwdtrmsg(d->msg_out + bc, my_expo); + KPRINT("sd53c8xx: %d: WDTN: initiating expo %d\n", target, my_expo); + c->s[target] = WideInit; + break; +#else + if (my_expo) { + bc += buildwdtrmsg(d->msg_out + bc, (c->v->feature & Wide) ? 1 : 0); + KPRINT("sd53c8xx: %d: WDTN: initiating expo %d\n", target, my_expo); + c->s[target] = WideInit; + break; + } + KPRINT("sd53c8xx: %d: WDTN: narrow\n", target); + /* fall through */ +#endif + case WideDone: + if (c->cap[target] & (1 << 4)) { + KPRINT("sd53c8xx: %d: SDTN: initiating %d %d\n", target, c->tpf, c->v->maxsyncoff); + bc += buildsdtrmsg(d->msg_out + bc, c->tpf, c->v->maxsyncoff); + c->s[target] = SyncInit; + break; + } + KPRINT("sd53c8xx: %d: SDTN: async only\n", target); + c->s[target] = BothDone; + break; + + case BothDone: + break; + } + + setmovedata(&d->msg_out_buf, DMASEG(d->msg_out), bc); + setmovedata(&d->cmd_buf, DMASEG(r->cmd), r->clen); + calcblockdma(d, DMASEG(r->data), r->dlen); + + if (DEBUG(0)) { + KPRINT("sd53c8xx: %d/%d: exec: ", target, r->lun); + for (bp = r->cmd; bp < &r->cmd[r->clen]; bp++) + KPRINT("%.2ux", *bp); + KPRINT("\n"); + if (!r->write) + KPRINT("sd53c8xx: %d/%d: exec: limit=(%d)%ld\n", + target, r->lun, d->dmablks, legetl(d->data_buf.dbc)); + else + dumpwritedata(r->data, r->dlen); + } + + setmovedata(&d->status_buf, DMASEG(&d->status), 1); + + d->p9status = SDnostatus; + d->parityerror = 0; + + d->stateb = A_STATE_ISSUE; /* start operation */ + + ilock(c); + if (c->ssm) + c->n->dcntl |= 0x10; /* SSI */ + if (c->running) { + c->n->istat |= Sigp; + } + else { + start(c, E_issue_check); + } + iunlock(c); + + while(waserror()) + ; + tsleep(d, done, d, 30 * 1000); + poperror(); + + if (!done(d)) { + KPRINT("sd53c8xx: %d/%d: exec: Timed out\n", target, r->lun); + dumpncrregs(c, 0); + dsafree(c, d); + reset(c); + qunlock(&c->q[target]); + r->status = SDtimeout; + return r->status = SDtimeout; /* assign */ + } + + if((status = d->p9status) == SDeio) + c->s[target] = NeitherDone; + if (d->parityerror) { + status = SDeio; + } + + /* + * adjust datalen + */ + r->rlen = r->dlen; + if (d->dmablks > 0) + r->rlen -= d->dmablks * A_BSIZE; + else if (d->flag == 0) + r->rlen -= legetl(d->data_buf.dbc); + if(!r->write) + dumpreaddata(r->data, r->rlen); + if (DEBUG(0)) + KPRINT("53c8xx: %d/%d: exec: p9status=%d status %d rlen %ld\n", + target, r->lun, d->p9status, status, r->rlen); + /* + * spot the identify + */ + if ((c->capvalid & (1 << target)) == 0 + && (status == SDok || status == SDcheck) + && r->cmd[0] == 0x12 && r->dlen >= 8) { + c->capvalid |= 1 << target; + bp = r->data; + c->cap[target] = bp[7]; + KPRINT("sd53c8xx: %d: capabilities %.2x\n", target, bp[7]); + } + if(!check && status == SDcheck && !(r->flags & SDnosense)){ + check = 1; + r->write = 0; + memset(r->cmd, 0, sizeof(r->cmd)); + r->cmd[0] = 0x03; + r->cmd[1] = r->lun<<5; + r->cmd[4] = sizeof(r->sense)-1; + r->clen = 6; + r->data = r->sense; + r->dlen = sizeof(r->sense)-1; + /* + * Clear out the microcode state + * so the Dsa can be re-used. + */ + lesetl(d->state, A_STATE_ALLOCATED); + goto docheck; + } + qunlock(&c->q[target]); + dsafree(c, d); + + if(status == SDok && check){ + status = SDcheck; + r->flags |= SDvalidsense; + } + KPRINT("sd53c8xx: %d: r flags %8.8uX status %d rlen %ld\n", + target, r->flags, status, r->rlen); + return r->status = status; +} + +static void +cribbios(Controller *c) +{ + c->bios.scntl3 = c->n->scntl3; + c->bios.stest2 = c->n->stest2; + print("sd53c8xx: bios scntl3(%.2x) stest2(%.2x)\n", c->bios.scntl3, c->bios.stest2); +} + +static int +bios_set_differential(Controller *c) +{ + /* Concept lifted from FreeBSD - thanks Gerard */ + /* basically, if clock conversion factors are set, then there is + * evidence the bios had a go at the chip, and if so, it would + * have set the differential enable bit in stest2 + */ + return (c->bios.scntl3 & 7) != 0 && (c->bios.stest2 & 0x20) != 0; +} + +#define NCR_VID 0x1000 +#define NCR_810_DID 0x0001 +#define NCR_820_DID 0x0002 /* don't know enough about this one to support it */ +#define NCR_825_DID 0x0003 +#define NCR_815_DID 0x0004 +#define SYM_810AP_DID 0x0005 +#define SYM_860_DID 0x0006 +#define SYM_896_DID 0x000b +#define SYM_895_DID 0x000c +#define SYM_885_DID 0x000d /* ditto */ +#define SYM_875_DID 0x000f /* ditto */ +#define SYM_1010_DID 0x0020 +#define SYM_1011_DID 0x0021 +#define SYM_875J_DID 0x008f + +static Variant variant[] = { +{ NCR_810_DID, 0x0f, "NCR53C810", Burst16, 8, 24, 0 }, +{ NCR_810_DID, 0x1f, "SYM53C810ALV", Burst16, 8, 24, Prefetch }, +{ NCR_810_DID, 0xff, "SYM53C810A", Burst16, 8, 24, Prefetch }, +{ SYM_810AP_DID, 0xff, "SYM53C810AP", Burst16, 8, 24, Prefetch }, +{ NCR_815_DID, 0xff, "NCR53C815", Burst16, 8, 24, BurstOpCodeFetch }, +{ NCR_825_DID, 0x0f, "NCR53C825", Burst16, 8, 24, Wide|BurstOpCodeFetch|Differential }, +{ NCR_825_DID, 0xff, "SYM53C825A", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide }, +{ SYM_860_DID, 0x0f, "SYM53C860", Burst16, 8, 24, Prefetch|Ultra }, +{ SYM_860_DID, 0xff, "SYM53C860LV", Burst16, 8, 24, Prefetch|Ultra }, +{ SYM_875_DID, 0x01, "SYM53C875r1", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra }, +{ SYM_875_DID, 0xff, "SYM53C875", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra|ClockDouble }, +{ SYM_875J_DID, 0xff, "SYM53C875j", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra|ClockDouble }, +{ SYM_885_DID, 0xff, "SYM53C885", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Wide|Ultra|ClockDouble }, +{ SYM_895_DID, 0xff, "SYM53C895", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 }, +{ SYM_896_DID, 0xff, "SYM53C896", Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 }, +{ SYM_1010_DID, 0xff, "SYM53C1010", Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 }, +{ SYM_1011_DID, 0xff, "SYM53C1010", Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 }, +}; + +#define offsetof(s, t) ((ulong)&((s *)0)->t) + +static int +xfunc(Controller *c, enum na_external x, unsigned long *v) +{ + switch (x) + { + case X_scsi_id_buf: + *v = offsetof(Dsa, scsi_id_buf[0]); return 1; + case X_msg_out_buf: + *v = offsetof(Dsa, msg_out_buf); return 1; + case X_cmd_buf: + *v = offsetof(Dsa, cmd_buf); return 1; + case X_data_buf: + *v = offsetof(Dsa, data_buf); return 1; + case X_status_buf: + *v = offsetof(Dsa, status_buf); return 1; + case X_dsa_head: + *v = DMASEG(&c->dsalist.head[0]); return 1; + default: + print("xfunc: can't find external %d\n", x); + return 0; + } + return 1; +} + +static int +na_fixup(Controller *c, ulong pa_reg, + struct na_patch *patch, int patches, + int (*externval)(Controller*, int, ulong*)) +{ + int p; + int v; + ulong *script, pa_script; + unsigned long lw, lv; + + script = c->script; + pa_script = c->scriptpa; + for (p = 0; p < patches; p++) { + switch (patch[p].type) { + case 1: + /* script relative */ + script[patch[p].lwoff] += pa_script; + break; + case 2: + /* register i/o relative */ + script[patch[p].lwoff] += pa_reg; + break; + case 3: + /* data external */ + lw = script[patch[p].lwoff]; + v = (lw >> 8) & 0xff; + if (!(*externval)(c, v, &lv)) + return 0; + v = lv & 0xff; + script[patch[p].lwoff] = (lw & 0xffff00ffL) | (v << 8); + break; + case 4: + /* 32 bit external */ + lw = script[patch[p].lwoff]; + if (!(*externval)(c, lw, &lv)) + return 0; + script[patch[p].lwoff] = lv; + break; + case 5: + /* 24 bit external */ + lw = script[patch[p].lwoff]; + if (!(*externval)(c, lw & 0xffffff, &lv)) + return 0; + script[patch[p].lwoff] = (lw & 0xff000000L) | (lv & 0xffffffL); + break; + } + } + return 1; +} + +static SDev* +sympnp(void) +{ + int ba; + Pcidev *p; + Variant *v; + void *scriptma; + Controller *ctlr; + SDev *sdev, *head, *tail; + ulong regpa, *script, scriptpa; + + p = nil; + head = tail = nil; + while(p = pcimatch(p, NCR_VID, 0)){ + for(v = variant; v < &variant[nelem(variant)]; v++){ + if(p->did == v->did && p->rid <= v->maxrid) + break; + } + if(v >= &variant[nelem(variant)]) + continue; + print("sd53c8xx: %s rev. 0x%2.2x intr=%d command=%4.4luX\n", + v->name, p->rid, p->intl, p->pcr); + + regpa = p->mem[1].bar; + ba = 2; + if(regpa & 0x04){ + if(p->mem[2].bar) + continue; + ba++; + } + regpa = upamalloc(regpa & ~0x0F, p->mem[1].size, 0); + if(regpa == 0) + continue; + + script = nil; + scriptpa = 0; + scriptma = nil; + if((v->feature & LocalRAM) && sizeof(na_script) <= 4096){ + scriptpa = p->mem[ba].bar; + if((scriptpa & 0x04) && p->mem[ba+1].bar){ + upafree(regpa, p->mem[1].size); + continue; + } + scriptpa = upamalloc(scriptpa & ~0x0F, + p->mem[ba].size, 0); + if(scriptpa) + script = KADDR(scriptpa); + } + if(scriptpa == 0){ + /* + * Either the map failed, or this chip does not have + * local RAM. It will need a copy of the microcode. + */ + scriptma = malloc(sizeof(na_script)); + if(scriptma == nil){ + upafree(regpa, p->mem[1].size); + continue; + } + scriptpa = DMASEG(scriptma); + script = scriptma; + } + + ctlr = malloc(sizeof(Controller)); + sdev = malloc(sizeof(SDev)); + if(ctlr == nil || sdev == nil){ +buggery: + if(ctlr) + free(ctlr); + if(sdev) + free(sdev); + if(scriptma) + free(scriptma); + else + upafree(scriptpa, p->mem[ba].size); + upafree(regpa, p->mem[1].size); + continue; + } + + ctlr->n = KADDR(regpa); + ctlr->v = v; + ctlr->script = script; + memmove(ctlr->script, na_script, sizeof(na_script)); + ctlr->scriptpa = scriptpa; + if(!na_fixup(ctlr, regpa, na_patches, NA_PATCHES, xfunc)){ + print("script fixup failed\n"); + goto buggery; + } + swabl(ctlr->script, ctlr->script, sizeof(na_script)); + + ctlr->dsalist.freechain = 0; + lesetl(ctlr->dsalist.head, 0); + + ctlr->pcidev = p; + + sdev->ifc = &sd53c8xxifc; + sdev->ctlr = ctlr; + if(!(v->feature & Wide)) + sdev->nunit = 8; + else + sdev->nunit = MAXTARGET; + ctlr->sdev = sdev; + + if(head != nil) + tail->next = sdev; + else + head = sdev; + tail = sdev; + } + + return head; +} + +static SDev* +symid(SDev* sdev) +{ + return scsiid(sdev, &sd53c8xxifc); +} + +static int +symenable(SDev* sdev) +{ + Pcidev *pcidev; + Controller *ctlr; + //char name[NAMELEN]; + + ctlr = sdev->ctlr; + pcidev = ctlr->pcidev; + + pcisetbme(pcidev); + //snprint(name, NAMELEN, "%s (%s)", sdev->name, sdev->ifc->name); + intrenable(pcidev->intl, interrupt, ctlr, pcidev->tbdf, name); + + ilock(ctlr); + synctabinit(ctlr); + cribbios(ctlr); + reset(ctlr); + iunlock(ctlr); + + return 1; +} + +static int +symdisable(SDev* sdev) +{ + Ncr *n; + Controller *ctlr; + + ctlr = sdev->ctlr; + n = ctlr->n; + + n->istat = Srst; /* software reset */ + microdelay(1); + n->istat = 0; + + n->scntl1 |= (1 << 3); /* bus reset */ + delay(500); + n->scntl1 &= ~(1 << 3); + + return 1; +} + +SDifc sd53c8xxifc = { + "53c8xx", /* name */ + + sympnp, /* pnp */ + nil, /* legacy */ + symid, /* id */ + symenable, /* enable */ + symdisable, /* disable */ + + scsiverify, /* verify */ + scsionline, /* online */ + symrio, /* rio */ + nil, /* rctl */ + nil, /* wctl */ + + scsibio, /* bio */ +}; diff --git a/os/boot/pc/sd53c8xx.i b/os/boot/pc/sd53c8xx.i new file mode 100644 index 00000000..50322366 --- /dev/null +++ b/os/boot/pc/sd53c8xx.i @@ -0,0 +1,769 @@ +unsigned long na_script[] = { + /* extern scsi_id_buf */ + /* extern msg_out_buf */ + /* extern cmd_buf */ + /* extern data_buf */ + /* extern status_buf */ + /* extern msgin_buf */ + /* extern dsa_0 */ + /* extern dsa_1 */ + /* extern dsa_head */ + /* SIR_MSG_IO_COMPLETE = 0 */ + /* error_not_cmd_complete = 1 */ + /* error_disconnected = 2 */ + /* error_reselected = 3 */ + /* error_unexpected_phase = 4 */ + /* error_weird_message = 5 */ + /* SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT = 6 */ + /* error_not_identify_after_reselect = 7 */ + /* error_too_much_data = 8 */ + /* error_too_little_data = 9 */ + /* SIR_MSG_REJECT = 10 */ + /* SIR_MSG_SDTR = 11 */ + /* SIR_EV_RESPONSE_OK = 12 */ + /* error_sigp_set = 13 */ + /* SIR_EV_PHASE_SWITCH_AFTER_ID = 14 */ + /* SIR_MSG_WDTR = 15 */ + /* SIR_MSG_IGNORE_WIDE_RESIDUE = 16 */ + /* SIR_NOTIFY_DISC = 100 */ + /* SIR_NOTIFY_RESELECT = 101 */ + /* SIR_NOTIFY_MSG_IN = 102 */ + /* SIR_NOTIFY_STATUS = 103 */ + /* SIR_NOTIFY_DUMP = 104 */ + /* SIR_NOTIFY_DUMP2 = 105 */ + /* SIR_NOTIFY_SIGP = 106 */ + /* SIR_NOTIFY_ISSUE = 107 */ + /* SIR_NOTIFY_WAIT_RESELECT = 108 */ + /* SIR_NOTIFY_ISSUE_CHECK = 109 */ + /* SIR_NOTIFY_DUMP_NEXT_CODE = 110 */ + /* SIR_NOTIFY_COMMAND = 111 */ + /* SIR_NOTIFY_DATA_IN = 112 */ + /* SIR_NOTIFY_DATA_OUT = 113 */ + /* SIR_NOTIFY_BLOCK_DATA_IN = 114 */ + /* SIR_NOTIFY_WSR = 115 */ + /* SIR_NOTIFY_LOAD_SYNC = 116 */ + /* SIR_NOTIFY_RESELECTED_ON_SELECT = 117 */ + /* STATE_FREE = 0 */ + /* STATE_ALLOCATED = 1 */ + /* STATE_ISSUE = 2 */ + /* STATE_DISCONNECTED = 3 */ + /* STATE_DONE = 4 */ + /* RESULT_OK = 0 */ + /* MSG_IDENTIFY = 0x80 */ + /* MSG_DISCONNECT = 0x04 */ + /* MSG_SAVE_DATA_POINTER = 0x02 */ + /* MSG_RESTORE_POINTERS = 0x03 */ + /* MSG_IGNORE_WIDE_RESIDUE = 0x23 */ + /* X_MSG = 0x01 */ + /* X_MSG_SDTR = 0x01 */ + /* X_MSG_WDTR = 0x03 */ + /* MSG_REJECT = 0x07 */ + /* BSIZE = 512 */ +/* 0000 */ 0x80880000L, /* jump wait_for_reselection */ +/* 0004 */ 0x00000514L, +/* 0008 */ 0x88880000L, /* call load_sync */ +/* 000c */ 0x0000074cL, +/* 0010 */ 0x60000200L, /* clear target */ +/* 0014 */ 0x00000000L, +/* 0018 */ 0x47000000L, /* select atn from scsi_id_buf, reselected_on_select */ +/* 001c */ 0x000004ecL, +/* 0020 */ 0x878b0000L, /* jump start1, when msg_in */ +/* 0024 */ 0x00000000L, +/* 0028 */ 0x1e000000L, /* move from msg_out_buf, when msg_out */ +/* 002c */ 0x00000001L, +/* 0030 */ 0x868b0000L, /* jump start1, when msg_out */ +/* 0034 */ 0x00fffff0L, +/* 0038 */ 0x82830000L, /* jump to_decisions, when not cmd */ +/* 003c */ 0x000005f0L, +/* 0040 */ 0x60000008L, /* clear atn */ +/* 0044 */ 0x00000000L, +/* 0048 */ 0x1a000000L, /* move from cmd_buf, when cmd */ +/* 004c */ 0x00000002L, +/* 0050 */ 0x81830000L, /* jump to_decisions, when not data_in */ +/* 0054 */ 0x000005d8L, +/* 0058 */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 005c */ 0x00000678L, +/* 0060 */ 0x00000034L, +/* 0064 */ 0xc0000004L, /* move memory 4, dmaaddr, scratchb */ +/* 0068 */ 0x0000067cL, +/* 006c */ 0x0000005cL, +/* 0070 */ 0x72360000L, /* move scratcha2 to sfbr */ +/* 0074 */ 0x00000000L, +/* 0078 */ 0x808c0000L, /* jump data_in_normal, if 0 */ +/* 007c */ 0x00000078L, +/* 0080 */ 0x29000200L, /* move BSIZE, ptr dmaaddr, when data_in */ +/* 0084 */ 0x0000067cL, +/* 0088 */ 0x7e5d0200L, /* move scratchb1 + BSIZE / 256 to scratchb1 */ +/* 008c */ 0x00000000L, +/* 0090 */ 0x7f5e0000L, /* move scratchb2 + 0 to scratchb2 with carry */ +/* 0094 */ 0x00000000L, +/* 0098 */ 0x7f5f0000L, /* move scratchb3 + 0 to scratchb3 with carry */ +/* 009c */ 0x00000000L, +/* 00a0 */ 0x7e36ff00L, /* move scratcha2 + 255 to scratcha2 */ +/* 00a4 */ 0x00000000L, +/* 00a8 */ 0xc0000004L, /* move memory 4, scratchb, dmaaddr */ +/* 00ac */ 0x0000005cL, +/* 00b0 */ 0x0000067cL, +/* 00b4 */ 0x818b0000L, /* jump data_in_block_loop, when data_in */ +/* 00b8 */ 0x00ffffb4L, +/* 00bc */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 00c0 */ 0x00000034L, +/* 00c4 */ 0x00000678L, +/* 00c8 */ 0x88880000L, /* call save_state */ +/* 00cc */ 0x000005e0L, +/* 00d0 */ 0x80880000L, /* jump to_decisions */ +/* 00d4 */ 0x00000558L, +/* 00d8 */ 0xc0000004L, /* move memory 4, scratchb, dmaaddr */ +/* 00dc */ 0x0000005cL, +/* 00e0 */ 0x0000067cL, +/* 00e4 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 00e8 */ 0x00000034L, +/* 00ec */ 0x00000678L, +/* 00f0 */ 0x80880000L, /* jump to_decisions */ +/* 00f4 */ 0x00000538L, +/* 00f8 */ 0x72370000L, /* move scratcha3 to sfbr */ +/* 00fc */ 0x00000000L, +/* 0100 */ 0x98040000L, /* int error_too_much_data, if not 0 */ +/* 0104 */ 0x00000008L, +/* 0108 */ 0x19000000L, /* move from data_buf, when data_in */ +/* 010c */ 0x00000003L, +/* 0110 */ 0x78370100L, /* move 1 to scratcha3 */ +/* 0114 */ 0x00000000L, +/* 0118 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 011c */ 0x00000034L, +/* 0120 */ 0x00000678L, +/* 0124 */ 0x88880000L, /* call save_state */ +/* 0128 */ 0x00000584L, +/* 012c */ 0x80880000L, /* jump post_data_to_decisions */ +/* 0130 */ 0x0000052cL, +/* 0134 */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 0138 */ 0x00000678L, +/* 013c */ 0x00000034L, +/* 0140 */ 0xc0000004L, /* move memory 4, dmaaddr, scratchb */ +/* 0144 */ 0x0000067cL, +/* 0148 */ 0x0000005cL, +/* 014c */ 0x72360000L, /* move scratcha2 to sfbr */ +/* 0150 */ 0x00000000L, +/* 0154 */ 0x808c0000L, /* jump data_out_normal, if 0 */ +/* 0158 */ 0x0000005cL, +/* 015c */ 0xc0000004L, /* move memory 4, dmaaddr, scratchb */ +/* 0160 */ 0x0000067cL, +/* 0164 */ 0x0000005cL, +/* 0168 */ 0x28000200L, /* move BSIZE, ptr dmaaddr, when data_out */ +/* 016c */ 0x0000067cL, +/* 0170 */ 0x7e5d0200L, /* move scratchb1 + BSIZE / 256 to scratchb1 */ +/* 0174 */ 0x00000000L, +/* 0178 */ 0x7f5e0000L, /* move scratchb2 + 0 to scratchb2 with carry */ +/* 017c */ 0x00000000L, +/* 0180 */ 0x7f5f0000L, /* move scratchb3 + 0 to scratchb3 with carry */ +/* 0184 */ 0x00000000L, +/* 0188 */ 0x7e36ff00L, /* move scratcha2 + 255 to scratcha2 */ +/* 018c */ 0x00000000L, +/* 0190 */ 0xc0000004L, /* move memory 4, scratchb, dmaaddr */ +/* 0194 */ 0x0000005cL, +/* 0198 */ 0x0000067cL, +/* 019c */ 0x808b0000L, /* jump data_out_block_loop, when data_out */ +/* 01a0 */ 0x00ffffa8L, +/* 01a4 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 01a8 */ 0x00000034L, +/* 01ac */ 0x00000678L, +/* 01b0 */ 0x80880000L, /* jump to_decisions */ +/* 01b4 */ 0x00000478L, +/* 01b8 */ 0x72370000L, /* move scratcha3 to sfbr */ +/* 01bc */ 0x00000000L, +/* 01c0 */ 0x98040000L, /* int error_too_little_data, if not 0 */ +/* 01c4 */ 0x00000009L, +/* 01c8 */ 0x18000000L, /* move from data_buf, when data_out */ +/* 01cc */ 0x00000003L, +/* 01d0 */ 0x78370100L, /* move 1 to scratcha3 */ +/* 01d4 */ 0x00000000L, +/* 01d8 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 01dc */ 0x00000034L, +/* 01e0 */ 0x00000678L, +/* 01e4 */ 0x88880000L, /* call save_state */ +/* 01e8 */ 0x000004c4L, +/* 01ec */ 0x80880000L, /* jump post_data_to_decisions */ +/* 01f0 */ 0x0000046cL, +/* 01f4 */ 0x1b000000L, /* move from status_buf, when status */ +/* 01f8 */ 0x00000004L, +/* 01fc */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0200 */ 0x00000004L, +/* 0204 */ 0x0f000001L, /* move 1, scratcha, when msg_in */ +/* 0208 */ 0x00000034L, +/* 020c */ 0x808c0007L, /* jump rejected, if MSG_REJECT */ +/* 0210 */ 0x00000088L, +/* 0214 */ 0x808c0004L, /* jump disconnected, if MSG_DISCONNECT */ +/* 0218 */ 0x00000298L, +/* 021c */ 0x808c0002L, /* jump msg_in_skip, if MSG_SAVE_DATA_POINTER */ +/* 0220 */ 0x00000090L, +/* 0224 */ 0x808c0003L, /* jump msg_in_skip, if MSG_RESTORE_POINTERS */ +/* 0228 */ 0x00000088L, +/* 022c */ 0x808c0023L, /* jump ignore_wide, if MSG_IGNORE_WIDE_RESIDUE */ +/* 0230 */ 0x000001f0L, +/* 0234 */ 0x808c0001L, /* jump extended, if X_MSG */ +/* 0238 */ 0x00000088L, +/* 023c */ 0x98040000L, /* int error_not_cmd_complete, if not 0 */ +/* 0240 */ 0x00000001L, +/* 0244 */ 0x7c027e00L, /* move scntl2&0x7e to scntl2 */ +/* 0248 */ 0x00000000L, +/* 024c */ 0x60000040L, /* clear ack */ +/* 0250 */ 0x00000000L, +/* 0254 */ 0x48000000L, /* wait disconnect */ +/* 0258 */ 0x00000000L, +/* 025c */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 0260 */ 0x00000678L, +/* 0264 */ 0x00000034L, +/* 0268 */ 0x78340400L, /* move STATE_DONE to scratcha0 */ +/* 026c */ 0x00000000L, +/* 0270 */ 0x78350000L, /* move RESULT_OK to scratcha1 */ +/* 0274 */ 0x00000000L, +/* 0278 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 027c */ 0x00000034L, +/* 0280 */ 0x00000678L, +/* 0284 */ 0x88880000L, /* call save_state */ +/* 0288 */ 0x00000424L, +/* 028c */ 0x98180000L, /* intfly 0 */ +/* 0290 */ 0x00000000L, +/* 0294 */ 0x80880000L, /* jump issue_check */ +/* 0298 */ 0x0000043cL, +/* 029c */ 0x98080000L, /* int SIR_MSG_REJECT */ +/* 02a0 */ 0x0000000aL, +/* 02a4 */ 0x60000040L, /* clear ack */ +/* 02a8 */ 0x00000000L, +/* 02ac */ 0x80880000L, /* jump to_decisions */ +/* 02b0 */ 0x0000037cL, +/* 02b4 */ 0x60000040L, /* clear ack */ +/* 02b8 */ 0x00000000L, +/* 02bc */ 0x80880000L, /* jump to_decisions */ +/* 02c0 */ 0x0000036cL, +/* 02c4 */ 0x60000040L, /* clear ack */ +/* 02c8 */ 0x00000000L, +/* 02cc */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 02d0 */ 0x00000004L, +/* 02d4 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */ +/* 02d8 */ 0x00000035L, +/* 02dc */ 0x808c0003L, /* jump ext_3, if 3 */ +/* 02e0 */ 0x00000030L, +/* 02e4 */ 0x808c0002L, /* jump ext_2, if 2 */ +/* 02e8 */ 0x00000098L, +/* 02ec */ 0x98040001L, /* int error_weird_message, if not 1 */ +/* 02f0 */ 0x00000005L, +/* 02f4 */ 0x60000040L, /* clear ack */ +/* 02f8 */ 0x00000000L, +/* 02fc */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0300 */ 0x00000004L, +/* 0304 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */ +/* 0308 */ 0x00000035L, +/* 030c */ 0x80880000L, /* jump ext_done */ +/* 0310 */ 0x000000c8L, +/* 0314 */ 0x60000040L, /* ext_3: clear ack */ +/* 0318 */ 0x00000000L, +/* 031c */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0320 */ 0x00000004L, +/* 0324 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */ +/* 0328 */ 0x00000035L, +/* 032c */ 0x60000040L, /* clear ack */ +/* 0330 */ 0x00000000L, +/* 0334 */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0338 */ 0x00000004L, +/* 033c */ 0x0f000001L, /* move 1, scratcha2, when msg_in */ +/* 0340 */ 0x00000036L, +/* 0344 */ 0x60000040L, /* clear ack */ +/* 0348 */ 0x00000000L, +/* 034c */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0350 */ 0x00000004L, +/* 0354 */ 0x0f000001L, /* move 1, scratcha3, when msg_in */ +/* 0358 */ 0x00000037L, +/* 035c */ 0x72350000L, /* move scratcha1 to sfbr */ +/* 0360 */ 0x00000000L, +/* 0364 */ 0x80840001L, /* jump ext_done, if not X_MSG_SDTR */ +/* 0368 */ 0x00000070L, +/* 036c */ 0x98080000L, /* sdtr: int SIR_MSG_SDTR */ +/* 0370 */ 0x0000000bL, +/* 0374 */ 0x60000040L, /* clear ack */ +/* 0378 */ 0x00000000L, +/* 037c */ 0x80880000L, /* jump to_decisions */ +/* 0380 */ 0x000002acL, +/* 0384 */ 0x60000040L, /* ext_2: clear ack */ +/* 0388 */ 0x00000000L, +/* 038c */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0390 */ 0x00000004L, +/* 0394 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */ +/* 0398 */ 0x00000035L, +/* 039c */ 0x60000040L, /* clear ack */ +/* 03a0 */ 0x00000000L, +/* 03a4 */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 03a8 */ 0x00000004L, +/* 03ac */ 0x0f000001L, /* move 1, scratcha2, when msg_in */ +/* 03b0 */ 0x00000036L, +/* 03b4 */ 0x72350000L, /* move scratcha1 to sfbr */ +/* 03b8 */ 0x00000000L, +/* 03bc */ 0x80840003L, /* jump ext_done, if not X_MSG_WDTR */ +/* 03c0 */ 0x00000018L, +/* 03c4 */ 0x98080000L, /* wdtr: int SIR_MSG_WDTR */ +/* 03c8 */ 0x0000000fL, +/* 03cc */ 0x60000040L, /* clear ack */ +/* 03d0 */ 0x00000000L, +/* 03d4 */ 0x80880000L, /* jump to_decisions */ +/* 03d8 */ 0x00000254L, +/* 03dc */ 0x58000008L, /* set atn */ +/* 03e0 */ 0x00000000L, +/* 03e4 */ 0x60000040L, /* clear ack */ +/* 03e8 */ 0x00000000L, +/* 03ec */ 0x78340700L, /* move MSG_REJECT to scratcha */ +/* 03f0 */ 0x00000000L, +/* 03f4 */ 0x9e030000L, /* int error_unexpected_phase, when not msg_out */ +/* 03f8 */ 0x00000004L, +/* 03fc */ 0x60000008L, /* clear atn */ +/* 0400 */ 0x00000000L, +/* 0404 */ 0x0e000001L, /* move 1, scratcha, when msg_out */ +/* 0408 */ 0x00000034L, +/* 040c */ 0x60000040L, /* clear ack */ +/* 0410 */ 0x00000000L, +/* 0414 */ 0x868b0000L, /* jump reject, when msg_out */ +/* 0418 */ 0x00ffffc0L, +/* 041c */ 0x80880000L, /* jump to_decisions */ +/* 0420 */ 0x0000020cL, +/* 0424 */ 0x60000040L, /* clear ack */ +/* 0428 */ 0x00000000L, +/* 042c */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0430 */ 0x00000004L, +/* 0434 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */ +/* 0438 */ 0x00000035L, +/* 043c */ 0x98080000L, /* int SIR_MSG_IGNORE_WIDE_RESIDUE */ +/* 0440 */ 0x00000010L, +/* 0444 */ 0x60000040L, /* clear ack */ +/* 0448 */ 0x00000000L, +/* 044c */ 0x80880000L, /* jump to_decisions */ +/* 0450 */ 0x000001dcL, +/* 0454 */ 0x58000008L, /* set atn */ +/* 0458 */ 0x00000000L, +/* 045c */ 0x60000040L, /* clear ack */ +/* 0460 */ 0x00000000L, +/* 0464 */ 0x9e030000L, /* int error_unexpected_phase, when not msg_out */ +/* 0468 */ 0x00000004L, +/* 046c */ 0x1e000000L, /* move from msg_out_buf, when msg_out */ +/* 0470 */ 0x00000001L, +/* 0474 */ 0x868b0000L, /* jump response_repeat, when msg_out */ +/* 0478 */ 0x00fffff0L, +/* 047c */ 0x878b0000L, /* jump response_msg_in, when msg_in */ +/* 0480 */ 0x00000010L, +/* 0484 */ 0x98080000L, /* int SIR_EV_RESPONSE_OK */ +/* 0488 */ 0x0000000cL, +/* 048c */ 0x80880000L, /* jump to_decisions */ +/* 0490 */ 0x0000019cL, +/* 0494 */ 0x0f000001L, /* move 1, scratcha, when msg_in */ +/* 0498 */ 0x00000034L, +/* 049c */ 0x808c0007L, /* jump rejected, if MSG_REJECT */ +/* 04a0 */ 0x00fffdf8L, +/* 04a4 */ 0x98080000L, /* int SIR_EV_RESPONSE_OK */ +/* 04a8 */ 0x0000000cL, +/* 04ac */ 0x80880000L, /* jump msg_in_not_reject */ +/* 04b0 */ 0x00fffd60L, +/* 04b4 */ 0x7c027e00L, /* move scntl2&0x7e to scntl2 */ +/* 04b8 */ 0x00000000L, +/* 04bc */ 0x60000040L, /* clear ack */ +/* 04c0 */ 0x00000000L, +/* 04c4 */ 0x48000000L, /* wait disconnect */ +/* 04c8 */ 0x00000000L, +/* 04cc */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 04d0 */ 0x00000678L, +/* 04d4 */ 0x00000034L, +/* 04d8 */ 0x78340300L, /* move STATE_DISCONNECTED to scratcha0 */ +/* 04dc */ 0x00000000L, +/* 04e0 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 04e4 */ 0x00000034L, +/* 04e8 */ 0x00000678L, +/* 04ec */ 0x88880000L, /* call save_state */ +/* 04f0 */ 0x000001bcL, +/* 04f4 */ 0x74020100L, /* move scntl2&0x01 to sfbr */ +/* 04f8 */ 0x00000000L, +/* 04fc */ 0x98040000L, /* int SIR_NOTIFY_WSR, if not 0 */ +/* 0500 */ 0x00000073L, +/* 0504 */ 0x80880000L, /* jump issue_check */ +/* 0508 */ 0x000001ccL, +/* 050c */ 0x98080000L, /* int SIR_NOTIFY_RESELECTED_ON_SELECT */ +/* 0510 */ 0x00000075L, +/* 0514 */ 0x80880000L, /* jump reselected */ +/* 0518 */ 0x00000008L, +/* 051c */ 0x54000000L, /* wait reselect sigp_set */ +/* 0520 */ 0x000001acL, +/* 0524 */ 0x60000200L, /* clear target */ +/* 0528 */ 0x00000000L, +/* 052c */ 0x9f030000L, /* int SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT, when not msg_in */ +/* 0530 */ 0x00000006L, +/* 0534 */ 0x0f000001L, /* move 1, scratchb, when msg_in */ +/* 0538 */ 0x0000005cL, +/* 053c */ 0x98041f80L, /* int error_not_identify_after_reselect, if not MSG_IDENTIFY and mask 0x1f */ +/* 0540 */ 0x00000007L, +/* 0544 */ 0xc0000004L, /* move memory 4, dsa_head, dsa */ +/* 0548 */ 0x00000008L, +/* 054c */ 0x00000010L, +/* 0550 */ 0x72100000L, /* move dsa0 to sfbr */ +/* 0554 */ 0x00000000L, +/* 0558 */ 0x80840000L, /* jump find_dsa_1, if not 0 */ +/* 055c */ 0x00000030L, +/* 0560 */ 0x72110000L, /* move dsa1 to sfbr */ +/* 0564 */ 0x00000000L, +/* 0568 */ 0x80840000L, /* jump find_dsa_1, if not 0 */ +/* 056c */ 0x00000020L, +/* 0570 */ 0x72120000L, /* move dsa2 to sfbr */ +/* 0574 */ 0x00000000L, +/* 0578 */ 0x80840000L, /* jump find_dsa_1, if not 0 */ +/* 057c */ 0x00000010L, +/* 0580 */ 0x72130000L, /* move dsa3 to sfbr */ +/* 0584 */ 0x00000000L, +/* 0588 */ 0x980c0000L, /* int error_reselected, if 0 */ +/* 058c */ 0x00000003L, +/* 0590 */ 0x88880000L, /* call load_state */ +/* 0594 */ 0x000000f8L, +/* 0598 */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 059c */ 0x00000678L, +/* 05a0 */ 0x00000034L, +/* 05a4 */ 0x72340000L, /* move scratcha0 to sfbr */ +/* 05a8 */ 0x00000000L, +/* 05ac */ 0x80840003L, /* jump find_dsa_next, if not STATE_DISCONNECTED */ +/* 05b0 */ 0x00000038L, +/* 05b4 */ 0x740a0f00L, /* move ssid & 15 to sfbr */ +/* 05b8 */ 0x00000000L, +/* 05bc */ 0xc0000001L, /* move memory 1, targ, find_dsa_smc1 */ +/* 05c0 */ 0x00000680L, +/* 05c4 */ 0x000005c8L, +/* 05c8 */ 0x808400ffL, /* jump find_dsa_next, if not 255 */ +/* 05cc */ 0x0000001cL, +/* 05d0 */ 0xc0000001L, /* move memory 1, lun, find_dsa_smc2 */ +/* 05d4 */ 0x00000684L, +/* 05d8 */ 0x000005e4L, +/* 05dc */ 0x725c0000L, /* move scratchb0 to sfbr */ +/* 05e0 */ 0x00000000L, +/* 05e4 */ 0x808cf8ffL, /* jump reload_sync, if 255 and mask ~7 */ +/* 05e8 */ 0x00000034L, +/* 05ec */ 0xc0000004L, /* move memory 4, next, dsa */ +/* 05f0 */ 0x0000068cL, +/* 05f4 */ 0x00000010L, +/* 05f8 */ 0x80880000L, /* jump find_dsa_loop */ +/* 05fc */ 0x00ffff50L, +/* 0600 */ 0x60000008L, /* clear atn */ +/* 0604 */ 0x00000000L, +/* 0608 */ 0x878b0000L, /* jump msg_in_phase, when msg_in */ +/* 060c */ 0x00fffbf4L, +/* 0610 */ 0x98080000L, /* int SIR_MSG_REJECT */ +/* 0614 */ 0x0000000aL, +/* 0618 */ 0x80880000L, /* jump to_decisions */ +/* 061c */ 0x00000010L, +/* 0620 */ 0x88880000L, /* call load_sync */ +/* 0624 */ 0x00000134L, +/* 0628 */ 0x60000040L, /* clear ack */ +/* 062c */ 0x00000000L, +/* 0630 */ 0x818b0000L, /* jump data_in_phase, when data_in */ +/* 0634 */ 0x00fffa20L, +/* 0638 */ 0x828a0000L, /* jump cmd_phase, if cmd */ +/* 063c */ 0x00fffa00L, +/* 0640 */ 0x808a0000L, /* jump data_out_phase, if data_out */ +/* 0644 */ 0x00fffaecL, +/* 0648 */ 0x838a0000L, /* jump status_phase, if status */ +/* 064c */ 0x00fffba4L, +/* 0650 */ 0x878a0000L, /* jump msg_in_phase, if msg_in */ +/* 0654 */ 0x00fffbacL, +/* 0658 */ 0x98080000L, /* int error_unexpected_phase */ +/* 065c */ 0x00000004L, +/* 0660 */ 0x838b0000L, /* jump status_phase, when status */ +/* 0664 */ 0x00fffb8cL, +/* 0668 */ 0x878a0000L, /* jump msg_in_phase, if msg_in */ +/* 066c */ 0x00fffb94L, +/* 0670 */ 0x98080000L, /* int error_unexpected_phase */ +/* 0674 */ 0x00000004L, +/* 0678 */ 0x00000000L, /* state: defw 0 */ +/* 067c */ 0x00000000L, /* dmaaddr: defw 0 */ +/* 0680 */ 0x00000000L, /* targ: defw 0 */ +/* 0684 */ 0x00000000L, /* lun: defw 0 */ +/* 0688 */ 0x00000000L, /* sync: defw 0 */ +/* 068c */ 0x00000000L, /* next: defw 0 */ + /* dsa_load_len = dsa_load_end - dsa_copy */ + /* dsa_save_len = dsa_save_end - dsa_copy */ +/* 0690 */ 0xc0000004L, /* move memory 4, dsa, load_state_smc0 + 4 */ +/* 0694 */ 0x00000010L, +/* 0698 */ 0x000006a0L, +/* 069c */ 0xc0000018L, /* move memory dsa_load_len, 0, dsa_copy */ +/* 06a0 */ 0x00000000L, +/* 06a4 */ 0x00000678L, +/* 06a8 */ 0x90080000L, /* return */ +/* 06ac */ 0x00000000L, +/* 06b0 */ 0xc0000004L, /* move memory 4, dsa, save_state_smc0 + 8 */ +/* 06b4 */ 0x00000010L, +/* 06b8 */ 0x000006c4L, +/* 06bc */ 0xc0000008L, /* move memory dsa_save_len, dsa_copy, 0 */ +/* 06c0 */ 0x00000678L, +/* 06c4 */ 0x00000000L, +/* 06c8 */ 0x90080000L, /* return */ +/* 06cc */ 0x00000000L, +/* 06d0 */ 0x721a0000L, /* move ctest2 to sfbr */ +/* 06d4 */ 0x00000000L, +/* 06d8 */ 0xc0000004L, /* move memory 4, dsa_head, dsa */ +/* 06dc */ 0x00000008L, +/* 06e0 */ 0x00000010L, +/* 06e4 */ 0x72100000L, /* move dsa0 to sfbr */ +/* 06e8 */ 0x00000000L, +/* 06ec */ 0x80840000L, /* jump issue_check_1, if not 0 */ +/* 06f0 */ 0x00000030L, +/* 06f4 */ 0x72110000L, /* move dsa1 to sfbr */ +/* 06f8 */ 0x00000000L, +/* 06fc */ 0x80840000L, /* jump issue_check_1, if not 0 */ +/* 0700 */ 0x00000020L, +/* 0704 */ 0x72120000L, /* move dsa2 to sfbr */ +/* 0708 */ 0x00000000L, +/* 070c */ 0x80840000L, /* jump issue_check_1, if not 0 */ +/* 0710 */ 0x00000010L, +/* 0714 */ 0x72130000L, /* move dsa3 to sfbr */ +/* 0718 */ 0x00000000L, +/* 071c */ 0x808c0000L, /* jump wait_for_reselection, if 0 */ +/* 0720 */ 0x00fffdf8L, +/* 0724 */ 0x88880000L, /* call load_state */ +/* 0728 */ 0x00ffff64L, +/* 072c */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 0730 */ 0x00000678L, +/* 0734 */ 0x00000034L, +/* 0738 */ 0x72340000L, /* move scratcha0 to sfbr */ +/* 073c */ 0x00000000L, +/* 0740 */ 0x808c0002L, /* jump start, if STATE_ISSUE */ +/* 0744 */ 0x00fff8c0L, +/* 0748 */ 0xc0000004L, /* move memory 4, next, dsa */ +/* 074c */ 0x0000068cL, +/* 0750 */ 0x00000010L, +/* 0754 */ 0x80880000L, /* jump issue_check_loop */ +/* 0758 */ 0x00ffff88L, +/* 075c */ 0xc0000004L, /* move memory 4, sync, scratcha */ +/* 0760 */ 0x00000688L, +/* 0764 */ 0x00000034L, +/* 0768 */ 0x72340000L, /* move scratcha0 to sfbr */ +/* 076c */ 0x00000000L, +/* 0770 */ 0x6a030000L, /* move sfbr to scntl3 */ +/* 0774 */ 0x00000000L, +/* 0778 */ 0x72350000L, /* move scratcha1 to sfbr */ +/* 077c */ 0x00000000L, +/* 0780 */ 0x6a050000L, /* move sfbr to sxfer */ +/* 0784 */ 0x00000000L, +/* 0788 */ 0x90080000L, /* return */ +/* 078c */ 0x00000000L, +}; + +#define NA_SCRIPT_SIZE 484 + +struct na_patch na_patches[] = { + { 0x0006, 5 }, /* 00000018 */ + { 0x000b, 4 }, /* 0000002c */ + { 0x0013, 4 }, /* 0000004c */ + { 0x0017, 1 }, /* 0000005c */ + { 0x0018, 2 }, /* 00000060 */ + { 0x001a, 1 }, /* 00000068 */ + { 0x001b, 2 }, /* 0000006c */ + { 0x0021, 1 }, /* 00000084 */ + { 0x002b, 2 }, /* 000000ac */ + { 0x002c, 1 }, /* 000000b0 */ + { 0x0030, 2 }, /* 000000c0 */ + { 0x0031, 1 }, /* 000000c4 */ + { 0x0037, 2 }, /* 000000dc */ + { 0x0038, 1 }, /* 000000e0 */ + { 0x003a, 2 }, /* 000000e8 */ + { 0x003b, 1 }, /* 000000ec */ + { 0x0043, 4 }, /* 0000010c */ + { 0x0047, 2 }, /* 0000011c */ + { 0x0048, 1 }, /* 00000120 */ + { 0x004e, 1 }, /* 00000138 */ + { 0x004f, 2 }, /* 0000013c */ + { 0x0051, 1 }, /* 00000144 */ + { 0x0052, 2 }, /* 00000148 */ + { 0x0058, 1 }, /* 00000160 */ + { 0x0059, 2 }, /* 00000164 */ + { 0x005b, 1 }, /* 0000016c */ + { 0x0065, 2 }, /* 00000194 */ + { 0x0066, 1 }, /* 00000198 */ + { 0x006a, 2 }, /* 000001a8 */ + { 0x006b, 1 }, /* 000001ac */ + { 0x0073, 4 }, /* 000001cc */ + { 0x0077, 2 }, /* 000001dc */ + { 0x0078, 1 }, /* 000001e0 */ + { 0x007e, 4 }, /* 000001f8 */ + { 0x0082, 2 }, /* 00000208 */ + { 0x0098, 1 }, /* 00000260 */ + { 0x0099, 2 }, /* 00000264 */ + { 0x009f, 2 }, /* 0000027c */ + { 0x00a0, 1 }, /* 00000280 */ + { 0x00b6, 2 }, /* 000002d8 */ + { 0x00c2, 2 }, /* 00000308 */ + { 0x00ca, 2 }, /* 00000328 */ + { 0x00d0, 2 }, /* 00000340 */ + { 0x00d6, 2 }, /* 00000358 */ + { 0x00e6, 2 }, /* 00000398 */ + { 0x00ec, 2 }, /* 000003b0 */ + { 0x0102, 2 }, /* 00000408 */ + { 0x010e, 2 }, /* 00000438 */ + { 0x011c, 4 }, /* 00000470 */ + { 0x0126, 2 }, /* 00000498 */ + { 0x0134, 1 }, /* 000004d0 */ + { 0x0135, 2 }, /* 000004d4 */ + { 0x0139, 2 }, /* 000004e4 */ + { 0x013a, 1 }, /* 000004e8 */ + { 0x014e, 2 }, /* 00000538 */ + { 0x0152, 4 }, /* 00000548 */ + { 0x0153, 2 }, /* 0000054c */ + { 0x0167, 1 }, /* 0000059c */ + { 0x0168, 2 }, /* 000005a0 */ + { 0x0170, 1 }, /* 000005c0 */ + { 0x0171, 1 }, /* 000005c4 */ + { 0x0175, 1 }, /* 000005d4 */ + { 0x0176, 1 }, /* 000005d8 */ + { 0x017c, 1 }, /* 000005f0 */ + { 0x017d, 2 }, /* 000005f4 */ + { 0x01a5, 2 }, /* 00000694 */ + { 0x01a6, 1 }, /* 00000698 */ + { 0x01a9, 1 }, /* 000006a4 */ + { 0x01ad, 2 }, /* 000006b4 */ + { 0x01ae, 1 }, /* 000006b8 */ + { 0x01b0, 1 }, /* 000006c0 */ + { 0x01b7, 4 }, /* 000006dc */ + { 0x01b8, 2 }, /* 000006e0 */ + { 0x01cc, 1 }, /* 00000730 */ + { 0x01cd, 2 }, /* 00000734 */ + { 0x01d3, 1 }, /* 0000074c */ + { 0x01d4, 2 }, /* 00000750 */ + { 0x01d8, 1 }, /* 00000760 */ + { 0x01d9, 2 }, /* 00000764 */ +}; +#define NA_PATCHES 79 + +enum na_external { + X_scsi_id_buf, + X_msg_out_buf, + X_cmd_buf, + X_data_buf, + X_status_buf, + X_msgin_buf, + X_dsa_0, + X_dsa_1, + X_dsa_head, +}; + +enum { + E_issue_check_next = 1864, + E_issue_check_1 = 1828, + E_issue_check_loop = 1764, + E_save_state_smc0 = 1724, + E_load_state_smc0 = 1692, + E_dsa_load_end = 1680, + E_sync = 1672, + E_dsa_save_end = 1664, + E_dsa_copy = 1656, + E_id_out_mismatch_recover = 1536, + E_next = 1676, + E_reload_sync = 1568, + E_find_dsa_smc2 = 1508, + E_lun = 1668, + E_find_dsa_smc1 = 1480, + E_targ = 1664, + E_find_dsa_next = 1516, + E_load_state = 1680, + E_find_dsa_1 = 1424, + E_find_dsa_loop = 1360, + E_find_dsa = 1348, + E_sigp_set = 1744, + E_reselected = 1316, + E_wsr_check = 1268, + E_response_msg_in = 1172, + E_response_repeat = 1132, + E_response = 1108, + E_reject = 988, + E_wdtr = 964, + E_sdtr = 876, + E_ext_done = 988, + E_ext_1 = 756, + E_ext_2 = 900, + E_ext_3 = 788, + E_issue_check = 1752, + E_extended = 708, + E_ignore_wide = 1060, + E_msg_in_skip = 692, + E_disconnected = 1204, + E_msg_in_not_reject = 532, + E_rejected = 668, + E_msg_in_phase = 516, + E_status_phase = 500, + E_data_out_mismatch = 464, + E_data_out_block_mismatch = 368, + E_data_out_normal = 440, + E_data_out_block_loop = 332, + E_data_out_phase = 308, + E_post_data_to_decisions = 1632, + E_data_in_mismatch = 272, + E_data_block_mismatch_recover = 216, + E_save_state = 1712, + E_data_in_block_mismatch = 136, + E_data_in_normal = 248, + E_data_in_block_loop = 112, + E_dmaaddr = 1660, + E_state = 1656, + E_data_in_phase = 88, + E_cmd_out_mismatch = 80, + E_cmd_phase = 64, + E_to_decisions = 1584, + E_id_out_mismatch = 48, + E_start1 = 40, + E_reselected_on_select = 1292, + E_load_sync = 1884, + E_start = 8, + E_wait_for_reselection = 1308, + E_idle = 0, +}; +#define A_dsa_save_len 8 +#define A_dsa_load_len 24 +#define A_BSIZE 512 +#define A_MSG_REJECT 7 +#define A_X_MSG_WDTR 3 +#define A_X_MSG_SDTR 1 +#define A_X_MSG 1 +#define A_MSG_IGNORE_WIDE_RESIDUE 35 +#define A_MSG_RESTORE_POINTERS 3 +#define A_MSG_SAVE_DATA_POINTER 2 +#define A_MSG_DISCONNECT 4 +#define A_MSG_IDENTIFY 128 +#define A_RESULT_OK 0 +#define A_STATE_DONE 4 +#define A_STATE_DISCONNECTED 3 +#define A_STATE_ISSUE 2 +#define A_STATE_ALLOCATED 1 +#define A_STATE_FREE 0 +#define A_SIR_NOTIFY_RESELECTED_ON_SELECT 117 +#define A_SIR_NOTIFY_LOAD_SYNC 116 +#define A_SIR_NOTIFY_WSR 115 +#define A_SIR_NOTIFY_BLOCK_DATA_IN 114 +#define A_SIR_NOTIFY_DATA_OUT 113 +#define A_SIR_NOTIFY_DATA_IN 112 +#define A_SIR_NOTIFY_COMMAND 111 +#define A_SIR_NOTIFY_DUMP_NEXT_CODE 110 +#define A_SIR_NOTIFY_ISSUE_CHECK 109 +#define A_SIR_NOTIFY_WAIT_RESELECT 108 +#define A_SIR_NOTIFY_ISSUE 107 +#define A_SIR_NOTIFY_SIGP 106 +#define A_SIR_NOTIFY_DUMP2 105 +#define A_SIR_NOTIFY_DUMP 104 +#define A_SIR_NOTIFY_STATUS 103 +#define A_SIR_NOTIFY_MSG_IN 102 +#define A_SIR_NOTIFY_RESELECT 101 +#define A_SIR_NOTIFY_DISC 100 +#define A_SIR_MSG_IGNORE_WIDE_RESIDUE 16 +#define A_SIR_MSG_WDTR 15 +#define A_SIR_EV_PHASE_SWITCH_AFTER_ID 14 +#define A_error_sigp_set 13 +#define A_SIR_EV_RESPONSE_OK 12 +#define A_SIR_MSG_SDTR 11 +#define A_SIR_MSG_REJECT 10 +#define A_error_too_little_data 9 +#define A_error_too_much_data 8 +#define A_error_not_identify_after_reselect 7 +#define A_SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT 6 +#define A_error_weird_message 5 +#define A_error_unexpected_phase 4 +#define A_error_reselected 3 +#define A_error_disconnected 2 +#define A_error_not_cmd_complete 1 +#define A_SIR_MSG_IO_COMPLETE 0 diff --git a/os/boot/pc/sdata.c b/os/boot/pc/sdata.c new file mode 100644 index 00000000..d035dc9c --- /dev/null +++ b/os/boot/pc/sdata.c @@ -0,0 +1,1639 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "error.h" + +#include "sd.h" + +extern SDifc sdataifc; + +enum { + DbgCONFIG = 0x0001, /* detected drive config info */ + DbgIDENTIFY = 0x0002, /* detected drive identify info */ + DbgSTATE = 0x0004, /* dump state on panic */ + DbgPROBE = 0x0008, /* trace device probing */ + DbgDEBUG = 0x0080, /* the current problem... */ + DbgINL = 0x0100, /* That Inil20+ message we hate */ + Dbg48BIT = 0x0200, /* 48-bit LBA */ + DbgBsy = 0x0400, /* interrupt but Bsy (shared IRQ) */ +}; +#define DEBUG (DbgDEBUG|DbgCONFIG) + +enum { /* I/O ports */ + Data = 0, + Error = 1, /* (read) */ + Features = 1, /* (write) */ + Count = 2, /* sector count<7-0>, sector count<15-8> */ + Ir = 2, /* interrupt reason (PACKET) */ + Sector = 3, /* sector number */ + Lbalo = 3, /* LBA<7-0>, LBA<31-24> */ + Cyllo = 4, /* cylinder low */ + Bytelo = 4, /* byte count low (PACKET) */ + Lbamid = 4, /* LBA<15-8>, LBA<39-32> */ + Cylhi = 5, /* cylinder high */ + Bytehi = 5, /* byte count hi (PACKET) */ + Lbahi = 5, /* LBA<23-16>, LBA<47-40> */ + Dh = 6, /* Device/Head, LBA<32-14> */ + Status = 7, /* (read) */ + Command = 7, /* (write) */ + + As = 2, /* Alternate Status (read) */ + Dc = 2, /* Device Control (write) */ +}; + +enum { /* Error */ + Med = 0x01, /* Media error */ + Ili = 0x01, /* command set specific (PACKET) */ + Nm = 0x02, /* No Media */ + Eom = 0x02, /* command set specific (PACKET) */ + Abrt = 0x04, /* Aborted command */ + Mcr = 0x08, /* Media Change Request */ + Idnf = 0x10, /* no user-accessible address */ + Mc = 0x20, /* Media Change */ + Unc = 0x40, /* Uncorrectable data error */ + Wp = 0x40, /* Write Protect */ + Icrc = 0x80, /* Interface CRC error */ +}; + +enum { /* Features */ + Dma = 0x01, /* data transfer via DMA (PACKET) */ + Ovl = 0x02, /* command overlapped (PACKET) */ +}; + +enum { /* Interrupt Reason */ + Cd = 0x01, /* Command/Data */ + Io = 0x02, /* I/O direction */ + Rel = 0x04, /* Bus Release */ +}; + +enum { /* Device/Head */ + Dev0 = 0xA0, /* Master */ + Dev1 = 0xB0, /* Slave */ + Lba = 0x40, /* LBA mode */ +}; + +enum { /* Status, Alternate Status */ + Err = 0x01, /* Error */ + Chk = 0x01, /* Check error (PACKET) */ + Drq = 0x08, /* Data Request */ + Dsc = 0x10, /* Device Seek Complete */ + Serv = 0x10, /* Service */ + Df = 0x20, /* Device Fault */ + Dmrd = 0x20, /* DMA ready (PACKET) */ + Drdy = 0x40, /* Device Ready */ + Bsy = 0x80, /* Busy */ +}; + +enum { /* Command */ + Cnop = 0x00, /* NOP */ + Cdr = 0x08, /* Device Reset */ + Crs = 0x20, /* Read Sectors */ + Crs48 = 0x24, /* Read Sectors Ext */ + Crd48 = 0x25, /* Read w/ DMA Ext */ + Crdq48 = 0x26, /* Read w/ DMA Queued Ext */ + Crsm48 = 0x29, /* Read Multiple Ext */ + Cws = 0x30, /* Write Sectors */ + Cws48 = 0x34, /* Write Sectors Ext */ + Cwd48 = 0x35, /* Write w/ DMA Ext */ + Cwdq48 = 0x36, /* Write w/ DMA Queued Ext */ + Cwsm48 = 0x39, /* Write Multiple Ext */ + Cedd = 0x90, /* Execute Device Diagnostics */ + Cpkt = 0xA0, /* Packet */ + Cidpkt = 0xA1, /* Identify Packet Device */ + Crsm = 0xC4, /* Read Multiple */ + Cwsm = 0xC5, /* Write Multiple */ + Csm = 0xC6, /* Set Multiple */ + Crdq = 0xC7, /* Read DMA queued */ + Crd = 0xC8, /* Read DMA */ + Cwd = 0xCA, /* Write DMA */ + Cwdq = 0xCC, /* Write DMA queued */ + Cstandby = 0xE2, /* Standby */ + Cid = 0xEC, /* Identify Device */ + Csf = 0xEF, /* Set Features */ +}; + +enum { /* Device Control */ + Nien = 0x02, /* (not) Interrupt Enable */ + Srst = 0x04, /* Software Reset */ +}; + +enum { /* PCI Configuration Registers */ + Bmiba = 0x20, /* Bus Master Interface Base Address */ + Idetim = 0x40, /* IE Timing */ + Sidetim = 0x44, /* Slave IE Timing */ + Udmactl = 0x48, /* Ultra DMA/33 Control */ + Udmatim = 0x4A, /* Ultra DMA/33 Timing */ +}; + +enum { /* Bus Master IDE I/O Ports */ + Bmicx = 0, /* Command */ + Bmisx = 2, /* Status */ + Bmidtpx = 4, /* Descriptor Table Pointer */ +}; + +enum { /* Bmicx */ + Ssbm = 0x01, /* Start/Stop Bus Master */ + Rwcon = 0x08, /* Read/Write Control */ +}; + +enum { /* Bmisx */ + Bmidea = 0x01, /* Bus Master IDE Active */ + Idedmae = 0x02, /* IDE DMA Error (R/WC) */ + Ideints = 0x04, /* IDE Interrupt Status (R/WC) */ + Dma0cap = 0x20, /* Drive 0 DMA Capable */ + Dma1cap = 0x40, /* Drive 0 DMA Capable */ +}; +enum { /* Physical Region Descriptor */ + PrdEOT = 0x80000000, /* Bus Master IDE Active */ +}; + +enum { /* offsets into the identify info. */ + Iconfig = 0, /* general configuration */ + Ilcyl = 1, /* logical cylinders */ + Ilhead = 3, /* logical heads */ + Ilsec = 6, /* logical sectors per logical track */ + Iserial = 10, /* serial number */ + Ifirmware = 23, /* firmware revision */ + Imodel = 27, /* model number */ + Imaxrwm = 47, /* max. read/write multiple sectors */ + Icapabilities = 49, /* capabilities */ + Istandby = 50, /* device specific standby timer */ + Ipiomode = 51, /* PIO data transfer mode number */ + Ivalid = 53, + Iccyl = 54, /* cylinders if (valid&0x01) */ + Ichead = 55, /* heads if (valid&0x01) */ + Icsec = 56, /* sectors if (valid&0x01) */ + Iccap = 57, /* capacity if (valid&0x01) */ + Irwm = 59, /* read/write multiple */ + Ilba = 60, /* LBA size */ + Imwdma = 63, /* multiword DMA mode */ + Iapiomode = 64, /* advanced PIO modes supported */ + Iminmwdma = 65, /* min. multiword DMA cycle time */ + Irecmwdma = 66, /* rec. multiword DMA cycle time */ + Iminpio = 67, /* min. PIO cycle w/o flow control */ + Iminiordy = 68, /* min. PIO cycle with IORDY */ + Ipcktbr = 71, /* time from PACKET to bus release */ + Iserbsy = 72, /* time from SERVICE to !Bsy */ + Iqdepth = 75, /* max. queue depth */ + Imajor = 80, /* major version number */ + Iminor = 81, /* minor version number */ + Icsfs = 82, /* command set/feature supported */ + Icsfe = 85, /* command set/feature enabled */ + Iudma = 88, /* ultra DMA mode */ + Ierase = 89, /* time for security erase */ + Ieerase = 90, /* time for enhanced security erase */ + Ipower = 91, /* current advanced power management */ + Ilba48 = 100, /* 48-bit LBA size (64 bits in 100-103) */ + Irmsn = 127, /* removable status notification */ + Isecstat = 128, /* security status */ +}; + +typedef struct Ctlr Ctlr; +typedef struct Drive Drive; + +typedef struct Prd { + ulong pa; /* Physical Base Address */ + int count; +} Prd; + +enum { + Nprd = SDmaxio/(64*1024)+2, +}; + +typedef struct Ctlr { + int cmdport; + int ctlport; + int irq; + int tbdf; + + Pcidev* pcidev; + void (*ienable)(Ctlr*); + SDev* sdev; + + Drive* drive[2]; + + Prd* prdt; /* physical region descriptor table */ + +// QLock; /* current command */ + Drive* curdrive; + int command; /* last command issued (debugging) */ +// Rendez; + int done; + + Lock; /* register access */ +} Ctlr; + +typedef struct Drive { + Ctlr* ctlr; + + int dev; + ushort info[256]; + int c; /* cylinder */ + int h; /* head */ + int s; /* sector */ + vlong sectors; /* total */ + int secsize; /* sector size */ + +// int dma; /* DMA R/W possible */ +// int dmactl; +// int rwm; /* read/write multiple possible */ +// int rwmctl; + + int pkt; /* PACKET device, length of pktcmd */ + uchar pktcmd[16]; +// int pktdma; /* this PACKET command using dma */ + + uchar sense[18]; + uchar inquiry[48]; + +// QLock; /* drive access */ + int command; /* current command */ + int write; + uchar* data; + int dlen; + uchar* limit; + int count; /* sectors */ + int block; /* R/W bytes per block */ + int status; + int error; + int flags; /* internal flags */ +} Drive; + +enum { /* internal flags */ + Lba48 = 0x1, /* LBA48 mode */ + Lba48always = 0x2, /* ... */ +}; + +static void +pc87415ienable(Ctlr* ctlr) +{ + Pcidev *p; + int x; + + p = ctlr->pcidev; + if(p == nil) + return; + + x = pcicfgr32(p, 0x40); + if(ctlr->cmdport == p->mem[0].bar) + x &= ~0x00000100; + else + x &= ~0x00000200; + pcicfgw32(p, 0x40, x); +} + +static int +atadebug(int cmdport, int ctlport, char* fmt, ...) +{ + int i, n; + va_list arg; + char buf[PRINTSIZE]; + + if(!(DEBUG & DbgPROBE)){ + USED(cmdport, ctlport, fmt); + return 0; + } + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + + if(cmdport){ + if(buf[n-1] == '\n') + n--; + n += snprint(buf+n, PRINTSIZE-n, " ataregs 0x%uX:", + cmdport); + for(i = Features; i < Command; i++) + n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX", + inb(cmdport+i)); + if(ctlport) + n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX", + inb(ctlport+As)); + n += snprint(buf+n, PRINTSIZE-n, "\n"); + } + putstrn(buf, n); + + return n; +} + +static int +ataready(int cmdport, int ctlport, int dev, int reset, int ready, int micro) +{ + int as; + + atadebug(cmdport, ctlport, "ataready: dev %uX reset %uX ready %uX", + dev, reset, ready); + + for(;;){ + /* + * Wait for the controller to become not busy and + * possibly for a status bit to become true (usually + * Drdy). Must change to the appropriate device + * register set if necessary before testing for ready. + * Always run through the loop at least once so it + * can be used as a test for !Bsy. + */ + as = inb(ctlport+As); + if(as & reset){ + /* nothing to do */; + } + else if(dev){ + outb(cmdport+Dh, dev); + dev = 0; + } + else if(ready == 0 || (as & ready)){ + atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as); + return as; + } + + if(micro-- <= 0){ + atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as); + break; + } + microdelay(1); + } + atadebug(cmdport, ctlport, "ataready: timeout"); + + return -1; +} + +static int +atacsfenabled(Drive* drive, vlong csf) +{ + int cmdset, i, x; + + for(i = 0; i < 3; i++){ + x = (csf>>(16*i)) & 0xFFFF; + if(x == 0) + continue; + cmdset = drive->info[Icsfe+i]; + if(cmdset == 0 || cmdset == 0xFFFF) + return 0; + return cmdset & x; + } + + return 0; +} + +/* +static int +atasf(int cmdport, int ctlport, int dev, uchar* command) +{ + int as, i; + + if(ataready(cmdport, ctlport, dev, Bsy|Drq, Drdy, 108*1000) < 0) + return -1; + + for(i = Features; i < Dh; i++) + outb(cmdport+i, command[i]); + outb(cmdport+Command, Csf); + microdelay(100); + as = ataready(cmdport, ctlport, 0, Bsy, Drdy|Df|Err, 109*1000); + if(as < 0 || (as & (Df|Err))) + return -1; + return 0; +} + */ + +static int +ataidentify(int cmdport, int ctlport, int dev, int pkt, void* info) +{ + int as, command, drdy; + + if(pkt){ + command = Cidpkt; + drdy = 0; + } + else{ + command = Cid; + drdy = Drdy; + } + as = ataready(cmdport, ctlport, dev, Bsy|Drq, drdy, 103*1000); + if(as < 0) + return as; + outb(cmdport+Command, command); + microdelay(1); + + as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 400*1000); + if(as < 0) + return -1; + if(as & Err) + return as; + + memset(info, 0, 512); + inss(cmdport+Data, info, 256); + inb(cmdport+Status); + + if(DEBUG & DbgIDENTIFY){ + int i; + ushort *sp; + + sp = (ushort*)info; + for(i = 0; i < 256; i++){ + if(i && (i%16) == 0) + print("\n"); + print(" %4.4uX ", *sp); + sp++; + } + print("\n"); + } + + return 0; +} + +static Drive* +atadrive(int cmdport, int ctlport, int dev) +{ + Drive *drive; + int as, i, pkt; + uchar buf[512], *p; + ushort iconfig, *sp; + + atadebug(0, 0, "identify: port 0x%uX dev 0x%2.2uX\n", cmdport, dev); + pkt = 1; +retry: + as = ataidentify(cmdport, ctlport, dev, pkt, buf); + if(as < 0) + return nil; + if(as & Err){ + if(pkt == 0) + return nil; + pkt = 0; + goto retry; + } + + if((drive = malloc(sizeof(Drive))) == nil) + return nil; + drive->dev = dev; + memmove(drive->info, buf, sizeof(drive->info)); + drive->sense[0] = 0x70; + drive->sense[7] = sizeof(drive->sense)-7; + + drive->inquiry[2] = 2; + drive->inquiry[3] = 2; + drive->inquiry[4] = sizeof(drive->inquiry)-4; + p = &drive->inquiry[8]; + sp = &drive->info[Imodel]; + for(i = 0; i < 20; i++){ + *p++ = *sp>>8; + *p++ = *sp++; + } + + drive->secsize = 512; + + /* + * Beware the CompactFlash Association feature set. + * Now, why this value in Iconfig just walks all over the bit + * definitions used in the other parts of the ATA/ATAPI standards + * is a mystery and a sign of true stupidity on someone's part. + * Anyway, the standard says if this value is 0x848A then it's + * CompactFlash and it's NOT a packet device. + */ + iconfig = drive->info[Iconfig]; + if(iconfig != 0x848A && (iconfig & 0xC000) == 0x8000){ + if(iconfig & 0x01) + drive->pkt = 16; + else + drive->pkt = 12; + } + else{ + if(drive->info[Ivalid] & 0x0001){ + drive->c = drive->info[Iccyl]; + drive->h = drive->info[Ichead]; + drive->s = drive->info[Icsec]; + } + else{ + drive->c = drive->info[Ilcyl]; + drive->h = drive->info[Ilhead]; + drive->s = drive->info[Ilsec]; + } + if(drive->info[Icapabilities] & 0x0200){ + if(drive->info[Icsfs+1] & 0x0400){ + drive->sectors = drive->info[Ilba48] + |(drive->info[Ilba48+1]<<16) + |((vlong)drive->info[Ilba48+2]<<32); + drive->flags |= Lba48; + } + else{ + drive->sectors = (drive->info[Ilba+1]<<16) + |drive->info[Ilba]; + } + drive->dev |= Lba; + } + else + drive->sectors = drive->c*drive->h*drive->s; + // atarwmmode(drive, cmdport, ctlport, dev); + } +// atadmamode(drive); + + if(DEBUG & DbgCONFIG){ + print("dev %2.2uX port %uX config %4.4uX capabilities %4.4uX", + dev, cmdport, iconfig, drive->info[Icapabilities]); + print(" mwdma %4.4uX", drive->info[Imwdma]); + if(drive->info[Ivalid] & 0x04) + print(" udma %4.4uX", drive->info[Iudma]); +// print(" dma %8.8uX rwm %ud", drive->dma, drive->rwm); + if(drive->flags&Lba48) + print("\tLLBA sectors %lld", drive->sectors); + print("\n"); + } + + return drive; +} + +static void +atasrst(int ctlport) +{ + /* + * Srst is a big stick and may cause problems if further + * commands are tried before the drives become ready again. + * Also, there will be problems here if overlapped commands + * are ever supported. + */ + microdelay(5); + outb(ctlport+Dc, Srst); + microdelay(5); + outb(ctlport+Dc, 0); + microdelay(2*1000); +} + +static SDev* +ataprobe(int cmdport, int ctlport, int irq) +{ + Ctlr* ctlr; + SDev *sdev; + Drive *drive; + int dev, error, rhi, rlo; + +// if(ioalloc(cmdport, 8, 0, "atacmd") < 0) +// return nil; +// if(ioalloc(ctlport+As, 1, 0, "atactl") < 0){ +// iofree(cmdport); +// return nil; +// } + + /* + * Try to detect a floating bus. + * Bsy should be cleared. If not, see if the cylinder registers + * are read/write capable. + * If the master fails, try the slave to catch slave-only + * configurations. + * There's no need to restore the tested registers as they will + * be reset on any detected drives by the Cedd command. + * All this indicates is that there is at least one drive on the + * controller; when the non-existent drive is selected in a + * single-drive configuration the registers of the existing drive + * are often seen, only command execution fails. + */ + dev = Dev0; + if(inb(ctlport+As) & Bsy){ + outb(cmdport+Dh, dev); + microdelay(1); +trydev1: + atadebug(cmdport, ctlport, "ataprobe bsy"); + outb(cmdport+Cyllo, 0xAA); + outb(cmdport+Cylhi, 0x55); + outb(cmdport+Sector, 0xFF); + rlo = inb(cmdport+Cyllo); + rhi = inb(cmdport+Cylhi); + if(rlo != 0xAA && (rlo == 0xFF || rhi != 0x55)){ + if(dev == Dev1){ +release: + // iofree(cmdport); + // iofree(ctlport+As); + return nil; + } + dev = Dev1; + if(ataready(cmdport, ctlport, dev, Bsy, 0, 20*1000) < 0) + goto trydev1; + } + } + + /* + * Disable interrupts on any detected controllers. + */ + outb(ctlport+Dc, Nien); +tryedd1: + if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 105*1000) < 0){ + /* + * There's something there, but it didn't come up clean, + * so try hitting it with a big stick. The timing here is + * wrong but this is a last-ditch effort and it sometimes + * gets some marginal hardware back online. + */ + atasrst(ctlport); + if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 106*1000) < 0) + goto release; + } + + /* + * Can only get here if controller is not busy. + * If there are drives Bsy will be set within 400nS, + * must wait 2mS before testing Status. + * Wait for the command to complete (6 seconds max). + */ + outb(cmdport+Command, Cedd); + delay(2); + if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 6*1000*1000) < 0) + goto release; + + /* + * If bit 0 of the error register is set then the selected drive + * exists. This is enough to detect single-drive configurations. + * However, if the master exists there is no way short of executing + * a command to determine if a slave is present. + * It appears possible to get here testing Dev0 although it doesn't + * exist and the EDD won't take, so try again with Dev1. + */ + error = inb(cmdport+Error); + atadebug(cmdport, ctlport, "ataprobe: dev %uX", dev); + if((error & ~0x80) != 0x01){ + if(dev == Dev1) + goto release; + dev = Dev1; + goto tryedd1; + } + + /* + * At least one drive is known to exist, try to + * identify it. If that fails, don't bother checking + * any further. + * If the one drive found is Dev0 and the EDD command + * didn't indicate Dev1 doesn't exist, check for it. + */ + if((drive = atadrive(cmdport, ctlport, dev)) == nil) + goto release; + if((ctlr = malloc(sizeof(Ctlr))) == nil){ + free(drive); + goto release; + } + if((sdev = malloc(sizeof(SDev))) == nil){ + free(ctlr); + free(drive); + goto release; + } + drive->ctlr = ctlr; + if(dev == Dev0){ + ctlr->drive[0] = drive; + if(!(error & 0x80)){ + /* + * Always leave Dh pointing to a valid drive, + * otherwise a subsequent call to ataready on + * this controller may try to test a bogus Status. + * Ataprobe is the only place possibly invalid + * drives should be selected. + */ + drive = atadrive(cmdport, ctlport, Dev1); + if(drive != nil){ + drive->ctlr = ctlr; + ctlr->drive[1] = drive; + } + else{ + outb(cmdport+Dh, Dev0); + microdelay(1); + } + } + } + else + ctlr->drive[1] = drive; + + ctlr->cmdport = cmdport; + ctlr->ctlport = ctlport; + ctlr->irq = irq; + ctlr->tbdf = BUSUNKNOWN; + ctlr->command = Cedd; /* debugging */ + + sdev->ifc = &sdataifc; + sdev->ctlr = ctlr; + sdev->nunit = 2; + ctlr->sdev = sdev; + + return sdev; +} + +static int +atasetsense(Drive* drive, int status, int key, int asc, int ascq) +{ + drive->sense[2] = key; + drive->sense[12] = asc; + drive->sense[13] = ascq; + + return status; +} + +static int +atamodesense(Drive* drive, uchar* cmd) +{ + int len; + + /* + * Fake a vendor-specific request with page code 0, + * return the drive info. + */ + if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F) + return atasetsense(drive, SDcheck, 0x05, 0x24, 0); + len = (cmd[7]<<8)|cmd[8]; + if(len == 0) + return SDok; + if(len < 8+sizeof(drive->info)) + return atasetsense(drive, SDcheck, 0x05, 0x1A, 0); + if(drive->data == nil || drive->dlen < len) + return atasetsense(drive, SDcheck, 0x05, 0x20, 1); + memset(drive->data, 0, 8); + drive->data[0] = sizeof(drive->info)>>8; + drive->data[1] = sizeof(drive->info); + memmove(drive->data+8, drive->info, sizeof(drive->info)); + drive->data += 8+sizeof(drive->info); + + return SDok; +} + +static void +atanop(Drive* drive, int subcommand) +{ + Ctlr* ctlr; + int as, cmdport, ctlport, timeo; + + /* + * Attempt to abort a command by using NOP. + * In response, the drive is supposed to set Abrt + * in the Error register, set (Drdy|Err) in Status + * and clear Bsy when done. However, some drives + * (e.g. ATAPI Zip) just go Bsy then clear Status + * when done, hence the timeout loop only on Bsy + * and the forced setting of drive->error. + */ + ctlr = drive->ctlr; + cmdport = ctlr->cmdport; + outb(cmdport+Features, subcommand); + outb(cmdport+Dh, drive->dev); + ctlr->command = Cnop; /* debugging */ + outb(cmdport+Command, Cnop); + + microdelay(1); + ctlport = ctlr->ctlport; + for(timeo = 0; timeo < 1000; timeo++){ + as = inb(ctlport+As); + if(!(as & Bsy)) + break; + microdelay(1); + } + drive->error |= Abrt; +} + +static void +ataabort(Drive* drive, int dolock) +{ + /* + * If NOP is available (packet commands) use it otherwise + * must try a software reset. + */ + if(dolock) + ilock(drive->ctlr); + if(atacsfenabled(drive, 0x0000000000004000LL)) + atanop(drive, 0); + else{ + atasrst(drive->ctlr->ctlport); + drive->error |= Abrt; + } + if(dolock) + iunlock(drive->ctlr); +} + +static int +atapktiodone(void* arg) +{ + return ((Ctlr*)arg)->done; +} + +static void +atapktinterrupt(Drive* drive) +{ + Ctlr* ctlr; + int cmdport, len; + + ctlr = drive->ctlr; + cmdport = ctlr->cmdport; + switch(inb(cmdport+Ir) & (/*Rel|*/Io|Cd)){ + case Cd: + outss(cmdport+Data, drive->pktcmd, drive->pkt/2); + break; + + case 0: + len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo); + if(drive->data+len > drive->limit){ + atanop(drive, 0); + break; + } + outss(cmdport+Data, drive->data, len/2); + drive->data += len; + break; + + case Io: + len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo); + if(drive->data+len > drive->limit){ + atanop(drive, 0); + break; + } + inss(cmdport+Data, drive->data, len/2); + drive->data += len; + break; + + case Io|Cd: + // if(drive->pktdma) + // atadmainterrupt(drive, drive->dlen); + // else + ctlr->done = 1; + break; + } +} + +static int +atapktio(Drive* drive, uchar* cmd, int clen) +{ + Ctlr *ctlr; + int as, cmdport, ctlport, len, r; + + if(cmd[0] == 0x5A && (cmd[2] & 0x3F) == 0) + return atamodesense(drive, cmd); + + r = SDok; + + drive->command = Cpkt; + memmove(drive->pktcmd, cmd, clen); + memset(drive->pktcmd+clen, 0, drive->pkt-clen); + drive->limit = drive->data+drive->dlen; + + ctlr = drive->ctlr; + cmdport = ctlr->cmdport; + ctlport = ctlr->ctlport; + + qlock(ctlr); + + as = ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 107*1000); + if(as < 0 || (as&Chk)){ + qunlock(ctlr); + return -1; + } + + ilock(ctlr); +// if(drive->dlen && drive->dmactl && !atadmasetup(drive, drive->dlen)) +// drive->pktdma = Dma; +// else +// drive->pktdma = 0; + + outb(cmdport+Features, 0/*drive->pktdma*/); + outb(cmdport+Count, 0); + outb(cmdport+Sector, 0); + len = 16*drive->secsize; + outb(cmdport+Bytelo, len); + outb(cmdport+Bytehi, len>>8); + outb(cmdport+Dh, drive->dev); + ctlr->done = 0; + ctlr->curdrive = drive; + ctlr->command = Cpkt; /* debugging */ +// if(drive->pktdma) +// atadmastart(ctlr, drive->write); + outb(cmdport+Command, Cpkt); + + if((drive->info[Iconfig] & 0x0060) != 0x0020){ + microdelay(1); + as = ataready(cmdport, ctlport, 0, Bsy, Drq|Chk, 4*1000); + if(as < 0 || (as & (Bsy|Chk))){ + drive->status = as<0 ? 0 : as; + ctlr->curdrive = nil; + ctlr->done = 1; + r = SDtimeout; + }else + atapktinterrupt(drive); + } + iunlock(ctlr); + + sleep(ctlr, atapktiodone, ctlr); + + qunlock(ctlr); + + if(drive->status & Chk) + r = SDcheck; + + return r; +} + +static int +atageniodone(void* arg) +{ + return ((Ctlr*)arg)->done; +} + +static uchar cmd48[256] = { + [Crs] Crs48, + [Crd] Crd48, + [Crdq] Crdq48, + [Crsm] Crsm48, + [Cws] Cws48, + [Cwd] Cwd48, + [Cwdq] Cwdq48, + [Cwsm] Cwsm48, +}; + +static int +atageniostart(Drive* drive, vlong lba) +{ + Ctlr *ctlr; + uchar cmd; + int as, c, cmdport, ctlport, h, len, s, use48; + + use48 = 0; + if((drive->flags&Lba48always) || (lba>>28) || drive->count > 256){ + if(!(drive->flags & Lba48)) + return -1; + use48 = 1; + c = h = s = 0; + }else if(drive->dev & Lba){ + c = (lba>>8) & 0xFFFF; + h = (lba>>24) & 0x0F; + s = lba & 0xFF; + } + else{ + c = lba/(drive->s*drive->h); + h = ((lba/drive->s) % drive->h); + s = (lba % drive->s) + 1; + } + + ctlr = drive->ctlr; + cmdport = ctlr->cmdport; + ctlport = ctlr->ctlport; + if(ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 101*1000) < 0) + return -1; + + ilock(ctlr); + + drive->block = drive->secsize; + if(drive->write) + drive->command = Cws; + else + drive->command = Crs; + + drive->limit = drive->data + drive->count*drive->secsize; + cmd = drive->command; + if(use48){ + outb(cmdport+Count, (drive->count>>8) & 0xFF); + outb(cmdport+Count, drive->count & 0XFF); + outb(cmdport+Lbalo, (lba>>24) & 0xFF); + outb(cmdport+Lbalo, lba & 0xFF); + outb(cmdport+Lbamid, (lba>>32) & 0xFF); + outb(cmdport+Lbamid, (lba>>8) & 0xFF); + outb(cmdport+Lbahi, (lba>>40) & 0xFF); + outb(cmdport+Lbahi, (lba>>16) & 0xFF); + outb(cmdport+Dh, drive->dev|Lba); + cmd = cmd48[cmd]; + + if(DEBUG & Dbg48BIT) + print("using 48-bit commands\n"); + }else{ + outb(cmdport+Count, drive->count); + outb(cmdport+Sector, s); + outb(cmdport+Cyllo, c); + outb(cmdport+Cylhi, c>>8); + outb(cmdport+Dh, drive->dev|h); + } + ctlr->done = 0; + ctlr->curdrive = drive; + ctlr->command = drive->command; /* debugging */ + outb(cmdport+Command, cmd); + + switch(drive->command){ + case Cws: + case Cwsm: + microdelay(1); + as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 1000); + if(as < 0 || (as & Err)){ + iunlock(ctlr); + return -1; + } + len = drive->block; + if(drive->data+len > drive->limit) + len = drive->limit-drive->data; + outss(cmdport+Data, drive->data, len/2); + break; + + case Crd: + case Cwd: + // atadmastart(ctlr, drive->write); + break; + } + iunlock(ctlr); + + return 0; +} + +static int +atagenioretry(Drive* drive) +{ + return atasetsense(drive, SDcheck, 4, 8, drive->error); +} + +static int +atagenio(Drive* drive, uchar* cmd, int) +{ + uchar *p; + Ctlr *ctlr; + int count, max; + vlong lba, len; + + /* + * Map SCSI commands into ATA commands for discs. + * Fail any command with a LUN except INQUIRY which + * will return 'logical unit not supported'. + */ + if((cmd[1]>>5) && cmd[0] != 0x12) + return atasetsense(drive, SDcheck, 0x05, 0x25, 0); + + switch(cmd[0]){ + default: + return atasetsense(drive, SDcheck, 0x05, 0x20, 0); + + case 0x00: /* test unit ready */ + return SDok; + + case 0x03: /* request sense */ + if(cmd[4] < sizeof(drive->sense)) + len = cmd[4]; + else + len = sizeof(drive->sense); + if(drive->data && drive->dlen >= len){ + memmove(drive->data, drive->sense, len); + drive->data += len; + } + return SDok; + + case 0x12: /* inquiry */ + if(cmd[4] < sizeof(drive->inquiry)) + len = cmd[4]; + else + len = sizeof(drive->inquiry); + if(drive->data && drive->dlen >= len){ + memmove(drive->data, drive->inquiry, len); + drive->data += len; + } + return SDok; + + case 0x1B: /* start/stop unit */ + /* + * NOP for now, can use the power management feature + * set later. + */ + return SDok; + + case 0x25: /* read capacity */ + if((cmd[1] & 0x01) || cmd[2] || cmd[3]) + return atasetsense(drive, SDcheck, 0x05, 0x24, 0); + if(drive->data == nil || drive->dlen < 8) + return atasetsense(drive, SDcheck, 0x05, 0x20, 1); + /* + * Read capacity returns the LBA of the last sector. + */ + len = drive->sectors-1; + p = drive->data; + *p++ = len>>24; + *p++ = len>>16; + *p++ = len>>8; + *p++ = len; + len = drive->secsize; + *p++ = len>>24; + *p++ = len>>16; + *p++ = len>>8; + *p = len; + drive->data += 8; + return SDok; + + case 0x9E: /* long read capacity */ + if((cmd[1] & 0x01) || cmd[2] || cmd[3]) + return atasetsense(drive, SDcheck, 0x05, 0x24, 0); + if(drive->data == nil || drive->dlen < 8) + return atasetsense(drive, SDcheck, 0x05, 0x20, 1); + /* + * Read capacity returns the LBA of the last sector. + */ + len = drive->sectors-1; + p = drive->data; + *p++ = len>>56; + *p++ = len>>48; + *p++ = len>>40; + *p++ = len>>32; + *p++ = len>>24; + *p++ = len>>16; + *p++ = len>>8; + *p++ = len; + len = drive->secsize; + *p++ = len>>24; + *p++ = len>>16; + *p++ = len>>8; + *p = len; + drive->data += 8; + return SDok; + + case 0x28: /* read */ + case 0x2A: /* write */ + break; + + case 0x5A: + return atamodesense(drive, cmd); + } + + ctlr = drive->ctlr; + lba = (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5]; + count = (cmd[7]<<8)|cmd[8]; + if(drive->data == nil) + return SDok; + if(drive->dlen < count*drive->secsize) + count = drive->dlen/drive->secsize; + qlock(ctlr); + while(count){ + max = (drive->flags&Lba48) ? 65536 : 256; + if(count > max) + drive->count = max; + else + drive->count = count; + if(atageniostart(drive, lba)){ + ilock(ctlr); + atanop(drive, 0); + iunlock(ctlr); + qunlock(ctlr); + return atagenioretry(drive); + } + + tsleep(ctlr, atageniodone, ctlr, 10*1000); + if(!ctlr->done){ + /* + * What should the above timeout be? In + * standby and sleep modes it could take as + * long as 30 seconds for a drive to respond. + * Very hard to get out of this cleanly. + */ + // atadumpstate(drive, cmd, lba, count); + ataabort(drive, 1); + return atagenioretry(drive); + } + + if(drive->status & Err){ + qunlock(ctlr); + return atasetsense(drive, SDcheck, 4, 8, drive->error); + } + count -= drive->count; + lba += drive->count; + } + qunlock(ctlr); + + return SDok; +} + +static int +atario(SDreq* r) +{ + Ctlr *ctlr; + Drive *drive; + SDunit *unit; + uchar cmd10[10], *cmdp, *p; + int clen, reqstatus, status; + + unit = r->unit; + if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil){ + r->status = SDtimeout; + return SDtimeout; + } + drive = ctlr->drive[unit->subno]; + + /* + * Most SCSI commands can be passed unchanged except for + * the padding on the end. The few which require munging + * are not used internally. Mode select/sense(6) could be + * converted to the 10-byte form but it's not worth the + * effort. Read/write(6) are easy. + */ + switch(r->cmd[0]){ + case 0x08: /* read */ + case 0x0A: /* write */ + cmdp = cmd10; + memset(cmdp, 0, sizeof(cmd10)); + cmdp[0] = r->cmd[0]|0x20; + cmdp[1] = r->cmd[1] & 0xE0; + cmdp[5] = r->cmd[3]; + cmdp[4] = r->cmd[2]; + cmdp[3] = r->cmd[1] & 0x0F; + cmdp[8] = r->cmd[4]; + clen = sizeof(cmd10); + break; + + default: + cmdp = r->cmd; + clen = r->clen; + break; + } + + qlock(drive); + drive->write = r->write; + drive->data = r->data; + drive->dlen = r->dlen; + + drive->status = 0; + drive->error = 0; + if(drive->pkt) + status = atapktio(drive, cmdp, clen); + else + status = atagenio(drive, cmdp, clen); + if(status == SDok){ + atasetsense(drive, SDok, 0, 0, 0); + if(drive->data){ + p = r->data; + r->rlen = drive->data - p; + } + else + r->rlen = 0; + } + else if(status == SDcheck && !(r->flags & SDnosense)){ + drive->write = 0; + memset(cmd10, 0, sizeof(cmd10)); + cmd10[0] = 0x03; + cmd10[1] = r->lun<<5; + cmd10[4] = sizeof(r->sense)-1; + drive->data = r->sense; + drive->dlen = sizeof(r->sense)-1; + drive->status = 0; + drive->error = 0; + if(drive->pkt) + reqstatus = atapktio(drive, cmd10, 6); + else + reqstatus = atagenio(drive, cmd10, 6); + if(reqstatus == SDok){ + r->flags |= SDvalidsense; + atasetsense(drive, SDok, 0, 0, 0); + } + } + qunlock(drive); + r->status = status; + if(status != SDok) + return status; + + /* + * Fix up any results. + * Many ATAPI CD-ROMs ignore the LUN field completely and + * return valid INQUIRY data. Patch the response to indicate + * 'logical unit not supported' if the LUN is non-zero. + */ + switch(cmdp[0]){ + case 0x12: /* inquiry */ + if((p = r->data) == nil) + break; + if((cmdp[1]>>5) && (!drive->pkt || (p[0] & 0x1F) == 0x05)) + p[0] = 0x7F; + /*FALLTHROUGH*/ + default: + break; + } + + return SDok; +} + +static void +atainterrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Drive *drive; + int cmdport, len, status; + + ctlr = arg; + + ilock(ctlr); + if(inb(ctlr->ctlport+As) & Bsy){ + iunlock(ctlr); + if(DEBUG & DbgBsy) + print("IBsy+"); + return; + } + cmdport = ctlr->cmdport; + status = inb(cmdport+Status); + if((drive = ctlr->curdrive) == nil){ + iunlock(ctlr); + if((DEBUG & DbgINL) && ctlr->command != Cedd) + print("Inil%2.2uX+", ctlr->command); + return; + } + + if(status & Err) + drive->error = inb(cmdport+Error); + else switch(drive->command){ + default: + drive->error = Abrt; + break; + + case Crs: + case Crsm: + if(!(status & Drq)){ + drive->error = Abrt; + break; + } + len = drive->block; + if(drive->data+len > drive->limit) + len = drive->limit-drive->data; + inss(cmdport+Data, drive->data, len/2); + drive->data += len; + if(drive->data >= drive->limit) + ctlr->done = 1; + break; + + case Cws: + case Cwsm: + len = drive->block; + if(drive->data+len > drive->limit) + len = drive->limit-drive->data; + drive->data += len; + if(drive->data >= drive->limit){ + ctlr->done = 1; + break; + } + if(!(status & Drq)){ + drive->error = Abrt; + break; + } + len = drive->block; + if(drive->data+len > drive->limit) + len = drive->limit-drive->data; + outss(cmdport+Data, drive->data, len/2); + break; + + case Cpkt: + atapktinterrupt(drive); + break; + + case Crd: + case Cwd: + // atadmainterrupt(drive, drive->count*drive->secsize); + break; + } + iunlock(ctlr); + + if(drive->error){ + status |= Err; + ctlr->done = 1; + } + + if(ctlr->done){ + ctlr->curdrive = nil; + drive->status = status; + wakeup(ctlr); + } +} + +static SDev* +atapnp(void) +{ + Ctlr *ctlr; + Pcidev *p; + int channel, ispc87415, pi, r; + SDev *legacy[2], *sdev, *head, *tail; + + legacy[0] = legacy[1] = head = tail = nil; + if(sdev = ataprobe(0x1F0, 0x3F4, IrqATA0)){ + head = tail = sdev; + legacy[0] = sdev; + } + if(sdev = ataprobe(0x170, 0x374, IrqATA1)){ + if(head != nil) + tail->next = sdev; + else + head = sdev; + tail = sdev; + legacy[1] = sdev; + } + + p = nil; + while(p = pcimatch(p, 0, 0)){ + /* + * Look for devices with the correct class and sub-class + * code and known device and vendor ID; add native-mode + * channels to the list to be probed, save info for the + * compatibility mode channels. + * Note that the legacy devices should not be considered + * PCI devices by the interrupt controller. + * For both native and legacy, save info for busmastering + * if capable. + * Promise Ultra ATA/66 (PDC20262) appears to + * 1) give a sub-class of 'other mass storage controller' + * instead of 'IDE controller', regardless of whether it's + * the only controller or not; + * 2) put 0 in the programming interface byte (probably + * as a consequence of 1) above). + * Sub-class code 0x04 is 'RAID controller', e.g. VIA VT8237. + */ + if(p->ccrb != 0x01) + continue; + if(p->ccru != 0x01 && p->ccru != 0x04 && p->ccru != 0x80) + continue; + pi = p->ccrp; + ispc87415 = 0; + + switch((p->did<<16)|p->vid){ + default: + continue; + + case (0x0002<<16)|0x100B: /* NS PC87415 */ + /* + * Disable interrupts on both channels until + * after they are probed for drives. + * This must be called before interrupts are + * enabled because the IRQ may be shared. + */ + ispc87415 = 1; + pcicfgw32(p, 0x40, 0x00000300); + break; + case (0x1000<<16)|0x1042: /* PC-Tech RZ1000 */ + /* + * Turn off prefetch. Overkill, but cheap. + */ + r = pcicfgr32(p, 0x40); + r &= ~0x2000; + pcicfgw32(p, 0x40, r); + break; + case (0x4D38<<16)|0x105A: /* Promise PDC20262 */ + case (0x4D30<<16)|0x105A: /* Promise PDC202xx */ + case (0x4D68<<16)|0x105A: /* Promise PDC20268 */ + case (0x4D69<<16)|0x105A: /* Promise Ultra/133 TX2 */ + case (0x3373<<16)|0x105A: /* Promise 20378 RAID */ + case (0x3149<<16)|0x1106: /* VIA VT8237 SATA/RAID */ + case (0x3112<<16)|0x1095: /* SiL 3112 SATA (DMA busted?) */ + case (0x3114<<16)|0x1095: /* SiL 3114 SATA/RAID */ + pi = 0x85; + break; + case (0x0004<<16)|0x1103: /* HighPoint HPT-370 */ + pi = 0x85; + /* + * Turn off fast interrupt prediction. + */ + if((r = pcicfgr8(p, 0x51)) & 0x80) + pcicfgw8(p, 0x51, r & ~0x80); + if((r = pcicfgr8(p, 0x55)) & 0x80) + pcicfgw8(p, 0x55, r & ~0x80); + break; + case (0x0640<<16)|0x1095: /* CMD 640B */ + /* + * Bugfix code here... + */ + break; + case (0x7441<<16)|0x1022: /* AMD 768 */ + /* + * Set: + * 0x41 prefetch, postwrite; + * 0x43 FIFO configuration 1/2 and 1/2; + * 0x44 status register read retry; + * 0x46 DMA read and end of sector flush. + */ + r = pcicfgr8(p, 0x41); + pcicfgw8(p, 0x41, r|0xF0); + r = pcicfgr8(p, 0x43); + pcicfgw8(p, 0x43, (r & 0x90)|0x2A); + r = pcicfgr8(p, 0x44); + pcicfgw8(p, 0x44, r|0x08); + r = pcicfgr8(p, 0x46); + pcicfgw8(p, 0x46, (r & 0x0C)|0xF0); + /*FALLTHROUGH*/ + case (0x7469<<16)|0x1022: /* AMD 3111 */ + /* + * This can probably be lumped in with the 768 above. + */ + /*FALLTHROUGH*/ + case (0x00D5<<16)|0x10DE: /* nVidia nForce3 */ + /* + * Ditto, although it may have a different base + * address for the registers (0x50?). + */ + break; + case (0x0646<<16)|0x1095: /* CMD 646 */ + case (0x0571<<16)|0x1106: /* VIA 82C686 */ + case (0x0211<<16)|0x1166: /* ServerWorks IB6566 */ + case (0x1230<<16)|0x8086: /* 82371FB (PIIX) */ + case (0x7010<<16)|0x8086: /* 82371SB (PIIX3) */ + case (0x7111<<16)|0x8086: /* 82371[AE]B (PIIX4[E]) */ + case (0x2411<<16)|0x8086: /* 82801AA (ICH) */ + case (0x2421<<16)|0x8086: /* 82801AB (ICH0) */ + case (0x244A<<16)|0x8086: /* 82801BA (ICH2, Mobile) */ + case (0x244B<<16)|0x8086: /* 82801BA (ICH2, High-End) */ + case (0x248A<<16)|0x8086: /* 82801CA (ICH3, Mobile) */ + case (0x248B<<16)|0x8086: /* 82801CA (ICH3, High-End) */ + case (0x24CA<<16)|0x8086: /* 82801DBM (ICH4, Mobile) */ + case (0x24CB<<16)|0x8086: /* 82801DB (ICH4, High-End) */ + case (0x24DB<<16)|0x8086: /* 82801EB (ICH5) */ + break; + } + + for(channel = 0; channel < 2; channel++){ + if(pi & (1<<(2*channel))){ + sdev = ataprobe(p->mem[0+2*channel].bar & ~0x01, + p->mem[1+2*channel].bar & ~0x01, + p->intl); + if(sdev == nil) + continue; + + ctlr = sdev->ctlr; + if(ispc87415) + ctlr->ienable = pc87415ienable; + + if(head != nil) + tail->next = sdev; + else + head = sdev; + tail = sdev; + ctlr->tbdf = p->tbdf; + } + else if((sdev = legacy[channel]) == nil) + continue; + else + ctlr = sdev->ctlr; + + ctlr->pcidev = p; + } + } + + return head; +} + +static SDev* +atalegacy(int port, int irq) +{ + return ataprobe(port, port+0x204, irq); +} + +static SDev* +ataid(SDev* sdev) +{ + int i; + Ctlr *ctlr; + + /* + * Legacy controllers are always 'C' and 'D' and if + * they exist and have drives will be first in the list. + * If there are no active legacy controllers, native + * controllers start at 'C'. + */ + if(sdev == nil) + return nil; + ctlr = sdev->ctlr; + if(ctlr->cmdport == 0x1F0 || ctlr->cmdport == 0x170) + i = 2; + else + i = 0; + while(sdev){ + if(sdev->ifc == &sdataifc){ + ctlr = sdev->ctlr; + if(ctlr->cmdport == 0x1F0) + sdev->idno = 'C'; + else if(ctlr->cmdport == 0x170) + sdev->idno = 'D'; + else{ + sdev->idno = 'C'+i; + i++; + } + // snprint(sdev->name, NAMELEN, "sd%c", sdev->idno); + } + sdev = sdev->next; + } + + return nil; +} + +static int +ataenable(SDev* sdev) +{ + Ctlr *ctlr; + + ctlr = sdev->ctlr; + + setvec(ctlr->irq+VectorPIC, atainterrupt, ctlr); + outb(ctlr->ctlport+Dc, 0); + if(ctlr->ienable) + ctlr->ienable(ctlr); + + return 1; +} + +SDifc sdataifc = { + "ata", /* name */ + + atapnp, /* pnp */ + atalegacy, /* legacy */ + ataid, /* id */ + ataenable, /* enable */ + nil, /* disable */ + + scsiverify, /* verify */ + scsionline, /* online */ + atario, /* rio */ + nil, /* rctl */ + nil, /* wctl */ + + scsibio, /* bio */ +}; diff --git a/os/boot/pc/sdmylex.c b/os/boot/pc/sdmylex.c new file mode 100644 index 00000000..bca23489 --- /dev/null +++ b/os/boot/pc/sdmylex.c @@ -0,0 +1,1294 @@ +/* + * Mylex MultiMaster (Buslogic BT-*) SCSI Host Adapter + * in both 24-bit and 32-bit mode. + * 24-bit mode works for Adaptec AHA-154xx series too. + * + * To do: + * allocate more Ccb's as needed, up to NMbox-1; + * add nmbox and nccb to Ctlr struct for the above; + * 64-bit LUN/explicit wide support necessary? + * + */ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "error.h" + +#include "sd.h" + +#define waserror() (0) +#define poperror() +typedef struct QLock{ int r; } QLock; +typedef struct Rendez{ int r; } Rendez; +#define intrenable(irq, f, c, tbdf, name) setvec(VectorPIC+(irq), f, c);\ + USED(tbdf); +#define ioalloc(p, b, c, d) (1) +#define iofree(p) + +#define K2BPA(va, tbdf) PADDR(va) +#define BPA2K(pa, tbdf) KADDR(pa) + +extern SDifc sdmylexifc; + +enum { /* registers */ + Rcontrol = 0x00, /* WO: control register */ + Rstatus = 0x00, /* RO: status register */ + Rcpr = 0x01, /* WO: command/parameter register */ + Rdatain = 0x01, /* RO: data-in register */ + Rinterrupt = 0x02, /* RO: interrupt register */ +}; + +enum { /* Rcontrol */ + Rsbus = 0x10, /* SCSI Bus Reset */ + Rint = 0x20, /* Interrupt Reset */ + Rsoft = 0x40, /* Soft Reset */ + Rhard = 0x80, /* Hard Reset */ +}; + +enum { /* Rstatus */ + Cmdinv = 0x01, /* Command Invalid */ + Dirrdy = 0x04, /* Data In Register Ready */ + Cprbsy = 0x08, /* Command/Parameter Register Busy */ + Hardy = 0x10, /* Host Adapter Ready */ + Inreq = 0x20, /* Initialisation Required */ + Dfail = 0x40, /* Diagnostic Failure */ + Dact = 0x80, /* Diagnostic Active */ +}; + +enum { /* Rcpr */ + Cinitialise = 0x01, /* Initialise Mailbox */ + Cstart = 0x02, /* Start Mailbox Command */ + Cinquiry = 0x04, /* Adapter Inquiry */ + Ceombri = 0x05, /* Enable OMBR Interrupt */ + Cinquire = 0x0B, /* Inquire Configuration */ + Cextbios = 0x28, /* AHA-1542: extended BIOS info. */ + Cmbienable = 0x29, /* AHA-1542: Mailbox interface enable */ + Ciem = 0x81, /* Initialise Extended Mailbox */ + Ciesi = 0x8D, /* Inquire Extended Setup Information */ + Cerrm = 0x8F, /* Enable strict round-robin mode */ + Cwide = 0x96, /* Wide CCB */ +}; + +enum { /* Rinterrupt */ + Imbl = 0x01, /* Incoming Mailbox Loaded */ + Mbor = 0x02, /* Mailbox Out Ready */ + Cmdc = 0x04, /* Command Complete */ + Rsts = 0x08, /* SCSI Reset State */ + Intv = 0x80, /* Interrupt Valid */ +}; + +typedef struct { + uchar code; /* action/completion code */ + uchar ccb[3]; /* CCB pointer (MSB, ..., LSB) */ +} Mbox24; + +typedef struct { + uchar ccb[4]; /* CCB pointer (LSB, ..., MSB) */ + uchar btstat; /* BT-7[45]7[SD] status */ + uchar sdstat; /* SCSI device status */ + uchar pad; + uchar code; /* action/completion code */ +} Mbox32; + +enum { /* mailbox commands */ + Mbfree = 0x00, /* Mailbox not in use */ + + Mbostart = 0x01, /* Start a mailbox command */ + Mboabort = 0x02, /* Abort a mailbox command */ + + Mbiok = 0x01, /* CCB completed without error */ + Mbiabort = 0x02, /* CCB aborted at request of host */ + Mbinx = 0x03, /* Aborted CCB not found */ + Mbierror = 0x04, /* CCB completed with error */ +}; + +typedef struct Ccb24 Ccb24; +typedef struct Ccb32 Ccb32; +typedef union Ccb Ccb; + +typedef struct Ccb24 { + uchar opcode; /* Operation code */ + uchar datadir; /* Data direction control */ + uchar cdblen; /* Length of CDB */ + uchar senselen; /* Length of sense area */ + uchar datalen[3]; /* Data length (MSB, ..., LSB) */ + uchar dataptr[3]; /* Data pointer (MSB, ..., LSB) */ + uchar linkptr[3]; /* Link pointer (MSB, ..., LSB) */ + uchar linkid; /* command linking identifier */ + uchar btstat; /* BT-* adapter status */ + uchar sdstat; /* SCSI device status */ + uchar reserved[2]; /* */ + uchar cs[12+0xFF]; /* Command descriptor block + Sense */ + + void* data; /* buffer if address > 24-bits */ + + Rendez; + int done; /* command completed */ + + Ccb* ccb; /* link on free list */ +} Ccb24; + + +typedef struct Ccb32 { + uchar opcode; /* Operation code */ + uchar datadir; /* Data direction control */ + uchar cdblen; /* Length of CDB */ + uchar senselen; /* Length of sense area */ + uchar datalen[4]; /* Data length (LSB, ..., MSB) */ + uchar dataptr[4]; /* Data pointer (LSB, ..., MSB) */ + uchar reserved[2]; + uchar btstat; /* BT-* adapter status */ + uchar sdstat; /* SCSI device status */ + uchar targetid; /* Target ID */ + uchar luntag; /* LUN & tag */ + uchar cdb[12]; /* Command descriptor block */ + uchar ccbctl; /* CCB control */ + uchar linkid; /* command linking identifier */ + uchar linkptr[4]; /* Link pointer (LSB, ..., MSB) */ + uchar senseptr[4]; /* Sense pointer (LSB, ..., MSB) */ + uchar sense[0xFF]; /* Sense bytes */ + + Rendez; + int done; /* command completed */ + + Ccb* ccb; /* link on free list */ +} Ccb32; + +typedef union Ccb { + Ccb24; + Ccb32; +} Ccb; + +enum { /* opcode */ + OInitiator = 0x00, /* initiator CCB */ + Ordl = 0x03, /* initiator CCB with + * residual data length returned + */ +}; + +enum { /* datadir */ + CCBdatain = 0x08, /* inbound, length is checked */ + CCBdataout = 0x10, /* outbound, length is checked */ +}; + +enum { /* btstat */ + Eok = 0x00, /* normal completion with no errors */ +}; + +enum { /* luntag */ + TagEnable = 0x20, /* Tag enable */ + SQTag = 0x00, /* Simple Queue Tag */ + HQTag = 0x40, /* Head of Queue Tag */ + OQTag = 0x80, /* Ordered Queue Tag */ +}; + +enum { /* CCB control */ + NoDisc = 0x08, /* No disconnect */ + NoUnd = 0x10, /* No underrrun error report */ + NoData = 0x20, /* No data transfer */ + NoStat = 0x40, /* No CCB status if zero */ + NoIntr = 0x80, /* No Interrupts */ +}; + +typedef struct { + int port; /* I/O port */ + int id; /* adapter SCSI id */ + int bus; /* 24 or 32 -bit */ + int irq; + int wide; + Pcidev* pcidev; + SDev* sdev; + int spurious; + + Lock issuelock; + + Lock ccblock; + QLock ccbq; + Rendez ccbr; + + Lock mboxlock; + void* mb; /* mailbox out + mailbox in */ + int mbox; /* current mailbox out index into mb */ + int mbix; /* current mailbox in index into mb */ + + Lock cachelock; + Ccb* ccb; /* list of free Ccb's */ + Ccb** cache; /* last completed Ccb */ +} Ctlr; + +/* + * The number of mailboxes should be a multiple of 8 (4 for Mbox32) + * to ensure the boundary between the out and in mailboxes doesn't + * straddle a cache-line boundary. + * The number of Ccb's should be less than the number of mailboxes to + * ensure no queueing is necessary on mailbox allocation. + */ +enum { + NMbox = 8*8, /* number of Mbox's */ + NCcb = NMbox-1, /* number of Ccb's */ +}; + +#define PADDR24(a, n) ((PADDR(a)+(n)) <= (1<<24)) + +static void +ccbfree(Ctlr* ctlr, Ccb* ccb) +{ + lock(&ctlr->ccblock); + if(ctlr->bus == 24) + ((Ccb24*)ccb)->ccb = ctlr->ccb; + else + ((Ccb32*)ccb)->ccb = ctlr->ccb; + if(ctlr->ccb == nil) + wakeup(&ctlr->ccbr); + ctlr->ccb = ccb; + unlock(&ctlr->ccblock); +} + +static int +ccbavailable(void* a) +{ + return ((Ctlr*)a)->ccb != nil; +} + +static Ccb* +ccballoc(Ctlr* ctlr) +{ + Ccb *ccb; + + for(;;){ + lock(&ctlr->ccblock); + if((ccb = ctlr->ccb) != nil){ + if(ctlr->bus == 24) + ctlr->ccb = ((Ccb24*)ccb)->ccb; + else + ctlr->ccb = ((Ccb32*)ccb)->ccb; + unlock(&ctlr->ccblock); + break; + } + + unlock(&ctlr->ccblock); + qlock(&ctlr->ccbq); + if(waserror()){ + qunlock(&ctlr->ccbq); + continue; + } + sleep(&ctlr->ccbr, ccbavailable, ctlr); + qunlock(&ctlr->ccbq); + poperror(); + } + + return ccb; +} + +static int +done24(void* arg) +{ + return ((Ccb24*)arg)->done; +} + +static int +mylex24rio(SDreq* r) +{ + ulong p; + Ctlr *ctlr; + Ccb24 *ccb; + Mbox24 *mb; + uchar *data, lun, *sense; + int d, n, btstat, sdstat, target; + + ctlr = r->unit->dev->ctlr; + target = r->unit->subno; + lun = (r->cmd[1]>>5) & 0x07; + + /* + * Ctlr->cache holds the last completed Ccb for this target if it + * returned 'check condition'. + * If this command is a request-sense and there is valid sense data + * from the last completed Ccb, return it immediately. + */ + lock(&ctlr->cachelock); + if((ccb = ctlr->cache[target]) != nil){ + ctlr->cache[target] = nil; + if(r->cmd[0] == 0x03 + && ccb->sdstat == SDcheck && lun == ((ccb->cs[1]>>5) & 0x07)){ + unlock(&ctlr->cachelock); + if(r->dlen){ + sense = &ccb->cs[ccb->cdblen]; + n = 8+sense[7]; + if(n > r->dlen) + n = r->dlen; + memmove(r->data, sense, n); + r->rlen = n; + } + ccbfree(ctlr, (Ccb*)ccb); + return SDok; + } + } + unlock(&ctlr->cachelock); + if(ccb == nil) + ccb = ccballoc(ctlr); + + /* + * Check if the transfer is to memory above the 24-bit limit the + * controller can address. If it is, try to allocate a temporary + * buffer as a staging area. + */ + n = r->dlen; + if(n && !PADDR24(r->data, n)){ + data = mallocz(n, 0); + if(data == nil || !PADDR24(data, n)){ + if(data != nil){ + free(data); + ccb->data = nil; + } + ccbfree(ctlr, (Ccb*)ccb); + return SDmalloc; + } + if(r->write) + memmove(data, r->data, n); + ccb->data = r->data; + } + else + data = r->data; + + /* + * Fill in the ccb. + */ + ccb->opcode = Ordl; + + ccb->datadir = (target<<5)|lun; + if(n == 0) + ccb->datadir |= CCBdataout|CCBdatain; + else if(!r->write) + ccb->datadir |= CCBdatain; + else + ccb->datadir |= CCBdataout; + + ccb->cdblen = r->clen; + ccb->senselen = 0xFF; + + ccb->datalen[0] = n>>16; + ccb->datalen[1] = n>>8; + ccb->datalen[2] = n; + p = PADDR(data); + ccb->dataptr[0] = p>>16; + ccb->dataptr[1] = p>>8; + ccb->dataptr[2] = p; + + ccb->linkptr[0] = ccb->linkptr[1] = ccb->linkptr[2] = 0; + ccb->linkid = 0; + ccb->btstat = ccb->sdstat = 0; + ccb->reserved[0] = ccb->reserved[1] = 0; + + memmove(ccb->cs, r->cmd, r->clen); + + /* + * There's one more mbox than there there is + * ccb so there is always one free. + */ + lock(&ctlr->mboxlock); + mb = ctlr->mb; + mb += ctlr->mbox; + p = PADDR(ccb); + mb->ccb[0] = p>>16; + mb->ccb[1] = p>>8; + mb->ccb[2] = p; + mb->code = Mbostart; + ctlr->mbox++; + if(ctlr->mbox >= NMbox) + ctlr->mbox = 0; + + /* + * This command does not require Hardy + * and doesn't generate a Cmdc interrupt. + */ + ccb->done = 0; + outb(ctlr->port+Rcpr, Cstart); + unlock(&ctlr->mboxlock); + + /* + * Wait for the request to complete and return the status. + * Since the buffer is not reference counted cannot return + * until the DMA is done writing into the buffer so the caller + * cannot free the buffer prematurely. + */ + while(waserror()) + ; + tsleep(ccb, done24, ccb, 30*1000); + poperror(); + + if(!done24(ccb)){ + print("%s: %d/%d: sd24rio timeout\n", + "sdmylex"/*ctlr->sdev->name*/, target, r->lun); + if(ccb->data != nil){ + free(data); + ccb->data = nil; + } + ccbfree(ctlr, (Ccb*)ccb); + + return SDtimeout; + } + + /* + * Save the status and patch up the number of + * bytes actually transferred. + * There's a firmware bug on some 956C controllers + * which causes the return count from a successful + * READ CAPACITY not be updated, so fix it here. + */ + sdstat = ccb->sdstat; + btstat = ccb->btstat; + + d = ccb->datalen[0]<<16; + d |= ccb->datalen[1]<<8; + d |= ccb->datalen[2]; + if(ccb->cs[0] == 0x25 && sdstat == SDok) + d = 0; + n -= d; + r->rlen = n; + + /* + * Tidy things up if a staging area was used for the data, + */ + if(ccb->data != nil){ + if(sdstat == SDok && btstat == 0 && !r->write) + memmove(ccb->data, data, n); + free(data); + ccb->data = nil; + } + + /* + * If there was a check-condition, save the + * ccb for a possible request-sense command. + */ + if(sdstat == SDcheck){ + if(r->flags & SDnosense){ + lock(&ctlr->cachelock); + if(ctlr->cache[target]) + ccbfree(ctlr, ctlr->cache[target]); + ctlr->cache[target] = (Ccb*)ccb; + unlock(&ctlr->cachelock); + return SDcheck; + } + sense = &ccb->cs[ccb->cdblen]; + n = 8+sense[7]; + if(n > sizeof(r->sense)-1) + n = sizeof(r->sense)-1; + memmove(r->sense, sense, n); + r->flags |= SDvalidsense; + } + ccbfree(ctlr, (Ccb*)ccb); + + if(btstat){ + if(btstat == 0x11) + return SDtimeout; + return SDeio; + } + return sdstat; +} + +static void +mylex24interrupt(Ureg*, void* arg) +{ + ulong pa; + Ctlr *ctlr; + Ccb24 *ccb; + Mbox24 *mb, *mbox; + int port, rinterrupt, rstatus; + + ctlr = arg; + port = ctlr->port; + + /* + * Save and clear the interrupt(s). The only + * interrupts expected are Cmdc, which is ignored, + * and Imbl which means something completed. + * There's one spurious interrupt left over from + * initialisation, ignore it. + */ + rinterrupt = inb(port+Rinterrupt); + rstatus = inb(port+Rstatus); + outb(port+Rcontrol, Rint); + if((rinterrupt & ~(Cmdc|Imbl)) != Intv && ctlr->spurious++) + print("%s: interrupt 0x%2.2ux\n", + "sdmylex"/*ctlr->sdev->name*/, rinterrupt); + if((rinterrupt & Cmdc) && (rstatus & Cmdinv)) + print("%s: command invalid\n", "sdmylex"/*ctlr->sdev->name*/); + + /* + * Look for something in the mail. + * If there is, save the status, free the mailbox + * and wakeup whoever. + */ + mb = ctlr->mb; + for(mbox = &mb[ctlr->mbix]; mbox->code; mbox = &mb[ctlr->mbix]){ + pa = (mbox->ccb[0]<<16)|(mbox->ccb[1]<<8)|mbox->ccb[2]; + ccb = BPA2K(pa, BUSUNKNOWN); + mbox->code = 0; + ccb->done = 1; + wakeup(ccb); + + ctlr->mbix++; + if(ctlr->mbix >= NMbox+NMbox) + ctlr->mbix = NMbox; + } +} + +static int +done32(void* arg) +{ + return ((Ccb32*)arg)->done; +} + +static int +mylex32rio(SDreq* r) +{ + ulong p; + uchar lun; + Ctlr *ctlr; + Ccb32 *ccb; + Mbox32 *mb; + int d, n, btstat, sdstat, target; + + ctlr = r->unit->dev->ctlr; + target = r->unit->subno; + lun = (r->cmd[1]>>5) & 0x07; + + /* + * Ctlr->cache holds the last completed Ccb for this target if it + * returned 'check condition'. + * If this command is a request-sense and there is valid sense data + * from the last completed Ccb, return it immediately. + */ + lock(&ctlr->cachelock); + if((ccb = ctlr->cache[target]) != nil){ + ctlr->cache[target] = nil; + if(r->cmd[0] == 0x03 + && ccb->sdstat == SDcheck && lun == (ccb->luntag & 0x07)){ + unlock(&ctlr->cachelock); + if(r->dlen){ + n = 8+ccb->sense[7]; + if(n > r->dlen) + n = r->dlen; + memmove(r->data, ccb->sense, n); + r->rlen = n; + } + ccbfree(ctlr, (Ccb*)ccb); + return SDok; + } + } + unlock(&ctlr->cachelock); + if(ccb == nil) + ccb = ccballoc(ctlr); + + /* + * Fill in the ccb. + */ + ccb->opcode = Ordl; + + n = r->dlen; + if(n == 0) + ccb->datadir = CCBdataout|CCBdatain; + else if(!r->write) + ccb->datadir = CCBdatain; + else + ccb->datadir = CCBdataout; + + ccb->cdblen = r->clen; + + ccb->datalen[0] = n; + ccb->datalen[1] = n>>8; + ccb->datalen[2] = n>>16; + ccb->datalen[3] = n>>24; + p = PADDR(r->data); + ccb->dataptr[0] = p; + ccb->dataptr[1] = p>>8; + ccb->dataptr[2] = p>>16; + ccb->dataptr[3] = p>>24; + + ccb->targetid = target; + ccb->luntag = lun; + if(r->unit->inquiry[7] & 0x02) + ccb->luntag |= SQTag|TagEnable; + memmove(ccb->cdb, r->cmd, r->clen); + ccb->btstat = ccb->sdstat = 0; + ccb->ccbctl = 0; + + /* + * There's one more mbox than there there is + * ccb so there is always one free. + */ + lock(&ctlr->mboxlock); + mb = ctlr->mb; + mb += ctlr->mbox; + p = PADDR(ccb); + mb->ccb[0] = p; + mb->ccb[1] = p>>8; + mb->ccb[2] = p>>16; + mb->ccb[3] = p>>24; + mb->code = Mbostart; + ctlr->mbox++; + if(ctlr->mbox >= NMbox) + ctlr->mbox = 0; + + /* + * This command does not require Hardy + * and doesn't generate a Cmdc interrupt. + */ + ccb->done = 0; + outb(ctlr->port+Rcpr, Cstart); + unlock(&ctlr->mboxlock); + + /* + * Wait for the request to complete and return the status. + * Since the buffer is not reference counted cannot return + * until the DMA is done writing into the buffer so the caller + * cannot free the buffer prematurely. + */ + while(waserror()) + ; + tsleep(ccb, done32, ccb, 30*1000); + poperror(); + + if(!done32(ccb)){ + print("%s: %d/%d: sd32rio timeout\n", + "sdmylex"/*ctlr->sdev->name*/, target, r->lun); + ccbfree(ctlr, (Ccb*)ccb); + + return SDtimeout; + } + + /* + * Save the status and patch up the number of + * bytes actually transferred. + * There's a firmware bug on some 956C controllers + * which causes the return count from a successful + * READ CAPACITY not to be updated, so fix it here. + */ + sdstat = ccb->sdstat; + btstat = ccb->btstat; + + d = ccb->datalen[0]; + d |= (ccb->datalen[1]<<8); + d |= (ccb->datalen[2]<<16); + d |= (ccb->datalen[3]<<24); + if(ccb->cdb[0] == 0x25 && sdstat == SDok) + d = 0; + n -= d; + r->rlen = n; + + /* + * If there was a check-condition, save the + * ccb for a possible request-sense command. + */ + if(sdstat == SDcheck){ + if(r->flags & SDnosense){ + lock(&ctlr->cachelock); + if(ctlr->cache[target]) + ccbfree(ctlr, ctlr->cache[target]); + ctlr->cache[target] = (Ccb*)ccb; + unlock(&ctlr->cachelock); + return SDcheck; + } + n = 8+ccb->sense[7]; + if(n > sizeof(r->sense)-1) + n = sizeof(r->sense)-1; + memmove(r->sense, ccb->sense, n); + r->flags |= SDvalidsense; + } + ccbfree(ctlr, (Ccb*)ccb); + + if(btstat){ + if(btstat == 0x11) + return SDtimeout; + return SDeio; + } + return sdstat; +} + +static void +mylex32interrupt(Ureg*, void* arg) +{ + ulong pa; + Ctlr *ctlr; + Ccb32 *ccb; + Mbox32 *mb, *mbox; + int port, rinterrupt, rstatus; + + ctlr = arg; + port = ctlr->port; + + /* + * Save and clear the interrupt(s). The only + * interrupts expected are Cmdc, which is ignored, + * and Imbl which means something completed. + * There's one spurious interrupt left over from + * initialisation, ignore it. + */ + rinterrupt = inb(port+Rinterrupt); + rstatus = inb(port+Rstatus); + outb(port+Rcontrol, Rint); + if((rinterrupt & ~(Cmdc|Imbl)) != Intv && ctlr->spurious++) + print("%s: interrupt 0x%2.2ux\n", + "sdmylex"/*ctlr->sdev->name*/, rinterrupt); + if((rinterrupt & Cmdc) && (rstatus & Cmdinv)) + print("%s: command invalid\n", "sdmylex"/*ctlr->sdev->name*/); + + /* + * Look for something in the mail. + * If there is, free the mailbox and wakeup whoever. + */ + mb = ctlr->mb; + for(mbox = &mb[ctlr->mbix]; mbox->code; mbox = &mb[ctlr->mbix]){ + pa = (mbox->ccb[3]<<24) + |(mbox->ccb[2]<<16) + |(mbox->ccb[1]<<8) + |mbox->ccb[0]; + if(ctlr->pcidev) + ccb = BPA2K(pa, ctlr->pcidev->tbdf); + else + ccb = BPA2K(pa, BUSUNKNOWN); + mbox->code = 0; + ccb->done = 1; + wakeup(ccb); + + ctlr->mbix++; + if(ctlr->mbix >= NMbox+NMbox) + ctlr->mbix = NMbox; + } +} + +static int +mylexrio(SDreq* r) +{ + int subno; + Ctlr *ctlr; + + subno = r->unit->subno; + ctlr = r->unit->dev->ctlr; + if(subno == ctlr->id || (!ctlr->wide && subno >= 8)) + r->status = SDtimeout; + else if(ctlr->bus == 24) + r->status = mylex24rio(r); + else + r->status = mylex32rio(r); + return r->status; +} + +/* + * Issue a command to a controller. The command and its length is + * contained in cmd and cmdlen. If any data is to be + * returned, datalen should be non-zero, and the returned data + * will be placed in data. + * If Cmdc is set, bail out, the invalid command will be handled + * when the interrupt is processed. + */ +static void +issueio(int port, uchar* cmd, int cmdlen, uchar* data, int datalen) +{ + int len; + + if(cmd[0] != Cstart && cmd[0] != Ceombri){ + while(!(inb(port+Rstatus) & Hardy)) + ; + } + outb(port+Rcpr, cmd[0]); + + len = 1; + while(len < cmdlen){ + if(!(inb(port+Rstatus) & Cprbsy)){ + outb(port+Rcpr, cmd[len]); + len++; + } + if(inb(port+Rinterrupt) & Cmdc) + return; + } + + if(datalen){ + len = 0; + while(len < datalen){ + if(inb(port+Rstatus) & Dirrdy){ + data[len] = inb(port+Rdatain); + len++; + } + if(inb(port+Rinterrupt) & Cmdc) + return; + } + } +} + +/* + * Issue a command to a controller, wait for it to complete then + * try to reset the interrupt. Should only be called at initialisation. + */ +static int +issue(Ctlr* ctlr, uchar* cmd, int cmdlen, uchar* data, int datalen) +{ + int port; + uchar rinterrupt, rstatus; + static Lock mylexissuelock; + + port = ctlr->port; + + ilock(&ctlr->issuelock); + issueio(port, cmd, cmdlen, data, datalen); + + while(!((rinterrupt = inb(port+Rinterrupt)) & Cmdc)) + ; + + rstatus = inb(port+Rstatus); + outb(port+Rcontrol, Rint); + iunlock(&ctlr->issuelock); + + if((rinterrupt & Cmdc) && (rstatus & Cmdinv)) + return 0; + return 1; +} + +static SDev* +mylexprobe(int port, int irq) +{ + SDev *sdev; + Ctlr *ctlr; + uchar cmd[6], data[256]; + int clen, dlen, timeo; + + if(ioalloc(port, 0x3, 0, "mylex") < 0) + return nil; + ctlr = nil; + + /* + * Attempt to hard-reset the board and reset + * the SCSI bus. If the board state doesn't settle to + * idle with mailbox initialisation required, either + * it isn't a compatible board or it's broken. + * If the controller has SCAM set this can take a while. + */ + if(getconf("*noscsireset") != nil) + outb(port+Rcontrol, Rhard); + else + outb(port+Rcontrol, Rhard|Rsbus); + for(timeo = 0; timeo < 100; timeo++){ + if(inb(port+Rstatus) == (Inreq|Hardy)) + break; + delay(100); + } + if(inb(port+Rstatus) != (Inreq|Hardy)){ +buggery: + if(ctlr != nil) + free(ctlr); + iofree(port); + return nil; + } + + if((ctlr = malloc(sizeof(Ctlr))) == nil) + goto buggery; + ctlr->port = port; + ctlr->irq = irq; + ctlr->bus = 24; + ctlr->wide = 0; + + /* + * Try to determine if this is a 32-bit MultiMaster controller + * by attempting to obtain the extended inquiry information; + * this command is not implemented on Adaptec 154xx + * controllers. If successful, the first byte of the returned + * data is the host adapter bus type, 'E' for 32-bit EISA, + * PCI and VLB buses. + */ + cmd[0] = Ciesi; + cmd[1] = 4; + clen = 2; + dlen = 256; + if(issue(ctlr, cmd, clen, data, dlen)){ + if(data[0] == 'E') + ctlr->bus = 32; + ctlr->wide = data[0x0D] & 0x01; + } + else{ + /* + * Inconceivable though it may seem, a hard controller reset + * is necessary here to clear out the command queue. Every + * board seems to lock-up in a different way if you give an + * invalid command and then try to clear out the + * command/parameter and/or data-in register. + * Soft reset doesn't do the job either. Fortunately no + * serious initialisation has been done yet so there's nothing + * to tidy up. + */ + outb(port+Rcontrol, Rhard); + for(timeo = 0; timeo < 100; timeo++){ + if(inb(port+Rstatus) == (Inreq|Hardy)) + break; + delay(100); + } + if(inb(port+Rstatus) != (Inreq|Hardy)) + goto buggery; + } + + /* + * If the BIOS is enabled on the AHA-1542C/CF and BIOS options for + * support of drives > 1Gb, dynamic scanning of the SCSI bus or more + * than 2 drives under DOS 5.0 are enabled, the BIOS disables + * accepting Cmbinit to protect against running with drivers which + * don't support those options. In order to unlock the interface it + * is necessary to read a lock-code using Cextbios and write it back + * using Cmbienable; the lock-code is non-zero. + */ + cmd[0] = Cinquiry; + clen = 1; + dlen = 4; + if(issue(ctlr, cmd, clen, data, dlen) == 0) + goto buggery; + if(data[0] >= 0x43){ + cmd[0] = Cextbios; + clen = 1; + dlen = 2; + if(issue(ctlr, cmd, clen, data, dlen) == 0) + goto buggery; + + /* + * Lock-code returned in data[1]. If it's non-zero write + * it back along with bit 0 of byte 0 cleared to enable + * mailbox initialisation. + */ + if(data[1]){ + cmd[0] = Cmbienable; + cmd[1] = 0; + cmd[2] = data[1]; + clen = 3; + if(issue(ctlr, cmd, clen, 0, 0) == 0) + goto buggery; + } + } + + /* + * Get the id, DMA and IRQ info from the board. This will + * cause an interrupt which will hopefully not cause any + * trouble because the interrupt number isn't known yet. + * This is necessary as the DMA won't be set up if the + * board has the BIOS disabled. + * + * If the IRQ is already known, this must be a 32-bit PCI + * or EISA card, in which case the returned DMA and IRQ can + * be ignored. + */ + cmd[0] = Cinquire; + clen = 1; + dlen = 3; + if(issue(ctlr, cmd, clen, data, dlen) == 0) + goto buggery; + + ctlr->id = data[2] & 0x07; + if(ctlr->irq < 0){ + switch(data[0]){ /* DMA Arbitration Priority */ + case 0x80: /* Channel 7 */ + outb(0xD6, 0xC3); + outb(0xD4, 0x03); + break; + case 0x40: /* Channel 6 */ + outb(0xD6, 0xC2); + outb(0xD4, 0x02); + break; + case 0x20: /* Channel 5 */ + outb(0xD6, 0xC1); + outb(0xD4, 0x01); + break; + case 0x01: /* Channel 0 */ + outb(0x0B, 0xC0); + outb(0x0A, 0x00); + break; + default: + if(ctlr->bus == 24) + goto buggery; + break; + } + + switch(data[1]){ /* Interrupt Channel */ + case 0x40: + ctlr->irq = 15; + break; + case 0x20: + ctlr->irq = 14; + break; + case 0x08: + ctlr->irq = 12; + break; + case 0x04: + ctlr->irq = 11; + break; + case 0x02: + ctlr->irq = 10; + break; + case 0x01: + ctlr->irq = 9; + break; + default: + goto buggery; + } + } + + if((sdev = malloc(sizeof(SDev))) == nil) + goto buggery; + sdev->ifc = &sdmylexifc; + sdev->ctlr = ctlr; + ctlr->sdev = sdev; + if(!ctlr->wide) + sdev->nunit = 8; + else + sdev->nunit = 16; + + return sdev; +} + +static int mylexport[8] = { + 0x330, 0x334, 0x230, 0x234, 0x130, 0x134, 0x000, 0x000, +}; + +static SDev* +mylexpnp(void) +{ + Pcidev *p; + Ctlr *ctlr; + ISAConf isa; + int cfg, ctlrno, i, x; + SDev *sdev, *head, *tail; + + p = nil; + head = tail = nil; + while(p = pcimatch(p, 0x104B, 0)){ + if((sdev = mylexprobe(p->mem[0].bar & ~0x01, p->intl)) == nil) + continue; + + ctlr = sdev->ctlr; + ctlr->pcidev = p; + + if(head != nil) + tail->next = sdev; + else + head = sdev; + tail = sdev; + } + + if(strncmp(KADDR(0xFFFD9), "EISA", 4) == 0){ + for(cfg = 0x1000; cfg < MaxEISA*0x1000; cfg += 0x1000){ + x = 0; + for(i = 0; i < 4; i++) + x |= inb(cfg+CfgEISA+i)<<(i*8); + if(x != 0x0142B30A && x != 0x0242B30A) + continue; + + x = inb(cfg+0xC8C); + if((sdev = mylexprobe(mylexport[x & 0x07], -1)) == nil) + continue; + + if(head != nil) + tail->next = sdev; + else + head = sdev; + tail = sdev; + } + } + + for(ctlrno = 0; ctlrno < 4; ctlrno++){ + memset(&isa, 0, sizeof(isa)); + if(!isaconfig("scsi", ctlrno, &isa)) + continue; + if(strcmp(isa.type, "aha1542")) + continue; + if((sdev = mylexprobe(isa.port, -1)) == nil) + continue; + + if(head != nil) + tail->next = sdev; + else + head = sdev; + tail = sdev; + } + + return head; +} + +static SDev* +mylexid(SDev* sdev) +{ + return scsiid(sdev, &sdmylexifc); +} + +static int +mylex24enable(Ctlr* ctlr) +{ + ulong p; + Ccb24 *ccb, *ccbp; + uchar cmd[6], *v; + int len; + + len = (sizeof(Mbox24)*NMbox*2)+(sizeof(Ccb24)*NCcb); + v = xspanalloc(len, 32, 0); + + if(!PADDR24(ctlr, sizeof(Ctlr)) || !PADDR24(v, len)) + return 0; + + ctlr->mb = v; + v += sizeof(Mbox24)*NMbox*2; + + ccb = (Ccb24*)v; + for(ccbp = ccb; ccbp < &ccb[NCcb]; ccbp++){ + ccbp->ccb = ctlr->ccb; + ctlr->ccb = (Ccb*)ccbp; + } + + /* + * Initialise the software controller and + * set the board scanning the mailboxes. + */ + ctlr->mbix = NMbox; + + cmd[0] = Cinitialise; + cmd[1] = NMbox; + p = K2BPA(ctlr->mb, BUSUNKNOWN); + cmd[2] = p>>16; + cmd[3] = p>>8; + cmd[4] = p; + + return issue(ctlr, cmd, 5, 0, 0); +} + +static int +mylex32enable(Ctlr* ctlr) +{ + ulong p; + Ccb32 *ccb, *ccbp; + uchar cmd[6], *v; + + v = xspanalloc((sizeof(Mbox32)*NMbox*2)+(sizeof(Ccb32)*NCcb), 32, 0); + + ctlr->mb = v; + v += sizeof(Mbox32)*NMbox*2; + + ccb = (Ccb32*)v; + for(ccbp = ccb; ccbp < &ccb[NCcb]; ccbp++){ + /* + * Fill in some stuff that doesn't change. + */ + ccbp->senselen = sizeof(ccbp->sense); + p = PADDR(ccbp->sense); + ccbp->senseptr[0] = p; + ccbp->senseptr[1] = p>>8; + ccbp->senseptr[2] = p>>16; + ccbp->senseptr[3] = p>>24; + + ccbp->ccb = ctlr->ccb; + ctlr->ccb = (Ccb*)ccbp; + } + + /* + * Attempt wide mode setup. + */ + if(ctlr->wide){ + cmd[0] = Cwide; + cmd[1] = 1; + if(!issue(ctlr, cmd, 2, 0, 0)) + ctlr->wide = 0; + } + + /* + * Initialise the software controller and + * set the board scanning the mailboxes. + */ + ctlr->mbix = NMbox; + + cmd[0] = Ciem; + cmd[1] = NMbox; + if(ctlr->pcidev) + p = K2BPA(ctlr->mb, ctlr->tbdf); + else + p = K2BPA(ctlr->mb, BUSUNKNOWN); + cmd[2] = p; + cmd[3] = p>>8; + cmd[4] = p>>16; + cmd[5] = p>>24; + + return issue(ctlr, cmd, 6, 0, 0); +} + +static int +mylexenable(SDev* sdev) +{ + int tbdf; + Ctlr *ctlr; + void (*interrupt)(Ureg*, void*); + char name[NAMELEN]; + + ctlr = sdev->ctlr; + if(ctlr->cache == nil){ + if((ctlr->cache = malloc(sdev->nunit*sizeof(Ccb*))) == nil) + return 0; + } + + tbdf = BUSUNKNOWN; + if(ctlr->bus == 32){ + if(ctlr->pcidev){ + tbdf = ctlr->pcidev->tbdf; + pcisetbme(ctlr->pcidev); + } + if(!mylex32enable(ctlr)) + return 0; + interrupt = mylex32interrupt; + } + else if(mylex24enable(ctlr)) + interrupt = mylex24interrupt; + else + return 0; + + snprint(name, NAMELEN, "sd%c (%s)", sdev->idno, sdev->ifc->name); + intrenable(ctlr->irq, interrupt, ctlr, tbdf, name); + + return 1; +} + +static int +mylexdisable(SDev* sdev) +{ + Ctlr *ctlr; + int port, timeo; + + ctlr = sdev->ctlr; + port = ctlr->port; + + if(getconf("*noscsireset") != nil) + outb(port+Rcontrol, Rhard); + else + outb(port+Rcontrol, Rhard|Rsbus); + for(timeo = 0; timeo < 100; timeo++){ + if(inb(port+Rstatus) == (Inreq|Hardy)) + break; + delay(100); + } + if(inb(port+Rstatus) != (Inreq|Hardy)) + return 0; + + return 1; +} + +SDifc sdmylexifc = { + "mylex", /* name */ + + mylexpnp, /* pnp */ + nil, /* legacy */ + mylexid, /* id */ + mylexenable, /* enable */ + mylexdisable, /* disable */ + + scsiverify, /* verify */ + scsionline, /* online */ + mylexrio, /* rio */ + nil, /* rctl */ + nil, /* wctl */ + + scsibio, /* bio */ +}; diff --git a/os/boot/pc/sdscsi.c b/os/boot/pc/sdscsi.c new file mode 100644 index 00000000..ff8668fe --- /dev/null +++ b/os/boot/pc/sdscsi.c @@ -0,0 +1,376 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "error.h" + +#include "sd.h" + +static int +scsitest(SDreq* r) +{ + r->write = 0; + memset(r->cmd, 0, sizeof(r->cmd)); + r->cmd[1] = r->lun<<5; + r->clen = 6; + r->data = nil; + r->dlen = 0; + r->flags = 0; + + r->status = ~0; + +// cgascreenputs("A", 1); + return r->unit->dev->ifc->rio(r); +} + +int +scsiverify(SDunit* unit) +{ + static SDreq *r; + int i, status; + static uchar *inquiry; + + if((r = sdmalloc(r, sizeof(SDreq))) == nil) + return 0; + + if((inquiry = sdmalloc(inquiry, sizeof(unit->inquiry))) == nil) + return 0; + + r->unit = unit; + r->lun = 0; /* ??? */ + + memset(unit->inquiry, 0, sizeof(unit->inquiry)); + r->write = 0; + r->cmd[0] = 0x12; + r->cmd[1] = r->lun<<5; + r->cmd[4] = sizeof(unit->inquiry)-1; + r->clen = 6; + r->data = inquiry; + r->dlen = sizeof(unit->inquiry)-1; + r->flags = 0; + + r->status = ~0; +// cgascreenputs("B", 1); + if(unit->dev->ifc->rio(r) != SDok){ + return 0; + } + memmove(unit->inquiry, inquiry, r->dlen); + + SET(status); + for(i = 0; i < 3; i++){ + while((status = scsitest(r)) == SDbusy) + ; + if(status == SDok || status != SDcheck) + break; + if(!(r->flags & SDvalidsense)) + break; + if((r->sense[2] & 0x0F) != 0x02) + continue; + + /* + * Unit is 'not ready'. + * If it is in the process of becoming ready or needs + * an initialising command, set status so it will be spun-up + * below. + * If there's no medium, that's OK too, but don't + * try to spin it up. + */ + if(r->sense[12] == 0x04){ + if(r->sense[13] == 0x02 || r->sense[13] == 0x01){ + status = SDok; + break; + } + } + if(r->sense[12] == 0x3A) + break; + } + + if(status == SDok){ + /* + * Try to ensure a direct-access device is spinning. + * Ignore the result. + */ + if((unit->inquiry[0] & 0x1F) == 0){ + memset(r->cmd, 0, sizeof(r->cmd)); + r->write = 0; + r->cmd[0] = 0x1B; + r->cmd[1] = r->lun<<5; + r->cmd[4] = 1; + r->clen = 6; + r->data = nil; + r->dlen = 0; + r->flags = 0; + + r->status = ~0; + unit->dev->ifc->rio(r); + } + return 1; + } + return 0; +} + +int +return0(void*) +{ + return 0; +} + +static int +scsirio(SDreq* r) +{ + /* + * Perform an I/O request, returning + * -1 failure + * 0 ok + * 2 retry + * The contents of r may be altered so the + * caller should re-initialise if necesary. + */ + r->status = ~0; +// cgascreenputs("C", 1); + switch(r->unit->dev->ifc->rio(r)){ + default: + return -1; + case SDcheck: + if(!(r->flags & SDvalidsense)) + return -1; + switch(r->sense[2] & 0x0F){ + case 0x00: /* no sense */ + case 0x01: /* recovered error */ + return 2; + case 0x06: /* check condition */ + /* + * 0x28 - not ready to ready transition, + * medium may have changed. + * 0x29 - power on or some type of reset. + */ + if(r->sense[12] == 0x28 && r->sense[13] == 0) + return 2; + if(r->sense[12] == 0x29) + return 2; + return -1; + case 0x02: /* not ready */ + /* + * If no medium present, bail out. + * If unit is becoming ready, rather than not + * not ready, wait a little then poke it again. */ + if(r->sense[12] == 0x3A) + return -1; + if(r->sense[12] != 0x04 || r->sense[13] != 0x01) + return -1; + + tsleep(nil, return0, 0, 500); + scsitest(r); + return 2; + default: + return -1; + } + return -1; + case SDok: + return 0; + } + return -1; +} + +int +scsionline(SDunit* unit) +{ + int ok; + static SDreq *r; + static uchar *p; + + if((r = sdmalloc(r, sizeof(SDreq))) == nil) + return 0; + + if((p = sdmalloc(p, 8)) == nil) + return 0; + + ok = 0; + + r->unit = unit; + r->lun = 0; /* ??? */ + for(;;){ + /* + * Read-capacity is mandatory for DA, WORM, CD-ROM and + * MO. It may return 'not ready' if type DA is not + * spun up, type MO or type CD-ROM are not loaded or just + * plain slow getting their act together after a reset. + */ + r->write = 0; + memset(r->cmd, 0, sizeof(r->cmd)); + r->cmd[0] = 0x25; + r->cmd[1] = r->lun<<5; + r->clen = 10; + r->data = p; + r->dlen = 8; + r->flags = 0; + + r->status = ~0; +// cgascreenputs("F", 1); + switch(scsirio(r)){ + default: + break; + case 0: + unit->sectors = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]; + /* + * Read-capacity returns the LBA of the last sector, + * therefore the number of sectors must be incremented. + */ + unit->sectors++; + unit->secsize = (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7]; + ok = 1; + break; + case 2: + continue; + } + break; + } + + return ok; +} + +int +scsiexec(SDunit* unit, int write, uchar* cmd, int clen, void* data, int* dlen) +{ + static SDreq *r; + int status; + + if((r = sdmalloc(r, sizeof(SDreq))) == nil) + return SDmalloc; + + r->unit = unit; + r->lun = cmd[1]>>5; /* ??? */ + r->write = write; + memmove(r->cmd, cmd, clen); + r->clen = clen; + r->data = data; + if(dlen) + r->dlen = *dlen; + r->flags = 0; + + r->status = ~0; + + /* + * Call the device-specific I/O routine. + * There should be no calls to 'error()' below this + * which percolate back up. + */ +// cgascreenputs("D", 1); + switch(status = unit->dev->ifc->rio(r)){ + case SDok: + if(dlen) + *dlen = r->rlen; + /*FALLTHROUGH*/ + case SDcheck: + /*FALLTHROUGH*/ + default: + /* + * It's more complicated than this. There are conditions + * which are 'ok' but for which the returned status code + * is not 'SDok'. + * Also, not all conditions require a reqsense, might + * need to do a reqsense here and make it available to the + * caller somehow. + * + * Mañana. + */ + break; + } + + return status; +} + +long +scsibio(SDunit* unit, int lun, int write, void* data, long nb, long bno) +{ + static SDreq *r; + long rlen; + + if((r = sdmalloc(r, sizeof(SDreq))) == nil) + return SDmalloc; + + r->unit = unit; + r->lun = lun; +again: + r->write = write; + if(write == 0) + r->cmd[0] = 0x28; + else + r->cmd[0] = 0x2A; + r->cmd[1] = (lun<<5); + r->cmd[2] = bno>>24; + r->cmd[3] = bno>>16; + r->cmd[4] = bno>>8; + r->cmd[5] = bno; + r->cmd[6] = 0; + r->cmd[7] = nb>>8; + r->cmd[8] = nb; + r->cmd[9] = 0; + r->clen = 10; + r->data = data; + r->dlen = nb*unit->secsize; + r->flags = 0; + + r->status = ~0; +// cgascreenputs("E", 1); + switch(scsirio(r)){ + default: + rlen = -1; + break; + case 0: + rlen = r->rlen; + break; + case 2: + rlen = -1; + if(!(r->flags & SDvalidsense)) + break; + switch(r->sense[2] & 0x0F){ + default: + break; + case 0x06: /* check condition */ + /* + * Check for a removeable media change. + * If so, mark it and zap the geometry info + * to force an online request. + */ + if(r->sense[12] != 0x28 || r->sense[13] != 0) + break; + if(unit->inquiry[1] & 0x80){ + unit->sectors = 0; + } + break; + case 0x02: /* not ready */ + /* + * If unit is becoming ready, + * rather than not not ready, try again. + */ + if(r->sense[12] == 0x04 && r->sense[13] == 0x01) + goto again; + break; + } + break; + } + + return rlen; +} + +SDev* +scsiid(SDev* sdev, SDifc* ifc) +{ + static char idno[16] = "0123456789abcdef"; + static char *p = idno; + + while(sdev){ + if(sdev->ifc == ifc){ + sdev->idno = *p++; + if(p >= &idno[sizeof(idno)]) + break; + } + sdev = sdev->next; + } + + return nil; +} diff --git a/os/boot/pc/trap.c b/os/boot/pc/trap.c new file mode 100644 index 00000000..503e03f4 --- /dev/null +++ b/os/boot/pc/trap.c @@ -0,0 +1,331 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +void intr0(void), intr1(void), intr2(void), intr3(void); +void intr4(void), intr5(void), intr6(void), intr7(void); +void intr8(void), intr9(void), intr10(void), intr11(void); +void intr12(void), intr13(void), intr14(void), intr15(void); +void intr16(void); +void intr24(void), intr25(void), intr26(void), intr27(void); +void intr28(void), intr29(void), intr30(void), intr31(void); +void intr32(void), intr33(void), intr34(void), intr35(void); +void intr36(void), intr37(void), intr38(void), intr39(void); +void intr64(void); +void intrbad(void); + +/* + * 8259 interrupt controllers + */ +enum +{ + Int0ctl= 0x20, /* control port (ICW1, OCW2, OCW3) */ + Int0aux= 0x21, /* everything else (ICW2, ICW3, ICW4, OCW1) */ + Int1ctl= 0xA0, /* control port */ + Int1aux= 0xA1, /* everything else (ICW2, ICW3, ICW4, OCW1) */ + + Icw1= 0x10, /* select bit in ctl register */ + Ocw2= 0x00, + Ocw3= 0x08, + + EOI= 0x20, /* non-specific end of interrupt */ + + Elcr1= 0x4D0, /* Edge/Level Triggered Register */ + Elcr2= 0x4D1, +}; + +int int0mask = 0xff; /* interrupts enabled for first 8259 */ +int int1mask = 0xff; /* interrupts enabled for second 8259 */ +int i8259elcr; /* mask of level-triggered interrupts */ + +/* + * trap/interrupt gates + */ +Segdesc ilt[256]; + +enum +{ + Maxhandler= 32, /* max number of interrupt handlers */ +}; + +typedef struct Handler Handler; +struct Handler +{ + void (*r)(Ureg*, void*); + void *arg; + Handler *next; +}; + +struct +{ + Handler *ivec[256]; + Handler h[Maxhandler]; + int nextfree; +} halloc; + +void +sethvec(int v, void (*r)(void), int type, int pri) +{ + ilt[v].d0 = ((ulong)r)&0xFFFF|(KESEL<<16); + ilt[v].d1 = ((ulong)r)&0xFFFF0000|SEGP|SEGPL(pri)|type; +} + +void +setvec(int v, void (*r)(Ureg*, void*), void *arg) +{ + Handler *h; + + if(halloc.nextfree >= Maxhandler) + panic("out of interrupt handlers"); + h = &halloc.h[halloc.nextfree++]; + h->next = halloc.ivec[v]; + h->r = r; + h->arg = arg; + halloc.ivec[v] = h; + + /* + * enable corresponding interrupt in 8259 + */ + if((v&~0x7) == VectorPIC){ + int0mask &= ~(1<<(v&7)); + outb(Int0aux, int0mask); + } else if((v&~0x7) == VectorPIC+8){ + int1mask &= ~(1<<(v&7)); + outb(Int1aux, int1mask); + } +} + +void +trapdisable(void) +{ + outb(Int0aux, 0xFF); + outb(Int1aux, 0xFF); +} + +void +trapenable(void) +{ + outb(Int0aux, int0mask); + outb(Int1aux, int1mask); +} + + +/* + * set up the interrupt/trap gates + */ +void +trapinit(void) +{ + int i, x; + + /* + * set all interrupts to panics + */ + for(i = 0; i < 256; i++) + sethvec(i, intrbad, SEGTG, 0); + + /* + * 80386 processor (and coprocessor) traps + */ + sethvec(0, intr0, SEGTG, 0); + sethvec(1, intr1, SEGTG, 0); + sethvec(2, intr2, SEGTG, 0); + sethvec(3, intr3, SEGTG, 0); + sethvec(4, intr4, SEGTG, 0); + sethvec(5, intr5, SEGTG, 0); + sethvec(6, intr6, SEGTG, 0); + sethvec(7, intr7, SEGTG, 0); + sethvec(8, intr8, SEGTG, 0); + sethvec(9, intr9, SEGTG, 0); + sethvec(10, intr10, SEGTG, 0); + sethvec(11, intr11, SEGTG, 0); + sethvec(12, intr12, SEGTG, 0); + sethvec(13, intr13, SEGTG, 0); + sethvec(14, intr14, SEGTG, 0); + sethvec(15, intr15, SEGTG, 0); + sethvec(16, intr16, SEGTG, 0); + + /* + * device interrupts + */ + sethvec(24, intr24, SEGIG, 0); + sethvec(25, intr25, SEGIG, 0); + sethvec(26, intr26, SEGIG, 0); + sethvec(27, intr27, SEGIG, 0); + sethvec(28, intr28, SEGIG, 0); + sethvec(29, intr29, SEGIG, 0); + sethvec(30, intr30, SEGIG, 0); + sethvec(31, intr31, SEGIG, 0); + sethvec(32, intr32, SEGIG, 0); + sethvec(33, intr33, SEGIG, 0); + sethvec(34, intr34, SEGIG, 0); + sethvec(35, intr35, SEGIG, 0); + sethvec(36, intr36, SEGIG, 0); + sethvec(37, intr37, SEGIG, 0); + sethvec(38, intr38, SEGIG, 0); + sethvec(39, intr39, SEGIG, 0); + + /* + * tell the hardware where the table is (and how long) + */ + putidt(ilt, sizeof(ilt)-1); + + /* + * Set up the first 8259 interrupt processor. + * Make 8259 interrupts start at CPU vector VectorPIC. + * Set the 8259 as master with edge triggered + * input with fully nested interrupts. + */ + outb(Int0ctl, Icw1|0x01); /* ICW1 - edge triggered, master, + ICW4 will be sent */ + outb(Int0aux, VectorPIC); /* ICW2 - interrupt vector offset */ + outb(Int0aux, 0x04); /* ICW3 - have slave on level 2 */ + outb(Int0aux, 0x01); /* ICW4 - 8086 mode, not buffered */ + + /* + * Set up the second 8259 interrupt processor. + * Make 8259 interrupts start at CPU vector VectorPIC+8. + * Set the 8259 as master with edge triggered + * input with fully nested interrupts. + */ + outb(Int1ctl, Icw1|0x01); /* ICW1 - edge triggered, master, + ICW4 will be sent */ + outb(Int1aux, VectorPIC+8); /* ICW2 - interrupt vector offset */ + outb(Int1aux, 0x02); /* ICW3 - I am a slave on level 2 */ + outb(Int1aux, 0x01); /* ICW4 - 8086 mode, not buffered */ + outb(Int1aux, int1mask); + + /* + * pass #2 8259 interrupts to #1 + */ + int0mask &= ~0x04; + outb(Int0aux, int0mask); + + /* + * Set Ocw3 to return the ISR when ctl read. + */ + outb(Int0ctl, Ocw3|0x03); + outb(Int1ctl, Ocw3|0x03); + + /* + * Check for Edge/Level register. + * This check may not work for all chipsets. + * First try a non-intrusive test - the bits for + * IRQs 13, 8, 2, 1 and 0 must be edge (0). If + * that's OK try a R/W test. + */ + x = (inb(Elcr2)<<8)|inb(Elcr1); + if(!(x & 0x2107)){ + outb(Elcr1, 0); + if(inb(Elcr1) == 0){ + outb(Elcr1, 0x20); + if(inb(Elcr1) == 0x20) + i8259elcr = x; + outb(Elcr1, x & 0xFF); + print("ELCR: %4.4uX\n", i8259elcr); + } + } +} + +/* + * dump registers + */ +static void +dumpregs(Ureg *ur) +{ + print("FLAGS=%lux TRAP=%lux ECODE=%lux PC=%lux\n", + ur->flags, ur->trap, ur->ecode, ur->pc); + print(" AX %8.8lux BX %8.8lux CX %8.8lux DX %8.8lux\n", + ur->ax, ur->bx, ur->cx, ur->dx); + print(" SI %8.8lux DI %8.8lux BP %8.8lux\n", + ur->si, ur->di, ur->bp); + print(" CS %4.4lux DS %4.4lux ES %4.4lux FS %4.4lux GS %4.4lux\n", + ur->cs & 0xFF, ur->ds & 0xFFFF, ur->es & 0xFFFF, ur->fs & 0xFFFF, ur->gs & 0xFFFF); + print(" CR0 %8.8lux CR2 %8.8lux CR3 %8.8lux\n", + getcr0(), getcr2(), getcr3()); +} + +/* + * All traps + */ +void +trap(Ureg *ur) +{ + int v; + int c; + Handler *h; + ushort isr; + + v = ur->trap; + /* + * tell the 8259 that we're done with the + * highest level interrupt (interrupts are still + * off at this point) + */ + c = v&~0x7; + isr = 0; + if(c==VectorPIC || c==VectorPIC+8){ + isr = inb(Int0ctl); + outb(Int0ctl, EOI); + if(c == VectorPIC+8){ + isr |= inb(Int1ctl)<<8; + outb(Int1ctl, EOI); + } + } + + if(v>=256 || (h = halloc.ivec[v]) == 0){ + if(v >= VectorPIC && v < VectorPIC+16){ + v -= VectorPIC; + /* + * Check for a default IRQ7. This can happen when + * the IRQ input goes away before the acknowledge. + * In this case, a 'default IRQ7' is generated, but + * the corresponding bit in the ISR isn't set. + * In fact, just ignore all such interrupts. + */ + if(isr & (1<pc); + return; + } + + switch(v){ + + case 0x02: /* NMI */ + print("NMI: nmisc=0x%2.2ux, nmiertc=0x%2.2ux, nmiesc=0x%2.2ux\n", + inb(0x61), inb(0x70), inb(0x461)); + return; + + default: + dumpregs(ur); + panic("exception/interrupt %d", v); + return; + } + } + + /* + * call the trap routines + */ + do { + (*h->r)(ur, h->arg); + h = h->next; + } while(h); +} + +void +realmode(int intr, Ureg *ureg) +{ + extern void realmode0(void); /* in l.s */ + extern int realmodeintr; + extern Ureg realmoderegs; + + realmoderegs = *ureg; + realmodeintr = intr; + trapdisable(); + realmode0(); + trapenable(); + *ureg = realmoderegs; +} diff --git a/os/boot/pc/ureg.h b/os/boot/pc/ureg.h new file mode 100644 index 00000000..0d0d43f0 --- /dev/null +++ b/os/boot/pc/ureg.h @@ -0,0 +1,27 @@ +typedef struct Ureg Ureg; + +struct Ureg +{ + ulong di; /* general registers */ + ulong si; /* ... */ + ulong bp; /* ... */ + ulong nsp; + ulong bx; /* ... */ + ulong dx; /* ... */ + ulong cx; /* ... */ + ulong ax; /* ... */ + ulong gs; /* data segments */ + ulong fs; /* ... */ + ulong es; /* ... */ + ulong ds; /* ... */ + ulong trap; /* trap type */ + ulong ecode; /* error code (or zero) */ + ulong pc; /* pc */ + ulong cs; /* old context */ + ulong flags; /* old flags */ + union { + ulong usp; + ulong sp; + }; + ulong ss; /* old stack segment */ +}; diff --git a/os/boot/pc/x16.h b/os/boot/pc/x16.h new file mode 100644 index 00000000..9c204328 --- /dev/null +++ b/os/boot/pc/x16.h @@ -0,0 +1,159 @@ +/* + * Can't write 16-bit code for 8a without getting into + * lots of bother, so define some simple commands and + * output the code directly. + * + * N.B. CALL16(x) kills DI, so don't expect it to be + * saved across calls. + */ +#define rAX 0 /* rX */ +#define rCX 1 +#define rDX 2 +#define rBX 3 +#define rSP 4 /* SP */ +#define rBP 5 /* BP */ +#define rSI 6 /* SI */ +#define rDI 7 /* DI */ + +#define rAL 0 /* rL */ +#define rCL 1 +#define rDL 2 +#define rBL 3 +#define rAH 4 /* rH */ +#define rCH 5 +#define rDH 6 +#define rBH 7 + +#define rES 0 /* rS */ +#define rCS 1 +#define rSS 2 +#define rDS 3 +#define rFS 4 +#define rGS 5 + +#define xSI 4 /* rI (index) */ +#define xDI 5 +#define xBP 6 +#define xBX 7 + +#define rCR0 0 /* rC */ +#define rCR2 2 +#define rCR3 3 +#define rCR4 4 + +#define OP(o, m, ro, rm) BYTE $o; /* op + modr/m byte */ \ + BYTE $(((m)<<6)|((ro)<<3)|(rm)) +#define OPrm(o, r, m) OP(o, 0x00, r, 0x06); /* general r <-> m */ \ + WORD $m; +#define OPrr(o, r0, r1) OP(o, 0x03, r0, r1); /* general r -> r */ + +#define LW(m, rX) OPrm(0x8B, rX, m) /* m -> rX */ +#define LXW(x, rI, r) OP(0x8B, 0x02, r, rI); /* x(rI) -> r */ \ + WORD $x +#define LBPW(x, r) OP(0x8B, 0x02, r, xBP); /* x(rBP) -> r */ \ + WORD $x +#define LB(m, rB) OPrm(0x8A, rB, m) /* m -> r[HL] */ +#define LXB(x, rI, r) OP(0x8A, 0x01, r, rI); /* x(rI) -> r */ \ + BYTE $x +#define LBPB(x, r) OP(0x8A, 0x01, r, xBP); /* x(rBP) -> r */ \ + BYTE $x +#define SW(rX, m) OPrm(0x89, rX, m) /* rX -> m */ +#define SXW(r, x, rI) OP(0x89, 0x02, r, rI); /* r -> x(rI) */ \ + WORD $x +#define SBPW(r, x) OP(0x89, 0x02, r, xBP); /* r -> x(rBP) */ \ + WORD $(x) +#define SBPWI(i, x) OP(0xC7, 0x01, 0, xBP); /* i -> x(rBP) */ \ + BYTE $(x); WORD $(i) +#define STB(rB, m) OPrm(0x88, rB, m) /* rB -> m */ +#define SXB(r, x, rI) OP(0x88, 0x01, r, rI); /* rB -> x(rI) */ \ + BYTE $x +#define SBPB(r, x) OP(0x88, 0x01, r, xBP); /* r -> x(rBP) */ \ + BYTE $x +#define SBPBI(i, x) OP(0xC6, 0x01, 0, xBP); /* i -> x(rBP) */ \ + BYTE $(x); BYTE $(i) +#define LWI(i, rX) BYTE $(0xB8+rX); /* i -> rX */ \ + WORD $i; +#define LBI(i, rB) BYTE $(0xB0+rB); /* i -> r[HL] */ \ + BYTE $i + +#define MW(r0, r1) OPrr(0x89, r0, r1) /* r0 -> r1 */ +#define MFSR(rS, rX) OPrr(0x8C, rS, rX) /* rS -> rX */ +#define MTSR(rX, rS) OPrr(0x8E, rS, rX) /* rX -> rS */ +#define MFCR(rC, rX) BYTE $0x0F; /* rC -> rX */ \ + OP(0x20, 0x03, rC, rX) +#define MTCR(rX, rC) BYTE $0x0F; /* rX -> rC */ \ + OP(0x22, 0x03, rC, rX) + +#define ADC(r0, r1) OPrr(0x11, r0, r1) /* r0 + r1 -> r1 */ +#define ADD(r0, r1) OPrr(0x01, r0, r1) /* r0 + r1 -> r1 */ +#define ADDI(i, r) OP(0x81, 0x03, 0x00, r);/* i+r -> r */ \ + WORD $i; +#define AND(r0, r1) OPrr(0x21, r0, r1) /* r0&r1 -> r1 */ +#define ANDI(i, r) OP(0x81, 0x03, 0x04, r);/* i&r -> r */ \ + WORD $i; +#define CLR(r) OPrr(0x31, r, r) /* r^r -> r */ +#define CLRB(r) OPrr(0x30, r, r) /* r^r -> r */ +#define CMP(r0, r1) OPrr(0x39, r0, r1) /* r1-r0 -> flags */ +#define CMPI(i, r) OP(0x81, 0x03, 0x07, r);/* r-i -> flags */ \ + WORD $i; +#define CMPBR(r0, r1) OPrr(0x38, r0, r1) /* r1-r0 -> flags */ +#define DEC(r) BYTE $(0x48|r) /* r-1 -> r */ +#define DIV(r) OPrr(0xF7, 0x06, r) /* rDX:rAX/r -> rAX, rDX:rAX%r -> rDX */ +#define INC(r) BYTE $(0x40|r) /* r+1 -> r */ +#define MUL(r) OPrr(0xF7, 0x04, r) /* r*rAX -> rDX:rAX */ +#define IMUL(r0, r1) BYTE $0x0F; /* r0*r1 -> r1 */ \ + OPrr(0xAF, r1, r0) /* (signed) */ +#define OR(r0, r1) OPrr(0x09, r0, r1) /* r0|r1 -> r1 */ +#define ORB(r0, r1) OPrr(0x08, r0, r1) /* r0|r1 -> r1 */ +#define ORI(i, r) OP(0x81, 0x03, 0x01, r);/* i|r -> r */ \ + WORD $i; +#define ROLI(i, r) OPrr(0xC1, 0x00, r); /* r<<>>i -> r */ \ + BYTE $i; +#define SHLI(i, r) OPrr(0xC1, 0x04, r); /* r< r */ \ + BYTE $i; +#define SHLBI(i, r) OPrr(0xC0, 0x04, r); /* r< r */ \ + BYTE $i; +#define SHRI(i, r) OPrr(0xC1, 0x05, r); /* r>>i -> r */ \ + BYTE $i; +#define SHRBI(i, r) OPrr(0xC0, 0x05, r); /* r>>i -> r */ \ + BYTE $i; +#define SUB(r0, r1) OPrr(0x29, r0, r1) /* r1-r0 -> r1 */ +#define SUBI(i, r) OP(0x81, 0x03, 0x05, r);/* r-i -> r */ \ + WORD $i; + +#define STOSW STOSL + +#define CALL16(f) LWI(f, rDI); /* &f -> rDI */ \ + BYTE $0xFF; /* (*rDI) */ \ + BYTE $0xD7; +#define FARJUMP16(s, o) BYTE $0xEA; /* jump to ptr16:16 */ \ + WORD $o; WORD $s +#define FARJUMP32(s, o) BYTE $0x66; /* jump to ptr32:16 */ \ + BYTE $0xEA; LONG $o; WORD $s +#define DELAY BYTE $0xEB; /* jmp .+2 */ \ + BYTE $0x00 +#define BIOSCALL(b) INT $b /* INT $b */ + +#define PEEKW BYTE $0x26; /* MOVW rES:[rBX], rAX */ \ + BYTE $0x8B; BYTE $0x07 +#define POKEW BYTE $0x26; /* MOVW rAX, rES:[rBX] */ \ + BYTE $0x89; BYTE $0x07 +#define OUTPORTB(p, d) LBI(d, rAL); /* d -> I/O port p */ \ + BYTE $0xE6; \ + BYTE $p; DELAY +#define PUSHA BYTE $0x60 +#define PUSHR(r) BYTE $(0x50|r) /* r -> (--rSP) */ +#define PUSHS(rS) BYTE $(0x06|((rS)<<3)) /* rS -> (--rSP) */ +#define PUSHI(i) BYTE $0x68; WORD $i; /* i -> --(rSP) */ +#define POPA BYTE $0x61 +#define POPR(r) BYTE $(0x58|r) /* (rSP++) -> r */ +#define POPS(rS) BYTE $$(0x07|((rS)<<3)) /* (rSP++) -> r */ +#define NOP BYTE $0x90 /* nop */ + +#define LGDT(gdtptr) BYTE $0x0F; /* LGDT */ \ + BYTE $0x01; BYTE $0x16; \ + WORD $gdtptr + +/* operand size switch. */ +#define OPSIZE BYTE $0x66 + diff --git a/os/boot/puma/8250.c b/os/boot/puma/8250.c new file mode 100644 index 00000000..b906b2ba --- /dev/null +++ b/os/boot/puma/8250.c @@ -0,0 +1,312 @@ +#include "boot.h" + +/* + * INS8250 uart + */ +enum +{ + /* + * register numbers + */ + Data= 0, /* xmit/rcv buffer */ + Iena= 1, /* interrupt enable */ + Ircv= (1<<0), /* for char rcv'd */ + Ixmt= (1<<1), /* for xmit buffer empty */ + Irstat=(1<<2), /* for change in rcv'er status */ + Imstat=(1<<3), /* for change in modem status */ + Istat= 2, /* interrupt flag (read) */ + Tctl= 2, /* test control (write) */ + Format= 3, /* byte format */ + Bits8= (3<<0), /* 8 bits/byte */ + Stop2= (1<<2), /* 2 stop bits */ + Pena= (1<<3), /* generate parity */ + Peven= (1<<4), /* even parity */ + Pforce=(1<<5), /* force parity */ + Break= (1<<6), /* generate a break */ + Dra= (1<<7), /* address the divisor */ + Mctl= 4, /* modem control */ + Dtr= (1<<0), /* data terminal ready */ + Rts= (1<<1), /* request to send */ + Ri= (1<<2), /* ring */ + Inton= (1<<3), /* turn on interrupts */ + Loop= (1<<4), /* loop back */ + Lstat= 5, /* line status */ + Inready=(1<<0), /* receive buffer full */ + Oerror=(1<<1), /* receiver overrun */ + Perror=(1<<2), /* receiver parity error */ + Ferror=(1<<3), /* rcv framing error */ + Outready=(1<<5), /* output buffer empty */ + Mstat= 6, /* modem status */ + Ctsc= (1<<0), /* clear to send changed */ + Dsrc= (1<<1), /* data set ready changed */ + Rire= (1<<2), /* rising edge of ring indicator */ + Dcdc= (1<<3), /* data carrier detect changed */ + Cts= (1<<4), /* complement of clear to send line */ + Dsr= (1<<5), /* complement of data set ready line */ + Ring= (1<<6), /* complement of ring indicator line */ + Dcd= (1<<7), /* complement of data carrier detect line */ + Scratch=7, /* scratchpad */ + Dlsb= 0, /* divisor lsb */ + Dmsb= 1, /* divisor msb */ + + Serial= 0, + Modem= 1, +}; + +typedef struct Uart Uart; +struct Uart +{ + int port; + int setup; + uchar sticky[8]; /* sticky write register values */ + uchar txbusy; + + Queue *iq; + Queue *oq; + void (*rx)(Queue *, int); + + ulong frame; + ulong overrun; +}; + +Uart uart[1]; + +static void uartkick(void*); + + +#define UartFREQ 1843200 + +#define uartwrreg(u,r,v) outb((u)->port + r, (u)->sticky[r] | (v)) +#define uartrdreg(u,r) inb((u)->port + r) + +/* + * set the baud rate by calculating and setting the baudrate + * generator constant. This will work with fairly non-standard + * baud rates. + */ +static void +uartsetbaud(Uart *up, int rate) +{ + ulong brconst; + + brconst = (UartFREQ+8*rate-1)/(16*rate); + + uartwrreg(up, Format, Dra); + outb(up->port+Dmsb, (brconst>>8) & 0xff); + outb(up->port+Dlsb, brconst & 0xff); + uartwrreg(up, Format, 0); +} + +/* + * toggle DTR + */ +static void +uartdtr(Uart *up, int n) +{ + if(n) + up->sticky[Mctl] |= Dtr; + else + up->sticky[Mctl] &= ~Dtr; + uartwrreg(up, Mctl, 0); +} + +/* + * toggle RTS + */ +static void +uartrts(Uart *up, int n) +{ + if(n) + up->sticky[Mctl] |= Rts; + else + up->sticky[Mctl] &= ~Rts; + uartwrreg(up, Mctl, 0); +} + +static void +uartintr(Ureg*, void *arg) +{ + Uart *up; + int ch; + int s, l, loops; + + up = arg; + for(loops = 0; loops < 1024; loops++){ + s = uartrdreg(up, Istat); + switch(s){ + case 6: /* receiver line status */ + l = uartrdreg(up, Lstat); + if(l & Ferror) + up->frame++; + if(l & Oerror) + up->overrun++; + break; + + case 4: /* received data available */ + case 12: + ch = inb(up->port+Data); + if(up->iq) + if(up->rx) + (*up->rx)(up->iq, ch); + else + qbputc(up->iq, ch); + break; + + case 2: /* transmitter empty */ + ch = -1; + if(up->oq) + ch = qbgetc(up->oq); + if(ch != -1) + outb(up->port+Data, ch); + else + up->txbusy = 0; + break; + + case 0: /* modem status */ + uartrdreg(up, Mstat); + break; + + default: + if(s&1) + return; + print("weird modem interrupt #%2.2ux\n", s); + break; + } + } + panic("uartintr: 0x%2.2ux\n", uartrdreg(up, Istat)); +} + +/* + * turn on a port's interrupts. set DTR and RTS + */ +static void +uartenable(Uart *up) +{ + /* + * turn on interrupts + */ + up->sticky[Iena] = 0; + if(up->oq) + up->sticky[Iena] |= Ixmt; + if(up->iq) + up->sticky[Iena] |= Ircv|Irstat; + uartwrreg(up, Iena, 0); + + /* + * turn on DTR and RTS + */ + uartdtr(up, 1); + uartrts(up, 1); +} + +void +uartspecial(int port, int baud, Queue **iq, Queue **oq, void (*rx)(Queue *, int)) +{ + Uart *up = &uart[0]; + + if(up->setup) + return; + up->setup = 1; + + *iq = up->iq = qopen(4*1024, 0, 0, 0); + *oq = up->oq = qopen(16*1024, 0, uartkick, up); + switch(port){ + + case 0: + up->port = 0x3F8; + setvec(V_COM1, uartintr, up); + break; + + case 1: + up->port = 0x2F8; + setvec(V_COM2, uartintr, up); + break; + + default: + return; + } + + /* + * set rate to 9600 baud. + * 8 bits/character. + * 1 stop bit. + * interrupts enabled. + */ + uartsetbaud(up, 9600); + up->sticky[Format] = Bits8; + uartwrreg(up, Format, 0); + up->sticky[Mctl] |= Inton; + uartwrreg(up, Mctl, 0x0); + + up->rx = rx; + uartenable(up); + if(baud) + uartsetbaud(up, baud); +} + +static void +uartputc(int c) +{ + Uart *up = &uart[0]; + int i; + + for(i = 0; i < 100; i++){ + if(uartrdreg(up, Lstat) & Outready) + break; + delay(1); + } + outb(up->port+Data, c); +} + +void +uartputs(char *s, int n) +{ + Uart *up = &uart[0]; + Block *b; + int nl; + char *p; + + nl = 0; + for(p = s; p < s+n; p++) + if(*p == '\n') + nl++; + b = iallocb(n+nl); + while(n--){ + if(*s == '\n') + *b->wp++ = '\r'; + *b->wp++ = *s++; + } + qbwrite(up->oq, b); +} + +/* + * (re)start output + */ +static void +uartkick(void *arg) +{ + Uart *up = arg; + int x, n, c; + + x = splhi(); + while(up->txbusy == 0 && (c = qbgetc(up->oq)) != -1) { + n = 0; + while((uartrdreg(up, Lstat) & Outready) == 0){ + if(++n > 100000){ + print("stuck serial line\n"); + break; + } + } + outb(up->port + Data, c); + } + splx(x); +} + +void +uartwait(void) +{ + Uart *up = &uart[0]; + + while(up->txbusy) + ; +} diff --git a/os/boot/puma/alarm.c b/os/boot/puma/alarm.c new file mode 100644 index 00000000..14cc93d3 --- /dev/null +++ b/os/boot/puma/alarm.c @@ -0,0 +1,123 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#define MAXALARM 10 + +Alarm alarmtab[MAXALARM]; + +/* + * Insert new into list after where + */ +void +insert(List **head, List *where, List *new) +{ + if(where == 0){ + new->next = *head; + *head = new; + }else{ + new->next = where->next; + where->next = new; + } + +} + +/* + * Delete old from list. where->next is known to be old. + */ +void +delete(List **head, List *where, List *old) +{ + if(where == 0){ + *head = old->next; + return; + } + where->next = old->next; +} + +Alarm* +newalarm(void) +{ + int i; + Alarm *a; + + for(i=0,a=alarmtab; i < nelem(alarmtab); i++,a++) + if(a->busy==0 && a->f==0){ + a->f = 0; + a->arg = 0; + a->busy = 1; + return a; + } + panic("newalarm"); + return 0; /* not reached */ +} + +Alarm* +alarm(int ms, void (*f)(Alarm*), void *arg) +{ + Alarm *a, *w, *pw; + ulong s; + + if(ms < 0) + ms = 0; + s = splhi(); + a = newalarm(); + a->dt = MS2TK(ms); + a->f = f; + a->arg = arg; + pw = 0; + for(w=m->alarm; w; pw=w, w=w->next){ + if(w->dt <= a->dt){ + a->dt -= w->dt; + continue; + } + w->dt -= a->dt; + break; + } + insert(&m->alarm, pw, a); + splx(s); + return a; +} + +void +cancel(Alarm *a) +{ + a->f = 0; +} + +void +alarminit(void) +{ +} + +#define NA 10 /* alarms per clock tick */ +void +checkalarms(void) +{ + int i, n, s; + Alarm *a; + void (*f)(void*); + Alarm *alist[NA]; + + s = splhi(); + a = m->alarm; + if(a){ + for(n=0; a && a->dt<=0 && nalarm, 0, a); + a = m->alarm; + } + if(a) + a->dt--; + + for(i = 0; i < n; i++){ + f = alist[i]->f; /* avoid race with cancel */ + if(f) + (*f)(alist[i]); + alist[i]->busy = 0; + } + } + splx(s); +} diff --git a/os/boot/puma/armv4.h b/os/boot/puma/armv4.h new file mode 100644 index 00000000..f9386e0f --- /dev/null +++ b/os/boot/puma/armv4.h @@ -0,0 +1,99 @@ +/* + * PSR + */ +#define PsrMusr 0x10 /* mode */ +#define PsrMfiq 0x11 +#define PsrMirq 0x12 +#define PsrMsvc 0x13 +#define PsrMabt 0x17 +#define PsrMund 0x1B +#define PsrMsys 0x1F +#define PsrMask 0x1F + +#define PsrDfiq 0x00000040 /* disable FIQ interrupts */ +#define PsrDirq 0x00000080 /* disable IRQ interrupts */ + +#define PsrV 0x10000000 /* overflow */ +#define PsrC 0x20000000 /* carry/borrow/extend */ +#define PsrZ 0x40000000 /* zero */ +#define PsrN 0x80000000 /* negative/less than */ + +/* + * Internal MMU coprocessor registers + */ +#define CpCPUID 0 /* R: */ +#define CpControl 1 /* R: */ +#define CpTTB 2 /* W: translation table base */ +#define CpDAC 3 /* W: domain access control */ +#define CpFSR 5 /* R: fault status */ +#define CpTLBflush 5 /* W: */ +#define CpFAR 6 /* R: fault address */ +#define CpTLBpurge 6 /* W: */ +#define CpCacheCtl 7 /* W: */ + +#define CpDebug 14 /* R/W: debug registers */ +/* + * Coprocessors + */ +#define CpMMU 15 + +/* + * Internal MMU coprocessor registers + */ +#define CpCmmu 0x00000001 /* M: MMU enable */ +#define CpCalign 0x00000002 /* A: alignment fault enable */ +#define CpCDcache 0x00000004 /* C: instruction/data cache on */ +#define CpCwb 0x00000008 /* W: write buffer turned on */ +#define CpCi32 0x00000010 /* P: 32-bit programme space */ +#define CpCd32 0x00000020 /* D: 32-bit data space */ +#define CpCbe 0x00000080 /* B: big-endian operation */ +#define CpCsystem 0x00000100 /* S: system permission */ +#define CpCrom 0x00000200 /* R: ROM permission */ +#define CpCIcache 0x00001000 /* C: Instruction Cache on */ + +/* + * Debug support internal registers + */ +#define CpDBAR 0 +#define CpDBVR 1 +#define CpDBMR 2 +#define CpDBCR 3 +#define CpIBCR 8 +/* + * MMU + */ +/* + * Small pages: + * L1: 12-bit index -> 4096 descriptors -> 16Kb + * L2: 8-bit index -> 256 descriptors -> 1Kb + * Each L2 descriptor has access permissions for 4 1Kb sub-pages. + * + * TTB + L1Tx gives address of L1 descriptor + * L1 descriptor gives PTBA + * PTBA + L2Tx gives address of L2 descriptor + * L2 descriptor gives PBA + */ +#define MmuTTB(pa) ((pa) & ~0x3FFF) /* translation table base */ +#define MmuL1x(pa) (((pa)>>20) & 0xFFF) /* L1 table index */ +#define MmuPTBA(pa) ((pa) & ~0x3FF) /* page table base address */ +#define MmuL2x(pa) (((pa)>>12) & 0xFF) /* L2 table index */ +#define MmuPBA(pa) ((pa) & ~0xFFF) /* page base address */ +#define MmuSBA(pa) ((pa) & ~0xFFFFF) /* section base address */ + +#define MmuL1page 0x011 /* descriptor is for L2 pages */ +#define MmuL1section 0x012 /* descriptor is for section */ + +#define MmuL2invalid 0x000 +#define MmuL2large 0x001 /* large */ +#define MmuL2small 0x002 /* small */ +#define MmuWB 0x004 /* data goes through write buffer */ +#define MmuIDC 0x008 /* data placed in cache */ + +#define MmuDAC(d) (((d) & 0xF)<<5) /* L1 domain */ +#define MmuAP(i, v) ((v)<<(((i)*2)+4)) /* access permissions */ +#define MmuL1AP(v) MmuAP(3, (v)) +#define MmuL2AP(v) MmuAP(3, (v))|MmuAP(2, (v))|MmuAP(1, (v))|MmuAP(0, (v)) +#define MmuAPsro 0 /* supervisor rw */ +#define MmuAPsrw 1 /* supervisor rw */ +#define MmuAPuro 2 /* supervisor rw + user ro */ +#define MmuAPurw 3 /* supervisor rw + user rw */ diff --git a/os/boot/puma/boot.h b/os/boot/puma/boot.h new file mode 100644 index 00000000..afa9ab30 --- /dev/null +++ b/os/boot/puma/boot.h @@ -0,0 +1,12 @@ +#include +#include "lib.h" + +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +#include "armv4.h" +#include "puma.h" + diff --git a/os/boot/puma/bootp.c b/os/boot/puma/bootp.c new file mode 100644 index 00000000..95a1f607 --- /dev/null +++ b/os/boot/puma/bootp.c @@ -0,0 +1,502 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "ip.h" + +uchar broadcast[Eaddrlen] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +}; + +static ushort tftpport = 5000; +static int Id = 1; +static Netaddr myaddr; +static Netaddr server; + +typedef struct { + uchar header[4]; + uchar data[Segsize]; +} Tftp; +static Tftp tftpb; + +static void +hnputs(uchar *ptr, ushort val) +{ + ptr[0] = val>>8; + ptr[1] = val; +} + +static void +hnputl(uchar *ptr, ulong val) +{ + ptr[0] = val>>24; + ptr[1] = val>>16; + ptr[2] = val>>8; + ptr[3] = val; +} + +static ulong +nhgetl(uchar *ptr) +{ + return ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]); +} + +static ushort +nhgets(uchar *ptr) +{ + return ((ptr[0]<<8) | ptr[1]); +} + +static short endian = 1; +static char* aendian = (char*)&endian; +#define LITTLE *aendian + +static ushort +ptcl_csum(void *a, int len) +{ + uchar *addr; + ulong t1, t2; + ulong losum, hisum, mdsum, x; + + addr = a; + losum = 0; + hisum = 0; + mdsum = 0; + + x = 0; + if((ulong)addr & 1) { + if(len) { + hisum += addr[0]; + len--; + addr++; + } + x = 1; + } + while(len >= 16) { + t1 = *(ushort*)(addr+0); + t2 = *(ushort*)(addr+2); mdsum += t1; + t1 = *(ushort*)(addr+4); mdsum += t2; + t2 = *(ushort*)(addr+6); mdsum += t1; + t1 = *(ushort*)(addr+8); mdsum += t2; + t2 = *(ushort*)(addr+10); mdsum += t1; + t1 = *(ushort*)(addr+12); mdsum += t2; + t2 = *(ushort*)(addr+14); mdsum += t1; + mdsum += t2; + len -= 16; + addr += 16; + } + while(len >= 2) { + mdsum += *(ushort*)addr; + len -= 2; + addr += 2; + } + if(x) { + if(len) + losum += addr[0]; + if(LITTLE) + losum += mdsum; + else + hisum += mdsum; + } else { + if(len) + hisum += addr[0]; + if(LITTLE) + hisum += mdsum; + else + losum += mdsum; + } + + losum += hisum >> 8; + losum += (hisum & 0xff) << 8; + while(hisum = losum>>16) + losum = hisum + (losum & 0xffff); + + return ~losum; +} + +static ushort +ip_csum(uchar *addr) +{ + int len; + ulong sum = 0; + + len = (addr[0]&0xf)<<2; + + while(len > 0) { + sum += addr[0]<<8 | addr[1] ; + len -= 2; + addr += 2; + } + + sum = (sum & 0xffff) + (sum >> 16); + sum = (sum & 0xffff) + (sum >> 16); + return (sum^0xffff); +} + +static void +udpsend(int ctlrno, Netaddr *a, void *data, int dlen) +{ + Udphdr *uh; + Etherhdr *ip; + Etherpkt pkt; + int len, ptcllen; + + + uh = (Udphdr*)&pkt; + + memset(uh, 0, sizeof(Etherpkt)); + memmove(uh->udpcksum+sizeof(uh->udpcksum), data, dlen); + + /* + * UDP portion + */ + ptcllen = dlen + (UDP_HDRSIZE-UDP_PHDRSIZE); + uh->ttl = 0; + uh->udpproto = IP_UDPPROTO; + uh->frag[0] = 0; + uh->frag[1] = 0; + hnputs(uh->udpplen, ptcllen); + hnputl(uh->udpsrc, myaddr.ip); + hnputs(uh->udpsport, myaddr.port); + hnputl(uh->udpdst, a->ip); + hnputs(uh->udpdport, a->port); + hnputs(uh->udplen, ptcllen); + uh->udpcksum[0] = 0; + uh->udpcksum[1] = 0; + dlen = (dlen+1)&~1; + hnputs(uh->udpcksum, ptcl_csum(&uh->ttl, dlen+UDP_HDRSIZE)); + + /* + * IP portion + */ + ip = (Etherhdr*)&pkt; + len = sizeof(Udphdr)+dlen; + ip->vihl = IP_VER|IP_HLEN; + ip->tos = 0; + ip->ttl = 255; + hnputs(ip->length, len-ETHER_HDR); + hnputs(ip->id, Id++); + ip->frag[0] = 0; + ip->frag[1] = 0; + ip->cksum[0] = 0; + ip->cksum[1] = 0; + hnputs(ip->cksum, ip_csum(&ip->vihl)); + + /* + * Ethernet MAC portion + */ + hnputs(ip->type, ET_IP); + memmove(ip->d, a->ea, sizeof(ip->d)); + + ethertxpkt(ctlrno, &pkt, len, Timeout); +} + +static void +nak(int ctlrno, Netaddr *a, int code, char *msg, int report) +{ + int n; + char buf[128]; + + buf[0] = 0; + buf[1] = Tftp_ERROR; + buf[2] = 0; + buf[3] = code; + strcpy(buf+4, msg); + n = strlen(msg) + 4 + 1; + udpsend(ctlrno, a, buf, n); + if(report) + print("\ntftp: error(%d): %s\n", code, msg); +} + +static int +udprecv(int ctlrno, Netaddr *a, void *data, int dlen) +{ + int n, len; + ushort csm; + Udphdr *h; + ulong addr, timo; + Etherpkt pkt; + static int rxactive; + + if(rxactive == 0) + timo = 1000; + else + timo = Timeout; + timo += TK2MS(m->ticks); + while(timo > TK2MS(m->ticks)){ + n = etherrxpkt(ctlrno, &pkt, timo-TK2MS(m->ticks)); + + if(n <= 0) + continue; + + h = (Udphdr*)&pkt; + if(nhgets(h->type) != ET_IP) + continue; + + if(ip_csum(&h->vihl)) { + print("ip chksum error\n"); + continue; + } + if(h->vihl != (IP_VER|IP_HLEN)) { + print("ip bad vers/hlen\n"); + continue; + } + + if(h->udpproto != IP_UDPPROTO) + continue; + + h->ttl = 0; + len = nhgets(h->udplen); + hnputs(h->udpplen, len); + + if(nhgets(h->udpcksum)) { + csm = ptcl_csum(&h->ttl, len+UDP_PHDRSIZE); + if(csm != 0) { + print("udp chksum error csum #%4lux len %d\n", csm, n); + break; + } + } + + if(a->port != 0 && nhgets(h->udpsport) != a->port) + continue; + + addr = nhgetl(h->udpsrc); + if(a->ip != Bcastip && addr != a->ip) + continue; + + len -= UDP_HDRSIZE-UDP_PHDRSIZE; + if(len > dlen) { + print("udp: packet too big\n"); + continue; + } + + memmove(data, h->udpcksum+sizeof(h->udpcksum), len); + a->ip = addr; + a->port = nhgets(h->udpsport); + memmove(a->ea, pkt.s, sizeof(a->ea)); + + rxactive = 1; + return len; + } + + return 0; +} + +static int tftpblockno; + +static int +tftpopen(int ctlrno, Netaddr *a, char *name, Tftp *tftp) +{ + int i, len, rlen, oport; + char buf[Segsize+2]; + + buf[0] = 0; + buf[1] = Tftp_READ; + len = sprint(buf+2, "%s", name) + 2; + len += sprint(buf+len+1, "octet") + 2; + + oport = a->port; + for(i = 0; i < 5; i++){ + a->port = oport; + udpsend(ctlrno, a, buf, len); + a->port = 0; + if((rlen = udprecv(ctlrno, a, tftp, sizeof(Tftp))) < sizeof(tftp->header)) + continue; + + switch((tftp->header[0]<<8)|tftp->header[1]){ + + case Tftp_ERROR: + print("tftpopen: error (%d): %s\n", + (tftp->header[2]<<8)|tftp->header[3], tftp->data); + return -1; + + case Tftp_DATA: + tftpblockno = 1; + len = (tftp->header[2]<<8)|tftp->header[3]; + if(len != tftpblockno){ + print("tftpopen: block error: %d\n", len); + nak(ctlrno, a, 1, "block error", 0); + return -1; + } + return rlen-sizeof(tftp->header); + } + } + + print("tftpopen: failed to connect to server\n"); + return -1; +} + +static int +tftpread(int ctlrno, Netaddr *a, Tftp *tftp, int dlen) +{ + int blockno, len, retry; + uchar buf[4]; + + buf[0] = 0; + buf[1] = Tftp_ACK; + buf[2] = tftpblockno>>8; + buf[3] = tftpblockno; + tftpblockno++; + + dlen += sizeof(tftp->header); + + retry = 0; +buggery: + udpsend(ctlrno, a, buf, sizeof(buf)); + + if((len = udprecv(ctlrno, a, tftp, dlen)) != dlen){ + print("tftpread: %d != %d\n", len, dlen); + nak(ctlrno, a, 2, "short read", 0); + if(retry++ < 5) + goto buggery; + return -1; + } + + blockno = (tftp->header[2]<<8)|tftp->header[3]; + if(blockno != tftpblockno){ + print("tftpread: block error: %d, expected %d\n", blockno, tftpblockno); + + if(blockno == tftpblockno-1 && retry++ < 5) + goto buggery; + nak(ctlrno, a, 1, "block error", 0); + + return -1; + } + + return len-sizeof(tftp->header); +} + +int +bootp(int ctlrno, char *file) +{ + Bootp req, rep; + int i, dlen, segsize, text, data, bss, total; + uchar *ea, *addr, *p; + ulong entry; + Exec *exec; + char name[128], *filename, *sysname; + + if((ea = etheraddr(ctlrno)) == 0){ + print("invalid ctlrno %d\n", ctlrno); + return -1; + } + + filename = 0; + sysname = 0; + if(file && *file){ + strcpy(name, file); + if(filename = strchr(name, ':')){ + if(filename != name && *(filename-1) != '\\'){ + sysname = name; + *filename++ = 0; + } + } + else + filename = name; + } + + + memset(&req, 0, sizeof(req)); + req.op = Bootrequest; + req.htype = 1; /* ethernet */ + req.hlen = Eaddrlen; /* ethernet */ + memmove(req.chaddr, ea, Eaddrlen); + + myaddr.ip = 0; + myaddr.port = BPportsrc; + memmove(myaddr.ea, ea, Eaddrlen); + + for(i = 0; i < 10; i++) { + server.ip = Bcastip; + server.port = BPportdst; + memmove(server.ea, broadcast, sizeof(server.ea)); + udpsend(ctlrno, &server, &req, sizeof(req)); + if(udprecv(ctlrno, &server, &rep, sizeof(rep)) <= 0) + continue; + if(memcmp(req.chaddr, rep.chaddr, Eaddrlen)) + continue; + if(rep.htype != 1 || rep.hlen != Eaddrlen) + continue; + if(sysname == 0 || strcmp(sysname, rep.sname) == 0) + break; + } + if(i >= 10) { + print("bootp timed out\n"); + return -1; + } + + if(filename == 0 || *filename == 0) + filename = rep.file; + + if(rep.sname[0] != '\0') + print("%s ", rep.sname); + print("(%d.%d.%d.%d!%d): %s\n", + rep.siaddr[0], + rep.siaddr[1], + rep.siaddr[2], + rep.siaddr[3], + server.port, + filename); + + myaddr.ip = nhgetl(rep.yiaddr); + myaddr.port = tftpport++; + server.ip = nhgetl(rep.siaddr); + server.port = TFTPport; + + if((dlen = tftpopen(ctlrno, &server, filename, &tftpb)) < 0) + return -1; + + exec = (Exec*)(tftpb.data); + if(dlen < sizeof(Exec) || GLLONG(exec->magic) != E_MAGIC){ + nak(ctlrno, &server, 0, "bad magic number", 1); + return -1; + } + text = GLLONG(exec->text); + data = GLLONG(exec->data); + bss = GLLONG(exec->bss); + total = text+data+bss; + entry = GLLONG(exec->entry); +print("load@%8.8lux: ", PADDR(entry)); + print("%d", text); + + addr = (uchar*)PADDR(entry); + p = tftpb.data+sizeof(Exec); + dlen -= sizeof(Exec); + segsize = text; + for(;;){ + if(dlen == 0){ + if((dlen = tftpread(ctlrno, &server, &tftpb, sizeof(tftpb.data))) < 0) + return -1; + p = tftpb.data; + } + if(segsize <= dlen) + i = segsize; + else + i = dlen; + memmove(addr, p, i); + + addr += i; + p += i; + segsize -= i; + dlen -= i; + + if(segsize <= 0){ + if(data == 0) + break; + print("+%d", data); + segsize = data; + data = 0; + addr = (uchar*)PGROUND((ulong)addr); + } + } + nak(ctlrno, &server, 3, "ok", 0); /* tftpclose */ + print("+%d=%d\n", bss, total); + print("entry: 0x%lux\n", entry); + + (*(void(*)(void))(PADDR(entry)))(); + + return 0; +} diff --git a/os/boot/puma/cga.c b/os/boot/puma/cga.c new file mode 100644 index 00000000..0331be6f --- /dev/null +++ b/os/boot/puma/cga.c @@ -0,0 +1,92 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "io.h" +#include "fns.h" + +enum { + Width = 160, + Height = 25, + + Attr = 7, /* white on black */ +}; + +#define CGASCREENBASE ((uchar*)KADDR(0xB8000)) + +static int pos; +static int screeninitdone; + +static uchar +cgaregr(int index) +{ + outb(0x3D4, index); + return inb(0x3D4+1) & 0xFF; +} + +static void +cgaregw(int index, int data) +{ + outb(0x3D4, index); + outb(0x3D4+1, data); +} + +static void +movecursor(void) +{ + cgaregw(0x0E, (pos/2>>8) & 0xFF); + cgaregw(0x0F, pos/2 & 0xFF); + CGASCREENBASE[pos+1] = Attr; +} + +static void +cgascreenputc(int c) +{ + int i; + + if(c == '\n'){ + pos = pos/Width; + pos = (pos+1)*Width; + } + else if(c == '\t'){ + i = 8 - ((pos/2)&7); + while(i-->0) + cgascreenputc(' '); + } + else if(c == '\b'){ + if(pos >= 2) + pos -= 2; + cgascreenputc(' '); + pos -= 2; + } + else{ + CGASCREENBASE[pos++] = c; + CGASCREENBASE[pos++] = Attr; + } + if(pos >= Width*Height){ + memmove(CGASCREENBASE, &CGASCREENBASE[Width], Width*(Height-1)); + memset(&CGASCREENBASE[Width*(Height-1)], 0, Width); + pos = Width*(Height-1); + } + movecursor(); +} + +void +screeninit(void) +{ + if(screeninitdone == 0){ + pos = cgaregr(0x0E)<<8; + pos |= cgaregr(0x0F); + pos *= 2; + screeninitdone = 1; + } +} + +void +cgascreenputs(char* s, int n) +{ + if(screeninitdone == 0) + screeninit(); + while(n-- > 0) + cgascreenputc(*s++); +} diff --git a/os/boot/puma/clock.c b/os/boot/puma/clock.c new file mode 100644 index 00000000..6c8902b7 --- /dev/null +++ b/os/boot/puma/clock.c @@ -0,0 +1,154 @@ +#include "boot.h" + + /* + * Control Word Read/Write Counter (mode 0) LSB, MSB + */ +#define PIT_RW_COUNTER0 0x30 +#define PIT_RW_COUNTER1 0x70 +#define PIT_RW_COUNTER2 0xB0 +#define PIT_COUNTERLATCH0 0x00 +#define PIT_COUNTERLATCH1 0x40 +#define PIT_COUNTERLATCH2 0x80 + +#define PIT_MODE_0 0 /* Interrupt on Terminal Count */ +#define PIT_MODE_1 2 /* Hardware Retriggeable One-shot */ +#define PIT_MODE_2 4 /* Rate Generator */ +#define PIT_MODE_3 6 /* Square Wave Mode */ +#define PIT_MODE_4 8 /* Software Triggered Mode */ +#define PIT_MODE_5 10 /* Hardware Triggered Mode (Retriggeable) */ + +/* + * Harris 82C54 Programmable Interval Timer + * On the Puma board the PIT is memory mapped + * starting at 0xf2000000 and with each of the 8-bit + * registers addressed on a consecutive 4-byte boundary. + */ +#undef inb +#undef outb +#define inb(port) ((*(uchar *)(port))&0xff) +#define outb(port, data) (*(uchar *)(port) = (data)) +enum +{ + Cnt0= 0xf2000000, /* counter locations */ + Cnt1= 0xf2000004, /* ... */ + Cnt2= 0xf2000008, /* ... */ + Ctlw= 0xf200000c, /* control word register*/ + + /* commands */ + Latch0= 0x00, /* latch counter 0's value */ + Load0= 0x30, /* load counter 0 with 2 bytes */ + Latch1= 0x40, /* latch counter 1's value */ + Load1= 0x70, /* load counter 1 with 2 bytes */ + + /* modes */ + Square= 0x06, /* periodic square wave */ + RateGen= 0x04, /* rate generator */ + + Freq= 3686400, /* Real clock frequency */ +}; + +static int cpufreq = 233000000; +static int aalcycles = 14; + +static void +clockintr(Ureg*, void*) +{ + m->ticks++; + checkalarms(); +} + +/* + * delay for l milliseconds more or less. delayloop is set by + * clockinit() to match the actual CPU speed. + */ +void +delay(int l) +{ + l *= m->delayloop; + if(l <= 0) + l = 1; + aamloop(l); +} + +void +microdelay(int l) +{ + l *= m->delayloop; + l /= 1000; + if(l <= 0) + l = 1; + aamloop(l); +} + +void +clockinit(void) +{ + int x, y; /* change in counter */ + int loops, incr; + + /* + * set vector for clock interrupts + */ + setvec(V_TIMER0, clockintr, 0); + + /* + * set clock for 1/HZ seconds + */ + outb(Ctlw, Load0|Square); + outb(Cnt0, (Freq/HZ)); /* low byte */ + outb(Cnt0, (Freq/HZ)>>8); /* high byte */ + + /* find biggest loop that doesn't wrap */ + incr = 16000000/(aalcycles*HZ*2); + x = 2000; + for(loops = incr; loops < 64*1024; loops += incr) { + /* + * measure time for the loop + * TEXT aamloop(SB), $-4 + * _aamloop: + * MOVW R0, R0 + * MOVW R0, R0 + * MOVW R0, R0 + * SUB $1, R0 + * CMP $0, R0 + * BNE _aamloop + * RET + * + * the time for the loop should be independent of external + * cache and memory system since it fits in the execution + * prefetch buffer. + * + */ + outb(Ctlw, Latch0); + x = inb(Cnt0); + x |= inb(Cnt0)<<8; + aamloop(loops); + outb(Ctlw, Latch0); + y = inb(Cnt0); + y |= inb(Cnt0)<<8; + x -= y; + + if(x < 0) + x += Freq/HZ; + + if(x > Freq/(3*HZ)) + break; + } + + /* + * counter goes at twice the frequency, once per transition, + * i.e., twice per square wave + */ + x >>= 1; + + /* + * figure out clock frequency and a loop multiplier for delay(). + */ + cpufreq = loops*((aalcycles*Freq)/x); + m->delayloop = (cpufreq/1000)/aalcycles; /* AAMLOOPs for 1 ms */ + + /* + * add in possible .2% error and convert to MHz + */ + m->speed = (cpufreq + cpufreq/500)/1000000; +} diff --git a/os/boot/puma/conf.c b/os/boot/puma/conf.c new file mode 100644 index 00000000..3661c6aa --- /dev/null +++ b/os/boot/puma/conf.c @@ -0,0 +1,181 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "dosfs.h" + +static char *confname[MAXCONF]; +static char *confval[MAXCONF]; +static int nconf; + +static char* defplan9ini = + "ether0=type=CS8900\r\n" + "vgasize=640x480x8\r\n" + "kernelpercent=40\r\n" + "console=1\r\nbaud=9600\r\n" +; + +extern char **ini; + +char* +getconf(char *name) +{ + int i; + + for(i = 0; i < nconf; i++) + if(strcmp(confname[i], name) == 0) + return confval[i]; + return 0; +} + +/* + * read configuration file + */ +int +plan9ini(Dos *dos, char *val) +{ + Dosfile rc; + int i, n; + char *cp, *p, *q, *line[MAXCONF]; + + cp = BOOTARGS; + if(dos) { + if(dosstat(dos, *ini, &rc) <= 0) + return -1; + + *cp = 0; + n = dosread(&rc, cp, BOOTARGSLEN-1); + if(n <= 0) + return -1; + cp[n] = 0; + } else if(val != nil){ + if(memchr(val, 0, BOOTARGSLEN-1) == nil) + return -1; + print("Using flash configuration\n"); + strcpy(cp, val); + n = strlen(cp); + }else{ + print("Using default configuration\n"); + strcpy(cp, defplan9ini); + n = strlen(cp); + } + + /* + * Make a working copy. + * We could change this to pass the parsed strings + * to the booted programme instead of the raw + * string, then it only gets done once. + */ + memmove(cp+BOOTARGSLEN, cp, n+1); + cp += BOOTARGSLEN; + + /* + * Strip out '\r', change '\t' -> ' '. + */ + p = cp; + for(q = cp; *q; q++){ + if(*q == '\r') + continue; + if(*q == '\t') + *q = ' '; + *p++ = *q; + } + *p = 0; + n = getcfields(cp, line, MAXCONF, "\n"); + for(i = 0; i < n; i++){ + cp = strchr(line[i], '='); + if(cp == 0) + continue; + *cp++ = 0; + if(cp - line[i] >= NAMELEN+1) + *(line[i]+NAMELEN-1) = 0; + confname[nconf] = line[i]; + confval[nconf] = cp; + nconf++; + } + return 0; +} + +static int +parseether(uchar *to, char *from) +{ + char nip[4]; + char *p; + int i; + + p = from; + while(*p == ' ') + ++p; + for(i = 0; i < 6; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +int +isaconfig(char *class, int ctlrno, ISAConf *isa) +{ + char cc[NAMELEN], *p, *q, *r; + int n; + + sprint(cc, "%s%d", class, ctlrno); + for(n = 0; n < nconf; n++){ + if(strncmp(confname[n], cc, NAMELEN)) + continue; + isa->nopt = 0; + p = confval[n]; + while(*p){ + while(*p == ' ' || *p == '\t') + p++; + if(*p == '\0') + break; + if(strncmp(p, "type=", 5) == 0){ + p += 5; + for(q = isa->type; q < &isa->type[NAMELEN-1]; q++){ + if(*p == '\0' || *p == ' ' || *p == '\t') + break; + *q = *p++; + } + *q = '\0'; + } + else if(strncmp(p, "port=", 5) == 0) + isa->port = strtoul(p+5, &p, 0); + else if(strncmp(p, "irq=", 4) == 0) + isa->irq = strtoul(p+4, &p, 0); + else if(strncmp(p, "mem=", 4) == 0) + isa->mem = strtoul(p+4, &p, 0); + else if(strncmp(p, "size=", 5) == 0) + isa->size = strtoul(p+5, &p, 0); + else if(strncmp(p, "ea=", 3) == 0){ + if(parseether(isa->ea, p+3) == -1) + memset(isa->ea, 0, 6); + } + else if(isa->nopt < NISAOPT){ + r = isa->opt[isa->nopt]; + while(*p && *p != ' ' && *p != '\t'){ + *r++ = *p++; + if(r-isa->opt[isa->nopt] >= ISAOPTLEN-1) + break; + } + *r = '\0'; + isa->nopt++; + } + while(*p && *p != ' ' && *p != '\t') + p++; + } + return 1; + } + return 0; +} diff --git a/os/boot/puma/console.c b/os/boot/puma/console.c new file mode 100644 index 00000000..d863472f --- /dev/null +++ b/os/boot/puma/console.c @@ -0,0 +1,181 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +static Queue* consiq; +static Queue* consoq; + +void +bothputs(char *s, int n) +{ + uartputs(s, n); +// cgascreenputs(s, n); +} + +static void (*consputs)(char*, int) = 0; + +void +consinit(void) +{ + char *p; + int baud, port; + static int cgadone; + + if((p = getconf("console")) == 0 || strcmp(p, "lcd") == 0 || strcmp(p, "screen") == 0){ + consiq = qopen(4*1024, 0, 0, 0); + consoq = qopen(8*1024, 0, 0, 0); + consputs = uartputs; + if(!cgadone) { + cgadone = 1; + //screeninit(); + //kbdinit(); + port = 1; + baud = 9600; + uartspecial(port, baud, &consiq, &consoq, kbdchar); + } + return; + } + if(0 || strstr(p, "lcd") == 0) + consputs = bothputs; + else + consputs = uartputs; + + port = strtoul(p, 0, 0); + baud = 0; + if(p = getconf("baud")) + baud = strtoul(p, 0, 0); + if(baud == 0) + baud = 9600; + uartspecial(port, baud, &consiq, &consoq, kbdchar); +} + +void +kbdchar(Queue *q, int c) +{ + c &= 0x7F; + if(c == 0x10) + panic("^p"); + if(q == 0) { + if(consiq != 0) + qbputc(consiq, c); + } else + qbputc(q, c); +} + +static int +getline(char *buf, int size, int dotimeout) +{ + int c, i=0; + ulong start; + char echo; + + for (;;) { + start = m->ticks; + do{ + if(dotimeout && ((m->ticks - start) > 5*HZ)) + return -2; + c = qbgetc(consiq); + }while(c == -1); + if(c == '\r') + c = '\n'; /* turn carriage return into newline */ + if(c == '\177') + c = '\010'; /* turn delete into backspace */ + if(c == '\025') + echo = '\n'; /* echo ^U as a newline */ + else + echo = c; + (*consputs)(&echo, 1); + + if(c == '\010'){ + if(i > 0) + i--; /* bs deletes last character */ + continue; + } + /* a newline ends a line */ + if (c == '\n') + break; + /* ^U wipes out the line */ + if (c =='\025') + return -1; + if(i == size) + return size; + buf[i++] = c; + } + buf[i] = 0; + return i; +} + +int +getstr(char *prompt, char *buf, int size, char *def) +{ + int len, isdefault; + + buf[0] = 0; + isdefault = (def && *def); + for (;;) { + if(isdefault) + print("%s[default==%s]: ", prompt, def); + else + print("%s: ", prompt); + len = getline(buf, size, isdefault); + switch(len){ + case -1: + /* ^U typed */ + continue; + case -2: + /* timeout, use default */ + (*consputs)("\n", 1); + len = 0; + break; + default: + break; + } + if(len >= size){ + print("line too long\n"); + continue; + } + break; + } + if(len == 0 && isdefault) + strcpy(buf, def); + return 0; +} + +int +sprint(char *s, char *fmt, ...) +{ + return donprint(s, s+PRINTSIZE, fmt, (&fmt+1)) - s; +} + +int +print(char *fmt, ...) +{ + char buf[PRINTSIZE]; + int n; + + if(consputs == 0) + return 0; + n = donprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf; + (*consputs)(buf, n); + return n; +} + +void +panic(char *fmt, ...) +{ + char buf[PRINTSIZE]; + int n; + + if(consputs){ + (*consputs)("panic: ", 7); + n = donprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf; + (*consputs)(buf, n); + (*consputs)("\n", 1); + } + spllo(); + for(;;) + idle(); +} diff --git a/os/boot/puma/dat.h b/os/boot/puma/dat.h new file mode 100644 index 00000000..f96b1c0b --- /dev/null +++ b/os/boot/puma/dat.h @@ -0,0 +1,205 @@ +typedef struct Block Block; +typedef struct Queue Queue; + +typedef struct List { + void *next; +} List; + +typedef struct { + int fake; + int pri; +} Lock; +#define lock(x) +#define unlock(x) + +typedef struct Alarm { + List; + int busy; + long dt; + void (*f)(void*); + void *arg; +} Alarm; + +enum { + Eaddrlen = 6, + ETHERMINTU = 60, /* minimum transmit size */ + ETHERMAXTU = 1514, /* maximum transmit size */ + ETHERHDRSIZE = 14, /* size of an ethernet header */ + + MaxEther = 2, +}; + +typedef struct { + uchar d[Eaddrlen]; + uchar s[Eaddrlen]; + uchar type[2]; + uchar data[1500]; + uchar crc[4]; +} Etherpkt; + +extern uchar broadcast[Eaddrlen]; + +enum { + Npart = 20+2, /* 8 sub partitions, disk, and partition */ + Maxxfer = 16*1024, /* maximum transfer size/cmd */ +}; + +typedef struct { + ulong start; + ulong end; + char name[NAMELEN+1]; +} Partition; + +typedef struct { + int online; + int npart; /* number of real partitions */ + Partition p[Npart]; + ulong offset; + Partition *current; /* current partition */ + + ulong cap; /* total bytes */ + int bytes; /* bytes/sector */ + int sectors; /* sectors/track */ + int heads; /* heads/cyl */ + long cyl; /* cylinders/drive */ + + char lba; /* true if drive has logical block addressing */ + char multi; /* non-zero if drive does multiple block xfers */ +} Disc; + +enum { + ScsiTestunit = 0x00, + ScsiExtsens = 0x03, + ScsiInquiry = 0x12, + ScsiModesense = 0x1a, + ScsiStartunit = 0x1B, + ScsiStopunit = 0x1B, + ScsiGetcap = 0x25, + ScsiRead = 0x08, + ScsiWrite = 0x0a, + ScsiExtread = 0x28, + ScsiExtwrite = 0x2a, + + /* data direction */ + ScsiIn = 1, + ScsiOut = 0, +}; + +typedef struct Scsibuf Scsibuf; +typedef struct Scsibuf { + void* virt; + void* phys; + Scsibuf* next; +}; + +typedef struct Scsidata { + uchar* base; + uchar* lim; + uchar* ptr; +} Scsidata; + +typedef struct Ureg Ureg; + +typedef struct Scsi { + ulong pid; + ushort target; + ushort lun; + ushort rflag; + ushort status; + Scsidata cmd; + Scsidata data; + Scsibuf* b; + uchar* save; + uchar cmdblk[16]; +} Scsi; + +typedef struct Segdesc { + ulong d0; + ulong d1; +} Segdesc; + +typedef struct Mach { + ulong ticks; /* of the clock since boot time */ + ulong delayloop; + int speed; /* general system clock in MHz */ + int oscclk; /* oscillator frequency in MHz */ + void* alarm; /* alarms bound to this clock */ +} Mach; + +extern Mach *m; + +#define E_MAGIC ((((4*20)+0)*20)+7) + +typedef struct Exec Exec; +struct Exec +{ + uchar magic[4]; /* magic number */ + uchar text[4]; /* size of text segment */ + uchar data[4]; /* size of initialized data */ + uchar bss[4]; /* size of uninitialized data */ + uchar syms[4]; /* size of symbol table */ + uchar entry[4]; /* entry point */ + uchar spsz[4]; /* size of sp/pc offset table */ + uchar pcsz[4]; /* size of pc/line number table */ +}; + +/* + * bootline passed by boot program + */ +#define BOOTLINE ((char *)0x18000-150) + +/* + * Where we leave configuration info. + */ +#define BOOTARGS ((char*)(0x18000)) +#define BOOTARGSLEN 1024 +#define MAXCONF 32 + +/* + * a parsed plan9.ini line + */ +#define ISAOPTLEN 16 +#define NISAOPT 8 + +typedef struct ISAConf { + char type[NAMELEN]; + ulong port; + ulong irq; + ulong mem; + ulong size; + uchar ea[6]; + + int nopt; + char opt[NISAOPT][ISAOPTLEN]; +} ISAConf; + +typedef struct { + int size; + ulong addr; +} Map; + +typedef struct { + char* name; + Map* map; + Map* mapend; + + Lock; +} RMap; + +typedef struct PCIcfg PCIcfg; + +extern uchar* vgamem; + +struct Block { + uchar *rp; + uchar *wp; + uchar *lim; + uchar *data; + Block* next; + ulong magic; +}; +#define BLEN(b) ((b)->wp-(b)->rp) + +typedef struct QLock { + int dummy; +} QLock; diff --git a/os/boot/puma/div.s b/os/boot/puma/div.s new file mode 100644 index 00000000..42406d85 --- /dev/null +++ b/os/boot/puma/div.s @@ -0,0 +1,122 @@ +/* + * Div/Mod taken from the Inferno 2.0 ebsit code + */ + +Q = 0 +N = 1 +D = 2 +CC = 3 +TMP = 11 + +TEXT save<>(SB), 1, $0 + MOVW R(Q), 0(FP) + MOVW R(N), 4(FP) + MOVW R(D), 8(FP) + MOVW R(CC), 12(FP) + + MOVW R(TMP), R(Q) /* numerator */ + MOVW 20(FP), R(D) /* denominator */ + CMP $0, R(D) + BNE s1 + MOVW -1(R(D)), R(TMP) /* divide by zero fault */ +s1: RET + +TEXT rest<>(SB), 1, $0 + MOVW 0(FP), R(Q) + MOVW 4(FP), R(N) + MOVW 8(FP), R(D) + MOVW 12(FP), R(CC) +/* + * return to caller + * of rest<> + */ + MOVW 0(R13), R14 + ADD $20, R13 + B (R14) + +TEXT div<>(SB), 1, $0 + MOVW $32, R(CC) +/* + * skip zeros 8-at-a-time + */ +e1: + AND.S $(0xff<<24),R(Q), R(N) + BNE e2 + SLL $8, R(Q) + SUB.S $8, R(CC) + BNE e1 + RET +e2: + MOVW $0, R(N) + +loop: +/* + * shift R(N||Q) left one + */ + SLL $1, R(N) + CMP $0, R(Q) + ORR.LT $1, R(N) + SLL $1, R(Q) + +/* + * compare numerator to denominator + * if less, subtract and set quotent bit + */ + CMP R(D), R(N) + ORR.HS $1, R(Q) + SUB.HS R(D), R(N) + SUB.S $1, R(CC) + BNE loop + RET + +TEXT _div(SB), 1, $16 + BL save<>(SB) + CMP $0, R(Q) + BGE d1 + RSB $0, R(Q), R(Q) + CMP $0, R(D) + BGE d2 + RSB $0, R(D), R(D) +d0: + BL div<>(SB) /* none/both neg */ + MOVW R(Q), R(TMP) + B out +d1: + CMP $0, R(D) + BGE d0 + RSB $0, R(D), R(D) +d2: + BL div<>(SB) /* one neg */ + RSB $0, R(Q), R(TMP) + B out + +TEXT _mod(SB), 1, $16 + BL save<>(SB) + CMP $0, R(D) + RSB.LT $0, R(D), R(D) + CMP $0, R(Q) + BGE m1 + RSB $0, R(Q), R(Q) + BL div<>(SB) /* neg numerator */ + RSB $0, R(N), R(TMP) + B out +m1: + BL div<>(SB) /* pos numerator */ + MOVW R(N), R(TMP) + B out + +TEXT _divu(SB), 1, $16 + BL save<>(SB) + BL div<>(SB) + MOVW R(Q), R(TMP) + B out + +TEXT _modu(SB), 1, $16 + BL save<>(SB) + BL div<>(SB) + MOVW R(N), R(TMP) + B out + +out: + BL rest<>(SB) + B out diff --git a/os/boot/puma/donprint.c b/os/boot/puma/donprint.c new file mode 100644 index 00000000..4125e690 --- /dev/null +++ b/os/boot/puma/donprint.c @@ -0,0 +1,332 @@ +#include "u.h" +#include "lib.h" + +#define PTR sizeof(char*) +#define SHORT sizeof(int) +#define INT sizeof(int) +#define LONG sizeof(long) +#define IDIGIT 30 +#define MAXCON 30 + +#define FLONG (1<<0) +#define FSHORT (1<<1) +#define FUNSIGN (1<<2) + +typedef struct Op Op; +struct Op +{ + char *p; + char *ep; + void *argp; + int f1; + int f2; + int f3; +}; + +static int noconv(Op*); +static int cconv(Op*); +static int dconv(Op*); +static int hconv(Op*); +static int lconv(Op*); +static int oconv(Op*); +static int sconv(Op*); +static int uconv(Op*); +static int xconv(Op*); +static int Xconv(Op*); +static int percent(Op*); + +static +int (*fmtconv[MAXCON])(Op*) = +{ + noconv, + cconv, dconv, hconv, lconv, + oconv, sconv, uconv, xconv, + Xconv, percent, +}; +static +char fmtindex[128] = +{ + ['c'] 1, + ['d'] 2, + ['h'] 3, + ['l'] 4, + ['o'] 5, + ['s'] 6, + ['u'] 7, + ['x'] 8, + ['X'] 9, + ['%'] 10, +}; + +static int convcount = { 11 }; +static int ucase; + +static void +PUT(Op *o, int c) +{ + static int pos; + int opos; + + if(c == '\t'){ + opos = pos; + pos = (opos+8) & ~7; + while(opos++ < pos && o->p < o->ep) + *o->p++ = ' '; + return; + } + if(o->p < o->ep){ + *o->p++ = c; + pos++; + } + if(c == '\n') + pos = 0; +} + +int +fmtinstall(char c, int (*f)(Op*)) +{ + + c &= 0177; + if(fmtindex[c] == 0) { + if(convcount >= MAXCON) + return 1; + fmtindex[c] = convcount++; + } + fmtconv[fmtindex[c]] = f; + return 0; +} + +char* +donprint(char *p, char *ep, char *fmt, void *argp) +{ + int sf1, c; + Op o; + + o.p = p; + o.ep = ep; + o.argp = argp; + +loop: + c = *fmt++; + if(c != '%') { + if(c == 0) { + if(o.p < o.ep) + *o.p = 0; + return o.p; + } + PUT(&o, c); + goto loop; + } + o.f1 = 0; + o.f2 = -1; + o.f3 = 0; + c = *fmt++; + sf1 = 0; + if(c == '-') { + sf1 = 1; + c = *fmt++; + } + while(c >= '0' && c <= '9') { + o.f1 = o.f1*10 + c-'0'; + c = *fmt++; + } + if(sf1) + o.f1 = -o.f1; + if(c != '.') + goto l1; + c = *fmt++; + while(c >= '0' && c <= '9') { + if(o.f2 < 0) + o.f2 = 0; + o.f2 = o.f2*10 + c-'0'; + c = *fmt++; + } +l1: + if(c == 0) + fmt--; + c = (*fmtconv[fmtindex[c&0177]])(&o); + if(c < 0) { + o.f3 |= -c; + c = *fmt++; + goto l1; + } + o.argp = (char*)o.argp + c; + goto loop; +} + +void +strconv(char *o, Op *op, int f1, int f2) +{ + int n, c; + char *p; + + n = strlen(o); + if(f1 >= 0) + while(n < f1) { + PUT(op, ' '); + n++; + } + for(p=o; c = *p++;) + if(f2 != 0) { + PUT(op, c); + f2--; + } + if(f1 < 0) { + f1 = -f1; + while(n < f1) { + PUT(op, ' '); + n++; + } + } +} + +int +numbconv(Op *op, int base) +{ + char b[IDIGIT]; + int i, f, n, r; + long v; + short h; + + f = 0; + switch(op->f3 & (FLONG|FSHORT|FUNSIGN)) { + case FLONG: + v = *(long*)op->argp; + r = LONG; + break; + + case FUNSIGN|FLONG: + v = *(ulong*)op->argp; + r = LONG; + break; + + case FSHORT: + h = *(int*)op->argp; + v = h; + r = SHORT; + break; + + case FUNSIGN|FSHORT: + h = *(int*)op->argp; + v = (ushort)h; + r = SHORT; + break; + + default: + v = *(int*)op->argp; + r = INT; + break; + + case FUNSIGN: + v = *(unsigned*)op->argp; + r = INT; + break; + } + if(!(op->f3 & FUNSIGN) && v < 0) { + v = -v; + f = 1; + } + b[IDIGIT-1] = 0; + for(i = IDIGIT-2;; i--) { + n = (ulong)v % base; + n += '0'; + if(n > '9'){ + n += 'a' - ('9'+1); + if(ucase) + n += 'A'-'a'; + } + b[i] = n; + if(i < 2) + break; + v = (ulong)v / base; + if(op->f2 >= 0 && i >= IDIGIT-op->f2) + continue; + if(v <= 0) + break; + } + if(f) + b[--i] = '-'; + strconv(b+i, op, op->f1, -1); + return r; +} + +static int +noconv(Op *op) +{ + + strconv("***", op, 0, -1); + return 0; +} + +static int +cconv(Op *op) +{ + char b[2]; + + b[0] = *(int*)op->argp; + b[1] = 0; + strconv(b, op, op->f1, -1); + return INT; +} + +static int +dconv(Op *op) +{ + return numbconv(op, 10); +} + +static int +hconv(Op*) +{ + return -FSHORT; +} + +static int +lconv(Op*) +{ + return -FLONG; +} + +static int +oconv(Op *op) +{ + return numbconv(op, 8); +} + +static int +sconv(Op *op) +{ + strconv(*(char**)op->argp, op, op->f1, op->f2); + return PTR; +} + +static int +uconv(Op*) +{ + return -FUNSIGN; +} + +static int +xconv(Op *op) +{ + return numbconv(op, 16); +} + +static int +Xconv(Op *op) +{ + int r; + + ucase = 1; + r = numbconv(op, 16); + ucase = 0; + return r; +} + +static int +percent(Op *op) +{ + + PUT(op, '%'); + return 0; +} diff --git a/os/boot/puma/dosboot.c b/os/boot/puma/dosboot.c new file mode 100644 index 00000000..365c8824 --- /dev/null +++ b/os/boot/puma/dosboot.c @@ -0,0 +1,614 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "dosfs.h" + +extern char *premature; + +/* + * predeclared + */ +static void bootdump(Dosboot*); +static void setname(Dosfile*, char*); +long dosreadseg(Dosfile*, long, long); + +/* + * debugging + */ +#define chatty 1 +#define chat if(chatty)print + +/* + * block io buffers + */ +enum +{ + Nbio= 16, +}; +typedef struct Clustbuf Clustbuf; +struct Clustbuf +{ + int age; + long sector; + uchar *iobuf; + Dos *dos; + int size; +}; +Clustbuf bio[Nbio]; + +/* + * get an io block from an io buffer + */ +Clustbuf* +getclust(Dos *dos, long sector) +{ + Clustbuf *p, *oldest; + int size; + + chat("getclust @ %d\n", sector); + + /* + * if we have it, just return it + */ + for(p = bio; p < &bio[Nbio]; p++){ + if(sector == p->sector && dos == p->dos){ + p->age = m->ticks; + chat("getclust %d in cache\n", sector); + return p; + } + } + + /* + * otherwise, reuse the oldest entry + */ + oldest = bio; + for(p = &bio[1]; p < &bio[Nbio]; p++){ + if(p->age <= oldest->age) + oldest = p; + } + p = oldest; + + /* + * make sure the buffer is big enough + */ + size = dos->clustsize*dos->sectsize; + if(p->iobuf==0 || p->size < size) + p->iobuf = ialloc(size, 0); + p->size = size; + + /* + * read in the cluster + */ + chat("getclust addr %d\n", (sector+dos->start)*dos->sectsize); + if((*dos->seek)(dos->dev, (sector+dos->start)*dos->sectsize) < 0){ + chat("can't seek block\n"); + return 0; + } + if((*dos->read)(dos->dev, p->iobuf, size) != size){ + chat("can't read block\n"); + return 0; + } + + p->age = m->ticks; + p->dos = dos; + p->sector = sector; + chat("getclust %d read\n", sector); + return p; +} + +/* + * walk the fat one level ( n is a current cluster number ). + * return the new cluster number or -1 if no more. + */ +static long +fatwalk(Dos *dos, int n) +{ + ulong k, sect; + Clustbuf *p; + int o; + + chat("fatwalk %d\n", n); + + if(n < 2 || n >= dos->fatclusters) + return -1; + + switch(dos->fatbits){ + case 12: + k = (3*n)/2; break; + case 16: + k = 2*n; break; + default: + return -1; + } + if(k >= dos->fatsize*dos->sectsize) + panic("getfat"); + + sect = (k/(dos->sectsize*dos->clustsize))*dos->clustsize + dos->fataddr; + o = k%(dos->sectsize*dos->clustsize); + p = getclust(dos, sect); + k = p->iobuf[o++]; + if(o >= dos->sectsize*dos->clustsize){ + p = getclust(dos, sect+dos->clustsize); + o = 0; + } + k |= p->iobuf[o]<<8; + if(dos->fatbits == 12){ + if(n&1) + k >>= 4; + else + k &= 0xfff; + if(k >= 0xff8) + k |= 0xf000; + } + k = k < 0xfff8 ? k : -1; + chat("fatwalk %d -> %d\n", n, k); + return k; +} + +/* + * map a file's logical cluster address to a physical sector address + */ +static long +fileaddr(Dosfile *fp, long ltarget) +{ + Dos *dos = fp->dos; + long l; + long p; + + chat("fileaddr %8.8s %d\n", fp->name, ltarget); + /* + * root directory is contiguous and easy + */ + if(fp->pstart == 0){ + if(ltarget*dos->sectsize*dos->clustsize >= dos->rootsize*sizeof(Dosdir)) + return -1; + l = dos->rootaddr + ltarget*dos->clustsize; + chat("fileaddr %d -> %d\n", ltarget, l); + return l; + } + + /* + * anything else requires a walk through the fat + */ + if(ltarget >= fp->lcurrent && fp->pcurrent){ + /* start at the currrent point */ + l = fp->lcurrent; + p = fp->pcurrent; + } else { + /* go back to the beginning */ + l = 0; + p = fp->pstart; + } + while(l != ltarget){ + /* walk the fat */ + p = fatwalk(dos, p); + if(p < 0) + return -1; + l++; + } + fp->lcurrent = l; + fp->pcurrent = p; + + /* + * clusters start at 2 instead of 0 (why? - presotto) + */ + l = dos->dataaddr + (p-2)*dos->clustsize; + chat("fileaddr %d -> %d\n", ltarget, l); + return l; +} + +/* + * read from a dos file + */ +long +dosread(Dosfile *fp, void *a, long n) +{ + long addr; + long rv; + int i; + int off; + Clustbuf *p; + uchar *from, *to; + + if((fp->attr & DDIR) == 0){ + if(fp->offset >= fp->length) + return 0; + if(fp->offset+n > fp->length) + n = fp->length - fp->offset; + } + + to = a; + for(rv = 0; rv < n; rv+=i){ + /* + * read the cluster + */ + addr = fileaddr(fp, fp->offset/fp->dos->clustbytes); + if(addr < 0) + return -1; + p = getclust(fp->dos, addr); + if(p == 0) + return -1; + + /* + * copy the bytes we need + */ + off = fp->offset % fp->dos->clustbytes; + from = &p->iobuf[off]; + i = n - rv; + if(i > fp->dos->clustbytes - off) + i = fp->dos->clustbytes - off; + memmove(to, from, i); + to += i; + fp->offset += i; + } + + return rv; +} + +/* + * walk a directory returns + * -1 if something went wrong + * 0 if not found + * 1 if found + */ +int +doswalk(Dosfile *file, char *name) +{ + Dosdir d; + long n; + + if((file->attr & DDIR) == 0){ + chat("walking non-directory!\n"); + return -1; + } + + setname(file, name); + + file->offset = 0; /* start at the beginning */ + while((n = dosread(file, &d, sizeof(d))) == sizeof(d)){ + chat("comparing to %8.8s.%3.3s\n", d.name, d.ext); + if(memcmp(file->name, d.name, sizeof(d.name)) != 0) + continue; + if(memcmp(file->ext, d.ext, sizeof(d.ext)) != 0) + continue; + if(d.attr & DVLABEL){ + chat("%8.8s.%3.3s is a LABEL\n", d.name, d.ext); + continue; + } + file->attr = d.attr; + file->pstart = GSHORT(d.start); + file->length = GLONG(d.length); + file->pcurrent = 0; + file->lcurrent = 0; + file->offset = 0; + return 1; + } + return n >= 0 ? 0 : -1; +} + + +/* + * instructions that boot blocks can start with + */ +#define JMPSHORT 0xeb +#define JMPNEAR 0xe9 + +/* + * read dos file system properties + */ +int +dosinit(Dos *dos, int start, int ishard) +{ + Dosboot *b; + int i; + Clustbuf *p; + Dospart *dp; + ulong mbroffset, offset; + + /* defaults till we know better */ + dos->start = start; + dos->sectsize = 512; + dos->clustsize = 1; + mbroffset = 0; + +dmddo: + /* get first sector */ + p = getclust(dos, mbroffset); + if(p == 0){ + chat("can't read boot block\n"); + return -1; + } + + /* + * If it's a hard disc then look for an MBR and pick either an + * active partition or the FAT with the lowest starting LBA. + * Things are tricky because we could be pointing to, amongst others: + * 1) a floppy BPB; + * 2) a hard disc MBR; + * 3) a hard disc extended partition table; + * 4) a logical drive on a hard disc; + * 5) a disc-manager boot block. + * They all have the same magic at the end of the block. + */ + if(p->iobuf[0x1FE] != 0x55 || p->iobuf[0x1FF] != 0xAA) { + chat("not DOS\n"); + return -1; + } + p->dos = 0; + b = (Dosboot *)p->iobuf; + if(ishard && b->mediadesc != 0xF8){ + dp = (Dospart*)&p->iobuf[0x1BE]; + offset = 0xFFFFFFFF; + for(i = 0; i < 4; i++, dp++){ + if(dp->type == DMDDO){ + mbroffset = 63; + goto dmddo; + } + if(dp->type != FAT12 && dp->type != FAT16 && dp->type != FATHUGE) + continue; + if(dp->flag & 0x80){ + offset = GLONG(dp->start); + break; + } + if(GLONG(dp->start) < offset) + offset = GLONG(dp->start); + } + if(i != 4 || offset != 0xFFFFFFFF){ + dos->start = mbroffset+offset; + p = getclust(dos, 0); + if(p == 0 || p->iobuf[0x1FE] != 0x55 || p->iobuf[0x1FF] != 0xAA) + return -1; + } + p->dos = 0; + } + + b = (Dosboot *)p->iobuf; + if(b->magic[0] != JMPNEAR && (b->magic[0] != JMPSHORT || b->magic[2] != 0x90)){ + chat("no dos file system\n"); + return -1; + } + + if(chatty) + bootdump(b); + + /* + * determine the systems' wondersous properties + */ + dos->sectsize = GSHORT(b->sectsize); + dos->clustsize = b->clustsize; + dos->clustbytes = dos->sectsize*dos->clustsize; + dos->nresrv = GSHORT(b->nresrv); + dos->nfats = b->nfats; + dos->rootsize = GSHORT(b->rootsize); + dos->volsize = GSHORT(b->volsize); + if(dos->volsize == 0) + dos->volsize = GLONG(b->bigvolsize); + dos->mediadesc = b->mediadesc; + dos->fatsize = GSHORT(b->fatsize); + dos->fataddr = dos->nresrv; + dos->rootaddr = dos->fataddr + dos->nfats*dos->fatsize; + i = dos->rootsize*sizeof(Dosdir) + dos->sectsize - 1; + i = i/dos->sectsize; + dos->dataaddr = dos->rootaddr + i; + dos->fatclusters = 2+(dos->volsize - dos->dataaddr)/dos->clustsize; + if(dos->fatclusters < 4087) + dos->fatbits = 12; + else + dos->fatbits = 16; + dos->freeptr = 2; + + /* + * set up the root + */ + dos->root.dos = dos; + dos->root.pstart = 0; + dos->root.pcurrent = dos->root.lcurrent = 0; + dos->root.offset = 0; + dos->root.attr = DDIR; + dos->root.length = dos->rootsize*sizeof(Dosdir); + + return 0; +} + +static void +bootdump(Dosboot *b) +{ + if(chatty == 0) + return; + print("magic: 0x%2.2x 0x%2.2x 0x%2.2x\n", + b->magic[0], b->magic[1], b->magic[2]); + print("version: \"%8.8s\"\n", b->version); + print("sectsize: %d\n", GSHORT(b->sectsize)); + print("allocsize: %d\n", b->clustsize); + print("nresrv: %d\n", GSHORT(b->nresrv)); + print("nfats: %d\n", b->nfats); + print("rootsize: %d\n", GSHORT(b->rootsize)); + print("volsize: %d\n", GSHORT(b->volsize)); + print("mediadesc: 0x%2.2x\n", b->mediadesc); + print("fatsize: %d\n", GSHORT(b->fatsize)); + print("trksize: %d\n", GSHORT(b->trksize)); + print("nheads: %d\n", GSHORT(b->nheads)); + print("nhidden: %d\n", GLONG(b->nhidden)); + print("bigvolsize: %d\n", GLONG(b->bigvolsize)); + print("driveno: %d\n", b->driveno); + print("reserved0: 0x%2.2x\n", b->reserved0); + print("bootsig: 0x%2.2x\n", b->bootsig); + print("volid: 0x%8.8x\n", GLONG(b->volid)); + print("label: \"%11.11s\"\n", b->label); +} + +/* + * grab next element from a path, return the pointer to unprocessed portion of + * path. + */ +static char * +nextelem(char *path, char *elem) +{ + int i; + + while(*path == '/') + path++; + if(*path==0 || *path==' ') + return 0; + for(i=0; *path!='\0' && *path!='/' && *path!=' '; i++){ + if(i==28){ + print("name component too long\n"); + return 0; + } + *elem++ = *path++; + } + *elem = '\0'; + return path; +} + +int +dosstat(Dos *dos, char *path, Dosfile *f) +{ + char element[NAMELEN]; + + *f = dos->root; + while(path = nextelem(path, element)){ + switch(doswalk(f, element)){ + case -1: + return -1; + case 0: + return 0; + } + } + return 1; +} + +/* + * boot + */ +int +dosboot(Dos *dos, char *path) +{ + Dosfile file; + long n; + long addr; + Exec *ep; + void (*b)(void); + + switch(dosstat(dos, path, &file)){ + + case -1: + print("error walking to %s\n", path); + return -1; + case 0: + print("%s not found\n", path); + return -1; + case 1: + print("found %8.8s.%3.3s attr 0x%ux start 0x%lux len %d\n", file.name, + file.ext, file.attr, file.pstart, file.length); + break; + } + + /* + * read header + */ + ep = (Exec*)ialloc(sizeof(Exec), 0); + n = sizeof(Exec); + if(dosreadseg(&file, n, (ulong) ep) != n){ + print(premature); + return -1; + } + if(GLLONG(ep->magic) != E_MAGIC){ + print("bad magic 0x%lux not a plan 9 executable!\n", GLLONG(ep->magic)); + return -1; + } + + /* + * read text + */ + addr = PADDR(GLLONG(ep->entry)); + n = GLLONG(ep->text); + print("+%d", n); + if(dosreadseg(&file, n, addr) != n){ + print(premature); + return -1; + } + + /* + * read data (starts at first page after kernel) + */ + addr = PGROUND(addr+n); + n = GLLONG(ep->data); + print("+%d", n); + if(dosreadseg(&file, n, addr) != n){ + print(premature); + return -1; + } + + /* + * bss and entry point + */ + print("+%d\nstart at 0x%lux\n", GLLONG(ep->bss), GLLONG(ep->entry)); + + /* + * Go to new code. It's up to the program to get its PC relocated to + * the right place. + */ + b = (void (*)(void))(PADDR(GLLONG(ep->entry))); + (*b)(); + return 0; +} + +/* + * read in a segment + */ +long +dosreadseg(Dosfile *fp, long len, long addr) +{ + char *a; + long n, sofar; + + a = (char *)addr; + for(sofar = 0; sofar < len; sofar += n){ + n = 8*1024; + if(len - sofar < n) + n = len - sofar; + n = dosread(fp, a + sofar, n); + if(n <= 0) + break; + print("."); + } + return sofar; +} + +/* + * set up a dos file name + */ +static void +setname(Dosfile *fp, char *from) +{ + char *to; + + to = fp->name; + for(; *from && to-fp->name < 8; from++, to++){ + if(*from == '.'){ + from++; + break; + } + if(*from >= 'a' && *from <= 'z') + *to = *from + 'A' - 'a'; + else + *to = *from; + } + while(to - fp->name < 8) + *to++ = ' '; + + to = fp->ext; + for(; *from && to-fp->ext < 3; from++, to++){ + if(*from >= 'a' && *from <= 'z') + *to = *from + 'A' - 'a'; + else + *to = *from; + } + while(to-fp->ext < 3) + *to++ = ' '; + + chat("name is %8.8s %3.3s\n", fp->name, fp->ext); +} diff --git a/os/boot/puma/dosfs.h b/os/boot/puma/dosfs.h new file mode 100644 index 00000000..a45065a6 --- /dev/null +++ b/os/boot/puma/dosfs.h @@ -0,0 +1,110 @@ +typedef struct Dosboot Dosboot; +typedef struct Dos Dos; +typedef struct Dosdir Dosdir; +typedef struct Dosfile Dosfile; +typedef struct Dospart Dospart; + +struct Dospart +{ + uchar flag; /* active flag */ + uchar shead; /* starting head */ + uchar scs[2]; /* starting cylinder/sector */ + uchar type; /* partition type */ + uchar ehead; /* ending head */ + uchar ecs[2]; /* ending cylinder/sector */ + uchar start[4]; /* starting sector */ + uchar len[4]; /* length in sectors */ +}; + +#define FAT12 0x01 +#define FAT16 0x04 +#define FATHUGE 0x06 +#define DMDDO 0x54 + +struct Dosboot{ + uchar magic[3]; + uchar version[8]; + uchar sectsize[2]; + uchar clustsize; + uchar nresrv[2]; + uchar nfats; + uchar rootsize[2]; + uchar volsize[2]; + uchar mediadesc; + uchar fatsize[2]; + uchar trksize[2]; + uchar nheads[2]; + uchar nhidden[4]; + uchar bigvolsize[4]; + uchar driveno; + uchar reserved0; + uchar bootsig; + uchar volid[4]; + uchar label[11]; + uchar reserved1[8]; +}; + +struct Dosfile{ + Dos *dos; /* owning dos file system */ + char name[8]; + char ext[3]; + uchar attr; + long length; + long pstart; /* physical start cluster address */ + long pcurrent; /* physical current cluster address */ + long lcurrent; /* logical current cluster address */ + long offset; +}; + +struct Dos{ + int dev; /* device id */ + long (*read)(int, void*, long); /* read routine */ + long (*seek)(int, long); /* seek routine */ + + int start; /* start of file system */ + int sectsize; /* in bytes */ + int clustsize; /* in sectors */ + int clustbytes; /* in bytes */ + int nresrv; /* sectors */ + int nfats; /* usually 2 */ + int rootsize; /* number of entries */ + int volsize; /* in sectors */ + int mediadesc; + int fatsize; /* in sectors */ + int fatclusters; + int fatbits; /* 12 or 16 */ + long fataddr; /* sector number */ + long rootaddr; + long dataaddr; + long freeptr; + + Dosfile root; +}; + +struct Dosdir{ + uchar name[8]; + uchar ext[3]; + uchar attr; + uchar reserved[10]; + uchar time[2]; + uchar date[2]; + uchar start[2]; + uchar length[4]; +}; + +#define DRONLY 0x01 +#define DHIDDEN 0x02 +#define DSYSTEM 0x04 +#define DVLABEL 0x08 +#define DDIR 0x10 +#define DARCH 0x20 + +extern int chatty; + +extern int dosboot(Dos*, char*); +extern int dosinit(Dos*, int, int); +extern long dosread(Dosfile*, void*, long); +extern int dosstat(Dos*, char*, Dosfile*); +extern int doswalk(Dosfile*, char*); + +extern int plan9ini(Dos*, char*); diff --git a/os/boot/puma/ebsit.trap.c b/os/boot/puma/ebsit.trap.c new file mode 100644 index 00000000..f95c0418 --- /dev/null +++ b/os/boot/puma/ebsit.trap.c @@ -0,0 +1,206 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "ebsit.h" +#include "dat.h" +#include "fns.h" + +#include "ureg.h" + +int inpanic; + +#define CSR ((ushort *) 0x2000000) + + +typedef struct Irqctlr { + uint addr; + uint enabled; + struct { + void (*r)(Ureg*, void*); + void *a; + } h[16]; +} Irqctlr; + +static Irqctlr irqctlr; + +void +csrset( int bit ) +{ +static ushort *csr_val = 0x8c; + + *csr_val ^= (1 << bit); + putcsr(*csr_val); +} + +void +intrinit( void ) +{ +int offset; +ulong op; + + + irqctlr.addr = 1; + irqctlr.enabled = 0; + + /* Reset Exception */ + offset = ((((ulong) _vsvccall) - 0x0)-8) >> 2; + op = ( 0xea << 24 ) | offset; + *((ulong *) 0x0) = op; + + /* Undefined Instruction Exception */ + offset = ((((ulong) _vundcall) - 0x4)-8) >> 2; + op = ( 0xea << 24 ) | offset; + *((ulong *) 0x4) = op; + + /* SWI Exception */ + offset = ((((ulong) _vsvccall) - 0x8)-8) >> 2; + op = ( 0xea << 24 ) | offset; + *((ulong *) 0x8) = op; + + /* Prefetch Abort Exception */ + offset = ((((ulong) _vpabcall) - 0xc)-8) >> 2; + op = ( 0xea << 24 ) | offset; + *((ulong *) 0xc) = op; + + /* Data Abort Exception */ + offset = ((((ulong) _vdabcall) - 0x10)-8) >> 2; + op = ( 0xea << 24 ) | offset; + *((ulong *) 0x10) = op; + + /* IRQ Exception */ + offset = ((((ulong) _virqcall) - 0x18)-8) >> 2; + op = ( 0xea << 24 ) | offset; + *((ulong *) 0x18) = op; + + +} + +void +intrenable(uint addr, int bit, void (*r)(Ureg*, void*), void* a) +{ + int i; + USED(addr); + for(i = 0; i < 16; i++) + { + if((bit & (1<psr, ureg->type, ureg->pc, ureg->link); + print("R14 %8.8uX R13 %8.8uX R12 %8.8uX R11 %8.8uX R10 %8.8uX\n", + ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10); + print("R9 %8.8uX R8 %8.8uX R7 %8.8uX R6 %8.8uX R5 %8.8uX\n", + ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5); + print("R4 %8.8uX R3 %8.8uX R2 %8.8uX R1 %8.8uX R0 %8.8uX\n", + ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0); + print("Last Interrupt's CSR: %8.8uX\n",lucifer); + print("CPSR %8.8uX SPSR %8.8uX\n", cpsrr(), spsrr()); +} + +void +dumpstack(void) +{ +} + +void +exception(Ureg* ureg) +{ + static Ureg old_ureg; + uint far =0; + uint fsr =0; + + static lasttype = 0; + + LOWBAT; + + USED(far, fsr); + + lasttype = ureg->type; + + /* + * All interrupts/exceptions should be resumed at ureg->pc-4, + * except for Data Abort which resumes at ureg->pc-8. + */ + + if(ureg->type == (PsrMabt+1)) + ureg->pc -= 8; + else + ureg->pc -= 4; + + switch(ureg->type){ + + case PsrMfiq: /* (Fast) */ + print("Fast\n"); + print("We should never be here\n"); + while(1); + + case PsrMirq: /* Interrupt Request */ + interrupt(ureg); + break; + + case PsrMund: /* Undefined instruction */ + print("Undefined instruction\n"); + case PsrMsvc: /* Jump through 0, SWI or reserved trap */ + print("SWI/SVC trap\n"); + case PsrMabt: /* Prefetch abort */ + print("Prefetch Abort\n"); + case PsrMabt+1: /* Data abort */ + print("Data Abort\n"); + + + default: + dumpregs(ureg); + /* panic("exception %uX\n", ureg->type); */ + break; + } + + LOWBAT; /* Low bat off after interrupt */ + + splhi(); + +} diff --git a/os/boot/puma/ether.c b/os/boot/puma/ether.c new file mode 100644 index 00000000..a3559051 --- /dev/null +++ b/os/boot/puma/ether.c @@ -0,0 +1,156 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "ether.h" + +static Ctlr ether[MaxEther]; + +static struct { + char *type; + int (*reset)(Ctlr*); +} cards[] = { + { "CS8900", cs8900reset, }, + { 0, } +}; + +int +etherinit(void) +{ + Ctlr *ctlr; + int ctlrno, i, mask, n; + + mask = 0; + for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){ + ctlr = ðer[ctlrno]; + memset(ctlr, 0, sizeof(Ctlr)); + if(isaconfig("ether", ctlrno, &ctlr->card) == 0) + continue; + for(n = 0; cards[n].type; n++){ + if(strcmp(cards[n].type, ctlr->card.type)) + continue; + ctlr->ctlrno = ctlrno; + if((*cards[n].reset)(ctlr)) + break; + + ctlr->iq = qopen(16*1024, 1, 0, 0); + ctlr->oq = qopen(16*1024, 1, 0, 0); + + ctlr->present = 1; + mask |= 1<ctlrno, ctlr->card.type, ctlr->card.port, ctlr->card.irq); + if(ctlr->card.mem) + print(" addr 0x%luX", ctlr->card.mem & ~KZERO); + if(ctlr->card.size) + print(" size 0x%luX", ctlr->card.size); + print(":"); + for(i = 0; i < sizeof(ctlr->card.ea); i++) + print(" %2.2uX", ctlr->card.ea[i]); + print("\n"); uartwait(); + setvec(ctlr->card.irq, ctlr->card.intr, ctlr); + break; + } + } + + return mask; +} + +static Ctlr* +attach(int ctlrno) +{ + Ctlr *ctlr; + + if(ctlrno >= MaxEther || ether[ctlrno].present == 0) + return 0; + + ctlr = ðer[ctlrno]; + if(ctlr->present == 1){ + ctlr->present = 2; + (*ctlr->card.attach)(ctlr); + } + + return ctlr; +} + +uchar* +etheraddr(int ctlrno) +{ + Ctlr *ctlr; + + if((ctlr = attach(ctlrno)) == 0) + return 0; + + return ctlr->card.ea; +} + +int +etherrxpkt(int ctlrno, Etherpkt *pkt, int timo) +{ + int n; + Ctlr *ctlr; + Block *b; + ulong start; + + if((ctlr = attach(ctlrno)) == 0) + return 0; + + start = m->ticks; + while((b = qget(ctlr->iq)) == 0){ + if(TK2MS(m->ticks - start) >= timo){ + /* + print("ether%d: rx timeout\n", ctlrno); + */ + return 0; + } + //delay(1); + } + + n = BLEN(b); + memmove(pkt, b->rp, n); + freeb(b); + + return n; +} + +int +etheriq(Ctlr *ctlr, Block *b, int freebp) +{ + if(memcmp(((Etherpkt*)b->rp)->d, ctlr->card.ea, Eaddrlen) != 0){ + if(freebp) + freeb(b); + return 0; + } + qbwrite(ctlr->iq, b); + return 1; +} + +int +ethertxpkt(int ctlrno, Etherpkt *pkt, int len, int) +{ + Ctlr *ctlr; + Block *b; + int s; + + if((ctlr = attach(ctlrno)) == 0) + return 0; + + if(qlen(ctlr->oq) > 16*1024){ + print("ether%d: tx queue full\n", ctlrno); + return 0; + } + b = iallocb(sizeof(Etherpkt)); + memmove(b->wp, pkt, len); + memmove(((Etherpkt*)b->wp)->s, ctlr->card.ea, Eaddrlen); + b->wp += len; + qbwrite(ctlr->oq, b); + s = splhi(); + (*ctlr->card.transmit)(ctlr); + splx(s); + + return 1; +} diff --git a/os/boot/puma/ether.h b/os/boot/puma/ether.h new file mode 100644 index 00000000..26f5f64b --- /dev/null +++ b/os/boot/puma/ether.h @@ -0,0 +1,82 @@ +/* + * All the goo for PC ethernet cards. + */ +typedef struct Card Card; +typedef struct RingBuf RingBuf; +typedef struct Type Type; +typedef struct Ctlr Ctlr; + +/* + * Hardware interface. + */ +struct Card { + ISAConf; + + int (*reset)(Ctlr*); + void (*attach)(Ctlr*); + + void *(*read)(Ctlr*, void*, ulong, ulong); + void *(*write)(Ctlr*, ulong, void*, ulong); + + void (*receive)(Ctlr*); + void (*transmit)(Ctlr*); + void (*intr)(Ureg*, Ctlr*); + void (*overflow)(Ctlr*); + + uchar bit16; /* true if a 16 bit interface */ + uchar ram; /* true if card has shared memory */ + + ulong dp8390; /* I/O address of 8390 (if any) */ + ulong data; /* I/O data port if no shared memory */ + uchar nxtpkt; /* software bndry */ + uchar tstart; /* 8390 ring addresses */ + uchar pstart; + uchar pstop; + + uchar dummyrr; /* do dummy remote read */ +}; + +/* + * Software ring buffer. + */ +struct RingBuf { + uchar owner; + uchar busy; /* unused */ + ushort len; + uchar pkt[sizeof(Etherpkt)]; +}; + +enum { + Host = 0, /* buffer owned by host */ + Interface = 1, /* buffer owned by card */ + + Nrb = 16, /* default number of receive buffers */ + Ntb = 2, /* default number of transmit buffers */ +}; + +/* + * Software controller. + */ +struct Ctlr { + Card card; /* hardware info */ + int ctlrno; + int present; + + Queue* iq; + Queue* oq; + + int inpackets; + int outpackets; + int crcs; /* input crc errors */ + int oerrs; /* output errors */ + int frames; /* framing errors */ + int overflows; /* packet overflows */ + int buffs; /* buffering errors */ +}; + +#define NEXT(x, l) (((x)+1)%(l)) +#define HOWMANY(x, y) (((x)+((y)-1))/(y)) +#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) + +extern int cs8900reset(Ctlr*); +extern int etheriq(Ctlr*, Block*, int); diff --git a/os/boot/puma/ether8900.c b/os/boot/puma/ether8900.c new file mode 100644 index 00000000..b50da272 --- /dev/null +++ b/os/boot/puma/ether8900.c @@ -0,0 +1,555 @@ +/* + * Crystal CS8900 ethernet controller + * Specifically for the Teralogic Puma architecture + */ + +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "ether.h" +#include "puma.h" + +/* + * On the Puma board the CS8900 can be addressed from either + * ISA I/O space or ISA memory space at the following locations. + * The cs8900 address pins are shifted by 1 relative to the CPU. + */ +enum { + IsaIOBase = 0xf0000000, + IsaMemBase = 0xe0000000, + + IOBase = 0x300, + MemBase = 0xc0000, +}; + +/* I/O accesses */ +#define out16(port, val) (*((ushort *)IsaIOBase + IOBase + (port)) = (val)) +#define in16(port) *((ushort *)IsaIOBase + IOBase + (port)) +#define in8(port) *((uchar *)IsaIOBase + ((IOBase+(port))<<1)) +#define regIOw(reg, val) do {out16(PpPtr, (reg)|0x3000); out16(PpData, val);} while(0) +#define regIOr(reg) (out16(PpPtr, (reg)|0x3000), in16(PpData)) +#define regIOr1(reg) (out16(PpPtr, (reg)|0x3000), in16(PpData1)) + +/* Memory accesses */ +#define regw(reg, val) *((ushort *)IsaMemBase + MemBase + (reg)) = (val) +#define regr(reg) *((ushort *)IsaMemBase + MemBase + (reg)) + +/* Puma frame copying */ +#define copyout(src, len) { \ + int _len = (len); \ + ushort *_src = (ushort *)(src); \ + ushort *_dst = (ushort *)IsaMemBase + MemBase + TxFrame; \ + while(_len > 0) { \ + *_dst++ = *_src++; \ + _dst++; \ + _len -= 2; \ + } \ + } +#define copyoutIO(src, len) { \ + int _len = (len); \ + ushort *_src = (ushort *)(src); \ + while(_len > 0) { \ + out16(RxTxData, *_src); \ + _src++; \ + _len -= 2; \ + } \ + } +#define copyin(dst, len) { \ + int _len = (len), _len2 = (len)&~1; \ + ushort *_src = (ushort *)IsaMemBase + MemBase + RxFrame; \ + ushort *_dst = (ushort *)(dst); \ + while(_len2 > 0) { \ + *_dst++ = *_src++; \ + _src++; \ + _len2 -= 2; \ + } \ + if(_len&1) \ + *(uchar*)_dst = (*_src)&0xff; \ + } +#define copyinIO(dst, len) { \ + int _i, _len = (len), _len2 = (len)&~1; \ + ushort *_dst = (ushort *)(dst); \ + _i = in16(RxTxData); USED(_i); /* RxStatus */ \ + _i = in16(RxTxData); USED(_i); /* RxLen */ \ + while(_len2 > 0) { \ + *_dst++ = in16(RxTxData); \ + _len2 -= 2; \ + } \ + if(_len&1) \ + *(uchar*)_dst = (in16(RxTxData))&0xff; \ + } + + + +enum { /* I/O Mode Register Offsets */ + RxTxData = 0x00, /* receive/transmit data - port 0 */ + TxCmdIO = 0x04, /* transmit command */ + TxLenIO = 0x06, /* transmit length */ + IsqIO = 0x08, /* Interrupt status queue */ + PpPtr = 0x0a, /* packet page pointer */ + PpData = 0x0c, /* packet page data */ + PpData1 = 0x0e, /* packet page data - port 1*/ +}; + +enum { /* Memory Mode Register Offsets */ + /* Bus Interface Registers */ + Ern = 0x0000, /* EISA registration numberion */ + Pic = 0x0002, /* Product identification code */ + Iob = 0x0020, /* I/O base address */ + Intr = 0x0022, /* interrupt number */ + Mba = 0x002c, /* memory base address */ + + Ecr = 0x0040, /* EEPROM command register */ + Edw = 0x0042, /* EEPROM data word */ + Rbc = 0x0050, /* receive frame byte counter */ + + /* Status and Control Registers */ + RxCfg = 0x0102, + RxCtl = 0x0104, + TxCfg = 0x0106, + BufCfg = 0x010a, + LineCtl = 0x0112, + SelfCtl = 0x0114, + BusCtl = 0x0116, + TestCtl = 0x0118, + Isq = 0x0120, + RxEvent = 0x0124, + TxEvent = 0x0128, + BufEvent = 0x012c, + RxMISS = 0x0130, + TxCol = 0x0132, + LineSt = 0x0134, + SelfSt = 0x0136, + BusSt = 0x0138, + Tdr = 0x013c, + + /* Initiate Transmit Registers */ + TxCmd = 0x0144, /* transmit command */ + TxLen = 0x0146, /* transmit length */ + + /* Address Filter Registers */ + IndAddr = 0x0158, /* individual address registers */ + + /* Frame Location */ + RxStatus = 0x0400, /* receive status */ + RxLen = 0x0402, /* receive length */ + RxFrame = 0x0404, /* receive frame location */ + TxFrame = 0x0a00, /* transmit frame location */ +}; + +enum { /* Ecr */ + Addr = 0x00ff, /* EEPROM word address (field) */ + Opcode = 0x0300, /* command opcode (field) */ +}; + +enum { /* Isq */ + Regnum = 0x003f, /* register number held by Isq (field) */ + IsqRxEvent = 0x04, + IsqTxEvent = 0x08, + IsqBufEvent = 0x0c, + IsqRxMiss = 0x10, + IsqTxCol = 0x12, + RegContent = 0xffc0, /* register data contents (field) */ +}; + +enum { /* RxCfg */ + Skip_1 = 0x0040, + StreamE = 0x0080, + RxOKiE = 0x0100, + RxDMAonly = 0x0200, + AutoRxDMAE = 0x0400, + BufferCRC = 0x0800, + CRCerroriE = 0x1000, + RuntiE = 0x2000, + ExtradataiE = 0x4000, +}; + +enum { /* RxEvent */ + IAHash = 0x0040, + Dribblebits = 0x0080, + RxOK = 0x0100, + Hashed = 0x0200, + IndividualAdr = 0x0400, + Broadcast = 0x0800, + CRCerror = 0x1000, + Runt = 0x2000, + Extradata = 0x4000, +}; + +enum { /* RxCtl */ + IAHashA = 0x0040, + PromiscuousA = 0x0080, + RxOKA = 0x0100, + MulticastA = 0x0200, + IndividualA = 0x0400, + BroadcastA = 0x0800, + CRCerrorA = 0x1000, + RuntA = 0x2000, + ExtradataA = 0x4000, +}; + +enum { /* TxCfg */ + LossofCRSiE = 0x0040, + SQEerroriE = 0x0080, + TxOKiE = 0x0100, + OutofWindowiE = 0x0200, + JabberiE = 0x0400, + AnycolliE = 0x0800, + Coll16iE = 0x8000, +}; + +enum { /* TxEvent */ + LossofCRS = 0x0040, + SQEerror = 0x0080, + TxOK = 0x0100, + OutofWindow = 0x0200, + Jabber = 0x0400, + NTxCols = 0x7800, /* number of Tx collisions (field) */ + coll16 = 0x8000, +}; + +enum { /* BufCfg */ + SWintX = 0x0040, + RxDMAiE = 0x0080, + Rdy4TxiE = 0x0100, + TxUnderruniE = 0x0200, + RxMissiE = 0x0400, + Rx128iE = 0x0800, + TxColOvfiE = 0x1000, + MissOvfloiE = 0x2000, + RxDestiE = 0x8000, +}; + +enum { /* BufEvent */ + SWint = 0x0040, + RxDMAFrame = 0x0080, + Rdy4Tx = 0x0100, + TxUnderrun = 0x0200, + RxMiss = 0x0400, + Rx128 = 0x0800, + RxDest = 0x8000, +}; + +enum { /* RxMiss */ + MissCount = 0xffc0, +}; + +enum { /* TxCol */ + ColCount = 0xffc0, +}; + +enum { /* LineCtl */ + SerRxOn = 0x0040, + SerTxOn = 0x0080, + Iface = 0x0300, /* (field) 01 - AUI, 00 - 10BASE-T, 10 - Auto select */ + ModBackoffE = 0x0800, + PolarityDis = 0x1000, + DefDis = 0x2000, + LoRxSquelch = 0x4000, +}; + +enum { /* LineSt */ + LinkOK = 0x0080, + AUI = 0x0100, + TenBT = 0x0200, + PolarityOK = 0x1000, + CRS = 0x4000, +}; + +enum { /* SelfCtl */ + RESET = 0x0040, + SWSuspend = 0x0100, + HWSleepE = 0x0200, + HWStandbyE = 0x0400, +}; + +enum { /* SelfSt */ + INITD = 0x0080, + SIBUSY = 0x0100, + EepromPresent = 0x0200, + EepromOK = 0x0400, + ElPresent = 0x0800, + EeSize = 0x1000, +}; + +enum { /* BusCtl */ + ResetRxDMA = 0x0040, + UseSA = 0x0200, + MemoryE = 0x0400, + DMABurst = 0x0800, + EnableIRQ = 0x8000, +}; + +enum { /* BusST */ + TxBidErr = 0x0080, + Rdy4TxNOW = 0x0100, +}; + +enum { /* TestCtl */ + FDX = 0x4000, /* full duplex */ +}; + +enum { /* TxCmd */ + TxStart = 0x00c0, /* bytes before transmit starts (field) */ + TxSt5 = 0x0000, /* start after 5 bytes */ + TxSt381 = 0x0040, /* start after 381 bytes */ + TxSt1021 = 0x0080, /* start after 1021 bytes */ + TxStAll = 0x00c0, /* start after the entire frame is in the cs8900 */ + Force = 0x0100, + Onecoll = 0x0200, + InhibitCRC = 0x1000, + TxPadDis = 0x2000, +}; + +static Queue *pendingTx[MaxEther]; + +static void +attach(Ctlr *ctlr) +{ + int reg; + + USED(ctlr); + /* enable transmit and receive */ + reg = regr(BusCtl); + regw(BusCtl, reg|EnableIRQ); + reg = regr(LineCtl); + regw(LineCtl, reg|SerRxOn|SerTxOn); +} + +static char pbuf[200]; +int +sprintx(void *f, char *to, int count) +{ + int i, printable; + char *start = to; + uchar *from = f; + + if(count < 0) { + print("BAD DATA COUNT %d\n", count); + return 0; + } + printable = 1; + if(count > 40) + count = 40; + for(i=0; i127) + printable = 0; + *to++ = '\''; + if(printable){ + memmove(to, from, count); + to += count; + }else{ + for(i=0; i0 && i%4==0) + *to++ = ' '; + sprint(to, "%2.2ux", from[i]); + to += 2; + } + } + *to++ = '\''; + *to = 0; + return to - start; +} + +static void +transmit(Ctlr *ctlr) +{ + int len, status; + Block *b; + + for(;;){ + /* is TxCmd pending ? - check */ + if(qlen(pendingTx[ctlr->ctlrno]) > 0) + break; + b = qget(ctlr->oq); + if(b == 0) + break; + len = BLEN(b); + regw(TxCmd, TxSt381); + regw(TxLen, len); + status = regr(BusSt); + if((status & Rdy4TxNOW) == 0) { + qbwrite(pendingTx[ctlr->ctlrno], b); + break; + } + /* + * Copy the packet to the transmit buffer. + */ + copyout(b->rp, len); + freeb(b); + } +} + +static void +interrupt(Ureg*, Ctlr *ctlr) +{ + int len, events, status; + Block *b; + Queue *q; + + while((events = regr(Isq)) != 0) { + status = events&RegContent; + + switch(events&Regnum) { + + case IsqBufEvent: + if(status&Rdy4Tx) { + if(qlen(pendingTx[ctlr->ctlrno]) > 0) + q = pendingTx[ctlr->ctlrno]; + else + q = ctlr->oq; + b = qget(q); + if(b == 0) + break; + len = BLEN(b); + copyout(b->rp, len); + freeb(b); + } else + if(status&TxUnderrun) { + print("TxUnderrun\n"); + } else + if(status&RxMiss) { + print("RxMiss\n"); + } else { + print("IsqBufEvent status = %ux\n", status); + } + break; + + case IsqRxEvent: + if(status&RxOK) { + len = regr(RxLen); + if((b = iallocb(len)) != 0) { + copyin(b->wp, len); + b->wp += len; + etheriq(ctlr, b, 1); + } + } else { + print("IsqRxEvent status = %ux\n", status); + } + break; + + case IsqTxEvent: + if(status&TxOK) { + if(qlen(pendingTx[ctlr->ctlrno]) > 0) + q = pendingTx[ctlr->ctlrno]; + else + q = ctlr->oq; + b = qget(q); + if(b == 0) + break; + len = BLEN(b); + regw(TxCmd, TxSt381); + regw(TxLen, len); +if((regr(BusSt) & Rdy4TxNOW) == 0) { + print("IsqTxEvent and Rdy4TxNow == 0\n"); +} + copyout(b->rp, len); + freeb(b); + } else { + print("IsqTxEvent status = %ux\n", status); + } + break; + case IsqRxMiss: + break; + case IsqTxCol: + break; + } + } +} + +int +cs8900reset(Ctlr* ctlr) +{ + int i, reg; + uchar ea[Eaddrlen]; + + ctlr->card.irq = V_ETHERNET; + pendingTx[ctlr->ctlrno] = qopen(16*1024, 1, 0, 0); + + /* + * If the Ethernet address is not set in the plan9.ini file + * a) try reading from the Puma board ROM. The ether address is found in + * bytes 4-9 of the ROM. The Teralogic Organizational Unique Id (OUI) + * is in bytes 4-6 and should be 00 10 8a. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ctlr->card.ea, Eaddrlen) == 0) { + uchar *rom = (uchar *)EPROM_BASE; + if(rom[4] != 0x00 || rom[5] != 0x10 || rom[6] != 0x8a) + panic("no ether address"); + memmove(ea, &rom[4], Eaddrlen); + } + memmove(ctlr->card.ea, ea, Eaddrlen); + + /* + * Identify the chip by reading the Pic register. + * The EISA registration number is in the low word + * and the product identification code in the high code. + * The ERN for Crystal Semiconductor is 0x630e. + * Bits 0-7 and 13-15 of the Pic should be zero for a CS8900. + */ + if(regIOr(Ern) != 0x630e || (regIOr(Pic) & 0xe0ff) != 0) + panic("no cs8900 found"); + + /* + * Reset the chip and ensure 16-bit mode operation + */ + regIOw(SelfCtl, RESET); + delay(10); + i=in8(PpPtr); USED(i); + i=in8(PpPtr+1); USED(i); + i=in8(PpPtr); USED(i); + i=in8(PpPtr+1); USED(i); + + /* + * Wait for initialisation and EEPROM reads to complete + */ + i=0; + for(;;) { + short st = regIOr(SelfSt); + if((st&SIBUSY) == 0 && st&INITD) + break; + if(i++ > 1000000) + panic("cs8900: initialisation failed"); + } + + /* + * Enable memory mode operation. + */ + regIOw(Mba, MemBase & 0xffff); + regIOw(Mba+2, MemBase >> 16); + regIOw(BusCtl, MemoryE|UseSA); + + /* + * Enable 10BASE-T half duplex, transmit in interrupt mode + */ + reg = regr(LineCtl); + regw(LineCtl, reg&~Iface); + reg = regr(TestCtl); + regw(TestCtl, reg&~FDX); + regw(BufCfg, Rdy4TxiE|TxUnderruniE|RxMissiE); + regw(TxCfg, TxOKiE|JabberiE|Coll16iE); + regw(RxCfg, RxOKiE); + regw(RxCtl, RxOKA|IndividualA|BroadcastA); + + for(i=0; icard.reset = cs8900reset; + ctlr->card.port = 0x300; + ctlr->card.attach = attach; + ctlr->card.transmit = transmit; + ctlr->card.intr = interrupt; + + print("Ether reset...\n");uartwait(); + + return 0; +} + diff --git a/os/boot/puma/flash.c b/os/boot/puma/flash.c new file mode 100644 index 00000000..be109f97 --- /dev/null +++ b/os/boot/puma/flash.c @@ -0,0 +1,226 @@ +#include "boot.h" + +typedef struct Flashdev Flashdev; +struct Flashdev { + uchar* base; + int size; + uchar* exec; + char* type; + char* config; + int conflen; +}; + +enum { + FLASHSEG = 256*1024, + CONFIGLIM = FLASHSEG, + BOOTOFF = FLASHSEG, + BOOTLEN = 3*FLASHSEG, /* third segment might be filsys */ + /* rest of flash is free */ +}; + +static Flashdev flash; + +/* + * configuration data is written between the bootstrap and + * the end of region 0. the region ends with allocation descriptors + * of the following form: + * + * byte order is big endian + * + * the last valid region found that starts with the string "#plan9.ini\n" is plan9.ini + */ +typedef struct Flalloc Flalloc; +struct Flalloc { + ulong check; /* checksum of data, or ~0 */ + ulong base; /* base of region; ~0 if unallocated, 0 if deleted */ + uchar len[3]; + uchar tag; /* see below */ + uchar sig[4]; +}; + +enum { + /* tags */ + Tdead= 0, + Tboot= 0x01, /* space reserved for boot */ + Tconf= 0x02, /* configuration data */ + Tnone= 0xFF, + + Noval= ~0, +}; + +static char flashsig[] = {0xF1, 0xA5, 0x5A, 0x1F}; +static char conftag[] = "#plan9.ini\n"; + +static ulong +checksum(uchar* p, int n) +{ + ulong s; + + for(s=0; --n >= 0;) + s += *p++; + return s; +} + +static int +validptr(Flalloc *ap, uchar *p) +{ + return p > (uchar*)end && p < (uchar*)ap; +} + +static int +flashcheck(Flalloc *ap, char **val, int *len) +{ + uchar *base; + int n; + + if(ap->base == Noval || ap->base >= FLASHSEG || ap->tag == Tnone) + return 0; + base = flash.base+ap->base; + if(!validptr(ap, base)) + return 0; + n = (((ap->len[0]<<8)|ap->len[1])<<8)|ap->len[2]; + if(n == 0xFFFFFF) + n = 0; + if(n < 0) + return 0; + if(n > 0 && !validptr(ap, base+n-1)) + return 0; + if(ap->check != Noval && checksum(base, n) != ap->check){ + print("flash: bad checksum\n"); + return 0; + } + *val = (char*)base; + *len = n; + return 1; +} + +int +flashinit(void) +{ + int f, n, len; + char *type, *val; + Flalloc *ap; + + flash.base = 0; + flash.exec = 0; + flash.type = 0; + /* TODO - check for presence and type */ +/* + * if((m->iomem->memc[0].base & 1) == 0){ + * print("flash: flash not present or not enabled\n"); + * return 0; + * } + * f = (m->bcsr[2]>>28)&0xF; + */ +f = 0; + switch(f){ + default: + print("flash boot: unknown or no flash\n"); + return 0; + case 4: n=8; type = "SM732x8"; break; + case 5: n=4; type = "SM732x8"; break; + case 6: n=8; type = "AMD29F0x0"; break; + case 7: n=4; type = "AMD29F0x0"; break; + case 8: n=2; type = "AMD29F0x0"; break; + } + flash.type = type; + flash.size = n*1024*1024; + flash.base = KADDR(FLASH_BASE); + flash.exec = flash.base + BOOTOFF; + flash.config = nil; + flash.conflen = 0; + + for(ap = (Flalloc*)(flash.base+CONFIGLIM)-1; memcmp(ap->sig, flashsig, 4) == 0; ap--){ + if(1) + print("conf #%8.8lux: #%x #%6.6lux\n", ap, ap->tag, ap->base); + if(ap->tag == Tconf && + flashcheck(ap, &val, &len) && + len >= sizeof(conftag)-1 && + memcmp(val, conftag, sizeof(conftag)-1) == 0){ + flash.config = val; + flash.conflen = len; + print("flash: found config %8.8lux(%d):\n%s\n", val, len, val); + } + } + if(flash.config){ + print("flash config %8.8lux(%d):\n%s\n", flash.config, flash.conflen, flash.config); + flash.config = nil; /* not that daring yet */ + }else + print("flash: no config\n"); + if(issqueezed(flash.exec) == E_MAGIC){ + print("flash: squeezed StrongARM kernel installed\n"); + return 1<<0; + } + if(GLLONG(flash.exec) == E_MAGIC){ + print("flash: unsqueezed stringARM kernel installed\n"); + return 1<<0; + } + flash.exec = 0; + print("flash: no StrongARM kernel in Flash\n"); + return 0; +} + +char* +flashconfig(int) +{ + return flash.config; +} + +int +flashbootable(int) +{ + return flash.exec != nil && (issqueezed(flash.exec) || GLLONG(flash.exec) == E_MAGIC); +} + +int +flashboot(int) +{ + ulong entry, addr; + void (*b)(void); + Exec *ep; + Block in; + long n; + uchar *p; + + if(flash.exec == 0) + return -1; + p = flash.exec; + if(GLLONG(p) == E_MAGIC){ + /* unsqueezed: copy data and perhaps text, then jump to it */ + ep = (Exec*)p; + entry = PADDR(GLLONG(ep->entry)); + p += sizeof(Exec); + addr = entry; + n = GLLONG(ep->text); + if(addr != (ulong)p){ + memmove((void*)addr, p, n); + print("text: %8.8lux <- %8.8lux [%ld]\n", addr, p, n); + } + p += n; + if(entry >= FLASH_BASE) + addr = 3*BY2PG; /* kernel text is in Flash, data in RAM */ + else + addr = PGROUND(addr+n); + n = GLLONG(ep->data); + memmove((void*)addr, p, n); + print("data: %8.8lux <- %8.8lux [%ld]\n", addr, p, n); + }else{ + in.data = p; + in.rp = in.data; + in.lim = p+BOOTLEN; + in.wp = in.lim; + n = unsqueezef(&in, &entry); + if(n < 0) + return -1; + } + print("entry=0x%lux\n", entry); + uartwait(); + /* scc2stop(); */ + /* + * Go to new code. It's up to the program to get its PC relocated to + * the right place. + */ + b = (void (*)(void))KADDR(PADDR(entry)); + (*b)(); + return -1; +} diff --git a/os/boot/puma/fns.h b/os/boot/puma/fns.h new file mode 100644 index 00000000..fd3d60d9 --- /dev/null +++ b/os/boot/puma/fns.h @@ -0,0 +1,111 @@ +void aamloop(int); +Alarm* alarm(int, void (*)(Alarm*), void*); +void alarminit(void); +int bootp(int, char*); +void cancel(Alarm*); +void checkalarms(void); +void clockinit(void); +void consinit(void); +void delay(int); +uchar* etheraddr(int); +int etherinit(void); +int etherrxpkt(int, Etherpkt*, int); +int ethertxpkt(int, Etherpkt*, int, int); +int flashboot(int); +int flashbootable(int); +char* flashconfig(int); +int flashinit(void); +char* getconf(char*); +int getcfields(char*, char**, int, char*); +int getstr(char*, char*, int, char*); +int hardinit(void); +long hardread(int, void*, long); +long hardseek(int, long); +long hardwrite(int, void*, long); +void* ialloc(ulong, int); +void idle(void); +int isaconfig(char*, int, ISAConf*); +int isgzipped(uchar*); +int issqueezed(uchar*); +void kbdinit(void); +void kbdchar(Queue*, int); +void machinit(void); +void meminit(void); +void microdelay(int); +void mmuinit(void); +uchar nvramread(int); +void outb(int, int); +void outs(int, ushort); +void outl(int, ulong); +void outsb(int, void*, int); +void outss(int, void*, int); +void outsl(int, void*, int); +void panic(char*, ...); +int optionsw(void); +int plan9boot(int, long (*)(int, long), long (*)(int, void*, long)); +Partition* setflashpart(int, char*); +Partition* sethardpart(int, char*); +Partition* setscsipart(int, char*); +void setvec(int, void (*)(Ureg*, void*), void*); +void screeninit(void); +void screenputs(char*, int); +void setr13(int, void*); +int splhi(void); +int spllo(void); +void splx(int); +void trapinit(void); +void uartspecial(int, int, Queue**, Queue**, void(*)(Queue*,int)); +void uartputs(char*, int); +void uartwait(void); +long unsqueezef(Block*, ulong*); + +#define GSHORT(p) (((p)[1]<<8)|(p)[0]) +#define GLONG(p) ((GSHORT(p+2)<<16)|GSHORT(p)) +#define GLSHORT(p) (((p)[0]<<8)|(p)[1]) +#define GLLONG(p) ((GLSHORT(p)<<16)|GLSHORT(p+2)) + +#define KADDR(a) ((void*)((ulong)(a)|KZERO)) +#define PADDR(a) ((ulong)(a)&~KZERO) + + +void mapinit(RMap*, Map*, int); +void mapfree(RMap*, ulong, int); +ulong mapalloc(RMap*, ulong, int, int); + +/* IBM bit field order */ +#define IBFEXT(v,a,b) (((ulong)(v)>>(32-(b)-1)) & ~(~0L<<(((b)-(a)+1)))) +#define IBIT(b) ((ulong)1<<(31-(b))) + +#define SIBIT(n) ((ushort)1<<(15-(n))) + +void* malloc(ulong); +void free(void*); + +extern Block* iallocb(int); +extern void freeb(Block*); +extern Queue* qopen(int, int, void (*)(void*), void*); +extern Block* qget(Queue*); +extern void qbwrite(Queue*, Block*); +extern long qlen(Queue*); +#define qpass qbwrite +extern void qbputc(Queue*, int); +extern int qbgetc(Queue*); + +int sio_inb(int); +void sio_outb(int, int); +void led(int); + +extern void _virqcall(void); +extern void _vfiqcall(void); +extern void _vundcall(void); +extern void _vsvccall(void); +extern void _vpabcall(void); +extern void _vdabcall(void); + +void flushIcache(void); +void writeBackDC(void); +void flushDcache(void); +void flushIcache(void); +void drainWBuffer(void); + +void pumainit(void); diff --git a/os/boot/puma/hard.c b/os/boot/puma/hard.c new file mode 100644 index 00000000..1f1104c3 --- /dev/null +++ b/os/boot/puma/hard.c @@ -0,0 +1,773 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#define DPRINT if(0)print + +typedef struct Drive Drive; +typedef struct Ident Ident; +typedef struct Controller Controller; + +enum +{ + /* ports */ + Pbase0= 0x1F0, /* primary */ + Pbase1= 0x170, /* secondary */ + Pbase2= 0x1E8, /* tertiary */ + Pbase3= 0x168, /* quaternary */ + Pdata= 0, /* data port (16 bits) */ + Perror= 1, /* error port (read) */ + Pprecomp= 1, /* buffer mode port (write) */ + Pcount= 2, /* sector count port */ + Psector= 3, /* sector number port */ + Pcyllsb= 4, /* least significant byte cylinder # */ + Pcylmsb= 5, /* most significant byte cylinder # */ + Pdh= 6, /* drive/head port */ + DHmagic= 0xA0, + DHslave= 0x10, + Pstatus= 7, /* status port (read) */ + Sbusy= (1<<7), + Sready= (1<<6), + Sdrq= (1<<3), + Serr= (1<<0), + Pcmd= 7, /* cmd port (write) */ + + /* commands */ + Crecal= 0x10, + Cread= 0x20, + Cwrite= 0x30, + Cident= 0xEC, + Cident2= 0xFF, /* pseudo command for post Cident interrupt */ + Csetbuf= 0xEF, + + /* file types */ + Qdir= 0, + + Timeout= 5, /* seconds to wait for things to complete */ + + NCtlr= 4, + NDrive= NCtlr*2, +}; + +/* + * ident sector from drive. this is from ANSI X3.221-1994 + */ +struct Ident +{ + ushort config; /* general configuration info */ + ushort cyls; /* # of cylinders (default) */ + ushort reserved0; + ushort heads; /* # of heads (default) */ + ushort b2t; /* unformatted bytes/track */ + ushort b2s; /* unformated bytes/sector */ + ushort s2t; /* sectors/track (default) */ + ushort reserved1[3]; +/* 10 */ + ushort serial[10]; /* serial number */ + ushort type; /* buffer type */ + ushort bsize; /* buffer size/512 */ + ushort ecc; /* ecc bytes returned by read long */ + ushort firm[4]; /* firmware revision */ + ushort model[20]; /* model number */ +/* 47 */ + ushort s2i; /* number of sectors/interrupt */ + ushort dwtf; /* double word transfer flag */ + ushort capabilities; + ushort reserved2; + ushort piomode; + ushort dmamode; + ushort cvalid; /* (cvald&1) if next 4 words are valid */ + ushort ccyls; /* current # cylinders */ + ushort cheads; /* current # heads */ + ushort cs2t; /* current sectors/track */ + ushort ccap[2]; /* current capacity in sectors */ + ushort cs2i; /* current number of sectors/interrupt */ +/* 60 */ + ushort lbasecs[2]; /* # LBA user addressable sectors */ + ushort dmasingle; + ushort dmadouble; +/* 64 */ + ushort reserved3[64]; + ushort vendor[32]; /* vendor specific */ + ushort reserved4[96]; +}; + +/* + * a hard drive + */ +struct Drive +{ + Controller *cp; + uchar driveno; + uchar dh; + + Disc; +}; + +/* + * a controller for 2 drives + */ +struct Controller +{ + int pbase; /* base port */ + uchar ctlrno; + + /* + * current operation + */ + int cmd; /* current command */ + char *buf; /* xfer buffer */ + int tcyl; /* target cylinder */ + int thead; /* target head */ + int tsec; /* target sector */ + int tbyte; /* target byte */ + int nsecs; /* length of transfer (sectors) */ + int sofar; /* bytes transferred so far */ + int status; + int error; + Drive *dp; /* drive being accessed */ +}; + +static int atactlrmask; +static Controller *atactlr[NCtlr]; +static int atadrivemask; +static Drive *atadrive[NDrive]; +static int pbase[NCtlr] = { + Pbase0, Pbase1, Pbase2, Pbase3, +}; + +static void hardintr(Ureg*, void*); +static long hardxfer(Drive*, Partition*, int, ulong, long); +static int hardident(Drive*); +static void hardsetbuf(Drive*, int); +static void hardpart(Drive*); +static int hardparams(Drive*); +static void hardrecal(Drive*); +static int hardprobe(Drive*, int, int, int); + +static void +atactlrprobe(int ctlrno, int irq) +{ + Controller *ctlr; + Drive *drive; + int driveno, port; + + if(atactlrmask & (1<pbase = port; + ctlr->ctlrno = ctlrno; + ctlr->buf = ialloc(Maxxfer, 0); + inb(ctlr->pbase+Pstatus); + setvec(irq, hardintr, ctlr); + + driveno = ctlrno*2; + atadrive[driveno] = ialloc(sizeof(Drive), 0); + drive = atadrive[driveno]; + drive->cp = ctlr; + drive->driveno = driveno; + drive->dh = DHmagic; + + driveno++; + atadrive[driveno] = ialloc(sizeof(Drive), 0); + drive = atadrive[driveno]; + drive->cp = ctlr; + drive->driveno = driveno; + drive->dh = DHmagic|DHslave; +} + +static Drive* +atadriveprobe(int driveno) +{ + Drive *drive; + int ctlrno; + ISAConf isa; + + ctlrno = driveno/2; + if(atactlr[ctlrno] == 0){ + if(atactlrmask & (1<online == 0){ + if(atadrivemask & (1<lba) + print("hd%d: LBA %d sectors, %ud bytes\n", + drive->driveno, drive->sectors, drive->cap); + else + print("hd%d: CHS %d/%d/%d %d bytes\n", + drive->driveno, drive->cyl, drive->heads, + drive->sectors, drive->cap); + drive->online = 1; + hardpart(drive); + hardsetbuf(drive, 1); + } + + return drive; +} + +int +hardinit(void) +{ + atactlrprobe(0, ATAvec0); + return 0xFF; +} + +long +hardseek(int driveno, long offset) +{ + Drive *drive; + + if((drive = atadriveprobe(driveno)) == 0) + return -1; + drive->offset = offset; + return offset; +} + +/* + * did an interrupt happen? + */ +static void +hardwait(Controller *cp) +{ + ulong start; + int x; + + x = spllo(); + for(start = m->ticks; TK2SEC(m->ticks - start) < Timeout && cp->cmd;) + if(cp->cmd == Cident2 && TK2SEC(m->ticks - start) >= 1) + break; + if(TK2SEC(m->ticks - start) >= Timeout){ + DPRINT("hardwait timed out %ux\n", inb(cp->pbase+Pstatus)); + hardintr(0, cp); + } + splx(x); +} + +Partition* +sethardpart(int driveno, char *p) +{ + Partition *pp; + Drive *dp; + + if((dp = atadriveprobe(driveno)) == 0) + return 0; + + for(pp = dp->p; pp < &dp->p[dp->npart]; pp++) + if(strcmp(pp->name, p) == 0){ + dp->current = pp; + return pp; + } + return 0; +} + +long +hardread(int driveno, void *a, long n) +{ + Drive *dp; + long rv, i; + int skip; + uchar *aa = a; + Partition *pp; + Controller *cp; + + if((dp = atadriveprobe(driveno)) == 0) + return 0; + + pp = dp->current; + if(pp == 0) + return -1; + cp = dp->cp; + + skip = dp->offset % dp->bytes; + for(rv = 0; rv < n; rv += i){ + i = hardxfer(dp, pp, Cread, dp->offset+rv-skip, n-rv+skip); + if(i == 0) + break; + if(i < 0) + return -1; + i -= skip; + if(i > n - rv) + i = n - rv; + memmove(aa+rv, cp->buf + skip, i); + skip = 0; + } + dp->offset += rv; + + return rv; +} + +/* + * wait for the controller to be ready to accept a command + */ +static int +cmdreadywait(Drive *drive) +{ + ulong end; + uchar dh, status; + Controller *ctlr; + + ctlr = drive->cp; + end = m->ticks+MS2TK(10)+1; + dh = (inb(ctlr->pbase+Pdh) & DHslave)^(drive->dh & DHslave); + + status = 0; + while(m->ticks < end){ + status = inb(ctlr->pbase+Pstatus); + if(status & Sbusy) + continue; + if(dh){ + outb(ctlr->pbase+Pdh, drive->dh); + dh = 0; + continue; + } + if(status & Sready) + return 0; + } + USED(status); + + DPRINT("hd%d: cmdreadywait failed %uX\n", drive->driveno, status); + outb(ctlr->pbase+Pdh, DHmagic); + return -1; +} + +/* + * transfer a number of sectors. hardintr will perform all the iterative + * parts. + */ +static long +hardxfer(Drive *dp, Partition *pp, int cmd, ulong start, long len) +{ + Controller *cp; + long lsec; + + if(dp->online == 0){ + DPRINT("disk not on line\n"); + return -1; + } + + if(cmd == Cwrite) + return -1; + + /* + * cut transfer size down to disk buffer size + */ + start = start / dp->bytes; + if(len > Maxxfer) + len = Maxxfer; + len = (len + dp->bytes - 1) / dp->bytes; + + /* + * calculate physical address + */ + cp = dp->cp; + lsec = start + pp->start; + if(lsec >= pp->end){ + DPRINT("read past end of partition\n"); + return 0; + } + if(dp->lba){ + cp->tsec = lsec & 0xff; + cp->tcyl = (lsec>>8) & 0xffff; + cp->thead = (lsec>>24) & 0xf; + } else { + cp->tcyl = lsec/(dp->sectors*dp->heads); + cp->tsec = (lsec % dp->sectors) + 1; + cp->thead = (lsec/dp->sectors) % dp->heads; + } + + /* + * can't xfer past end of disk + */ + if(lsec+len > pp->end) + len = pp->end - lsec; + cp->nsecs = len; + + if(cmdreadywait(dp) < 0) + return -1; + + /* + * start the transfer + */ + cp->cmd = cmd; + cp->dp = dp; + cp->sofar = 0; + cp->status = 0; + DPRINT("xfer:\ttcyl %d, tsec %d, thead %d\n", cp->tcyl, cp->tsec, cp->thead); + DPRINT("\tnsecs %d, sofar %d\n", cp->nsecs, cp->sofar); + outb(cp->pbase+Pcount, cp->nsecs); + outb(cp->pbase+Psector, cp->tsec); + outb(cp->pbase+Pdh, dp->dh | (dp->lba<<6) | cp->thead); + outb(cp->pbase+Pcyllsb, cp->tcyl); + outb(cp->pbase+Pcylmsb, cp->tcyl>>8); + outb(cp->pbase+Pcmd, cmd); + + hardwait(cp); + + if(cp->status & Serr){ + DPRINT("hd%d err: status %lux, err %lux\n", + dp->driveno, cp->status, cp->error); + DPRINT("\ttcyl %d, tsec %d, thead %d\n", + cp->tcyl, cp->tsec, cp->thead); + DPRINT("\tnsecs %d, sofar %d\n", cp->nsecs, cp->sofar); + return -1; + } + + return cp->nsecs*dp->bytes; +} + +/* + * set read ahead mode (1 == on, 0 == off) + */ +static void +hardsetbuf(Drive *dp, int on) +{ + Controller *cp = dp->cp; + + if(cmdreadywait(dp) < 0) + return; + + cp->cmd = Csetbuf; + /* BUG: precomp varies by hard drive...this is safari-specific? */ + outb(cp->pbase+Pprecomp, on ? 0xAA : 0x55); + outb(cp->pbase+Pdh, dp->dh); + outb(cp->pbase+Pcmd, Csetbuf); + + hardwait(cp); +} + +static int +isatapi(Drive *drive) +{ + Controller *cp; + + cp = drive->cp; + outb(cp->pbase+Pdh, drive->dh); + microdelay(1); + if(inb(cp->pbase+Pstatus)) + return 0; + if(inb(cp->pbase+Pcylmsb) != 0xEB || inb(cp->pbase+Pcyllsb) != 0x14) + return 0; + return 1; +} + +/* + * get parameters from the drive + */ +static int +hardident(Drive *dp) +{ + Controller *cp; + Ident *ip; + + dp->bytes = 512; + cp = dp->cp; + + if(isatapi(dp) || cmdreadywait(dp) < 0) + return -1; + + cp->nsecs = 1; + cp->sofar = 0; + cp->cmd = Cident; + cp->dp = dp; + outb(cp->pbase+Pdh, dp->dh); + outb(cp->pbase+Pcmd, Cident); + + hardwait(cp); + + if(cp->status & Serr) + return -1; + + hardwait(cp); + + ip = (Ident*)cp->buf; + DPRINT("LBA%d: %lud\n", + ip->capabilities & (1<<9) == 1, (ip->lbasecs[0]) | (ip->lbasecs[1]<<16)); + DPRINT("DEF: %ud/%ud/%ud\nMAP %ud/%ud/%ud\n", + ip->cyls, ip->heads, ip->s2t, + ip->ccyls, ip->cheads, ip->cs2t); + if(ip->capabilities & (1<<9)){ + dp->lba = 1; + dp->sectors = (ip->lbasecs[0]) | (ip->lbasecs[1]<<16); + dp->cap = dp->bytes * dp->sectors; +/*print("\nata%d model %s with %d lba sectors\n", dp->driveno, id, dp->sectors);/**/ + } else { + dp->lba = 0; + + /* use default (unformatted) settings */ + dp->cyl = ip->cyls; + dp->heads = ip->heads; + dp->sectors = ip->s2t; +/*print("\nata%d model %s with default %d cyl %d head %d sec\n", dp->driveno, + id, dp->cyl, dp->heads, dp->sectors);/**/ + + if(ip->cvalid&(1<<0)){ + /* use current settings */ + dp->cyl = ip->ccyls; + dp->heads = ip->cheads; + dp->sectors = ip->cs2t; +/*print("\tchanged to %d cyl %d head %d sec\n", dp->cyl, dp->heads, dp->sectors);/**/ + } + dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors; + } + + return 0; +} + +/* + * probe the given sector to see if it exists + */ +static int +hardprobe(Drive *dp, int cyl, int sec, int head) +{ + Controller *cp; + + cp = dp->cp; + if(cmdreadywait(dp) < 0) + return -1; + + /* + * start the transfer + */ + cp->cmd = Cread; + cp->dp = dp; + cp->sofar = 0; + cp->nsecs = 1; + cp->status = 0; + outb(cp->pbase+Pcount, 1); + outb(cp->pbase+Psector, sec+1); + outb(cp->pbase+Pdh, dp->dh | (dp->lba<<6) | head); + outb(cp->pbase+Pcyllsb, cyl); + outb(cp->pbase+Pcylmsb, cyl>>8); + outb(cp->pbase+Pcmd, Cread); + + hardwait(cp); + + if(cp->status & Serr) + return -1; + + return 0; +} + +/* + * figure out the drive parameters + */ +static int +hardparams(Drive *dp) +{ + int i, hi, lo; + + /* + * first try the easy way, ask the drive and make sure it + * isn't lying. + */ + dp->bytes = 512; + if(hardident(dp) < 0) + return -1; + if(dp->lba){ + i = dp->sectors - 1; + if(hardprobe(dp, (i>>8)&0xffff, (i&0xff)-1, (i>>24)&0xf) == 0) + return 0; + } else { + if(hardprobe(dp, dp->cyl-1, dp->sectors-1, dp->heads-1) == 0) + return 0; + } + + DPRINT("hardparam: cyl %d sectors %d heads %d\n", dp->cyl, dp->sectors, dp->heads); + /* + * the drive lied, determine parameters by seeing which ones + * work to read sectors. + */ + dp->lba = 0; + for(i = 0; i < 16; i++) + if(hardprobe(dp, 0, 0, i) < 0) + break; + dp->heads = i; + for(i = 0; i < 64; i++) + if(hardprobe(dp, 0, i, 0) < 0) + break; + dp->sectors = i; + for(i = 512; ; i += 512) + if(hardprobe(dp, i, dp->sectors-1, dp->heads-1) < 0) + break; + lo = i - 512; + hi = i; + for(; hi-lo > 1;){ + i = lo + (hi - lo)/2; + if(hardprobe(dp, i, dp->sectors-1, dp->heads-1) < 0) + hi = i; + else + lo = i; + } + dp->cyl = lo + 1; + dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors; + + if(dp->cyl == 0 || dp->heads == 0 || dp->sectors == 0 || dp->cap == 0) + return -1; + + return 0; +} + +/* + * read partition table. The partition table is just ascii strings. + */ +#define MAGIC "plan9 partitions" +static void +hardpart(Drive *dp) +{ + Partition *pp; + Controller *cp; + char *field[3], *line[Npart+1], *p, buf[NAMELEN]; + ulong n; + int i; + + cp = dp->cp; + + /* + * we always have a partition for the whole disk + * and one for the partition table + */ + pp = &dp->p[0]; + strcpy(pp->name, "disk"); + pp->start = 0; + pp->end = dp->cap / dp->bytes; + pp++; + strcpy(pp->name, "partition"); + pp->start = dp->p[0].end - 1; + pp->end = dp->p[0].end; + dp->npart = 2; + + /* + * Check if the partitions are described in plan9.ini. + * If not, read the disc. + */ + sprint(buf, "hd%dpartition", dp->driveno); + if((p = getconf(buf)) == 0){ + /* + * read last sector from disk, null terminate. This used + * to be the sector we used for the partition tables. + * However, this sector is special on some PC's so we've + * started to use the second last sector as the partition + * table instead. To avoid reconfiguring all our old systems + * we first look to see if there is a valid partition + * table in the last sector. If so, we use it. Otherwise + * we switch to the second last. + */ + hardxfer(dp, pp, Cread, 0, dp->bytes); + cp->buf[dp->bytes-1] = 0; + n = getcfields(cp->buf, line, Npart+1, "\n"); + if(n == 0 || strncmp(line[0], MAGIC, sizeof(MAGIC)-1)){ + dp->p[0].end--; + dp->p[1].start--; + dp->p[1].end--; + hardxfer(dp, pp, Cread, 0, dp->bytes); + cp->buf[dp->bytes-1] = 0; + n = getcfields(cp->buf, line, Npart+1, "\n"); + } + } + else{ + strcpy(cp->buf, p); + n = getcfields(cp->buf, line, Npart+1, "\n"); + } + + /* + * parse partition table. + */ + if(n && strncmp(line[0], MAGIC, sizeof(MAGIC)-1) == 0){ + for(i = 1; i < n; i++){ + pp++; + if(getcfields(line[i], field, 3, " ") != 3) + break; + strncpy(pp->name, field[0], NAMELEN); + pp->start = strtoul(field[1], 0, 0); + pp->end = strtoul(field[2], 0, 0); + if(pp->start > pp->end || pp->start >= dp->p[0].end) + break; + dp->npart++; + } + } + return; +} + +/* + * we get an interrupt for every sector transferred + */ +static void +hardintr(Ureg*, void *arg) +{ + Controller *cp; + Drive *dp; + long loop; + + cp = arg; + dp = cp->dp; + + loop = 0; + while((cp->status = inb(cp->pbase+Pstatus)) & Sbusy) + if(++loop > 100000){ + print("hardintr 0x%lux\n", cp->status); + break; + } + switch(cp->cmd){ + case Cread: + case Cident: + if(cp->status & Serr){ + cp->cmd = 0; + cp->error = inb(cp->pbase+Perror); + return; + } + loop = 0; + while((inb(cp->pbase+Pstatus) & Sdrq) == 0) + if(++loop > 100000){ + print("hardintr 2 cmd %ux status %ux", + cp->cmd, inb(cp->pbase+Pstatus)); + cp->cmd = 0; + return; + } + inss(cp->pbase+Pdata, &cp->buf[cp->sofar*dp->bytes], + dp->bytes/2); + cp->sofar++; + if(cp->sofar >= cp->nsecs){ + if(cp->cmd == Cident && (cp->status & Sready) == 0) + cp->cmd = Cident2; /* sometimes we get a second intr */ + else + cp->cmd = 0; + inb(cp->pbase+Pstatus); + } + break; + case Csetbuf: + case Cident2: + cp->cmd = 0; + break; + default: + cp->cmd = 0; + break; + } +} diff --git a/os/boot/puma/io.h b/os/boot/puma/io.h new file mode 100644 index 00000000..f4e66700 --- /dev/null +++ b/os/boot/puma/io.h @@ -0,0 +1,3 @@ +#define inb(port) sio_inb(port) +#define outb(port, data) sio_outb(port, data) + diff --git a/os/boot/puma/ip.h b/os/boot/puma/ip.h new file mode 100644 index 00000000..a39b5b4b --- /dev/null +++ b/os/boot/puma/ip.h @@ -0,0 +1,98 @@ +typedef struct Udphdr Udphdr; +struct Udphdr +{ + uchar d[6]; /* Ethernet destination */ + uchar s[6]; /* Ethernet source */ + uchar type[2]; /* Ethernet packet type */ + + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + + /* Udp pseudo ip really starts here */ + uchar ttl; + uchar udpproto; /* Protocol */ + uchar udpplen[2]; /* Header plus data length */ + uchar udpsrc[4]; /* Ip source */ + uchar udpdst[4]; /* Ip destination */ + uchar udpsport[2]; /* Source port */ + uchar udpdport[2]; /* Destination port */ + uchar udplen[2]; /* data length */ + uchar udpcksum[2]; /* Checksum */ +}; + +typedef struct Etherhdr Etherhdr; +struct Etherhdr +{ + uchar d[6]; + uchar s[6]; + uchar type[2]; + + /* Now we have the ip fields */ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar ttl; /* Time to live */ + uchar proto; /* Protocol */ + uchar cksum[2]; /* Header checksum */ + uchar src[4]; /* Ip source */ + uchar dst[4]; /* Ip destination */ +}; + +enum +{ + IP_VER = 0x40, + IP_HLEN = 0x05, + UDP_EHSIZE = 22, + UDP_PHDRSIZE = 12, + UDP_HDRSIZE = 20, + ETHER_HDR = 14, + IP_UDPPROTO = 17, + ET_IP = 0x800, + Bcastip = 0xffffffff, + BPportsrc = 68, + BPportdst = 67, + TFTPport = 69, + Timeout = 5000, /* milliseconds */ + Bootrequest = 1, + Bootreply = 2, + Tftp_READ = 1, + Tftp_WRITE = 2, + Tftp_DATA = 3, + Tftp_ACK = 4, + Tftp_ERROR = 5, + Segsize = 512, + TFTPSZ = Segsize+10, +}; + +typedef struct Bootp Bootp; +struct Bootp +{ + uchar op; /* opcode */ + uchar htype; /* hardware type */ + uchar hlen; /* hardware address len */ + uchar hops; /* hops */ + uchar xid[4]; /* a random number */ + uchar secs[2]; /* elapsed snce client started booting */ + uchar pad[2]; + uchar ciaddr[4]; /* client IP address (client tells server) */ + uchar yiaddr[4]; /* client IP address (server tells client) */ + uchar siaddr[4]; /* server IP address */ + uchar giaddr[4]; /* gateway IP address */ + uchar chaddr[16]; /* client hardware address */ + char sname[64]; /* server host name (optional) */ + char file[128]; /* boot file name */ + char vend[128]; /* vendor-specific goo */ +}; + +typedef struct Netaddr Netaddr; +struct Netaddr +{ + ulong ip; + ushort port; + char ea[Eaddrlen]; +}; diff --git a/os/boot/puma/kbd.c b/os/boot/puma/kbd.c new file mode 100644 index 00000000..55fab0f2 --- /dev/null +++ b/os/boot/puma/kbd.c @@ -0,0 +1,482 @@ +#include "boot.h" + + +enum { + Data= 0x60, /* data port */ + + Status= 0x64, /* status port */ + Inready= 0x01, /* input character ready */ + Outbusy= 0x02, /* output busy */ + Sysflag= 0x04, /* system flag */ + Cmddata= 0x08, /* cmd==0, data==1 */ + Inhibit= 0x10, /* keyboard/mouse inhibited */ + Minready= 0x20, /* mouse character ready */ + Rtimeout= 0x40, /* general timeout */ + Parity= 0x80, + + Cmd= 0x64, /* command port (write only) */ + + Spec= 0x80, + + PF= Spec|0x20, /* num pad function key */ + View= Spec|0x00, /* view (shift window up) */ + KF= Spec|0x40, /* function key */ + Shift= Spec|0x60, + Break= Spec|0x61, + Ctrl= Spec|0x62, + Latin= Spec|0x63, + Caps= Spec|0x64, + Num= Spec|0x65, + No= Spec|0x7F, /* no mapping */ + + Home= KF|13, + Up= KF|14, + Pgup= KF|15, + Print= KF|16, + Left= View, + Right= View, + End= '\r', + Down= View, + Pgdown= View, + Ins= KF|20, + Del= 0x7F, +}; + +uchar kbtab[] = +{ +[0x00] No, 0x1b, '1', '2', '3', '4', '5', '6', +[0x08] '7', '8', '9', '0', '-', '=', '\b', '\t', +[0x10] 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', +[0x18] 'o', 'p', '[', ']', '\n', Ctrl, 'a', 's', +[0x20] 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', +[0x28] '\'', '`', Shift, '\\', 'z', 'x', 'c', 'v', +[0x30] 'b', 'n', 'm', ',', '.', '/', Shift, No, +[0x38] Latin, ' ', Caps, KF|1, KF|2, KF|3, KF|4, KF|5, +[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, KF|12, Home, +[0x48] No, No, No, No, No, No, No, No, +[0x50] No, No, No, No, No, No, No, KF|11, +[0x58] KF|12, No, No, No, No, No, No, No, +}; + +uchar kbtabshift[] = +{ +[0x00] No, 0x1b, '!', '@', '#', '$', '%', '^', +[0x08] '&', '*', '(', ')', '_', '+', '\b', '\t', +[0x10] 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', +[0x18] 'O', 'P', '{', '}', '\n', Ctrl, 'A', 'S', +[0x20] 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', +[0x28] '"', '~', Shift, '|', 'Z', 'X', 'C', 'V', +[0x30] 'B', 'N', 'M', '<', '>', '?', Shift, No, +[0x38] Latin, ' ', Caps, KF|1, KF|2, KF|3, KF|4, KF|5, +[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, KF|12, Home, +[0x48] No, No, No, No, No, No, No, No, +[0x50] No, No, No, No, No, No, No, KF|11, +[0x58] KF|12, No, No, No, No, No, No, No, +}; + +uchar kbtabesc1[] = +{ +[0x00] No, No, No, No, No, No, No, No, +[0x08] No, No, No, No, No, No, No, No, +[0x10] No, No, No, No, No, No, No, No, +[0x18] No, No, No, No, No, Ctrl, No, No, +[0x20] No, No, No, No, No, No, No, No, +[0x28] No, No, No, No, No, No, No, No, +[0x30] No, No, No, No, No, No, No, Print, +[0x38] Latin, No, No, No, No, No, No, No, +[0x40] No, No, No, No, No, No, Break, Home, +[0x48] Up, Pgup, No, Down, No, Right, No, End, +[0x50] Left, Pgdown, Ins, Del, No, No, No, No, +[0x58] No, No, No, No, No, No, No, No, +}; + +struct latin +{ + uchar l; + char c[2]; +}latintab[] = { + L'¡', "!!", /* spanish initial ! */ + L'¢', "c|", /* cent */ + L'¢', "c$", /* cent */ + L'£', "l$", /* pound sterling */ + L'¤', "g$", /* general currency */ + L'¥', "y$", /* yen */ + L'¥', "j$", /* yen */ + L'¦', "||", /* broken vertical bar */ + L'§', "SS", /* section symbol */ + L'¨', "\"\"", /* dieresis */ + L'©', "cr", /* copyright */ + L'©', "cO", /* copyright */ + L'ª', "sa", /* super a, feminine ordinal */ + L'«', "<<", /* left angle quotation */ + L'¬', "no", /* not sign, hooked overbar */ + L'­', "--", /* soft hyphen */ + L'®', "rg", /* registered trademark */ + L'¯', "__", /* macron */ + L'°', "s0", /* degree (sup o) */ + L'±', "+-", /* plus-minus */ + L'²', "s2", /* sup 2 */ + L'³', "s3", /* sup 3 */ + L'´', "''", /* grave accent */ + L'µ', "mu", /* mu */ + L'¶', "pg", /* paragraph (pilcrow) */ + L'·', "..", /* centered . */ + L'¸', ",,", /* cedilla */ + L'¹', "s1", /* sup 1 */ + L'º', "so", /* sup o */ + L'»', ">>", /* right angle quotation */ + L'¼', "14", /* 1/4 */ + L'½', "12", /* 1/2 */ + L'¾', "34", /* 3/4 */ + L'¿', "??", /* spanish initial ? */ + L'À', "A`", /* A grave */ + L'Á', "A'", /* A acute */ + L'Â', "A^", /* A circumflex */ + L'Ã', "A~", /* A tilde */ + L'Ä', "A\"", /* A dieresis */ + L'Ä', "A:", /* A dieresis */ + L'Å', "Ao", /* A circle */ + L'Å', "AO", /* A circle */ + L'Æ', "Ae", /* AE ligature */ + L'Æ', "AE", /* AE ligature */ + L'Ç', "C,", /* C cedilla */ + L'È', "E`", /* E grave */ + L'É', "E'", /* E acute */ + L'Ê', "E^", /* E circumflex */ + L'Ë', "E\"", /* E dieresis */ + L'Ë', "E:", /* E dieresis */ + L'Ì', "I`", /* I grave */ + L'Í', "I'", /* I acute */ + L'Î', "I^", /* I circumflex */ + L'Ï', "I\"", /* I dieresis */ + L'Ï', "I:", /* I dieresis */ + L'Ð', "D-", /* Eth */ + L'Ñ', "N~", /* N tilde */ + L'Ò', "O`", /* O grave */ + L'Ó', "O'", /* O acute */ + L'Ô', "O^", /* O circumflex */ + L'Õ', "O~", /* O tilde */ + L'Ö', "O\"", /* O dieresis */ + L'Ö', "O:", /* O dieresis */ + L'Ö', "OE", /* O dieresis */ + L'Ö', "Oe", /* O dieresis */ + L'×', "xx", /* times sign */ + L'Ø', "O/", /* O slash */ + L'Ù', "U`", /* U grave */ + L'Ú', "U'", /* U acute */ + L'Û', "U^", /* U circumflex */ + L'Ü', "U\"", /* U dieresis */ + L'Ü', "U:", /* U dieresis */ + L'Ü', "UE", /* U dieresis */ + L'Ü', "Ue", /* U dieresis */ + L'Ý', "Y'", /* Y acute */ + L'Þ', "P|", /* Thorn */ + L'Þ', "Th", /* Thorn */ + L'Þ', "TH", /* Thorn */ + L'ß', "ss", /* sharp s */ + L'à', "a`", /* a grave */ + L'á', "a'", /* a acute */ + L'â', "a^", /* a circumflex */ + L'ã', "a~", /* a tilde */ + L'ä', "a\"", /* a dieresis */ + L'ä', "a:", /* a dieresis */ + L'å', "ao", /* a circle */ + L'æ', "ae", /* ae ligature */ + L'ç', "c,", /* c cedilla */ + L'è', "e`", /* e grave */ + L'é', "e'", /* e acute */ + L'ê', "e^", /* e circumflex */ + L'ë', "e\"", /* e dieresis */ + L'ë', "e:", /* e dieresis */ + L'ì', "i`", /* i grave */ + L'í', "i'", /* i acute */ + L'î', "i^", /* i circumflex */ + L'ï', "i\"", /* i dieresis */ + L'ï', "i:", /* i dieresis */ + L'ð', "d-", /* eth */ + L'ñ', "n~", /* n tilde */ + L'ò', "o`", /* o grave */ + L'ó', "o'", /* o acute */ + L'ô', "o^", /* o circumflex */ + L'õ', "o~", /* o tilde */ + L'ö', "o\"", /* o dieresis */ + L'ö', "o:", /* o dieresis */ + L'ö', "oe", /* o dieresis */ + L'÷', "-:", /* divide sign */ + L'ø', "o/", /* o slash */ + L'ù', "u`", /* u grave */ + L'ú', "u'", /* u acute */ + L'û', "u^", /* u circumflex */ + L'ü', "u\"", /* u dieresis */ + L'ü', "u:", /* u dieresis */ + L'ü', "ue", /* u dieresis */ + L'ý', "y'", /* y acute */ + L'þ', "th", /* thorn */ + L'þ', "p|", /* thorn */ + L'ÿ', "y\"", /* y dieresis */ + L'ÿ', "y:", /* y dieresis */ + 0, 0, +}; + +enum +{ + /* controller command byte */ + Cscs1= (1<<6), /* scan code set 1 */ + Cmousedis= (1<<5), /* mouse disable */ + Ckbddis= (1<<4), /* kbd disable */ + Csf= (1<<2), /* system flag */ + Cmouseint= (1<<1), /* mouse interrupt enable */ + Ckbdint= (1<<0), /* kbd interrupt enable */ +}; + +static uchar ccc; + +int +latin1(int k1, int k2) +{ + struct latin *l; + + for(l=latintab; l->l; l++) + if(k1==l->c[0] && k2==l->c[1]) + return l->l; + return 0; +} + +/* + * wait for output no longer busy + */ +static int +outready(void) +{ + int tries; + + for(tries = 0; (inb(Status) & Outbusy); tries++){ + if(tries > 500) + return -1; + delay(2); + } + return 0; +} + +/* + * wait for input + */ +static int +inready(void) +{ + int tries; + + for(tries = 0; !(inb(Status) & Inready); tries++){ + if(tries > 500) + return -1; + delay(2); + } + return 0; +} + +/* + * ask 8042 to enable the use of address bit 20 + */ +void +i8042a20(void) +{ + outready(); + outb(Cmd, 0xD1); + outready(); + outb(Data, 0xDF); + outready(); +} + +/* + * ask 8042 to reset the machine + */ +void +i8042reset(void) +{ + ushort *s = (ushort*)(KZERO|0x472); + int i, x; + + *s = 0x1234; /* BIOS warm-boot flag */ + + outready(); + outb(Cmd, 0xFE); /* pulse reset line (means resend on AT&T machines) */ + outready(); + + /* + * Pulse it by hand (old somewhat reliable) + */ + x = 0xDF; + for(i = 0; i < 5; i++){ + x ^= 1; + outready(); + outb(Cmd, 0xD1); + outready(); + outb(Data, x); /* toggle reset */ + delay(100); + } +} + +/* + * keyboard interrupt + */ +int +kbdintr0(void) +{ + int s, c; + static int esc1, esc2; + static int shift; + static int caps; + static int ctl; + static int num; + static int lstate, k1, k2; + int keyup; + + /* + * get status + */ + s = inb(Status); + if(!(s&Inready)) + return -1; + + /* + * get the character + */ + c = inb(Data); + + /* + * e0's is the first of a 2 character sequence + */ + if(c == 0xe0){ + esc1 = 1; + return 0; + } else if(c == 0xe1){ + esc2 = 2; + return 0; + } + + keyup = c&0x80; + c &= 0x7f; + if(c > sizeof kbtab){ + print("unknown key %ux\n", c|keyup); + kbdchar(0, k1); + return 0; + } + + if(esc1){ + c = kbtabesc1[c]; + esc1 = 0; + kbdchar(0, c); + return 0; + } else if(esc2){ + esc2--; + return 0; + } else if(shift) + c = kbtabshift[c]; + else + c = kbtab[c]; + + if(caps && c<='z' && c>='a') + c += 'A' - 'a'; + + /* + * keyup only important for shifts + */ + if(keyup){ + switch(c){ + case Shift: + shift = 0; + break; + case Ctrl: + ctl = 0; + break; + } + return 0; + } + + /* + * normal character + */ + if(!(c & Spec)){ + if(ctl) + c &= 0x1f; + switch(lstate){ + case 1: + k1 = c; + lstate = 2; + return 0; + case 2: + k2 = c; + lstate = 0; + c = latin1(k1, k2); + if(c == 0){ + kbdchar(0, k1); + c = k2; + } + /* fall through */ + default: + break; + } + } else { + switch(c){ + case Caps: + caps ^= 1; + return 0; + case Num: + num ^= 1; + return 0; + case Shift: + shift = 1; + return 0; + case Latin: + lstate = 1; + return 0; + case Ctrl: + ctl = 1; + return 0; + } + } + kbdchar(0, c); + return 0; +} + +static void +kbdintr(Ureg*, void*) +{ + while(kbdintr0() == 0) + ; +} + +static char *initfailed = "kbd init failed\n"; + +void +kbdinit(void) +{ + int c; + + /* wait for a quiescent controller */ + while((c = inb(Status)) & (Outbusy | Inready)) + if(c & Inready) + inb(Data); + + /* get current controller command byte */ + outb(Cmd, 0x20); + if(inready() < 0){ + print("kbdinit: can't read ccc\n"); + ccc = 0; + } else + ccc = inb(Data); + + /* enable kbd xfers and interrupts */ + ccc &= ~Ckbddis; + ccc |= Csf | Ckbdint | Cscs1; + if(outready() < 0) + print(initfailed); + outb(Cmd, 0x60); + if(outready() < 0) + print(initfailed); + outb(Data, ccc); + if(outready() < 0) + print(initfailed); + + setvec(V_KEYBOARD, kbdintr, 0); +} diff --git a/os/boot/puma/l.s b/os/boot/puma/l.s new file mode 100644 index 00000000..a4679fc2 --- /dev/null +++ b/os/boot/puma/l.s @@ -0,0 +1,427 @@ +/* + * File: l.s + * Purpose: + * Puma Board StrongARM 110 Architecture Specific Assembly + * + */ + +#include "mem.h" +#include "armv4.h" +#include "puma.h" + +#define DRAMWAIT 100000 /* 3.125μsec per iteration */ +#define TL750R(r) (TL750_BASE+(r)*4) + +#define BOOTBASE 0x00200000 + +TEXT _main(SB),1,$-4 + MOVW R15, R7 /* save PC on entry */ + +/* + * initialise DRAM controller on the TL750 (SDRAM mode) + */ + MOVW $DRAMWAIT, R0 /* wait 312 ms after reset before touching DRAM */ +dram1: + SUB.S $1, R0 + BNE dram1 + + MOVW $TL750R(0x103), R0 /* DMC_DELAY */ + MOVW $0x03333333, R1 /* DRAM timing parameters */ + MOVW R1, (R0) + + MOVW $TL750R(0x101), R0 /* DMC_SDRAM */ + MOVW $0x03133011, R1 /* SDRAM parameters for Puma */ + MOVW R1, (R0) + + MOVW $DRAMWAIT, R0 /* wait 312 ms for initialisation */ +dram2: + SUB.S $1, R0 + BNE dram2 + + MOVW $setR12(SB),R12 + +/* + * copy bootstrap to final location in DRAM + */ + MOVW R7, baddr(SB) + MOVW $(BOOTBASE+8), R0 + CMP R0, R7 + BEQ inplace + MOVW $((128*1024)/4), R6 +copyboot: + MOVW.P 4(R7), R5 + MOVW.P R5, 4(R0) + SUB.S $1, R6 + BNE copyboot + MOVW $bootrel(SB), R7 + MOVW R7, R15 + +TEXT bootrel(SB), $-4 + +/* + * set C environment and invoke main + */ +inplace: + MOVW $mach0(SB),R13 + MOVW R13,m(SB) + ADD $(MACHSIZE-12),R13 + + /* disable MMU activity */ + BL mmuctlregr(SB) + BIC $(CpCmmu|CpCDcache|CpCwb|CpCIcache), R0 + BL mmuctlregw(SB) + + BL main(SB) +loop: + B loop + +TEXT idle(SB),$0 + RET + +/* + * basic timing loop to determine CPU frequency + */ +TEXT aamloop(SB), $-4 /* 3 */ +_aamloop: + MOVW R0, R0 /* 1 */ + MOVW R0, R0 /* 1 */ + MOVW R0, R0 /* 1 */ + SUB $1, R0 /* 1 */ + CMP $0, R0 /* 1 */ + BNE _aamloop /* 3 */ + RET /* 3 */ + +/* + * Function: setr13( mode, pointer ) + * Purpose: + * Sets the stack pointer for a particular mode + */ + +TEXT setr13(SB), $-4 + MOVW 4(FP), R1 + + MOVW CPSR, R2 + BIC $PsrMask, R2, R3 + ORR R0, R3 + MOVW R3, CPSR + + MOVW R13, R0 + MOVW R1, R13 + + MOVW R2, CPSR + + RET + +/* + * Function: _vundcall + * Purpose: + * Undefined Instruction Trap Handler + * + */ + +TEXT _vundcall(SB), $-4 +_vund: + MOVM.DB [R0-R3], (R13) + MOVW $PsrMund, R0 + B _vswitch + +/* + * Function: _vsvccall + * Purpose: + * Reset or SWI Handler + * + */ + +TEXT _vsvccall(SB), $-4 +_vsvc: + SUB $12, R13 + MOVW R14, 8(R13) + MOVW CPSR, R14 + MOVW R14, 4(R13) + MOVW $PsrMsvc, R14 + MOVW R14, (R13) + B _vsaveu + +/* + * Function: _pabcall + * Purpose: + * Prefetch Abort Trap Handler + * + */ + +TEXT _vpabcall(SB), $-4 +_vpab: + MOVM.DB [R0-R3], (R13) + MOVW $PsrMabt, R0 + B _vswitch + +/* + * Function: _vdabcall + * Purpose: + * Data Abort Trap Handler + * + */ + +TEXT _vdabcall(SB), $-4 +_vdab: + MOVM.DB [R0-R3], (R13) + MOVW $(PsrMabt+1), R0 + B _vswitch + +/* + * Function: _virqcall + * Purpose: + * IRQ Trap Handler + * + */ + +TEXT _virqcall(SB), $-4 /* IRQ */ +_virq: + MOVM.DB [R0-R3], (R13) + MOVW $PsrMirq, R0 + B _vswitch + +/* + * Function: _vfiqcall + * Purpose: + * FIQ Trap Handler + * + */ + +TEXT _vfiqcall(SB), $-4 /* FIQ */ +_vfiq: + MOVM.DB [R0-R3], (R13) + MOVW $PsrMfiq, R0 + /* FALLTHROUGH */ + +_vswitch: /* switch to svc mode */ + MOVW SPSR, R1 + MOVW R14, R2 + MOVW R13, R3 + + MOVW CPSR, R14 + BIC $PsrMask, R14 + ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14 + MOVW R14, CPSR + + MOVM.DB.W [R0-R2], (R13) + MOVM.DB (R3), [R0-R3] + +_vsaveu: /* Save Registers */ + SUB $4, R13 /* save link */ + MOVW R14, (R13) /* MOVW.W R14,4(R13)*/ + + SUB $8, R13 + + MOVW R13, R14 /* ur->sp */ + ADD $(6*4), R14 + MOVW R14, 0(R13) + + MOVW 8(SP), R14 /* ur->link */ + MOVW R14, 4(SP) + + MOVM.DB.W [R0-R12], (R13) + MOVW R0, R0 /* gratuitous noop */ + + MOVW $setR12(SB), R12 /* static base (SB) */ + MOVW R13, R0 /* argument is ureg */ + SUB $8, R13 /* space for arg+lnk*/ + BL trap(SB) + + +_vrfe: /* Restore Regs */ + MOVW CPSR, R0 /* splhi on return */ + ORR $(PsrDirq|PsrDfiq), R0, R1 + MOVW R1, CPSR + ADD $(8+4*15), R13 /* [r0-R14]+argument+link */ + MOVW (R13), R14 /* restore link */ + MOVW 8(R13), R0 + MOVW R0, SPSR + MOVM.DB.S (R13), [R0-R14] /* restore user registers */ + MOVW R0, R0 /* gratuitous nop */ + ADD $12, R13 /* skip saved link+type+SPSR*/ + RFE /* MOVM.IA.S.W (R13), [R15] */ + + +/* + * Function: splhi + * Purpose: + * Disable Interrupts + * Returns: + * Previous interrupt state + */ + +TEXT splhi(SB), $-4 + MOVW CPSR, R0 + ORR $(PsrDirq|PsrDfiq), R0, R1 + MOVW R1, CPSR + RET + +/* + * Function: spllo + * Purpose: + * Enable Interrupts + * Returns: + * Previous interrupt state + */ + +TEXT spllo(SB), $-4 + MOVW CPSR, R0 + BIC $(PsrDirq), R0, R1 + MOVW R1, CPSR + RET + +/* + * Function: splx(level) + * Purpose: + * Restore interrupt level + */ + +TEXT splx(SB), $-4 + MOVW R0, R1 + MOVW CPSR, R0 + MOVW R1, CPSR + RET + +/* + * Function: islo + * Purpose: + * Check if interrupts are enabled + * + */ + +TEXT islo(SB), $-4 + MOVW CPSR, R0 + AND $(PsrDirq), R0 + EOR $(PsrDirq), R0 + RET + +/* + * Function: cpsrr + * Purpose: + * Returns current program status register + * + */ + +TEXT cpsrr(SB), $-4 + MOVW CPSR, R0 + RET + +/* + * Function: spsrr + * Purpose: + * Returns saved program status register + * + */ + +TEXT spsrr(SB), $-4 + MOVW SPSR, R0 + RET + +/* + * MMU Operations + */ +TEXT mmuctlregr(SB), $-4 + MRC CpMMU, 0, R0, C(CpControl), C(0) + RET + +TEXT mmuctlregw(SB), $-4 + MCR CpMMU, 0, R0, C(CpControl), C(0) + MOVW R0, R0 + MOVW R0, R0 + RET + +/* + * Cache Routines + */ + +/* + * Function: flushIcache + * Purpose: + * Flushes the *WHOLE* instruction cache + */ + +TEXT flushIcache(SB), $-4 + MCR CpMMU, 0, R0, C(CpCacheCtl), C(5), 0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + RET + + + +/* + * Function: flushDentry + * Purpose: + * Flushes an entry of the data cache + */ + +TEXT flushDentry(SB), $-4 + MCR CpMMU, 0, R0, C(CpCacheCtl), C(6), 1 + RET + +/* + * Function: drainWBuffer + * Purpose: + * Drains the Write Buffer + */ + +TEXT drainWBuffer(SB), $-4 + MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4 + RET + +/* + * Function: writeBackDC + * Purpose: + * Drains the dcache prior to flush + */ + +TEXT writeBackDC(SB), $-4 + MOVW $0xE0000000, R0 + MOVW $8192, R1 + ADD R0, R1 + +wbflush: + MOVW (R0), R2 + ADD $32, R0 + CMP R1,R0 + BNE wbflush + RET + +/* + * Function: flushDcache(SB) + * Purpose: + * Flush the dcache + */ + +TEXT flushDcache(SB), $-4 + MCR CpMMU, 0, R0, C(CpCacheCtl), C(6), 0 + RET + +/* + * Function: writeBackBDC(SB) + * Purpose: + * Write back the Baby D-Cache + */ + +TEXT writeBackBDC(SB), $-4 + MOVW $0xE4000000, R0 + MOVW $0x200, R1 + ADD R0, R1 + +wbbflush: + MOVW (R0), R2 + ADD $32, R0 + CMP R1,R0 + BNE wbbflush + MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + RET + +GLOBL mach0+0(SB), $MACHSIZE +GLOBL m(SB), $4 +GLOBL baddr(SB), $4 diff --git a/os/boot/puma/lib.h b/os/boot/puma/lib.h new file mode 100644 index 00000000..0853aa88 --- /dev/null +++ b/os/boot/puma/lib.h @@ -0,0 +1,107 @@ +/* + * functions (possibly) linked in, complete, from libc. + */ + +/* + * mem routines + */ +extern void* memccpy(void*, void*, int, long); +extern void* memset(void*, int, long); +extern int memcmp(void*, void*, long); +extern void* memmove(void*, void*, long); +extern void* memchr(void*, int, long); + +/* + * string routines + */ +extern char* strcat(char*, char*); +extern char* strchr(char*, char); +extern int strcmp(char*, char*); +extern char* strcpy(char*, char*); +extern char* strncat(char*, char*, long); +extern char* strncpy(char*, char*, long); +extern int strncmp(char*, char*, long); +extern long strlen(char*); +extern char* strrchr(char*, char); +extern char* strstr(char*, char*); + +/* + * print routines + * Fconv isn't used but is defined to satisfy prototypes in libg.h + * that are never called. + */ +typedef struct Fconv Fconv; + +extern char* donprint(char*, char*, char*, void*); +extern int sprint(char*, char*, ...); +extern int print(char*, ...); + +#define PRINTSIZE 256 + +/* + * one-of-a-kind + */ +extern int atoi(char*); +extern long strtol(char*, char**, int); +extern ulong strtoul(char*, char**, int); +extern char end[]; +extern char edata[]; + +/* + * Syscall data structures + */ + +#define MORDER 0x0003 /* mask for bits defining order of mounting */ +#define MREPL 0x0000 /* mount replaces object */ +#define MBEFORE 0x0001 /* mount goes before others in union directory */ +#define MAFTER 0x0002 /* mount goes after others in union directory */ +#define MCREATE 0x0004 /* permit creation in mounted directory */ +#define MMASK 0x0007 /* all bits on */ + +#define OREAD 0 /* open for read */ +#define OWRITE 1 /* write */ +#define ORDWR 2 /* read and write */ +#define OEXEC 3 /* execute, == read but check execute permission */ +#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ +#define OCEXEC 32 /* or'ed in, close on exec */ +#define ORCLOSE 64 /* or'ed in, remove on close */ + +#define NCONT 0 /* continue after note */ +#define NDFLT 1 /* terminate after note */ + +typedef struct Qid Qid; +typedef struct Dir Dir; +typedef struct Waitmsg Waitmsg; + +#define ERRLEN 64 +#define DIRLEN 116 +#define NAMELEN 28 + +struct Qid +{ + ulong path; + ulong vers; +}; + +struct Dir +{ + char name[NAMELEN]; + char uid[NAMELEN]; + char gid[NAMELEN]; + Qid qid; + ulong mode; + long atime; + long mtime; + ulong length; + short type; + short dev; +}; + +struct Waitmsg +{ + int pid; /* of loved one */ + int status; /* unused; a placeholder */ + ulong time[3]; /* of loved one */ + char msg[ERRLEN]; +}; +#define nelem(x) (sizeof(x)/sizeof((x)[0])) diff --git a/os/boot/puma/main.c b/os/boot/puma/main.c new file mode 100644 index 00000000..51df6d9c --- /dev/null +++ b/os/boot/puma/main.c @@ -0,0 +1,552 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "dosfs.h" + +typedef struct Type Type; +typedef struct Medium Medium; +typedef struct Mode Mode; + +enum { + Dany = -1, + Nmedia = 16, + + /* DS1 switch options */ + Sflashfs = 1<<0, /* take local fs from flash */ + Snotflash = 1<<1, /* don't boot from flash */ +}; + +enum { /* type */ + Tflash, + Tether, + Thard, + + Tany = -1, +}; + +enum { /* flag and name */ + Fnone = 0x00, + + Fdos = 0x01, + Ndos = 0x00, + Fboot = 0x02, + Nboot = 0x01, + Fbootp = 0x04, + Nbootp = 0x02, + Fflash = 0x08, + NName = 0x03, + + Fany = Fbootp|Fboot|Fdos|Fflash, + + Fini = 0x10, + Fprobe = 0x80, +}; + +enum { /* mode */ + Mauto = 0x00, + Mlocal = 0x01, + Manual = 0x02, + NMode = 0x03, +}; + +typedef struct Type { + int type; + char *cname; + int flag; + int (*init)(void); + long (*read)(int, void*, long); + long (*seek)(int, long); + Partition* (*setpart)(int, char*); + char* name[NName]; + + int mask; + Medium* media; +} Type; + +typedef struct Medium { + Type* type; + int flag; + Partition* partition; + Dos; + + Medium* next; +} Medium; + +typedef struct Mode { + char* name; + int mode; +} Mode; + +static Type types[] = { + { Tflash, "flash", + Fflash, + flashinit, 0, 0, 0, + { 0, "F", 0, } + }, + { Tether, "ether", + Fbootp, + etherinit, 0, 0, 0, + { 0, 0, "e", }, + }, + { Thard, "ata", + Fini|Fboot|Fdos, + 0, 0, 0, 0, /* not used now, will be later with PCMCIA */ + { "hd", "h", 0, }, + }, + {-1}, +}; + +static Medium media[Nmedia]; +static Medium *curmedium = media; + +static Mode modes[NMode+1] = { + [Mauto] { "auto", Mauto, }, + [Mlocal] { "local", Mlocal, }, + [Manual] { "manual", Manual, }, +}; + +static char *inis[] = { + "inferno/inferno.ini", + "inferno.ini", + "plan9/plan9.ini", + "plan9.ini", + 0, +}; +char **ini; +void printversion(void); + +static int +parse(char *line, int *type, int *flag, int *dev, char *file) +{ + Type *tp; + char buf[2*NAMELEN], *v[4], *p; + int i; + + strcpy(buf, line); + switch(getcfields(buf, v, 4, "!")){ + + case 3: + break; + + case 2: + v[2] = ""; + break; + + default: + return 0; + } + + *flag = 0; + for(tp = types; tp->cname; tp++){ + for(i = 0; i < NName; i++){ + + if(tp->name[i] == 0 || strcmp(v[0], tp->name[i])) + continue; + *type = tp->type; + *flag |= 1<type->name[Nbootp], mp->dev); + return bootp(mp->dev, file); + } + + if(flag & Fflash){ + if(mp->flag & Fflash && flashbootable(0)) + flashboot(mp->dev); + } + + if(flag & Fboot){ + + if(mp->flag & Fini){ + (*mp->type->setpart)(mp->dev, "disk"); + plan9ini(mp, nil); + } + if(file == 0 || *file == 0) + file = mp->partition->name; + (*mp->type->setpart)(mp->dev, file); + sprint(BOOTLINE, "%s!%d!%s", mp->type->name[Nboot], mp->dev, file); + return plan9boot(mp->dev, mp->seek, mp->read); + } + + if(flag & Fdos){ + if(mp->type->setpart) + (*mp->type->setpart)(mp->dev, "disk"); + if(mp->flag & Fini) + plan9ini(mp, nil); + if(file == 0 || *file == 0){ + strcpy(ixdos, *ini); + if(p = strrchr(ixdos, '/')) + p++; + else + p = ixdos; + strcpy(p, "infernopuma"); + if(dosstat(mp, ixdos, &df) <= 0) + return -1; + } + else + strcpy(ixdos, file); + sprint(BOOTLINE, "%s!%d!%s", mp->type->name[Ndos], mp->dev, ixdos); + return dosboot(mp, ixdos); + } + + return -1; +} + +static Medium* +allocm(Type *tp) +{ + Medium **l; + + if(curmedium >= &media[Nmedia]) + return 0; + + for(l = &tp->media; *l; l = &(*l)->next) + ; + *l = curmedium++; + return *l; +} + +Medium* +probe(int type, int flag, int dev) +{ + Type *tp; + int dombr, i, start; + Medium *mp; + Dosfile df; + Partition *pp; + + for(tp = types; tp->cname; tp++){ + if(type != Tany && type != tp->type || tp->init == 0) + continue; + + if(flag != Fnone){ + for(mp = tp->media; mp; mp = mp->next){ + if((flag & mp->flag) && (dev == Dany || dev == mp->dev)) + return mp; + } + } + if((tp->flag & Fprobe) == 0){ + tp->flag |= Fprobe; + tp->mask = (*tp->init)(); + } + + for(i = 0; tp->mask; i++){ + if((tp->mask & (1<mask &= ~(1<dev = i; + mp->flag = tp->flag; + mp->seek = tp->seek; + mp->read = tp->read; + mp->type = tp; + + if(mp->flag & Fboot){ + if((mp->partition = (*tp->setpart)(i, "boot")) == 0) + mp->flag &= ~Fboot; + if((mp->flag & Fflash) == 0) + (*tp->setpart)(i, "disk"); + } + + if(mp->flag & Fdos){ + start = 0; + dombr = 1; + if(mp->type->setpart){ + if(pp = (*mp->type->setpart)(i, "dos")){ + if(start = pp->start) + dombr = 0; + } + (*tp->setpart)(i, "disk"); + } + if(dosinit(mp, start, dombr) < 0) + mp->flag &= ~(Fini|Fdos); + else + print("dos init failed\n"); + } + + if(mp->flag & Fini){ + mp->flag &= ~Fini; + for(ini = inis; *ini; ini++){ + if(dosstat(mp, *ini, &df) <= 0) + continue; + mp->flag |= Fini; + break; + } + } + + if((flag & mp->flag) && (dev == Dany || dev == i)) + return mp; + } + } + + return 0; +} + +static void +pause(void) +{ + long d; + for(d=0; d<10000000; d++) + ; + USED(d); +} + +static void +flash(int n) +{ + int i; + + if(n <= 0) + return; + for(i=0; icname; tp++){ + if(tp->type == Tether) + continue; + if((mp = probe(tp->type, Fini, Dany)) && (mp->flag & Fini)){ + plan9ini(mp, nil); + break; + } + } + + if(mp == 0 || (mp->flag & Fini) == 0) + plan9ini(nil, flashconfig(0)); + + //consinit(); /* establish new console location */ + + if((options & Snotflash) == 0 && flashbootable(0)){ + print("Flash boot\n"); + flashboot(0); + } + + tried = 0; + mode = Mauto; + p = getconf("bootfile"); + flag = 0; + + if(p != 0) { + mode = Manual; + for(i = 0; i < NMode; i++){ + if(strcmp(p, modes[i].name) == 0){ + mode = modes[i].mode; + goto done; + } + } + if(parse(p, &type, &flag, &dev, file) == 0) { + print("Bad bootfile syntax: %s\n", p); + goto done; + } + mp = probe(type, flag, dev); + if(mp == 0) { + print("Cannot access device: %s\n", p); + goto done; + } + tried = boot(mp, flag, file); + } +done: + if(tried == 0 && mode != Manual){ + flag = Fany; + if(mode == Mlocal) + flag &= ~Fbootp; + if(options & Snotflash) + flag &= ~Fflash; + if((mp = probe(Tany, flag, Dany)) != 0) + boot(mp, flag & mp->flag, 0); + } + + def[0] = 0; + probe(Tany, Fnone, Dany); + + flag = 0; + for(tp = types; tp->cname; tp++){ + for(mp = tp->media; mp; mp = mp->next){ + if(flag == 0){ + flag = 1; + print("Boot devices:"); + } + + if(mp->flag & Fbootp) + print(" %s!%d", mp->type->name[Nbootp], mp->dev); + if(mp->flag & Fdos) + print(" %s!%d", mp->type->name[Ndos], mp->dev); + if(mp->flag & Fflash || mp->flag & Fboot) + print(" %s!%d", mp->type->name[Nboot], mp->dev); + } + } + if(flag) + print("\n"); + + for(;;){ + if(getstr("boot from", line, sizeof(line), def) >= 0){ + if(parse(line, &type, &flag, &dev, file)){ + if(mp = probe(type, flag, dev)) + boot(mp, flag, file); + } + } + def[0] = 0; + } +} + +void +machinit(void) +{ + memset(m, 0, sizeof(*m)); + m->delayloop = 20000; +} + +void +printversion(void) +{ + print("StrongARM SA-110 "); + print("%d MHz system\n", m->speed); + print("\n"); +{extern long baddr; print("%8.8lux\n", baddr);} +} + +int +optionsw() +{ + return 0; +} + +int +getcfields(char* lp, char** fields, int n, char* sep) +{ + int i; + + for(i = 0; lp && *lp && i < n; i++){ + while(*lp && strchr(sep, *lp) != 0) + *lp++ = 0; + if(*lp == 0) + break; + fields[i] = lp; + while(*lp && strchr(sep, *lp) == 0){ + if(*lp == '\\' && *(lp+1) == '\n') + *lp++ = ' '; + lp++; + } + } + + return i; +} + +static Map memv[512]; +static RMap rammap = {"physical memory"}; + +void +meminit(void) +{ + ulong e; + + mapinit(&rammap, memv, sizeof(memv)); + e = PADDR(end); + mapfree(&rammap, e, 4*1024*1024-e); +} + +void* +ialloc(ulong n, int align) +{ + ulong a; + int s; + + if(align <= 0) + align = 4; + s = splhi(); + a = mapalloc(&rammap, 0, n, align); + splx(s); + if(a == 0) + panic("ialloc"); + return memset(KADDR(a), 0, n); +} + +void* +malloc(ulong n) +{ + ulong *p; + + n = ((n+sizeof(int)-1)&~(sizeof(int)-1))+2*sizeof(int); + p = ialloc(n, sizeof(int)); + *p++ = 0xcafebeef; + *p++ = n; + return p; +} + +void +free(ulong *p) +{ + int s; + + if(p){ + if(*(p -= 2) != 0xcafebeef) + panic("free"); + s = splhi(); + mapfree(&rammap, (ulong)p, p[1]); + splx(s); + } +} + +void +sched(void) +{ +} diff --git a/os/boot/puma/mem.h b/os/boot/puma/mem.h new file mode 100644 index 00000000..35bca0a6 --- /dev/null +++ b/os/boot/puma/mem.h @@ -0,0 +1,38 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ + +/* + * Sizes + */ +#define BI2BY 8 /* bits per byte */ +#define BI2WD 32 /* bits per word */ +#define BY2WD 4 /* bytes per word */ +#define BY2PG 4096 /* bytes per page */ +#define WD2PG (BY2PG/BY2WD) /* words per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define PGROUND(s) (((s)+(BY2PG-1))&~(BY2PG-1)) + +#define MAXMACH 1 /* max # cpus system can run */ + +/* + * Time + */ +#define HZ (20) /* clock frequency */ +#define MS2HZ (1000/HZ) /* millisec per clock tick */ +#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ +#define TK2MS(t) ((((ulong)(t))*1000)/HZ) /* ticks to milliseconds */ +#define MS2TK(t) ((((ulong)(t))*HZ)/1000) /* milliseconds to ticks */ + +/* + * Fundamental addresses + */ + +/* + * Address spaces + * + * Kernel is at 0x00008000 + */ +#define KZERO 0x00000000 /* base of kernel address space */ +#define KTZERO KZERO /* first address in kernel text */ +#define MACHSIZE 4096 diff --git a/os/boot/puma/mkfile b/os/boot/puma/mkfile new file mode 100644 index 00000000..383b5442 --- /dev/null +++ b/os/boot/puma/mkfile @@ -0,0 +1,71 @@ +<../../../mkconfig + +SYSTARG=Inferno +OBJTYPE=arm +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +TARGET=${O}boot +OBJ=\ + l.$O\ + div.$O\ + 8250.$O\ + alarm.$O\ + bootp.$O\ + clock.$O\ + console.$O\ + conf.$O\ + dosboot.$O\ + donprint.$O\ + ether.$O\ + ether8900.$O\ + flash.$O\ + kbd.$O\ + main.$O\ + plan9boot.$O\ + puma.$O\ + qio.$O\ + rmap.$O\ + trap.$O\ + zqs.$O + +HFILES=\ + lib.h\ + mem.h\ + dat.h\ + fns.h\ + io.h\ + boot.h\ + armv4.h\ + puma.h\ + +CFLAGS=-w -I. +LFLAGS=-H1 -R4 -T0x00200000 -E_main #-a +#LFLAGS=-H1 -R4 -T0x00008080 -E_main #-a +#LFLAGS = -H1 -R4 -T0xa00000c0 -E_main #-a + +all:V: $TARGET + +$TARGET: $OBJ + $LD -o $target $LFLAGS -l $prereq -lc + ls -l $target + +installall:V: install +install:V: bb $TARGET + cp $TARGET /arm + +%.$O: %.s + $AS $stem.s + +%.$O: %.c + $CC $CFLAGS $stem.c + +%.$O: $HFILES + +clock.$O floppy.$O trap.$O: ureg.h +conf.$O dosboot.$O main.$O: dosfs.h +ether.$O ether2000.$O ether509.$O ether8003.$O ether8390.$O: ether.h +bootp.$O: ip.h + +clean: + rm -f *.[$OS] [$OS].out y.tab.? y.debug y.output $TARGET + diff --git a/os/boot/puma/outb.c b/os/boot/puma/outb.c new file mode 100644 index 00000000..e66c994e --- /dev/null +++ b/os/boot/puma/outb.c @@ -0,0 +1,20 @@ +typedef unsigned short ushort; +typedef unsigned char uchar; + +enum { + IsaIOBase = 0xf0000000, + IsaMemBase = 0xe0000000, + + IOBase = 0x300, + MemBase = 0xc0000, + + TxFrame = 0x0a00, +}; + +#define regw(reg, val) *((ushort *)IsaMemBase + MemBase + (reg)) = (val) + +void +main(void) +{ + regw(TxFrame, 0x1234); +} diff --git a/os/boot/puma/plan9boot.c b/os/boot/puma/plan9boot.c new file mode 100644 index 00000000..5151e1b7 --- /dev/null +++ b/os/boot/puma/plan9boot.c @@ -0,0 +1,93 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +char *premature = "premature EOF\n"; + +/* + * read in a segment + */ +static long +readseg(int dev, long (*read)(int, void*, long), long len, long addr) +{ + char *a; + long n, sofar; + + a = (char *)addr; + for(sofar = 0; sofar < len; sofar += n){ + n = 8*1024; + if(len - sofar < n) + n = len - sofar; + n = (*read)(dev, a + sofar, n); + if(n <= 0) + break; + print("."); + } + return sofar; +} + +/* + * boot + */ +int +plan9boot(int dev, long (*seek)(int, long), long (*read)(int, void*, long)) +{ + long n; + long addr; + void (*b)(void); + Exec *ep; + + if((*seek)(dev, 0) < 0) + return -1; + + /* + * read header + */ + ep = (Exec *) ialloc(sizeof(Exec), 0); + n = sizeof(Exec); + if(readseg(dev, read, n, (ulong) ep) != n){ + print(premature); + return -1; + } + if(GLLONG(ep->magic) != E_MAGIC){ + print("bad magic 0x%lux not a plan 9 executable!\n", GLLONG(ep->magic)); + return -1; + } + + /* + * read text + */ + addr = PADDR(GLLONG(ep->entry)); + n = GLLONG(ep->text); + print("%d", n); + if(readseg(dev, read, n, addr) != n){ + print(premature); + return -1; + } + + /* + * read data (starts at first page after kernel) + */ + addr = PGROUND(addr+n); + n = GLLONG(ep->data); + print("+%d", n); + if(readseg(dev, read, n, addr) != n){ + print(premature); + return -1; + } + + /* + * bss and entry point + */ + print("+%d\nstart at 0x%lux\n", GLLONG(ep->bss), GLLONG(ep->entry)); uartwait(); + + /* + * Go to new code. It's up to the program to get its PC relocated to + * the right place. + */ + b = (void (*)(void))(PADDR(GLLONG(ep->entry))); + (*b)(); + return 0; +} diff --git a/os/boot/puma/puma.c b/os/boot/puma/puma.c new file mode 100644 index 00000000..bd4a822d --- /dev/null +++ b/os/boot/puma/puma.c @@ -0,0 +1,123 @@ +#include "boot.h" + +//#define GPIO1_PORT 0x0078 +//#define GPIO2_PORT 0x0079 + +#define GPIO2_N_LED1_DIAG 0x10 /* diagnostic led mask */ + +#define HARI2_N_LED1 0x01 +#define HARI2_N_LED2 0x02 +#define HARI2_N_LED3 0x04 +#define HARI2_N_LED4 0x08 + +/* + * National Semiconductor PC87306 Super I/O + */ +enum { + Index= 0x398, /* sio configuration index register */ + Data= 0x399, /* sio configuration data register */ +}; + + +// byte flip table for Puma SuperIO port permuted as 76543210 -> 01234567 +unsigned char +byteflip[] = { + 0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0, + 0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8, + 0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4, + 0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC, + 0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2, + 0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA, + 0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6, + 0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE, + 0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1, + 0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9, + 0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5, + 0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD, + 0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3, + 0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB, + 0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7, + 0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF +}; + +int +sio_inb(int port) +{ + unsigned char b = *(uchar *)IOBADDR(port); +// b = byteflip[b]; + return b&0xff; +} + +void +sio_outb(int port, int data) +{ +// data = byteflip[data]; + *(uchar *)IOBADDR(port) = data; +} + +static void +siocfg(int r, int v, int rsv) +{ + sio_outb(Index, r); + if(rsv) + v = (sio_inb(Data)&rsv) | (v&~rsv); + sio_outb(Data, v); + sio_outb(Data, v); /* ``write data twice'' */ +} + +void +sioinit(void) +{ + /* determine which byte flip is required ... */ + + siocfg(SIO_CONFIG_FAR, FAR_LPT_LPTA | FAR_UART1_COM1 | FAR_UART2_COM2, 0); + siocfg(SIO_CONFIG_FER, FER_LPT_ENABLE | FER_UART1_ENABLE | FER_UART2_ENABLE | FER_IDE_ENABLE, 0); + siocfg(SIO_CONFIG_KRR, KRR_KBC_ENABLE | KRR_KBC_MUST_BE_1 | KRR_RTC_ENABLE, 0x50); + siocfg(SIO_CONFIG_SCF0,SCF0_GPIO_PORT1_ENABLE | SCF0_GPIO_PORT2_ENABLE, 0xC0); + + /* force UART interrupt pins low, not tristate by setting ienable in MCR (!) */ + sio_outb(COM1_PORT+UART_MCR, sio_inb(COM1_PORT+UART_MCR)|(1<<3)); + sio_outb(COM2_PORT+UART_MCR, sio_inb(COM2_PORT+UART_MCR)|(1<<3)); +} + +void +led(int on) +{ + int gpio, hari, hbits; + int s; + + s = splhi(); + + gpio = sio_inb(GPIO2_PORT); + if (on) + gpio &= ~GPIO2_N_LED1_DIAG; + else + gpio |= GPIO2_N_LED1_DIAG; + sio_outb(GPIO2_PORT, gpio); + + hari = HARI2_INIT; + hbits = HARI2_N_LED1|HARI2_N_LED2|HARI2_N_LED3|HARI2_N_LED4; + if (on) + hari &= ~hbits; + else + hari |= hbits; + *(uchar *)HARI2 = hari; + + + splx(s); + +} + +void +pumainit(void) +{ + /* + * Initialise HARI2 for: + * a) Leds off + * b) No Timer2 aborts + * c) Ethernet on IRQ + */ + *(uchar *)HARI2 = HARI2_INIT|HARI2_N_LED1|HARI2_N_LED2|HARI2_N_LED3|HARI2_N_LED4; + sioinit(); +} + diff --git a/os/boot/puma/puma.h b/os/boot/puma/puma.h new file mode 100644 index 00000000..a5835cff --- /dev/null +++ b/os/boot/puma/puma.h @@ -0,0 +1,435 @@ +/* + * Teralogic TL750 - Puma Evaluation Board + */ + +/* + * Puma addresses + */ +#define EPROM_BASE 0x80000000 /* EPROM */ +#define FLASH_BASE 0xa0000000 /* Flash */ +#define TL750_BASE 0xc0000000 /* TL750 registers */ +#define ISAMEM_BASE 0xe0000000 /* ISA memory space */ +#define ISAIO_BASE 0xf0000000 /* ISA I/O space */ + +#define ISAIO_SHIFT 2 +#define IOBADDR(io_port) (ISAIO_BASE + (io_port << ISAIO_SHIFT)) + +/* Hardware address register for interrupts (HARI) */ +#define HARI1 0xE2000000 /* Interrupt status on read, User interrupt on write */ +#define HARI2 0xE3000000 /* More interrupt status on read, LEDs on write */ +#define HARI1_FIQ_MASK 0x92 /* FIQ indicator bits in HARI1, others are IRQ */ +#define HARI2_INIT 0x20 /* No timer2 aborts, Ethernet on IRQ */ + + + +/* + * Interrupt Vectors + * corresponding to the HARIx_xxx_IRQ/FIQ bits above. + * + * HARI1 interrupts + */ +#define V_LPT 0 /* Parallel port interrupt */ +#define V_NM0 1 /* MPEG Decode Interrupt */ +#define V_NM1 2 /* MPEG Decode Interrupt */ +#define V_COM2 3 /* Serial Port 2 Interrupt */ +#define V_COM1 4 /* Serial Port 1 Interrupt */ +#define V_MOUSE 5 /* Mouse Interrupt */ +#define V_KEYBOARD 6 /* Keyboard Interrupt */ +#define V_ETHERNET 7 /* Ethernet Interrupt */ +/* + * HARI2 interrupts + */ +#define V_TIMER0 8 /* 82C54 Timer 0 Interrupt */ +#define V_TIMER1 9 /* 82C54 Timer 1 Interrupt */ +#define V_TIMER2 10 /* 82C54 Timer 2 Interrupt */ +#define V_SOFTWARE 11 /* Software Interrupt */ +#define V_IDE 12 /* IDE Hard Drive Interrupt */ +#define V_SMARTCARD 13 /* Smart Card Interrupt */ +#define V_TL750 14 /* TL750 Interrupt */ + /* Nothing in vector 15 for now */ +#define V_MAXNUM 15 + +/* + * Definitions for National Semiconductor PC87306 SuperIO configuration + */ +#define SIO_CONFIG_INDEX 0x398 /* SuperIO configuration index register */ +#define SIO_CONFIG_DATA 0x399 /* SuperIO configuration data register */ + +#define SIO_CONFIG_RESET_VAL 0x88 /* Value read from first read of sio_config_index reg after reset */ +/* + * PC87306 Configuration Registers (The value listed is the configuration space + * index.) + */ +#define SIO_CONFIG_FER 0x00 /* Function Enable Register */ + +#define FER_LPT_ENABLE 0x01 /* Enable Parallel Port */ +#define FER_UART1_ENABLE 0x02 /* Enable Serial Port 1 */ +#define FER_UART2_ENABLE 0x04 /* Enable Serial Port 2 */ +#define FER_FDC_ENABLE 0x08 /* Enable Floppy Controller */ +#define FER_FDC_4DRIVE_ENCODING 0x10 /* Select Floppy 4 Drive Encoding */ +#define FER_FDC_ADDR_ENABLE 0x20 /* Select Floppy Secondary Address */ + /* 0: [0x3F0..0x3F7] */ + /* 1: [0x370..0x377] */ +#define FER_IDE_ENABLE 0x40 /* Enable IDE Controller */ +#define FER_IDE_ADDR_SELECT 0x80 /* Select IDE Secondary Address */ + /* 0: [0x1F0..0x1F7,0x3F6,0x3F7] */ + /* 1: [0x170..0x177,0x376,0x377] */ + +#define SIO_CONFIG_FAR 0x01 /* Function Address Register */ + +#define FAR_LPT_ADDR_MASK 0x03 /* Select LPT Address */ + /* If (PNP0[4] == 0) then: */ + /* 0: LPTB [0x378..0x37F] IRQ5/7 */ + /* 1: LPTA [0x3BC..0x3BE] IRQ7 */ + /* 2: LPTC [0x278..0x27F] IRQ5 */ + /* 3: Reserved */ + /* Else ignored. */ +#define FAR_LPT_LPTB 0 /* 0: LPTB 0x378 irq5/7 */ +#define FAR_LPT_LPTA 1 /* 1: LPTA 0x3BC irq 7 */ +#define FAR_LPT_LPTC 2 /* 2: LPTC 0x278 irq5 */ + +#define FAR_UART1_ADDR_MASK 0x0C /* Select Serial Port 1 Address */ + /* 0: COM1 [0x3F8..0x3FF] */ + /* 1: COM2 [0x2F8..0x2FF] */ + /* 2: COM3 (See FAR[7:6]) */ + /* 3: COM4 (See FAR[7:6]) */ +#define FAR_UART1_COM1 0x00 +#define FAR_UART2_ADDR_MASK 0x30 /* Select Serial Port 2 Address */ + /* 0: COM1 [0x3F8..0x3FF] */ + /* 1: COM2 [0x2F8..0x2FF] */ + /* 2: COM3 (See FAR[7:6]) */ + /* 3: COM4 (See FAR[7:6]) */ +#define FAR_UART2_COM2 0x10 +#define FAR_EXTENDED_UART_ADDR_SELECT 0xC0 /* Extended Address Selects */ + /* COM3@IRQ4, COM4@IRQ3 */ + /* 0: COM3@0x3E8, COM4@0x2E8 */ + /* 1: COM3@0x338, COM4@0x238 */ + /* 2: COM3@0x2E8, COM4@0x2E0 */ + /* 3: COM3@0x220, COM4@0x228 */ + +#define SIO_CONFIG_PTR 0x02 /* Power & Test Register */ + +#define PTR_POWER_DOWN 0x01 /* Power down all enabled functions */ +#define PTR_LPT_IRQ_SELECT 0x08 /* Select LPT IRQ if (FAR[1:0] == 0) */ + /* 0: IRQ5 */ + /* 1: IRQ7 */ +#define PTR_UART1_TEST_MODE 0x10 /* Set serial port 1 test mode */ +#define PTR_UART2_TEST_MODE 0x20 /* Set serial port 2 test mode */ +#define PTR_LOCK_CONFIGURATION 0x40 /* Prevent all further config writes */ + /* Only a RESET will reenable writes */ +#define PTR_LPT_EXTENDED_MODE_SELECT 0x80 /* Select Mode if not EPP/ECP */ + /* 0: Compatible Mode */ + /* 1: Extended Mode */ + +#define SIO_CONFIG_FCR 0x03 /* Function Control Register */ + /* WARNING: The FCR register must be written */ + /* using read-modify-write! */ +#define FCR_TDR_MODE_SELECT 0x01 /* ? (floppy/tape) */ +#define FCR_IDE_DMA_ENABLE 0x02 /* Enable IDE DMA mode */ +#define FCR_EPP_ZERO_WAIT_STATE 0x40 /* Enable EPP zero wait state */ + +#define SIO_CONFIG_PCR 0x04 /* Printer Control Register */ + +#define PCR_EPP_ENABLE 0x01 /* Enable parallel port EPP mode */ +#define PCR_EPP_VERSION_SELECT 0x02 /* Select version of EPP mode */ + /* 0: Version 1.7 */ + /* 1: Version 1.9 (IEEE 1284) */ +#define PCR_ECP_ENABLE 0x04 /* Enable parallel port ECP mode */ +#define PCR_ECP_POWER_DOWN_CLOCK_ENABLE 0x08 /* Enable clock in power-down state */ + /* 0: Freeze ECP clock */ + /* 1: Run ECP clock */ +#define PCR_ECP_INT_POLARITY_CONTROL 0x20 /* Interrupt polarity control */ + /* 0: Level high or negative pulse */ + /* 1: Level low or positive pulse */ +#define PCR_ECP_INT_IO_CONTROL 0x40 /* Interrupt I/O control */ + /* WARNING: Slightly safer to choose */ + /* open drain if you don't know the */ + /* exact requirements of the circuit */ + /* 0: Totem-pole output */ + /* 1: Open drain output */ +#define PCR_RTC_RAM_WRITE_DISABLE 0x80 /* Disable writes to RTC RAM */ + /* 0: Enable writes */ + /* 1: Disable writes */ + +#define SIO_CONFIG_KRR 0x05 /* Keyboard & RTC Control Register */ + /* WARNING: The KRR register must be written */ + /* with a 1 in bit 2, else the KBC will not */ + /* work! */ +#define KRR_KBC_ENABLE 0x01 /* Enable keyboard controller */ +#define KRR_KBC_SPEED_CONTROL 0x02 /* Select clock divisor if !KRR[7] */ + /* 0: Divide by 3 */ + /* 1: Divide by 2 */ +#define KRR_KBC_MUST_BE_1 0x04 /* Reserved: This bit must be 1! */ +#define KRR_RTC_ENABLE 0x08 /* Enable real time clock */ +#define KRR_RTC_RAMSEL 0x20 /* Select RTC RAM bank */ +#define KRR_KBC_CLOCK_SOURCE_SELECT 0x80 /* Select clock source */ + /* 0: Use X1 clock source */ + /* 1: Use SYSCLK clock source */ + +#define SIO_CONFIG_PMC 0x06 /* Power Management Control Register */ + +#define PMC_IDE_TRISTATE_CONTROL 0x01 /* ? (power management) */ +#define PMC_FDC_TRISTATE_CONTROL 0x02 /* ? (power management) */ +#define PMC_UART1_TRISTATE_CONTROL 0x04 /* ? (power management) */ +#define PMC_SELECTIVE_LOCK 0x20 /* ? (power management) */ +#define PMC_LPT_TRISTATE_CONTROL 0x40 /* ? (power management) */ + +#define SIO_CONFIG_TUP 0x07 /* Tape, UARTS & Parallel Port Register */ + +#define TUP_EPP_TIMEOUT_INT_ENABLE 0x04 /* Enable EPP timeout interrupts */ + +#define SIO_CONFIG_SID 0x08 /* Super I/O Identification Register */ + +#define SID_ID_MASK 0xF8 /* Super I/O ID field */ +#define SID_ID_PC87306 0x70 /* PC87306 ID value */ + +#define SIO_CONFIG_ASC 0x09 /* Advanced Super I/O Config Register */ + /* WARNING: The ASC register must be written */ + /* with a 0 in bit 3! */ + /* WARNING: The ASC register resets to 1 in */ + /* bit 7 (PC/AT mode)! */ +#define ASC_VLD_MASK 0x03 /* ? (floppy/tape) */ +#define ASC_ENHANCED_TDR_SUPPORT 0x04 /* ? (floppy/tape) */ +#define ASC_MUST_BE_0 0x08 /* Reserved: Must be 0 */ +#define ASC_ECP_CNFGA 0x20 /* ? */ +#define ASC_DENSEL_POLARITY_BIT 0x40 /* ? (floppy/tape) */ +#define ASC_SYSTEM_OPERATION_MODE 0x80 /* Select system operation mode */ + /* 0: PS/2 mode */ + /* 1: PC/AT mode */ + +#define SIO_CONFIG_CS0LA 0x0A /* Chip Select 0 Low Address Register */ + +#define SIO_CONFIG_CS0CF 0x0B /* Chip Select 0 Configuration Register */ + /* WARNING: The CS0CF register must be */ + /* written with a 1 in bit 7! */ +#define CS0CF_CS0_DECODE 0x08 /* Select CS0 decode sensitivity */ + /* 0: Decode full 16-bit address */ + /* 1: Decode only bits 15 thru 12 */ +#define CS0CF_CS0_WRITE_ENABLE 0x10 /* Enable CS0 on write cycles */ +#define CS0CF_CS0_READ_ENABLE 0x20 /* Enable CS0 on read cycles */ +#define CS0CF_CS0_MUST_BE_1 0x80 /* Reserved: Must be 1 */ + +#define SIO_CONFIG_CS1LA 0x0C /* Chip Select 1 Low Address Register */ + +#define SIO_CONFIG_CS1CF 0x0D /* Chip Select 1 Configuration Register */ + +#define CS1CF_CS1_DECODE 0x08 /* Select CS1 decode sensitivity */ + /* 0: Decode full 16-bit address */ + /* 1: Decode only bits 15 thru 12 */ +#define CS1CF_CS1_WRITE_ENABLE 0x10 /* Enable CS1 on write cycles */ +#define CS1CF_CS1_READ_ENABLE 0x20 /* Enable CS1 on read cycles */ + +#define SIO_CONFIG_IRC 0x0E /* Infrared Configuration Register */ + +#define IRC_UART2_INTERFACE_MODE 0x01 /* Select UART2 interface mode */ + /* 0: Normal (modem) mode */ + /* 1: IR mode */ +#define IRC_IR_FULL_DUPLEX 0x02 /* Select IR duplex mode */ + /* 0: Full duplex mode */ + /* 1: Half duplex mode */ +#define IRC_ENCODED_IR_TRANSMITTER_DRIVE 0x10 /* IR transmitter drive control */ + /* 0: IRTX active for 1.6usec */ + /* 1: IRTX active for 3/16 baud */ +#define IRC_ENCODED_IR_MODE 0x20 /* IR encode mode */ + /* 0: Encoded mode */ + /* 1: Non-encoded mode */ + +#define SIO_CONFIG_GPBA 0x0F /* GP I/O Port Base Address Config Register */ + +#define SIO_CONFIG_CS0HA 0x10 /* Chip Select 0 High Address Register */ + +#define SIO_CONFIG_CS1HA 0x11 /* Chip Select 1 High Address Register */ + +#define SIO_CONFIG_SCF0 0x12 /* Super I/O Configuration Register 0 */ + +#define SCF0_RTC_RAM_LOCK 0x01 /* Lock (1) will prevent all further */ + /* accesses to RTC RAM. Only RESET */ + /* return this bit to a 0. */ +#define SCF0_IRQ1_12_LATCH_ENABLE 0x02 /* Enable IRQ1/IRQ12 latching */ +#define SCF0_IRQ12_TRISTATE 0x04 /* IRQ12 tri-state control */ + /* 0: Use quasi-bidirectional buffer */ + /* 1: Tri-state IRQ12 */ +#define SCF0_UART2_TRISTATE 0x08 /* Force UART2/IR outputs to */ + /* tri-state when disabled */ +#define SCF0_GPIO_PORT1_ENABLE 0x10 /* Enable GPIO port 1 */ +#define SCF0_GPIO_PORT2_ENABLE 0x20 /* Enable GPIO port 2 */ + +#define SIO_CONFIG_SCF1 0x18 /* Super I/O Configuration Register 1 */ + +#define SCF1_REPORTED_ECP_DMA 0x06 /* Reported ECP DMA number */ + /* 0: Jumpered 8-bit DMA */ + /* 1: DMA channel 1 */ + /* 2: DMA channel 2 */ + /* 3: DMA channel 3 */ +#define SCF1_SELECTED_ECP_DMA 0x08 /* Selected ECP DMA pins */ + /* 0: PDRQ0 & PDACK0 */ + /* 1: PDRQ1 & PDACK1 */ +#define SCF1_SCRATCH_BITS 0xC0 /* ? */ + +#define SIO_CONFIG_LPTBA 0x19 /* LPT Base Address */ + +#define SIO_CONFIG_PNP0 0x1B /* Plug & Play Configuration Register 0 */ + +#define PNP0_LPT_INT_SELECT_CONTROL 0x10 /* LPT IRQ select control */ + /* 0: IRQ selected by FAR[1:0] */ + /* 1: IRQ selected by PNP0[5] */ +#define PNP0_LPT_INT_MAPPING 0x20 /* LPT IRQ mapping */ + /* 0: IRQ5 */ + /* 1: IRQ7 */ +#define PNP0_LPTA_BASE_ADDR_SELECT 0x40 /* LPTA base address */ + /* 0: Always 0x3BC */ + /* 1: Selected by LPTBA[7:0] */ + +#define SIO_CONFIG_PNP1 0x1C /* Plug & Play Configuration Register 1 */ + +#define PNP1_UARTS_INT_SELECT_CONTROL 0x01 /* UART interrupt select control */ + /* 0: Use FAR[3:2] & FAR[5:4] */ + /* 1: Use PNP1[2] & PNP1[6] */ +#define PNP1_UART1_INT_MAPPING 0x04 /* UART1 interrupt mapping */ + /* 0: IRQ3 */ + /* 1: IRQ4 */ +#define PNP1_UART2_INT_MAPPING 0x40 /* UART2 interrupt mapping */ + /* 0: IRQ3 */ + /* 1: IRQ4 */ +/*---------------------------------------------------------------------------*/ + +/* + * Definitions for the SuperIO UART. + */ +#define COM1_PORT 0x3f8 +#define COM2_PORT 0x2f8 + +/* + * Register offsets. + */ +#define UART_RX 0 /* Receive port, read only */ +#define UART_TX 0 /* transmit port, write only */ +#define UART_IER 1 /* Interrupt enable, read/write */ +#define UART_IIR 2 /* Interrupt id, read only */ +#define UART_FIFO_CONTROL 2 /* FIFO control, write only */ +#define UART_LCR 3 /* Line control register */ +#define UART_MCR 4 /* Modem control register */ +#define UART_LSR 5 /* Line Status register */ +#define UART_MSR 6 /* Modem Status register */ + +/* with the DLAB bit set, the first two registers contain the baud rate */ +#define UART_DLLSB 0 +#define UART_DLMSB 1 + +/* + * Line control register + */ +#define LCR_DB 3 /* Data bits in transmission (0 = 5, 1 = 6, 2 = 7, 3 = 8) */ +#define LCR_SB 4 /* Stop bit */ +#define LCR_PE 8 /* Parity enable */ +#define LCR_EP 16 /* Even parity */ +#define LCR_SP 32 /* Stick parity */ +#define LCR_BC 64 /* break control */ +#define LCR_DLAB 128 /* Divisor latch access bit */ + + +/* + * Modem Control register + */ +#define MCR_DTR 1 /* Data Terminal Ready */ +#define MCR_RTS 2 /* Request To Send */ +#define MCR_OUT1 4 /* Out1 (not used) */ +#define MCR_IRQ_ENABLE 8 /* Enable IRQ */ +#define MCR_LOOP 16 /* Loopback mode */ + +/* + * Line status bits. + */ +#define LSR_DR 0x01 /* Data ready */ +#define LSR_OE 0x02 /* Overrun error */ +#define LSR_PE 0x04 /* Parity error */ +#define LSR_FE 0x08 /* Framing error */ +#define LSR_BI 0x10 /* Break interrupt */ +#define LSR_THRE 0x20 /* Transmitter holding register empty */ +#define LSR_TEMT 0x40 /* Transmitter empty */ +#define LSR_FFE 0x80 /* Receiver FIFO error */ + +#define LSR_ERROR (LSR_OE | LSR_PE | LSR_FE) + +/* + * Interrupt Identification register (IIR) + */ +#define IIR_IP 1 /* No Interrupt pending */ +#define IIR_RECEIVE_LINE_STATUS 6 /* Overrun, Parity, Framing erros, Break */ +#define IIR_RDA 4 /* Receive data available */ +#define IIR_FIFO_FLAG 8 /* FIFO flag */ +#define IIR_FIFO_TIMEOUT (IIR_RDA+IIR_FIFO_FLAG) /* Got data some time ago, but FIFO time out */ +#define IIR_THRE 2 /* Transmitter holding register empty. */ +#define IIR_MS 0 /* CTS, DSR, RING, DCD changed */ +#define IIR_HPIP 6 /* Highest priority interrupt pending */ + +/* + * Interrupt enable register (IER) + */ +#define IER_RDA 1 /* Received data available */ +#define IER_THRE 2 /* Transmitter holding register empty */ +#define IER_RLS 4 /* Receiver line status */ +#define IER_MS 8 /* Modem status */ + +/* + * PC87306 Parallel I/O Port + */ +#define LPT1_PORT 0x03BC + +/* + * PC87306 General Purpose I/O Ports + */ +#define GPIO1_PORT 0x0078 +#define GPIO2_PORT 0x0079 + +/* + * PC87306 IDE Port + */ +#define IDE_PORT_1 0x01F0 +#define IDE_PORT_2 0x03F6 +#define IDE_PORT_3 0x03F7 + +/* + * PC87306 Floppy Port + */ +#define FDC_PORT 0x03F0 + +/* + * PC87306 Real Time Clock/battery backed up RAM port + */ +#define RTC_INDEX_PORT 0x0070 +#define RTC_DATA_PORT 0x0071 + +/* + * Offsets in RTC memory (RAMSEL = 0) + */ +#define RTC_SECONDS 0 +#define RTC_SECONDS_ALARM 1 +#define RTC_MINUTES 2 +#define RTC_MINUTES_ALARM 3 +#define RTC_HOURS 4 +#define RTC_HOURS_ALARM 5 +#define RTC_DAY_OF_WEEK 6 +#define RTC_DAY_OF_MONTH 7 +#define RTC_MONTH 8 +#define RTC_YEAR 9 +#define RTC_CONTROL_A 0xA +#define RTC_CONTROL_B 0xB +#define RTC_CONTROL_C 0xC +#define RTC_CONTROL_D 0xD + +#define RTC_NVRAM0_START 0xE +#define RTC_NVRAM0_SIZE 114 +#define RTC_NVRAM1_START 0 +#define RTC_NVRAM1_SIZE 128 +#define RTC_NVRAM_SIZE (RTC_NVRAM0_SIZE+RTC_NVRAM1_SIZE) + +#define RTC_PWNVRAM_START 0x38 /* Start of protected NVRAM */ +#define RTC_PWNVRAM_SIZE 8 /* Size of protected NVRAM */ + + +/* + * PC87306 Keyboard controller ports + */ +#define KEYBD_DATA_PORT 0x0060 +#define KEYBD_CTRL_PORT 0x0064 diff --git a/os/boot/puma/qio.c b/os/boot/puma/qio.c new file mode 100644 index 00000000..e014433e --- /dev/null +++ b/os/boot/puma/qio.c @@ -0,0 +1,128 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +struct Queue { + Block* first; + Block* last; + void (*kick)(void*); + void* arg; + long len; +}; + +Block * +iallocb(int n) +{ + Block *b; + + b = (Block*)malloc(sizeof(Block)+n); + b->data = (uchar*)b + sizeof(Block); + b->rp = b->wp = b->data; + b->lim = b->data + n; + b->next = 0; + b->magic = 0xcafebee0; + return b; +} + +void +freeb(Block *b) +{ + if(b){ + if(b->magic != 0xcafebee0) + panic("freeb"); + b->magic = 0; + b->next = (Block*)0xdeadbabe; + free(b); + } +} + +Queue * +qopen(int limit, int msg, void (*kick)(void*), void *arg) +{ + Queue *q; + + USED(limit, msg); + q = (Queue*)malloc(sizeof(Queue)); + q->first = q->last = 0; + q->kick = kick; + q->arg = arg; + q->len = 0; + return q; +} + +Block * +qget(Queue *q) +{ + int s; + Block *b; + + s = splhi(); + if((b = q->first) != 0){ + q->first = b->next; + b->next = 0; + q->len -= BLEN(b); + if(q->len < 0) + panic("qget"); + } + splx(s); + return b; +} + +void +qbwrite(Queue *q, Block *b) +{ + int s; + + s = splhi(); + b->next = 0; + if(q->first == 0) + q->first = b; + else + q->last->next = b; + q->last = b; + q->len += BLEN(b); + splx(s); + if(q->kick) + q->kick(q->arg); +} + +long +qlen(Queue *q) +{ + return q->len; +} + +int +qbgetc(Queue *q) +{ + Block *b; + int s, c; + + c = -1; + s = splhi(); + while(c < 0 && (b = q->first) != nil){ + if(b->rp < b->wp){ + c = *b->rp++; + q->len--; + } + if(b->rp >= b->wp){ + q->first = b->next; + b->next = nil; + } + } + splx(s); + return c; +} + +void +qbputc(Queue *q, int c) +{ + Block *b; + + b = iallocb(1); + *b->wp++ = c; + qbwrite(q, b); +} diff --git a/os/boot/puma/rmap.c b/os/boot/puma/rmap.c new file mode 100644 index 00000000..8b8a0bef --- /dev/null +++ b/os/boot/puma/rmap.c @@ -0,0 +1,104 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +void +mapinit(RMap *rmap, Map *map, int size) +{ + lock(rmap); + rmap->map = map; + rmap->mapend = map+(size/sizeof(Map)); + unlock(rmap); +} + +void +mapfree(RMap* rmap, ulong addr, int size) +{ + Map *mp; + ulong t; + + if(size <= 0) + return; + + lock(rmap); + for(mp = rmap->map; mp->addr <= addr && mp->size; mp++) + ; + + if(mp > rmap->map && (mp-1)->addr+(mp-1)->size == addr){ + (mp-1)->size += size; + if(addr+size == mp->addr){ + (mp-1)->size += mp->size; + while(mp->size){ + mp++; + (mp-1)->addr = mp->addr; + (mp-1)->size = mp->size; + } + } + } + else{ + if(addr+size == mp->addr && mp->size){ + mp->addr -= size; + mp->size += size; + } + else do{ + if(mp >= rmap->mapend){ + print("mapfree: %s: losing 0x%uX, %d\n", + rmap->name, addr, size); + break; + } + t = mp->addr; + mp->addr = addr; + addr = t; + t = mp->size; + mp->size = size; + mp++; + }while(size = t); + } + unlock(rmap); +} + +ulong +mapalloc(RMap* rmap, ulong addr, int size, int align) +{ + Map *mp; + ulong maddr, oaddr; + + lock(rmap); + for(mp = rmap->map; mp->size; mp++){ + maddr = mp->addr; + + if(addr){ + if(maddr > addr) + continue; + if(addr+size > maddr+mp->size) + break; + maddr = addr; + } + + if(align > 0) + maddr = ((maddr+align-1)/align)*align; + if(mp->addr+mp->size-maddr < size) + continue; + + oaddr = mp->addr; + mp->addr = maddr+size; + mp->size -= maddr-oaddr+size; + if(mp->size == 0){ + do{ + mp++; + (mp-1)->addr = mp->addr; + }while((mp-1)->size = mp->size); + } + + unlock(rmap); + if(oaddr != maddr) + mapfree(rmap, oaddr, maddr-oaddr); + + return maddr; + } + unlock(rmap); + + return 0; +} diff --git a/os/boot/puma/squeeze.h b/os/boot/puma/squeeze.h new file mode 100644 index 00000000..b06c1b79 --- /dev/null +++ b/os/boot/puma/squeeze.h @@ -0,0 +1,34 @@ + +/* + * squeezed file format: + * Sqhdr + * original Exec header + * two Squeeze tables + * squeezed segment + * unsqueezed segment, if any + */ +#define SQMAGIC (ulong)0xFEEF0F1E + +typedef struct Sqhdr Sqhdr; +struct Sqhdr { + uchar magic[4]; /* SQMAGIC */ + uchar text[4]; /* squeezed length of text (excluding tables) */ + uchar data[4]; /* squeezed length of data (excluding tables) */ + uchar asis[4]; /* length of unsqueezed segment */ + uchar toptxt[4]; /* value for 0 encoding in text */ + uchar topdat[4]; /* value for 0 encoding in data */ + uchar sum[4]; /* simple checksum of unsqueezed data */ + uchar flags[4]; +}; +#define SQHDRLEN (8*4) + +/* + * certain power instruction types are rearranged by sqz + * so as to move the variable part of the instruction word to the + * low order bits. note that the mapping is its own inverse. + */ +#define QREMAP(X)\ + switch((X)>>26){\ + case 19: case 31: case 59: case 63:\ + (X) = (((X) & 0xFC00F801) | (((X)>>15)&0x7FE) | (((X)&0x7FE)<<15));\ + } diff --git a/os/boot/puma/sum.c b/os/boot/puma/sum.c new file mode 100644 index 00000000..ce7e33fd --- /dev/null +++ b/os/boot/puma/sum.c @@ -0,0 +1,13 @@ +int +sum(int a, int b, int c) +{ + return a+b+c; +} + +void +main(void) +{ + int s; + + s = sum(1, 2, 3); +} diff --git a/os/boot/puma/trap.c b/os/boot/puma/trap.c new file mode 100644 index 00000000..6322e5c7 --- /dev/null +++ b/os/boot/puma/trap.c @@ -0,0 +1,190 @@ +#include "boot.h" + +typedef struct IrqEntry { + void (*r)(Ureg*, void*); + void *a; +} IrqEntry; + +IrqEntry Irq[V_MAXNUM+1]; + +static void dumpstk(ulong *); +void dumpregs(Ureg* ureg); + +void +setvec(int v, void (*f)(Ureg*, void*), void* a) +{ + if(v < 0 || v >= V_MAXNUM) + panic("setvec: interrupt source %d out of range\n", v); + Irq[v].r = f; + Irq[v].a = a; +} + +ulong irqstack[64]; +ulong fiqstack[64]; +ulong abtstack[64]; +ulong undstack[64]; + +static void +safeintr(Ureg*, void *a) +{ + int v = (int)a; +// print("spurious interrupt %d\n", v); + USED(v); +} + +void +trapinit(void) +{ + int offset; + ulong op; + int v; + int s; + + s = splhi(); + + /* set up stacks for various exceptions */ + setr13(PsrMirq, irqstack+nelem(irqstack)-1); + setr13(PsrMfiq, fiqstack+nelem(fiqstack)-1); + setr13(PsrMabt, abtstack+nelem(abtstack)-1); + setr13(PsrMund, undstack+nelem(undstack)-1); + + for(v = 0; v <= V_MAXNUM; v++) { + Irq[v].r = safeintr; + Irq[v].a = (void *)v; + } + + /* Reset Exception */ + offset = ((((ulong) _vsvccall) - 0x0)-8) >> 2; + op = ( 0xea << 24 ) | offset; + *((ulong *) 0x0) = op; + + /* Undefined Instruction Exception */ + offset = ((((ulong) _vundcall) - 0x4)-8) >> 2; + op = ( 0xea << 24 ) | offset; + *((ulong *) 0x4) = op; + + /* SWI Exception */ + offset = ((((ulong) _vsvccall) - 0x8)-8) >> 2; + op = ( 0xea << 24 ) | offset; + *((ulong *) 0x8) = op; + + /* Prefetch Abort Exception */ + offset = ((((ulong) _vpabcall) - 0xc)-8) >> 2; + op = ( 0xea << 24 ) | offset; + *((ulong *) 0xc) = op; + + /* Data Abort Exception */ + offset = ((((ulong) _vdabcall) - 0x10)-8) >> 2; + op = ( 0xea << 24 ) | offset; + *((ulong *) 0x10) = op; + + /* IRQ Exception */ + offset = ((((ulong) _virqcall) - 0x18)-8) >> 2; + op = ( 0xea << 24 ) | offset; + *((ulong *) 0x18) = op; + + /* FIQ Exception */ + offset = ((((ulong) _vfiqcall) - 0x1c)-8) >> 2; + op = ( 0xea << 24 ) | offset; + *((ulong *) 0x1c) = op; + + + flushIcache(); + writeBackDC(); + flushDcache(); + flushIcache(); + drainWBuffer(); + + splx(s); +} + +/* + * trap is called splhi(). + */ + +void +trap(Ureg* ureg) +{ + ushort mask; + IrqEntry *ip; + + /* + * All interrupts/exceptions should be resumed at ureg->pc-4, + * except for Data Abort which resumes at ureg->pc-8. + */ + ureg->pc -= 4; + + switch(ureg->type) { + case PsrMirq: /* Interrupt Request */ + mask = *(uchar*)HARI1 | ((*(uchar*)HARI2) << 8); + ip = Irq; + while (mask != 0) { + if(mask&1) + ip->r(ureg, ip->a); + ip++; + mask >>= 1; + } + break; + + case PsrMfiq: /* FIQ */ + mask = *(uchar*)HARI1 & HARI1_FIQ_MASK; + ip = Irq; + while (mask != 0) { + if(mask&1) + ip->r(ureg, ip->a); + ip++; + mask >>= 1; + } + break; + + case PsrMund: /* Undefined instruction */ + dumpregs(ureg); + panic("Undefined Instruction Exception\n"); + break; + + case PsrMsvc: /* Jump through 0 or SWI */ + dumpregs(ureg); + panic("SVC/SWI Exception\n"); + break; + + case PsrMabt: /* Prefetch abort */ + ureg->pc -= 4; + /* FALLTHROUGH */ + + case PsrMabt+1: { /* Data abort */ + uint far =0; + uint fsr =0; + + USED(far,fsr); + fsr = 0; /*mmuregr(CpFSR);*/ + far = 0; /*mmuregr(CpFAR); */ + if (ureg->type == PsrMabt) + print("Prefetch Abort/"); + print("Data Abort\n"); + + print("Data Abort: FSR %8.8uX FAR %8.8uX\n", fsr, far); + /* FALLTHROUGH */ + } + default: + dumpregs(ureg); + panic("exception %uX\n", ureg->type); + break; + } + + splhi(); +} + +void +dumpregs(Ureg* ureg) +{ + print("PSR %8.8uX type %2.2uX PC %8.8uX LINK %8.8uX\n", + ureg->psr, ureg->type, ureg->pc, ureg->link); + print("R14 %8.8uX R13 %8.8uX R12 %8.8uX R11 %8.8uX R10 %8.8uX\n", + ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10); + print("R9 %8.8uX R8 %8.8uX R7 %8.8uX R6 %8.8uX R5 %8.8uX\n", + ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5); + print("R4 %8.8uX R3 %8.8uX R2 %8.8uX R1 %8.8uX R0 %8.8uX\n", + ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0); + print("Stack is at: %8.8uX\n", ureg); +/* print("CPSR %8.8uX SPSR %8.8uX\n", cpsrr(), spsrr());*/ +} diff --git a/os/boot/puma/ureg.h b/os/boot/puma/ureg.h new file mode 100644 index 00000000..6f853744 --- /dev/null +++ b/os/boot/puma/ureg.h @@ -0,0 +1,22 @@ +typedef struct Ureg { + uint r0; + uint r1; + uint r2; + uint r3; + uint r4; + uint r5; + uint r6; + uint r7; + uint r8; + uint r9; + uint r10; + uint r11; + uint r12; + uint r13; + uint r14; + uint link; + uint type; + uint psr; +// uint sp; + uint pc; +} Ureg; diff --git a/os/boot/puma/zqs.c b/os/boot/puma/zqs.c new file mode 100644 index 00000000..7c000c68 --- /dev/null +++ b/os/boot/puma/zqs.c @@ -0,0 +1,216 @@ +#include "boot.h" +#include "squeeze.h" + +#define EXECHDRLEN (8*4) + +typedef struct Squeeze Squeeze; +struct Squeeze { + int n; + ulong tab[7*256]; +}; + +#define GET4(p) (((((((p)[0]<<8)|(p)[1])<<8)|(p)[2])<<8)|(p)[3]) + +/* + * for speed of unsqueezing from Flash, certain checks are + * not done inside the loop (as they would be in the unsqueeze program zqs), + * but instead the checksum is expected to catch corrupted files. + * in fact the Squeeze array bounds can't be exceeded in practice + * because the tables are always full for a squeezed kernel. + */ +enum { + QFLAG = 1, /* invert powerpc-specific code transformation */ + CHECK = 0, /* check precise bounds in Squeeze array (otherwise checksum detects error) */ +}; + +static ulong chksum; +static int rdtab(Block*, Squeeze*, int); +static ulong* unsqueeze(ulong*, uchar*, uchar*, Squeeze*, Squeeze*, ulong); +static uchar* unsqzseg(uchar*, Block*, long, long, char*); +static Alarm* unsqzal; + +int +issqueezed(uchar *b) +{ + return GET4(b) == SQMAGIC? GET4(b+SQHDRLEN): 0; +} + +static void +unsqzdot(void*) +{ + unsqzal = alarm(500, unsqzdot, nil); + print("."); +} + +long +unsqueezef(Block *b, ulong *entryp) +{ + uchar *loada, *wp; + ulong toptxt, topdat, oldsum; + long asis, nst, nsd; + Sqhdr *sqh; + Exec *ex; + + if(BLEN(b) < SQHDRLEN+EXECHDRLEN) + return -1; + sqh = (Sqhdr*)b->rp; + if(GET4(sqh->magic) != SQMAGIC) + return -1; + chksum = 0; + toptxt = GET4(sqh->toptxt); + topdat = GET4(sqh->topdat); + oldsum = GET4(sqh->sum); + asis = GET4(sqh->asis); + nst = GET4(sqh->text); + nsd = GET4(sqh->data); + b->rp += SQHDRLEN; + ex = (Exec*)b->rp; + if(GET4(ex->magic) != E_MAGIC){ + print("zqs: not StrongARM executable\n"); + return -1; + } + *entryp = GET4(ex->entry); + b->rp += EXECHDRLEN; + loada = KADDR(PADDR(*entryp)); + wp = unsqzseg(loada, b, nst, toptxt, "text"); + if(wp == nil){ + print("zqs: format error\n"); + return -1; + } + if(nsd){ + wp = (uchar*)PGROUND((ulong)wp); + wp = unsqzseg(wp, b, nsd, topdat, "data"); + if(wp == nil){ + print("zqs: format error\n"); + return -1; + } + } + if(asis){ + memmove(wp, b->rp, asis); + wp += asis; + b->rp += asis; + } + if(chksum != oldsum){ + print("\nsqueezed kernel: checksum error: %8.8lux need %8.8lux\n", chksum, oldsum); + return -1; + } + return wp-loada; +} + +static uchar * +unsqzseg(uchar *wp, Block *b, long ns, long top, char *what) +{ + static Squeeze sq3, sq4; + + print("unpack %s %8.8lux %lud:", what, wp, ns); + if(ns == 0) + return wp; + if(rdtab(b, &sq3, 0) < 0) + return nil; + if(rdtab(b, &sq4, 8) < 0) + return nil; + if(BLEN(b) < ns){ + print(" **size error\n"); + return nil; + } + unsqzal = alarm(500, unsqzdot, nil); + wp = (uchar*)unsqueeze((ulong*)wp, b->rp, b->rp+ns, &sq3, &sq4, top); + cancel(unsqzal); + unsqzal = nil; + print("\n"); + if(wp == nil){ + print("zqs: corrupt squeezed data stream\n"); + return nil; + } + b->rp += ns; + return wp; +} + +static ulong* +unsqueeze(ulong *wp, uchar *rp, uchar *ep, Squeeze *sq3, Squeeze *sq4, ulong top) +{ + ulong nx, csum; + int code, n; + + if(QFLAG){ + QREMAP(top); /* adjust top just once, outside the loop */ + } + csum = chksum; + while(rp < ep){ + /* no function calls within this loop for speed */ + code = *rp; + rp++; + n = 0; + nx = code>>4; + do{ + if(nx == 0){ + nx = top; + }else{ + if(nx==1){ + nx = (((((rp[3]<<8)|rp[2])<<8)|rp[1])<<8)|rp[0]; + rp += 4; + }else if(nx <= 8){ /* 2 to 8 */ + nx = ((nx-2)<<8) | rp[0]; + if(CHECK && nx >= sq4->n) + return nil; /* corrupted file */ + nx = sq4->tab[nx] | rp[1]; + rp += 2; + }else{ /* 9 to 15 */ + nx = ((nx-9)<<8) | rp[0]; + if(CHECK && nx >= sq3->n) + return nil; /* corrupted file */ + nx = sq3->tab[nx]; + rp++; + } + if(rp > ep) + return nil; /* corrupted file */ + if(QFLAG){ + QREMAP(nx); + } + } + *wp = nx; + wp++; + csum += nx; + nx = code & 0xF; + }while(++n == 1); + } + chksum = csum; + return wp; +} + +static int +rdtab(Block *b, Squeeze *sq, int shift) +{ + uchar *p, *ep; + ulong v, w; + int i; + + if(BLEN(b) < 2) + return -1; + i = (b->rp[0]<<8) | b->rp[1]; + if(1) + print(" T%d", i); + b->rp += 2; + if((i -= 2) > 0){ + if(BLEN(b) < i) + return -1; + } + sq->n = 0; + p = b->rp; + ep = b->rp+i; + b->rp += i; + v = 0; + while(p < ep){ + w = 0; + do{ + if(p >= ep) + return -1; + w = (w<<7) | (*p & 0x7F); + }while(*p++ & 0x80); + v += w; + if(0) + print("%d %8.8lux %8.8lux\n", sq->n, v, w); + sq->tab[sq->n++] = v<next = *head; + *head = new; + }else{ + new->next = where->next; + where->next = new; + } + +} + +/* + * Delete old from list. where->next is known to be old. + */ +void +delete(List **head, List *where, List *old) +{ + if(where == 0){ + *head = old->next; + return; + } + where->next = old->next; +} + +Alarm* +newalarm(void) +{ + int i; + Alarm *a; + + for(i=0,a=alarmtab; i < nelem(alarmtab); i++,a++) + if(a->busy==0 && a->f==0){ + a->f = 0; + a->arg = 0; + a->busy = 1; + return a; + } + panic("newalarm"); + return 0; /* not reached */ +} + +Alarm* +alarm(int ms, void (*f)(Alarm*), void *arg) +{ + Alarm *a, *w, *pw; + ulong s; + + if(ms < 0) + ms = 0; + s = splhi(); + a = newalarm(); + a->dt = MS2TK(ms); + a->f = f; + a->arg = arg; + pw = 0; + for(w=m->alarm; w; pw=w, w=w->next){ + if(w->dt <= a->dt){ + a->dt -= w->dt; + continue; + } + w->dt -= a->dt; + break; + } + insert(&m->alarm, pw, a); + splx(s); + return a; +} + +void +cancel(Alarm *a) +{ + a->f = 0; +} + +void +alarminit(void) +{ +} + +#define NA 10 /* alarms per clock tick */ +void +checkalarms(void) +{ + int i, n, s; + Alarm *a; + void (*f)(Alarm*); + Alarm *alist[NA]; + + s = splhi(); + a = m->alarm; + if(a){ + for(n=0; a && a->dt<=0 && nalarm, 0, a); + a = m->alarm; + } + if(a) + a->dt--; + + for(i = 0; i < n; i++){ + f = alist[i]->f; /* avoid race with cancel */ + if(f) + (*f)(alist[i]); + alist[i]->busy = 0; + } + } + splx(s); +} diff --git a/os/boot/rpcg/all.h b/os/boot/rpcg/all.h new file mode 100644 index 00000000..0612bf04 --- /dev/null +++ b/os/boot/rpcg/all.h @@ -0,0 +1,6 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "mem.h" +#include "io.h" diff --git a/os/boot/rpcg/archrpcg.c b/os/boot/rpcg/archrpcg.c new file mode 100644 index 00000000..87df2196 --- /dev/null +++ b/os/boot/rpcg/archrpcg.c @@ -0,0 +1,279 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "archrpcg.h" +#include "etherif.h" + +/* + * board-specific support for the RPCG RXLite + */ + +enum { + SYSMHZ = 66, /* target frequency */ + + /* sccr */ + RTSEL = IBIT(8), /* =0, select main oscillator (OSCM); =1, select external crystal (EXTCLK) */ + RTDIV = IBIT(7), /* =0, divide by 4; =1, divide by 512 */ + CRQEN = IBIT(9), /* =1, switch to high frequency when CPM active */ + PRQEN = IBIT(10), /* =1, switch to high frequency when interrupt pending */ + + /* plprcr */ + CSRC = IBIT(21), /* =0, clock is DFNH; =1, clock is DFNL */ +}; + +static char flashsig[] = "RPXsignature=1.0\nNAME=qbrpcg\nSTART=FFC20100\nVERSION=1.1\n"; +static char* geteeprom(char*); + +/* + * called early in main.c, after machinit: + * using board and architecture specific registers, initialise + * 8xx registers that need it and complete initialisation of the Mach structure. + */ +void +archinit(void) +{ + IMM *io; + int mf, t; + ulong v; + + v = getimmr() & 0xFFFF; + switch(v>>8){ + case 0x00: t = 0x86000; break; + case 0x20: t = 0x82300; break; + case 0x21: t = 0x823a0; break; + default: t = 0; break; + } + m->cputype = t; + m->bcsr = KADDR(BCSRMEM); + io = m->iomem; + m->clockgen = 8*MHz; + mf = (io->plprcr >> 20)+1; /* use timing set by bootstrap */ + m->cpuhz = m->clockgen*mf; + m->bcsr[0] = DisableColTest | DisableFullDplx | DisableUSB | HighSpdUSB | LedOff; /* first write enables bcsr regs */ +return; + io->plprcrk = KEEP_ALIVE_KEY; + io->plprcr &= ~CSRC; /* general system clock is DFNH */ +/* io->mptpr = 0x0800; /* memory prescaler = 8 for refresh */ + /* use memory refresh time set by RPXLite monitor */ + io->plprcrk = ~KEEP_ALIVE_KEY; +} + +void +cpuidprint(void) +{ + int t, v; + + print("Inferno bootstrap\n"); + print("PVR: "); + t = getpvr()>>16; + switch(t){ + case 0x01: print("MPC601"); break; + case 0x03: print("MPC603"); break; + case 0x04: print("MPC604"); break; + case 0x06: print("MPC603e"); break; + case 0x07: print("MPC603e-v7"); break; + case 0x50: print("MPC8xx"); break; + default: print("PowerPC version #%x", t); break; + } + print(", revision #%lux\n", getpvr()&0xffff); + print("IMMR: "); + v = getimmr() & 0xFFFF; + switch(v>>8){ + case 0x00: print("MPC860/821"); break; + case 0x20: print("MPC823"); break; + case 0x21: print("MPC823A"); break; + default: print("Type #%lux", v>>8); break; + } + print(", mask #%lux\n", v&0xFF); + print("options: #%lux\n", archoptionsw()); + print("bcsr: %8.8lux\n", m->bcsr[0]); + print("PLPRCR=%8.8lux SCCR=%8.8lux\n", m->iomem->plprcr, m->iomem->sccr); + print("%lud MHz system\n", m->cpuhz/MHz); + print("\n"); +//print("%s\n", geteeprom("EA")); +print("BR0=%8.8lux OR0=%8.8lux\n", m->iomem->memc[0].base, m->iomem->memc[0].option); +print("MPTPR=%8.8lux\n", m->iomem->mptpr); +} + +static char* defplan9ini[2] = { + /* 860/821 */ + "ether0=type=SCC port=1 ea=0010ec000051\r\n" + "vgasize=640x480x8\r\n" + "kernelpercent=40\r\n" + "console=0\r\nbaud=9600\r\n", + + /* 823 */ + "ether0=type=SCC port=2 ea=0010ec000051\r\n" + "vgasize=640x480x8\r\n" + "kernelpercent=40\r\n" + "console=0\r\nbaud=9600\r\n", +}; + +char * +archconfig(void) +{ + print("Using default configuration\n"); + return defplan9ini[MPCMODEL(m->cputype) == 0x823]; +} + +/* + * provide value for #r/switch (devrtc.c) + */ +int +archoptionsw(void) +{ + return (m->bcsr[0]&DipSwitchMask)>>4; +} + +/* + * invoked by clock.c:/^clockintr + */ +static void +twinkle(void) +{ + if(m->ticks%MS2TK(1000) == 0) + m->bcsr[0] ^= LedOff; +} + +void (*archclocktick)(void) = twinkle; + +/* + * for flash.c:/^flashreset + * retrieve flash type, virtual base and length and return 0; + * return -1 on error (no flash) + */ +int +archflashreset(char *type, void **addr, long *length) +{ + if((m->iomem->memc[BOOTCS].base & 1) == 0) + return -1; /* shouldn't happen */ + strcpy(type, "AMD29F0x0"); + *addr = KADDR(FLASHMEM); + *length = 4*1024*1024; + return 0; +} + +int +archether(int ctlrno, Card *ether) +{ + char *ea; + + if(ctlrno > 0) + return -1; + strcpy(ether->type, "SCC"); + ether->port = 2; + ea = geteeprom("EA"); + if(ea != nil) + parseether(ether->ea, ea); + return 1; +} + +/* + * enable the clocks for the given SCC ether and reveal them to the caller. + * do anything else required to prepare the transceiver (eg, set full-duplex, reset loopback). + */ +int +archetherenable(int cpmid, int *rcs, int *tcs) +{ + IMM *io; + + switch(cpmid){ + default: + /* no other SCCs are wired for ether on RPXLite*/ + return -1; + + case SCC2ID: + io = ioplock(); + m->bcsr[0] |= EnableEnet; + io->papar |= SIBIT(6)|SIBIT(4); /* enable CLK2 and CLK4 */ + io->padir &= ~(SIBIT(6)|SIBIT(4)); + *rcs = CLK4; + *tcs = CLK2; + iopunlock(); + break; + } + return 0; +} + +void +archetherdisable(int id) +{ + USED(id); + m->bcsr[0] &= ~EnableEnet; +} + +/* + * do anything extra required to enable the UART on the given CPM port + */ +void +archenableuart(int id, int irda) +{ + USED(id, irda); +} + +/* + * do anything extra required to disable the UART on the given CPM port + */ +void +archdisableuart(int id) +{ + USED(id); +} + +/* + * enable/disable the LCD panel's backlight + */ +void +archbacklight(int on) +{ + USED(on); +} + +static char* +geteeprom(char *s) +{ + static int init; + static char res[64]; + static uchar eeprom[257]; + uchar *l, *p; + int i, j; + + if(!init){ + i2csetup(); + if(i2crecv(0xa8|1|(0<<8), eeprom, 128) < 0 || + i2crecv(0xa8|1|(128<<8), eeprom+128, 128) < 0){ + print("i2c failed\n"); + return nil; + } + if(0){ + print("eeprom:\n"); + for(i=0; i<16; i++){for(j=0; j<16; j++)print(" %2.2ux[%c]", eeprom[i*16+j], eeprom[i*16+j]); print("\n");} + } + eeprom[256] = 0xFF; + init = 1; + } + for(l = eeprom; *l != 0xFF && *l != '\n';){ + p = l; + while(*l != '\n' && *l != 0xFF && *l != '=') + l++; + if(*l == '='){ + if(l-p == strlen(s) && strncmp(s, (char*)p, strlen(s)) == 0){ + p = l+1; + while(*l != '\n' && *l != 0xFF) + l++; + memmove(res, p, l-p); + res[l-p] = 0; + return res; + } + } + while(*l != '\n' && *l != 0xFF) + l++; + if(*l == '\n') + l++; + } + return nil; +} diff --git a/os/boot/rpcg/archrpcg.h b/os/boot/rpcg/archrpcg.h new file mode 100644 index 00000000..2ea4e39b --- /dev/null +++ b/os/boot/rpcg/archrpcg.h @@ -0,0 +1,48 @@ +/* + * values for RPXLite AW + */ +enum { + /* CS assignment */ + BOOTCS = 0, + DRAM1 = 1, + /* CS2 is routed to expansion header */ + BCSRCS = 3, + NVRAMCS = 4, + /* CS5 is routed to expansion header */ + PCMCIA0CS = 6, /* select even bytes */ + PCMCIA1CS = 7, /* select odd bytes */ +}; + +/* + * BCSR bits (there are 4 8-bit registers that we access as ulong) + */ +enum { + EnableEnet = IBIT(0), + EnableXcrLB= IBIT(1), + DisableColTest= IBIT(2), + DisableFullDplx=IBIT(3), + LedOff= IBIT(4), + DisableUSB= IBIT(5), + HighSpdUSB= IBIT(6), + EnableUSBPwr= IBIT(7), + /* 8,9,10 unused */ + PCCVCCMask= IBIT(12)|IBIT(13), + PCCVPPMask= IBIT(14)|IBIT(15), + PCCVCC0V= 0, + PCCVCC5V= IBIT(13), + PCCVCC3V= IBIT(12), + PCCVPP0V= 0, + PCCVPP5V= IBIT(14), + PCCVPP12V= IBIT(15), + PCCVPPHiZ= IBIT(14)|IBIT(15), + /* 16-23 NYI */ + DipSwitchMask= IBIT(24)|IBIT(25)|IBIT(26)|IBIT(27), + DipSwitch0= IBIT(24), + DipSwitch1= IBIT(25), + DipSwitch2= IBIT(26), + DipSwitch3= IBIT(27), + /* bit 28 RESERVED */ + FlashComplete= IBIT(29), + NVRAMBattGood= IBIT(30), + RTCBattGood= IBIT(31), +}; diff --git a/os/boot/rpcg/boot.h b/os/boot/rpcg/boot.h new file mode 100644 index 00000000..1b2a09fb --- /dev/null +++ b/os/boot/rpcg/boot.h @@ -0,0 +1,8 @@ +#include +#include "lib.h" + +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" diff --git a/os/boot/rpcg/bootp.c b/os/boot/rpcg/bootp.c new file mode 100644 index 00000000..13e7ba1a --- /dev/null +++ b/os/boot/rpcg/bootp.c @@ -0,0 +1,509 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "ip.h" + +#define XPADDR(a) ((ulong)(a) & ~KSEGM) + +enum { + CHECKSUM = 1, /* set zero if trouble booting from Linux */ +}; + +uchar broadcast[Eaddrlen] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +}; + +static ushort tftpport = 5000; +static int Id = 1; +static Netaddr myaddr; +static Netaddr server; + +typedef struct { + uchar header[4]; + uchar data[Segsize]; +} Tftp; +static Tftp tftpb; + +static void +hnputs(uchar *ptr, ushort val) +{ + ptr[0] = val>>8; + ptr[1] = val; +} + +static void +hnputl(uchar *ptr, ulong val) +{ + ptr[0] = val>>24; + ptr[1] = val>>16; + ptr[2] = val>>8; + ptr[3] = val; +} + +static ulong +nhgetl(uchar *ptr) +{ + return ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]); +} + +static ushort +nhgets(uchar *ptr) +{ + return ((ptr[0]<<8) | ptr[1]); +} + +static short endian = 1; +static char* aendian = (char*)&endian; +#define LITTLE *aendian + +static ushort +ptcl_csum(void *a, int len) +{ + uchar *addr; + ulong t1, t2; + ulong losum, hisum, mdsum, x; + + addr = a; + losum = 0; + hisum = 0; + mdsum = 0; + + x = 0; + if((ulong)addr & 1) { + if(len) { + hisum += addr[0]; + len--; + addr++; + } + x = 1; + } + while(len >= 16) { + t1 = *(ushort*)(addr+0); + t2 = *(ushort*)(addr+2); mdsum += t1; + t1 = *(ushort*)(addr+4); mdsum += t2; + t2 = *(ushort*)(addr+6); mdsum += t1; + t1 = *(ushort*)(addr+8); mdsum += t2; + t2 = *(ushort*)(addr+10); mdsum += t1; + t1 = *(ushort*)(addr+12); mdsum += t2; + t2 = *(ushort*)(addr+14); mdsum += t1; + mdsum += t2; + len -= 16; + addr += 16; + } + while(len >= 2) { + mdsum += *(ushort*)addr; + len -= 2; + addr += 2; + } + if(x) { + if(len) + losum += addr[0]; + if(LITTLE) + losum += mdsum; + else + hisum += mdsum; + } else { + if(len) + hisum += addr[0]; + if(LITTLE) + hisum += mdsum; + else + losum += mdsum; + } + + losum += hisum >> 8; + losum += (hisum & 0xff) << 8; + while(hisum = losum>>16) + losum = hisum + (losum & 0xffff); + + return ~losum; +} + +static ushort +ip_csum(uchar *addr) +{ + int len; + ulong sum = 0; + + len = (addr[0]&0xf)<<2; + + while(len > 0) { + sum += addr[0]<<8 | addr[1] ; + len -= 2; + addr += 2; + } + + sum = (sum & 0xffff) + (sum >> 16); + sum = (sum & 0xffff) + (sum >> 16); + return (sum^0xffff); +} + +static void +udpsend(int ctlrno, Netaddr *a, void *data, int dlen) +{ + Udphdr *uh; + Etherhdr *ip; + static Etherpkt pkt; + int len, ptcllen; + + + uh = (Udphdr*)&pkt; + + memset(uh, 0, sizeof(Etherpkt)); + memmove(uh->udpcksum+sizeof(uh->udpcksum), data, dlen); + + /* + * UDP portion + */ + ptcllen = dlen + (UDP_HDRSIZE-UDP_PHDRSIZE); + uh->ttl = 0; + uh->udpproto = IP_UDPPROTO; + uh->frag[0] = 0; + uh->frag[1] = 0; + hnputs(uh->udpplen, ptcllen); + hnputl(uh->udpsrc, myaddr.ip); + hnputs(uh->udpsport, myaddr.port); + hnputl(uh->udpdst, a->ip); + hnputs(uh->udpdport, a->port); + hnputs(uh->udplen, ptcllen); + uh->udpcksum[0] = 0; + uh->udpcksum[1] = 0; + /*dlen = (dlen+1)&~1; */ + hnputs(uh->udpcksum, ptcl_csum(&uh->ttl, dlen+UDP_HDRSIZE)); + + /* + * IP portion + */ + ip = (Etherhdr*)&pkt; + len = sizeof(Udphdr)+dlen; + ip->vihl = IP_VER|IP_HLEN; + ip->tos = 0; + ip->ttl = 255; + hnputs(ip->length, len-ETHER_HDR); + hnputs(ip->id, Id++); + ip->frag[0] = 0; + ip->frag[1] = 0; + ip->cksum[0] = 0; + ip->cksum[1] = 0; + hnputs(ip->cksum, ip_csum(&ip->vihl)); + + /* + * Ethernet MAC portion + */ + hnputs(ip->type, ET_IP); + memmove(ip->d, a->ea, sizeof(ip->d)); + + ethertxpkt(ctlrno, &pkt, len, Timeout); +} + +static void +nak(int ctlrno, Netaddr *a, int code, char *msg, int report) +{ + int n; + char buf[128]; + + buf[0] = 0; + buf[1] = Tftp_ERROR; + buf[2] = 0; + buf[3] = code; + strcpy(buf+4, msg); + n = strlen(msg) + 4 + 1; + udpsend(ctlrno, a, buf, n); + if(report) + print("\ntftp: error(%d): %s\n", code, msg); +} + +static int +udprecv(int ctlrno, Netaddr *a, void *data, int dlen) +{ + int n, len; + ushort csm; + Udphdr *h; + ulong addr, timo; + static Etherpkt pkt; + static int rxactive; + + if(rxactive == 0) + timo = 1000; + else + timo = Timeout; + timo += TK2MS(m->ticks); + while(timo > TK2MS(m->ticks)){ + n = etherrxpkt(ctlrno, &pkt, timo-TK2MS(m->ticks)); + if(n <= 0) + continue; + + h = (Udphdr*)&pkt; + if(nhgets(h->type) != ET_IP) + continue; + + if(ip_csum(&h->vihl)) { + print("ip chksum error\n"); + continue; + } + if(h->vihl != (IP_VER|IP_HLEN)) { + print("ip bad vers/hlen\n"); + continue; + } + + if(h->udpproto != IP_UDPPROTO) + continue; + + h->ttl = 0; + len = nhgets(h->udplen); + hnputs(h->udpplen, len); + + if(CHECKSUM && nhgets(h->udpcksum)) { + csm = ptcl_csum(&h->ttl, len+UDP_PHDRSIZE); + if(csm != 0) { + print("udp chksum error csum #%4lux len %d\n", csm, n); + break; + } + } + + if(a->port != 0 && nhgets(h->udpsport) != a->port) + continue; + + addr = nhgetl(h->udpsrc); + if(a->ip != Bcastip && addr != a->ip) + continue; + + len -= UDP_HDRSIZE-UDP_PHDRSIZE; + if(len > dlen) { + print("udp: packet too big\n"); + continue; + } + + memmove(data, h->udpcksum+sizeof(h->udpcksum), len); + a->ip = addr; + a->port = nhgets(h->udpsport); + memmove(a->ea, pkt.s, sizeof(a->ea)); + + rxactive = 1; + return len; + } + + return 0; +} + +static int tftpblockno; + +static int +tftpopen(int ctlrno, Netaddr *a, char *name, Tftp *tftp) +{ + int i, len, rlen, oport; + char buf[Segsize+2]; + + buf[0] = 0; + buf[1] = Tftp_READ; + len = sprint(buf+2, "%s", name) + 2; + len += sprint(buf+len+1, "octet") + 2; + + oport = a->port; + for(i = 0; i < 5; i++){ + a->port = oport; + udpsend(ctlrno, a, buf, len); + a->port = 0; + if((rlen = udprecv(ctlrno, a, tftp, sizeof(Tftp))) < sizeof(tftp->header)) + continue; + + switch((tftp->header[0]<<8)|tftp->header[1]){ + + case Tftp_ERROR: + print("tftpopen: error (%d): %s\n", + (tftp->header[2]<<8)|tftp->header[3], tftp->data); + return -1; + + case Tftp_DATA: + tftpblockno = 1; + len = (tftp->header[2]<<8)|tftp->header[3]; + if(len != tftpblockno){ + print("tftpopen: block error: %d\n", len); + nak(ctlrno, a, 1, "block error", 0); + return -1; + } + return rlen-sizeof(tftp->header); + } + } + + print("tftpopen: failed to connect to server\n"); + return -1; +} + +static int +tftpread(int ctlrno, Netaddr *a, Tftp *tftp, int dlen) +{ + int blockno, len, retry; + uchar buf[4]; + + buf[0] = 0; + buf[1] = Tftp_ACK; + buf[2] = tftpblockno>>8; + buf[3] = tftpblockno; + tftpblockno++; + + dlen += sizeof(tftp->header); + + retry = 0; +buggery: + udpsend(ctlrno, a, buf, sizeof(buf)); + + if((len = udprecv(ctlrno, a, tftp, dlen)) < dlen){ + print("tftpread: %d != %d\n", len, dlen); + nak(ctlrno, a, 2, "short read", 0); + if(retry++ < 5) + goto buggery; + return -1; + } + + blockno = (tftp->header[2]<<8)|tftp->header[3]; + if(blockno != tftpblockno){ + print("?"); + + if(blockno == tftpblockno-1 && retry++ < 8) + goto buggery; + print("tftpread: block error: %d, expected %d\n", blockno, tftpblockno); + nak(ctlrno, a, 1, "block error", 0); + + return -1; + } + + return len-sizeof(tftp->header); +} + +int +bootp(int ctlrno, char *file) +{ + Bootp req, rep; + int i, dlen, segsize, text, data, bss, total; + uchar *ea, *addr, *p; + ulong entry; + Exec *exec; + char name[128], *filename, *sysname; + + if((ea = etheraddr(ctlrno)) == 0){ + print("invalid ctlrno %d\n", ctlrno); + return -1; + } + + filename = 0; + sysname = 0; + if(file && *file){ + strcpy(name, file); + if(filename = strchr(name, ':')){ + if(filename != name && *(filename-1) != '\\'){ + sysname = name; + *filename++ = 0; + } + } + else + filename = name; + } + + + memset(&req, 0, sizeof(req)); + req.op = Bootrequest; + req.htype = 1; /* ethernet */ + req.hlen = Eaddrlen; /* ethernet */ + memmove(req.chaddr, ea, Eaddrlen); + + myaddr.ip = 0; + myaddr.port = BPportsrc; + memmove(myaddr.ea, ea, Eaddrlen); + + for(i = 0; i < 10; i++) { + server.ip = Bcastip; + server.port = BPportdst; + memmove(server.ea, broadcast, sizeof(server.ea)); + udpsend(ctlrno, &server, &req, sizeof(req)); + if(udprecv(ctlrno, &server, &rep, sizeof(rep)) <= 0) + continue; + if(memcmp(req.chaddr, rep.chaddr, Eaddrlen)) + continue; + if(rep.htype != 1 || rep.hlen != Eaddrlen) + continue; + if(sysname == 0 || strcmp(sysname, rep.sname) == 0) + break; + } + if(i >= 10) { + print("bootp timed out\n"); + return -1; + } + + if(filename == 0 || *filename == 0) + filename = rep.file; + + if(rep.sname[0] != '\0') + print("%s ", rep.sname); + print("(%d.%d.%d.%d!%d): %s\n", + rep.siaddr[0], + rep.siaddr[1], + rep.siaddr[2], + rep.siaddr[3], + server.port, + filename);uartwait(); + + myaddr.ip = nhgetl(rep.yiaddr); + myaddr.port = tftpport++; + server.ip = nhgetl(rep.siaddr); + server.port = TFTPport; + + if((dlen = tftpopen(ctlrno, &server, filename, &tftpb)) < 0) + return -1; + exec = (Exec*)(tftpb.data); + if(dlen < sizeof(Exec) || GLLONG(exec->magic) != Q_MAGIC){ + nak(ctlrno, &server, 0, "bad magic number", 1); + return -1; + } + text = GLLONG(exec->text); + data = GLLONG(exec->data); + bss = GLLONG(exec->bss); + total = text+data+bss; + entry = GLLONG(exec->entry); +print("load@%8.8lux: ", XPADDR(entry));uartwait(); + print("%d", text); + + addr = (uchar*)XPADDR(entry); + p = tftpb.data+sizeof(Exec); + dlen -= sizeof(Exec); + segsize = text; + for(;;){ + if(dlen == 0){ + if((dlen = tftpread(ctlrno, &server, &tftpb, sizeof(tftpb.data))) < 0) + return -1; + p = tftpb.data; + } + if(segsize <= dlen) + i = segsize; + else + i = dlen; + memmove(addr, p, i); + + addr += i; + p += i; + segsize -= i; + dlen -= i; + + if(segsize <= 0){ + if(data == 0) + break; + print("+%d", data); + segsize = data; + data = 0; + addr = (uchar*)PGROUND((ulong)addr); + } + } + nak(ctlrno, &server, 3, "ok", 0); /* tftpclose */ + print("+%d=%d\n", bss, total); + print("entry: 0x%lux\n", entry); + uartwait(); + scc2stop(); + splhi(); + (*(void(*)(void))(XPADDR(entry)))(); + + return 0; +} diff --git a/os/boot/rpcg/clock.c b/os/boot/rpcg/clock.c new file mode 100644 index 00000000..4d2d6bfd --- /dev/null +++ b/os/boot/rpcg/clock.c @@ -0,0 +1,71 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +enum { + Timebase = 4, /* system clock cycles per time base cycle */ +}; + +void (*archclocktick)(void); /* set by arch*.c when desired */ + +static ulong clkreload; + +void +delay(int l) +{ + ulong i, j; + + j = m->delayloop; + while(l-- > 0) + for(i=0; i < j; i++) + ; +} + +void +microdelay(int l) +{ + ulong i; + + l *= m->delayloop; + l /= 1000; + if(l <= 0) + l = 1; + for(i = 0; i < l; i++) + ; +} + +void +clockintr(Ureg*, void*) +{ + putdec(clkreload); + m->ticks++; + checkalarms(); + if(archclocktick != nil) + archclocktick(); +} + +void +clockinit(void) +{ + long x; + + m->delayloop = m->cpuhz/1000; /* initial estimate */ + do { + x = gettbl(); + delay(10); + x = gettbl() - x; + } while(x < 0); + + /* + * fix count + */ + m->delayloop = ((vlong)m->delayloop*(10*m->clockgen/1000))/(x*Timebase); + if(m->delayloop == 0) + m->delayloop = 1; + clkreload = (m->clockgen/Timebase)/HZ-1; + putdec(clkreload); +} diff --git a/os/boot/rpcg/conf.c b/os/boot/rpcg/conf.c new file mode 100644 index 00000000..4ecb4b3e --- /dev/null +++ b/os/boot/rpcg/conf.c @@ -0,0 +1,173 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "dosfs.h" + +static char *confname[MAXCONF]; +static char *confval[MAXCONF]; +static int nconf; + +extern char **ini; + +char* +getconf(char *name) +{ + int i; + + for(i = 0; i < nconf; i++) + if(strcmp(confname[i], name) == 0) + return confval[i]; + return 0; +} + +/* + * read configuration file + */ +int +plan9ini(Dos *dos, char *val) +{ + Dosfile rc; + int i, n; + char *cp, *p, *q, *line[MAXCONF]; + + cp = BOOTARGS; + if(dos) { + if(dosstat(dos, *ini, &rc) <= 0) + return -1; + + *cp = 0; + n = dosread(&rc, cp, BOOTARGSLEN-1); + if(n <= 0) + return -1; + cp[n] = 0; + } else if(val != nil){ + if(memchr(val, 0, BOOTARGSLEN-1) == nil) + return -1; + print("Using flash configuration\n"); + strcpy(cp, val); + n = strlen(cp); + }else{ + strcpy(cp, archconfig()); + n = strlen(cp); + } + + /* + * Make a working copy. + * We could change this to pass the parsed strings + * to the booted programme instead of the raw + * string, then it only gets done once. + */ + memmove(cp+BOOTARGSLEN, cp, n+1); + cp += BOOTARGSLEN; + + /* + * Strip out '\r', change '\t' -> ' '. + */ + p = cp; + for(q = cp; *q; q++){ + if(*q == '\r') + continue; + if(*q == '\t') + *q = ' '; + *p++ = *q; + } + *p = 0; + n = getcfields(cp, line, MAXCONF, "\n"); + for(i = 0; i < n; i++){ + cp = strchr(line[i], '='); + if(cp == 0) + continue; + *cp++ = 0; + if(cp - line[i] >= NAMELEN+1) + *(line[i]+NAMELEN-1) = 0; + confname[nconf] = line[i]; + confval[nconf] = cp; + nconf++; + } + return 0; +} + +int +parseether(uchar *to, char *from) +{ + char nip[4]; + char *p; + int i; + + p = from; + while(*p == ' ') + ++p; + for(i = 0; i < 6; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +int +isaconfig(char *class, int ctlrno, ISAConf *isa) +{ + char cc[NAMELEN], *p, *q, *r; + int n; + + sprint(cc, "%s%d", class, ctlrno); + for(n = 0; n < nconf; n++){ + if(strncmp(confname[n], cc, NAMELEN)) + continue; + isa->nopt = 0; + p = confval[n]; + while(*p){ + while(*p == ' ' || *p == '\t') + p++; + if(*p == '\0') + break; + if(strncmp(p, "type=", 5) == 0){ + p += 5; + for(q = isa->type; q < &isa->type[NAMELEN-1]; q++){ + if(*p == '\0' || *p == ' ' || *p == '\t') + break; + *q = *p++; + } + *q = '\0'; + } + else if(strncmp(p, "port=", 5) == 0) + isa->port = strtoul(p+5, &p, 0); + else if(strncmp(p, "irq=", 4) == 0) + isa->irq = strtoul(p+4, &p, 0); + else if(strncmp(p, "mem=", 4) == 0) + isa->mem = strtoul(p+4, &p, 0); + else if(strncmp(p, "size=", 5) == 0) + isa->size = strtoul(p+5, &p, 0); + else if(strncmp(p, "ea=", 3) == 0){ + if(parseether(isa->ea, p+3) == -1) + memset(isa->ea, 0, 6); + } + else if(isa->nopt < NISAOPT){ + r = isa->opt[isa->nopt]; + while(*p && *p != ' ' && *p != '\t'){ + *r++ = *p++; + if(r-isa->opt[isa->nopt] >= ISAOPTLEN-1) + break; + } + *r = '\0'; + isa->nopt++; + } + while(*p && *p != ' ' && *p != '\t') + p++; + } + return 1; + } + return 0; +} diff --git a/os/boot/rpcg/console.c b/os/boot/rpcg/console.c new file mode 100644 index 00000000..9a05c122 --- /dev/null +++ b/os/boot/rpcg/console.c @@ -0,0 +1,173 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +static Queue* consiq; +static Queue* consoq; + +void +bothputs(char *s, int n) +{ + uartputs(s, n); + screenputs(s, n); +} + +static void (*consputs)(char*, int) = bothputs; /* or screenputs */ + +void +consinit(void) +{ + char *p; + int baud, port; + static int cgadone; + + p = getconf("console"); + if(0) + if(p == 0 || strcmp(p, "lcd") == 0 || strcmp(p, "screen") == 0){ + consiq = qopen(4*1024, 0, 0, 0); + consoq = qopen(8*1024, 0, 0, 0); + consputs = screenputs; + return; + } + if(p!=0 && strstr(p, "lcd") == 0) + consputs = bothputs; + else + consputs = uartputs; +//consputs = screenputs; + port = 0; + if(p) + port = strtoul(p, 0, 0); + baud = 0; + if(p = getconf("baud")) + baud = strtoul(p, 0, 0); + if(baud == 0) + baud = 9600; + uartspecial(port, baud, &consiq, &consoq, kbdchar); +} + +void +kbdchar(Queue *q, int c) +{ + c &= 0x7F; + if(c == 0x10) + panic("^p"); + qbputc(q, c); +} + +static int +getline(char *buf, int size, int dotimeout) +{ + int c, i=0; + ulong start; + char echo; + + for (;;) { + start = m->ticks; + do{ + if(dotimeout && ((m->ticks - start) > 5*HZ)) + return -2; + c = qbgetc(consiq); + }while(c == -1); + if(c == '\r') + c = '\n'; /* turn carriage return into newline */ + if(c == '\177') + c = '\010'; /* turn delete into backspace */ + if(c == '\025') + echo = '\n'; /* echo ^U as a newline */ + else + echo = c; + (*consputs)(&echo, 1); + + if(c == '\010'){ + if(i > 0) + i--; /* bs deletes last character */ + continue; + } + /* a newline ends a line */ + if (c == '\n') + break; + /* ^U wipes out the line */ + if (c =='\025') + return -1; + if(i == size) + return size; + buf[i++] = c; + } + buf[i] = 0; + return i; +} + +int +getstr(char *prompt, char *buf, int size, char *def) +{ + int len, isdefault; + + buf[0] = 0; + isdefault = (def && *def); + for (;;) { + if(isdefault) + print("%s[default==%s]: ", prompt, def); + else + print("%s: ", prompt); + len = getline(buf, size, isdefault); + switch(len){ + case -1: + /* ^U typed */ + continue; + case -2: + /* timeout, use default */ + (*consputs)("\n", 1); + len = 0; + break; + default: + break; + } + if(len >= size){ + print("line too long\n"); + continue; + } + break; + } + if(len == 0 && isdefault) + strcpy(buf, def); + return 0; +} + +int +sprint(char *s, char *fmt, ...) +{ + return donprint(s, s+PRINTSIZE, fmt, (&fmt+1)) - s; +} + +int +print(char *fmt, ...) +{ + char buf[PRINTSIZE]; + int n; + + if(consputs == 0) + return 0; + n = donprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf; + (*consputs)(buf, n); + return n; +} + +void +panic(char *fmt, ...) +{ + char buf[PRINTSIZE]; + int n; + + if(consputs){ + (*consputs)("panic: ", 7); + n = donprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf; + (*consputs)(buf, n); + (*consputs)("\n", 1); + } + spllo(); + for(;;) + idle(); +} diff --git a/os/boot/rpcg/cpm.c b/os/boot/rpcg/cpm.c new file mode 100644 index 00000000..a9a45d60 --- /dev/null +++ b/os/boot/rpcg/cpm.c @@ -0,0 +1,162 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +enum { + BDSIZE= 1024, /* TO DO: check this */ +}; + +static Map bdmapv[BDSIZE/sizeof(BD)]; +static RMap bdmap = {"buffer descriptors"}; + +void +cpminit(void) +{ + IMM *io; + + io = m->iomem; + io->sdcr = 1; + io->lccr &= ~1; /* disable LCD */ + io->pcint = 0; /* disable all port C interrupts */ + io->pcso = 0; + io->pcdir =0; + io->pcpar = 0; + io->pcdat = 0; + io->papar = 0; + io->padir = 0; + io->paodr = 0; + io->padat = 0; + io->pbpar = 0; + io->pbdir = 0; + io->pbodr = 0; + io->pbdat = 0; + eieio(); + + for(io->cpcr = 0x8001; io->cpcr & 1;) /* reset all CPM channels */ + eieio(); + + mapinit(&bdmap, bdmapv, sizeof(bdmapv)); + mapfree(&bdmap, DPBASE, BDSIZE); +} + +void +cpmop(int op, int cno, int param) +{ + IMM *io; + int s; + + s = splhi(); + io = m->iomem; + eieio(); + while(io->cpcr & 1) + eieio(); + io->cpcr = (op<<8)|(cno<<4)|(param<<1)|1; + eieio(); + while(io->cpcr & 1) + eieio(); + splx(s); +} + +/* + * connect SCCx clocks in NSMI mode (x=1 for USB) + */ +void +sccnmsi(int x, int rcs, int tcs) +{ + IMM *io; + ulong v; + int sh; + + sh = (x-1)*8; /* each SCCx field in sicr is 8 bits */ + v = (((rcs&7)<<3) | (tcs&7)) << sh; + io = ioplock(); + io->sicr = (io->sicr & ~(0xFF<gsmrl & (3<<4)){ + cpmop(GracefulStopTx, SCC2ID, 0); + cpmop(CloseRxBD, SCC2ID, 0); + delay(1); + scc->gsmrl &= ~(3<<4); /* disable current use */ + archetherdisable(SCC2ID); + } +} + +BD * +bdalloc(int n) +{ + ulong a; + + a = mapalloc(&bdmap, 0, n*sizeof(BD), 0); + if(a == 0) + panic("bdalloc"); + return KADDR(a); +} + +void +bdfree(BD *b, int n) +{ + if(b){ + eieio(); + mapfree(&bdmap, PADDR(b), n*sizeof(BD)); + } +} + +/* + * initialise receive and transmit buffer rings. + */ +int +ioringinit(Ring* r, int nrdre, int ntdre, int bufsize) +{ + int i, x; + + /* the ring entries must be aligned on sizeof(BD) boundaries */ + r->nrdre = nrdre; + if(r->rdr == nil) + r->rdr = bdalloc(nrdre); + /* the buffer size must align with cache lines since the cache doesn't snoop */ + bufsize = (bufsize+CACHELINESZ-1)&~(CACHELINESZ-1); + if(r->rrb == nil) + r->rrb = malloc(nrdre*bufsize); + if(r->rdr == nil || r->rrb == nil) + return -1; + dcflush(r->rrb, nrdre*bufsize); + x = PADDR(r->rrb); + for(i = 0; i < nrdre; i++){ + r->rdr[i].length = 0; + r->rdr[i].addr = x; + r->rdr[i].status = BDEmpty|BDInt; + x += bufsize; + } + r->rdr[i-1].status |= BDWrap; + r->rdrx = 0; + + r->ntdre = ntdre; + if(r->tdr == nil) + r->tdr = bdalloc(ntdre); + if(r->txb == nil) + r->txb = malloc(ntdre*sizeof(Block*)); + if(r->tdr == nil || r->txb == nil) + return -1; + for(i = 0; i < ntdre; i++){ + r->txb[i] = nil; + r->tdr[i].addr = 0; + r->tdr[i].length = 0; + r->tdr[i].status = 0; + } + r->tdr[i-1].status |= BDWrap; + r->tdrh = 0; + r->tdri = 0; + r->ntq = 0; + return 0; +} diff --git a/os/boot/rpcg/crc32.c b/os/boot/rpcg/crc32.c new file mode 100644 index 00000000..78bdacba --- /dev/null +++ b/os/boot/rpcg/crc32.c @@ -0,0 +1,42 @@ +#include "boot.h" + +/* + * from Rob Warnock + */ +static ulong crc32tab[256]; /* initialised on first call to crc32 */ + +enum { + CRC32POLY = 0x04c11db7 /* AUTODIN II, Ethernet, & FDDI */ +}; + +/* + * Build auxiliary table for parallel byte-at-a-time CRC-32. + */ +static void +initcrc32(void) +{ + int i, j; + ulong c; + + for(i = 0; i < 256; i++) { + for(c = i << 24, j = 8; j > 0; j--) + if(c & (1<<31)) + c = (c<<1) ^ CRC32POLY; + else + c <<= 1; + crc32tab[i] = c; + } +} + +ulong +crc32(void *buf, int n, ulong crc) +{ + uchar *p; + + if(crc32tab[1] == 0) + initcrc32(); + crc = ~crc; + for(p = buf; --n >= 0;) + crc = (crc << 8) ^ crc32tab[(crc >> 24) ^ *p++]; + return ~crc; +} diff --git a/os/boot/rpcg/dat.h b/os/boot/rpcg/dat.h new file mode 100644 index 00000000..edadc030 --- /dev/null +++ b/os/boot/rpcg/dat.h @@ -0,0 +1,217 @@ +typedef struct Alarm Alarm; +typedef struct Block Block; +typedef struct IMM IMM; +typedef struct Queue Queue; + +typedef struct List { + void *next; +} List; + +typedef struct { + int fake; + int pri; +} Lock; +#define lock(x) +#define unlock(x) + +struct Alarm { + List; + int busy; + long dt; + void (*f)(Alarm*); + void *arg; +}; + +enum { + Eaddrlen = 6, + ETHERMINTU = 60, /* minimum transmit size */ + ETHERMAXTU = 1514, /* maximum transmit size */ + ETHERHDRSIZE = 14, /* size of an ethernet header */ + + MaxEther = 4, +}; + +typedef struct { + uchar d[Eaddrlen]; + uchar s[Eaddrlen]; + uchar type[2]; + uchar data[1500]; + uchar crc[4]; +} Etherpkt; + +extern uchar broadcast[Eaddrlen]; + +enum { + Npart = 20+2, /* 8 sub partitions, disk, and partition */ + Maxxfer = 16*1024, /* maximum transfer size/cmd */ +}; + +typedef struct { + ulong start; + ulong end; + char name[NAMELEN+1]; +} Partition; + +typedef struct { + int online; + int npart; /* number of real partitions */ + Partition p[Npart]; + ulong offset; + Partition *current; /* current partition */ + + ulong cap; /* total bytes */ + int bytes; /* bytes/sector */ + int sectors; /* sectors/track */ + int heads; /* heads/cyl */ + long cyl; /* cylinders/drive */ + + char lba; /* true if drive has logical block addressing */ + char multi; /* non-zero if drive does multiple block xfers */ +} Disc; + +enum { + ScsiTestunit = 0x00, + ScsiExtsens = 0x03, + ScsiInquiry = 0x12, + ScsiModesense = 0x1a, + ScsiStartunit = 0x1B, + ScsiStopunit = 0x1B, + ScsiGetcap = 0x25, + ScsiRead = 0x08, + ScsiWrite = 0x0a, + ScsiExtread = 0x28, + ScsiExtwrite = 0x2a, + + /* data direction */ + ScsiIn = 1, + ScsiOut = 0, +}; + +typedef struct Scsibuf Scsibuf; +typedef struct Scsibuf { + void* virt; + void* phys; + Scsibuf* next; +}; + +typedef struct Scsidata { + uchar* base; + uchar* lim; + uchar* ptr; +} Scsidata; + +typedef struct Ureg Ureg; + +typedef struct Scsi { + ulong pid; + ushort target; + ushort lun; + ushort rflag; + ushort status; + Scsidata cmd; + Scsidata data; + Scsibuf* b; + uchar* save; + uchar cmdblk[16]; +} Scsi; + +typedef struct Segdesc { + ulong d0; + ulong d1; +} Segdesc; + +typedef struct Mach { + ulong ticks; /* of the clock since boot time */ + ulong delayloop; + long cpuhz; /* general system clock (cycles) */ + long clockgen; /* clock generator frequency (cycles) */ + ulong cpupvr; /* cpu type in processor version register */ + ulong cputype; /* cpu variant in BCD (eg, 0x823xx) */ + void* alarm; /* alarms bound to this clock */ + ulong* bcsr; + IMM* iomem; +} Mach; + +/* Mach.cputype */ +#define MPCREV(x) ((x) & 0xFF) +#define MPCMODEL(x) (((x)>>8) & 0xFFF) +#define MPCFAMILY(x) (((x)>>24) & 0x0F) + + +extern Mach *m; + +#define Q_MAGIC ((((4*21)+0)*21)+7) + +typedef struct Exec Exec; +struct Exec +{ + uchar magic[4]; /* magic number */ + uchar text[4]; /* size of text segment */ + uchar data[4]; /* size of initialized data */ + uchar bss[4]; /* size of uninitialized data */ + uchar syms[4]; /* size of symbol table */ + uchar entry[4]; /* entry point */ + uchar spsz[4]; /* size of sp/pc offset table */ + uchar pcsz[4]; /* size of pc/line number table */ +}; + +/* + * bootline passed by boot program + */ +#define BOOTLINE ((char *)0x200000-150) + +/* + * Where we leave configuration info. + */ +#define BOOTARGS ((char*)(0x200000)) +#define BOOTARGSLEN 1024 +#define MAXCONF 32 + +/* + * a parsed plan9.ini line + */ +#define ISAOPTLEN 16 +#define NISAOPT 8 + +typedef struct ISAConf { + char type[NAMELEN]; + ulong port; + ulong irq; + ulong mem; + ulong size; + uchar ea[6]; + + int nopt; + char opt[NISAOPT][ISAOPTLEN]; +} ISAConf; + +typedef struct { + int size; + ulong addr; +} Map; + +typedef struct { + char* name; + Map* map; + Map* mapend; + + Lock; +} RMap; + +typedef struct PCIcfg PCIcfg; + +extern uchar* vgamem; + +struct Block { + uchar *rp; + uchar *wp; + uchar *lim; + uchar *data; + Block* next; + ulong magic; +}; +#define BLEN(b) ((b)->wp-(b)->rp) + +typedef struct QLock { + int dummy; +} QLock; diff --git a/os/boot/rpcg/defont0.c b/os/boot/rpcg/defont0.c new file mode 100644 index 00000000..9a25e1bd --- /dev/null +++ b/os/boot/rpcg/defont0.c @@ -0,0 +1,216 @@ +#include +#include +#include +#include + + + +static ulong bits0[] = { + 0x907070f0, 0xf0f07000, 0xf0888888, 0xf8707070, 0xe0e0e0e0, 0xe09070f0, 0x70f870f0, 0xf870f088, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000000e0, + 0xd0808080, 0x80808800, 0x8888c888, 0x80888888, 0x90909090, 0x90d08080, 0x80808080, 0x80888888, + 0x00000000, 0x08000000, 0x0c300000, 0x00000006, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000003c, 0xc03c0000, + 0x00006000, 0x06001e00, 0x60181860, 0x78000000, 0x00000000, 0x00000000, 0x0000001c, 0x18380090, + 0xb06060e0, 0xe0e0f800, 0xf088a888, 0x80808080, 0x90909090, 0x90b060e0, 0x808060e0, 0x80808888, + 0x00182428, 0x3e707018, 0x18180000, 0x00000006, 0x3c183c3c, 0x1c3e3c7e, 0x3c3c0000, 0x0200403c, + 0x3c187c1e, 0x787e7e1e, 0x663c7c66, 0x6066623c, 0x7c3c7c3c, 0x7e6266c2, 0x66667e30, 0xc00c1000, + 0x08006000, 0x06003000, 0x60181860, 0x18000000, 0x00000000, 0x10000000, 0x00000030, 0x180c0090, + 0x90101080, 0x80808818, 0x88f8a888, 0xe0807070, 0xe0e0e0e0, 0xe0901080, 0x70e01080, 0xe098f088, + 0x00182428, 0x6adad818, 0x18181000, 0x0000000c, 0x66386666, 0x2c3e667e, 0x66660000, 0x06006066, + 0x42186632, 0x6c606032, 0x66181864, 0x60667224, 0x66246666, 0x186262da, 0x62620630, 0x600c3800, + 0x10006000, 0x06003000, 0x60000060, 0x18000000, 0x00000000, 0x30000000, 0x00000030, 0x180c00e0, + 0x00e0e0f0, 0xf0f00018, 0x88889850, 0x80880808, 0x201c1c1c, 0x1c00e0f0, 0x0080e0f0, 0x80888888, + 0x00182428, 0x68dad808, 0x300c5418, 0x0000000c, 0x66580606, 0x2c206002, 0x66661818, 0x0cfe3006, + 0x9e2c6660, 0x66606060, 0x6618186c, 0x60667266, 0x66666660, 0x186262da, 0x36660c30, 0x600c2800, + 0x103c6c3c, 0x3e3c7e3e, 0x6c787866, 0x18d46c3c, 0x6c3e763c, 0x7e6666c2, 0x66667e18, 0x18180000, + 0x44180000, 0x18241c24, 0xf0888820, 0x8070f0f0, 0x20202020, 0x201c243e, 0x1cf8241c, 0x80708870, + 0x0018247c, 0x78745008, 0x300c3818, 0x00000018, 0x66180606, 0x4c206006, 0x76661818, 0x18fe180c, + 0xb62c6660, 0x66606060, 0x66181868, 0x607e5a66, 0x66666470, 0x186266da, 0x34340c30, 0x300c6c00, + 0x18667666, 0x66663066, 0x76181864, 0x18fe7666, 0x76663666, 0x306662da, 0x62620608, 0x1810323c, + 0x44247c7c, 0x24342042, 0x00000000, 0x00000000, 0x20202020, 0x20222408, 0x22002420, 0x00000000, + 0x00180028, 0x3c287610, 0x300cee7e, 0x00fe0018, 0x66180c18, 0x4c3c7c0c, 0x3c3e0000, 0x30000c18, + 0xb62c7c60, 0x667c7c6e, 0x7e181878, 0x605a5a66, 0x6466783c, 0x186234da, 0x18341830, 0x300c4400, + 0x18066660, 0x66663066, 0x66181868, 0x18d66666, 0x66663860, 0x306662da, 0x34620c30, 0x180c5a20, + 0x44241010, 0x242c2042, 0x0e3e103e, 0x3e3c1c3e, 0x3c1c1c1c, 0x1c3e1c08, 0x3e222418, 0x0e0e0e0e, + 0x0008007c, 0x1e5cdc00, 0x300c387e, 0x00fe0030, 0x66181806, 0x7e066618, 0x6e060000, 0x18001818, + 0xb67e6660, 0x66606066, 0x6618186c, 0x605a4e66, 0x78666c0e, 0x1862346c, 0x2c183030, 0x180c4400, + 0x003e6660, 0x667e3066, 0x66181878, 0x18d66666, 0x6666303c, 0x306634da, 0x18341808, 0x18104c38, + 0x3c181010, 0x18241c42, 0x11081008, 0x20222208, 0x00000000, 0x00220408, 0x22361804, 0x11111111, + 0x00000028, 0x16b6cc00, 0x300c5418, 0x00000030, 0x66183006, 0x7e066618, 0x66060000, 0x0cfe3000, + 0x9a466660, 0x66606066, 0x6618186c, 0x605a4e66, 0x60666606, 0x1862346c, 0x6c183030, 0x180c0000, + 0x00666660, 0x66603066, 0x6618186c, 0x18d66666, 0x66663006, 0x3066346c, 0x2c343018, 0x18180020, + 0x00091010, 0x000e0942, 0x10081008, 0x20222208, 0x0f06060f, 0x0a09041e, 0x002a0e38, 0x10101010, + 0x00180028, 0x56b6cc00, 0x300c1018, 0x18001860, 0x66187e66, 0x0c666630, 0x66661818, 0x06fe6018, + 0x40466632, 0x6c606036, 0x66181866, 0x605a4624, 0x60246666, 0x1834186c, 0x46186030, 0x0c0c0000, + 0x006e6666, 0x6e66306e, 0x66181866, 0x18d66666, 0x666e3066, 0x306e186c, 0x46186030, 0x180c003c, + 0x08090909, 0x1f110aff, 0x0e081008, 0x382c2208, 0x08020901, 0x0a0a0911, 0x09220907, 0x0e0e0e0e, + 0x00180028, 0x7c1c7600, 0x18180000, 0x18001860, 0x3c7e7e3c, 0x0c3c3c30, 0x3c3c1818, 0x02004018, + 0x3e467c1e, 0x787e601e, 0x663c1866, 0x7e42463c, 0x603c663c, 0x1818186c, 0x66187e30, 0x0c0c0000, + 0x00367c3c, 0x363c7c36, 0x667e1866, 0x7ed6663c, 0x7c367c3c, 0x1e36186c, 0x66187e30, 0x180c0008, + 0x080f0606, 0x04110c18, 0x01081008, 0x20222208, 0x0e020203, 0x0a0c0d1e, 0x0d220e08, 0x01010101, + 0x00000000, 0x10000000, 0x18180000, 0x080000c0, 0x00000000, 0x00000000, 0x00000008, 0x00000000, + 0x00000000, 0x00000000, 0x00001800, 0x00000000, 0x000c0000, 0x00000000, 0x00000030, 0x060c00fe, + 0x00000000, 0x00000006, 0x00001800, 0x00000000, 0x60060000, 0x00000000, 0x0010001c, 0x18380008, + 0x08090606, 0x040e0a18, 0x11081f08, 0x20221c3e, 0x08020401, 0x0f0a0b11, 0x0b220908, 0x11111111, + 0x00000000, 0x00000000, 0x0c300000, 0x080000c0, 0x00000000, 0x00000000, 0x00000008, 0x00000000, + 0x00000000, 0x00000000, 0x00007000, 0x00000000, 0x00060000, 0x00000000, 0x0000003c, 0x063c0000, + 0x00000000, 0x00000066, 0x00001800, 0x00000000, 0x60060000, 0x00000000, 0x00300000, 0x00000008, + 0x0f090909, 0x04030900, 0x0e000000, 0x00000000, 0x0f0f0f0f, 0x0209091e, 0x09000f07, 0x0e0e0e0e, + 0x00000000, 0x00000000, 0x00000000, 0x10000000, 0x00000000, 0x00000000, 0x00000010, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x0000003c, 0x00007000, 0x00000000, 0x60060000, 0x00000000, 0x00600000, 0x0000000f, + +}; + +static GBitmap strike0 = { + bits0, + 0, + 32, + 0, + {0, 0, 1024, 14}, + {0, 0, 1024, 14}, +}; + +static Fontchar info0[] = { + { 0, 0, 14, 0, 8 }, + { 8, 0, 14, 0, 8 }, + { 16, 0, 14, 0, 8 }, + { 24, 0, 14, 0, 8 }, + { 32, 0, 14, 0, 8 }, + { 40, 0, 14, 0, 8 }, + { 48, 0, 14, 0, 8 }, + { 56, 0, 14, 0, 8 }, + { 64, 0, 14, 0, 8 }, + { 72, 0, 14, 0, 8 }, + { 80, 0, 14, 0, 8 }, + { 88, 0, 14, 0, 8 }, + { 96, 0, 14, 0, 8 }, + { 104, 0, 14, 0, 8 }, + { 112, 0, 14, 0, 8 }, + { 120, 0, 14, 0, 8 }, + { 128, 0, 14, 0, 8 }, + { 136, 0, 14, 0, 8 }, + { 144, 0, 14, 0, 8 }, + { 152, 0, 14, 0, 8 }, + { 160, 0, 14, 0, 8 }, + { 168, 0, 14, 0, 8 }, + { 176, 0, 14, 0, 8 }, + { 184, 0, 14, 0, 8 }, + { 192, 0, 14, 0, 8 }, + { 200, 0, 14, 0, 8 }, + { 208, 0, 14, 0, 8 }, + { 216, 0, 14, 0, 8 }, + { 224, 0, 14, 0, 8 }, + { 232, 0, 14, 0, 8 }, + { 240, 0, 14, 0, 8 }, + { 248, 0, 14, 0, 8 }, + { 256, 0, 0, 0, 8 }, + { 264, 2, 11, 0, 8 }, + { 272, 2, 6, 0, 8 }, + { 280, 2, 11, 0, 8 }, + { 288, 1, 12, 0, 8 }, + { 296, 2, 11, 0, 8 }, + { 304, 2, 11, 0, 8 }, + { 312, 2, 7, 0, 8 }, + { 320, 1, 13, 0, 8 }, + { 328, 1, 13, 0, 8 }, + { 336, 3, 10, 0, 8 }, + { 344, 4, 10, 0, 8 }, + { 352, 9, 14, 0, 8 }, + { 360, 6, 8, 0, 8 }, + { 368, 9, 11, 0, 8 }, + { 376, 1, 13, 0, 8 }, + { 384, 2, 11, 0, 8 }, + { 392, 2, 11, 0, 8 }, + { 400, 2, 11, 0, 8 }, + { 408, 2, 11, 0, 8 }, + { 416, 2, 11, 0, 8 }, + { 424, 2, 11, 0, 8 }, + { 432, 2, 11, 0, 8 }, + { 440, 2, 11, 0, 8 }, + { 448, 2, 11, 0, 8 }, + { 456, 2, 11, 0, 8 }, + { 464, 4, 11, 0, 8 }, + { 472, 4, 14, 0, 8 }, + { 480, 2, 11, 0, 8 }, + { 488, 4, 10, 0, 8 }, + { 496, 2, 11, 0, 8 }, + { 504, 2, 11, 0, 8 }, + { 512, 2, 11, 0, 8 }, + { 520, 2, 11, 0, 8 }, + { 528, 2, 11, 0, 8 }, + { 536, 2, 11, 0, 8 }, + { 544, 2, 11, 0, 8 }, + { 552, 2, 11, 0, 8 }, + { 560, 2, 11, 0, 8 }, + { 568, 2, 11, 0, 8 }, + { 576, 2, 11, 0, 8 }, + { 584, 2, 11, 0, 8 }, + { 592, 2, 13, 0, 8 }, + { 600, 2, 11, 0, 8 }, + { 608, 2, 11, 0, 8 }, + { 616, 2, 11, 0, 8 }, + { 624, 2, 11, 0, 8 }, + { 632, 2, 11, 0, 8 }, + { 640, 2, 11, 0, 8 }, + { 648, 2, 13, 0, 8 }, + { 656, 2, 11, 0, 8 }, + { 664, 2, 11, 0, 8 }, + { 672, 2, 11, 0, 8 }, + { 680, 2, 11, 0, 8 }, + { 688, 2, 11, 0, 8 }, + { 696, 2, 11, 0, 8 }, + { 704, 2, 11, 0, 8 }, + { 712, 2, 11, 0, 8 }, + { 720, 2, 11, 0, 8 }, + { 728, 1, 13, 0, 8 }, + { 736, 1, 13, 0, 8 }, + { 744, 1, 13, 0, 8 }, + { 752, 2, 8, 0, 8 }, + { 760, 11, 12, 0, 8 }, + { 768, 2, 7, 0, 8 }, + { 776, 4, 11, 0, 8 }, + { 784, 1, 11, 0, 8 }, + { 792, 4, 11, 0, 8 }, + { 800, 1, 11, 0, 8 }, + { 808, 4, 11, 0, 8 }, + { 816, 1, 11, 0, 8 }, + { 824, 4, 14, 0, 8 }, + { 832, 1, 11, 0, 8 }, + { 840, 1, 11, 0, 8 }, + { 848, 1, 14, 0, 8 }, + { 856, 1, 11, 0, 8 }, + { 864, 1, 11, 0, 8 }, + { 872, 4, 11, 0, 8 }, + { 880, 4, 11, 0, 8 }, + { 888, 4, 11, 0, 8 }, + { 896, 4, 14, 0, 8 }, + { 904, 4, 14, 0, 8 }, + { 912, 4, 11, 0, 8 }, + { 920, 4, 11, 0, 8 }, + { 928, 2, 11, 0, 8 }, + { 936, 4, 11, 0, 8 }, + { 944, 4, 11, 0, 8 }, + { 952, 4, 11, 0, 8 }, + { 960, 4, 11, 0, 8 }, + { 968, 4, 14, 0, 8 }, + { 976, 4, 11, 0, 8 }, + { 984, 1, 12, 0, 8 }, + { 992, 1, 12, 0, 8 }, + { 1000, 1, 12, 0, 8 }, + { 1008, 5, 8, 0, 8 }, + { 1016, 0, 14, 0, 8 }, + { 1024, 0, 14, 0, 8 }, + { 0, 0, 0, 0, 0 } +}; + +GSubfont defont0 = { + 129, + 14, + 2, + info0, + &strike0, +}; diff --git a/os/boot/rpcg/devether.c b/os/boot/rpcg/devether.c new file mode 100644 index 00000000..c7a7a5dc --- /dev/null +++ b/os/boot/rpcg/devether.c @@ -0,0 +1,157 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "etherif.h" + +static Ctlr ether[MaxEther]; + +static struct { + char *type; + int (*reset)(Ctlr*); +} cards[] = { + { "SCC", sccethreset, }, + { "SCC2", sccethreset, }, + { 0, } +}; + +int +etherinit(void) +{ + Ctlr *ctlr; + int ctlrno, i, mask, n; + + mask = 0; + for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){ + ctlr = ðer[ctlrno]; + memset(ctlr, 0, sizeof(Ctlr)); + if(archether(ctlrno, &ctlr->card) <= 0) + continue; + for(n = 0; cards[n].type; n++){ + if(strcmp(cards[n].type, ctlr->card.type)) + continue; + ctlr->ctlrno = ctlrno; + if((*cards[n].reset)(ctlr)) + break; + + ctlr->iq = qopen(16*1024, 1, 0, 0); + ctlr->oq = qopen(16*1024, 1, 0, 0); + + ctlr->present = 1; + mask |= 1<ctlrno, ctlr->card.type, ctlr->card.port, ctlr->card.irq); + if(ctlr->card.mem) + print(" addr 0x%luX", PADDR(ctlr->card.mem)); + if(ctlr->card.size) + print(" size 0x%luX", ctlr->card.size); + print(":"); + for(i = 0; i < sizeof(ctlr->card.ea); i++) + print(" %2.2uX", ctlr->card.ea[i]); + print("\n"); uartwait(); + setvec(VectorPIC + ctlr->card.irq, ctlr->card.intr, ctlr); + break; + } + } + + return mask; +} + +static Ctlr* +attach(int ctlrno) +{ + Ctlr *ctlr; + + if(ctlrno >= MaxEther || ether[ctlrno].present == 0) + return 0; + + ctlr = ðer[ctlrno]; + if(ctlr->present == 1){ + ctlr->present = 2; + (*ctlr->card.attach)(ctlr); + } + + return ctlr; +} + +uchar* +etheraddr(int ctlrno) +{ + Ctlr *ctlr; + + if((ctlr = attach(ctlrno)) == 0) + return 0; + + return ctlr->card.ea; +} + +int +etherrxpkt(int ctlrno, Etherpkt *pkt, int timo) +{ + int n; + Ctlr *ctlr; + Block *b; + ulong start; + + if((ctlr = attach(ctlrno)) == 0) + return 0; + + start = m->ticks; + while((b = qget(ctlr->iq)) == 0){ + if(TK2MS(m->ticks - start) >= timo){ + /* + print("ether%d: rx timeout\n", ctlrno); + */ + return 0; + } + } + + n = BLEN(b); + memmove(pkt, b->rp, n); + freeb(b); + + return n; +} + +int +etheriq(Ctlr *ctlr, Block *b, int freebp) +{ + if(memcmp(((Etherpkt*)b->rp)->d, ctlr->card.ea, Eaddrlen) != 0 && + memcmp(((Etherpkt*)b->rp)->d, broadcast, Eaddrlen) != 0){ + if(freebp) + freeb(b); + return 0; + } + qbwrite(ctlr->iq, b); + return 1; +} + +int +ethertxpkt(int ctlrno, Etherpkt *pkt, int len, int) +{ + Ctlr *ctlr; + Block *b; + int s; + + if((ctlr = attach(ctlrno)) == 0) + return 0; + + if(qlen(ctlr->oq) > 16*1024){ + print("ether%d: tx queue full\n", ctlrno); + return 0; + } + b = iallocb(sizeof(Etherpkt)); + memmove(b->wp, pkt, len); + memmove(((Etherpkt*)b->wp)->s, ctlr->card.ea, Eaddrlen); + b->wp += len; + qbwrite(ctlr->oq, b); + s = splhi(); + (*ctlr->card.transmit)(ctlr); + splx(s); + + return 1; +} diff --git a/os/boot/rpcg/devuart.c b/os/boot/rpcg/devuart.c new file mode 100644 index 00000000..14e38592 --- /dev/null +++ b/os/boot/rpcg/devuart.c @@ -0,0 +1,230 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +/* + * SMC1 in UART mode + */ + +typedef struct Uartsmc Uartsmc; +struct Uartsmc { + IOCparam; + ushort maxidl; + ushort idlc; + ushort brkln; + ushort brkec; + ushort brkcr; + ushort rmask; +}; + +typedef struct Uart Uart; +struct Uart +{ + int port; + int setup; + uchar txbusy; + + Queue* iq; + Queue* oq; + void (*rx)(Queue*, int); + void (*boot)(uchar*, int); + + ulong frame; + ulong overrun; + uchar rxbuf[128]; + char txbuf[16]; + BD* rxb; + BD* txb; +}; + +Uart uart[1]; +int predawn = 1; + +static void uartintr(Ureg*, void*); +static void uartkick(void*); + +static int +baudgen(int baud) +{ + int d; + + d = ((m->cpuhz/baud)+8)>>4; + if(d >= (1<<12)) + return ((d+15)>>3)|1; + return d<<1; +} + +static void +smcsetup(Uart *up, int baud) +{ + IMM *io; + Uartsmc *p; + BD *bd; + SMC *smc; + + archenableuart(SMC1ID, 0); + io = m->iomem; + io->pbpar |= IBIT(24)|IBIT(25); /* enable SMC1 TX/RX */ + io->pbdir &= ~(IBIT(24)|IBIT(25)); + io->brgc1 = baudgen(baud) | BaudEnable; + io->simode &= ~0xF000; /* SMC1 to NMSI mode, Tx/Rx clocks are BRG1 */ + + bd = bdalloc(1); + p = (Uartsmc*)KADDR(SMC1P); + p->rbase = (ushort)bd; + up->rxb = bd; + bd->status = BDEmpty|BDWrap|BDInt; + bd->length = 0; + bd->addr = PADDR(up->rxbuf); + bd = bdalloc(1); + p->tbase = (ushort)bd; + up->txb = bd; + bd->status = BDWrap|BDInt; + bd->length = 0; + bd->addr = PADDR(up->txbuf); + + cpmop(InitRxTx, SMC1ID, 0); + + /* protocol parameters */ + p->rfcr = 0x18; + p->tfcr = 0x18; + p->mrblr = 1; + p->maxidl = 1; + p->brkln = 0; + p->brkec = 0; + p->brkcr = 1; + smc = IOREGS(0xA80, SMC); + smc->smce = 0xff; /* clear events */ + smc->smcm = 0x17; /* enable all possible interrupts */ + setvec(VectorCPIC+4, uartintr, up); + smc->smcmr = 0x4820; /* 8-bit mode, no parity, 1 stop bit, UART mode, ... */ + smc->smcmr |= 3; /* enable rx/tx */ +} + +static void +uartintr(Ureg*, void *arg) +{ + Uart *up; + int ch, i; + BD *bd; + SMC *smc; + Block *b; + + up = arg; + smc = IOREGS(0xA80, SMC); + smc->smce = 0xff; /* clear all events */ + if((bd = up->rxb) != nil && (bd->status & BDEmpty) == 0){ + if(up->iq != nil && bd->length > 0){ + if(up->boot != nil){ + up->boot(up->rxbuf, bd->length); + }else if(up->rx != nil){ + for(i=0; ilength; i++){ + ch = up->rxbuf[i]; + up->rx(up->iq, ch); + } + }else{ + b = iallocb(bd->length); + memmove(b->wp, up->rxbuf, bd->length); + b->wp += bd->length; + qbwrite(up->iq, b); + } + } + bd->status |= BDEmpty|BDInt; + } else if((bd = up->txb) != nil && (bd->status & BDReady) == 0){ + ch = -1; + if(up->oq) + ch = qbgetc(up->oq); + if(ch != -1){ + up->txbuf[0] = ch; + bd->length = 1; + bd->status |= BDReady; + }else + up->txbusy = 0; + } + /* TO DO: modem status, errors, etc */ +} + +static void +uartkick(void *arg) +{ + Uart *up = arg; + int s, c, i; + + s = splhi(); + while(up->txbusy == 0 && (c = qbgetc(up->oq)) != -1){ + if(predawn){ + while(up->txb->status & BDReady) + ; + } else { + for(i = 0; i < 100; i++){ + if((up->txb->status & BDReady) == 0) + break; + delay(1); + } + } + up->txbuf[0] = c; + up->txb->length = 1; + up->txb->status |= BDReady; + up->txbusy = !predawn; + } + splx(s); +} + +void +uartspecial(int port, int baud, Queue **iq, Queue **oq, void (*rx)(Queue*,int)) +{ + Uart *up = &uart[0]; + + if(up->setup) + return; + up->setup = 1; + + *iq = up->iq = qopen(4*1024, 0, 0, 0); + *oq = up->oq = qopen(16*1024, 0, uartkick, up); + up->rx = rx; + USED(port); + up->port = SMC1ID; + if(baud == 0) + baud = 9600; + smcsetup(up, baud); + /* if using SCCn's UART, would also set DTR and RTS, but SMC doesn't use them */ +} + +void +uartsetboot(void (*f)(uchar*, int)) +{ + uart[0].boot = f; +} + +void +uartputs(char *s, int n) +{ + Uart *up = &uart[0]; + Block *b; + int nl; + char *p; + + nl = 0; + for(p = s; p < s+n; p++) + if(*p == '\n') + nl++; + b = iallocb(n+nl); + while(n--){ + if(*s == '\n') + *b->wp++ = '\r'; + *b->wp++ = *s++; + } + qbwrite(up->oq, b); +} + +void +uartwait(void) +{ + Uart *up = &uart[0]; + + while(up->txbusy) + ; +} diff --git a/os/boot/rpcg/dload.c b/os/boot/rpcg/dload.c new file mode 100644 index 00000000..c05423a2 --- /dev/null +++ b/os/boot/rpcg/dload.c @@ -0,0 +1,103 @@ +#include +#include +#include +#include + +static char *kernelfile = "/power/ipaq"; +ulong crc32(void *buf, int n, ulong crc); + +void +main(int argc, char **argv) +{ + int ifd, n; + char buf[64], reply[1]; + int i, execsize; + Fhdr f; + ulong csum; + + ARGBEGIN{ + }ARGEND + ifd = open(kernelfile, OREAD); + if(ifd < 0){ + fprint(2, "dload: can't open %s: %r\n", kernelfile); + exits("open"); + } + i = 0; + if(crackhdr(ifd, &f) == 0){ + fprint(2, "dload: not an executable file: %r\n"); + exits("format"); + } + if(f.magic != Q_MAGIC){ + fprint(2, "dload: not a powerpc executable\n"); + exits("format"); + } + execsize = f.txtsz + f.datsz + f.txtoff; + seek(ifd, 0, 0); + csum = ~0; + while(execsize > 0 && (n = read(ifd, buf, sizeof(buf))) > 0){ + if(n > execsize) + n = execsize; + for(;;){ + if(write(1, buf, sizeof(buf)) != sizeof(buf)){ /* always writes full buffer */ + fprint(2, "dload: write error: %r\n"); + exits("write"); + } + if(read(0, reply, 1) != 1){ + fprint(2, "dload: bad reply\n"); + exits("read"); + } + if(reply[0] != 'n') + break; + fprint(2, "!"); + } + if(reply[0] != 'y'){ + fprint(2, "dload: bad ack: %c\n", reply[0]); + exits("reply"); + } + if(++i%10 == 0) + fprint(2, "."); + execsize -= n; + } + exits(0); +} + +/* + * from Rob Warnock + */ +static ulong crc32tab[256]; /* initialised on first call to crc32 */ + +enum { + CRC32POLY = 0x04c11db7 /* AUTODIN II, Ethernet, & FDDI */ +}; + +/* + * Build auxiliary table for parallel byte-at-a-time CRC-32. + */ +static void +initcrc32(void) +{ + int i, j; + ulong c; + + for(i = 0; i < 256; i++) { + for(c = i << 24, j = 8; j > 0; j--) + if(c & (1<<31)) + c = (c<<1) ^ CRC32POLY; + else + c <<= 1; + crc32tab[i] = c; + } +} + +ulong +crc32(void *buf, int n, ulong crc) +{ + uchar *p; + + if(crc32tab[1] == 0) + initcrc32(); + crc = ~crc; + for(p = buf; --n >= 0;) + crc = (crc << 8) ^ crc32tab[(crc >> 24) ^ *p++]; + return ~crc; +} diff --git a/os/boot/rpcg/donprint.c b/os/boot/rpcg/donprint.c new file mode 100644 index 00000000..4125e690 --- /dev/null +++ b/os/boot/rpcg/donprint.c @@ -0,0 +1,332 @@ +#include "u.h" +#include "lib.h" + +#define PTR sizeof(char*) +#define SHORT sizeof(int) +#define INT sizeof(int) +#define LONG sizeof(long) +#define IDIGIT 30 +#define MAXCON 30 + +#define FLONG (1<<0) +#define FSHORT (1<<1) +#define FUNSIGN (1<<2) + +typedef struct Op Op; +struct Op +{ + char *p; + char *ep; + void *argp; + int f1; + int f2; + int f3; +}; + +static int noconv(Op*); +static int cconv(Op*); +static int dconv(Op*); +static int hconv(Op*); +static int lconv(Op*); +static int oconv(Op*); +static int sconv(Op*); +static int uconv(Op*); +static int xconv(Op*); +static int Xconv(Op*); +static int percent(Op*); + +static +int (*fmtconv[MAXCON])(Op*) = +{ + noconv, + cconv, dconv, hconv, lconv, + oconv, sconv, uconv, xconv, + Xconv, percent, +}; +static +char fmtindex[128] = +{ + ['c'] 1, + ['d'] 2, + ['h'] 3, + ['l'] 4, + ['o'] 5, + ['s'] 6, + ['u'] 7, + ['x'] 8, + ['X'] 9, + ['%'] 10, +}; + +static int convcount = { 11 }; +static int ucase; + +static void +PUT(Op *o, int c) +{ + static int pos; + int opos; + + if(c == '\t'){ + opos = pos; + pos = (opos+8) & ~7; + while(opos++ < pos && o->p < o->ep) + *o->p++ = ' '; + return; + } + if(o->p < o->ep){ + *o->p++ = c; + pos++; + } + if(c == '\n') + pos = 0; +} + +int +fmtinstall(char c, int (*f)(Op*)) +{ + + c &= 0177; + if(fmtindex[c] == 0) { + if(convcount >= MAXCON) + return 1; + fmtindex[c] = convcount++; + } + fmtconv[fmtindex[c]] = f; + return 0; +} + +char* +donprint(char *p, char *ep, char *fmt, void *argp) +{ + int sf1, c; + Op o; + + o.p = p; + o.ep = ep; + o.argp = argp; + +loop: + c = *fmt++; + if(c != '%') { + if(c == 0) { + if(o.p < o.ep) + *o.p = 0; + return o.p; + } + PUT(&o, c); + goto loop; + } + o.f1 = 0; + o.f2 = -1; + o.f3 = 0; + c = *fmt++; + sf1 = 0; + if(c == '-') { + sf1 = 1; + c = *fmt++; + } + while(c >= '0' && c <= '9') { + o.f1 = o.f1*10 + c-'0'; + c = *fmt++; + } + if(sf1) + o.f1 = -o.f1; + if(c != '.') + goto l1; + c = *fmt++; + while(c >= '0' && c <= '9') { + if(o.f2 < 0) + o.f2 = 0; + o.f2 = o.f2*10 + c-'0'; + c = *fmt++; + } +l1: + if(c == 0) + fmt--; + c = (*fmtconv[fmtindex[c&0177]])(&o); + if(c < 0) { + o.f3 |= -c; + c = *fmt++; + goto l1; + } + o.argp = (char*)o.argp + c; + goto loop; +} + +void +strconv(char *o, Op *op, int f1, int f2) +{ + int n, c; + char *p; + + n = strlen(o); + if(f1 >= 0) + while(n < f1) { + PUT(op, ' '); + n++; + } + for(p=o; c = *p++;) + if(f2 != 0) { + PUT(op, c); + f2--; + } + if(f1 < 0) { + f1 = -f1; + while(n < f1) { + PUT(op, ' '); + n++; + } + } +} + +int +numbconv(Op *op, int base) +{ + char b[IDIGIT]; + int i, f, n, r; + long v; + short h; + + f = 0; + switch(op->f3 & (FLONG|FSHORT|FUNSIGN)) { + case FLONG: + v = *(long*)op->argp; + r = LONG; + break; + + case FUNSIGN|FLONG: + v = *(ulong*)op->argp; + r = LONG; + break; + + case FSHORT: + h = *(int*)op->argp; + v = h; + r = SHORT; + break; + + case FUNSIGN|FSHORT: + h = *(int*)op->argp; + v = (ushort)h; + r = SHORT; + break; + + default: + v = *(int*)op->argp; + r = INT; + break; + + case FUNSIGN: + v = *(unsigned*)op->argp; + r = INT; + break; + } + if(!(op->f3 & FUNSIGN) && v < 0) { + v = -v; + f = 1; + } + b[IDIGIT-1] = 0; + for(i = IDIGIT-2;; i--) { + n = (ulong)v % base; + n += '0'; + if(n > '9'){ + n += 'a' - ('9'+1); + if(ucase) + n += 'A'-'a'; + } + b[i] = n; + if(i < 2) + break; + v = (ulong)v / base; + if(op->f2 >= 0 && i >= IDIGIT-op->f2) + continue; + if(v <= 0) + break; + } + if(f) + b[--i] = '-'; + strconv(b+i, op, op->f1, -1); + return r; +} + +static int +noconv(Op *op) +{ + + strconv("***", op, 0, -1); + return 0; +} + +static int +cconv(Op *op) +{ + char b[2]; + + b[0] = *(int*)op->argp; + b[1] = 0; + strconv(b, op, op->f1, -1); + return INT; +} + +static int +dconv(Op *op) +{ + return numbconv(op, 10); +} + +static int +hconv(Op*) +{ + return -FSHORT; +} + +static int +lconv(Op*) +{ + return -FLONG; +} + +static int +oconv(Op *op) +{ + return numbconv(op, 8); +} + +static int +sconv(Op *op) +{ + strconv(*(char**)op->argp, op, op->f1, op->f2); + return PTR; +} + +static int +uconv(Op*) +{ + return -FUNSIGN; +} + +static int +xconv(Op *op) +{ + return numbconv(op, 16); +} + +static int +Xconv(Op *op) +{ + int r; + + ucase = 1; + r = numbconv(op, 16); + ucase = 0; + return r; +} + +static int +percent(Op *op) +{ + + PUT(op, '%'); + return 0; +} diff --git a/os/boot/rpcg/dosboot.c b/os/boot/rpcg/dosboot.c new file mode 100644 index 00000000..cd8d1276 --- /dev/null +++ b/os/boot/rpcg/dosboot.c @@ -0,0 +1,614 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "dosfs.h" + +extern char *premature; + +/* + * predeclared + */ +static void bootdump(Dosboot*); +static void setname(Dosfile*, char*); +long dosreadseg(Dosfile*, long, long); + +/* + * debugging + */ +#define chatty 1 +#define chat if(chatty)print + +/* + * block io buffers + */ +enum +{ + Nbio= 16, +}; +typedef struct Clustbuf Clustbuf; +struct Clustbuf +{ + int age; + long sector; + uchar *iobuf; + Dos *dos; + int size; +}; +Clustbuf bio[Nbio]; + +/* + * get an io block from an io buffer + */ +Clustbuf* +getclust(Dos *dos, long sector) +{ + Clustbuf *p, *oldest; + int size; + + chat("getclust @ %d\n", sector); + + /* + * if we have it, just return it + */ + for(p = bio; p < &bio[Nbio]; p++){ + if(sector == p->sector && dos == p->dos){ + p->age = m->ticks; + chat("getclust %d in cache\n", sector); + return p; + } + } + + /* + * otherwise, reuse the oldest entry + */ + oldest = bio; + for(p = &bio[1]; p < &bio[Nbio]; p++){ + if(p->age <= oldest->age) + oldest = p; + } + p = oldest; + + /* + * make sure the buffer is big enough + */ + size = dos->clustsize*dos->sectsize; + if(p->iobuf==0 || p->size < size) + p->iobuf = ialloc(size, 0); + p->size = size; + + /* + * read in the cluster + */ + chat("getclust addr %d\n", (sector+dos->start)*dos->sectsize); + if((*dos->seek)(dos->dev, (sector+dos->start)*dos->sectsize) < 0){ + chat("can't seek block\n"); + return 0; + } + if((*dos->read)(dos->dev, p->iobuf, size) != size){ + chat("can't read block\n"); + return 0; + } + + p->age = m->ticks; + p->dos = dos; + p->sector = sector; + chat("getclust %d read\n", sector); + return p; +} + +/* + * walk the fat one level ( n is a current cluster number ). + * return the new cluster number or -1 if no more. + */ +static long +fatwalk(Dos *dos, int n) +{ + ulong k, sect; + Clustbuf *p; + int o; + + chat("fatwalk %d\n", n); + + if(n < 2 || n >= dos->fatclusters) + return -1; + + switch(dos->fatbits){ + case 12: + k = (3*n)/2; break; + case 16: + k = 2*n; break; + default: + return -1; + } + if(k >= dos->fatsize*dos->sectsize) + panic("getfat"); + + sect = (k/(dos->sectsize*dos->clustsize))*dos->clustsize + dos->fataddr; + o = k%(dos->sectsize*dos->clustsize); + p = getclust(dos, sect); + k = p->iobuf[o++]; + if(o >= dos->sectsize*dos->clustsize){ + p = getclust(dos, sect+dos->clustsize); + o = 0; + } + k |= p->iobuf[o]<<8; + if(dos->fatbits == 12){ + if(n&1) + k >>= 4; + else + k &= 0xfff; + if(k >= 0xff8) + k |= 0xf000; + } + k = k < 0xfff8 ? k : -1; + chat("fatwalk %d -> %d\n", n, k); + return k; +} + +/* + * map a file's logical cluster address to a physical sector address + */ +static long +fileaddr(Dosfile *fp, long ltarget) +{ + Dos *dos = fp->dos; + long l; + long p; + + chat("fileaddr %8.8s %d\n", fp->name, ltarget); + /* + * root directory is contiguous and easy + */ + if(fp->pstart == 0){ + if(ltarget*dos->sectsize*dos->clustsize >= dos->rootsize*sizeof(Dosdir)) + return -1; + l = dos->rootaddr + ltarget*dos->clustsize; + chat("fileaddr %d -> %d\n", ltarget, l); + return l; + } + + /* + * anything else requires a walk through the fat + */ + if(ltarget >= fp->lcurrent && fp->pcurrent){ + /* start at the currrent point */ + l = fp->lcurrent; + p = fp->pcurrent; + } else { + /* go back to the beginning */ + l = 0; + p = fp->pstart; + } + while(l != ltarget){ + /* walk the fat */ + p = fatwalk(dos, p); + if(p < 0) + return -1; + l++; + } + fp->lcurrent = l; + fp->pcurrent = p; + + /* + * clusters start at 2 instead of 0 (why? - presotto) + */ + l = dos->dataaddr + (p-2)*dos->clustsize; + chat("fileaddr %d -> %d\n", ltarget, l); + return l; +} + +/* + * read from a dos file + */ +long +dosread(Dosfile *fp, void *a, long n) +{ + long addr; + long rv; + int i; + int off; + Clustbuf *p; + uchar *from, *to; + + if((fp->attr & DDIR) == 0){ + if(fp->offset >= fp->length) + return 0; + if(fp->offset+n > fp->length) + n = fp->length - fp->offset; + } + + to = a; + for(rv = 0; rv < n; rv+=i){ + /* + * read the cluster + */ + addr = fileaddr(fp, fp->offset/fp->dos->clustbytes); + if(addr < 0) + return -1; + p = getclust(fp->dos, addr); + if(p == 0) + return -1; + + /* + * copy the bytes we need + */ + off = fp->offset % fp->dos->clustbytes; + from = &p->iobuf[off]; + i = n - rv; + if(i > fp->dos->clustbytes - off) + i = fp->dos->clustbytes - off; + memmove(to, from, i); + to += i; + fp->offset += i; + } + + return rv; +} + +/* + * walk a directory returns + * -1 if something went wrong + * 0 if not found + * 1 if found + */ +int +doswalk(Dosfile *file, char *name) +{ + Dosdir d; + long n; + + if((file->attr & DDIR) == 0){ + chat("walking non-directory!\n"); + return -1; + } + + setname(file, name); + + file->offset = 0; /* start at the beginning */ + while((n = dosread(file, &d, sizeof(d))) == sizeof(d)){ + chat("comparing to %8.8s.%3.3s\n", d.name, d.ext); + if(memcmp(file->name, d.name, sizeof(d.name)) != 0) + continue; + if(memcmp(file->ext, d.ext, sizeof(d.ext)) != 0) + continue; + if(d.attr & DVLABEL){ + chat("%8.8s.%3.3s is a LABEL\n", d.name, d.ext); + continue; + } + file->attr = d.attr; + file->pstart = GSHORT(d.start); + file->length = GLONG(d.length); + file->pcurrent = 0; + file->lcurrent = 0; + file->offset = 0; + return 1; + } + return n >= 0 ? 0 : -1; +} + + +/* + * instructions that boot blocks can start with + */ +#define JMPSHORT 0xeb +#define JMPNEAR 0xe9 + +/* + * read dos file system properties + */ +int +dosinit(Dos *dos, int start, int ishard) +{ + Dosboot *b; + int i; + Clustbuf *p; + Dospart *dp; + ulong mbroffset, offset; + + /* defaults till we know better */ + dos->start = start; + dos->sectsize = 512; + dos->clustsize = 1; + mbroffset = 0; + +dmddo: + /* get first sector */ + p = getclust(dos, mbroffset); + if(p == 0){ + chat("can't read boot block\n"); + return -1; + } + + /* + * If it's a hard disc then look for an MBR and pick either an + * active partition or the FAT with the lowest starting LBA. + * Things are tricky because we could be pointing to, amongst others: + * 1) a floppy BPB; + * 2) a hard disc MBR; + * 3) a hard disc extended partition table; + * 4) a logical drive on a hard disc; + * 5) a disc-manager boot block. + * They all have the same magic at the end of the block. + */ + if(p->iobuf[0x1FE] != 0x55 || p->iobuf[0x1FF] != 0xAA) { + chat("not DOS\n"); + return -1; + } + p->dos = 0; + b = (Dosboot *)p->iobuf; + if(ishard && b->mediadesc != 0xF8){ + dp = (Dospart*)&p->iobuf[0x1BE]; + offset = 0xFFFFFFFF; + for(i = 0; i < 4; i++, dp++){ + if(dp->type == DMDDO){ + mbroffset = 63; + goto dmddo; + } + if(dp->type != FAT12 && dp->type != FAT16 && dp->type != FATHUGE) + continue; + if(dp->flag & 0x80){ + offset = GLONG(dp->start); + break; + } + if(GLONG(dp->start) < offset) + offset = GLONG(dp->start); + } + if(i != 4 || offset != 0xFFFFFFFF){ + dos->start = mbroffset+offset; + p = getclust(dos, 0); + if(p == 0 || p->iobuf[0x1FE] != 0x55 || p->iobuf[0x1FF] != 0xAA) + return -1; + } + p->dos = 0; + } + + b = (Dosboot *)p->iobuf; + if(b->magic[0] != JMPNEAR && (b->magic[0] != JMPSHORT || b->magic[2] != 0x90)){ + chat("no dos file system\n"); + return -1; + } + + if(chatty) + bootdump(b); + + /* + * determine the systems' wondersous properties + */ + dos->sectsize = GSHORT(b->sectsize); + dos->clustsize = b->clustsize; + dos->clustbytes = dos->sectsize*dos->clustsize; + dos->nresrv = GSHORT(b->nresrv); + dos->nfats = b->nfats; + dos->rootsize = GSHORT(b->rootsize); + dos->volsize = GSHORT(b->volsize); + if(dos->volsize == 0) + dos->volsize = GLONG(b->bigvolsize); + dos->mediadesc = b->mediadesc; + dos->fatsize = GSHORT(b->fatsize); + dos->fataddr = dos->nresrv; + dos->rootaddr = dos->fataddr + dos->nfats*dos->fatsize; + i = dos->rootsize*sizeof(Dosdir) + dos->sectsize - 1; + i = i/dos->sectsize; + dos->dataaddr = dos->rootaddr + i; + dos->fatclusters = 2+(dos->volsize - dos->dataaddr)/dos->clustsize; + if(dos->fatclusters < 4087) + dos->fatbits = 12; + else + dos->fatbits = 16; + dos->freeptr = 2; + + /* + * set up the root + */ + dos->root.dos = dos; + dos->root.pstart = 0; + dos->root.pcurrent = dos->root.lcurrent = 0; + dos->root.offset = 0; + dos->root.attr = DDIR; + dos->root.length = dos->rootsize*sizeof(Dosdir); + + return 0; +} + +static void +bootdump(Dosboot *b) +{ + if(chatty == 0) + return; + print("magic: 0x%2.2x 0x%2.2x 0x%2.2x\n", + b->magic[0], b->magic[1], b->magic[2]); + print("version: \"%8.8s\"\n", b->version); + print("sectsize: %d\n", GSHORT(b->sectsize)); + print("allocsize: %d\n", b->clustsize); + print("nresrv: %d\n", GSHORT(b->nresrv)); + print("nfats: %d\n", b->nfats); + print("rootsize: %d\n", GSHORT(b->rootsize)); + print("volsize: %d\n", GSHORT(b->volsize)); + print("mediadesc: 0x%2.2x\n", b->mediadesc); + print("fatsize: %d\n", GSHORT(b->fatsize)); + print("trksize: %d\n", GSHORT(b->trksize)); + print("nheads: %d\n", GSHORT(b->nheads)); + print("nhidden: %d\n", GLONG(b->nhidden)); + print("bigvolsize: %d\n", GLONG(b->bigvolsize)); + print("driveno: %d\n", b->driveno); + print("reserved0: 0x%2.2x\n", b->reserved0); + print("bootsig: 0x%2.2x\n", b->bootsig); + print("volid: 0x%8.8x\n", GLONG(b->volid)); + print("label: \"%11.11s\"\n", b->label); +} + +/* + * grab next element from a path, return the pointer to unprocessed portion of + * path. + */ +static char * +nextelem(char *path, char *elem) +{ + int i; + + while(*path == '/') + path++; + if(*path==0 || *path==' ') + return 0; + for(i=0; *path!='\0' && *path!='/' && *path!=' '; i++){ + if(i==28){ + print("name component too long\n"); + return 0; + } + *elem++ = *path++; + } + *elem = '\0'; + return path; +} + +int +dosstat(Dos *dos, char *path, Dosfile *f) +{ + char element[NAMELEN]; + + *f = dos->root; + while(path = nextelem(path, element)){ + switch(doswalk(f, element)){ + case -1: + return -1; + case 0: + return 0; + } + } + return 1; +} + +/* + * boot + */ +int +dosboot(Dos *dos, char *path) +{ + Dosfile file; + long n; + long addr; + Exec *ep; + void (*b)(void); + + switch(dosstat(dos, path, &file)){ + + case -1: + print("error walking to %s\n", path); + return -1; + case 0: + print("%s not found\n", path); + return -1; + case 1: + print("found %8.8s.%3.3s attr 0x%ux start 0x%lux len %d\n", file.name, + file.ext, file.attr, file.pstart, file.length); + break; + } + + /* + * read header + */ + ep = (Exec*)ialloc(sizeof(Exec), 0); + n = sizeof(Exec); + if(dosreadseg(&file, n, (ulong) ep) != n){ + print(premature); + return -1; + } + if(GLLONG(ep->magic) != Q_MAGIC){ + print("bad magic 0x%lux not a plan 9 executable!\n", GLLONG(ep->magic)); + return -1; + } + + /* + * read text + */ + addr = PADDR(GLLONG(ep->entry)); + n = GLLONG(ep->text); + print("+%d", n); + if(dosreadseg(&file, n, addr) != n){ + print(premature); + return -1; + } + + /* + * read data (starts at first page after kernel) + */ + addr = PGROUND(addr+n); + n = GLLONG(ep->data); + print("+%d", n); + if(dosreadseg(&file, n, addr) != n){ + print(premature); + return -1; + } + + /* + * bss and entry point + */ + print("+%d\nstart at 0x%lux\n", GLLONG(ep->bss), GLLONG(ep->entry)); + + /* + * Go to new code. It's up to the program to get its PC relocated to + * the right place. + */ + b = (void (*)(void))(PADDR(GLLONG(ep->entry))); + (*b)(); + return 0; +} + +/* + * read in a segment + */ +long +dosreadseg(Dosfile *fp, long len, long addr) +{ + char *a; + long n, sofar; + + a = (char *)addr; + for(sofar = 0; sofar < len; sofar += n){ + n = 8*1024; + if(len - sofar < n) + n = len - sofar; + n = dosread(fp, a + sofar, n); + if(n <= 0) + break; + print("."); + } + return sofar; +} + +/* + * set up a dos file name + */ +static void +setname(Dosfile *fp, char *from) +{ + char *to; + + to = fp->name; + for(; *from && to-fp->name < 8; from++, to++){ + if(*from == '.'){ + from++; + break; + } + if(*from >= 'a' && *from <= 'z') + *to = *from + 'A' - 'a'; + else + *to = *from; + } + while(to - fp->name < 8) + *to++ = ' '; + + to = fp->ext; + for(; *from && to-fp->ext < 3; from++, to++){ + if(*from >= 'a' && *from <= 'z') + *to = *from + 'A' - 'a'; + else + *to = *from; + } + while(to-fp->ext < 3) + *to++ = ' '; + + chat("name is %8.8s %3.3s\n", fp->name, fp->ext); +} diff --git a/os/boot/rpcg/dosfs.h b/os/boot/rpcg/dosfs.h new file mode 100644 index 00000000..a45065a6 --- /dev/null +++ b/os/boot/rpcg/dosfs.h @@ -0,0 +1,110 @@ +typedef struct Dosboot Dosboot; +typedef struct Dos Dos; +typedef struct Dosdir Dosdir; +typedef struct Dosfile Dosfile; +typedef struct Dospart Dospart; + +struct Dospart +{ + uchar flag; /* active flag */ + uchar shead; /* starting head */ + uchar scs[2]; /* starting cylinder/sector */ + uchar type; /* partition type */ + uchar ehead; /* ending head */ + uchar ecs[2]; /* ending cylinder/sector */ + uchar start[4]; /* starting sector */ + uchar len[4]; /* length in sectors */ +}; + +#define FAT12 0x01 +#define FAT16 0x04 +#define FATHUGE 0x06 +#define DMDDO 0x54 + +struct Dosboot{ + uchar magic[3]; + uchar version[8]; + uchar sectsize[2]; + uchar clustsize; + uchar nresrv[2]; + uchar nfats; + uchar rootsize[2]; + uchar volsize[2]; + uchar mediadesc; + uchar fatsize[2]; + uchar trksize[2]; + uchar nheads[2]; + uchar nhidden[4]; + uchar bigvolsize[4]; + uchar driveno; + uchar reserved0; + uchar bootsig; + uchar volid[4]; + uchar label[11]; + uchar reserved1[8]; +}; + +struct Dosfile{ + Dos *dos; /* owning dos file system */ + char name[8]; + char ext[3]; + uchar attr; + long length; + long pstart; /* physical start cluster address */ + long pcurrent; /* physical current cluster address */ + long lcurrent; /* logical current cluster address */ + long offset; +}; + +struct Dos{ + int dev; /* device id */ + long (*read)(int, void*, long); /* read routine */ + long (*seek)(int, long); /* seek routine */ + + int start; /* start of file system */ + int sectsize; /* in bytes */ + int clustsize; /* in sectors */ + int clustbytes; /* in bytes */ + int nresrv; /* sectors */ + int nfats; /* usually 2 */ + int rootsize; /* number of entries */ + int volsize; /* in sectors */ + int mediadesc; + int fatsize; /* in sectors */ + int fatclusters; + int fatbits; /* 12 or 16 */ + long fataddr; /* sector number */ + long rootaddr; + long dataaddr; + long freeptr; + + Dosfile root; +}; + +struct Dosdir{ + uchar name[8]; + uchar ext[3]; + uchar attr; + uchar reserved[10]; + uchar time[2]; + uchar date[2]; + uchar start[2]; + uchar length[4]; +}; + +#define DRONLY 0x01 +#define DHIDDEN 0x02 +#define DSYSTEM 0x04 +#define DVLABEL 0x08 +#define DDIR 0x10 +#define DARCH 0x20 + +extern int chatty; + +extern int dosboot(Dos*, char*); +extern int dosinit(Dos*, int, int); +extern long dosread(Dosfile*, void*, long); +extern int dosstat(Dos*, char*, Dosfile*); +extern int doswalk(Dosfile*, char*); + +extern int plan9ini(Dos*, char*); diff --git a/os/boot/rpcg/etherif.h b/os/boot/rpcg/etherif.h new file mode 100644 index 00000000..a4e790a6 --- /dev/null +++ b/os/boot/rpcg/etherif.h @@ -0,0 +1,59 @@ +/* + * All the goo for PC ethernet cards. + */ +typedef struct Card Card; +typedef struct Type Type; +typedef struct Ctlr Ctlr; + +/* + * Hardware interface. + */ +struct Card { + ISAConf; + + int (*reset)(Ctlr*); + void (*attach)(Ctlr*); + + void *(*read)(Ctlr*, void*, ulong, ulong); + void *(*write)(Ctlr*, ulong, void*, ulong); + + void (*receive)(Ctlr*); + void (*transmit)(Ctlr*); + void (*intr)(Ureg*, void*); + void (*overflow)(Ctlr*); + + uchar bit16; /* true if a 16 bit interface */ + uchar ram; /* true if card has shared memory */ + + ulong dp8390; /* I/O address of 8390 (if any) */ + ulong data; /* I/O data port if no shared memory */ + uchar nxtpkt; /* software bndry */ + uchar tstart; /* 8390 ring addresses */ + uchar pstart; + uchar pstop; + + uchar dummyrr; /* do dummy remote read */ +}; + +/* + * Software controller. + */ +struct Ctlr { + Card card; /* hardware info */ + int ctlrno; + int present; + + Queue* iq; + Queue* oq; + + int inpackets; + int outpackets; + int crcs; /* input crc errors */ + int oerrs; /* output errors */ + int frames; /* framing errors */ + int overflows; /* packet overflows */ + int buffs; /* buffering errors */ +}; + +extern int sccethreset(Ctlr*); +extern int etheriq(Ctlr*, Block*, int); diff --git a/os/boot/rpcg/etherscc.c b/os/boot/rpcg/etherscc.c new file mode 100644 index 00000000..1e47d473 --- /dev/null +++ b/os/boot/rpcg/etherscc.c @@ -0,0 +1,411 @@ +/* + * SCCn ethernet + */ + +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "etherif.h" + +enum { + Nrdre = 32, /* receive descriptor ring entries */ + Ntdre = 4, /* transmit descriptor ring entries */ + + Rbsize = ETHERMAXTU+4, /* ring buffer size (+4 for CRC) */ + Bufsize = (Rbsize+7)&~7, /* aligned */ +}; + +enum { + /* ether-specific Rx BD bits */ + RxMiss= 1<<8, + RxeLG= 1<<5, + RxeNO= 1<<4, + RxeSH= 1<<3, + RxeCR= 1<<2, + RxeOV= 1<<1, + RxeCL= 1<<0, + RxError= (RxeLG|RxeNO|RxeSH|RxeCR|RxeOV|RxeCL), /* various error flags */ + + /* ether-specific Tx BD bits */ + TxPad= 1<<14, /* pad short frames */ + TxTC= 1<<10, /* transmit CRC */ + TxeDEF= 1<<9, + TxeHB= 1<<8, + TxeLC= 1<<7, + TxeRL= 1<<6, + TxeUN= 1<<1, + TxeCSL= 1<<0, + + /* scce */ + RXB= 1<<0, + TXB= 1<<1, + BSY= 1<<2, + RXF= 1<<3, + TXE= 1<<4, + + /* gsmrl */ + ENR= 1<<5, + ENT= 1<<4, + + /* port A */ + RXD1= SIBIT(15), + TXD1= SIBIT(14), + + /* port B */ + RTS1= IBIT(19), + + /* port C */ + CTS1= SIBIT(11), + CD1= SIBIT(10), +}; + +typedef struct Etherparam Etherparam; +struct Etherparam { + SCCparam; + ulong c_pres; /* preset CRC */ + ulong c_mask; /* constant mask for CRC */ + ulong crcec; /* CRC error counter */ + ulong alec; /* alighnment error counter */ + ulong disfc; /* discard frame counter */ + ushort pads; /* short frame PAD characters */ + ushort ret_lim; /* retry limit threshold */ + ushort ret_cnt; /* retry limit counter */ + ushort mflr; /* maximum frame length reg */ + ushort minflr; /* minimum frame length reg */ + ushort maxd1; /* maximum DMA1 length reg */ + ushort maxd2; /* maximum DMA2 length reg */ + ushort maxd; /* rx max DMA */ + ushort dma_cnt; /* rx dma counter */ + ushort max_b; /* max bd byte count */ + ushort gaddr[4]; /* group address filter */ + ulong tbuf0_data0; /* save area 0 - current frm */ + ulong tbuf0_data1; /* save area 1 - current frm */ + ulong tbuf0_rba0; + ulong tbuf0_crc; + ushort tbuf0_bcnt; + ushort paddr[3]; /* physical address LSB to MSB increasing */ + ushort p_per; /* persistence */ + ushort rfbd_ptr; /* rx first bd pointer */ + ushort tfbd_ptr; /* tx first bd pointer */ + ushort tlbd_ptr; /* tx last bd pointer */ + ulong tbuf1_data0; /* save area 0 - next frame */ + ulong tbuf1_data1; /* save area 1 - next frame */ + ulong tbuf1_rba0; + ulong tbuf1_crc; + ushort tbuf1_bcnt; + ushort tx_len; /* tx frame length counter */ + ushort iaddr[4]; /* individual address filter*/ + ushort boff_cnt; /* back-off counter */ + ushort taddr[3]; /* temp address */ +}; + +typedef struct { + SCC* scc; + int port; + int cpm; + + BD* rdr; /* receive descriptor ring */ + void* rrb; /* receive ring buffers */ + int rdrx; /* index into rdr */ + + BD* tdr; /* transmit descriptor ring */ + void* trb; /* transmit ring buffers */ + int tdrx; /* index into tdr */ +} Mot; +static Mot mot[MaxEther]; + +static int sccid[] = {-1, SCC1ID, SCC2ID, SCC3ID, SCC4ID}; +static int sccparam[] = {-1, SCC1P, SCC2P, SCC3P, SCC4P}; +static int sccreg[] = {-1, 0xA00, 0xA20, 0xA40, 0xA60}; +static int sccirq[] = {-1, 0x1E, 0x1D, 0x1C, 0x1B}; + +static void +attach(Ctlr *ctlr) +{ + mot[ctlr->ctlrno].scc->gsmrl |= ENR|ENT; + eieio(); +} + +static void +transmit(Ctlr *ctlr) +{ + int len; + Mot *motp; + Block *b; + BD *tdre; + + motp = &mot[ctlr->ctlrno]; + while(((tdre = &motp->tdr[motp->tdrx])->status & BDReady) == 0){ + b = qget(ctlr->oq); + if(b == 0) + break; + + /* + * Copy the packet to the transmit buffer. + */ + len = BLEN(b); + memmove(KADDR(tdre->addr), b->rp, len); + + /* + * Give ownership of the descriptor to the chip, increment the + * software ring descriptor pointer and tell the chip to poll. + */ + tdre->length = len; + eieio(); + tdre->status = (tdre->status & BDWrap) | BDReady|TxPad|BDInt|BDLast|TxTC; + eieio(); + motp->scc->todr = 1<<15; /* transmit now */ + eieio(); + motp->tdrx = NEXT(motp->tdrx, Ntdre); + + freeb(b); + + } +} + +static void +interrupt(Ureg*, void *ap) +{ + int len, events, status; + Mot *motp; + BD *rdre; + Block *b; + Ctlr *ctlr; + + ctlr = ap; + motp = &mot[ctlr->ctlrno]; + + /* + * Acknowledge all interrupts and whine about those that shouldn't + * happen. + */ + events = motp->scc->scce; + eieio(); + motp->scc->scce = events; + eieio(); + if(events & (TXE|BSY|RXB)) + print("ETHER.SCC#%d: scce = 0x%uX\n", ctlr->ctlrno, events); + //print(" %ux|", events); + /* + * Receiver interrupt: run round the descriptor ring logging + * errors and passing valid receive data up to the higher levels + * until we encounter a descriptor still owned by the chip. + */ + if(events & (RXF|RXB) || 1){ + rdre = &motp->rdr[motp->rdrx]; + while(((status = rdre->status) & BDEmpty) == 0){ + if(status & RxError || (status & (BDFirst|BDLast)) != (BDFirst|BDLast)){ + //if(status & RxBuff) + // ctlr->buffs++; + if(status & (1<<2)) + ctlr->crcs++; + if(status & (1<<1)) + ctlr->overflows++; + //print("eth rx: %ux\n", status); + if(status & RxError) + print("~"); + else if((status & BDLast) == 0) + print("@"); + } + else{ + /* + * We have a packet. Read it into the next + * free ring buffer, if any. + */ + len = rdre->length-4; + if((b = iallocb(len)) != 0){ + memmove(b->wp, KADDR(rdre->addr), len); + b->wp += len; + etheriq(ctlr, b, 1); + } + } + + /* + * Finished with this descriptor, reinitialise it, + * give it back to the chip, then on to the next... + */ + rdre->length = 0; + rdre->status = (rdre->status & BDWrap) | BDEmpty | BDInt; + eieio(); + + motp->rdrx = NEXT(motp->rdrx, Nrdre); + rdre = &motp->rdr[motp->rdrx]; + } + } + + /* + * Transmitter interrupt: handle anything queued for a free descriptor. + */ + if(events & TXB) + transmit(ctlr); + if(events & TXE) + cpmop(RestartTx, motp->cpm, 0); +} + +static void +ringinit(Mot* motp) +{ + int i, x; + + /* + * Initialise the receive and transmit buffer rings. The ring + * entries must be aligned on 16-byte boundaries. + */ + if(motp->rdr == 0) + motp->rdr = bdalloc(Nrdre); + if(motp->rrb == 0) + motp->rrb = ialloc(Nrdre*Bufsize, 0); + x = PADDR(motp->rrb); + for(i = 0; i < Nrdre; i++){ + motp->rdr[i].length = 0; + motp->rdr[i].addr = x; + motp->rdr[i].status = BDEmpty|BDInt; + x += Bufsize; + } + motp->rdr[i-1].status |= BDWrap; + motp->rdrx = 0; + + if(motp->tdr == 0) + motp->tdr = bdalloc(Ntdre); + if(motp->trb == 0) + motp->trb = ialloc(Ntdre*Bufsize, 0); + x = PADDR(motp->trb); + for(i = 0; i < Ntdre; i++){ + motp->tdr[i].addr = x; + motp->tdr[i].length = 0; + motp->tdr[i].status = TxPad|BDInt|BDLast|TxTC; + x += Bufsize; + } + motp->tdr[i-1].status |= BDWrap; + motp->tdrx = 0; +} + +/* + * This follows the MPC823 user guide: section16.9.23.7's initialisation sequence, + * except that it sets the right bits for the MPC823ADS board when SCC2 is used, + * and those for the 860/821 development board for SCC1. + */ +static void +sccsetup(Mot *ctlr, SCC *scc, uchar *ea) +{ + int i, rcs, tcs, w; + Etherparam *p; + IMM *io; + + + i = 2*(ctlr->port-1); + io = ioplock(); + w = (TXD1|RXD1)<papar |= w; /* enable TXDn and RXDn pins */ + io->padir &= ~w; + io->paodr &= ~w; /* not open drain */ + + w = (CD1|CTS1)<pcpar &= ~w; /* enable CLSN (CTSn) and RENA (CDn) */ + io->pcdir &= ~w; + io->pcso |= w; + iopunlock(); + + /* clocks and transceiver control: details depend on the board's wiring */ + archetherenable(ctlr->cpm, &rcs, &tcs); + + sccnmsi(ctlr->port, rcs, tcs); /* connect the clocks */ + + p = (Etherparam*)KADDR(sccparam[ctlr->port]); + memset(p, 0, sizeof(*p)); + p->rfcr = 0x18; + p->tfcr = 0x18; + p->mrblr = Bufsize; + p->rbase = PADDR(ctlr->rdr); + p->tbase = PADDR(ctlr->tdr); + + cpmop(InitRxTx, ctlr->cpm, 0); + + p->c_pres = ~0; + p->c_mask = 0xDEBB20E3; + p->crcec = 0; + p->alec = 0; + p->disfc = 0; + p->pads = 0x8888; + p->ret_lim = 0xF; + p->mflr = Rbsize; + p->minflr = ETHERMINTU+4; + p->maxd1 = Bufsize; + p->maxd2 = Bufsize; + p->p_per = 0; /* only moderate aggression */ + + for(i=0; ipaddr[2-i/2] = (ea[i+1]<<8)|ea[i]; /* it's not the obvious byte order */ + + scc->psmr = (2<<10)|(5<<1); /* 32-bit CRC, ignore 22 bits before SFD */ + scc->dsr = 0xd555; + scc->gsmrh = 0; /* normal operation */ + scc->gsmrl = (1<<28)|(4<<21)|(1<<19)|0xC; /* transmit clock invert, 48 bit preamble, repetitive 10 preamble, ethernet */ + eieio(); + scc->scce = ~0; /* clear all events */ + eieio(); + scc->sccm = TXE | RXF | TXB; /* enable interrupts */ + eieio(); + + io = ioplock(); + w = RTS1<<(ctlr->port-1); /* enable TENA pin (RTSn) */ + io->pbpar |= w; + io->pbdir |= w; + iopunlock(); + + /* gsmrl enable is deferred until attach */ +} + +/* + * Prepare the SCCx ethernet for booting. + */ +int +sccethreset(Ctlr* ctlr) +{ + uchar ea[Eaddrlen]; + Mot *motp; + SCC *scc; + char line[50], def[50]; + + /* + * Since there's no EPROM, insist that the configuration entry + * (see conf.c and flash.c) holds the Ethernet address. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ctlr->card.ea, Eaddrlen) == 0){ + print("no preset Ether address\n"); + for(;;){ + strcpy(def, "00108bf12900"); /* valid MAC address to be used only for initial configuration */ + if(getstr("ether MAC address", line, sizeof(line), def) < 0) + return -1; + if(parseether(ctlr->card.ea, line) >= 0 || ctlr->card.ea[0] == 0xFF) + break; + print("invalid MAC address\n"); + } + } + + scc = IOREGS(sccreg[ctlr->card.port], SCC); + ctlr->card.irq = VectorCPIC+sccirq[ctlr->card.port]; + + motp = &mot[ctlr->ctlrno]; + motp->scc = scc; + motp->port = ctlr->card.port; + motp->cpm = sccid[ctlr->card.port]; + + ringinit(motp); + + sccsetup(motp, scc, ctlr->card.ea); + + /* enable is deferred until attach */ + + ctlr->card.reset = sccethreset; + ctlr->card.attach = attach; + ctlr->card.transmit = transmit; + ctlr->card.intr = interrupt; + + return 0; +} diff --git a/os/boot/rpcg/fblt.c b/os/boot/rpcg/fblt.c new file mode 100644 index 00000000..5014e35c --- /dev/null +++ b/os/boot/rpcg/fblt.c @@ -0,0 +1,531 @@ +#include +#include +#include +#include + +/* + * bitblt operates a 'word' at a time. + * WBITS is the number of bits in a word + * LWBITS=log2(WBITS), + * W2L is the number of words in a long + * WMASK has bits set for the low order word of a long + * WType is a pointer to a word + */ +#ifndef WBITS +#define WBITS 32 +#define LWBITS 5 +#define W2L 1 +#define WMASK ~0UL +typedef ulong *WType; +#endif + +#define DEBUG + +#ifdef TEST +/* + * globals used for testing + */ +int FORCEFORW; +int FORCEBAKW; +GBitmap *curdm, *cursm; +Point curpt; +Rectangle curr; +Fcode curf; +void *mem; +#endif + +static void +gbitexplode(ulong sw, ulong *buf, int sdep, int x) +{ + int j, o, q, n, nw, inc, qinc; + ulong s, dw, pix; + + inc = 1 << sdep; + pix = (1 << inc) - 1; + nw = 1 << x; + n = 32 >> x; + qinc = (nw << sdep) - inc; + for(o = 32 - n; o >= 0; o -= n){ + dw = 0; + s = sw >> o; + q = 0; + for(j = 0; j < n; j += inc){ + dw |= (s & (pix << j)) << q; + q += qinc; + } + for(j = 0; j < x; j++) + dw |= dw << (inc << j); + *buf++ = dw; + } +} + +/* +void +main(void) +{ + ulong buf[128]; + + gbitexplode(0x7777, buf, 0, 3); + exits(0); +} +*/ + +void +gbitblt(GBitmap *dm, Point pt, GBitmap *sm, Rectangle r, Fcode fcode) +{ + int width; /* width in bits of dst */ + int wwidth; /* floor width in words */ + int height; /* height in pixels minus 1 */ + int sdep; /* src ldepth */ + int ddep; /* dst ldepth */ + int deltadep; /* diff between ldepths */ + int sspan; /* words between scanlines in src */ + int dspan; /* words between scanlines in dst */ + int soff; /* bit offset of src start point */ + int sdest; /* bit offset of src start point that matches doff when expanded */ + int doff; /* bit offset of dst start point */ + int delta; /* amount to shift src by */ + int sign; /* of delta */ + ulong *saddr; + ulong *daddr; + ulong *s; + ulong *d; + ulong mask; + ulong tmp; /* temp storage source word */ + ulong sw; /* source word constructed */ + ulong dw; /* dest word fetched */ + ulong lmask; /* affected pixels in leftmost dst word */ + ulong rmask; /* affected pixels in rightmost dst word */ + int i; + int j; + ulong buf[32]; /* for expanding a source */ + ulong *p; /* pointer into buf */ + int spare; /* number of words already converted */ + + +#ifdef TEST + curdm = dm; + cursm = sm; + curpt = pt; + curr = r; + curf = fcode; +#endif + + gbitbltclip(&dm); + + width = r.max.x - r.min.x; + if(width <= 0) + return; + height = r.max.y - r.min.y - 1; + if(height < 0) + return; + + ddep = dm->ldepth; + pt.x <<= ddep; + width <<= ddep; + + sdep = sm->ldepth; + r.min.x <<= sdep; + r.max.x <<= sdep; + + dspan = dm->width * W2L; + sspan = sm->width * W2L; + + daddr = (ulong*)((WType)dm->base + + dm->zero*W2L + pt.y*dspan + + (pt.x >> LWBITS)); + saddr = (ulong*)((WType)sm->base + + sm->zero*W2L + r.min.y*sspan + + (r.min.x >> LWBITS)); + + doff = pt.x & (WBITS - 1); + lmask = WMASK >> doff; + rmask = (WMASK << (WBITS - ((doff+width) & (WBITS-1))))&WMASK; + if(!rmask) + rmask = WMASK; + soff = r.min.x & (WBITS-1); + wwidth = ((pt.x+width-1)>>LWBITS) - (pt.x>>LWBITS); + + if(sm == dm){ +#ifdef TEST + if(!FORCEBAKW && + (FORCEFORW || sm != dm || saddr > daddr || + (saddr == daddr && soff > doff))) + ; + else{ + daddr += height * dspan; + saddr += height * sspan; + sspan -= 2 * W2L * sm->width; + dspan -= 2 * W2L * dm->width; + } +#else + if(r.min.y < pt.y){ /* bottom to top */ + daddr += height * dspan; + saddr += height * sspan; + sspan -= 2 * W2L * sm->width; + dspan -= 2 * W2L * dm->width; + }else if(r.min.y == pt.y && r.min.x < pt.x) + abort()/*goto right*/; +#endif + } + if(wwidth == 0) /* collapse masks for narrow cases */ + lmask &= rmask; + fcode &= F; + + deltadep = ddep - sdep; + sdest = doff >> deltadep; + delta = soff - sdest; + sign = 0; + if(delta < 0){ + sign = 1; + delta = -delta; + } + + p = 0; + for(j = 0; j <= height; j++){ + d = daddr; + s = saddr; + mask = lmask; + tmp = 0; + if(!sign) + tmp = *s++; + spare = 0; + for(i = wwidth; i >= 0; i--){ + if(spare) + sw = *p++; + else{ + if(sign){ + sw = tmp << (WBITS-delta); + tmp = *s++; + sw |= tmp >> delta; + }else{ + sw = tmp << delta; + tmp = *s++; + if(delta) + sw |= tmp >> (WBITS-delta); + } + spare = 1 << deltadep; + if(deltadep >= 1){ + gbitexplode(sw, buf, sdep, deltadep); + p = buf; + sw = *p++; + } + } + + dw = *d; + switch(fcode){ /* ltor bit aligned */ + case Zero: *d = dw & ~mask; break; + case DnorS: *d = dw ^ ((~sw | dw) & mask); break; + case DandnotS: *d = dw ^ ((sw & dw) & mask); break; + case notS: *d = dw ^ ((~sw ^ dw) & mask); break; + case notDandS: *d = dw ^ ((sw | dw) & mask); break; + case notD: *d = dw ^ mask; break; + case DxorS: *d = dw ^ (sw & mask); break; + case DnandS: *d = dw ^ ((sw | ~dw) & mask); break; + case DandS: *d = dw ^ ((~sw & dw) & mask); break; + case DxnorS: *d = dw ^ (~sw & mask); break; + case D: break; + case DornotS: *d = dw | (~sw & mask); break; + case S: *d = dw ^ ((sw ^ dw) & mask); break; + case notDorS: *d = dw ^ (~(sw & dw) & mask); break; + case DorS: *d = dw | (sw & mask); break; + case F: *d = dw | mask; break; + } + d++; + + mask = WMASK; + if(i == 1) + mask = rmask; + spare--; + } + saddr += sspan; + daddr += dspan; + } +} + +#ifdef TEST +void prprog(void); +GBitmap *bb1, *bb2; +ulong *src, *dst, *xdst, *xans; +int swds, dwds; +long ticks; +int timeit; + +long +func(int f, long s, int sld, long d, int dld) +{ + long a; + int sh, i, db, sb; + + db = 1 << dld; + sb = 1 << sld; + sh = db - sb; + if(sh > 0) { + a = s; + for(i = sb; i>= -sh; + + switch(f){ + case Zero: d = 0; break; + case DnorS: d = ~(d|s); break; + case DandnotS: d = d & ~s; break; + case notS: d = ~s; break; + case notDandS: d = ~d & s; break; + case notD: d = ~d; break; + case DxorS: d = d ^ s; break; + case DnandS: d = ~(d&s); break; + case DandS: d = d & s; break; + case DxnorS: d = ~(d^s); break; + case S: d = s; break; + case DornotS: d = d | ~s; break; + case D: d = d; break; + case notDorS: d = ~d | s; break; + case DorS: d = d | s; break; + case F: d = ~0; break; + } + + d &= ((1<r.min.x; + to += bb1->r.min.x; + fy = bb2->r.min.y + 1; + ty = bb1->r.min.y + 1; + if(timeit) { + memcpy(dst, xdst, dwds * sizeof(long)); + ticks -= *_clock; + gbitblt(bb1, Pt(to,ty), bb2, Rect(fr,fy,fr+w,fy+2), op); + ticks += *_clock; + return; + } + f = fr; + t = to; + memcpy(dst, xdst, dwds * sizeof(long)); + for(i=0; ibase, bb1->zero, bb1->width, bb1->ldepth, + bb1->r.min.x, bb1->r.min.y, bb1->r.max.x, bb1->r.max.y); + print("src bitmap b %#lux, z %d, w %d, ld %d, r [%d,%d][%d,%d]\n", + bb2->base, bb2->zero, bb2->width, bb2->ldepth, + bb2->r.min.x, bb2->r.min.y, bb2->r.max.x, bb2->r.max.y); + for(j=0; 7*j < dwds; j++) { + print("\ns"); + for(i=0; i<7 && 7*j+i < dwds; i++) + print(" %.8lux", src[7*j + i]); + print("\nd"); + for(i=0; i<7 && 7*j+i < dwds; i++) + print(" %.8lux", xdst[7*j + i]); + print("\ng"); + for(i=0; i<7 && 7*j+i < dwds; i++) + print(" %.8lux", xans[7*j + i]); + print("\nb"); + for(i=0; i<7 && 7*j+i < dwds; i++) + print(" %.8lux", dst[7*j + i]); + print("\n"); + } + prprog(); + } +} + +void +prprog(void) +{ + exits(0); +} + +int +main(int argc, char *argv[]) +{ + int f, t, w, i, sld, dld, op, iters, simple; + ulong s, d, spix, dpix, apix, fpix, m, *ps, *pd; + Point sorg, dorg; + GBitmap *bs, *bd; + long seed; + char *ct; + + sld = 0; + dld = 0; + timeit = 0; + iters = 200; + simple = 0; + ARGBEGIN { + case 'i': + iters = atoi(ARGF()); + break; + case 's': + simple = 1; + break; + case 't': + timeit = 1; + ct = ARGF(); + if(ct) + iters = atoi(ct); + break; + } ARGEND + if(argc > 0) + sld = atoi(argv[0]); + if(argc > 1) + dld = atoi(argv[1]); + if(!timeit && !simple) { + seed = time(0); + print("seed %lux\n", seed); srand(seed); /**/ + } + + print("sld %d dld %d\n", sld, dld); + op = 1; + + /* bitmaps for 1-bit tests */ + bd = gballoc(Rect(0,0,32,1), dld); + bs = gballoc(Rect(0,0,32,1), sld); + for(i=0; iwidth; i++) + bs->base[i] = lrand(); + + /* bitmaps for rect tests */ + if(simple) { + dorg = Pt(0,0); + sorg = Pt(0,0); + } else { + dorg = Pt(nrand(63)-31,nrand(63)-31); + sorg = Pt(nrand(63)-31,nrand(63)-31); + } + bb1 = gballoc(Rpt(dorg,add(dorg,Pt(200,4))), dld); + bb2 = gballoc(Rpt(sorg,add(sorg,Pt(200,4))), sld); + dwds = bb1->width * Dy(bb1->r); + swds = bb2->width * Dy(bb2->r); + dst = bb1->base; + src = bb2->base; + xdst = malloc(dwds * sizeof(long)); + xans = malloc(dwds * sizeof(long)); + for(i=0; ibase; + pd = bd->base; + FORCEFORW = 1; + FORCEBAKW = 0; + for(i=0; i<1000; i++, FORCEFORW = !FORCEFORW, FORCEBAKW = !FORCEBAKW) { + f = nrand(32 >> sld); + t = nrand(32 >> dld); + s = lrand(); + d = lrand(); + ps[0] = s; + pd[0] = d; +#ifdef T386 + spix = (byterev(s) >> (32 - ((f+1)<> (32 - ((t+1)<> (32 - ((f+1)<> (32 - ((t+1)<= 0;) + s += *p++; + return s; +} + +static int +validptr(Flalloc *ap, uchar *p) +{ + return p > (uchar*)&end && p < (uchar*)ap; +} + +static int +flashcheck(Flalloc *ap, char **val, int *len) +{ + uchar *base; + int n; + + if(ap->base == Noval || ap->base >= FLASHSEG || ap->tag == Tnone) + return 0; + base = flash.base+ap->base; + if(!validptr(ap, base)) + return 0; + n = (((ap->len[0]<<8)|ap->len[1])<<8)|ap->len[2]; + if(n == 0xFFFFFF) + n = 0; + if(n < 0) + return 0; + if(n > 0 && !validptr(ap, base+n-1)) + return 0; + if(ap->check != Noval && checksum(base, n) != ap->check){ + print("flash: bad checksum\n"); + return 0; + } + *val = (char*)base; + *len = n; + return 1; +} + +int +flashinit(void) +{ + int len; + char *val; + Flalloc *ap; + void *addr; + long mbytes; + char type[20]; + + flash.base = 0; + flash.exec = 0; + flash.size = 0; + if(archflashreset(type, &addr, &mbytes) < 0){ + print("flash: flash not present or not enabled\n"); /* shouldn't happen */ + return 0; + } + flash.size = mbytes; + flash.base = addr; + flash.exec = flash.base + BOOTOFF; + flash.config = nil; + flash.conflen = 0; + + for(ap = (Flalloc*)(flash.base+CONFIGLIM)-1; memcmp(ap->sig, flashsig, 4) == 0; ap--){ + if(0) + print("conf #%8.8lux: #%x #%6.6lux\n", ap, ap->tag, ap->base); + if(ap->tag == Tconf && + flashcheck(ap, &val, &len) && + len >= sizeof(conftag)-1 && + memcmp(val, conftag, sizeof(conftag)-1) == 0){ + flash.config = val; + flash.conflen = len; + if(0) + print("flash: found config %8.8lux(%d):\n%s\n", val, len, val); + } + } + if(flash.config == nil) + print("flash: no config\n"); + else + print("flash config %8.8lux(%d):\n%s\n", flash.config, flash.conflen, flash.config); + if(issqueezed(flash.exec) == Q_MAGIC){ + print("flash: squeezed powerpc kernel installed\n"); + return 1<<0; + } + if(GLLONG(flash.exec) == Q_MAGIC){ + print("flash: unsqueezed powerpc kernel installed\n"); + return 1<<0; + } + flash.exec = 0; + print("flash: no powerpc kernel in Flash\n"); + return 0; +} + +char* +flashconfig(int) +{ + return flash.config; +} + +int +flashbootable(int) +{ + return flash.exec != nil && (issqueezed(flash.exec) || GLLONG(flash.exec) == Q_MAGIC); +} + +int +flashboot(int) +{ + ulong entry, addr; + void (*b)(void); + Exec *ep; + Block in; + long n; + uchar *p; + + if(flash.exec == 0) + return -1; + p = flash.exec; + if(GLLONG(p) == Q_MAGIC){ + /* unsqueezed: copy data and perhaps text, then jump to it */ + ep = (Exec*)p; + entry = PADDR(GLLONG(ep->entry)); + p += sizeof(Exec); + addr = entry; + n = GLLONG(ep->text); + if(addr != (ulong)p){ + memmove((void*)addr, p, n); + print("text: %8.8lux <- %8.8lux [%ld]\n", addr, p, n); + } + p += n; + if(entry >= FLASHMEM) + addr = 3*BY2PG; /* kernel text is in Flash, data in RAM */ + else + addr = PGROUND(addr+n); + n = GLLONG(ep->data); + memmove((void*)addr, p, n); + print("data: %8.8lux <- %8.8lux [%ld]\n", addr, p, n); + }else{ + in.data = p; + in.rp = in.data; + in.lim = p+BOOTLEN; + in.wp = in.lim; + n = unsqueezef(&in, &entry); + if(n < 0) + return -1; + } + print("entry=0x%lux\n", entry); + uartwait(); + scc2stop(); + /* + * Go to new code. It's up to the program to get its PC relocated to + * the right place. + */ + b = (void (*)(void))KADDR(PADDR(entry)); + (*b)(); + return -1; +} diff --git a/os/boot/rpcg/fns.h b/os/boot/rpcg/fns.h new file mode 100644 index 00000000..fe747180 --- /dev/null +++ b/os/boot/rpcg/fns.h @@ -0,0 +1,118 @@ +Alarm* alarm(int, void (*)(Alarm*), void*); +void alarminit(void); +void archbacklight(int); +char* archconfig(void); +void archdisableuart(int); +void archenableuart(int, int); +void archenableusb(int); +void archetherdisable(int); +int archetherenable(int, int*, int*); +int archflashreset(char*, void**, long*); +void archinit(void); +int archoptionsw(void); +int bootp(int, char*); +void cancel(Alarm*); +void checkalarms(void); +void clockinit(void); +void clockintr(Ureg*, void*); +void consinit(void); +void cpminit(void); +void cpuidprint(void); +#define dcflush(a,b) +void delay(int); +void eieio(void); +uchar* etheraddr(int); +int etherinit(void); +int etherrxpkt(int, Etherpkt*, int); +int ethertxpkt(int, Etherpkt*, int, int); +void exception(void); +int flashboot(int); +int flashbootable(int); +char* flashconfig(int); +int flashinit(void); +void free(void*); +void freeb(Block*); +int getcfields(char*, char**, int, char*); +char* getconf(char*); +ulong getdec(void); +ulong gethid0(void); +ulong getimmr(void); +ulong getmsr(void); +ulong getpvr(void); +int getstr(char*, char*, int, char*); +ulong gettbl(void); +ulong gettbu(void); +int hardinit(void); +long hardread(int, void*, long); +long hardseek(int, long); +long hardwrite(int, void*, long); +long i2crecv(int, void*, long); +long i2csend(int, void*, long); +void i2csetup(void); +void* ialloc(ulong, int); +Block* iallocb(int); +void idle(void); +int isaconfig(char*, int, ISAConf*); +int issqueezed(uchar*); +void kbdchar(Queue*, int); +void kbdinit(void); +void kbdreset(void); +void machinit(void); +void* malloc(ulong); +ulong mapalloc(RMap*, ulong, int, int); +void mapfree(RMap*, ulong, int); +void mapinit(RMap*, Map*, int); +void meminit(void); +void microdelay(int); +void mmuinit(void); +int optionsw(void); +void panic(char*, ...); +int parseether(uchar*, char*); +int plan9boot(int, long (*)(int, long), long (*)(int, void*, long)); +void putdec(ulong); +void puthid0(ulong); +void putmsr(ulong); +int qbgetc(Queue*); +void qbputc(Queue*, int); +void qbwrite(Queue*, Block*); +Block* qget(Queue*); +long qlen(Queue*); +Queue* qopen(int, int, void (*)(void*), void*); +#define qpass qbwrite +void scc2stop(void); +void sccnmsi(int, int, int); +void sched(void); +void screeninit(void); +void screenputs(char*, int); +void sdraminit(ulong); +Partition* sethardpart(int, char*); +Partition* setscsipart(int, char*); +void setvec(int, void (*)(Ureg*, void*), void*); +int splhi(void); +int spllo(void); +void splx(int); +void trapinit(void); +void uartputs(char*, int); +void uartsetboot(void (*f)(uchar*, int)); +void uartspecial(int, int, Queue**, Queue**, void(*)(Queue*,int)); +void uartwait(void); +long unsqueezef(Block*, ulong*); + +#define GSHORT(p) (((p)[1]<<8)|(p)[0]) +#define GLONG(p) ((GSHORT(p+2)<<16)|GSHORT(p)) +#define GLSHORT(p) (((p)[0]<<8)|(p)[1]) +#define GLLONG(p) ((GLSHORT(p)<<16)|GLSHORT(p+2)) + +#define KADDR(a) ((void*)((ulong)(a)|KZERO)) +#define PADDR(a) ((((ulong)(a)&KSEGM)!=KSEG0)?(ulong)(a):((ulong)(a)&~KZERO)) + +/* IBM bit field order */ +#define IBIT(b) ((ulong)1<<(31-(b))) +#define SIBIT(n) ((ushort)1<<(15-(n))) + +#define IOREGS(x, T) ((T*)((char*)m->iomem+(x))) + +int uartinit(void); +Partition* setuartpart(int, char*); +long uartread(int, void*, long); +long uartseek(int, long); diff --git a/os/boot/rpcg/g.mx b/os/boot/rpcg/g.mx new file mode 100644 index 00000000..2398c12a --- /dev/null +++ b/os/boot/rpcg/g.mx @@ -0,0 +1,1987 @@ +S0030000FC +S325000100007C6000A63C800000608480427C632078606310007C0004AC7C6001247C0004AC52 +S325000100204C00012C7C908AA63C800A007C908BA64C00012C3C800C007C908BA64C00012C0A +S325000100403C8004007C908BA64C00012C7C0004AC7C988AA63C800A007C988BA64C00012C2F +S325000100603C800C007C988BA64C00012C3C8004007C988BA64C00012C388000077C9E23A6D3 +S325000100807CBE9AA63C80FA207C9E9BA63860FF88906400043C6001016063244090640000DE +S325000100A0380000003C40001460427FF83C40001460427FF83C60FFC26063D7103882800866 +S325000100C07C0418004182002C38A2A1F87CA4285038A500037CA516707CA903A63884FFFC7B +S325000100E03863FFFC84A3000494A400044200FFF83C60FFC2606302007C6803A64E80002026 +S325000101003862A1F8388227107C8320514081001C7C8416707C8903A63863FFFC3800000065 +S32500010120940300044200FFFC3822A3C89022801438210FF8380000004800AA01480086F587 +S32500010140480000003D00FA40806800003CA008007C63287890680000480000007C6000A6CC +S325000101604E8000207C0004AC7C6001247C0004AC4C00012C4E8000207C0006AC4E800020AE +S325000101804E8000207C6000A6606480007C0004AC7C8001247C0004AC4C00012C4E800020C4 +S325000101A07C6000A65464045E7C0004AC7C8001247C0004AC4C00012C4E8000207C8000A61A +S325000101C0506404207C0004AC7C8001247C0004AC4C00012C4E8000207C6C42E64E80002062 +S325000101E07C7F42A64E8000207C7E9AA64E8000207C7602A64E8000207C7603A64E800020EE +S325000102003821FF58BC4100303C40001460427FF87C9142A69081002C7CB042A690A10028B2 +S325000102207CC902A690C100247C8102A6908100207CB342A690A1001C7CD242A690C100187C +S325000102407C9A02A6908100107CBB02A690A1000C90010008386100087C60092D3800000018 +S325000102604E800020B8410030800100287C1043A6800100247C0903A6800100207C0103A6A8 +S325000102808001001C7C0FF120800100187C1243A6800100107C1A03A68001000C7C1B03A671 +S325000102A08021002C4E8000207C3143A67C0000267C1343A67C0802A64BFFFF4938000000D6 +S325000102C04800AC294BFFFFA17C1242A67C0803A67C1042A64C00012C4C0000647FE802A6C1 +S325000102E097E1FFE84BFFFF05812280147064FFFF5484C23F418200802C0400204182006CA6 +S325000103002C040021418200587C0A0378914900143D00FA409109001C814900203CC0007AEE +S3250001032060C6120090C9000C80EA028454E7653E394700018089000C7C8451D6908900086D +S3250001034080E9001C3CC03E0090C7000083E100007FE803A6382100184E8000203D40000828 +S32500010360614A23A04BFFFFA83D400008614A23004BFFFF9C3D400008614A60004BFFFF9076 +S325000103807FE802A697E1FFE038629EC0480035D538629ED3480035CD4BFFFE49546A843E40 +S325000103A02C0A0006418101B4418201A42C0A0001418201902C0A00034182017C2C0A0004DD +S325000103C04182016838629F0891410008480035954BFFFE117066FFFF90C1001038629F1CDA +S325000103E081010010910100084800357938629F2E480035714BFFFDF5706AFFFF9141001881 +S325000104007D464671418201182C060020418201042C060021418200F038629F4F7D4846705C +S32500010420910100084800353D38629F5A80810018708400FF90810008480035294800019D1D +S325000104409061001038629F6880810010908100084800351138629F7880E2801480E7001C11 +S3250001046080E7000090E10008480034F98142801438629F87808A002080840284908100083C +S3250001048080CA002080C6028090C1000C480034D538629FA481028014810800083D20000F84 +S325000104A0612942407D084BD691010008480034B538629FB5480034AD8142801438629FB75A +S325000104C080AA002080A5010090A1000880EA002080E7010490E1000C4800348938629FD0EB +S325000104E08082801480840020A084017A908100084800347183E100007FE803A638210020A9 +S325000105004E80002038629F47480034594BFFFF1C38629F404800344D4BFFFF1038629F3523 +S32500010520480034414BFFFF0438629EE7480034354BFFFEA038629EE0480034294BFFFE9459 +S3250001054038629ED94800341D4BFFFE8838629EEE480034114BFFFE7C2C0A00074182001C87 +S325000105602C0A0050418200084BFFFE5C38629F01480033F14BFFFE5C38629EF6480033E5A7 +S325000105804BFFFE507FE802A697E1FFF03862A0A7480033D180628014806300145463C23EF5 +S325000105A070630FFF2C03082340820028386000015463103A388283C07C6322148063000080 +S325000105C083E100007FE803A6382100104E8000207C0303784BFFFFDC806280148063001CB5 +S325000105E080630000706300F05463E13E4E8000208102801480C8000038E000327FE63B96AB +S325000106007FFF39D67CDF30514082001480E8001C80C700006CC6080090C700004E8000204A +S325000106207FE802A697E1FFF080A2801480A5002080A5010070A50001408200183860FFFF96 +S3250001064083E100007FE803A6382100104E80002038E2A0C490E100084800CDA18081001802 +S325000106603CC0FFC090C4000080E1001C3C800040908700007C03037883E100007FE803A666 +S32500010680382100104E8000207FE802A697E1FFE87C030000408100183860FFFF83E100003C +S325000106A07FE803A6382100184E800020806100203882A0CE908100084800CD4180C10020CB +S325000106C0390000029106001C3862A0D2480000CD7C671B787C030000418200148061002037 +S325000106E03863002C90E10008480029053860000183E100007FE803A6382100184E800020D1 +S325000107003821FFF0814280142C030004418200103860FFFF382100104E800020812A002075 +S32500010720810A001C80E8000064E7800090E80000A0E9095260E70A00B0E90952A0E9095055 +S3250001074070E7F5FFB0E9095080A1001838E0000790E500008101001C38A0000590A80000D5 +S325000107607C030378382100104E80002080A2801480A5001C808500003FE07FFF63FFFFFF28 +S325000107807FE42039908500004E8000204E8000204E8000204E8000207FE802A697E1FFD073 +S325000107A09061003480C284247C0600004082006048008929386000A93882C3A89081000806 +S325000107C038C0008090C1000C48008F4D7C0300004180019C3C600000606380A93882C3A88F +S325000107E0388400809081000838C0008090C1000C48008F257C0300004180017438E000FF00 +S3250001080098E2C4A839000001910284243922C3A888C900002C0600FF4182001088E90000EA +S325000108202C07000A408200187C03037883E100007FE803A6382100304E80002091210028DB +S3250001084088A900002C05000A4182001088C900002C0600FF408201009121002C88C90000DE +S325000108602C06003D40820050806100344800CB5D8121002C810100287C8848507C041800BF +S3250001088040820034806100344800CB41906100188061003480A1002890A1000880E10018D9 +S325000108A090E1000C4800CAAD8121002C7C03000041820038888900002C04000A418200108F +S325000108C088A900002C0500FF40820018888900002C04000A4082FF3C392900014BFFFF34AE +S325000108E0392900014BFFFFD039490001890900002C08000A41820010888900002C0400FF0F +S325000109004082004C38628048914100089121002C914100287CCA485090C1000C4800C64520 +S3250001092038E2804880A1002C7CE53A14810100287CE83850980700003862804883E10000D7 +S325000109407FE803A6382100304E800020392900014BFFFF9C88E900002C07003D4082000816 +S325000109604BFFFEF8392900014BFFFED83862A0D548002FF17C03037883E100007FE803A6C8 +S32500010980382100304E8000203821FFF080A2801480A500087CA51BD638A500087CA5267000 +S325000109A02C051000418000183865000F7C631E7060630001382100104E80002054A3083CA7 +S325000109C0382100104E8000207FE802A697E1FFD89061002C38600009900100084BFFFDB10C +S325000109E0810280148128002080A90ABC60A500C090A90ABC9121002480A90AB83FE0FFFF7F +S32500010A0063FFFF3F7FE5283990A90AB8806100304BFFFF798121002464680001910909F076 +S32500010A2080A90AE03FE0FFFF63FF0FFF7FE5283990A90AE038600001480032B18141002C75 +S32500010A407C691B783CA0FA2060A53E8090A10020B0650000906A00B43C8000006084B000FA +S32500010A60B0830000B0030002390A00247508E0003CA020007C0828004182011038EA002401 +S32500010A8090E9000438600001480032618141002C7C691B7881010020B0680002906A00B88A +S32500010AA038C03000B0C30000B003000238AA00A474A5E0003CE020007C053800418200B8F0 +S32500010AC0388A00A4908900047C03037839000009910100089001000C48003085812100205A +S32500010AE038E0001898E9000438A0001898A9000539000001B109000638C00001B0C9002870 +S32500010B00B009002CB009002E39000001B1090030808280148084002039240A8038C000FF46 +S32500010B2098C9000691210018388000179889000A386000143CA0FFC260A50C9C90A1000854 +S32500010B4080E1002C90E1000C4800A1298121001838E04820B0E90002A0A9000260A500034A +S32500010B60B0A9000283E100007FE803A6382100284E800020388A00A43FE0FFFF63FFFFFF4D +S32500010B807FE420394BFFFF4038EA00243FE0FFFF63FFFFFF7FE738394BFFFEE87FE802A630 +S32500010BA097E1FFD08121003880A2801480A5002038A50A8038E000FF98E50006810900B4D3 +S32500010BC0910100207C0800004182001480810020A0840000708480004182009C816900B847 +S32500010BE07C0B00004182001491610020A08B0000708480004182001483E100007FE803A694 +S32500010C00382100304E8000203940FFFF80C900107C0600004182001C9121002C806900104E +S32500010C2048009409816100208121002C7C6A1B782C0AFFFF4182002C994900A438C00001DD +S32500010C40B0CB0002A08B000060848000B08B000083E100007FE803A6382100304E8000205B +S32500010C609809000883E100007FE803A6382100304E80002080A9000C7C050000418200144C +S32500010C8080810020A08400027C0400404181002481010020A0A8000060A59000B0A8000089 +S32500010CA083E100007FE803A6382100304E80002080A900187C0500004182002438690024D4 +S32500010CC080A10020A0A5000290A1000880E900187CE803A64E8000214BFFFFB89121002CF0 +S32500010CE080C900147C060000418200487C0A037881010020A10800027C0A40404080FF945C +S32500010D00914100247CCA4A1488C600248069000C90C1000880E900147CE803A64E800021F9 +S32500010D208121002C80A10024394500014BFFFFC480610020A0630002480090E9906100183D +S32500010D408063000480C1002C38C6002490C1000880810020A08400029081000C4800C2054A +S32500010D608121001881010020A1080002808900047D044214910900048061002C8063000CE7 +S32500010D80912100084800920D4BFFFF087FE802A697E1FFE09061001C4BFFF4098121001CE2 +S32500010DA090610018888900087C0400004082001C80690010480092758121001C90610014A1 +S32500010DC02C03FFFF4082001C806100184BFFF3F183E100007FE803A6382100204E800020FF +S32500010DE080C280B47C06000041820064810900B8A108000071088000418200084BFFFFF0E5 +S32500010E0080C1001498C900A480C900B838A00001B0A6000280C900B8A0A6000060A5800073 +S32500010E20B0A600007D284B7880A280B47C0500004082001038A0000198A800084BFFFF6872 +S32500010E407C05037898A800084BFFFF5C7C0A0378914100102C0A00644080FFA880C900B8BD +S32500010E60A0C6000070C68000408200084BFFFF943860000148001C098121001C80C1001093 +S32500010E80394600014BFFFFCC7FE802A697E1FFE0906100243902C4B080A800047C050000DF +S32500010EA04182001483E100007FE803A6382100204E8000209101001C38C0000190C8000476 +S32500010EC038601000900100089001000C90010010480090098121001C9069000C8101002C3A +S32500010EE09068000038604000900100083CE0FFC260E70E8C90E1000C9121001048008FDDD1 +S32500010F00814100288121001C9069001080C100309066000080A1003490A90014388000094F +S32500010F20908900007C0A000040820008394025807D234B78914100084BFFFA9183E10000AD +S32500010F407FE803A6382100204E8000209062C4C84E8000207FE802A697E1FFD88181003017 +S32500010F607C6D1B7838C2C4B090C100247C0B03787C6A1B7891A1002C7D0D62147C0A40402C +S32500010F804080002C88AA00007CA507742C05000A40820008396B0001394A000191A1002C04 +S32500010FA07D0D62147C0A40404180FFDC7C6C5A1448008E71818100308141002C7C6B1B7851 +S32500010FC07D856378398CFFFF7C0500004182005488AA00007CA507742C05000A40820018EF +S32500010FE080CB000439060001910B00043900000D99060000808B000438C4000190CB00046B +S325000110007D465378394A000188C6000098C400007D856378398CFFFF7C0500004082FFB477 +S3250001102080610024806300109161000848008F6583E100007FE803A6382100284E80002098 +S325000110403821FFF03902C4B088E800087C070000418200084BFFFFF4382100104E80002038 +S325000110607FE802A697E1FFE0812100289061002438A2C5707C0900004081001490A1001C6E +S32500011080808500007C0400004082001483E100007FE803A6382100204E8000207D234B78B0 +S325000110A048008D819061001880630004808100249081000880E1002890E1000C4800BEA5F4 +S325000110C08121001880E10028808900047CE43A1490E900048061001C8063000091210008F4 +S325000110E048008EB183E100007FE803A6382100204E800020386000014E8000207FE802A6F1 +S3250001110097E1FFF08061001838A2819890A100084800C3517C0300004182001C8061001889 +S325000111203882819D908100084800C3397C03000040820038900281F83CC0002090C281FC04 +S32500011140386281F83863000838E281A290E100084800C2A9386281F883E100007FE803A6E2 +S32500011160382100104E8000207C03037883E100007FE803A6382100104E8000207FE802A63D +S3250001118097E1FFE88082C5707C0400004082001C3C600001900100089001000C90010010E0 +S325000111A048008D399062C5708102C5747C080000418200108062C57448008CC19002C57465 +S325000111C0386281A74800279D3C60FFC2606311604BFFFD7D38C0FFFF90C2C5789002C57C8D +S325000111E09002C5C09002C5C47C03037883E100007FE803A6382100184E8000207FE802A6DA +S3250001120097E1FFD08181003890610034810100349101001C3942C57080E2801480E70000B0 +S3250001122090E1002C918100387C0C000040810140812A000491410028900A00047C0900006A +S32500011240408200A0900A0054806A000048008CC981810038814100287C691B787C03000095 +S325000112604082008080C2801480C600008101002C7CC8305054C5083C7CC62A1454C6183850 +S3250001128054A5482C7CC6285038E000327CC63B9628063A984180FFB480EA005438E7000171 +S325000112A090EA00542C07000341800020386281C1480026B17C03037883E100007FE803A6D9 +S325000112C0382100304E800020386281D039000001910100084BFFFC81814100284BFFFF6C6B +S325000112E080A9000480C900007D4628507C0A6000408100087D8A63788061001C91210020D6 +S3250001130080E9000090E10008914100249141000C4800BC5181610024814100288121002009 +S32500011320808900007CE45A1490E9000080E9000080C900047C073040418000507D234B7839 +S3250001134048008B39816100248141002880E100387D8B385080E1001C7CA75A1490A1001C06 +S32500011360918100387C0C00004181FEC88061001C80E100347C67185083E100007FE803A6BB +S32500011380382100304E800020912A00044BFFFFC07FE802A697E1FFE0808100289081001C4B +S325000113A08122C5C07C0900004081005880E1002C7C093800408100088121002C8102C5C077 +S325000113C07CC9405090C2C5C08061001C38E2C57038E7001090E10008912100189121000CDE +S325000113E04800BB81812100188081001C7CE44A1490E1001C8081002C7CE9205090E1002CA1 +S3250001140080E1002C7C070000408100C83862815038A0004090A100084BFFFDE58141002C56 +S325000114207C691B782C030040408000187C03037883E100007FE803A6382100204E80002011 +S325000114407C035000408100307C8A48509082C5C03862C5703863001038A281507CA55214E4 +S3250001146090A1000880A2C5C090A1000C4800BAF58121002C8061001C38C2815090C1000862 +S32500011480912100189121000C4800BAD9812100188101002C7CC9405090C1002C8101001C8A +S325000114A07CC84A1490C1001C80C2C57C38C6000190C2C57C386281D2388000019081000842 +S325000114C04BFFFA9580E1002C7C0700004181FF408061001C810100287C68185083E10000C4 +S325000114E07FE803A6382100204E8000203821FFE87C0B03782C0B0100408000545569C00E54 +S32500011500394000087C0A00004081002875248000418200445526083C3FE004C163FF1DB7DB +S325000115207FE93278394AFFFF7C0A00004181FFE05567103A38A2DC307CE72A1491270000A5 +S32500011540396B00012C0B01004180FFB4382100184E8000205529083C4BFFFFCC7FE802A6EE +S3250001156097E1FFF081410018906100148082DC347C0400004082000C4BFFFF758141001826 +S3250001158080C1001C3FE0FFFF63FFFFFF7FE8327881210014394AFFFF7C0A00004180003C9F +S325000115A05505463E7D264B783929000188C600007CA5327854A5103A38C2DC307CA53214B4 +S325000115C080A500005507402E7CE82A78394AFFFF7C0A00004080FFCC3FE0FFFF63FFFFFF00 +S325000115E07FE3427883E100007FE803A6382100104E800020816100088121000C7C0B0000DE +S32500011600408200148083000090890000912300004E80002080AB000090A90000912B00000F +S325000116204E800020814100088121000C7C0A00004082001080890000908300004E800020DB +S3250001164080C9000090CA00004E8000207FE802A697E1FFE87C09037838E2ED382C09000A06 +S325000116604080002C80C700047C0600004082001080A7000C7C0500004182003039290001CE +S3250001168038E700142C09000A4180FFDC386282D8480023597C03037883E100007FE803A60F +S325000116A0382100184E8000209007000C9007001038A0000190A700047CE33B7883E10000F0 +S325000116C07FE803A6382100184E8000207FE802A697E1FFD89061002C7C03000040800008D2 +S325000116E09001002C4BFFEABD906100184BFFFF61818280147C6A1B7880C1002C54C5083CA8 +S325000117007CC62A1454C620367CC62A1438E003E87CC63B9690C3000880A1003090A3000C51 +S3250001172080810034908300107C0B0378812C00187C0900004182003480A90008808A0008C4 +S325000117407C0520004181005480A90008808A00087CA5205090AA00087D2B4B7881290000A0 +S325000117607C0900004082FFD4386C001891610008914100249141000C4BFFFE7D8061001800 +S325000117804BFFEA3D8061002483E100007FE803A6382100284E800020808A00088109000845 +S325000117A07C844050908900084BFFFFC09003000C4E8000204E8000207FE802A697E1FFA8BF +S325000117C04BFFE9E1814280149061004C812A00187C090000418200A47C0B03787C09000024 +S325000117E04182001080E900087C070000408100A47C0900004182001080E9000838E7FFFFD0 +S3250001180090E900087C090378916100507C095800408000685527103A3901001C7CE7421424 +S3250001182080E7000080E7000C7C07000041820028912100545523103A38C1001C7C63321457 +S32500011840806300007CE803A64E80002181610050812100545525103A3881001C7CA522148A +S3250001186080A500009005000439290001916100507C0958004180FFA08061004C4BFFE94120 +S3250001188083E100007FE803A6382100584E8000202C0B000A418000084BFFFF589161005041 +S325000118A05568103A38C1001C7D08321491280000386A0018900100089121000C4BFFFD69C0 +S325000118C081428014812A001880E10050396700014BFFFF0CA121000A5527043E54E7C23E7B +S325000118E098E300005526043E98C300014E800020814100085544463E988300005546843E00 +S3250001190098C300015548C23E99030002994300034E8000207C671B78886300005463C00E76 +S32500011920888700015484801E7C63237888A7000254A5402E7C632B7888C700037C633378AA +S325000119404E8000207C671B78886300005463402E88A700017C632B784E8000203821FFD83C +S325000119608222809C814100307C691B787C0F03787C0C03787C0B03787C100378706500014E +S32500011980418200207C0A000041820014888900007D8C2214394AFFFF392900013A00000191 +S325000119A02C0A001041800054A0E90000A08900027D6B3A14A0C900047DAB2214A109000660 +S325000119C07DCD3214A0A900087DCE4214A0E9000A7DCE2A14A089000C7DCE3A14A0C9000E1D +S325000119E07DAE22147D6D3214394AFFF0392900102C0A00104080FFB42C0A00024180001C9D +S32500011A00A0C900007D6B3214394AFFFE392900022C0A00024080FFEC7C10000041820070A2 +S32500011A207C0A00004182000C88A900007DEF2A14889100007C8407747C0400004182004850 +S32500011A407DEF5A145588C23E7D2F4214718800FF5508402E7D2942145528843F41820014F0 +S32500011A607127FFFF7D283A145528843F4082FFF43FE0FFFF63FFFFFF7FE34A7838210028BF +S32500011A804E8000207D8C5A144BFFFFBC7C0A00004182000C88A900007D8C2A1488910000EF +S32500011AA07C8407747C0400004182000C7D8C5A144BFFFF947DEF5A144BFFFF8C3821FFF00A +S32500011AC07C691B787C0B037888E3000070E7000F54EA103A7C0A00004081002888C900006C +S32500011AE054C6402E890900017CC643787D6B3214394AFFFE392900027C0A00004181FFE08E +S32500011B007168FFFF5566843E7D0832147104FFFF5507843E7C843A146883FFFF382100106E +S32500011B204E8000207FE802A697E1FFD0906100343862B7C89061002C9001000838E005F059 +S32500011B4090E1000C4800B1E98061002C3863002A80A1003C90A10008810100409101000C57 +S32500011B604800B4018121002C80C1004039460008980900163900001199090017980900141C +S32500011B80980900153869001891410020914100084BFFFD458061002C3863001A8102822090 +S32500011BA0910100084BFFFD4D8061002C38630022A0E2822490E100084BFFFD1D8061002C14 +S32500011BC03863001E8101003881080000910100084BFFFD218061002C3863002480810038FB +S32500011BE0A0840004908100084BFFFCED8061002C3863002680E1002090E100084BFFFCD983 +S32500011C008121002C98090028980900293869001680E1004038E7001490E100084BFFFD41D0 +S32500011C205464043E908100188061002C3863002881010018910100084BFFFC9D3922B7C8B9 +S32500011C40810100403948002C390000459909000E9809000F390000FF990900169121002866 +S32500011C60386900109141002438AAFFF290A100084BFFFC6580610028386300128082801CAB +S32500011C8038E4000190E2801C908100084BFFFC49812100289809001498090015980900187C +S32500011CA0980900193869000E4BFFFE155468043E91010018806100283863001880810018DA +S32500011CC0908100084BFFFC11806100283863000C39000800910100084BFFFBFD80610028B7 +S32500011CE080C1003838C6000690C10008388000069081000C4800B26D8061003438E2B7C817 +S32500011D0090E10008808100249081000C38E0138890E100104800471583E100007FE803A6B5 +S32500011D20382100304E8000207FE802A697E1FF60906100A49801001C38A0000598A1001DC2 +S32500011D409801001E80C100AC98C1001F3861001C3863000480C100B090C100084800B69D27 +S32500011D60806100B04800B66538830005806100A4810100A89101000838C1001C90C1000CED +S32500011D80908100104BFFFDA180C100B47C0600004182001C38629C48810100AC9101000897 +S32500011DA080C100B090C1000C48001BB983E100007FE803A6382100A04E8000207FE802A648 +S32500011DC097E1FFD09061003480A281F47C0500004082029C392003E880828014808400003A +S32500011DE05485083C7C842A145484183854A5482C7C84285038C000327C8433967C89221446 +S32500011E009081001880A2801480A5000054A6083C7CA5321454A5183854C6482C7CA530509A +S32500011E2038E000327CA53B96810100187C082840408102288061003438E2BDB890E10008D1 +S32500011E4080828014808400005486083C7C8432145484183854C6482C7C84305038E00032FD +S32500011E607C843B96810100187C8440509081000C480044319061002C7C0300004181000820 +S32500011E804BFFFF843882BDB8908100203864000C4BFFFAB55466043E2C060800418200086C +S32500011EA04BFFFF64806100203863000E4BFFFC11812100207C0300004182001038629C5EC5 +S32500011EC048001AA14BFFFF4088E9000E2C0700454182001038629C6F48001A894BFFFF28A4 +S32500011EE0890900172C080011418200084BFFFF1898090016386900264BFFFA4D5466043EB6 +S32500011F00806100203863001890C1002890C100084BFFF9C580610020386300284BFFFA29FB +S32500011F207C030000408200F480A10038A0A500047C050000418200208061002038630022A1 +S32500011F404BFFFA0581010038A10800047C0340004082FEB4806100203863001A4BFFF9B9E5 +S32500011F60814100389061001C80AA00003880FFFF7C0520004182001080AA00007C0328002E +S32500011F804082FE8480C100283926FFF880C100407C0930004081001038629CA6480019C52E +S32500011FA04BFFFE648061003C808100203884002A90810008912100289121000C4800AFA5FD +S32500011FC080A1003880E1001C90E5000080610020386300224BFFF97181210038B069000446 +S32500011FE03869000638A2BDB838A5000690A1000838E0000690E1000C4800AF693880000114 +S32500012000908281F48061002883E100007FE803A6382100304E80002080610020386300168C +S32500012020808100283884000C908100084BFFF9315469043E5528043E7C0800004182FEEC2C +S3250001204038629C815527043E90E1000880E1002C90E1000C4800190D7C03037883E10000B5 +S325000120607FE803A6382100304E800020392013884BFFFD687FE802A697E1FDC89061023CB4 +S325000120809801002638E0000198E10027386100263863000238829CBB9081000880C10244B4 +S325000120A090C1000C48001879390300023861002638630001910102307C681A1438829CBE60 +S325000120C090810008480018598161024038A3000280C102307CA62A1490A10230A08B0004C1 +S325000120E0908102287C090378912102342C0900054080007081010228B10B00048061023CC1 +S325000121009161000838E1002690E1000C80810230908100104BFFFA1181210240B0090004B8 +S325000121208061023C9121000880A1024890A1000C39000204910100104BFFFC858161024047 +S32500012140812102482C0300044080003480C1023439260001912102342C0900054180FF9814 +S3250001216038629D05480017FD3860FFFF83E100007FE803A6382102384E8000208889000024 +S325000121805484402E88C900017C8433782C040003418200482C040005418200084BFFFFB0BE +S325000121A038629CC488C9000254C6402E890900037CC6437890C1000838A9000490A1000C36 +S325000121C0480017A13860FFFF83E100007FE803A6382102384E8000203960000188890002C0 +S325000121E05484402E88C900037C843378916283E87C0458004182004838629CDE9081000825 +S32500012200480017618061023C80C1024090C10008388000019081000C38C29CF990C1001036 +S32500012220900100144BFFFB053860FFFF83E100007FE803A6382102384E8000203863FFFC87 +S3250001224083E100007FE803A6382102384E8000207FE802A697E1FFD0812283E890610034F9 +S325000122609801002038800004988100217D25467098A100229921002338A9000190A283E899 +S32500012280810100403908000491010040900100248061003480E1003890E1000838A1002089 +S325000122A090A1000C38E0000490E100104BFFF8798061003480E1003890E1000880A1003CFE +S325000122C090A1000C81010040910100104BFFFAF1816100408141003C7C691B787C035800B2 +S325000122E04080006438629D2C912100089161000C480016718061003480C1003890C10008E2 +S32500012300388000029081000C38C29D4090C10010900100144BFFFA158101002438C8000102 +S3250001232090C100242C080005408000084BFFFF643860FFFF83E100007FE803A638210030E0 +S325000123404E800020890A00025508402E88AA00037D082B789101002C808283E87C082000FC +S325000123604182007C38629D4B480015F9814283E88121002C38CAFFFF7C09300040820018B4 +S3250001238080A1002439050001910100242C0500084180FF0038629D4D912100089141000CE7 +S325000123A0480015C18061003480E1003890E1000838A0000190A1000C38E29D7590E100100E +S325000123C0900100144BFFF9653860FFFF83E100007FE803A6382100304E8000203863FFFC92 +S325000123E083E100007FE803A6382100304E8000207FE802A697E1FC48906103BC48003E6981 +S32500012400814103C0906100C07C0300004082002838629D8180A103BC90A1000848001545A3 +S325000124203860FFFF83E100007FE803A6382103B84E8000209001002C900100287C0A00008D +S325000124404182001488AA00007CA507747C050000408205C03861024C9001000838A0016C03 +S3250001246090A1000C4800A8C938E0000198E1024C390000019901024D388000069881024E35 +S325000124803861024C3863001C80A100C090A10008390000069101000C4800AAC990028220B1 +S325000124A038A00044B0A28224386282203863000680C100C090C10008388000069081000CEF +S325000124C04800AAA1900100DC808100DC2C04000A4080009038C0FFFF90C2823838E0004331 +S325000124E0B0E2823C3862823838630006390283689101000838A0000690A1000C4800AA655E +S32500012500806103BC390282389101000838A1024C90A1000C38E0016C90E100104BFFF609D2 +S32500012520806103BC38C2823890C10008390100E09101000C38A0016C90A100104BFFF881E6 +S325000125407C0300004181045C80C100DC38C6000190C100DC808100DC2C04000A4180FF783B +S3250001256080C100DC2C06000A4180002038629D94480013F13860FFFF83E100007FE803A6F9 +S32500012580382103B84E8000208101002C7C0800004182040080A1002C88A500007CA5077423 +S325000125A07C050000418203EC8901010C7D0807747C0800004182001838629DA538E100E016 +S325000125C038E7002C90E100084800139938629DA9888100F490810008888100F59081000C2B +S325000125E0888100F690810010888100F790810014A082823C908100188081002C9081001C2C +S32500012600480013614BFFEA3D386100E0386300104BFFF30590628220A08283A438E4000126 +S32500012620B0E283A4B0828224386100E0386300144BFFF2E59062823838E00045B0E2823C60 +S32500012640806103BC38C2823890C100088121002C9121000C38C2C5C890C100104BFFFA19F5 +S325000126607C030000408000183860FFFF83E100007FE803A6382103B84E8000203882C5C8A9 +S3250001268039440004906100D82C030020418002CC890A00005508402E88CA00017D0833782A +S325000126A05508801E88EA000254E7402E888A00037CE723787D083B782C0806EB4082029C2B +S325000126C088EA000454E7402E88AA00057CE72B7854E7801E88CA000654C6402E890A000744 +S325000126E07CC643787CED337888AA000854A5402E890A00097CA5437854A5801E888A000AF6 +S325000127005484402E88CA000B7C8433787CAC2378890A000C5508402E88CA000D7D083378A8 +S325000127205508801E88EA000E54E7402E888A000F7CE723787D083B78910100C8918100CCDF +S325000127407CC8621491A100D07CC66A1490C100C488AA001454A5402E890A00157CA54378B0 +S3250001276054A5801E888A00165484402E88CA00177C8433787CA5237838629DD090A100B491 +S325000127803FE01FFF63FFFFFF7FE8283991010008480011D14BFFE8AD38629DDF80C100D003 +S325000127A090C10008480011BD808100B43FE01FFF63FFFFFF7FE42039908100BC38E2C5C821 +S325000127C039A70024808100D83944FFE0816100D0916100D47C0A000040820050806103BC09 +S325000127E0388282389081000838E2C5C890E1000C39000200910100104BFFFA59816100D4F1 +S325000128007C6A1B787C030000408000183860FFFF83E100007FE803A6382103B84E800020D5 +S3250001282038C2C5C839A60004914100D87C0B5000418100087D6A5B78806100BC91A100B89B +S3250001284091A10008914100DC9141000C4800A715818100CC814100DC80A100BC7D05521476 +S32500012860910100BC80A100B87DA5521491A100B880A100D47D6A285080A100D87D4A28502C +S32500012880914100D8916100D47C0B00004181FF487C0C000040820088806103BC38C282380B +S325000128A090C10008390000039101000C38A29DE690A10010900100144BFFF47138629DE9CC +S325000128C080E100C890E1000880A100C490A1000C4800109138629DF1810100B4910100084C +S325000128E0480010814BFFE75D4800137D4BFFD8B580C100B43FE01FFF63FFFFFF7FE630395B +S325000129007CC803A64E8000217C03037883E100007FE803A6382103B84E80002038629DE24B +S32500012920918100084800103D81A100B8814100D8816100CC916100D4900100CC80E100BC7F +S3250001294038E70FFF3FE0FFFF63FFF0007FE7383990E100BC4BFFFE80806103BC38C28238B4 +S3250001296090C100089001000C39029DBF9101001038A0000190A100144BFFF3B13860FFFF7F +S3250001298083E100007FE803A6382103B84E80002038C100E038C6006C90C1002C4BFFFC0CA8 +S325000129A03861024C3863001C390100E03908001C9101000838A0000690A1000C4800AB0D46 +S325000129C0814100287C030000418200084BFFFB7C88E100E12C0700014082FB7088C100E225 +S325000129E02C0600064082FB647C0A00004182FB747D435378388100E03884002C908100089A +S32500012A004800AA617C0300004182FB584BFFFB3C38610030914100084800A9E13861003008 +S32500012A203880003A908100084800A2E17C0300004182004038E100309061002C7C0338001A +S32500012A404182FA1488A3FFFF7CA507742C05005C408200084BFFFA0038C1003090C100289C +S32500012A6038C3000190C1002C980300004BFFF9E8390100309101002C4BFFF9DC3821FFF081 +S32500012A807C691B7880A28014814500047D254B783929FFFF7C050000408100187C0703781A +S32500012AA07C0750404080FFE838E700014BFFFFF4382100104E8000203821FFF08102801442 +S32500012AC0810800047D0341D638A003E87D482BD741810008394000017C0903787C095040E8 +S32500012AE040800010392900017C0950404180FFF8382100104E8000207FE802A697E1FFF002 +S32500012B00806280284BFFD6F580A2801480E5000038E7000190E500004BFFECA18102823053 +S32500012B207C0800004182000C7D0803A64E80002183E100007FE803A6382100104E80002053 +S32500012B407FE802A697E1FFB88142801480CA000838E003E87CC63BD690CA00044BFFD67D36 +S32500012B60906100443860000A4BFFFF154BFFD66D808100447CE4185090E100447C07000047 +S32500012B80408000084BFFFFD83861002881028014810800049101000848009D69386100203F +S32500012BA0810280148108000C5504103A7D0822145508083C38A003E87D082BD69101000880 +S32500012BC048009D253861003039010008392100288089000080A900049088000090A80004CD +S32500012BE038A1001038C10020810600008126000491050000912500044800A0DD38610028C4 +S32500012C00810100445508103A9101000848009CD93861003838C10008390100308128000004 +S32500012C208088000491260000908600043881001038A1002880C500008105000490C40000C3 +S32500012C40910400044800935939010004392100388089000080A900049088000090A8000446 +S32500012C6048009DD581428014906A0004810A00047C0800004082000C38800001908A000486 +S32500012C8080AA000C38C000047CA533D638E000327CA53BD63865FFFF906280284BFFD55DA4 +S32500012CA083E100007FE803A6382100484E8000207FE802A697E1FFE89061001C7C0903788F +S32500012CC080A282347C09280040800060912100145523103A38C226107C6332148063000088 +S32500012CE08101001C910100084800A779812100147C030000408200245523103A38822690E0 +S32500012D007C6322148063000083E100007FE803A6382100184E8000203929000180A28234A6 +S32500012D207C0928004180FFA87C03037883E100007FE803A6382100184E8000207FE802A69B +S32500012D4097E1FF28816100E03CE0002090E100A87C0300004182021080E280A080E7000079 +S32500012D6090E1000838E100B490E1000C48002AA9814100A87C030000418100183860FFFF15 +S32500012D8083E100007FE803A6382100D84E800020980A0000386100B49141000838A003FFF6 +S32500012DA090A1000C48001FA57C6A1B787C030000418100183860FFFF83E100007FE803A6E7 +S32500012DC0382100D84E80002080A100A87CA51A1498050000806100A838630400810100A8C6 +S32500012DE09101000838CA000190C1000C4800A175808100A8396404007D6A5B787D6C5B78B4 +S32500012E00888A00007C8407747C04000041820028890A00007D0807742C08000D4082011804 +S32500012E20394A0001888A00007C8407747C0400004082FFE0980B00007D83637838E10020A2 +S32500012E4090E10008388000209081000C38C2886B90C1001048005EF1906100AC7C0A03787A +S32500012E60808100AC7C0A200040800044914100B05543103A38A100207C632A148063000097 +S32500012E8038E0003D90E1000848009E8181828234814100B07C03000040820028394A0001DE +S32500012EA0808100AC7C0A20004180FFC47C03037883E100007FE803A6382100D84E800020A7 +S32500012EC039630001980300005547103A390100207CE7421480E700007CE758502C07001DF8 +S32500012EE0418000185546103A38A100207CC62A1480C600009806001B5587103A38A226105F +S32500012F007CE72A145546103A38A100207CC62A1480C6000090C700005588103A38C2269037 +S32500012F207D08321491680000388C0001908282344BFFFF6C888A00007C8407742C040009BE +S32500012F404082000C38E0002098EA00007D685B78396B0001892A00007D29077499280000F0 +S32500012F604BFFFEC07C0B00004182005C7D635B7890010008388003FF9081000C4800A5BDCF +S32500012F807C030000408200183860FFFF83E100007FE803A6382100D84E8000203862885036 +S32500012FA0480009C1806100A880E100E090E100084800A449806100A84800A4117C6A1B7881 +S32500012FC04BFFFE144BFFD5C190610018806100A880C1001890C100084800A421806100A8D4 +S32500012FE04800A3E97C6A1B784BFFFDEC7FE802A697E1FFD89061002C8121003088A90000CC +S325000130007CA507742C050020408200183929000188A900007CA507742C0500204182FFF0AF +S325000130207C0A03789141001C2C0A0006408000C0888900007C8407747C04000040820018F8 +S325000130403860FFFF83E100007FE803A6382100284E8000203889000188A900007CA5077461 +S3250001306098A10024890400007D0807747C080000408200183860FFFF83E100007FE803A6F7 +S32500013080382100284E8000203904000191010020888400007C840774988100259801002646 +S325000130A03861002490010008388000109081000C48009EB58141001C812100208101002CE5 +S325000130C07D08521498680000890900007D0807742C08003A4082000839290001394A00014D +S325000130E09141001C2C0A00064180FF487C03037883E100007FE803A6382100284E800020BA +S325000131007FE802A697E1FFB87C691B783861002C3902886D910100089121000C81010050D3 +S3250001312091010010480007F97C09037880C282347C093000408000489121001C5523103A59 +S3250001314038E226107C633A14806300003881002C9081000838C0001C90C1000C4800A1F5BB +S325000131608121001C7C030000418200283929000180C282347C0930004180FFC07C03037896 +S3250001318083E100007FE803A6382100484E80002081010054900800345525103A388226904F +S325000131A07CA5221480A5000090A1002880E1002888E700007CE707747C07000041820040D7 +S325000131C080C1002888C600007CC607742C0600204182035080E1002888E700007CE7077436 +S325000131E02C0700094182033C80C1002888C600007CC607747C060000408200183860000121 +S3250001320083E100007FE803A6382100484E80002080610028388288729081000838C00005D1 +S3250001322090C1000C4800A12D814100547C030000408200D48101002839080005910100283F +S325000132407D49537838CA001B7C0930404080002C80A1002888A500007CA507747C05000045 +S325000132604182001880C1002888C600007CC607742C0600204082005C9809000080810028BE +S32500013280888400007C8407747C0400004182FF2081010028890800007D0807742C080020AF +S325000132A0408200084BFFFF0880810028888400007C8407742C040009408200084BFFFEF001 +S325000132C080A1002838A5000190A100284BFFFFB080E1002888E700007CE707742C0700095C +S325000132E04182FF9880A10028388500019081002888A500007CA5077498A9000039290001C0 +S325000133004BFFFF448061002838E2887890E10008388000059081000C4800A0397C03000003 +S3250001332040820028806100283863000539010028910100089001000C48009C2D80A10054D4 +S325000133409065001C4BFFFF388061002838A2887E90A1000838E0000490E1000C48009FF53D +S325000133607C03000040820028806100283863000438C1002890C100089001000C48009BE952 +S3250001338081010054906800204BFFFEF480610028390288839101000838A0000490A1000CFA +S325000133A048009FB17C03000040820028806100283863000438810028908100089001000CC6 +S325000133C048009BA580C10054906600244BFFFEB08061002838C2888890C10008390000050D +S325000133E09101000C48009F6D7C03000040820028806100283863000538E1002890E1000808 +S325000134009001000C48009B6180810054906400284BFFFE6C806100283882888E90810008AD +S3250001342038C0000390C1000C48009F29812100547C0300004082003C3869002C80A1002894 +S3250001344038A5000390A100084BFFFBA52C03FFFF4082FE2C806100543863002C90010008B4 +S32500013460388000069081000C480098C54BFFFE1080C900342C0600084080FE0480A9003497 +S3250001348054A520367CA54A143945003881010028890800007D0807747C0800004182001807 +S325000134A080E1002888E700007CE707742C07002040820018980A000080C9003438C60001E4 +S325000134C090C900344BFFFDB881010028890800007D0807742C080009408200084BFFFFD8F6 +S325000134E07D445378394A000180A10028390500019101002888A500007CA5077498A400006E +S325000135007CE9505081090034550820367CE8385038E7FFC82C07000F4180FF744BFFFF9805 +S325000135208101002839080001910100284BFFFC947FE802A697E1FFF09061001480C1001830 +S3250001354090C100084BFFDA118061001480A1001890A100084800729183E100007FE803A6B0 +S32500013560382100104E8000207FE802A697E1FFD0386295484BFFF73D7C691B787C090000A5 +S32500013580418200247D234B789121002C38A2955B90A1000848009C7D8121002C7C0300004B +S325000135A04182009C3C80FFC260841054908282F4900100247C090000418200187D234B78E0 +S325000135C0900100089001000C4800999D90610024900100283862955F4BFFF6D981210028F1 +S325000135E07C03000041820014900100089001000C480099757C691B787C090000408200081B +S32500013600392025808061002491210008388281909081000C38C281D890C100103D00FFC24C +S325000136206108374C910100144BFFD86183E100007FE803A6382100304E8000203CE0FFC2A6 +S3250001364060E7363090E282F44BFFFF687FE802A697E1FFF09061001480C1001870C8007F92 +S32500013660910100182C08001040820010386295644800037981010018806100149101000803 +S3250001368048006A4D83E100007FE803A6382100104E8000207FE802A697E1FFE090610024DE +S325000136A09001001880C2801480C6000090C100148101002C7C0800004182001C80A2801412 +S325000136C080A5000080C100147CA62850280500FA4181010480628190480069517C691B7874 +S325000136E02C03FFFF408200084BFFFFC82C03000D408200083920000A2C09007F40820008D4 +S32500013700392000089121001C2C090015408200C03900000A99010013386100133880000152 +S325000137209081000880C282F47CC803A64E80002181810024816100288141001C812100180D +S325000137402C0A0008408200187C0900004081FF5838E9FFFF90E100184BFFFF4C2C0A000A30 +S32500013760408200207CEC4A14980700007D234B7883E100007FE803A6382100204E800020BD +S325000137802C0A0015408200183860FFFF83E100007FE803A6382100204E8000207C095800AF +S325000137A0408200187D635B7883E100007FE803A6382100204E8000203909000191010018AD +S325000137C07C8C4A14994400004BFFFEDC992100134BFFFF483860FFFE83E100007FE803A614 +S325000137E0382100204E8000207FE802A697E1FFE090610024808100289804000080E100308A +S325000138007C0700004182001880810030888400007C8407747C040000408200FC7C06037850 +S3250001382090C10018808100187C040000418200683862956780C1002490C1000880810030CF +S325000138409081000C4800011D8061002880E1002C90E1000880C1001890C1000C4BFFFE3998 +S325000138607C691B782C03FFFE4182008C2C03FFFF4182FFB48101002C7C0940004180002C4B +S3250001388038629580480000DD808100187C0400004082FFA03862957980E1002490E10008AD +S325000138A0480000C14BFFFFA47C0900004082001080E100187C070000408200187C030378E4 +S325000138C083E100007FE803A6382100204E8000208061002880A1003090A1000848009B1D73 +S325000138E07C03037883E100007FE803A6382100204E8000203862957E38800001908100086D +S3250001390080C282F47CC803A64E8000217C0903784BFFFF6438C000014BFFFF087FE802A606 +S3250001392097E1FFE89061001C38C3010090C10008808100209081000C38E1002038E7000425 +S3250001394090E10010480007F18081001C7C64185083E100007FE803A6382100184E80002067 +S325000139607FE802A697E1FEE09061012480E282F47C070000408200187C03037883E1000032 +S325000139807FE803A6382101204E8000203861002038C1002038C6010090C1000881010124D7 +S325000139A09101000C38C1012438C6000490C1001048000785390100207C881850386100208E +S325000139C09081001C90810008810282F47D0803A64E8000218061001C83E100007FE803A613 +S325000139E0382101204E8000207FE802A697E1FEE0812282F4906101247C0900004182007408 +S32500013A003862958F38800007908100087D2803A64E8000213861002038A1002038A501003C +S32500013A2090A1000880E1012490E1000C38A1012438A5000490A10010480006FD38E100209F +S32500013A407D071850386100209101000880E282F47CE803A64E8000213862959738800001CD +S32500013A609081000880C282F47CC803A64E8000214BFFC7154BFFC70D4BFFFFFC7FE802A6FA +S32500013A8097E1FFE0808280148124002038C0000190C90030808908403FE0FFFF63FFFFFE1E +S32500013AA07FE4203990890840B0090968B0090964B0090960B0090962B0090966B009095210 +S32500013AC0B0090950B0090954B009095690090ABC90090AB8B0090AC29121001C90090AC42B +S32500013AE04BFFC6998101001C3CA0000060A58001B0A809C080C1001CA0C609C070C6000132 +S32500013B00418200184BFFC67580C1001CA0C609C070C600014082FFF0386281E038E2B3C83A +S32500013B2090E10008388004009081000C480065E9386281E03CC0FA2060C6240090C10008E2 +S32500013B40390004009101000C480065F183E100007FE803A6382100204E8000207FE802A6FB +S32500013B6097E1FFE89061001C4BFFC639906100108102801481080020910100144BFFC5FD16 +S32500013B8081010014A10809C071080001418200184BFFC5E981010014A10809C07108000147 +S32500013BA04082FFF08081001C5484402E80A1002054A520367C842B7880C1002454C6083CF4 +S32500013BC07C8433786084000180A10014B08509C04BFFC5A980E10014A0E709C070E7000146 +S32500013BE0418200184BFFC59580E10014A0E709C070E700014082FFF0806100104BFFC5C1B0 +S32500013C0083E100007FE803A6382100184E8000203821FFE85465183838A5FFF8810100200B +S32500013C20710800075508183880E1002470E700077D083B787D0B283080E280148127002097 +S32500013C4080E90AEC390000FF7D0828307D0840F87CE740387CE75B7890E90AEC382100183B +S32500013C604E8000207FE802A697E1FFE080E2801480E7002039270A209121001C80E90000AB +S32500013C8070E70030418200543860000538C0000490C100089001000C4BFFFEC53860000744 +S32500013CA039000004910100089001000C4BFFFEB1386000014BFFEDC980A1001C80E5000055 +S32500013CC03FE0FFFF63FFFFCF7FE7383990E50000386000044BFFCA9983E100007FE803A687 +S32500013CE0382100204E8000207FE802A697E1FFE07C681B78386281E0900100085507183839 +S32500013D0090E1000C90010010480065997C681B789061001C7C030000408200103862833B0B +S32500013D204BFFFCC98101001C7D03437883E100007FE803A6382100204E8000207FE802A6AA +S32500013D4097E1FFE89061001C7C030000418200384BFFC4298121001C386281E07528E00009 +S32500013D603CA020007C0828004182002C7D274B7890E1000880810020548418389081000C5F +S32500013D80480063B983E100007FE803A6382100184E8000203FE0FFFF63FFFFFF7FE7483981 +S32500013DA04BFFFFD07FE802A697E1FFE83E0000006210900081E1002481C100207C6A1B78D4 +S32500013DC091C3000C9061001C80E300007C070000408200247DC373784BFFFF113E000000E0 +S32500013DE06210900081E1002481C100208141001C906A000080C1002838C6000F3FE0FFFF67 +S32500013E0063FFFFF07FED3039808A00047C0400004082002C91A100287C6E69D64800518558 +S32500013E203E0000006210900081E1002481C1002081A100288141001C906A000480CA0000E3 +S32500013E407C060000418201A880AA00047C0500004182019C80AA000474A5E0003CC020001B +S32500013E607C05300041820174808A00047C8C23787C0B03787C0B700040800044556418389B +S32500013E8080EA00007C843A14B004000255661838808A00007CC622149186000455651838FB +S32500013EA0810A00007CA54214B20500007D8C6A14396B00017C0B70004180FFC4556818388E +S32500013EC080CA00007D083214A0A8FFF860A52000B0A8FFF8900A000891EA0020808A0010BC +S32500013EE07C040000408200187DE37B784BFFFDFD81E100248141001C906A0010810A0014BD +S32500013F007C0800004082001855E3103A4800509581E100248141001C906A001480EA0010A1 +S32500013F207C070000418200A080CA00147C060000418200947C0B03787C0B7800408000504C +S32500013F405567103A80AA00147CE72A14900700005564183880EA00107C843A149004000475 +S32500013F6055661838808A00107CC62214B00600025568183880CA00107D083214B0080000FB +S32500013F80396B00017C0B78004180FFB855651838810A00107CA54214A0E5FFF860E72000FF +S32500013FA0B0E5FFF8900A0018900A001C900A00247C03037883E100007FE803A63821001869 +S32500013FC04E8000203860FFFF83E100007FE803A6382100184E800020808A00043FE0FFFF58 +S32500013FE063FFFFFF7FE420394BFFFE843860FFFF83E100007FE803A6382100184E80002069 +S325000140003821FFF0818282D0816100187C6A1B782C0B00094082005C7D8B6378388C00087C +S325000140203FE0FFFF63FFFFF87FEC20397D675B78396B0001918282D07C07600040800014C7 +S32500014040808A0000810A00047C0440404180000C382100104E80002080EA000039270001D1 +S32500014060912A000038800020988700004BFFFFC080A30000808300047C0520404080001C97 +S325000140808103000039280001912300009968000038AC000190A282D02C0B000A408200080A +S325000140A0900282D0382100104E800020814283E07C6907747D26077470C6007F7CC90774A5 +S325000140C07D24077438C285A87C843214888400007C8407747C0400004082002C2C0A001E07 +S325000140E04180000C386000014E8000207D26077438E285A87CC63A14390A0001910283E036 +S32500014100994600007D250774390285A87CA5421488A500007CA5077454A5103A38C284D063 +S325000141207CA5321480E1000890E500007C0303784E8000207FE802A697E1FFC890610018F4 +S3250001414080E1004090E1001C80810048908100208141004438EA000190E10044894A0000FF +S325000141607D4A07742C0A0025418200487C0A00004082003080A1001880E1001C7C05384069 +S325000141804080000C80810018980400008061001883E100007FE803A6382100384E800020AB +S325000141A038610018914100084BFFFE594BFFFFA49001002438A0FFFF90A100289001002C3E +S325000141C03967000191610044894700007D4A07747C0C03782C0A002D4082001C39800001F1 +S325000141E07D6A5B78396B000191610044894A00007D4A07742C0A00304180000C2C0A00396C +S325000142004081010C7C0C00004182001080A100247D050050910100242C0A002E41820084F6 +S325000142207C0A00004082000C38CBFFFF90C100443861001838A285A87146007F7CA5321438 +S3250001424088A500007CA5077454A5103A38C284D07CA5321480A500007CA803A64E800021B5 +S325000142607C0300004080002C7CA300508081002C7C852B7890A1002C81410044396A000185 +S3250001428091610044894A00007D4A07744BFFFF9480E100207CE71A1490E100204BFFFEB450 +S325000142A07D6A5B78396B000191610044894A00007D4A07742C0A00304180FF682C0A003950 +S325000142C0408100084BFFFF5C808100287C040000408000089001002880C1002854C5103A73 +S325000142E07CC62A1454C6083C7CC6521438C6FFD090C100287D6A5B78396B00019161004456 +S32500014300894A00007D4A07744BFFFFAC810100245507103A7D083A145508083C7D085214E1 +S325000143203908FFD0910100247D6A5B78396B000191610044894A00007D4A07744BFFFEB806 +S325000143407FE802A697E1FFE0906100244800907D7C6A1B789061001C80E1002C7C070000F0 +S325000143604180003C9141001C80C1002C7C0A30004080002C80610028388000209081000842 +S325000143804BFFFC8180C1001C394600019141001C80C1002C7C0A30004180FFDC81610024BF +S325000143A07D665B78396B00019161001488C600007CC607747C0600004182003480E1003080 +S325000143C07C0700004182FFDC8061002890C100084BFFFC31816100148141001C8101003056 +S325000143E03908FFFF910100304BFFFFB880C1002C7C0600004080004880A1002C7D0500509E +S325000144009101002C9141001C80A1002C7C0A28004080002C80610028390000209101000806 +S325000144204BFFFBE180A1001C394500019141001C80A1002C7C0A28004180FFDC83E10000AA +S325000144407FE803A6382100204E8000207FE802A697E1FFA881C282E881A100607C6C1B7806 +S325000144607C0F037880C3001470C600072C0600044181015C418201442C060001418201281F +S325000144802C0600024182010080AC00088145000038E0000490E1002880CC001470C60004D4 +S325000144A04082000C7C0A0000418000D0980100573960001C7FEA6B967FFF69D67CFF505029 +S325000144C0392700302C09003940810014392900277C0E0000418200083929FFE038C1003AB0 +S325000144E07CC65A14992600002C0B0002408000547C0F0000418200183881003A396BFFFFFE +S325000145007C845A1438A0002D98A400003861003A7C635A149181000880EC000C90E1000CB6 +S3250001452038E0FFFF90E100104BFFFE198061002883E100007FE803A6382100584E80002060 +S325000145407D4A6B96810C00107C0800004180001880EC00103900001E7CE740507C0B38000D +S32500014560408000107C0A0000418100084BFFFF84396BFFFF4BFFFF407D4A005039E00001EB +S325000145804BFFFF2C80AC000880A500007CA907347D2407347C8A2378388000049081002874 +S325000145A04BFFFEF8808C00088144000038C0000490C100284BFFFEE4810C000881480000DC +S325000145C038A0000490A100284BFFFED02C060005418200302C060006418200084BFFFEAC66 +S325000145E080AC000880A500007CA907345524043E7C8A237838800004908100284BFFFE9CC6 +S3250001460080E300088147000038800004908100284BFFFE887FE802A697E1FFE87C671B78B7 +S32500014620386280B890E100089001000C3880FFFF908100104BFFFD0D7C03037883E1000002 +S325000146407FE803A6382100184E8000207FE802A697E1FFE07C681B7880E3000880E7000035 +S3250001466098E1001E9801001F3861001E910100088088000C9081000C3880FFFF908100108B +S325000146804BFFFCC13860000483E100007FE803A6382100204E8000207FE802A697E1FFF01F +S325000146A03900000A910100084BFFFDA583E100007FE803A6382100104E8000203860FFFECA +S325000146C04E8000203860FFFF4E8000207FE802A697E1FFF038E0000890E100084BFFFD719A +S325000146E083E100007FE803A6382100104E8000207FE802A697E1FFE87C671B788063000819 +S325000147008063000090E100088107000C9101000C81070010910100104BFFFC2938600004BF +S3250001472083E100007FE803A6382100184E8000203860FFFC4E8000207FE802A697E1FFF0A8 +S3250001474038A0001090A100084BFFFD0583E100007FE803A6382100104E8000207FE802A60B +S3250001476097E1FFE839000001910282E838C0001090C100084BFFFCD9900282E883E10000BC +S325000147807FE803A6382100184E8000207FE802A697E1FFF038E0002590E100084BFFF865D0 +S325000147A07C03037883E100007FE803A6382100104E8000207FE802A697E1FFD89061002CB2 +S325000147C03862889880A1003090A100084BFFF19581A100308181002C3942A1F83902A1F856 +S325000147E0390801407C0A40404080003080CA00047C0D30004082001080EA000C7C0C38002B +S32500014800418201B4394A00143902A1F8390801407C0A40404180FFD83962A1F838C2A1F8C7 +S325000148203946001438C2A1F838C601407C0A30404080002C808A0000810B00007C044000D4 +S32500014840418100087D4B5378394A001438C2A1F838C601407C0A30404180FFDC7D6A5B783F +S3250001486080CC001480EC00107D6639D69141002480CA00087C0600004182011480AA001087 +S325000148807C055800418001089161001C916A0010386288BD80AC000C7CAD2A14810C00103A +S325000148A07CA541D690A100084BFFF0B98141002C806A000080EA000C812100307CE93A14BA +S325000148C080AA00107CE729D690E1000880AA00087CA803A64E8000218141002C7C03000061 +S325000148E040800020386288CF4BFFF0797C03037883E100007FE803A6382100284E80002050 +S32500014900806A00008101002481080008910100088101001C9101000C80CA00047CC803A65E +S325000149204E800021816100308141002480A1001C7C03280041820020386288E14BFFF02560 +S325000149407C03037883E100007FE803A6382100284E8000208082801480840000908A0000BF +S3250001496080C1002C90CA000C916A0004386288F3916100084BFFEFED8061002483E10000C0 +S325000149807FE803A6382100284E8000207D635B789161001C90010008480045818161001C2B +S325000149A08141002481A100308181002C906A00084BFFFED89141002480A2801480A5000097 +S325000149C090AA0000386288A791A100084BFFEF958061002483E100007FE803A638210028CB +S325000149E04E8000207FE802A697E1FFD89061002C3862890580E1003090E100084BFFEF6577 +S32500014A00816100308141002C2C0B0002418001AC80EA00347C0B3800408001A0810A003867 +S32500014A202C08000C418201802C080010418200183860FFFF83E100007FE803A63821002841 +S32500014A404E800020556B083C810A0030812A00107D0849D6916100247C0B40404180001451 +S32500014A60386289114BFFEF85816100248141002C808A001080EA00147C8439D67C8B2396E2 +S32500014A80810A00147C8441D680EA003C7C843A1480EA0010810A00147CE741D67FEB3B9697 +S32500014AA07FFF39D67CFF585090E100187D43537890810020908100084BFFFCFD8181002C70 +S32500014AC07C6A1B7880C3000881010018396800017CC6421488C6000090C1002480CC001018 +S32500014AE080EC00147CC639D67C0B3000418000287D836378808C001480A100207C852214CB +S32500014B00908100084BFFFCB18181002C7C6A1B787C0B0378810A00087D085A1489080000BE +S32500014B205508402E808100247C8A437880EC00382C07000C4082002080C1003070C6000150 +S32500014B404182005C554AE13E280A0FF841800008614AF0003CE0000060E7FFF87C0A38407C +S32500014B60408000347D4453783862891880A1003090A10008908100249081000C4BFFEDE57B +S32500014B808061002483E100007FE803A6382100284E8000203880FFFF4BFFFFD0714A0FFF8E +S32500014BA04BFFFFA85567083C7D0B3A14392000027D684BD64BFFFE943860FFFF83E10000F0 +S32500014BC07FE803A6382100284E8000207FE802A697E1FFD87C691B7881030000910100243F +S32500014BE03862892A9121002C390900049101000880C1003090C1000C4BFFED6981A1002CE7 +S32500014C008181003081610024808D00147C04000040820070810B00107D0C41D680CB0014E7 +S32500014C207D0831D680AB002454A528347C082800418000183860FFFF83E100007FE803A6AE +S32500014C40382100284E800020808B004080AB00147CAC29D67C842A143862893D9181000875 +S32500014C60908100209081000C4BFFECF98061002083E100007FE803A6382100284E800020CC +S32500014C80808D001C7C0C20004180001080AD00187C050000408200A87C0A0378812D001478 +S32500014CA0914100207C0A6000418200507D635B78912100084BFFFD3181A1002C818100309D +S32500014CC0816100247C691B787C030000408000183860FFFF83E100007FE803A6382100286D +S32500014CE04E80002080C1002039460001914100207C0A60004082FFB8914D001C912D0018BD +S32500014D0038E9FFFE80CB00147CE731D680AB00447CE53A14386289509181000890E1002069 +S32500014D2090E1000C4BFFEC3D8061002083E100007FE803A6382100284E800020814D001CAE +S32500014D40812D00184BFFFF5C7FE802A697E1FFC87C6A1B788903000F7108001040820050E4 +S32500014D60808A0020810A00107C044000418000187C03037883E100007FE803A638210038CF +S32500014D804E80002080EA0020810100447CE74214808A00107C07200040810014810A0010E8 +S32500014DA0812A00207D09405091010044810100409101001C7C0B03789161003080E10044FC +S32500014DC07C0B38004080010C7D43537880CA00209141003C80EA000080E700187CC63BD601 +S32500014DE090C100084BFFFDE97C6A1B787C030000408000183860FFFF83E100007FE803A649 +S32500014E00382100384E8000208061003C80630000914100084BFFF9A18181003C7C03000091 +S32500014E20408200183860FFFF83E100007FE803A6382100384E80002080CC002080EC000030 +S32500014E4080E700187FE63BD67FFF39D67D5F3050810300087DA8521480C1004480810030A0 +S32500014E607D64305080EC000080E700187CEA38507C0B380040810010810C00008108001833 +S32500014E807D6A40508061001C91A100089161002C9161000C480080CD8161002C8141003CA0 +S32500014EA080E1001C7CA75A1490A1001C808A00207CE45A1490EA002080E100307D675A141B +S32500014EC09161003080E100447C0B38004180FEFC7D635B7883E100007FE803A638210038D2 +S32500014EE04E8000207FE802A697E1FFC08883000F7084001040820020386289634BFFEA6558 +S32500014F003860FFFF83E100007FE803A6382100404E8000209061004480C1004890C10008E2 +S32500014F2048000EA981210044900900207D234B78388100209081000838E0002090E1000CC2 +S32500014F404BFFFE092C030020408201103862897B388100209081000838C1002038C6000828 +S32500014F6090C1000C4BFFE9FD806100443863000438A1002090A1000838E0000890E1000C0A +S32500014F8048008549812100447C030000418200084BFFFF9C3869000C38C1002038C6000813 +S32500014FA090C10008390000039101000C4800851D812100447C030000418200084BFFFF70E4 +S32500014FC088A1002B70A500084182002838628995388100209081000838C1002038C60008A5 +S32500014FE090C1000C4BFFE97D812100444BFFFF4088A1002B98A9000F88E1003B54E7402E3D +S325000150008901003A7CE7437890E900148881003F5484402E88A1003E7C842B785484801E0C +S3250001502088A1003D54A5402E88C1003C7CA533787C842B7890890010900900189009001C19 +S32500015040900900203860000183E100007FE803A6382100404E8000207C03000041800018A4 +S325000150607C03037883E100007FE803A6382100404E8000203860FFFF83E100007FE803A62A +S32500015080382100404E8000207FE802A697E1FFD07C6D1B7880E1003890E3000C38C002009E +S325000150A090C3001038800001908300147C0903787DA36B7891A100349121001C91210008B5 +S325000150C04BFFF6F581A100347C6A1B787C03000040820020386289AD4BFFE8893860FFFF43 +S325000150E083E100007FE803A6382100304E80002081030008890801FE2C08005540820380D4 +S3250001510080C3000888C601FF2C0600AA408203709003000C8123000880C1003C7C06000094 +S3250001512041820010890900152C0800F8408201E8812A000888A900002C0500E9418200381E +S3250001514088C900002C0600EB4082001088E900022C07009041820020386289CD4BFFE805C8 +S325000151603860FFFF83E100007FE803A6382100304E8000207D234B789121002C4800031DFE +S325000151808141002C81210034888A000C5484402E88CA000B7C8433789089001088EA000D30 +S325000151A090E900148089001080A900147C8429D69089001888CA000F54C6402E890A000E4B +S325000151C07CC6437890C9001C888A00109089002088CA001254C6402E890A00117CC643786E +S325000151E090C9002488EA001454E7402E888A00137CE7237890E90028810900287C08000002 +S325000152004082003088EA002354E7402E88AA00227CE72B7854E7801E88CA002154C6402E29 +S32500015220890A00207CC643787CE7337890E90028888A00159089002C88CA001754C6402EAB +S32500015240890A00167CC6437890C900308089001C9089003C8089002080A900307C8429D61D +S3250001526080A9003C7C8522149089004080C9002454C6283480E900107CC63A143946FFFFC9 +S3250001528080C900107D4A33D680C900407CC6521490C900448089002880A900447C85205001 +S325000152A080A900147C842BD6388400029089003480C900342C060FF74080005038E0000CB5 +S325000152C090E9003839000002910900489129004C9009006090090068900900649009006CF7 +S325000152E038A0001098A9005B81090024550828349109005C7C03037883E100007FE803A658 +S32500015300382100304E80002038A0001090A900384BFFFFB4808A0008392401BE3980FFFFD4 +S325000153207C0B03782C0B000440800064890900042C0800544082000C3920003F4BFFFD74C6 +S3250001534088A900042C0500014182001088C900042C0600044082011889090000710800801B +S32500015360418200A88889000B5484402E88E9000A7C843B785484801E890900095508402E4F +S3250001538088A900087D082B787C8C43782C0B0004408200183900FFFF7C0C40004082000C00 +S325000153A0900A000C4BFFFD8C80C1001C7CC6621490CD000C7DA36B78900100084BFFF3F91D +S325000153C07C6A1B787C0300004182001480E3000888E701FE2C070055418200183860FFFF25 +S325000153E083E100007FE803A6382100304E80002080A3000888A501FF2C0500AA4082FFE0E7 +S32500015400900A000C4BFFFD2C88E9000B54E7402E88A9000A7CE72B7854E7801E88C90009D3 +S3250001542054C6402E890900087CC643787CE733787C0760404080002C88C9000B54C6402E45 +S325000154408889000A7CC6237854C6801E88A9000954A5402E88E900087CA53B787CCC2B78BC +S32500015460396B0001392900104BFFFEBC88E900042C0700064082FFEC4BFFFEE0386289C4A0 +S325000154804BFFE4E13860FFFF83E100007FE803A6382100304E8000207FE802A697E1FFE807 +S325000154A07C691B78386289E1888900009081000888C9000190C1000C9121001C890900022E +S325000154C0910100104BFFE49D38628A0180A1001C38A5000390A100084BFFE4898121001C68 +S325000154E038628A138909000C5508402E88A9000B7D082B78910100084BFFE46938628A212B +S3250001550080E1001C88E7000D90E100084BFFE4558121001C38628A308889000F5484402E17 +S3250001552088C9000E7C843378908100084BFFE43538628A3C8101001C8908001091010008A5 +S325000155404BFFE4218121001C38628A4788A9001254A5402E88E900117CA53B7890A1000893 +S325000155604BFFE4018121001C38628A55888900145484402E88C900137C843378908100082B +S325000155804BFFE3E138628A628101001C89080015910100084BFFE3CD8121001C38628A7640 +S325000155A088A9001754A5402E88E900167CA53B7890A100084BFFE3AD8121001C38628A8362 +S325000155C0888900195484402E88C900187C843378908100084BFFE38D8121001C38628A90FB +S325000155E08909001B5508402E88A9001A7D082B78910100084BFFE36D8121001C38628A9C07 +S3250001560088E9001F54E7402E88A9001E7CE72B7854E7801E88C9001D54C6402E8909001C78 +S325000156207CC643787CE7337890E100084BFFE3358121001C38628AA988A9002354A5402E37 +S32500015640890900227CA5437854A5801E888900215484402E88C900207C8433787CA52378CC +S3250001566090A100084BFFE2FD38628AB98101001C89080024910100084BFFE2E938628AC6F8 +S3250001568080A1001C88A5002590A100084BFFE2D538628ADA80E1001C88E7002690E10008B1 +S325000156A04BFFE2C18121001C38628AEC8889002A5484402E88E900297C843B785484801EE4 +S325000156C0890900285508402E88A900277D082B787C844378908100084BFFE28938628AFC15 +S325000156E080E1001C38E7002B90E100084BFFE27583E100007FE803A6382100184E800020EF +S325000157007FE802A697E1FFE8816100207C691B7888C900007CC607742C06002F4082001851 +S325000157203929000188C900007CC607742C06002F4182FFF088A900007CA507747C05000090 +S32500015740418200C088C900007CC607742C060020418200B07C0A037888A900007CA507741E +S325000157607C0500004182001488C900007CC607742C06002F4082001C980B00007D234B7877 +S3250001578083E100007FE803A6382100184E80002088E900007CE707742C0700204082001CAF +S325000157A0980B00007D234B7883E100007FE803A6382100184E8000202C0A001C40820020D5 +S325000157C038628B0E4BFFE19D7C03037883E100007FE803A6382100184E8000207D655B7845 +S325000157E0396B00017D264B783929000188C600007CC6077498C50000394A00014BFFFF5CA3 +S325000158007C03037883E100007FE803A6382100184E8000207FE802A697E1FFD080E1003CC1 +S325000158207C641B7838C0000380A4004C8104005090A7000080A400549107000490A7000824 +S3250001584038C6FFFF3884000C38E7000C7C0600004181FFD88061003838A1001490A10008F8 +S325000158604BFFFEA1906100387C030000418200508061003C38A1001490A100084BFFF66991 +S325000158802C03FFFF418200247C030000418200084BFFFFC47C03037883E100007FE803A628 +S325000158A0382100304E8000203860FFFF83E100007FE803A6382100304E8000203860000150 +S325000158C083E100007FE803A6382100304E8000207FE802A697E1FFA880E1006090E100086E +S325000158E038C1003490C1000C4BFFFF2D814100602C03FFFF418204207C030000418203F82E +S325000159002C030001418203B43860002090010008480036097C6B1B7838A0002038610034BF +S3250001592090A1003090A10008916100289161000C4800040581410028810100307C03400002 +S3250001594041820020806282CC4BFFE0193860FFFF83E100007FE803A6382100584E80002041 +S3250001596088CA000054C6402E888A00017CC6237854C6801E88AA000254A5402E88EA00038E +S325000159807CA53B787CC62B782C0606EB4182004C38628B7C890A00005508402E88CA0001C3 +S325000159A07D0833785508801E88EA000254E7402E888A00037CE723787D083B7891010008B3 +S325000159C04BFFDFA13860FFFF83E100007FE803A6382100584E80002088EA001454E7402E1E +S325000159E088AA00157CE72B7854E7801E890A00165508402E892A00177D084B787CE74378D6 +S32500015A0074E7E0003C8020007C0720004182027488CA001454C6402E88EA00157CC63B7822 +S32500015A2054C6801E890A00165508402E88AA00177D082B787CC6437890C1002C890A0004B1 +S32500015A405508402E88CA00057D0833785508801E88EA000654E7402E888A00077CE72378BA +S32500015A607D083B7838628BA791010030910100084BFFDEF13861003480A1003090A100084F +S32500015A808101002C9101000C480002AD81610030814100287C03580041820020806282CCD6 +S32500015AA04BFFDEC13860FFFF83E100007FE803A6382100584E80002080E1002C7C8B3A146B +S32500015AC038840FFF3FE0FFFF63FFF0007FE420399081002C88AA000854A5402E890A000950 +S32500015AE07CA5437854A5801E888A000A5484402E88CA000B7C8433787CA5237838628BAB36 +S32500015B0090A1003090A100084BFFDE593861003480E1003090E1000880A1002C90A1000C02 +S32500015B204800021581410028810100307C03400041820020806282CC4BFFDE293860FFFFAA +S32500015B4083E100007FE803A6382100584E80002038628BAF88CA000C54C6402E888A000D52 +S32500015B607CC6237854C6801E88AA000E54A5402E88EA000F7CA53B787CC62B7890C100084F +S32500015B80888A00145484402E88EA00157C843B785484801E890A00165508402E88AA001725 +S32500015BA07D082B787C8443789081000C4BFFDDB581410028890A00145508402E88CA00153F +S32500015BC07D0833785508801E892A00165529402E888A00177D2923787D084B787508E000CF +S32500015BE03CA020007C0828004182004888EA001454E7402E890A00157CE7437854E7801E1D +S32500015C00888A00165484402E88CA00177C8433787CE723787CE803A64E8000217C03037807 +S32500015C2083E100007FE803A6382100584E80002088EA001454E7402E888A00157CE72378F6 +S32500015C4054E7801E88AA001654A5402E890A00177CA543787CE72B783FE0FFFF63FFFFFFAC +S32500015C607FE738397CE803A64E8000217C03037883E100007FE803A6382100584E8000203D +S32500015C8088CA001454C6402E890A00157CC6437854C6801E888A00165484402E88EA0017BB +S32500015CA07C843B787CC623783FE0FFFF63FFFFFF7FE630394BFFFD8438628B4A38A10034BB +S32500015CC038A5000490A1000838E1003438E7000C90E1000C888100439081001080810048F8 +S32500015CE09081001480E1004490E100184BFFDC754BFFFC1838628B3C914100084BFFDC658B +S32500015D003860FFFF83E100007FE803A6382100584E80002038628B27914100084BFFDC4542 +S32500015D203860FFFF83E100007FE803A6382100584E8000207FE802A697E1FFD88161003043 +S32500015D409061002C80E1003490E100247C0A03787C0A580040800044392020007C8A5850EB +S32500015D607C044800408000087D2A58508061002C808100249141001C7C845214908100089E +S32500015D809121000C4BFFEFC58141001C906100207C030000418100187D43537883E1000009 +S32500015DA07FE803A6382100284E80002038628BC44BFFDBB1816100308081002080C1001C0E +S32500015DC07D4622144BFFFF8C7FE802A697E1FFE0812100287C6B1B783943000488E9000053 +S32500015DE07CE707747C070000418200147CCB505038C6FFFC2C060008418001047CAB5050BD +S32500015E0038A5FFFC2C050008408000247D455378394A000138C0002098C500007CAB505039 +S32500015E2038A5FFFC2C0500084180FFE4394B000C88A900007CA507747C05000041820014F1 +S32500015E407C8B50503884FFF42C0400034180005C7C8B50503884FFF42C0400034080002428 +S32500015E607D445378394A000138A0002098A400007C8B50503884FFF42C0400034180FFE4AA +S32500015E8038628BC638AB000490A10008390B000C9101000C4BFFDACD83E100007FE803A69D +S32500015EA0382100204E800020888900007C8407742C0400614180001488A900007CA50774B5 +S32500015EC02C05007A4081001C888900007C840774988A000039290001394A00014BFFFF5401 +S32500015EE0890900007D0807743908FFE0990A000039290001394A00014BFFFF3888C900008D +S32500015F007CC607742C06002E4082000C392900014BFFFEEC88A900007CA507742C05006199 +S32500015F204180001488C900007CC607742C06007A4081001C88A900007CA5077498AA0000DF +S32500015F4039290001394A00014BFFFE94888900007C8407743884FFE0988A000039290001CB +S32500015F60394A00014BFFFE787FE802A697E1FFC8900100287C0A03782C0A00044080006074 +S32500015F8091410030554818387C8A4214548428347C88205038A2D7D07C642A149061003443 +S32500015FA09001000838E0011890E1000C48006D818061003080C1003490C100084BFFA6CDC1 +S32500015FC0816100347C0300004181002880810030394400012C0A00044180FFA880610028E1 +S32500015FE083E100007FE803A6382100384E8000207C0A03785547183838C282807CE7321415 +S3250001600080E700007C0700004182FFC4914100245543183838E282807C633A1480630000FF +S32500016020916100084800743D81610034814100247C0300004182000C394A00014BFFFFB897 +S3250001604080810030908B00EC7D635B785548183838E282807D083A14810800047D0803A6B7 +S325000160604E8000217C030000418200084BFFFF603860400038A0000190A100089001000CB0 +S325000160809001001048003E5580E10034906700F43860400038C0000190C100089001000C36 +S325000160A09001001048003E3581410034906A00F839000001910A00F03880000180C10030A6 +S325000160C07C843030810100287D0423789081002838629A1180CA00EC90C100089141000CA8 +S325000160E0808A001C90810010808A0020908100144BFFD87181410034808A00247C040000CC +S325000161004182002C38629A31808A00247484E0003D2020007C04480041820098810A0024CF +S32500016120910100084BFFD83D8141003480CA00287C0600004182001438629A3E80AA0028DA +S3250001614090A100084BFFD81D38629A4B4BFFD8157C0A03782C0A00064080003038629A4D5C +S325000161609141002C80C100347CAA321488A5002C90A100084BFFD7ED80A1002C39450001CD +S325000161802C0A00064180FFD838629A554BFFD7D54BFFAEB181410034806A0020810A00D0A1 +S325000161A0910100089141000C48004AC94BFFFE20810A00243FE0FFFF63FFFFFF7FE8403991 +S325000161C04BFFFF607FE802A697E1FFE82C03000440800080546618387CA3321454A528346A +S325000161E07CA6285038C2D7D07CA5321480A500F07C0500004182005C546718387D033A1468 +S32500016200550828347D0740503922D7D07D484A1480AA00F02C0500014082002438C0000259 +S3250001622090CA00F07D43537891410014810A00BC7D0803A64E800021814100147D435378D7 +S3250001624083E100007FE803A6382100184E8000207C03037883E100007FE803A63821001885 +S325000162604E8000207FE802A697E1FFE84BFFFF597C030000408200187C03037883E1000062 +S325000162807FE803A6382100184E8000203863002C83E100007FE803A6382100184E800020EE +S325000162A07FE802A697E1FFD84BFFFF1D906100207C030000408200187C03037883E100004B +S325000162C07FE803A6382100284E80002080828014808400009081001880610020806300F49D +S325000162E048003C357C6A1B787C0300004082005080A2801480A5000080E100187CA72850E5 +S3250001630054A4083C7CA5221454A518385484482C7CA5205038C000327CA5339680C1003434 +S325000163207C0530404180FFB47C03037883E100007FE803A6382100284E800020810300048C +S32500016340812300007D094050806100309141001C808A000090810008910100249101000C06 +S3250001636048006C018061001C48003B118061002483E100007FE803A6382100284E800020E8 +S325000163807FE802A697E1FFE87C691B7880610020806300009121001C3889002C9081000858 +S325000163A038E0000690E1000C48007121812100207C03000041820028806900003902836826 +S325000163C09101000838A0000690A1000C480070FD812100207C030000408200288061001C24 +S325000163E0806300F49121000848003BA93860000183E100007FE803A6382100184E8000206D +S3250001640080E100247C0700004182000C7D234B7848003A697C03037883E100007FE803A6E2 +S32500016420382100184E8000207FE802A697E1FFD89061002C4BFFFD917C030000408200184A +S325000164407C03037883E100007FE803A6382100284E80002090610024806300F848003BC520 +S325000164602C0340004081002838629A5780A1002C90A100084BFFD4ED7C03037883E1000043 +S325000164807FE803A6382100284E800020386005F0480039919061002080630004810100302D +S325000164A09101000880C1003490C1000C48006AB580610020806300043863000680E10024F4 +S325000164C038E7002C90E1000838A0000690A1000C48006A91812100208081003480C900044F +S325000164E07C8622149089000480610024806300F89121000848003A9D4BFF9CA98121002432 +S325000165009061001C7D234B78810900CC7D0803A64E8000218061001C4BFF9CA53860000170 +S3250001652083E100007FE803A6382100284E8000207FE802A697E1FFF880A300EC54A7183899 +S325000165407CA53A1454A5103A3902A3387CA5421480A5000080C5000060C6003090C50000E0 +S325000165604BFF9C1983E100007FE803A6382100084E8000207FE802A697E1FFD89061002CD7 +S325000165808081002C808400EC548518387C842A145484103A38C2A3387D24321481090020E8 +S325000165A0550818389121002080C900187D08321491010018A1080000710880004082001CFF +S325000165C08061002C806300F84800394D7C691B787C0300004082001483E100007FE803A6BD +S325000165E0382100284E80002080A3000480C300007CA6285080610018806300049121001C73 +S32500016600810900009101000890A1002490A1000C4800695180C1001881010024B106000203 +S325000166204BFF9B5981210018A0A9000070A5200060A5DC00B0A900004BFF9B41808100205C +S32500016640808400003D00000061088000B104000C4BFF9B298121002080C9002038C6000110 +S3250001666038E000047FE63BD67FFF39D67CDF305090C900208061001C480038018121002060 +S325000166804BFFFF1C7FE802A697E1FFC8808100409081001C80C400EC54C718387CC63A14A7 +S325000166A054C6103A3902A3387CC6421490C1002880A60000A0A5001090A100304BFF9ABDCB +S325000166C0808100288084000081010030B10400104BFF9AA98181001C8141002881010030C8 +S325000166E0710800154182002438629BD880AC00EC90A1000880A1003090A1000C4BFFD265B1 +S325000167008181001C8141002881010030710800094082000480AA001454A5183880CA000C93 +S325000167207D65321491610024A0AB00007CA92B7870A58000408201247128003F408200C427 +S3250001674071250C002C050C00408200B8A0AB00023865FFFC90610034480036C98161002482 +S325000167607C0300004182004C9061002080630004808B000490810008808100349081000C12 +S32500016780480067E1812100208081003480C900047C862214908900048061001C9121000812 +S325000167A0390000019101000C4BFFFBD981610024B00B0002A08B0000708420006084900066 +S325000167C0B08B00004BFF99B58181001C8141002880AA001438A5000138C000207FE533D636 +S325000167E07FFF31D67CBF285090AA001480EA001454E71838810A000C7D6742144BFFFF28C1 +S32500016800712700044182001080CC010438C6000190CC01047124000241820010810C011049 +S3250001682039080001910C01107126003F4182001438629BF44BFFD12D816100244BFFFF7480 +S32500016840712808004082FF6C38629BF64BFFD115816100244BFFFF5C80E1003070E7000273 +S32500016860418200107D8363784BFFFD0D8141002880A1003070A50010418200183860000636 +S3250001688080EA000890E100089001000C4BFFD2D183E100007FE803A6382100384E80002089 +S325000168A07FE802A697E1FFE03DA0000061AD90007C6A1B78906100248103000C7C0800004E +S325000168C04082001C386000204BFFD4213DA0000061AD900081410024906A000C80CA00101B +S325000168E07C060000408200243C6000006063BE0090010008480026253DA0000061AD900065 +S3250001690081410024906A001080AA001074A5E0003CC020007C0530004182015C808A001046 +S325000169207C8C23787C0B03782C0B0020408000445567183880AA000C7CE72A14B0070002B4 +S325000169405564183880EA000C7C843A14918400045568183880CA000C7D083214B1A80000C3 +S32500016960398C05F0396B00012C0B00204180FFC45564183880EA000C7C843A14A0C4FFF8AE +S3250001698060C62000B0C4FFF8900A001480CA00187C06000040820014386000044BFFD34DD1 +S325000169A081410024906A0018808A001C7C04000040820018386017C0900100084800255D86 +S325000169C081410024906A001C810A001C7508E0003D2020007C0848004182008880EA001C96 +S325000169E07CEC3B787C0B03782C0B00044080004855651838810A00187CA5421491850004F2 +S32500016A005564183880EA00187C843A14B004000255661838808A00187CC6221439005C00A6 +S32500016A20B1060000398C05F0396B00012C0B00044180FFC05567183880AA00187CE72A148F +S32500016A40A087FFF860842000B087FFF8900A002083E100007FE803A6382100204E8000204A +S32500016A6080EA001C3FE0FFFF63FFFFFF7FE738394BFFFF70808A00103FE0FFFF63FFFFFFE5 +S32500016A807FE420394BFFFE9C7FE802A697E1FFD080A3000454A5083C3985FFFE80A2801424 +S32500016AA08145002038A000037CAB6030A0CA09527CC75B78B0EA09527D6658F8A0AA0950B1 +S32500016AC07CA63038B0CA09507D6558F8A08A09547C852838B0AA095438E000307CEB603042 +S32500016AE07D6458F8A10A09627D042038B08A09627D6858F8A0EA09607CE84038B10A0960A2 +S32500016B00A0CA09647CC75B78B0EA0964906100348063000838E1002890E1000838810024D3 +S32500016B209081000C4BFF9BDD8061003480630004808100289081000880E1002490E1000C2F +S32500016B404BFFD0D180A1003480A5000454A5103A38C283A87CA53214806500009061001C04 +S32500016B609001000838A000A490A1000C480061C13D8020008161001C8141003438800018B1 +S32500016B80988B000438E0001898EB000538A005F0B0AB0006810A000C7508E0007C08600009 +S32500016BA04182018480CA000CB0CB000080AA001874A5E0007C05600041820158810A0018DA +S32500016BC0B10B00027C03037880AA000890A100089001000C4BFFCF8981A100388181003CB4 +S32500016BE08161001C38E0FFFF90EB00303CA0DEBB60A520E390AB0034900B0038900B003C39 +S32500016C00900B00403C80000060848888B08B004438E0000FB0EB004638A005EEB0AB004AEB +S32500016C2039000040B10B004C38C005F0B0CB004E388005F0B08B0050B00B00787C0A0378AA +S32500016C402C0A00064080003C7CEA621488E7000154E7402E7D0C5214890800007CE7437862 +S32500016C60392000027D0A4BD65508083C7D085850B0E80076394A00022C0A00064180FFCCE1 +S32500016C803900080AB10D00083CC0000060C6D555B0CD000E900D00043CC0108860C6000C9E +S32500016CA090CD00004BFF94D580E100383C8000006084FFFFB08700104BFF94C180A1003847 +S32500016CC038E0001AB0E500144BFF94B1810280148168002081010034810800043908FFFFA1 +S32500016CE0392010007D284030808B0ABC7C87437890EB0ABC80CB0AB87CC44378908B0AB85A +S32500016D0083E100007FE803A6382100304E800020810A00183FE0FFFF63FFFFFF7FE8403981 +S32500016D204BFFFEA080CA000C3FE0FFFF63FFFFFF7FE630394BFFFE747FE802A697E1FF7011 +S32500016D40906100943861008A9001000838A0000690A1000C48005FD93861008A8101009417 +S32500016D603908002C9101000838C0000690C1000C48006759812100947C030000408200909B +S32500016D8038629BF84BFFCBDD3861001C39029C11910100084800666538629C1E38C1004EE8 +S32500016DA090C10008390000329101000C38C1001C90C100104BFFCA357C0300004080001854 +S32500016DC03860FFFF83E100007FE803A6382100904E800020806100943863002C3881004E88 +S32500016DE0908100084BFFC209812100947C0300004080001C88C9002C2C0600FF418200104C +S32500016E0038629C304BFFCB5D4BFFFF8080E9001C54E7103A388280887CE7221480E70000FE +S32500016E2080C2801480C600207D663A1480C9001C54C6103A38E280307CC63A1480C6000075 +S32500016E4038C6001090C9002080E900EC54E818387CE7421454E7103A3882A3387C672214D7 +S32500016E60916100809163000080E9001C90E3000480E9001C54E7103A390283107CE7421418 +S32500016E8080E7000090E30008906100844BFFFA158061008480A1008090A1000881010094E6 +S32500016EA03908002C9101000C4BFFFBE1812100943D00FFC261086E38910900B83CE0FFC228 +S32500016EC060E7663090E900BC3CC0FFC260C6667490C900CC3CA0FFC260A5678490A900D021 +S32500016EE07C03037883E100007FE803A6382100904E8000203821FFD08161003C82810038C5 +S32500016F00824100407C751B7838A000017CAE583038A000017CA570303A65FFFF38A0000148 +S32500016F207CAA903038A000207CAF96307D4558307E2E2850388000207DAF20507C0D000010 +S32500016F40418000787C0A03787EB06C307C0C03787C0B03787C0B7800408000247E665830D2 +S32500016F607E0630387CC660307D4A33787D8C8A147D6B72147C0B78004180FFE47C0B037820 +S32500016F807C0B90004080001C7DC758307D4738307D4A3B78396B00017C0B90004180FFEC1D +S32500016FA07E88A3783A940004914800007DAF68507C0D00004080FF90382100304E800020CB +S32500016FC07FE802A697E1FEF89061010C3861010C480006498141011880C101248081011C93 +S32500016FE07CC430514181001483E100007FE803A6382101084E8000208081012880E1012083 +S325000170007C8720503884FFFF908100FC80A100FC7C0500004080001483E100007FE803A649 +S32500017020382101084E80002080E1010C81A7000C808101107C87683090E1011091A100F402 +S325000170407CCB6830836A000C8081011C7C87D83090E1011C936100F8808101247C87D8307D +S3250001706090E1012480E1010C83470008832A000880E10114934100E87CE7D1D680A1010C14 +S3250001708080A500047CE72A14808101107C842E707CE43A1454E7103A80C1010C80C60000A8 +S325000170A07EE7321492E100CC80C10120932100EC7CC6C9D6808A00047CC622148101011CD7 +S325000170C07D082E707CC8321454C6103A80AA00007EC62A1492C100D080A1011070AC001F5C +S325000170E03900FFFF7D086430910100B0918100DC7D0C5A147108001F38A000207D08285085 +S3250001710038C0FFFF7CC840303FE0FFFF63FFFFFF7FE84039910100AC810100AC7C08000071 +S325000171204082000C38A0FFFF90A100AC80C1011C70C6001F90C100E4810101107C8B4214EF +S325000171403884FFFF7C842E7080A101107CA52E707C8520509081010080A1010C7C0A280080 +S325000171604082005880E10120808101147C0720004080046080C100FC7CC6D1D654C6103A05 +S325000171807EF73214808100FC7C84C9D65484103A7ED6221480EA000854E7083C7F27C85036 +S325000171A0932100EC8081010C808400085484083C7F44D050934100E8808101007C040000D1 +S325000171C04082001480C100AC810100B07D06303890C100B080C1012C70C6000F90C1012C96 +S325000171E07E9B68507D85A63080C100E47E6530507C1803787C1300004080000C3B000001B1 +S325000172007E7300507C1003787C15037892A100A480A100FC7C152800418100F892E100CC6D +S325000172207EECBB7892E100C492C100D07ECFB37881A100B091A100C07C0E03787C1800007B +S32500017240408200107DE57B7839EF000481C500007C12037882210100922100A87C110000F9 +S325000172604180008C7C1200004182027C7E0783783A10000481670000814C00008101012CB9 +S325000172802C08000841810194418201702C08000441810110418200F87C080000418200D83B +S325000172A02C080001418200B02C080002418200942C0800034182006C398C0004918100C48D +S325000172C039A0FFFF91A100C02C1100014082000C81A100AC91A100C03A52FFFF3A31FFFF1F +S325000172E0922100A87C1100004080FF7C5727103A7ED63A145747103A7EF73A143AB500015F +S3250001730092A100A480A100FC7C1528004081FF1083E100007FE803A6382101084E80002025 +S325000173203FE0FFFF63FFFFFF7FE65A787CC652787CC668387D46327890CC00004BFFFF7C16 +S325000173407D6750387CE768387D473A7890EC00004BFFFF683FE0FFFF63FFFFFF7FE85A785E +S325000173607D0853787D0868387D484278910C00004BFFFF483FE0FFFF63FFFFFF7FE56A7821 +S325000173807D45283890AC00004BFFFF307D6553787CA568387D452A7890AC00004BFFFF1C9C +S325000173A02C080005418200482C080006418200302C080007418200084BFFFF003FE0FFFFE9 +S325000173C063FFFFFF7FE852787D6843787D0868387D484278910C00004BFFFEE07D67683888 +S325000173E07D473A7890EC00004BFFFED07D446A78908C00004BFFFEC43FE0FFFF63FFFFFF34 +S325000174007FE75A787CE750387CE768387D473A7890EC00004BFFFEA42C08000C41810070EF +S32500017420418200582C080009418200342C08000A4182FE882C08000B418200084BFFFE7CA1 +S325000174403FE0FFFF63FFFFFF7FE75A787CE768387D473B7890EC00004BFFFE603FE0FFFFB5 +S3250001746063FFFFFF7FE65A787CC668387D46327890CC00004BFFFE447D6852787D08683863 +S325000174807D484278910C00004BFFFE302C08000D418200342C08000E4182001C2C08000FB5 +S325000174A0418200084BFFFE147D476B7890EC00004BFFFE087D6668387D46337890CC0000DE +S325000174C04BFFFDF87D6750383FE0FFFF63FFFFFF7FE73A787CE768387D473A7890EC000070 +S325000174E04BFFFDD8930100D47C180000418200AC38E000207CF338507DC738307DE57B78CB +S3250001750039EF000491E100C881C5000091C100BC926100D87DC89C307CEB437838E0000193 +S325000175207CF2A0309241001C2C1400014180FD4C7D635B7838A1002490A100089361000CE3 +S32500017540928100F0928100104BFFF9AD836100F8834100E8832100EC830100D482E100CC6F +S3250001756082C100D082A100A4828100F0826100D88241001C822100A881E100C881C100BC2A +S3250001758081A100C0818100C4390100243A080004816800004BFFFCE47DCB98307DE77B781E +S325000175A039EF000491E100C881C7000091C100BC926100D87C1300004182FF6438E0002050 +S325000175C07CF338507DC73C307D6B3B784BFFFF5080C10120810101147C0640004082FBDC75 +S325000175E080A1011C80E101107C053800418000084BFFFBC84800600181A100F4836100F8AA +S32500017600834100E8832100EC818100DC82E100CC82C100D04BFFFBA43821FFE87C691B7866 +S3250001762080E30018810300107D68385080E3001C810300147D48385080E3000480C30000B9 +S3250001764080C600207C0730004080003080E9000080E70020808900047CE43850810900109B +S325000176607CC83A1490C9001080A900047D053A14910900047D6758508109000880E90000E6 +S3250001768080E700247C08380040800030810900008108002480A900087D0540508089001415 +S325000176A07CE4421490E9001480C900087C864214908900087D485050808900047C845A14D6 +S325000176C080C9000080C600287C0430004081002480E900047CEB3A1480A9000080A50028BF +S325000176E07CE53850810900187CE7405090E9001880C900087CC65214810900008108002C3C +S325000177007C06400040810024808900087C8A221480E9000080E7002C7C87205080A9001CBA +S325000177207C8428509089001C8109001080E9000C80E700207C0838004080002C8109000CC1 +S325000177408108002080A900107D054050808900047CE4421490E9000480C900107C8642143D +S3250001776090890010808900148109000C810800247C0440004080002C8089000C8084002490 +S3250001778080C900147C86205080A900087D0522149109000880E900147CA7221490A9001465 +S325000177A080A900188089000C808400287C0520004081001080E9000C80E7002890E9001839 +S325000177C08089001C8109000C8108002C7C0440004081001080C9000C80C6002C90C9001C65 +S325000177E0382100184E8000203821FFF0814100187C691B787C070378394AFFFF7C0A000084 +S32500017800418000207D244B7839290001888400007CE72214394AFFFF7C0A00004080FFE866 +S325000178207CE33B78382100104E80002081210008390227107C0940404081000C7C0918400D +S325000178404180000C7C0303784E800020386000014E8000207FE802A697E1FFE080C3000438 +S3250001786038A0FFFF7C0628004182001480C300043CA000047C062840418000187C030378C6 +S3250001788083E100007FE803A6382100204E80002088C3000B2C0600FF4182FFE480E3000472 +S325000178A0810282F87D683A14906100249161001C916100084BFFFF79816100247C0300002D +S325000178C0408200187C03037883E100007FE803A6382100204E80002088AB000854A5402E50 +S325000178E0890B00097CA5437854A5402E888B000A7CAA23783D0000FF6108FFFF7C0A40005A +S32500017900408200087C0A03787C0A0000408000187C03037883E100007FE803A63821002050 +S325000179204E8000207C0A0000408100307D635B78914100188121001C7CAA4A1438A5FFFF21 +S3250001794090A100084BFFFEE981610024814100187C0300004182007C80CB000038A0FFFFF7 +S325000179607C062800418200288061001C91410018914100084BFFFE758141001880E100248E +S3250001798080E700007C0338004082002C80A1002880E1001C90E500008081002C9144000097 +S325000179A03860000183E100007FE803A6382100204E80002038628CD04BFFBFA97C030378AA +S325000179C083E100007FE803A6382100204E8000207C03037883E100007FE803A638210020DE +S325000179E04E8000207FE802A697E1FFB8900282F890028300900282FC386100203881003879 +S32500017A009081000838C1003490C1000C4BFF8C157C0300004080002038628CE54BFFBF4519 +S32500017A207C03037883E100007FE803A6382100484E80002081010034910282FC80A1003822 +S32500017A4090A282F880E282F83CE7000490E28300900283049002830880A282F83FE0000387 +S32500017A6063FFFFF07D3F2A149121003C3869000C390282F09101000838A0000490A1000CB9 +S32500017A8048005A497C030000408200A080E1003C88E7000B2C070002408200248061003CC4 +S32500017AA038A1004090A1000838E1004490E1000C4BFFFDA57C030000408200108101003C98 +S32500017AC03928FFF04BFFFFA4808100442C04000B408000108101003C3928FFF04BFFFF8C2F +S32500017AE080610040390280C09101000838A0000B90A1000C480059D57C0300004182001061 +S32500017B008101003C3928FFF04BFFFF6080E1004090E2830480810044908283088101003C6D +S32500017B203928FFF04BFFFF4480A283047C050000408200B438628D504BFFBE2980628300B4 +S32500017B404800356D2C0306EB4082002038628D804BFFBE113860000183E100007FE803A665 +S32500017B60382100484E80002081028300890800005508402E80C2830088C600017D083378C9 +S32500017B805508801E80E2830088E7000254E7402E80828300888400037CE723787D083B781A +S32500017BA02C0806EB4082002038628DAA4BFFBDB53860000183E100007FE803A6382100487C +S32500017BC04E8000209002830038628DD64BFFBD957C03037883E100007FE803A638210048F3 +S32500017BE04E80002038628D6280E2830490E1000880A2830890A1000C81028304910100100F +S32500017C004BFFBD614BFFFF38806283044E8000207FE802A697E1FFF080C283007C06000060 +S32500017C204182006880628300480034857C0300004082004480A2830088A5000054A5402EEE +S32500017C4081028300890800017CA5437854A5801E80828300888400025484402E80C2830074 +S32500017C6088C600037C8433787CA523782C0506EB408200183860000183E100007FE803A63C +S32500017C80382100104E8000207C03037883E100007FE803A6382100104E8000207FE802A6B2 +S32500017CA097E1FFB080E283007C070000408200183860FFFF83E100007FE803A638210050A1 +S32500017CC04E8000208142830088CA000054C6402E888A00017CC6237854C6801E88AA0002B3 +S32500017CE054A5402E88EA00037CA53B787CC62B782C0606EB408202387D49537888CA0014D2 +S32500017D0054C6402E888A00157CC6237854C6801E88EA001654E7402E890A00177CE743781F +S32500017D207CC63B7874C6E0003D0020007C064000418201C488A9001454A5402E88C9001574 +S32500017D407CA5337854A5801E88E9001654E7402E888900177CE723787CA53B7890A1004C12 +S32500017D60398A00208161004C88E9000454E7402E88A900057CE72B7854E7801E88C9000661 +S32500017D8054C6402E91210040890900077CC643787CEA33787C0B6000418200507D635B780E +S32500017DA0916100489181002091810008914100249141000C480051AD38628DF980C1004873 +S32500017DC090C10008808100209081000C80E1002490E100104BFFBB8D818100208161004821 +S32500017DE081410024812100407D8C521480A1004C3CE0FFC07C053840418000E439403000B6 +S32500017E0088A9000854A5402E890900097CA5437854A5801E8889000A5484402E88C9000BF0 +S32500017E207C8433787CA523787D43537891410048918100209181000890A1002490A1000C51 +S32500017E404800512138628E1980A1004890A10008810100209101000C80C1002490C1001078 +S32500017E604BFFBB0138628E398081004C908100084BFFBAF14BFF91CD4BFFBDED8101004CCF +S32500017E807508E0003CA020007C0828004182002480E1004C7CE803A64E8000213860FFFFB0 +S32500017EA083E100007FE803A6382100504E80002080E1004C3FE0FFFF63FFFFFF7FE73839AF +S32500017EC07CE803A64E8000213860FFFF83E100007FE803A6382100504E8000207C8A5A148A +S32500017EE038840FFF3FE0FFFF63FFF0007FEA20394BFFFF1088AA001454A5402E88EA0015F3 +S32500017F007CA53B7854A5801E890A00165508402E88CA00177D0833787CA543783FE0FFFFE9 +S32500017F2063FFFFFF7FE528394BFFFE349141003480C1003490C100283D0A000C9101003090 +S32500017F4080C1003090C1002C386100283881004C90810008480032057C0300004080FF0888 +S32500017F603860FFFF83E100007FE803A6382100504E8000207FE802A697E1FF907C691B78CB +S32500017F803861003491210008480054713861003438C1002490C10008390000049101000C28 +S32500017FA038C29AD590C1001048000D9D2C030002418201482C030003418200187C030378BA +S32500017FC083E100007FE803A6382100704E8000208101007C9008000039429E0080AA000492 +S32500017FE07C050000418201007C0903782C090003408000E45524103A7C8452148084001C10 +S325000180007C040000418200C4806100249121001C5528103A9141006C7D0852148108001CEA +S32500018020910100084800543D8141006C8121001C7C0300004082009480C10078810A0000C1 +S32500018040910600008101007C38A000017CA54830808800007C852B7890A800008061002825 +S3250001806038A1002090A100089001000C48004EF981010080906800007C030000408200144C +S3250001808080A1002080E100287C053800418200288061008480A1002C90A100084800535D88 +S325000180A03860000183E100007FE803A6382100704E8000207C03037883E100007FE803A687 +S325000180C0382100704E800020392900012C0900034180FF24394A003080AA00047C05000001 +S325000180E04082FF087C03037883E100007FE803A6382100704E80002039029AD79101002C21 +S325000181004BFFFED07FE802A697E1FF30906100D47C0303784BFF8E39814100D8812100D4AA +S3250001812071440004418200503C60001F6063FF6A38C29AD890C10008810900008108002489 +S325000181409101000C80A9000C90A100104BFFB7D1806100D48063000C810100DC9101000896 +S325000181604BFFA29183E100007FE803A6382100D04E800020714600084182002881090004B8 +S32500018180710800084182001C7C0303784BFFFA85814100D8812100D47C03000040820274EE +S325000181A071460002418200FC8109000471080010418200348069000C38A29ADE90A10008B2 +S325000181C080E9000080E700187CE803A64E800021806100D43863000C900100084BFFAB6169 +S325000181E0812100D4808100DC7C040000418200A480C100DC88C600007CC607747C06000094 +S32500018200418200908069000C80A100DC90A1000881090000810800187D0803A64E80002191 +S32500018220812100D43C60001F6063FF6A38829AE39081000880C9000080C6002090C1000C7E +S325000182408109000C91010010810100DC910100144BFFB6CD812100D48069000C80A9001466 +S3250001826090A1000880A9001090A1000C480017A5906100247C0303784BFF8CD58061002485 +S3250001828083E100007FE803A6382100D04E80002080E9000838E7000890E100DC4BFFFF68B6 +S325000182A0714700014182015880890000808400187C040000418200248069000C39029AECA0 +S325000182C09101000880A9000080A500187CA803A64E800021812100D480C9000470C60010D2 +S325000182E0418200103869000C900100084BFFAA5180C100DC7C06000041820084810100DCD5 +S32500018300890800007D0807747C080000418200703861002C810100DC91010008480050DDDC +S325000183203C60001F6063FF6A38C29AF690C10008810100D4810800008108001C9101000C4A +S3250001834080A100D480A5000C90A1001038A1002C90A100144BFFB5C9806100D43863000C41 +S325000183603901002C910100084BFFD56983E100007FE803A6382100D04E8000203861002C1E +S32500018380808280A080840000908100084800506D3861002C38C0002F90C1000848004F3D79 +S325000183A07C03000041820050392300017D234B7838A29AF190A1000848005041806100D438 +S325000183C03863000C3881002C9081000838C100AC90C1000C4BFFD4417C0300004181FF440C +S325000183E03860FFFF83E100007FE803A6382100D04E8000203921002C4BFFFFB43860FFFF3C +S3250001840083E100007FE803A6382100D04E8000208069000C4BFFF889814100D8812100D4FA +S325000184204BFFFD803821FFF08142801838C2E53838C608007C0A3040418000107C030378ED +S32500018440382100104E8000203923002C80E900007C0700004182001880E900003927007C2A +S3250001846080E900007C0700004082FFF038EA008090E2801891490000806900003821001080 +S325000184804E8000207FE802A697E1FFA89061005C39629E00810B00047C080000418200302C +S325000184A080E1005C2C07FFFF41820038808B00008101005C7C08200041820028396B003080 +S325000184C0810B00047C0800004082FFD87C03037883E100007FE803A6382100584E800020DB +S325000184E080AB000C7C0500004182FFD4808100607C0400004182005C814B002C7C0A0000A9 +S3250001850041820050810A0004812100607D2840394182003480A100642C05FFFF4182001410 +S3250001852080EA000C80C100647C063800408200187D43537883E100007FE803A638210058D5 +S325000185404E800020814A007C7C0A00004082FFB8808B0008708400804082002880AB00083C +S3250001856060A5008090AB000891610054808B000C7C8803A64E80002181610054906B0028DA +S325000185807C0D0378808B00287C0400004182FF30810B0028392000017D2968307D084839D9 +S325000185A04082000C39AD00014BFFFFDC3880000191A1004C7C8468307C8420F8810B00283F +S325000185C07D042038908B00287D635B78916100544BFFFE5581A1004C816100547C6A1B78C5 +S325000185E07C0300004082000C39AD00014BFFFF9891A3000C80CB000890C30004810B0014D5 +S325000186009103001480AB001090A300109163000090610044808300047084000241820078CC +S325000186207DA36B7838C29AFF90C10008812B00187D2803A64E80002181A1004C816100549F +S3250001864081410044906A00087C0300004082001880EA00043FE0FFFF63FFFFFD7FE73839F2 +S3250001866090EA000480AA000470A50018408200287DA36B7838A29B0490A1000880EB0018F8 +S325000186807CE803A64E80002181A1004C816100548141004480CA000470C60001418200B82D +S325000186A09001004838A0000190A1005080CA000080C600187C0600004182005C7DA36B7834 +S325000186C038A29B0990A10008810A0000810800187D0803A64E8000217C0300004182001839 +S325000186E080830000908100487C04000041820008900100508061004C38C29B0D90C10008C3 +S3250001870081010054810800187D0803A64E80002181410044386A000C8081004890810008A8 +S32500018720810100509101000C4BFFC96181A1004C81610054814100447C030000408000DC89 +S3250001874080EA00043FE0FFFF63FFFFEE7FE7383990EA000480AA000470A50010418200705D +S3250001876080CA00043FE0FFFF63FFFFEF7FE6303990CA0004398282B8918280A0808C0000D7 +S325000187807C04000041820048386A000C80AC000090A1000838A1002090A1000C4BFFD0796B +S325000187A081A1004C81610054814100447C0300004181005880C280A039860004918280A017 +S325000187C0808C00007C0400004082FFC080EA0004810100607D0738394182FDCC808100644F +S325000187E02C04FFFF4182001080C100647C0668004082FDB47D43537883E100007FE803A670 +S32500018800382100584E800020808A000460840010908A00044BFFFFB838629B124BFFB1450A +S3250001882081A1004C81610054814100444BFFFF287FE802A697E1FEF8480004C14BFF7AA127 +S32500018840480006654BFFB2394800254D4BFFAD1D4BFF7B314BFF8F614BFFA2E938629B23FE +S325000188604BFFB101900280B44BFF791D38629B2C4BFFB0F14BFF7D657C6A1B7838629B32F7 +S32500018880914100E8914100084BFFB0D97C0B037839429E0080CA00047C060000418200209C +S325000188A080AA00002C050002408203FC394A003080CA00047C0600004082FFE87C0B0000E0 +S325000188C0418203C480CB000470C60010418203B880A100E870A50002408200147C03037804 +S325000188E04BFFF3317C03000040820388900100F0900100F438629B4C4BFFA3B990610024F5 +S32500018900900100FC80A100247C0500004182005838E0000290E100F47C0A03782C0A000329 +S32500018920408002BC80610024914100F855451838388283487CA5221480A5000090A10008BF +S3250001894048004B21814100F87C030000408202845547183838C283487CE7321480E7000416 +S3250001896090E100F4808100F07C0400004082001080C100F42C060002408201D4980100B0FF +S325000189803860FFFF9001000838C0FFFF90C1000C4BFFFAF5900100FC39029E0091010020FD +S325000189A081010020810800047C080000418200D480E100208147002C914101047C0A000094 +S325000189C0418200A4808100FC7C0400004082001838C0000190C100FC38629B884BFFAF8551 +S325000189E08141010480EA000470E700044182002438629B9680CA000080C6002490C1000821 +S32500018A00810A000C9101000C4BFFAF5981410104810A0004710800014182002438629B9D3F +S32500018A2080EA000080E7001C90E10008808A000C9081000C4BFFAF2D81410104808A00049B +S32500018A4070840018408200E4810A000471080002408200D8814A007C914101047C0A000015 +S32500018A604082FF6480A1002038A5003090A1002081010020810800047C0800004082FF3483 +S32500018A80808100FC7C0400004182000C38629BAB4BFFAED138629BAD38E1002890E100083E +S32500018AA0388000509081000C38E100B090E100104BFFAD397C0300004180006838610028A7 +S32500018AC0388100EC9081000838C100FC90C1000C392101009121001038A1007890A10014CC +S32500018AE04BFFF4957C03000041820038806100EC80A100FC90A10008810101009101000CDE +S32500018B004BFFF9857C03000041820018810100FC9101000838C1007890C1000C4BFFF5E91E +S32500018B20980100B04BFFFF7038629BA480EA000080E7002090E10008808A000C9081000CB6 +S32500018B404BFFAE21814101044BFFFF0C3900001F910100FC808100F42C04000140820018F3 +S32500018B6080C100FC3FE0FFFF63FFFFFB7FE6303990C100FC808100E8708400024182001863 +S32500018B8080C100FC3FE0FFFF63FFFFF77FE6303990C100FC3860FFFF80C100FC90C10008D5 +S32500018BA038A0FFFF90A1000C4BFFF8DD7C0300004182FDCC81030004812100FC7D2840382E +S32500018BC0910100089001000C4BFFF53D4BFFFDB0394A00012C0A00034180FD4C8061002418 +S32500018BE038C100EC90C10008390100FC9101000C38A1010090A1001038E1007890E100142B +S32500018C004BFFF3757C0300004082001838629B5580810024908100084BFFAD494BFFFD48AB +S32500018C20806100EC808100FC90810008810101009101000C4BFFF8517C030000408200183D +S32500018C4038629B6E81010024910100084BFFAD154BFFFD14810100FC9101000838E100781A +S32500018C6090E1000C4BFFF4A1906100F04BFFFCF838629B404BFFACED7C0303784BFFF021C5 +S32500018C804BFFFC6C7C0303784BFFEF81906100187C03037880E1001890E100084BFFA0A1E7 +S32500018CA04BFFFC3091410020806A000038A0001090A1000838C0FFFF90C1000C4BFFF7C9DD +S32500018CC0814100207C6B1B787C0300004182FBE0808B000470840010408200084BFFFBD022 +S32500018CE091610104386B000C900100084BFFA051816101044BFFFBC87FE802A697E1FFE891 +S32500018D008062801490010008388000249081000C4800401D80C2801439004E209106000487 +S32500018D204BFF74C180E28014906700103CA0FA2090A7002083E100007FE803A6382100187E +S32500018D404E8000207FE802A697E1FFE87C691B78900100147C0900004182001488E90000C6 +S32500018D607CE707747C070000408200188061001483E100007FE803A6382100184E800020E9 +S32500018D8080C10014810100247C064000418000188061001483E100007FE803A6382100185C +S32500018DA04E80002088A900007CA507747C05000041820028806100289121001C88C900005D +S32500018DC07CC6077490C1000848003F418121001C7C030000408200C0890900007D0807745D +S32500018DE07C080000408200188061001483E100007FE803A6382100184E8000208081001431 +S32500018E005484103A80E100207C843A149124000088C900007CC607747C060000418200282A +S32500018E20806100289121001C88E900007CE7077490E1000848003ED58121001C7C030000F4 +S32500018E404182001480E1001438E7000190E100144BFFFF04888900007C8407742C04005CB5 +S32500018E604082001488A900017CA507742C05000A4182000C392900014BFFFF987D264B7893 +S32500018E803929000138E0002098E60000392900014BFFFF807D284B7839290001980800001B +S32500018EA04BFFFF047FE802A697E1FFE03862813838C2C7D090C10008390010009101000CD4 +S32500018EC04800125538C2271074C6E0003CE020007C0638004182003038A227103862813844 +S32500018EE090A100083CE000407CE5385090E1000C4800124983E100007FE803A638210020E0 +S32500018F004E80002038A227103FE0FFFF63FFFFFF7FE528394BFFFFC87FE802A697E1FFD89A +S32500018F209061002C810100307C0800004181000C38A0000490A100304BFF72699061002096 +S32500018F4038628138900100088081002C9081000C80E1003090E10010480013499061002409 +S32500018F60806100204BFF725980E100247C0700004082000C38629BC74BFFAA718061002498 +S32500018F809001000880A1002C90A1000C48003DA183E100007FE803A6382100284E8000209E +S32500018FA07FE802A697E1FFE8388300033FE0FFFF63FFFFFC7FE42039386400089061001C97 +S32500018FC038800004908100084BFFFF5138A300043D00CAFE6108BEEF9103000038650004EC +S32500018FE08121001C9125000083E100007FE803A6382100184E8000207FE802A697E1FFE0BD +S325000190007C0300004182005038C3FFF890C1001880C600003CE0CAFE60E7BEEF7C06380084 +S325000190204182000C38629BCE4BFFA9C14BFF7175812100189061001C38628138912100089F +S32500019040810900049101000C480010F18061001C4BFF716D83E100007FE803A63821002082 +S325000190604E8000204E8000207FE802A697E1FFF0808283809081000C390000FF9904001090 +S325000190804BFF70F98081000C88C4000060C6000198C400004BFF70E58081000C38C000177F +S325000190A098C400144BFF70D583E100007FE803A6382100104E8000203821FFF080E2838032 +S325000190C09807001489070000710800FE99070000382100104E8000207FE802A697E1FFC092 +S325000190E038C2837090C100308106000C7C0800004182001483E100007FE803A63821004000 +S325000191004E800020386283C84BFFA8598141003038800001908A000C3CA0FA2060A50860F6 +S3250001912090A1003890AA00103CA0FA2060A53C8090AA00144BFFFF858121003080A9002C7B +S325000191407C050000408200283860008438A0000290A100084BFFFDC5812100309069002C6B +S3250001916080E9002C38E7008490E9002880E900307C0700004082001C3860008438E00002DF +S3250001918090E100084BFFFD95812100309069003080E900187C0700004082004838600001D1 +S325000191A04BFFAB49812100309069001880C9003074C6E0003CE020007C06380041820248F1 +S325000191C080A9003080E9001890A7000480E90018B007000280C9001838A02000B0A600008A +S325000191E080C9001C7C06000040820048386000024BFFAAF9812100309069001C80A9002CB4 +S3250001920074A5E0003CC020007C053000418201E48089002C80C9001C9086000480C9001CC0 +S32500019220B006000280A9001C38802800B085000080A280148145002080EA0AB860E70030D6 +S3250001924090EA0AB8A0EA0AC260E70030B0EA0AC280EA0ABC60E7003090EA0ABC81490014D3 +S3250001926080E9001874E7E0003CC020007C0730004182016C80A90018B0AA00008089001C6C +S325000192807484E0003D0020007C0440004182013C80E9001CB0EA000238C0001898CA00043B +S325000192A038800018988A000538E00084B0EA0006900A0008900A000CA08A0000B08A0010B8 +S325000192C0B00A0012900A0018A0AA0002B0AA0020900A001CB00A00224BFF6EA181E2801461 +S325000192E03DC0000161CE86A08181003838C0000198CC000C980C0000980C00047DCD7378F0 +S3250001930080EF00087CE773D6388000027CE723D63A07FFFD7C0B03787C0B80004080008087 +S32500019320394000037C0A000041800068808F000838AA00027C842E305567083C38E700067D +S325000193407C843BD67D247051408000087D2900507C096800408000307D2D4B78996C00087E +S3250001936088AC00003FE0FFFF63FFFFF97FE52839390000037D0A40505508083C7CA54378A6 +S3250001938098AC0000394AFFFF7C0A00004080FFA0396B00017C0B80004180FF883860002070 +S325000193A03CC0FFC260C6951890C10008390283709101000C480018BD83E100007FE803A660 +S325000193C0382100404E80002080E9001C3FE0FFFF63FFFFFF7FE738394BFFFEBC80A90018E1 +S325000193E03FE0FFFF63FFFFFF7FE528394BFFFE8C8089002C3FE0FFFF63FFFFFF7FE42039E2 +S325000194004BFFFE1480A900303FE0FFFF63FFFFFF7FE528394BFFFDB07FE802A697E1FFE0F1 +S3250001942080A1002890A10018812500109121001488E9001090E1001C4BFF6D418141001837 +S325000194408121001C80810014992400107126001441820034386283D6912100084BFFA50522 +S325000194608121001880C900207C060000418200089009002083E100007FE803A638210020CF +S325000194804E8000207127000241820018808A00202C0400034082000C38A0000190AA002004 +S325000194A0712600014182FFD0810A00202C0800044082FFC438800001908A002083E10000BC +S325000194C07FE803A6382100204E800020806300202C0300024080000C386000014E80002087 +S325000194E07C0303784E8000207FE802A697E1FFE89061001C80C1001C900600249001001446 +S325000195008061001C4BFFFFC97C0300004082007480A280B47C0500004182FFE880E100148A +S3250001952038E7000190E100142C0700644081002C80C1001C39000001910600208081001C90 +S3250001954038C0000190C4002483E100007FE803A6382100184E800020386000014BFF952127 +S325000195607C03037880E1001C90E100084BFFFEAD8061001C4BFFFF597C0300004182FF948B +S3250001958083E100007FE803A6382100184E8000207FE802A697E1FFD88181003439628370CF +S325000195A02C0C0080408100183860FFFF83E100007FE803A6382100284E8000203940000120 +S325000195C080CB002C3FE0FFFF63FFFFFE7FE5183998A60000706500014182001480EB002C5A +S325000195E07C66467098C70001394A000191610024806B002C914100207C63521480A1003033 +S3250001960090A100089181000C48003959812100243900000391090020808900183D000000F8 +S325000196206108B000B104000080A9002C74A5E0003CC020007C053000418200C88089002C7A +S3250001964080C9001C9086000480C9001C80A10034808100207CA52214B0A600028109001C54 +S325000196603CE0000060E7B800B0E800004BFFF9FD810100248108001038E0008198E8000C8C +S325000196804BFF6AF9806100244BFFFE614BFFFA2D81610024814B0020808B001CA0840000BA +S325000196A0708880004082000C70850007418200183860FFFF83E100007FE803A638210028FB +S325000196C04E8000202C0A0001418200183860FFFF83E100007FE803A6382100284E8000200A +S325000196E0808B00247C0400004082FFC88061003483E100007FE803A6382100284E80002033 +S325000197008089002C3FE0FFFF63FFFFFF7FE420394BFFFF307FE802A697E1FFD081E1003C67 +S325000197203DC020007C6C1B78394283702C0F0080408100183860FFFF83E100007FE803A67E +S32500019740382100304E800020808A002C6068000199040000706800014182002480AA0028DD +S325000197603FE0FFFF63FFFFFE7FE4603998850000808A00287D8846709904000138A00004E6 +S3250001978090AA002080EA003074E7E0007C077000418201D080CA0030810A001890C8000493 +S325000197A0810A00183CE0000060E7B000B0E800007C0D0378816A001CB00B0008718500018F +S325000197C04182002C810A00287508E0007C0870004182017C80EA002890EB00043880000284 +S325000197E0B08B0002396B000839A0040080AA002C74A5E0007C0570004182013C9141002C5E +S32500019800808A002C908B000438CF0001B0CB000261A8A800B10B000091A100207C0D00001F +S325000198204182001480AA001C3C80000060848000B08500004BFFF83580A1002C80A50010B6 +S32500019840388000819885000C4BFF69318061002C4BFFFC994BFFF86581C1003C8141002CBC +S3250001986081AA0020816A001CA16B000080A100207C0500004182001080EA001CA0E70008D9 +S325000198807D6B3B78818A0018A18C00027C0C7000408100087DCC7378716800074082000C31 +S325000198A071658000418200183860FFFF83E100007FE803A6382100304E8000202C0D0001B5 +S325000198C0418200183860FFFF83E100007FE803A6382100304E800020808A0018A0840000DF +S325000198E070848000418200183860FFFF83E100007FE803A6382100304E80002080EA002403 +S325000199007C0700004082FFA48061003880EA003090E10008918100189181000C4800364521 +S325000199208061001883E100007FE803A6382100304E8000209141002C808A002C3FE0FFFFEB +S3250001994063FFFFFF7FE420394BFFFEBC80EA00283FE0FFFF63FFFFFF7FE738394BFFFE7C36 +S3250001996080CA00303FE0FFFF63FFFFFF7FE630394BFFFE287FE802A697E1FFD88161003437 +S325000199809061002C80A1003890A100247C0A03787C0A58004080004C392020007CEA585083 +S325000199A07C074800408000087D2A58508061002C80E100249141001C7CE7521490E10008FC +S325000199C09121000C80C100307CC803A64E8000218141001C906100207C030000418100182D +S325000199E07D43537883E100007FE803A6382100284E800020386285574BFF9F698161003414 +S32500019A0080E100208081001C7D443A144BFFFF847FE802A697E1FFD09061003490010008B1 +S32500019A2080A100387CA803A64E8000217C030000408000183860FFFF83E100007FE803A6A9 +S32500019A40382100304E80002038600020900100084BFFF4C97C6B1B783880002080610034C9 +S32500019A6080E1003C90E100089081002C9081000C91610020916100104BFFFEFD8141002034 +S32500019A8080C1002C7C03300041820020806282CC4BFF9ED13860FFFF83E100007FE803A6CD +S32500019AA0382100304E800020888A00005484402E88EA00017C843B785484801E890A00029F +S32500019AC05508402E88AA00037D082B787C8443782C0406EB4182004C3862855988CA0000A2 +S32500019AE054C6402E888A00017CC6237854C6801E88AA000254A5402E88EA00037CA53B784B +S32500019B007CC62B7890C100084BFF9E593860FFFF83E100007FE803A6382100304E8000203E +S32500019B2088AA001454A5402E890A00157CA5437854A5801E88CA001654C6402E88EA0017DD +S32500019B407CC63B787CA5337874A5E0003CE020007C05380041820294888A00145484402E8A +S32500019B6088AA00157C842B785484801E88CA001654C6402E890A00177CC643787C8433783C +S32500019B809081002888CA000454C6402E888A00057CC6237854C6801E88AA000654A5402E5C +S32500019BA088EA00077CA53B787CC62B783862858490C1002C90C100084BFF9DA9806100344E +S32500019BC08081003C908100088101002C9101000C80A1002890A100104BFFFD9D8161002C60 +S32500019BE0814100207C03580041820020806282CC4BFF9D713860FFFF83E100007FE803A630 +S32500019C00382100304E800020808100287CCB221438C60FFF3FE0FFFF63FFF0007FEB3039D2 +S32500019C2088EA000854E7402E88AA00097CE72B7854E7801E88CA000A54C6402E890A000B5E +S32500019C407CC643787CE733783862858790E1002C90E10008916100289161000C4BFF9D0532 +S32500019C60806100348101003C9101000880E1002C90E1000C80810028908100104BFFFCF9DD +S32500019C808141002080E1002C7C03380041820020806282CC4BFF9CCD3860FFFF83E10000D7 +S32500019CA07FE803A6382100304E8000203862859388AA000C54A5402E890A000D7CA5437843 +S32500019CC054A5801E888A000E5484402E88CA000F7C8433787CA5237890A10008890A0014D8 +S32500019CE05508402E88CA00157D0833785508801E88EA001654E7402E888A00177CE72378A8 +S32500019D007D083B789101000C4BFF9C594BFF73354BFF9F554BFF648D8141002088EA001454 +S32500019D2054E7402E88AA00157CE72B7854E7801E890A00165508402E892A00177D084B78C7 +S32500019D407CE7437874E7E0003C8020007C0720004182004888CA001454C6402E88EA0015A4 +S32500019D607CC63B7854C6801E890A00165508402E88AA00177D082B787CC643787CC803A660 +S32500019D804E8000217C03037883E100007FE803A6382100304E80002088CA001454C6402EFA +S32500019DA0890A00157CC6437854C6801E888A00165484402E88EA00177C843B787CC62378B8 +S32500019DC03FE0FFFF63FFFFFF7FE630397CC803A64E8000217C03037883E100007FE803A6E7 +S32500019DE0382100304E800020888A00145484402E88CA00157C8433785484801E88EA001669 +S32500019E0054E7402E88AA00177CE72B787C843B783FE0FFFF63FFFFFF7FE420394BFFFD64AC +S32500019E207FE802A697E1FFE89061001C386300184BFFF17138A3001890A3000C8103000C7F +S32500019E40910300049103000080C3000C80E1001C7CC63A1490C30008900300103D00CAFE70 +S32500019E606108BEE09103001483E100007FE803A6382100184E8000207FE802A697E1FFF0E3 +S32500019E807C691B787C0300004182004090610014810300143CE0CAFE60E7BEE07C0838009F +S32500019EA041820010386281284BFF9B4181210014900900143CE0DEAD60E7BABE90E900100D +S32500019EC07D234B784BFFF13583E100007FE803A6382100104E8000207FE802A697E1FFE86F +S32500019EE0386000144BFFF0BD900300049003000080A1002490A30008808100289083000CC6 +S32500019F009003001083E100007FE803A6382100184E8000207FE802A697E1FFE89061001C49 +S32500019F204BFF62818141001C90610014812A0000912100107C0900004182003C80A90010E0 +S32500019F4090AA00009009001080E90004810900007CE83850810A00107CE7405090EA00101C +S32500019F6080CA00107C0600004080000C3862812E4BFF9A79806100144BFF624580610010B5 +S32500019F8083E100007FE803A6382100184E8000207FE802A697E1FFE89061001C4BFF6205BB +S32500019FA08141001C812100207C6B1B789009001080EA00007C0700004082005C912A000011 +S32500019FC0912A000480A9000480C900007CA6285080CA00107CA62A1490AA00107D635B78F4 +S32500019FE04BFF61DD8121001C80A900087C050000418200148069000C80E900087CE803A618 +S3250001A0004E80002183E100007FE803A6382100184E80002080AA0004912500104BFFFFA496 +S3250001A020806300104E8000207FE802A697E1FFE09061002438C0FFFF90C100144BFF616552 +S3250001A04081610014814100247C6C1B78916100147C0B000040800010812A00007C09000015 +S3250001A060408200207D8363784BFF61558061001483E100007FE803A6382100204E8000204C +S3250001A08080E9000080C900047C073040408000208169000038EB000190E90000896B0000B5 +S3250001A0A0808A00103884FFFF908A001080E9000080C900047C0730404180FF9480E9001025 +S3250001A0C090EA0000900900104BFFFF847FE802A697E1FFE89061001C386000014BFFFD45E9 +S3250001A0E07C681B788083000438C4000190C3000480C1002098C400008061001C9101000833 +S3250001A1004BFFFE9183E100007FE803A6382100184E800020812100089123000480C1000CDD +S3250001A12038A000087CC62BD654C618387CC64A1490C300084E8000207FE802A697E1FFD844 +S3250001A14081810034816100307C6E1B787C0C00004181001483E100007FE803A638210028E0 +S3250001A1604E8000208143000480AA00047C0558404181001080CA00007C0600004082011C5E +S3250001A18080AE00047C0A28404081001C38CAFFFC80C6000080EAFFF87CC63A147C065800AD +S3250001A1A0418200A47D0B6214812A00047C0848004082001080EA00007C07000040820060D7 +S3250001A1C0808E00087C0A20404180002C3862829880AE000090A100089161000C9181001054 +S3250001A1E04BFF978183E100007FE803A6382100284E80002081AA0004916A00047DAB6B78DA +S3250001A20081AA0000918A0000394A00087DAC6B787C0D00004182FFD04BFFFFA880AA000420 +S3250001A2207D0C2850910A000480EA00007CA7621490AA000083E100007FE803A63821002845 +S3250001A2404E80002080CAFFF87C866214908AFFF87C8B621480AA00047C0428004082FF88A3 +S3250001A26080AA0000808AFFF87CA42A1490AAFFF880EA00007C0700004182FF6C394A000877 +S3250001A280388AFFFC80CA000490C40000810A0000910AFFF84BFFFFDC394A00084BFFFECC77 +S3250001A2A07FE802A697E1FFD881E1003081C1003481A100387C701B7881630004810B0000E4 +S3250001A2C07C08000041820028818B00047C0F00004182005C7C0C784040810028396B000879 +S3250001A2E0810B00007C0800004082FFE07C03037883E100007FE803A6382100284E800020C9 +S3250001A3007D0F7214812B00007D2C4A147C084840408100187C03037883E100007FE803A61E +S3250001A320382100284E8000207DEC7B787C0D0000408100147C8D62143884FFFF7C846B96B3 +S3250001A3407D8469D680CB000080EB00047CC63A147CCC30507C067040408000084BFFFF80E1 +S3250001A36081AB00047CEC721490EB00047D0C72147D0D4050812B00007D084850910B0000AB +S3250001A38080CB00007C06000040820028396B000838CBFFFC810B00049106000080AB000003 +S3250001A3A090ABFFF87C050000418200084BFFFFE07C0D6000418200207E03837891A100086D +S3250001A3C0918100207CCD605090C1000C4BFFFD6D818100207D83637883E100007FE803A6C9 +S3250001A3E0382100284E8000207FE802A697E1FFB8388002809081003438A001E090A1003808 +S3250001A40038C0000390C1003C90010040386100344800042180E100407C07000040820014A8 +S3250001A42083E100007FE803A6382100484E800020388000039082825480A1004090A2824812 +S3250001A44038E000A090E28250900282589002825C3900028091028260388001E0908282645C +S3250001A46038A2824838C2824880E600108106001490E5002091050024810600188086001C4C +S3250001A480910500289085002C7C0303783880FFFF9081000838C0FFFF90C1000C3900FFFF63 +S3250001A4A09101001048000641386000FF900100089001000C900100104800062D3862824817 +S3250001A4C038A1000890A1002C80E1002C900700008081002C9004000438C2824890C1001029 +S3250001A4E0390100143882824880A4001080C4001490A8000090C8000480C4001880E4001C87 +S3250001A50090C8000890E8000C900100244BFFCAB538E0000890E280A8900280AC8102800C4B +S3250001A52089080105910280B083E100007FE803A6382100484E8000207FE802A697E1FFB879 +S3250001A5407C691B7880C282487C0600004082001483E100007FE803A6382100484E80002015 +S3250001A5609061004C2C030008418202302C030009418201D02C03000A4182010480A282609A +S3250001A580810280B07CA8285080C280A87C062800418000103860000A4BFFFFA18121004C06 +S3250001A5A07129007F408100C8A8E280087C093800408000BC5528183880C2800C7D28321421 +S3250001A5C03881003C38A280A880C5000080E5000490C4000090E400043862824838E100087E +S3250001A5E090E100348081003488C900047CC607748101003C7CC8321490C4000080C1003457 +S3250001A600810100409106000480A2801090A10010390100149101003080A10030A8E90000F1 +S3250001A62090E50000808100309004000480C1003091210044A90900089106000880A10030C4 +S3250001A64088E2800A7CE7077490E5000C38E0000C90E100244BFFC96D808100448884000511 +S3250001A66080C1003C7C843214908280A883E100007FE803A6382100484E8000203900000892 +S3250001A680910280A88882800A7C840774810280AC7C882214908280AC88C2800A7CC6077431 +S3250001A6A080E282647CC63850810280AC7C0830004081000C80A2825C90A280AC386282488F +S3250001A6C038E1000890E10034808100349004000080C10034810280AC9106000438A2824881 +S3250001A6E090A1001038E1001490E10030808100309004000080C10030810280AC91060004C4 +S3250001A70080A1003080E2826090E500088081003088C2800A7CC6077454C6083C80E280AC72 +S3250001A7207CC63A1490C4000C900100244BFFC89583E100007FE803A6382100484E800020C3 +S3250001A740808280A83884FFF880A280B07C842BD67084000738C000087C84305080E280B0B5 +S3250001A7607C8439D680A280A87C852214908280A880E280A8808282607C0720004180FEF048 +S3250001A7803860000A4BFFFDB583E100007FE803A6382100484E80002080C280B038C6000899 +S3250001A7A0810280A87C0830004180FEC480A280B080E280A87CA5385090A280A83860002019 +S3250001A7C04BFFFD7980A280B080E280A87CA5385090A280A883E100007FE803A638210048BE +S3250001A7E04E8000207FE802A697E1FFF07C691B788101001838C8FFFF90C100187C080000F1 +S3250001A8004081002038E9000190E10014886900007C6307744BFFFD25812100144BFFFFD41F +S3250001A82083E100007FE803A6382100104E8000207FE802A697E1FFD87C6A1B7880E2801479 +S3250001A84080E7002090E10024388001E09083000438E0028090E3000038A0000390A3000802 +S3250001A860806300009141002C80EA00047C6339D638A0001090A100084BFFE6A13DA0017D47 +S3250001A88061AD78408181002C81610024906C000C80AC000080CC00047CA531D690AC0010BF +S3250001A8A0808B00303FE0FFFF63FFFFBF7FE42039908B0030810C000C7508E0003D2020009F +S3250001A8C07C0848004182020C80EC000C90EB085080EC0000810C00047CE741D654E7183887 +S3250001A8E038E7007F390000807CE743D654E7881C60E70D6690EB084038A000037C050000C6 +S3250001A900418200142C050002418201A02C050003418201687C0A03782C0A00104080001C3F +S3250001A9205546083C7CC65A14B1460E00394A00012C0A00104180FFEC808C0004548458289E +S3250001A9403FE0100063FF00217FE42378908B084880CC000054C6502A3FE0010063FF00E48F +S3250001A9607FE6337890CB084480C28014818600087D4C6BD67CCC53D67C0668004081000806 +S3250001A980394A00019141001C2C0A00104180000C38E0001090E1001CB00B097638E01FFF06 +S3250001A9A0B0EB0972A0AB097270A5FDFFB0AB097238A01FFFB0AB0970810B0ABC61085001F7 +S3250001A9C0910B0ABC810B0AB861085001910B0AB8A10B0AC27108AFFEB10B0AC24BFF579D44 +S3250001A9E080C100243D0055CC6108AA33910603804BFF578981410024808A02803FE0FFFF74 +S3250001AA0063FFFFE07FE4203938A281108101001C7CA5421488A500007C842B78908A028046 +S3250001AA204BFF575980A100243CE0AA3360E755CC90E503804BFF57458102825091028250D7 +S3250001AA4080A1002438E0000798E508584BFF572D8101002480A8084060A5000190A8084044 +S3250001AA60386000014BFF5D3183E100007FE803A6382100284E8000207C0A03782C0A010043 +S3250001AA804080FEB85544402E554520367C842B787C8453785545083C7CA55A14B0850E0024 +S3250001AAA0394A00014BFFFFD87C0A03782C0A00104080FE885546402E554720367CC63B786D +S3250001AAC07CC65378B0CB0E00394A00014BFFFFE080EC000C3FE0FFFF63FFFFFF7FE7383960 +S3250001AAE04BFFFDEC80810008548B273E80E1000C54EA273E80A1001054A9273E3FE0FFFF0F +S3250001AB0063FFFFFF7FE81A78710800FF5508083C80C2801480C600207D0832145565402E8D +S3250001AB20554620367CA533787CA54B78B0A80E00386000014E8000203821FFE83D4055CCA2 +S3250001AB40614AAA333D20FA209149030038C000C3B0C902009149032038E000C1B0E902204B +S3250001AB603CA0AA3360A555CC90A903209149034038C00082B0C90240382100184E800020E2 +S3250001AB807FE802A697E1FFE081410028906100247145000341820010386287E44BFF8E4D93 +S3250001ABA081410028812100243CC07C1060C643A690C900003C807C08608402A69089000405 +S3250001ABC03CE07C1260E743A690E900087544E0003CC020007C043000418200887D485378D3 +S3250001ABE07D0A437855053E7F4182000C2C05007F408200303FE003FF63FFFFFC7FE5503919 +S3250001AC003FE0480063FF00037FE52B7890A9000C83E100007FE803A6382100204E8000203A +S3250001AC205544843E64843C009089000C7144FFFF64846000908900103C807C08608403A678 +S3250001AC40908900143CE04E8060E7002190E9001883E100007FE803A6382100204E80002002 +S3250001AC603FE0FFFF63FFFFFF7FE850394BFFFF747FE802A697E1FFE89061001C8082E53011 +S3250001AC802C0400304180000C386287EC4BFF8D5D80C1001C7D4602148082E53039040001B4 +S3250001ACA09102E5305484203638A2E03038A502007D642A145547103A38C2E0307CE7321436 +S3250001ACC080E7000090EB000881010020910B000080E1002490EB00045544103A38E2E03034 +S3250001ACE07C843A149164000080A28014816500202C0A00104180003038EAFFF070E7001F90 +S3250001AD0038C000017CC7383080CB09487CC73B7890EB094883E100007FE803A63821001845 +S3250001AD204E8000202C0A000841800038714800075508083C38E0001F7D08385039200001E8 +S3250001AD407D284030808B00147C884378910B001483E100007FE803A6382100184E80002076 +S3250001AD605544083C608400013900001F7C84405038A000017CA4203080AB00147CA42378DF +S3250001AD80908B001483E100007FE803A6382100184E8000207FE802A697E1FFE03D4055CCA6 +S3250001ADA0614AAA33810280148128002080A900043FE0FFFF63FFFFF37FE5283990A9000484 +S3250001ADC09009001438E0FFFF90E900189009094038E0FFFF90E90944900909483CE000E173 +S3250001ADE060E79F0090E9094080A9094060A5008090A909409149030039000001B109020058 +S3250001AE0080C9001464C6004090C900149149030C4BFF53693860FFFF4BFF53E17C090378F4 +S3250001AE202C0930004080002C7D234B789121001C3D00FFC2610803A8910100084BFFFD4551 +S3250001AE4080E1001C392701002C0930004180FFDC83E100007FE803A6382100204E80002031 +S3250001AE607FE802A697E1FFD87C6A1B787C0903782C09002840800064386288065526103A86 +S3250001AE8038A284307CC62A1480C6000090C10008810A00009101000C912100245528103A38 +S3250001AEA038E284307D083A1481080004910100109141002080AA000490A100144BFF8AA5DD +S3250001AEC0810100243928000280C10020394600082C0900284180FFA483E100007FE803A645 +S3250001AEE0382100284E8000207FE802A697E1FFE87C6A1B789061001C80C3000054C9C23E88 +S3250001AF002C090005418200802C0900094182005C38C0000190C280B47C0900004180003C4F +S3250001AF202C09001540800034386288345524103A390295A07C8442148084000090810008D0 +S3250001AF404BFF8A218061001C4BFFFF19386288424BFF8A11480000003862881B912100080E +S3250001AF604BFF8A014BFFFFE07D435378900100084BFF7B8983E100007FE803A63821001875 +S3250001AF804E8000204800001583E100007FE803A6382100184E8000207FE802A697E1FFD82E +S3250001AFA09061002C810280148168002091610018890B001C550CF0BE7D8A0E7091810024C9 +S3250001AFC071880001418200E02C0A00044082002438800001B08B09304BFF51A1818100241E +S3250001AFE081610018A0AB093054A5AAFE39450010914100205545103A3882E0307CA52214A6 +S3250001B000814500007C0A00004082000848000000808A000C7C0400004182002038A0001F5B +S3250001B0207CAC285038C000017CC5283080CB00107CC52B7890AB00108061002C80EA0004D2 +S3250001B04090E100089141001C80EA00007CE803A64E80002180C1001C814600087C0A00006A +S3250001B060418200084BFFFFD480C100202C060010418000248101001880A1002038A5FFF0B2 +S3250001B080392000017D2528308088094C7C852B7890A8094C83E100007FE803A638210028D2 +S3250001B0A04E800020394A00084BFFFF487C691B7888A3000054A5402E888300017CA52378AD +S3250001B0C054A5402E88C300027CA5337854A5402E88C300037CA533783CE0FEEF60E70F1EEB +S3250001B0E07C05380040820030886300205463402E88C900217C6333785463402E88E90022BA +S3250001B1007C633B785463402E890900237C6343784E8000207C0303784E8000207FE802A63D +S3250001B12097E1FFE8386001F43CA0FFC260A5B21C90A100089001000C4BFF65959062822CF2 +S3250001B14038628BE04BFF881D83E100007FE803A6382100184E8000207FE802A697E1FFB8E3 +S3250001B160818100507C6B1B788083000480A300007C8520502C040040408000183860FFFF83 +S3250001B18083E100007FE803A6382100484E80002081430000890A00005508402E88EA000110 +S3250001B1A07D083B785508402E892A00027D084B785508402E888A00037D0823783CA0FEEFBF +S3250001B1C060A50F1E7C082800418200183860FFFF83E100007FE803A6382100484E80002016 +S3250001B1E0900282EC88CA001054C6402E88AA00117CC62B7854C6402E88EA00127CC63B7835 +S3250001B20054C6402E88EA00137CCE3B78888A00145484402E890A00157C8443785484402E08 +S3250001B22088AA00167C842B785484402E88AA00177C842B789081003888EA001854E7402E6E +S3250001B24088CA00197CE7337854E7402E890A001A7CE7437854E7402E890A001B7CE7437885 +S3250001B26090E1003488AA000C54A5402E888A000D7CA5237854A5402E88CA000E7CA533780F +S3250001B28054A5402E88CA000F7CA5337890A10030890A00045508402E88EA00057D083B78A1 +S3250001B2A05508402E892A00067D084B785508402E888A00077D0D237888CA000854C6402ED0 +S3250001B2C088AA00097CC62B7854C6402E88EA000A7CC63B7854C6402E88EA000B7CC63B7854 +S3250001B2E090C1002880C3000038C6002090C3000081430000888A00005484402E890A00016A +S3250001B3007C8443785484402E88AA00027C842B785484402E88AA00037C842B782C0406EB0F +S3250001B3204182002038628BE24BFF86393860FFFF83E100007FE803A6382100484E8000207A +S3250001B34088CA001454C6402E88AA00157CC62B7854C6402E88EA00167CC63B7854C6402E3F +S3250001B36088EA00177CC63B7890CC000081030000390800209103000080EC000074E7E000CC +S3250001B3803D0020007C0740004182015480CC00007CC3337890C100449161004C916100086B +S3250001B3A091A1000C91C1001038A28BFF90A1001448000141816100287C6A1B787C030000B1 +S3250001B3C04082002038628C044BFF85993860FFFF83E100007FE803A6382100484E80002059 +S3250001B3E07C0B00004182005C38EA0FFF3FE0FFFF63FFF0007FE3383980E1004C90E1000808 +S3250001B4009161000C810100389101001038C28C1790C10014480000DD7C6A1B787C030000AC +S3250001B4204082002038628C1C4BFF85393860FFFF83E100007FE803A6382100484E80002040 +S3250001B44080C100307C060000418200407D435378914100408081004C8084000090810008E8 +S3250001B460808100309081000C48001AF981610030808100407D445A1480E1004C80C70000A6 +S3250001B4807C865A1490870000808282EC80C100347C0430004182003038628C2F810282EC50 +S3250001B4A09101000880C1003490C1000C4BFF84B53860FFFF83E100007FE803A638210048EB +S3250001B4C04E80002080C100447C66505083E100007FE803A6382100484E80002080CC000021 +S3250001B4E03FE0FFFF63FFFFFF7FE630394BFFFEA47FE802A697E1FFE07C6A1B7838628C67A2 +S3250001B5008081003490810008914100249141000C80A1002C90A100104BFF84498101002CAF +S3250001B5207C080000408200188061002483E100007FE803A6382100204E80002080610028BD +S3250001B5403902EE00910100089001000C480003257C030000408000187C03037883E100005F +S3250001B5607FE803A6382100204E8000208061002838E20A0890E10008388000089081000CC2 +S3250001B580480002F1814100287C030000408000187C03037883E100007FE803A63821002041 +S3250001B5A04E80002080CA000480EA00007CC7305080A1002C7C0628004080002038628C7F9F +S3250001B5C04BFF83A17C03037883E100007FE803A6382100204E800020386001F43D00FFC296 +S3250001B5E06108B21C910100089001000C4BFF60E1814100289062822C80610024810A000031 +S3250001B60091010008810A00008121002C7D084A149101000C38E2EE0090E1001039020A08D9 +S3250001B6209101001480A1003090A100184800006D906100248062822C4BFF61759002822C09 +S3250001B64038628C8E4BFF831D814100247C0A00004082002038628C904BFF83097C03037871 +S3250001B66083E100007FE803A6382100204E80002080E100288081002C80C700007C862214B3 +S3250001B680908700007D43537883E100007FE803A6382100204E8000203821FFE8820100243F +S3250001B6A081610030812100207C6C1B78556536BE2C050013418201942C05001F4182018C4A +S3250001B6C02C05003B418201842C05003F4182017C81A282EC7C0980404080016088890000F7 +S3250001B6E07C8F2378392900017C0E03787C8A26707C0A00004082002C7D6A5B78914C00008E +S3250001B700398C00047DAD521471EA000F39CE00012C0E00014082FFC07C0A00004182FFDC77 +S3250001B7202C0A00014082009488E9000354E7402E88C900027CE7337854E7402E89090001BB +S3250001B7407CE7437854E7402E890900007CEA4378392900047C098040408100107C030378F2 +S3250001B760382100184E800020554736BE2C070013418200202C07001F418200182C07003B0F +S3250001B780418200102C07003F418200084BFFFF703FE0FC0063FFF8017FE8503955478BFE4E +S3250001B7A070E707FE7D083B78714407FE548478207D0A23784BFFFF48280A000841810044D1 +S3250001B7C038AAFFFE54A5402E890900007CA5437854A5103A80C1002C7CA5321480A500046E +S3250001B7E0890900017CAA4378392900027C0980404081FF787C030378382100184E80002094 +S3250001B800390AFFF75508402E88C900007D0833785508103A80E100287D083A1481480004CC +S3250001B820392900017C0980404081FF407C030378382100184E80002091A282EC7D83637884 +S3250001B840382100184E8000203FE0FC0063FFF8017FE6583955658BFE70A507FE7CC62B78D4 +S3250001B860716707FE54E778207CCB3B784BFFFE647FE802A697E1FFD08083000480A30000EB +S3250001B8807C8520502C040002408000183860FFFF83E100007FE803A6382100304E800020A5 +S3250001B8A081030000890800005508402E9061003480E3000088E700017D083B7838628CB398 +S3250001B8C09101001C910100084BFF809981C100388161003480CB000038C6000290CB000080 +S3250001B8E08081001C39A4FFFE7C0D00004081002C810B0004812B00007D0940507C08680096 +S3250001B900408000183860FFFF83E100007FE803A6382100304E800020900E0000814B00005D +S3250001B92080CB00007D866A14810B00007CC86A1490CB00007C0D03787C0A60404080009011 +S3250001B9407C0B03787C0A6040418000183860FFFF83E100007FE803A6382100304E8000205E +S3250001B9605568383088CA000070C6007F7D0B33787D455378394A000188A5000070A500808E +S3250001B980418200207C0A60404180FFD83860FFFF83E100007FE803A6382100304E8000207E +S3250001B9A07DAD5A14808E000038A4000190AE00005484103A7C84721480E1003C7DA53830F0 +S3250001B9C090A400047C0A60404180FF787C03037883E100007FE803A6382100304E800020E5 +S3250001B9E03821FFF07C6B1B7880A1001C810100247D45421480C10018808100207D26221430 +S3250001BA008081001C7C0A20404080000839290001914B0004912B0000382100104E800020FE +S3250001BA203821FFF07C6B1B788101001C80C100247D4640508081001880E100207D27205039 +S3250001BA4080E1001C7C0A3840408100083929FFFF914B0004912B0000382100104E80002048 +S3250001BA603821FFD8CA2100307C6E1B78DA210020810100203FE0000F63FFFFFF7FE84039CC +S3250001BA80650C001081A10024810100205508653E710807FF38C004337D2830507C0B037861 +S3250001BAA07C0A03787C090000418000B02C090020408000847C090000408200607DAB6B783D +S3250001BAC07D8A637880A1002074A580007C050000418200207C0B0000418200287C040378D2 +S3250001BAE07D6B20503FE0FFFF63FFFFFF7FEA5278914E0000916E0004382100284E800020E6 +S3250001BB007C0703787D4A3850914E0000916E0004382100284E8000207DA54C3038C00020CA +S3250001BB207CC930507D8630307CAB33787D8A4C304BFFFF942C0900204082000C7D8B63789E +S3250001BB404BFFFF842C0900404080FF7C38C9FFE07D8B34304BFFFF707C0703787D29385030 +S3250001BB602C09000A418100207DAB48307D85483038C000207CC930507DA634307CAA33784E +S3250001BB804BFFFF44FC80881ED8828020814280244BFFFF347FE802A697E1FFE8C061002062 +S3250001BBA0D86100084BFFFEBD83E100007FE803A6382100184E8000203D20433080E1000430 +S3250001BBC074E780007C0700004182009C808100087C0400004182007C7C0603788101000852 +S3250001BBE07CC8305090C1000880C100043FE0FFFF63FFFFFF7FE6327890C1000480E1000496 +S3250001BC00912280206CE6800090C28024C8228020FC21D828C8828428FC01013280A100080C +S3250001BC20912280206CA4800090828024C8628020FC63D828FC03E0004080000CC8428428DA +S3250001BC40FC63102AFC00182AFC1C00284E8000207C04037880C100047C8620509081000411 +S3250001BC604BFFFF9C80A10004912280206CA4800090828024C8628020FC63D828C84284283B +S3250001BC80FC0300B281010008912280206D07800090E28024C8228020FC21D828FC01E00081 +S3250001BCA04080000CC8428428FC21102AFC00082A4E8000207FE802A697E1FFF038C1000415 +S3250001BCC038E10014810700008087000491060000908600044BFFFEE5FC00001883E1000047 +S3250001BCE07FE803A6382100104E8000203821FFD83DA080008241003C822100408161002C59 +S3250001BD008181003081210034814100387C0A00004082000C7C090000418200F47C0B68405B +S3250001BD20418000E07C0F03787C0E03787C096840418000147C096800408200307C0A784031 +S3250001BD40408000285525083C55480FFE7CA94378554A083C39CE00017C0968404180FFE8EC +S3250001BD607C0968004182FFD87C0F03787C0D03787C0E00004180006455E4083C55A70FFEF6 +S3250001BD807C8F3B7855AD083C7C0B4840418100147C0B4800408200287C0C50404180002001 +S3250001BDA07D9063787D8A60507C0C804040810008396BFFFF7D69585061AD00015547F87E7B +S3250001BDC05525F8007CEA2B785529F87E39CEFFFF7C0E00004080FFA47C1200004182000C9E +S3250001BDE091B2000491F200007C1100004182000C9191000491710000382100284E8000207F +S3250001BE007D6D5B787D8F63784BFFFF207D8C53964BFFFF0C7FE802A697E1FFE07C691B78E9 +S3250001BE2080E100287C07000040820010808100307C0400004182005438E1000439010028D6 +S3250001BE408088000080A800049087000090A7000438A1000C38C1003080E600008106000456 +S3250001BE6090E5000091050004912100147C06037890C100184BFFFE7983E100007FE803A64B +S3250001BE80382100204E8000207C0803789109000080C1002C80A100347CC62B9690C9000479 +S3250001BEA083E100007FE803A6382100204E8000207FE802A697E1FFE07C691B788101002818 +S3250001BEC07C0800004082001080A100307C05000041820054390100043881002880A40000D9 +S3250001BEE080C4000490A8000090C8000438C1000C38E10030810700008087000491060000E7 +S3250001BF00908600047C04037890810014912100184BFFFDDD83E100007FE803A63821002005 +S3250001BF204E8000207C0403789089000080E1002C80C100347FE733967FFF31D67CFF38503F +S3250001BF4090E9000483E100007FE803A6382100204E800020808300047C0400004082001821 +S3250001BF6080A300007C0603787CA5305090A300004E800020808300047C0503787C8428505D +S3250001BF8090830004810300003FE0FFFF63FFFFFF7FE84278910300004E8000207FE802A6D0 +S3250001BFA097E1FFD07C691B78906100348081003C7C84FE7080C100387C06200040820018F6 +S3250001BFC0810100447D08FE7080A100407C054000418200AC80E1003854E70FFE90E1002C92 +S3250001BFE07C07000041820010386100384BFFFF698121003480E1004054E70FFE90E1002809 +S3250001C0007C07000041820010386100404BFFFF498121003438E100043901003880880000EB +S3250001C02080A800049087000090A7000438A1000C38C1004080E600008106000490E50000F7 +S3250001C04091050004912100147C06037890C100184BFFFC9D80E1002C808100287C072000D7 +S3250001C0604182000C806100344BFFFEED83E100007FE803A6382100304E8000208081003C78 +S3250001C080810100447C8443D69089000480E900047CE7FE7090E9000083E100007FE803A672 +S3250001C0A0382100304E8000207FE802A697E1FFD07C6A1B78906100348101003C7D08FE705D +S3250001C0C080A100387C0540004082001880E100447CE7FE70808100407C043800418200A48F +S3250001C0E080C1003854C60FFE90C1002C7C06000041820010386100384BFFFE5D814100345B +S3250001C10080C1004054C60FFE7C06000041820010386100404BFFFE418141003438C10004C6 +S3250001C12038E10038810700008087000491060000908600043881000C38A1004080C5000040 +S3250001C14080E5000490C4000090E400047C07037890E10014914100184BFFFB9580C1002CEF +S3250001C1607C0600004182000C806100344BFFFDE983E100007FE803A6382100304E80002037 +S3250001C1808101003C80E100447FE83BD67FFF39D67D1F4050910A000480CA00047CC6FE7067 +S3250001C1A090CA000083E100007FE803A6382100304E8000203821FFF081610020814100180F +S3250001C1C02C0B0020418000387D46FE7090C300002C0B0040418000147D48FE70910300046D +S3250001C1E0382100104E800020390BFFE07D48463091030004382100104E8000207C0B00000D +S3250001C200418100189143000080E1001C90E30004382100104E8000207D465E3090C300007A +S3250001C22038E000207CEB38507D47383080A1001C7CA55C307CE72B7890E3000438210010D4 +S3250001C2404E8000203821FFF081410020816100182C0A0020418000387C05037890A3000047 +S3250001C2602C0A0040418000147C05037890A30004382100104E800020390AFFE07D68443067 +S3250001C28091030004382100104E8000207C0A0000418100189163000080E1001C90E3000460 +S3250001C2A0382100104E8000207D66543090C3000038E000207CEA38507D67383080A1001CB7 +S3250001C2C07CA554307CE72B7890E30004382100104E8000203821FFF0814100208161001CB6 +S3250001C2E02C0A0020418000387C05037890A300042C0A0040418000147C05037890A300003B +S3250001C300382100104E800020390AFFE07D68403091030000382100104E8000207C0A0000D7 +S3250001C320418100189163000480E1001890E30000382100104E8000207D66503090C3000427 +S3250001C34038E000207CEA38507D673C3080A100187CA550307CE72B7890E3000038210010A4 +S3250001C3604E80002080C1000880A100107CC6283890C300008081000C810100147C8440383E +S3250001C380908300044E8000208101000880E100107D083B789103000080C1000C80A1001448 +S3250001C3A07CC62B7890C300044E80002080A10008812100107CA54A7890A300008101000CCD +S3250001C3C080E100147D083A78910300044E8000208121000880E9000090E300008089000491 +S3250001C3E09083000480C9000438C6000190C90004808900047C0400004082001080A90000EE +S3250001C40038A5000190A900004E8000208121000880C9000090C300008109000491030004A4 +S3250001C420810900047C08000040820010808900003884FFFF90890000808900043884FFFF6E +S3250001C440908900044E800020812100087C6A1B7880A9000438A5000190A9000481090004D1 +S3250001C4607C0800004082001080890000388400019089000080890000908A000080C9000410 +S3250001C48090CA00044E800020812100087C6A1B7880E900047C0700004082001081090000DA +S3250001C4A03908FFFF91090000810900043908FFFF9109000481090000910A000080A90004DF +S3250001C4C090AA00044E8000207FE802A697E1FFD0814100388121003C90610034390100207C +S3250001C4E08083000080A300049088000090A8000480A100402C0500064181029C4182023CBE +S3250001C5002C05000341810164418201002C050001418200942C05000241820030480010D915 +S3250001C5208101003438A1002080C5000080E5000490C8000090E8000483E100007FE803A64F +S3250001C540382100304E80002088AA000090A1002C7C07037890E1002838610020390100083C +S3250001C5603881002880A4000080C4000490A8000090C8000438C1001038E1004481070000E5 +S3250001C5808087000491060000908600047D2803A64E80002180C100388101002499060000DD +S3250001C5A04BFFFF8088AA00007CA5077490A1002C80E1002C54E70FFE90E100283861002059 +S3250001C5C038A1000838C1002880E600008106000490E500009105000439010010388100440B +S3250001C5E080A4000080C4000490A8000090C800047D2803A64E8000218101003880A10024F8 +S3250001C60098A800004BFFFF1CA90A00009101002C80A1002C54A50FFE90A100283861002098 +S3250001C620390100083881002880A4000080C4000490A8000090C8000438C1001038E100446A +S3250001C640810700008087000491060000908600047D2803A64E80002180C100388101002433 +S3250001C660B10600004BFFFEBC2C050004418200702C050005418200084BFFFEA4808A000099 +S3250001C6809081002C8081002C54840FFE908100283861002038E100083901002880880000C7 +S3250001C6A080A800049087000090A7000438A1001038C1004480E600008106000490E5000069 +S3250001C6C0910500047D2803A64E80002180A1003880E1002490E500004BFFFE48A10A0000EE +S3250001C6E09101002C7C05037890A100283861002038C1000838E10028810700008087000492 +S3250001C70091060000908600043881001038A1004480C5000080E5000490C4000090E4000401 +S3250001C7207D2803A64E8000218081003880C10024B0C400004BFFFDEC808A00009081002C29 +S3250001C7407C040378908100283861002038A1000838C1002880E600008106000490E500007D +S3250001C76091050004390100103881004480A4000080C4000490A8000090C800047D2803A683 +S3250001C7804E8000218101003880A1002490A800004BFFFD902C050007418200DC2C05000885 +S3250001C7A0418200D42C0500094182006C2C05000A418200084BFFFD6880CA000090C1002CF6 +S3250001C7C07C06037890C100283861002038E10008390100288088000080A800049087000055 +S3250001C7E090A7000438A1001038C1004480E600008106000490E50000910500047D2803A683 +S3250001C8004E80002180A1003880E1002490E500004BFFFD1080CA000090C1002C80C1002C44 +S3250001C82054C60FFE90C10028386100203881000838A1002880C5000080E5000490C40000D4 +S3250001C84090E4000438E10010390100448088000080A800049087000090A700047D2803A6DE +S3250001C8604E80002180E1003880810024908700004BFFFCB0386100203881000880AA000053 +S3250001C88080CA000490A4000090C4000438C1001038E1004481070000808700049106000027 +S3250001C8A0908600047D2803A64E80002180C10038390100208088000080A8000490860000FD +S3250001C8C090A600044BFFFC5C3821FFF081010018910300047C06037890C300003821001042 +S3250001C8E04E8000203821FFF081010018910300047D06FE7090C30000382100104E8000202E +S3250001C9003821FFF080A1001890A300047C08037891030000382100104E8000203821FFF026 +S3250001C92080A1001890A300047CA8FE7091030000382100104E8000203821FFF080E1001842 +S3250001C94090E300047C05037890A30000382100104E8000203821FFF080E1001854E7801E39 +S3250001C9607CE88670910300047D06FE7090C30000382100104E8000203821FFF080A10018A2 +S3250001C98070A5FFFF90A300047C04037890830000382100104E8000203821FFF080C1001840 +S3250001C9A054C6C00E7CC7C67090E300047CE5FE7090A30000382100104E8000203821FFF0F7 +S3250001C9C080810018708400FF908300047C08037891030000382100104E8000203821FFF0FB +S3250001C9E080810018708400FF5483C00E7C63C670382100104E80002080610008706300FF58 +S3250001CA004E8000203821FFF0808100187084FFFF5483801E7C638670382100104E8000202D +S3250001CA20806100087063FFFF4E800020806100084E800020806100084E80002080610008B0 +S3250001CA404E800020806100084E800020810100087C0800004082001880A100047C0500007C +S3250001CA604082000C7C0303784E800020386000014E80002080E10008808100107C07200055 +S3250001CA804082001480C100048101000C7C0640004182000C7C0303784E80002038600001D4 +S3250001CAA04E80002080A1000880E100107C0538004082001C8081000480C1000C7C0430004E +S3250001CAC04082000C7C0303784E800020386000014E8000208101000480A1000C7C082800B3 +S3250001CAE04180002480E100048081000C7C0720004082001C80C10008810100107C0640407A +S3250001CB004080000C386000014E8000207C0303784E80002080A1000480E1000C7C05380088 +S3250001CB20418000248081000480C1000C7C0430004082001C8101000880A100107C08284082 +S3250001CB404181000C386000014E8000207C0303784E80002080E100048081000C7C0720007C +S3250001CB604181002480C100048101000C7C0640004082001C80A1000880E100107C053840C2 +S3250001CB804081000C386000014E8000207C0303784E8000208081000480C1000C7C04300050 +S3250001CBA0418100248101000480A1000C7C0828004082001C80E10008808100107C072040EE +S3250001CBC04180000C386000014E8000207C0303784E80002080C100048101000C7C0640403D +S3250001CBE04180002480A1000480E1000C7C0538004082001C8081000880C100107C043040D6 +S3250001CC004080000C386000014E8000207C0303784E8000208101000480A1000C7C08284033 +S3250001CC204180002480E100048081000C7C0720004082001C80C10008810100107C06404038 +S3250001CC404181000C386000014E8000207C0303784E80002080A1000480E1000C7C05384005 +S3250001CC60418100248081000480C1000C7C0430004082001C8101000880A100107C08284040 +S3250001CC804081000C386000014E8000207C0303784E80002080E100048081000C7C072040FC +S3250001CCA04181002480C100048101000C7C0640004082001C80A1000880E100107C05384081 +S3250001CCC04180000C386000014E8000207C0303784E8000208121000C814100088081001484 +S3250001CCE080A100107CC921D67CE920167D0451D67CE742147D0549D67CE7421490C3000413 +S3250001CD0090E300004E8000208881000B3863FFFF8CC300017C062000418200107C060000B7 +S3250001CD204082FFF07C0303784E800020906100048081000C7C0400004081007C80A100086B +S3250001CD4054A5063E50A5442E50A5801E7CA62B787CA72B787CA82B782C04001040810050F2 +S3250001CD607069000741820018212900087D2103A67CA01D2A7C634A147C8920507C892671A7 +S3250001CD804081002C7D2903A690A300003943000890C3000490EA0000386A0008910A00047F +S3250001CDA04200FFE85484073F4182000C7C8103A67CA01D2A806100044E8000209061000485 +S3250001CDC08121000C7C6A1B787C090000418200A841800184816100082C0900104081008C73 +S3250001CDE07DAB4A147D8A4A147C0A5840418100CC7D67527870E7000340820084714700038C +S3250001CE004182002020E700047D2748507CE103A67E005C2A7D6B3A147E00552A7D4A3A148F +S3250001CE207D2E2671408100447DC903A6396BFFFC394AFFFC860B0004960A0004862B0004AA +S3250001CE40962A0004860B0004960A0004862B0004962A00044200FFE05529073F4182001895 +S3250001CE60396B0004394A00047D2103A67E005C2A7E00552A806100044E8000207D2E26711F +S3250001CE80408100247DC903A67E0B84AA396B00107E0A85AA394A00104200FFF05529073F0D +S3250001CEA04182FFD47D2103A67E005C2A7E00552A806100044E8000202C090004418000843C +S3250001CEC07DA7627870E700034082007871A70003418200188E0DFFFF9E0CFFFF3929FFFF22 +S3250001CEE071A700034082FFF07D2E2671408100347DC903A6860DFFFC960CFFFC862DFFFC60 +S3250001CF00962CFFFC860DFFFC960CFFFC862DFFFC962CFFFC4200FFE05529073F4182FF58B8 +S3250001CF207D2E16714081001C7DC903A6860DFFFC960CFFFC4200FFF8552907BF4182FF384A +S3250001CF407C0D58404081FF308E0DFFFF9E0CFFFF4BFFFFF090000000806100044E800020DC +S3250001CF604BFFFE5C3821FFD08281003881C1003C7C731B787C6B1B787C1203787C0D03781C +S3250001CF807C1003787C0F0378888B00007C8407742C04000C41810268418202502C04000939 +S3250001CFA0418202482C04000A418202402C04000B4182023888CB00007CC607742C06002D77 +S3250001CFC04182020888EB00007CE707742C07002B418201F87C0E00004082001888AB00007B +S3250001CFE07CA507742C050030418201B039C0000A2C0E0002418000782C0E002441810070B1 +S3250001D0002C0E00104082001488EB00007CE707742C070030418200F07C0D037838A0FFFFA8 +S3250001D0207E25739688CB00007CC607747CCA33787DCC73782C0600304180000C2C0A00396A +S3250001D040408100BC2C0A00614180000C2C0A007A408100A42C0A00414180000C2C0A005AFF +S3250001D0604081008C7C0C7000418000547C100000408200087E6B9B787C14000041820008A2 +S3250001D080917400007C0F0000418200103860FFFF382100304E8000207C12000041820014B4 +S3250001D0A07C0303787C6D1850382100304E8000207DA36B78382100304E8000207C0D8840DC +S3250001D0C04081000839E000017CAD71D67D4562147C0A68404080000839E000017D4D537819 +S3250001D0E0396B00013A1000014BFFFF3C398AFFC94BFFFF74398AFFA94BFFFF6C398AFFD0BA +S3250001D1004BFFFF64890B00017D0807742C08007841820018888B00017C8407742C04005828 +S3250001D120418200084BFFFEF488AB00027CA507742C0500304180001C88CB00027CC60774C0 +S3250001D1402C0600394181000C396B00024BFFFECC88EB00027CE707742C0700614180001419 +S3250001D160890B00027D0807742C0800664081FFDC888B00027C8407742C0400414180FE9C80 +S3250001D18088AB00027CA507742C0500464181FE8C396B00024BFFFE8439C00008890B0001EC +S3250001D1A07D0807742C08007841820018888B00017C8407742C040058418200084BFFFE3483 +S3250001D1C039C000104BFFFE2C7D685B78396B0001890800007D0807742C08002D4082FDF8C5 +S3250001D1E03A4000014BFFFDF0396B0001888B00007C8407742C04000C4081FDA02C04000D6C +S3250001D2004182FFE82C0400204182FFE04BFFFDA87FE802A697E1FFD8814100309061002C0F +S3250001D22088AA00007CA5077490A100207C0500004082001483E100007FE803A6382100287C +S3250001D2404E8000207D435378480001819061001C8061002C80A1002090A100084BFFFAADFF +S3250001D2607C691B787C0900004182005C7D234B789121002480810030908100088101001C6A +S3250001D2809101000C480000CD812100247C030000408200187D234B7883E100007FE803A6DE +S3250001D2A0382100284E8000203869000180C1002090C100084BFFFA557C691B787C09000006 +S3250001D2C04082FFAC7C03037883E100007FE803A6382100284E8000207FE802A697E1FFE88F +S3250001D2E07C691B7880A100207C050000408200247D234B787C080378910100084BFFFA0DBA +S3250001D30083E100007FE803A6382100184E8000207C040378908100147D234B78810100200E +S3250001D320910100084BFFF9E57C0300004182001039230001906100144BFFFFE08061001452 +S3250001D34083E100007FE803A6382100184E8000203821FFF081A100188141001C7C6C1B7818 +S3250001D3607C0A0000408100587D896378398C0001892900007D2907747DAB6B7839AD00019B +S3250001D380896B00007D6B0774394AFFFF7C095800418200247C095840408100103860000163 +S3250001D3A0382100104E8000203860FFFF382100104E8000207C0900004082FFA87C0303783A +S3250001D3C0382100104E8000207FE802A697E1FFF0906100147C07037890E100084BFFF92D8D +S3250001D3E0810100147C68185083E100007FE803A6382100104E8000207FE802A697E1FFE013 +S3250001D400814100287C691B789061001C7D234B789121002491410028914100087C0803788A +S3250001D4209101000C3900271091010010480001617C0300004082001880A100243925271058 +S3250001D44081010028394827104BFFFFC48061001C83E100007FE803A6382100204E8000207E +S3250001D4603821FFF0818100187C6B1B787D695B78396B0001892900007D2907747D8A6378B6 +S3250001D480398C0001894A00007D4A07747C095000418200247C09504040810010386000016F +S3250001D4A0382100104E8000203860FFFF382100104E8000207C0900004082FFB47C0303782D +S3250001D4C0382100104E8000203821FFE8812100247C6D1B78818100207C0900404081005074 +S3250001D4E07DAB6B7839AD0001896B00007D8A6378398C0001894A00007C0B50004182002406 +S3250001D5007C0B50404081001038600001382100184E8000203860FFFF382100184E8000202F +S3250001D5203929FFFF7C0900404181FFB87C030378382100184E8000203821FFF08121001CE7 +S3250001D5407C6A1B7880E1001870EB00FF7C090040408100307D475378394A000188E7000040 +S3250001D5607C07580040820010386AFFFF382100104E8000203929FFFF7C0900404181FFD842 +S3250001D5807C030378382100104E8000203821FFF0812100207C6A1B78816100188081001C99 +S3250001D5A0708C00FF7C090040408100407D465378394A00017D645B78396B0001888400002C +S3250001D5C0988600005484063E7C046000408200107D435378382100104E8000203929FFFF16 +S3250001D5E07C0900404181FFC87C030378382100104E8000207C040378808400007C04000006 +S3150001D600418200084BFFFFF04E8000200000000021 +S3250001D61000810E02001415F0001403E80000000000146530000000010000000000000000A0 +S3250001D6300000000000000000FFFFFFFF0000001E0000001D0000001C0000001B0000000065 +S3250001D6500000000000000000000000000000000000000000000000000000000000000000B3 +S3250001D670000000000000000000000000000000000000000000000000000000000000000093 +S3250001D690FFFFFFFF00000A0000000A2000000A4000000A6000140104000000000000000076 +S3250001D6B0000000000000000000000000000000012A2A2A000000000023706C616E392E6936 +S3250001D6D06E690A00000000005250587369676E61747572653D312E300A4E414D453D71627F +S3250001D6F0727063670A53544152543D46464332303130300A56455253494F4E3D312E310AC9 +S3250001D710000000000001000000000401080205030C06060909070710100000000000000082 +S3250001D7306672656562007167657400000000000000141BAF0000000000000000000000003F +S3250001D7500000000000000000000000000000000000000000000000000000000000000000B2 +S3250001D770000000000000000000000000000000000000000000000000000000000000000092 +S3250001D79000000000000000000000000000000000626F6F74006469736B00626F6F740075EA +S3250001D7B06172743A207374617274207472616E736D697373696F6E0A00756172743A2074A5 +S3250001D7D0696D656F75740A006E007900000000000000000000000000001403200000000077 +S3250001D7F0000000000000000000000000000000000000000000000000000000000000000012 +S3250001D8100000000000000000000000000000000000000000000000000000000000000000F1 +S3250001D8300000000000000000FFC206F000000000000000000000000000000000000000001A +S3250001D8500000000000000000000000000000000000000000000000000000000000000000B1 +S3250001D87000000000000000000000000000000000000000000000000000141A00FFC26E38FC +S3250001D89000141A04FFC26E3800000000000000006D6170667265653A2025733A206C6F735E +S3250001D8B0696E672030782575582C2025640A000000141A9300141AA700141AB300141AC312 +S3250001D8D0000000000014054000000000000000006E6577616C61726D000000000000000081 +S3250001D8F00000000000000000F1A55A1FFFC2363000000000000000000000000000000000DB +S3250001D9100000000000000000FFFFFFFF0000000000000004000000080000000C00000000DC +S3250001D9306275666665722064657363726970746F7273006264616C6C6F63000000000000B3 +S3250001D95000141A810000000000141A860000000100141A8C00000002000000000000000090 +S3250001D970FFFFFFFFFFFF000000000000000000000000000000000000000000000000000096 +S3250001D9900000000000000000000000000000000000000000000000000000000013880000D5 +S3250001D9B0FFFFFFFFFA203C00FA203D00FA203E00FA203F000000000000141FD70014203B7D +S3250001D9D06932632073657475702E2E2E0A004932432325780A0000000000000B00000000BA +S3250001D9F0000000000000000000140E4000000000000000200000000000000000000000008E +S3250001DA10000004000000000E0000000000000000000004000000000E0000000000000000CB +S3250001DA3041F0000000000000001407460014074C0014075100140754001407580014075B12 +S3250001DA500014075E0014076200140766001407690014076C0014076F001407720014077586 +S3250001DA70001407780014077B0014077E0014078100140784001407880014078C001407909D +S3250001DA9000140794001407980014079C001407A0001407A4001407A8001407AC001407B087 +S3250001DAB0001407B4001407B8001407BC001407C0001407C4001407C8001407CC001407D067 +S3250001DAD0001407D4001407D8FFC24714FFC2474CFFC24798FFC247BCFFC247C4FFC247CCD9 +S3250001DAF0FFC247F0FFC24830FFC24838FFC2485CFFC2488C000000000000000000000000A3 +S3250001DB100000000000000000000000000000000000000000000000000000000000000000EE +S3250001DB300000000000000000000000000000000000000000000000000000000000000000CE +S3250001DB507072656D617475726520454F460A002E00626164206D61676963203078256C7591 +S3250001DB7078206E6F74206120706C616E20392065786563757461626C65210A002564002BDF +S3250001DB9025644025382E386C7578002B25640A7374617274206174203078256C75780A0058 +S3250001DBB000000000000000000000000000000000000000000000000000000000000000004E +S3250001DBD000000000000A000000000000000000000000000000000000000000000000000024 +S3250001DBF0000000000000000000000000000000000000000000000000090000000000000005 +S3250001DC100000000102000000030000000400000500000006000700000800000000000000C9 +S3250001DC30726573657276656420300073797374656D207265736574006D616368696E652040 +S3250001DC50636865636B00646174612061636365737300696E737472756374696F6E206163DD +S3250001DC70636573730065787465726E616C20696E7465727275707400616C69676E6D656E24 +S3250001DC90740070726F6772616D20657863657074696F6E00666C6F6174696E672D706F6949 +S3250001DCB06E7420756E617661696C61626C650064656372656D656E74657200726573657218 +S3250001DCD0766564204100726573657276656420420073797374656D2063616C6C0074726123 +S3250001DCF06365207472617000666C6F6174696E6720706F696E742061737369737400726542 +S3250001DD10736572766564204600736F66747761726520656D756C6174696F6E0049544C4279 +S3250001DD30206D6973730044544C42206D6973730049544C42206572726F720044544C422033 +S3250001DD506572726F72004341555345005352523100504300474F4B004C52004352005845A5 +S3250001DD7052004354520052300052310052320052330052340052350052360052370052389B +S3250001DD900052390052313000523131005231320052313300523134005231350052313600E7 +S3250001DDB052313700523138005231390052323000523231005232320052323300523234008D +S3250001DDD0523235005232360052323700523238005232390052333000523331007365746866 +S3250001DDF0766563006F7574206F6620696E746572727570742068616E646C6572730025736B +S3250001DE1009252E386C757809257309252E386C75780A00657863657074696F6E2F696E741D +S3250001DE30657272757074202325780A00657863657074696F6E2025730A005E5020746F20DD +S3250001DE5072657365740A00005573696E6720666C61736820636F6E66696775726174696F20 +S3250001DE706E0A000A002573256400747970653D00706F72743D006972713D006D656D3D00E2 +S3250001DE9073697A653D0065613D00000000000000676574636C75737420402025640A00678B +S3250001DEB06574636C75737420256420696E2063616368650A00676574636C75737420616439 +S3250001DED064722025640A0063616E2774207365656B20626C6F636B0A0063616E2774207279 +S3250001DEF065616420626C6F636B0A00676574636C75737420256420726561640A0066617497 +S3250001DF1077616C6B2025640A006765746661740066617477616C6B202564202D3E20256446 +S3250001DF300A0066696C65616464722025382E38732025640A0066696C656164647220256498 +S3250001DF50202D3E2025640A0066696C6561646472202564202D3E2025640A0077616C6B6932 +S3250001DF706E67206E6F6E2D6469726563746F7279210A00636F6D706172696E6720746F203A +S3250001DF9025382E38732E25332E33730A0025382E38732E25332E33732069732061204C4116 +S3250001DFB042454C0A0063616E2774207265616420626F6F7420626C6F636B0A006E6F74206A +S3250001DFD0444F530A006E6F20646F732066696C652073797374656D0A006D616769633A200D +S3250001DFF0307825322E327820307825322E327820307825322E32780A0076657273696F6E35 +S3250001E0103A202225382E3873220A007365637473697A653A2025640A00616C6C6F637369CD +S3250001E0307A653A2025640A006E72657372763A2025640A006E666174733A2025640A0072F5 +S3250001E0506F6F7473697A653A2025640A00766F6C73697A653A2025640A006D656469616452 +S3250001E0706573633A20307825322E32780A0066617473697A653A2025640A0074726B736903 +S3250001E0907A653A2025640A006E68656164733A2025640A006E68696464656E3A2025640A76 +S3250001E0B000626967766F6C73697A653A2025640A0064726976656E6F3A2025640A00726563 +S3250001E0D0736572766564303A20307825322E32780A00626F6F747369673A20307825322EB7 +S3250001E0F032780A00766F6C69643A20307825382E38780A006C6162656C3A20222531312EBF +S3250001E110313173220A006E616D6520636F6D706F6E656E7420746F6F206C6F6E670A0065A2 +S3250001E13072726F722077616C6B696E6720746F2025730A002573206E6F7420666F756E64EC +S3250001E1500A00666F756E642025382E38732E25332E3373206174747220307825757820738A +S3250001E17074617274203078256C7578206C656E2025640A00626164206D6167696320307865 +S3250001E190256C7578206E6F74206120706C616E20392065786563757461626C65210A002B3C +S3250001E1B02564002B2564002B25640A7374617274206174203078256C75780A002E006E61DD +S3250001E1D06D652069732025382E38732025332E33730A0000000000002E007A71733A206E5A +S3250001E1F06F7420706F77657250432065786563757461626C650A0074657874007A71733A9C +S3250001E21020666F726D6174206572726F720A0064617461007A71733A20666F726D617420EF +S3250001E2306572726F720A000A73717565657A6564206B65726E656C3A20636865636B737542 +S3250001E2506D206572726F723A2025382E386C7578206E6565642025382E386C75780A007503 +S3250001E2706E7061636B2025732025382E386C757820256C75643A00202A2A73697A6520650E +S3250001E29072726F720A000A007A71733A20636F72727570742073717565657A65642064615C +S3250001E2B074612073747265616D0A00205425640025642025382E386C75782025382E386CAB +S3250001E2D075780A0000000000666C6173683A2062616420636865636B73756D0A00666C61F1 +S3250001E2F073683A20666C617368206E6F742070726573656E74206F72206E6F7420656E616C +S3250001E310626C65640A00636F6E66202325382E386C75783A20232578202325362E366C7543 +S3250001E330780A00666C6173683A20666F756E6420636F6E6669672025382E386C75782825A1 +S3250001E35064293A0A25730A00666C6173683A206E6F20636F6E6669670A00666C6173682020 +S3250001E370636F6E6669672025382E386C7578282564293A0A25730A00666C6173683A207339 +S3250001E390717565657A656420706F7765727063206B65726E656C20696E7374616C6C6564D2 +S3250001E3B00A00666C6173683A20756E73717565657A656420706F7765727063206B65726E9B +S3250001E3D0656C20696E7374616C6C65640A00666C6173683A206E6F20706F776572706320E6 +S3250001E3F06B65726E656C20696E20466C6173680A00746578743A2025382E386C7578203CDF +S3250001E4102D2025382E386C7578205B256C645D0A00646174613A2025382E386C7578203C39 +S3250001E4302D2025382E386C7578205B256C645D0A00656E7472793D3078256C75780A0000E6 +S3250001E450907070F0F0F07000F0888888F8707070E0E0E0E0E09070F070F870F0F870F0882D +S3250001E470000000000000000000000000000000000000000000000000000000000000000085 +S3250001E490000000000000000000000000000000000000000000000000000000000000000065 +S3250001E4B000000000000000000000000000000000000000000000000000000000000000E065 +S3250001E4D0D0808080808088008888C888808888889090909090D0808080808080808888881D +S3250001E4F000000000080000000C3000000000000600000000000000000000000000000000BB +S3250001E5100000000000000000000000000000000000000000000000000000003CC03C0000AC +S3250001E5300000600006001E00601818607800000000000000000000000000001C18380090DC +S3250001E550B06060E0E0E0F800F088A888808080809090909090B060E0808060E080808888E4 +S3250001E570001824283E70701818180000000000063C183C3C1C3E3C7E3C3C00000200403CDE +S3250001E5903C187C1E787E7E1E663C7C666066623C7C3C7C3C7E6266C266667E30C00C10002E +S3250001E5B008006000060030006018186018000000000000001000000000000030180C0090AA +S3250001E5D0901010808080881888F8A888E0807070E0E0E0E0E090108070E01080E098F08814 +S3250001E5F0001824286ADAD818181810000000000C663866662C3E667E6666000006006066D0 +S3250001E610421866326C606032661818646066722466246666186262DA62620630600C380093 +S3250001E63010006000060030006000006018000000000000003000000000000030180C00E0E1 +S3250001E65000E0E0F0F0F000188888985080880808201C1C1C1C00E0F00080E0F08088888823 +S3250001E6700018242868DAD808300C54180000000C665806062C206002666618180CFE300695 +S3250001E6909E2C6660666060606618186C6066726666666660186262DA36660C30600C2800F9 +S3250001E6B0103C6C3C3E3C7E3E6C78786618D46C3C6C3E763C7E6666C266667E1818180000C9 +S3250001E6D04418000018241C24F08888208070F0F020202020201C243E1CF8241C8070887001 +S3250001E6F00018247C78745008300C381800000018661806064C2060067666181818FE180CC1 +S3250001E710B62C66606660606066181868607E5A6666666470186266DA34340C30300C6C0072 +S3250001E73018667666666630667618186418FE766676663666306662DA626206081810323C58 +S3250001E75044247C7C2434204200000000000000002020202020222408220024200000000034 +S3250001E770001800283C287610300CEE7E00FE001866180C184C3C7C0C3C3E000030000C181A +S3250001E790B62C7C60667C7C6E7E181878605A5A666466783C186234DA18341830300C44001E +S3250001E7B018066660666630666618186818D6666666663860306662DA34620C30180C5A209E +S3250001E7D044241010242C20420E3E103E3E3C1C3E3C1C1C1C1C3E1C083E2224180E0E0E0E98 +S3250001E7F00008007C1E5CDC00300C387E00FE0030661818067E0666186E06000018001818AE +S3250001E810B67E6660666060666618186C605A4E6678666C0E1862346C2C183030180C44006D +S3250001E830003E6660667E30666618187818D666666666303C306634DA1834180818104C3887 +S3250001E8503C18101018241C4211081008202222080000000000220408223618041111111110 +S3250001E8700000002816B6CC00300C541800000030661830067E066618660600000CFE30008D +S3250001E8909A466660666060666618186C605A4E66606666061862346C6C183030180C00006B +S3250001E8B000666660666030666618186C18D66666666630063066346C2C34301818180020CD +S3250001E8D000091010000E094210081008202222080F06060F0A09041E002A0E3810101010F4 +S3250001E8F00018002856B6CC00300C10181800186066187E660C6666306666181806FE601813 +S3250001E910404666326C60603666181866605A4624602466661834186C461860300C0C00001A +S3250001E930006E66666E66306E6618186618D66666666E3066306E186C46186030180C003C84 +S3250001E950080909091F110AFF0E081008382C2208080209010A0A0911092209070E0E0E0ED3 +S3250001E970001800287C1C760018180000180018603C7E7E3C0C3C3C303C3C18180200401848 +S3250001E9903E467C1E787E601E663C18667E42463C603C663C1818186C66187E300C0C000036 +S3250001E9B000367C3C363C7C36667E18667ED6663C7C367C3C1E36186C66187E30180C00083C +S3250001E9D0080F060604110C1801081008202222080E0202030A0C0D1E0D220E080101010198 +S3250001E9F0000000001000000018180000080000C000000000000000000000000800000000F0 +S3250001EA1000000000000000000000180000000000000C00000000000000000030060C00FE7B +S3250001EA300000000000000006000018000000000060060000000000000010001C18380008B7 +S3250001EA5008090606040E0A1811081F0820221C3E080204010F0A0B110B22090811111111AC +S3250001EA7000000000000000000C300000080000C00000000000000000000000080000000073 +S3250001EA900000000000000000000070000000000000060000000000000000003C063C00006B +S3250001EAB0000000000000006600001800000000006006000000000000003000000000000823 +S3250001EAD00F090909040309000E000000000000000F0F0F0F0209091E09000F070E0E0E0E12 +S3250001EAF00000000000000000000000001000000000000000000000000000001000000000DF +S3250001EB100000000000000000000000000000000000000000000000000000000000000000DE +S3250001EB30000000000000003C00007000000000006006000000000000006000000000000F3D +S3250001EB50636F6E736F6C65006C63640073637265656E006C63640062617564005E70002536 +S3250001EB70735B64656661756C743D3D25735D3A200025733A20000A006C696E6520746F6FEC +S3250001EB90206C6F6E670A0070616E69633A20000A0000000000000000001406200014062B96 +S3250001EBB000140638001406460014065200140665001406780014068200140694001406AFFC +S3250001EBD0001406BB001406C6001406D1001406DD001406E8001406FE001407090014071C12 +S3250001EBF000140726001407300014073B000000000000000E000800000008000E00080000E8 +S3250001EC100010000E000800000018000E000800000020000E000800000028000E0008000015 +S3250001EC300030000E000800000038000E000800000040000E000800000048000E0008000075 +S3250001EC500050000E000800000058000E000800000060000E000800000068000E00080000D5 +S3250001EC700070000E000800000078000E000800000080000E000800000088000E0008000035 +S3250001EC900090000E000800000098000E0008000000A0000E0008000000A8000E0008000095 +S3250001ECB000B0000E0008000000B8000E0008000000C0000E0008000000C8000E00080000F5 +S3250001ECD000D0000E0008000000D8000E0008000000E0000E0008000000E8000E0008000055 +S3250001ECF000F0000E0008000000F8000E0008000001000000000800000108020B00080000C2 +S3250001ED1001100206000800000118020B000800000120010C000800000128020B0008000019 +S3250001ED300130020B0008000001380207000800000140010D000800000148010D0008000076 +S3250001ED500150030A000800000158040A000800000160090E000800000168060800080000C8 +S3250001ED700170090B000800000178010D000800000180020B000800000188020B000800002C +S3250001ED900190020B000800000198020B0008000001A0020B0008000001A8020B0008000094 +S3250001EDB001B0020B0008000001B8020B0008000001C0020B0008000001C8020B00080000F4 +S3250001EDD001D0040B0008000001D8040E0008000001E0020B0008000001E8040A000800004C +S3250001EDF001F0020B0008000001F8020B000800000200020B000800000208020B00080000B2 +S3250001EE100210020B000800000218020B000800000220020B000800000228020B000800000F +S3250001EE300230020B000800000238020B000800000240020B000800000248020B000800006F +S3250001EE500250020D000800000258020B000800000260020B000800000268020B00080000CD +S3250001EE700270020B000800000278020B000800000280020B000800000288020D000800002D +S3250001EE900290020B000800000298020B0008000002A0020B0008000002A8020B000800008F +S3250001EEB002B0020B0008000002B8020B0008000002C0020B0008000002C8020B00080000EF +S3250001EED002D0020B0008000002D8010D0008000002E0010D0008000002E8010D000800004C +S3250001EEF002F002080008000002F80B0C0008000003000207000800000308040B00080000A8 +S3250001EF100310010B000800000318040B000800000320010B000800000328040B0008000008 +S3250001EF300330010B000800000338040E000800000340010B000800000348010B0008000068 +S3250001EF500350010E000800000358010B000800000360010B000800000368040B00080000C8 +S3250001EF700370040B000800000378040B000800000380040E000800000388040E000800001C +S3250001EF900390040B000800000398040B0008000003A0020B0008000003A8040B0008000084 +S3250001EFB003B0040B0008000003B8040B0008000003C0040B0008000003C8040E00080000DF +S3250001EFD003D0040B0008000003D8010C0008000003E0010C0008000003E8010C0008000048 +S3250001EFF003F005080008000003F8000E000800000400000E000800000000000000000000C7 +S3250001F010534343005343433200657468657225643A2025733A20706F7274203078256C7575 +S3250001F0305820697271202564002061646472203078256C7558002073697A65203078256C37 +S3250001F0507558003A002025322E327558000A00657468657225643A207478207175657565B8 +S3250001F0702066756C6C0A0000666C6173680046006574686572006500617461006864006861 +S3250001F090006175746F006C6F63616C006D616E75616C00696E6665726E6F2F696E666572E3 +S3250001F0B06E6F2E696E6900696E6665726E6F2E696E6900706C616E392F706C616E392E6901 +S3250001F0D06E6900706C616E392E696E69002100002573212564006469736B002573212564A0 +S3250001F0F0212573006469736B00696D706300257321256421257300626F6F74006469736B92 +S3250001F11000646F73006469736B00646F7320696E6974206661696C65640A00707265646132 +S3250001F130776E0A006461776E0A006F7074696F6E733D232575780A00466C61736820626FAE +S3250001F1506F740A00626F6F7466696C650042616420626F6F7466696C652073796E74617815 +S3250001F1703A2025730A0043616E6E6F7420616363657373206465766963653A2025730A00FB +S3250001F190426F6F7420646576696365733A00202573212564002025732125640020257321EA +S3250001F1B02564000A00626F6F742066726F6D00706879736963616C206D656D6F727900699E +S3250001F1D0616C6C6F63006672656500000000000045544845522E5343432325643A20736310 +S3250001F1F06365203D2030782575580A007E0040006E6F207072657365742045746865722089 +S3250001F210616464726573730A00303031303862663132393030006574686572204D41432001 +S3250001F2306164647265737300696E76616C6964204D414320616464726573730A00000000E9 +S3250001F2500A746674703A206572726F72282564293A2025730A0069702063686B73756D20D1 +S3250001F2706572726F720A0069702062616420766572732F686C656E0A007564702063686BC4 +S3250001F29073756D206572726F72206373756D202325346C7578206C656E2025640A00756400 +S3250001F2B0703A207061636B657420746F6F206269670A002573006F637465740074667470B2 +S3250001F2D06F70656E3A206572726F7220282564293A2025730A00746674706F70656E3A2021 +S3250001F2F0626C6F636B206572726F723A2025640A00626C6F636B206572726F720074667412 +S3250001F310706F70656E3A206661696C656420746F20636F6E6E65637420746F207365727605 +S3250001F33065720A0074667470726561643A20256420213D2025640A0073686F727420726540 +S3250001F3506164003F0074667470726561643A20626C6F636B206572726F723A2025642C205A +S3250001F37065787065637465642025640A00626C6F636B206572726F7200696E76616C696435 +S3250001F3902063746C726E6F2025640A00626F6F74702074696D6564206F75740A0025732060 +S3250001F3B0002825642E25642E25642E2564212564293A2025730A00626164206D6167696349 +S3250001F3D0206E756D626572006C6F61644025382E386C75783A20002564002B2564006F6B00 +S3250001F3F0002B25643D25640A00656E7472793A203078256C75780A000000000000141A6820 +S3250001F41000000008FFC27AE40000000000000000000000000000000000141A6E0000000012 +S3250001F43000000000000000000000000200141A7000000004FFC26068000000000000000088 +S3250001F45000000000000000000000000000141A7600000000000000000000000300141A7848 +S3250001F470000000130000000000000000000000000000000000141A7C00141A7F000000000B +S3250001F4900000000000000000FFFFFFFF000000000000000000000000000000000000000059 +S3250001F4B0000000000000000000000000000000000000000000000000496E6665726E6F2044 +S3250001F4D0626F6F7473747261700A005056523A20004D5043363031004D5043363033004D9E +S3250001F4F05043363034004D504336303365004D5043363033652D7637004D5043387878002A +S3250001F510506F77657250432076657273696F6E20232578002C207265766973696F6E202330 +S3250001F530256C75780A00494D4D523A20004D50433836302F383231004D5043383233004D8B +S3250001F55050433832334100547970652023256C7578002C206D61736B2023256C75780A00FD +S3250001F5706F7074696F6E733A2023256C75780A00626373723A2025382E386C75780A0050E9 +S3250001F5904C505243523D25382E386C757820534343523D25382E386C75780A00256C756460 +S3250001F5B0204D487A2073797374656D0A000A004252303D25382E386C7578204F52303D25BC +S3250001F5D0382E386C75780A004D505450523D25382E386C75780A006574686572303D7479AB +S3250001F5F070653D53434320706F72743D312065613D3030313065633030303035310D0A7657 +S3250001F610676173697A653D3634307834383078380D0A6B65726E656C70657263656E743DEF +S3250001F63034300D0A636F6E736F6C653D300D0A626175643D393630300D0A006574686572EA +S3250001F650303D747970653D53434320706F72743D322065613D303031306563303030303559 +S3250001F670310D0A76676173697A653D3634307834383078380D0A6B65726E656C7065726355 +S3250001F690656E743D34300D0A636F6E736F6C653D300D0A626175643D393630300D0A0055C9 +S3250001F6B073696E672064656661756C7420636F6E66696775726174696F6E0A00414D4432D7 +S3250001F6D039463078300053434300454100693263206661696C65640A00656570726F6D3A0E +S3250001F6F00A002025322E3275785B25635D000A00CFFFCC240FFFCC040CAFCC0403AFCC082E +S3250001F7103FBFCC27FFFFCC25FFFFCC25FFFFCC25CFFFCC240FFFCC040CAFCC8403AFCC8867 +S3250001F7303FBFCC27FFFFCC25FFFFCC25FFFFCC25FFFFCC25FFFFCC25FFFFCC25FFFFCC2538 +S3250001F750FFFFCC25FFFFCC25FFFFCC25FFFFCC25CFFFCC240FFFCC040CFFCC0403FFCC0091 +S3250001F7703FFFCC27FFFFCC25FFFFCC25FFFFCC25CFFFCC240FFFCC040CFFCC8403FFCC842B +S3250001F7900CFFCC0033FFCC27FFFFCC25FFFFCC25FFFFCC25FFFFCC25FFFFCC25FFFFCC25BC +S3250001F7B0FFFFCC25FFFFCC25FFFFCC25FFFFCC25C0FFCC2403FFCC240FFFCC240FFFCC24D9 +S3250001F7D03FFFCC27FFFFCC25FFFFCC25FFFFCC25FFFFCC25FFFFCC25FFFFCC25FFFFCC2558 +S3150001F7F0FFFFCC25FFFFCC25FFFFCC25FFFFCC2546 +S700000100FE diff --git a/os/boot/rpcg/gbitbltclip.c b/os/boot/rpcg/gbitbltclip.c new file mode 100644 index 00000000..cbf877f5 --- /dev/null +++ b/os/boot/rpcg/gbitbltclip.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include + +void +gbitbltclip(void *vp) +{ + int dx, dy; + int i; + struct{ + GBitmap *dm; + Point p; + GBitmap *sm; + Rectangle r; + Fcode f; + }*bp; + + bp = vp; + dx = Dx(bp->r); + dy = Dy(bp->r); + if(bp->p.x < bp->dm->clipr.min.x){ + i = bp->dm->clipr.min.x-bp->p.x; + bp->r.min.x += i; + bp->p.x += i; + dx -= i; + } + if(bp->p.y < bp->dm->clipr.min.y){ + i = bp->dm->clipr.min.y-bp->p.y; + bp->r.min.y += i; + bp->p.y += i; + dy -= i; + } + if(bp->p.x+dx > bp->dm->clipr.max.x) + bp->r.max.x -= bp->p.x+dx-bp->dm->clipr.max.x; + if(bp->p.y+dy > bp->dm->clipr.max.y) + bp->r.max.y -= bp->p.y+dy-bp->dm->clipr.max.y; + if(bp->r.min.x < bp->sm->clipr.min.x){ + i = bp->sm->clipr.min.x-bp->r.min.x; + bp->p.x += i; + bp->r.min.x += i; + } + if(bp->r.min.y < bp->sm->clipr.min.y){ + i = bp->sm->clipr.min.y-bp->r.min.y; + bp->p.y += i; + bp->r.min.y += i; + } + if(bp->r.max.x > bp->sm->clipr.max.x) + bp->r.max.x = bp->sm->clipr.max.x; + if(bp->r.max.y > bp->sm->clipr.max.y) + bp->r.max.y = bp->sm->clipr.max.y; +} diff --git a/os/boot/rpcg/gnot.h b/os/boot/rpcg/gnot.h new file mode 100644 index 00000000..7b99e6bb --- /dev/null +++ b/os/boot/rpcg/gnot.h @@ -0,0 +1,71 @@ + +extern void *bbmalloc(int); +extern void bbfree(void *, int); +extern int bbonstack(void); +extern void bbexec(void(*)(void), int, int); + +/* + * Graphics types + */ + +typedef struct GBitmap GBitmap; +typedef struct GFont GFont; +typedef struct GSubfont GSubfont; +typedef struct GCacheinfo GCacheinfo; + +struct GBitmap +{ + ulong *base; /* pointer to start of data */ + long zero; /* base+zero=&word containing (0,0) */ + ulong width; /* width in 32 bit words of total data area */ + int ldepth; /* log base 2 of number of bits per pixel */ + Rectangle r; /* rectangle in data area, local coords */ + Rectangle clipr; /* clipping region */ + GBitmap *cache; /* zero; distinguishes bitmap from layer */ +}; + + +/* + * GFont etc. are not used in the library, only in devbit.c. + * GSubfont is only barely used. + */ +struct GSubfont +{ + short n; /* number of chars in font */ + char height; /* height of bitmap */ + char ascent; /* top of bitmap to baseline */ + Fontchar *info; /* n+1 character descriptors */ + GBitmap *bits; /* where the characters are */ +}; +struct GCacheinfo +{ + ulong xright; /* right edge of bits */ + Fontchar; +}; + +struct GFont +{ + uchar height; /* max height of bitmap, interline spacing */ + char ascent; /* top of bitmap to baseline */ + char width; /* widest so far; used in caching only */ + char ldepth; /* of images */ + short id; /* of font */ + int ncache; /* number of entries in cache */ + GCacheinfo *cache; /* cached characters */ + GBitmap *b; /* cached images */ +}; + +extern ulong *gaddr(GBitmap*, Point); +extern uchar *gbaddr(GBitmap*, Point); +extern void gbitblt(GBitmap*, Point, GBitmap*, Rectangle, Fcode); +extern void gbitbltclip(void*); +extern void gtexture(GBitmap*, Rectangle, GBitmap*, Fcode); +extern Point gsubfstrsize(GSubfont*, char*); +extern int gsubfstrwidth(GSubfont*, char*); +extern Point gsubfstring(GBitmap*, Point, GSubfont*, char*, Fcode); +extern Point gbitbltstring(GBitmap*, Point, GSubfont*, char*, Fcode); +extern void gsegment(GBitmap*, Point, Point, int, Fcode); +extern void gpoint(GBitmap*, Point, int, Fcode); +extern void gflushcpucache(void); +extern GBitmap* gballoc(Rectangle, int); +extern void gbfree(GBitmap*); diff --git a/os/boot/rpcg/i2c.c b/os/boot/rpcg/i2c.c new file mode 100644 index 00000000..dfd07fa8 --- /dev/null +++ b/os/boot/rpcg/i2c.c @@ -0,0 +1,360 @@ +#include "boot.h" + +/* + * basic read/write interface to mpc8xx I2C bus (master mode) + */ + +typedef struct I2C I2C; + +struct I2C { + uchar i2mod; + uchar rsv12a[3]; + uchar i2add; + uchar rsv12b[3]; + uchar i2brg; + uchar rsv12c[3]; + uchar i2com; + uchar rsv12d[3]; + uchar i2cer; + uchar rsv12e[3]; + uchar i2cmr; +}; + +enum { + /* i2c-specific BD flags */ + RxeOV= 1<<1, /* overrun */ + TxS= 1<<10, /* transmit start condition */ + TxeNAK= 1<<2, /* last transmitted byte not acknowledged */ + TxeUN= 1<<1, /* underflow */ + TxeCL= 1<<0, /* collision */ + TxERR= (TxeNAK|TxeUN|TxeCL), + + /* i2cmod */ + REVD= 1<<5, /* =1, LSB first */ + GCD= 1<<4, /* =1, general call address disabled */ + FLT= 1<<3, /* =0, not filtered; =1, filtered */ + PDIV= 3<<1, /* predivisor field */ + EN= 1<<0, /* enable */ + + /* i2com */ + STR= 1<<7, /* start transmit */ + I2CM= 1<<0, /* master */ + I2CS= 0<<0, /* slave */ + + /* i2cer */ + TXE = 1<<4, + BSY = 1<<2, + TXB = 1<<1, + RXB = 1<<0, + + /* port B bits */ + I2CSDA = IBIT(27), + I2CSCL = IBIT(26), + + Rbit = 1<<0, /* bit in address byte denoting read */ + + /* maximum I2C I/O (can change) */ + MaxIO= 128, + Bufsize = MaxIO+4, /* extra space for address/clock bytes and alignment */ + Freq = 100000, + I2CTimeout = 250, /* msec */ +}; + +/* data cache needn't be flushed if buffers allocated in uncached INTMEM */ +#define DCFLUSH(a,n) + +/* + * I2C software structures + */ + +struct Ctlr { + Lock; + QLock io; + int init; + I2C* i2c; + IOCparam* sp; + + BD* rd; + BD* td; + int phase; + int timeout; + char* addr; + char* txbuf; + char* rxbuf; +}; +typedef struct Ctlr Ctlr; + +static Ctlr i2ctlr[1]; +extern int predawn; + +static void interrupt(Ureg*, void*); + +static void +enable(void) +{ + I2C *i2c; + + i2c = i2ctlr->i2c; + i2c->i2cer = ~0; /* clear events */ + eieio(); + i2c->i2mod |= EN; + eieio(); + i2c->i2cmr = TXE|BSY|TXB|RXB; /* enable all interrupts */ + eieio(); +} + +static void +disable(void) +{ + I2C *i2c; + + i2c = i2ctlr->i2c; + i2c->i2cmr = 0; /* mask all interrupts */ + i2c->i2mod &= ~EN; +} + +/* + * called by the reset routine of any driver using the I2C + */ +void +i2csetup(void) +{ + IMM *io; + I2C *i2c; + IOCparam *sp; + Ctlr *ctlr; + long f, e, emin; + int p, d, dmax; + + ctlr = i2ctlr; + if(ctlr->init) + return; + print("i2c setup...\n"); + ctlr->init = 1; + i2c = KADDR(INTMEM+0x860); + ctlr->i2c = i2c; + sp = KADDR(INTMEM+0x3c80); + ctlr->sp = sp; + disable(); + + if(ctlr->txbuf == nil){ + ctlr->txbuf = ialloc(Bufsize, 2); + ctlr->addr = ctlr->txbuf+Bufsize; + } + if(ctlr->rxbuf == nil) + ctlr->rxbuf = ialloc(Bufsize, 2); + if(ctlr->rd == nil){ + ctlr->rd = bdalloc(1); + ctlr->rd->addr = PADDR(ctlr->rxbuf); + ctlr->rd->length = 0; + ctlr->rd->status = BDWrap; + } + if(ctlr->td == nil){ + ctlr->td = bdalloc(2); + ctlr->td->addr = PADDR(ctlr->txbuf); + ctlr->td->length = 0; + ctlr->td->status = BDWrap|BDLast; + } + + /* select port pins */ + io = ioplock(); + io->pbdir |= I2CSDA | I2CSCL; + io->pbodr |= I2CSDA | I2CSCL; + io->pbpar |= I2CSDA | I2CSCL; + iopunlock(); + + /* explicitly initialise parameters, because InitRxTx can't be used (see i2c/spi relocation errata) */ + sp = ctlr->sp; + sp->rbase = PADDR(ctlr->rd); + sp->tbase = PADDR(ctlr->td); + sp->rfcr = 0x18; + sp->tfcr = 0x18; + sp->mrblr = Bufsize; + sp->rstate = 0; + sp->rptr = 0; + sp->rbptr = sp->rbase; + sp->rcnt = 0; + sp->tstate = 0; + sp->tbptr = sp->tbase; + sp->tptr = 0; + sp->tcnt = 0; + eieio(); + + i2c->i2com = I2CM; + i2c->i2mod = 0; /* normal mode */ + i2c->i2add = 0; + + emin = Freq; + dmax = (m->cpuhz/Freq)/2-3; + for(d=0; d < dmax; d++){ + for(p=3; p>=0; p--){ + f = (m->cpuhz>>(p+2))/(2*(d+3)); + e = Freq - f; + if(e < 0) + e = -e; + if(e < emin){ + emin = e; + i2c->i2brg = d; + i2c->i2mod = (i2c->i2mod&~PDIV)|((3-p)<<1); /* set PDIV */ + } + } + } + //print("i2brg=%d i2mod=#%2.2ux\n", i2c->i2brg, i2c->i2mod); + setvec(VectorCPIC+0x10, interrupt, i2ctlr); +} + +enum { + Idling, + Done, + Busy, + Sending, + Recving, +}; + +static void +interrupt(Ureg*, void *arg) +{ + int events; + Ctlr *ctlr; + I2C *i2c; + + ctlr = arg; + i2c = ctlr->i2c; + events = i2c->i2cer; + eieio(); + i2c->i2cer = events; + if(events & (BSY|TXE)){ + print("I2C#%x\n", events); + if(ctlr->phase != Idling){ + ctlr->phase = Idling; + } + }else{ + if(events & TXB){ + //print("i2c: xmt %d %4.4ux %4.4ux\n", ctlr->phase, ctlr->td->status, ctlr->td[1].status); + if(ctlr->phase == Sending){ + ctlr->phase = Done; + } + } + if(events & RXB){ + //print("i2c: rcv %d %4.4ux %d\n", ctlr->phase, ctlr->rd->status, ctlr->rd->length); + if(ctlr->phase == Recving){ + ctlr->phase = Done; + } + } + } +} + +static int +done(void *a) +{ + return ((Ctlr*)a)->phase < Busy; +} + +static void +i2cwait(Ctlr *ctlr) +{ + int i; + + ctlr->timeout = 0; + i = 0; + while(!done(ctlr)){ + if(predawn){ + if(++i > 100){ + ctlr->phase = Done; + ctlr->timeout = 1; + return; + } + delay(1); + interrupt(nil, ctlr); + } + } +} + +long +i2csend(int addr, void *buf, long n) +{ + Ctlr *ctlr; + int i, p, s; + + ctlr = i2ctlr; + if(n > MaxIO) + return -1; + i = 1; + ctlr->txbuf[0] = addr & ~1; + if(addr & 1){ + ctlr->txbuf[1] = addr>>8; + i++; + } + memmove(ctlr->txbuf+i, buf, n); + DCFLUSH(ctlr->txbuf, Bufsize); + ctlr->phase = Sending; + ctlr->rd->status = BDEmpty|BDWrap|BDInt; + ctlr->td->addr = PADDR(ctlr->txbuf); + ctlr->td->length = n+i; + ctlr->td->status = BDReady|BDWrap|BDLast|BDInt; + enable(); + ctlr->i2c->i2com = STR|I2CM; + eieio(); + i2cwait(ctlr); + disable(); + p = ctlr->phase; + s = ctlr->td->status; + if(s & BDReady || s & TxERR || p != Done || ctlr->timeout) + return -1; + return n; +} + +long +i2crecv(int addr, void *buf, long n) +{ + Ctlr *ctlr; + int p, s, flag; + BD *td; + long nr; + + ctlr = i2ctlr; + if(n > MaxIO) + return -1; + ctlr->txbuf[0] = addr|Rbit; + if(addr & 1){ /* special select sequence */ + ctlr->addr[0] = addr &~ 1; + ctlr->addr[1] = addr>>8; + } + DCFLUSH(ctlr->txbuf, Bufsize); + DCFLUSH(ctlr->rxbuf, Bufsize); + ctlr->phase = Recving; + ctlr->rd->addr = PADDR(ctlr->rxbuf); + ctlr->rd->status = BDEmpty|BDWrap|BDInt; + flag = 0; + td = ctlr->td; + td[1].status = 0; + if(addr & 1){ + /* special select sequence */ + td->addr = PADDR(ctlr->addr); + td->length = 2; + /* td->status made BDReady below */ + td++; + flag = TxS; + } + td->addr = PADDR(ctlr->txbuf); + td->length = n+1; + td->status = BDReady|BDWrap|BDLast | flag; /* not BDInt: leave that to receive */ + if(flag) + ctlr->td->status = BDReady; + enable(); + ctlr->i2c->i2com = STR|I2CM; + eieio(); + i2cwait(ctlr); + disable(); + p = ctlr->phase; + s = ctlr->td->status; + if(flag) + s |= ctlr->td[1].status; + nr = ctlr->rd->length; + if(nr > n) + nr = n; /* shouldn't happen */ + if(s & TxERR || s & BDReady || p != Done || ctlr->rd->status & BDEmpty || ctlr->timeout) + return -1; + memmove(buf, ctlr->rxbuf, nr); + return nr; +} diff --git a/os/boot/rpcg/initfads.c b/os/boot/rpcg/initfads.c new file mode 100644 index 00000000..eed7c319 --- /dev/null +++ b/os/boot/rpcg/initfads.c @@ -0,0 +1,187 @@ +/* + * Called from l.s in EPROM to set up a minimal working environment. + * Since there is no DRAM yet, and therefore no stack, no function + * calls may be made from sysinit0, and values can't be stored, + * except to INTMEM. Global values are accessed by offset from SB, + * which has been set by l.s to point into EPROM. + * + * This is FADS-specific in CS assignment and access of the FADS BCSR + * to discover memory size and speed. + */ + +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "archfads.h" + +#define MB (1024*1024) + +enum { + UPMSIZE = 64, /* memory controller instruction RAM */ + SPEED = 50, /* maximum memory clock in MHz */ + SDRAMSIZE = 4*MB, + + /* mcr */ + WriteRAM = 0<<30, + ReadRAM = 1<<30, + ExecRAM = 2<<30, + + SelUPMA = 0<<23, + SelUPMB = 1<<23, + + Once = 1<<8, +}; + +/* + * mpc8bug uses the following for 60ns EDO DRAMs 32-50MHz + */ +static ulong upma50[UPMSIZE] = { + 0x8FFFEC24, 0xFFFEC04, 0xCFFEC04, 0xFFEC04, + 0xFFEC00, 0x37FFEC47, 0xFFFFFFFF, 0xFFFFFFFF, + 0x8FFFEC24, 0xFFFEC04, 0x8FFEC04, 0xFFEC0C, + 0x3FFEC00, 0xFFEC44, 0xFFCC08, 0xCFFCC44, + 0xFFEC0C, 0x3FFEC00, 0xFFEC44, 0xFFCC00, + 0x3FFFC847, 0x3FFFEC47, 0xFFFFFFFF, 0xFFFFFFFF, + 0x8FAFCC24, 0xFAFCC04, 0xCAFCC00, 0x11BFCC47, + 0xC0FFCC84, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x8FAFCC24, 0xFAFCC04, 0xCAFCC00, 0x3AFCC4C, + 0xCAFCC00, 0x3AFCC4C, 0xCAFCC00, 0x3AFCC4C, + 0xCAFCC00, 0x33BFCC4F, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xC0FFCC84, 0xFFCC04, 0x7FFCC04, 0x3FFFCC06, + 0xFFFFCC85, 0xFFFFCC05, 0xFFFFCC05, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x33FFCC07, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +}; + +/* + * the FADS manual table 3-7 suggests the following for 60ns EDO DRAMs at 20MHz + */ +static ulong upma20[UPMSIZE] = { + 0x8FFFCC04, 0x08FFCC00, 0x33FFCC47, ~0, ~0, ~0, ~0, ~0, + [0x08] 0x8FFFCC04, 0x08FFCC08, 0x08FFCC08, 0x08FFCC08, 0x08FFCC00, 0x3FFFCC47, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, + [0x18] 0x8FEFCC00, 0x39BFCC47, ~0, ~0, ~0, ~0, ~0, ~0, + [0x20] 0x8FEFCC00, 0x09AFCC48, 0x09AFCC48, 0x08AFCC48, 0x39BFCC47, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, + [0x30] 0x80FFCC84, 0x17FFCC04, 0xFFFFCC86, 0xFFFFCC05, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, + [0x3C] 0x33FFCC07, ~0, ~0, ~0, +}; + +void +sysinit0(int inrom) +{ + ulong *upm, *bcsr; + IMM *io; + int i, mb; + + io = (IMM*)INTMEM; /* running before maps, no KADDR */ + + /* system interface unit initialisation, FADS manual table 3-2, except as noted */ + io->siumcr = 0x01012440; + io->sypcr = 0xFFFFFF88; + io->tbscrk = KEEP_ALIVE_KEY; + io->tbscr = 0xC3; /* time base enabled */ + io->rtcsck = KEEP_ALIVE_KEY; + io->rtcsc = 0xC1; /* don't FRZ, real-time clock enabled */ + io->rtcsck = ~KEEP_ALIVE_KEY; + io->piscrk = KEEP_ALIVE_KEY; + io->piscr = 0x82; + + io->memc[BCSRCS].option = 0xFFFF8110; /* 32k block, all types access, CS early negate, 1 ws */ + io->memc[BCSRCS].base = BCSRMEM | 1; /* base, 32-bit port, no parity, GPCM */ + + io->memc[BOOTCS].base = FLASHMEM | 1; + io->memc[BOOTCS].option = 0xFF800D54; + + if(!inrom) + return; /* can't initialise DRAM controller from DRAM */ + + bcsr = (ulong*)BCSRMEM; +// bcsr[1] &= ~DisableDRAM; + /* could check DRAM speed here; assume 60ns */ + switch((bcsr[2]>>23)&3){ + default: return; /* can't happen; for the compiler */ + case 0: mb = 4; break; + case 1: mb = 32; break; + case 2: mb = 16; break; + case 3: mb = 8; break; + } + + upm = upma50; + for(i=0; imdr = upm[i]; + io->mcr = WriteRAM | SelUPMA | i; + } + io->mptpr = 0x0400; + if(SPEED >= 32) + io->mamr = (0x9C<<24) | 0xA21114; /* 50MHz BRGCLK; FADS manual says 0xC0, mpc8bug sets 0x9C */ + else if(SPEED >= 20) + io->mamr = (0x60<<24) | 0xA21114; /* 25MHz BRGCLK */ + else + io->mamr = (0x40<<24) | 0xA21114; /* 16.67MHz BRGCLK */ + io->memc[DRAM1].option = ~((mb<<20)-1)|0x0800; /* address mask, SAM=1 */ + io->memc[DRAM1].base = 0 | 0x81; /* base at 0, 32-bit port size, no parity, UPMA */ +} + +/* + * the FADS manual table 3-9's suggestion for MB811171622A-100 32+MHz-50MHz + */ +static ulong upmb50[UPMSIZE] = { + [0x00] 0x1F07FC04, 0xEEAEFC04, 0x11ADFC04, 0xEFBBBC00, 0x1FF77C47, + [0x05] 0x1FF77C34, 0xEFEABC34, 0x1FB57C35, + [0x08] 0x1F07FC04, 0xEEAEFC04, 0x10ADFC04, 0xF0AFFC00, 0xF0AFFC00, 0xF1AFFC00, 0xEFBBBC00, 0x1FF77C47, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, + [0x18] 0x1F27FC04, 0xEEAEBC00, 0x01B93C04, 0x1FF77C47, ~0, ~0, ~0, ~0, + [0x20] 0x1F07FC04, 0xEEAEBC00, 0x10AD7C00, 0xF0AFFC00, 0xF0AFFC00, 0xE1BBBC04, 0x1FF77C47, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, + [0x30] 0x1FF5FC84, 0xFFFFFC04, 0xFFFFFC04, 0xFFFFFC04, 0xFFFFFC84, 0xFFFFFC07, ~0, ~0, ~0, ~0, ~0, ~0, + [0x3C] 0x7FFFFC07, ~0, ~0, ~0, +}; + +/* + * the FADS manual table 3-8's suggestion for MB811171622A-100 up to 32MHz + */ +static ulong upmb32[UPMSIZE] = { + [0x00] 0x126CC04, 0xFB98C00, 0x1FF74C45, ~0, ~0, + [0x05] 0x1FE77C34, 0xEFAABC34, 0x1FA57C35, + [0x08] 0x0026FC04, 0x10ADFC00, 0xF0AFFC00, 0xF1AFFC00, 0xEFBBBC00, 0x1FF77C45, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, + [0x18] 0x0E26BC04, 0x01B93C00, 0x1FF77C45, ~0, ~0, ~0, ~0, ~0, + [0x20] 0x0E26BC00, 0x10AD7C00, 0xF0AFFC00, 0xF0AFFC00, 0xE1BBBC04, 0x1FF77C45, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, + [0x30] 0x1FF5FC84, 0xFFFFFC04, 0xFFFFFC84, 0xFFFFFC05, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, + [0x3C] 0x7FFFFC07, ~0, ~0, ~0, +}; + +/* + * optionally called by archfads.c:/^archinit to initialise access to SDRAM + */ +void +sdraminit(ulong base) +{ + ulong *upm; + IMM *io; + int i; + + io = (IMM*)INTMEM; /* running before maps, no KADDR */ + if(SPEED > 32) + upm = upmb50; + else + upm = upmb32; + for(i=0; imdr = upm[i]; + io->mcr = WriteRAM | SelUPMB | i; + } + io->memc[SDRAM].option = ~(SDRAMSIZE-1)|0x0A00; /* address mask, SAM=1, G5LS=1 */ + io->memc[SDRAM].base = base | 0xC1; + if(SPEED > 32){ + io->mbmr = 0xD0802114; /* 50MHz BRGCLK */ + io->mar = 0x88; + }else{ + io->mbmr = 0x80802114; /* 32MHz BRGCLK */ + io->mar = 0x48; + } + io->mcr = ExecRAM | SelUPMB | (SDRAM<<13) | Once | 5; /* run MRS command in locations 5-8 of UPMB */ + io->mbmr = (io->mbmr & ~0xF) | 8; + io->mcr = ExecRAM | SelUPMB | (SDRAM<<13) | Once | 0x30; /* run refresh sequence */ + io->mbmr = (io->mbmr & ~0xF) | 4; /* 4-beat refresh bursts */ +} diff --git a/os/boot/rpcg/initpaq.c b/os/boot/rpcg/initpaq.c new file mode 100644 index 00000000..d10d92ce --- /dev/null +++ b/os/boot/rpcg/initpaq.c @@ -0,0 +1,101 @@ +/* + * Called from l.s in EPROM to set up a minimal working environment. + * Since there is no DRAM yet, and therefore no stack, no function + * calls may be made from sysinit0, and values can't be stored, + * except to INTMEM. Global values are accessed by offset from SB, + * which has been set by l.s to point into EPROM. + * + * This is PowerPAQ-specific: + * - assumes 8mbytes + * - powerpaq CS assignment + */ + +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "archpaq.h" + +#define MB (1024*1024) + +enum { + DRAMSIZE = 8*MB, + FLASHSIZE = 8*MB, + + UPMSIZE = 64, /* memory controller instruction RAM */ + SPEED = 50, /* maximum memory clock in MHz */ + + /* mcr */ + WriteRAM = 0<<30, + ReadRAM = 1<<30, + ExecRAM = 2<<30, + + SelUPMA = 0<<23, + SelUPMB = 1<<23, + + Once = 1<<8, +}; + +/* + * mpc8bug uses the following for 60ns EDO DRAMs 32-50MHz + */ +static ulong upmb50[UPMSIZE] = { + 0x8FFFEC24, 0xFFFEC04, 0xCFFEC04, 0xFFEC04, + 0xFFEC00, 0x37FFEC47, 0xFFFFFFFF, 0xFFFFFFFF, + 0x8FFFEC24, 0xFFFEC04, 0x8FFEC04, 0xFFEC0C, + 0x3FFEC00, 0xFFEC44, 0xFFCC08, 0xCFFCC44, + 0xFFEC0C, 0x3FFEC00, 0xFFEC44, 0xFFCC00, + 0x3FFFC847, 0x3FFFEC47, 0xFFFFFFFF, 0xFFFFFFFF, + 0x8FAFCC24, 0xFAFCC04, 0xCAFCC00, 0x11BFCC47, + 0xC0FFCC84, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x8FAFCC24, 0xFAFCC04, 0xCAFCC00, 0x3AFCC4C, + 0xCAFCC00, 0x3AFCC4C, 0xCAFCC00, 0x3AFCC4C, + 0xCAFCC00, 0x33BFCC4F, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xC0FFCC84, 0xFFCC04, 0x7FFCC04, 0x3FFFCC06, + 0xFFFFCC85, 0xFFFFCC05, 0xFFFFCC05, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x33FFCC07, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +}; + +void +sysinit0(int inrom) +{ + ulong *upm; + IMM *io; + int i; + + io = (IMM*)INTMEM; /* running before maps, no KADDR */ + + /* system interface unit initialisation, FADS manual table 3-2, except as noted */ + io->siumcr = 0x01012440; + io->sypcr = 0xFFFFFF88; + io->tbscrk = KEEP_ALIVE_KEY; + io->tbscr = 0xC3; /* time base enabled */ + io->rtcsck = KEEP_ALIVE_KEY; + io->rtcsc = 0xC1; /* don't FRZ, real-time clock enabled */ + io->rtcsck = ~KEEP_ALIVE_KEY; + io->piscrk = KEEP_ALIVE_KEY; + io->piscr = 0x82; + + io->memc[BOOTCS].base = FLASHMEM | 1; + io->memc[BOOTCS].option = ~(FLASHSIZE-1)|(1<<8)|(2<<4); /* mask, BIH, 2 wait states */ + + if(!inrom) + return; /* can't initialise DRAM controller from DRAM */ + + /* could check DRAM speed here; assume 60ns */ + /* could probe DRAM for size here; assume DRAMSIZE */ + io->mptpr = 0x400; /* powerpaq flash has 0x1000 */ + io->mbmr = (0xC0<<24) | 0xA21114; /* 50MHz BRGCLK */ + upm = upmb50; + for(i=0; imdr = upm[i]; + io->mcr = WriteRAM | SelUPMB | i; + } + io->memc[DRAM1].option = ~(DRAMSIZE-1)|0x0800; /* address mask, SAM=1 */ + io->memc[DRAM1].base = 0 | 0xC1; /* base at 0, 32-bit port size, no parity, UPMB */ +} diff --git a/os/boot/rpcg/initrpcg.c b/os/boot/rpcg/initrpcg.c new file mode 100644 index 00000000..3c69cdd9 --- /dev/null +++ b/os/boot/rpcg/initrpcg.c @@ -0,0 +1,91 @@ + +/* + * Called from l.s in EPROM to set up a minimal working environment. + * Since there is no DRAM yet, and therefore no stack, no function + * calls may be made from sysinit, and values can't be stored, + * except to INTMEM. Global values are accessed by offset from SB, + * which has been set by l.s to point into EPROM. + */ + +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "archrpcg.h" + +#define MB (1024*1024) + +enum { + UPMSIZE = 64, /* memory controller instruction RAM */ + DRAMSIZE = 16*MB, + FLASHSIZE = 4*MB, + + WriteRAM = 0<<30, + ReadRAM = 1<<30, + ExecRAM = 2<<30, + + SelUPMA = 0<<23, + SelUPMB = 1<<23, +}; +/* RPCG values for RPXLite AW */ +static ulong upma50[UPMSIZE] = { + 0xCFFFCC24, 0x0FFFCC04, 0x0CAFCC04, 0x03AFCC08, + 0x3FBFCC27, 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, + 0xCFFFCC24, 0x0FFFCC04, 0x0CAFCC84, 0x03AFCC88, + 0x3FBFCC27, 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, + 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, + 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, + 0xCFFFCC24, 0x0FFFCC04, 0x0CFFCC04, 0x03FFCC00, + 0x3FFFCC27, 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, + 0xCFFFCC24, 0x0FFFCC04, 0x0CFFCC84, 0x03FFCC84, + 0x0CFFCC00, 0x33FFCC27, 0xFFFFCC25, 0xFFFFCC25, + 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, + 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, + 0xC0FFCC24, 0x03FFCC24, 0x0FFFCC24, 0x0FFFCC24, + 0x3FFFCC27, 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, + 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, + 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, 0xFFFFCC25, +}; + +void +sysinit0(int inrom) +{ + ulong *upm; + IMM *io; + int i; + + io = (IMM*)INTMEM; /* running before maps, no KADDR */ +// io->siumcr = 0x01012440; +// io->sypcr = 0xFFFFFF88; + io->tbscrk = KEEP_ALIVE_KEY; + io->tbscr = 0xC3; + io->rtcsck = KEEP_ALIVE_KEY; + io->rtcsc = 0xC1; + io->rtcsck = ~KEEP_ALIVE_KEY; + io->piscrk = KEEP_ALIVE_KEY; + io->piscr = 0x82; +return; + io->memc[BCSRCS].option = 0xFFFF8910; /* 32k block, all types access, CSNT, CS early negate, burst inhibit, 1 ws */ + io->memc[BCSRCS].base = BCSRMEM | 1; /* base, 32-bit port, no parity, GPCM */ + +// io->memc[BOOTCS].base = FLASHMEM | 0x801; /* base, 16 bit port */ +// io->memc[BOOTCS].option = ~(FLASHSIZE-1)|(1<<8)|(4<<4); /* mask, BIH, 4 wait states */ + + if(1||!inrom) + return; /* can't initialise DRAM controller from DRAM */ + + /* TO DO: could check DRAM size and speed now */ + + upm = upma50; + for(i=0; imdr = upm[i]; + io->mcr = WriteRAM | SelUPMA | i; + } + io->mptpr = 0x0800; /* divide by 8 */ + io->mamr = (0x58<<24) | 0xA01430; /* 40MHz BRGCLK */ + io->memc[DRAM1].option = ~(DRAMSIZE-1)|0x0E00; /* address mask, SAM=1, G5LA/S=3 */ + io->memc[DRAM1].base = 0 | 0x81; /* base at 0, 32-bit port size, no parity, UPMA */ +} diff --git a/os/boot/rpcg/io.h b/os/boot/rpcg/io.h new file mode 100644 index 00000000..8ae1d5b3 --- /dev/null +++ b/os/boot/rpcg/io.h @@ -0,0 +1,463 @@ +enum +{ + /* software interrupt vectors (SIU and CPM) */ + VectorPIC= 0, /* level 0 to level 7, assigned by software */ + CPIClevel= 4, + VectorIRQ= VectorPIC+8, /* IRQ0 to IRQ7 */ + VectorCPIC= VectorIRQ+8, /* 32 CPM interrupts: 0 (error) to 0x1F (PC15) */ +}; + +enum +{ + BUSUNKNOWN = 0, +}; + +/* + * Buffer Descriptors and IO Rings + */ + +typedef struct BD BD; +struct BD { + ushort status; + ushort length; + ulong addr; +}; + +BD* bdalloc(int); +void bdfree(BD*, int); + +enum { + /* Rx BDs, bits common to all protocols */ + BDEmpty= 1<<15, + BDWrap= 1<<13, + BDInt= 1<<12, + BDLast= 1<<11, + BDFirst= 1<<10, + + /* Tx BDs */ + BDReady= 1<<15, + /* BDWrap, BDInt, BDLast */ +}; + +typedef struct Ring Ring; +struct Ring { + BD* rdr; /* receive descriptor ring */ + void* rrb; /* receive ring buffers */ + int rdrx; /* index into rdr */ + int nrdre; /* length of rdr */ + + BD* tdr; /* transmit descriptor ring */ + Block** txb; /* corresponding transmit ring buffers */ + int tdrh; /* host index into tdr */ + int tdri; /* interface index into tdr */ + int ntdre; /* length of tdr */ + int ntq; /* pending transmit requests */ +}; + +#define NEXT(x, l) (((x)+1)%(l)) +#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1) +#define HOWMANY(x, y) (((x)+((y)-1))/(y)) +#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) + +int ioringinit(Ring*, int, int, int); + +/* + * CPM + */ +enum { + /* commands */ + InitRxTx = 0, + InitRx = 1, + InitTx = 2, + EnterHunt= 3, + StopTx= 4, + GracefulStopTx = 5, + InitIDMA = 5, + RestartTx = 6, + CloseRxBD = 7, + SetGroupAddr = 8, + SetTimer = 8, + GCITimeout = 9, + GCIAbort = 10, + StopIDMA = 11, + StartDSP = 12, + ArmIDMA = 13, + InitDSP = 13, + USBCmd = 15, + + /* channel IDs */ + SCC1ID= 0, + USBID= 0, + I2CID= 1, + IDMA1ID= 1, + SCC2ID= 4, + SPIID= 5, + IDMA2ID= 5, + TIMERID= 5, + SCC3ID= 8, + SMC1ID= 9, + DSP1ID=9, + SCC4ID= 12, + SMC2ID= 13, + DSP2ID= 13, + + BaudEnable = 1<<16, + + /* sicr */ + CLK1 = 4, /* SCC1,2 */ + CLK2 = 5, + CLK3 = 6, + CLK4 = 7, + CLK5 = CLK1, /* SCC3,4 */ + CLK6 = CLK2, + CLK7 = CLK3, + CLK8 = CLK4, +}; + +void cpmop(int, int, int); +#define ioplock() (m->iomem) +#define iopunlock() + +/* + * the structures below follow hardware/firmware layouts in the 8xx manuals: + * mind the data types, offsets and alignment + */ + +/* + * basic IO controller parameters (SMC and SCC) + */ +typedef struct IOCparam IOCparam; +struct IOCparam { + ushort rbase; + ushort tbase; + uchar rfcr; + uchar tfcr; + ushort mrblr; + ulong rstate; + ulong rptr; + ushort rbptr; + ushort rcnt; + ulong rtmp; + ulong tstate; + ulong tptr; + ushort tbptr; + ushort tcnt; + ulong ttmp; +}; + +typedef struct SCCparam SCCparam; +struct SCCparam { + IOCparam; + ulong rcrc; + ulong tcrc; +}; + +typedef struct SCC SCC; +struct SCC { + ulong gsmrl; + ulong gsmrh; + ushort psmr; + uchar rsvscc0[2]; + ushort todr; + ushort dsr; + ushort scce; + uchar rsvscc1[2]; + ushort sccm; + uchar rsvscc3; + uchar sccs; + ushort irmode; + ushort irsip; +}; + +typedef struct SMC SMC; +struct SMC { + uchar pad1[2]; + ushort smcmr; + uchar pad2[2]; + uchar smce; + uchar pad3[3]; + uchar smcm; + uchar pad4[5]; +}; + +typedef struct SPI SPI; +struct SPI { + ushort spmode; + uchar res1[4]; + uchar spie; + uchar res2[3]; + uchar spim; + uchar res3[2]; + uchar spcom; + uchar res4[10]; +}; + +typedef struct USB USB; +struct USB { /* 823 only */ + uchar usmod; + uchar usadr; + uchar uscom; + uchar rsvu1; + ushort usep[4]; + uchar rsvu2[4]; + ushort usber; + uchar rsvu3[2]; + ushort usbmr; + uchar rsvu4; + uchar usbs; + uchar rsvu5[8]; +}; + +typedef struct IMM IMM; +struct IMM { + struct { /* general SIU */ + ulong siumcr; + ulong sypcr; + uchar rsv0[0xE-0x8]; + ushort swsr; + ulong sipend; + ulong simask; + ulong siel; + uchar sivec; + uchar padv[3]; + ulong tesr; + uchar rsv1[0x30-0x24]; + ulong sdcr; + uchar rsv2[0x80-0x34]; + }; + struct { /* PCMCIA */ + struct { + ulong base; + ulong option; + } pcmr[8]; + uchar rsv3[0xe0-0xc0]; + ulong pgcra; + ulong pgcrb; + ulong pscr; + uchar rsv4[0xf0-0xec]; + ulong pipr; + uchar rsv5[4]; + ulong per; + uchar rsv6[4]; + }; + struct { /* MEMC */ + struct { + ulong base; + ulong option; + } memc[8]; + uchar rsv7a[0x24]; + ulong mar; + ulong mcr; + uchar rsv7b[4]; + ulong mamr; + ulong mbmr; + ushort mstat; + ushort mptpr; + ulong mdr; + uchar rsv7c[0x80]; + }; + struct { /* system integration timers */ + ushort tbscr; + uchar rsv8a[2]; + ulong tbrefu; + ulong tbrefl; + uchar rsv8b[0x14]; + ushort rtcsc; + uchar rsv8c[2]; + ulong rtc; + ulong rtsec; + ulong rtcal; + uchar rsv8d[0x10]; + ushort piscr; + ushort rsv8e; + ulong pitc; + ulong pitr; + uchar rsv8f[0x34]; + }; + struct { /* 280: clocks and resets */ + ulong sccr; + ulong plprcr; + ulong rsr; + uchar rsv9[0x300-0x28c]; + }; + struct { /* 300: system integration timers keys */ + ulong tbscrk; + ulong tbrefuk; + ulong tbreflk; + ulong tbk; + uchar rsv10a[0x10]; + ulong rtcsck; + ulong rtck; + ulong rtseck; + ulong rtcalk; + uchar rsv10b[0x10]; + ulong piscrk; + ulong pitck; + uchar rsv10c[0x38]; + }; + struct { /* 380: clocks and resets keys */ + ulong sccrk; + ulong plprcrk; + ulong rsrk; + uchar rsv11[0x800-0x38C]; + }; + struct { /* 800: video controller */ + ushort vccr; + ushort pad11a; + uchar vsr; + uchar pad11b; + uchar vcmr; + uchar pad11c; + ulong vbcb; + ulong pad11d; + ulong vfcr0; + ulong vfaa0; + ulong vfba0; + ulong vfcr1; + ulong vfaa1; + ulong vfba1; + uchar rsv11a[0x840-0x828]; + }; + struct { /* 840: LCD */ + ulong lccr; + ulong lchcr; + ulong lcvcr; + ulong rsv11b; + ulong lcfaa; + ulong lcfba; + uchar lcsr; + uchar rsv11c[0x860-0x859]; + }; + struct { /* 860: I2C */ + uchar i2mod; + uchar rsv12a[3]; + uchar i2add; + uchar rsv12b[3]; + uchar i2brg; + uchar rsv12c[3]; + uchar i2com; + uchar rsv12d[3]; + uchar i2cer; + uchar rsv12e[3]; + uchar i2cmr; + uchar rsv12[0x900-0x875]; + }; + struct { /* 900: DMA */ + uchar rsv13[4]; + ulong sdar; + uchar sdsr; + uchar pad1[3]; + uchar sdmr; + uchar pad2[3]; + uchar idsr1; + uchar pad3[3]; + uchar idmr1; + uchar pad4[3]; + uchar idsr2; + uchar pad5[3]; + uchar idmr2; + uchar pad6[0x930-0x91D]; + }; + struct { /* CPM interrupt control */ + ushort civr; + uchar pad7[0x940-0x932]; + ulong cicr; + ulong cipr; + ulong cimr; + ulong cisr; + }; + struct { /* input/output port */ + ushort padir; + ushort papar; + ushort paodr; + ushort padat; + uchar pad8[8]; + ushort pcdir; + ushort pcpar; + ushort pcso; + ushort pcdat; + ushort pcint; + uchar pad9[6]; + ushort pddir; + ushort pdpar; + ushort rsv14a; + ushort pddat; + uchar rsv14[0x980-0x978]; + }; + struct { /* CPM timers */ + ushort tgcr; + uchar rsv15a[0x990-0x982]; + ushort tmr1; + ushort tmr2; + ushort trr1; + ushort trr2; + ushort tcr1; + ushort tcr2; + ushort tcn1; + ushort tcn2; + ushort tmr3; + ushort tmr4; + ushort trr3; + ushort trr4; + ushort tcr3; + ushort tcr4; + ushort tcn3; + ushort tcn4; + ushort ter1; + ushort ter2; + ushort ter3; + ushort ter4; + uchar rsv15[0x9C0-0x9B8]; + }; + struct { /* CPM */ + ushort cpcr; + uchar res0[2]; + ushort rccr; + uchar res1; + uchar rmds; + uchar res2a[4]; + ushort rctr1; + ushort rctr2; + ushort rctr3; + ushort rctr4; + uchar res2[2]; + ushort rter; + uchar res3[2]; + ushort rtmr; + uchar rsv16[0x9F0-0x9DC]; + }; + union { /* BRG */ + struct { + ulong brgc1; + ulong brgc2; + ulong brgc3; + ulong brgc4; + }; + ulong brgc[4]; + }; + uchar skip0[0xAB2-0xA00]; /* USB, SCC, SMC, SPI: address using cpmdev(CP...)->regs */ + struct { /* PIP */ + ushort pipc; /* not 823 */ + ushort ptpr; /* not 823 */ + ulong pbdir; + ulong pbpar; + uchar pad10[2]; + ushort pbodr; + ulong pbdat; + uchar pad11[0xAE0-0xAC8]; + }; + struct { /* SI */ + ulong simode; + uchar sigmr; + uchar pad12; + uchar sistr; + uchar sicmr; + uchar pad13[4]; + ulong sicr; + ulong sirp; + uchar pad14[0xB00-0xAF4]; + }; + ulong vcram[64]; + ushort siram[256]; + ushort lcdmap[256]; +}; diff --git a/os/boot/rpcg/ip.h b/os/boot/rpcg/ip.h new file mode 100644 index 00000000..a39b5b4b --- /dev/null +++ b/os/boot/rpcg/ip.h @@ -0,0 +1,98 @@ +typedef struct Udphdr Udphdr; +struct Udphdr +{ + uchar d[6]; /* Ethernet destination */ + uchar s[6]; /* Ethernet source */ + uchar type[2]; /* Ethernet packet type */ + + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + + /* Udp pseudo ip really starts here */ + uchar ttl; + uchar udpproto; /* Protocol */ + uchar udpplen[2]; /* Header plus data length */ + uchar udpsrc[4]; /* Ip source */ + uchar udpdst[4]; /* Ip destination */ + uchar udpsport[2]; /* Source port */ + uchar udpdport[2]; /* Destination port */ + uchar udplen[2]; /* data length */ + uchar udpcksum[2]; /* Checksum */ +}; + +typedef struct Etherhdr Etherhdr; +struct Etherhdr +{ + uchar d[6]; + uchar s[6]; + uchar type[2]; + + /* Now we have the ip fields */ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar ttl; /* Time to live */ + uchar proto; /* Protocol */ + uchar cksum[2]; /* Header checksum */ + uchar src[4]; /* Ip source */ + uchar dst[4]; /* Ip destination */ +}; + +enum +{ + IP_VER = 0x40, + IP_HLEN = 0x05, + UDP_EHSIZE = 22, + UDP_PHDRSIZE = 12, + UDP_HDRSIZE = 20, + ETHER_HDR = 14, + IP_UDPPROTO = 17, + ET_IP = 0x800, + Bcastip = 0xffffffff, + BPportsrc = 68, + BPportdst = 67, + TFTPport = 69, + Timeout = 5000, /* milliseconds */ + Bootrequest = 1, + Bootreply = 2, + Tftp_READ = 1, + Tftp_WRITE = 2, + Tftp_DATA = 3, + Tftp_ACK = 4, + Tftp_ERROR = 5, + Segsize = 512, + TFTPSZ = Segsize+10, +}; + +typedef struct Bootp Bootp; +struct Bootp +{ + uchar op; /* opcode */ + uchar htype; /* hardware type */ + uchar hlen; /* hardware address len */ + uchar hops; /* hops */ + uchar xid[4]; /* a random number */ + uchar secs[2]; /* elapsed snce client started booting */ + uchar pad[2]; + uchar ciaddr[4]; /* client IP address (client tells server) */ + uchar yiaddr[4]; /* client IP address (server tells client) */ + uchar siaddr[4]; /* server IP address */ + uchar giaddr[4]; /* gateway IP address */ + uchar chaddr[16]; /* client hardware address */ + char sname[64]; /* server host name (optional) */ + char file[128]; /* boot file name */ + char vend[128]; /* vendor-specific goo */ +}; + +typedef struct Netaddr Netaddr; +struct Netaddr +{ + ulong ip; + ushort port; + char ea[Eaddrlen]; +}; diff --git a/os/boot/rpcg/l.s b/os/boot/rpcg/l.s new file mode 100644 index 00000000..a1022ff3 --- /dev/null +++ b/os/boot/rpcg/l.s @@ -0,0 +1,388 @@ +#include "mem.h" + +/* special instruction definitions */ +#define BDNE BC 0,2, +#define BDNZ BC 16,0, +#define NOOP OR R0,R0,R0 + +/* + * common ppc special purpose registers + */ +#define DSISR 18 +#define DAR 19 /* Data Address Register */ +#define DEC 22 /* Decrementer */ +#define SRR0 26 /* Saved Registers (exception) */ +#define SRR1 27 +#define SPRG0 272 /* Supervisor Private Registers */ +#define SPRG1 273 +#define SPRG2 274 +#define SPRG3 275 +#define TBRU 269 /* Time base Upper/Lower (Reading) */ +#define TBRL 268 +#define TBWU 285 /* Time base Upper/Lower (Writing) */ +#define TBWL 284 +#define PVR 287 /* Processor Version */ + +/* + * mpc82x-specific special purpose registers of interest here + */ +#define EIE 80 +#define EID 81 +#define NRI 82 +#define IMMR 638 +#define IC_CST 560 +#define IC_ADR 561 +#define IC_DAT 562 +#define DC_CST 568 +#define DC_ADR 569 +#define DC_DAT 570 +#define MI_CTR 784 +#define MI_AP 786 +#define MI_EPN 787 +#define MI_TWC 789 +#define MI_RPN 790 +#define MI_DBCAM 816 +#define MI_DBRAM0 817 +#define MI_DBRAM1 818 +#define MD_CTR 792 +#define M_CASID 793 +#define MD_AP 794 +#define MD_EPN 795 +#define M_TWB 796 +#define MD_TWC 797 +#define MD_RPN 798 +#define M_TW 799 +#define MD_DBCAM 824 +#define MD_DBRAM0 825 +#define MD_DBRAM1 826 + +/* as on 603e, apparently mtmsr needs help in some chip revisions */ +#define WAITMSR SYNC; ISYNC + +/* use of SPRG registers in save/restore */ +#define SAVER0 SPRG0 +#define SAVER1 SPRG1 +#define SAVELR SPRG2 +#define SAVECR SPRG3 + +#define UREGSIZE ((8+32)*4) +#define UREGSPACE (UREGSIZE+8) /* allow for arg to trap, and align */ + +/* + * This code is loaded by the ROM loader at location 0x3000, + * or lives in flash memory at FLASHMEM+0x100 + * Move it to high memory so that it can load the kernel at 0x0000. + */ + +#define LOADCODEBASE 0x3000 /* when downloaded in S records */ +#define FLASHCODEBASE (FLASHMEM+0x20000+0x100) /* when in flash */ + + TEXT start(SB), $-4 + MOVW MSR, R3 + MOVW $(EE|IP|RI), R4 + ANDN R4, R3 + OR $ME, R3 + SYNC + MOVW R3, MSR /* turn off interrupts but enable traps */ + WAITMSR + +/* + * reset the caches and disable them for now + */ + MOVW SPR(IC_CST), R4 /* read and clear */ + MOVW $(5<<25), R4 + MOVW R4, SPR(IC_CST) /* unlock all */ + ISYNC + MOVW $(6<<25), R4 + MOVW R4, SPR(IC_CST) /* invalidate all */ + ISYNC + MOVW $(2<<25), R4 + MOVW R4, SPR(IC_CST) /* disable i-cache */ + ISYNC + + SYNC + MOVW SPR(DC_CST), R4 /* read and clear */ + MOVW $(10<<24), R4 + MOVW R4, SPR(DC_CST) /* unlock all */ + ISYNC + MOVW $(12<<24), R4 + MOVW R4, SPR(DC_CST) /* invalidate all */ + ISYNC + MOVW $(4<<24), R4 + MOVW R4, SPR(DC_CST) /* disable i-cache */ + ISYNC + + MOVW $7, R4 + MOVW R4, SPR(158) /* cancel `show cycle' for normal instruction execution */ + +/* + * set other system configuration values + */ + MOVW SPR(IMMR), R5 /* save initial space pointer */ + MOVW $INTMEM, R4 + MOVW R4, SPR(IMMR) /* set internal memory base */ + MOVW $0xFFFFFF88, R3 + MOVW R3, 4(R4) /* disable watchdog in sypcr */ + MOVW $0x01012440, R3 + MOVW R3, 0(R4) /* siumcr */ + +/* + * system initialisation (init and map DRAM) + */ + MOVW $0, R0 + MOVW $setSB(SB), R2 +#ifndef confrpcg + MOVW $(0xF000<<16), R3 +/*MOVW R0, R3*/ + ANDCC R5, R3 /* initial space is high? */ + BEQ notrom + MOVW $FLASHCODEBASE, R5 /* where $start(SB) actually is now */ + MOVW $start(SB), R4 /* logical start address */ + SUB R4, R5, R6 /* text relocation value */ + MOVW $etext(SB), R7 + SUB R4, R7 + ADD R5, R7 /* data address in ROM */ + MOVW $bdata(SB), R8 + SUB R8, R2 + ADD R7, R2 /* relocate SB: SB' = romdata+(SB-bdata) */ + MOVW $sysinit0(SB), R4 + ADD R6, R4 /* relocate sysinit0's address */ + MOVW R4, CTR + MOVW $inmem(SB), R4 + ADD R6, R4 + MOVW R4, LR /* and the return address */ + BR (CTR) /* call sysinit0 */ + TEXT inmem(SB), $-4 + MOVW $FLASHCODEBASE, R3 + BR cpu0 +notrom: + MOVW $start(SB), R6 + SUB R6, R2 + ADD $LOADCODEBASE, R2 + BL sysinit0(SB) + MOVW $LOADCODEBASE, R3 +#endif + +/* + * cpu 0 + * relocate bootstrap to our link addresses for text and data + * set new PC + */ +cpu0: + MOVW $setSB(SB), R2 /* set correct static base register */ +#ifndef confrpcg + MOVW $start(SB), R4 + MOVW $etext(SB), R5 + SUB R4, R5 + CMP R4, R3 /* already there? */ + BNE copytext + ADD R5, R3 /* start of data image */ +#else + MOVW $etext(SB), R3 +#endif + BR copydata + +copytext: + ADD $3, R5 + SRAW $2, R5 + MOVW R5, CTR + SUB $4, R4 + SUB $4, R3 +copyt: /* copy text */ + MOVWU 4(R3), R5 + MOVWU R5, 4(R4) + BDNZ copyt + ADD $4, R3 + +copydata: + /* copy data */ + MOVW $bdata(SB), R4 + CMP R4, R3 /* already there? */ + BEQ loadkpc + MOVW $edata(SB), R5 + SUB R4, R5 + ADD $3, R5 + SRAW $2, R5 + MOVW R5, CTR + SUB $4, R4 + SUB $4, R3 +copyd: + MOVWU 4(R3), R5 + MOVWU R5, 4(R4) + BDNZ copyd +#endif + + /* load correct PC */ +loadkpc: + MOVW $start1(SB), R3 + MOVW R3, LR + BR (LR) +TEXT start1(SB), $-4 + MOVW $edata(SB), R3 + MOVW $end(SB), R4 + SUBCC R3, R4 + BLE skipz + SRAW $2, R4 + MOVW R4, CTR + SUB $4, R3 + MOVW $0, R0 +zero: + MOVWU R0, 4(R3) + BDNZ zero +skipz: + MOVW $mach0(SB), R1 + MOVW R1, m(SB) + ADD $(MACHSIZE-8), R1 + MOVW $0, R0 + + BL sysinit0(SB) + + BL main(SB) + BR 0(PC) + +TEXT ledx(SB), $0 + + MOVW $BCSRMEM, R8 + MOVW 0(R8), R3 + MOVW $(0x0800<<16), R5 + ANDN R5, R3, R3 + MOVW R3, 0(R8) + BR 0(PC) + +TEXT getmsr(SB), $0 + MOVW MSR, R3 + RETURN + +TEXT putmsr(SB), $0 + SYNC + MOVW R3, MSR + WAITMSR + RETURN + +TEXT eieio(SB), $0 + EIEIO + RETURN + +TEXT idle(SB), $0 + RETURN + +TEXT spllo(SB), $0 + MOVW MSR, R3 + OR $EE, R3, R4 + SYNC + MOVW R4, MSR + WAITMSR + RETURN + +TEXT splhi(SB), $0 + MOVW MSR, R3 + RLWNM $0, R3, $~EE, R4 + SYNC + MOVW R4, MSR + WAITMSR + RETURN + +TEXT splx(SB), $0 + MOVW MSR, R4 + RLWMI $0, R3, $EE, R4 + SYNC + MOVW R4, MSR + WAITMSR + RETURN + +TEXT gettbl(SB), $0 +/* MOVW SPR(TBRL), R3 */ + WORD $0x7c6c42e6 /* mftbl on 8xx series */ + RETURN + +TEXT getpvr(SB), $0 + MOVW SPR(PVR), R3 + RETURN + +TEXT getimmr(SB), $0 + MOVW SPR(IMMR), R3 + RETURN + +TEXT getdec(SB), $0 + MOVW SPR(DEC), R3 + RETURN + +TEXT putdec(SB), $0 + MOVW R3, SPR(DEC) + RETURN + +/* + * save state in Ureg on kernel stack. + * enter with R0 giving the PC from the call to `exception' from the vector. + * on return, SB (R2) has been set, and R3 has the Ureg* + */ +TEXT saveureg(SB), $-4 + SUB $UREGSPACE, R1 + MOVMW R2, 48(R1) /* r2:r31 */ + MOVW $setSB(SB), R2 + MOVW SPR(SAVER1), R4 + MOVW R4, 44(R1) + MOVW SPR(SAVER0), R5 + MOVW R5, 40(R1) + MOVW CTR, R6 + MOVW R6, 36(R1) + MOVW XER, R4 + MOVW R4, 32(R1) + MOVW SPR(SAVECR), R5 /* CR */ + MOVW R5, 28(R1) + MOVW SPR(SAVELR), R6 /* LR */ + MOVW R6, 24(R1) + /* pad at 20(R1) */ + MOVW SPR(SRR0), R4 + MOVW R4, 16(R1) /* old PC */ + MOVW SPR(SRR1), R5 + MOVW R5, 12(R1) + MOVW R0, 8(R1) /* cause/vector, encoded in LR from vector */ + ADD $8, R1, R3 /* Ureg* */ + STWCCC R3, (R1) /* break any pending reservations */ + MOVW $0, R0 /* R0ISZERO */ + BR (LR) + +/* + * restore state from Ureg + * SB (R2) is unusable on return + */ +TEXT restoreureg(SB), $-4 + MOVMW 48(R1), R2 /* r2:r31 */ + /* defer R1 */ + MOVW 40(R1), R0 + MOVW R0, SPR(SAVER0) + MOVW 36(R1), R0 + MOVW R0, CTR + MOVW 32(R1), R0 + MOVW R0, XER + MOVW 28(R1), R0 + MOVW R0, CR /* CR */ + MOVW 24(R1), R0 + MOVW R0, SPR(SAVELR) /* LR */ + /* pad, skip */ + MOVW 16(R1), R0 + MOVW R0, SPR(SRR0) /* old PC */ + MOVW 12(R1), R0 + MOVW R0, SPR(SRR1) /* old MSR */ + /* cause, skip */ + MOVW 44(R1), R1 /* old SP */ + BR (LR) + +TEXT exception(SB), $-4 + MOVW R1, SPR(SAVER1) + MOVW CR, R0 + MOVW R0, SPR(SAVECR) + MOVW LR, R0 + BL saveureg(SB) + MOVW $0, R0 + BL trap(SB) + BL restoreureg(SB) + MOVW SPR(SAVELR), R0 + MOVW R0, LR + MOVW SPR(SAVER0), R0 + ISYNC + RFI + +GLOBL mach0+0(SB), $MACHSIZE +GLOBL m(SB), $4 diff --git a/os/boot/rpcg/lib.h b/os/boot/rpcg/lib.h new file mode 100644 index 00000000..1eb0532d --- /dev/null +++ b/os/boot/rpcg/lib.h @@ -0,0 +1,106 @@ +/* + * functions (possibly) linked in, complete, from libc. + */ + +/* + * mem routines + */ +extern void* memccpy(void*, void*, int, long); +extern void* memset(void*, int, long); +extern int memcmp(void*, void*, long); +extern void* memmove(void*, void*, long); +extern void* memchr(void*, int, long); + +/* + * string routines + */ +extern char* strcat(char*, char*); +extern char* strchr(char*, char); +extern int strcmp(char*, char*); +extern char* strcpy(char*, char*); +extern char* strncat(char*, char*, long); +extern char* strncpy(char*, char*, long); +extern int strncmp(char*, char*, long); +extern long strlen(char*); +extern char* strrchr(char*, char); +extern char* strstr(char*, char*); + +/* + * print routines + * Fconv isn't used but is defined to satisfy prototypes in libg.h + * that are never called. + */ +typedef struct Fconv Fconv; + +extern char* donprint(char*, char*, char*, void*); +extern int sprint(char*, char*, ...); +extern int print(char*, ...); + +#define PRINTSIZE 256 + +/* + * one-of-a-kind + */ +extern int atoi(char*); +extern long strtol(char*, char**, int); +extern ulong strtoul(char*, char**, int); +extern long end; + +/* + * Syscall data structures + */ + +#define MORDER 0x0003 /* mask for bits defining order of mounting */ +#define MREPL 0x0000 /* mount replaces object */ +#define MBEFORE 0x0001 /* mount goes before others in union directory */ +#define MAFTER 0x0002 /* mount goes after others in union directory */ +#define MCREATE 0x0004 /* permit creation in mounted directory */ +#define MMASK 0x0007 /* all bits on */ + +#define OREAD 0 /* open for read */ +#define OWRITE 1 /* write */ +#define ORDWR 2 /* read and write */ +#define OEXEC 3 /* execute, == read but check execute permission */ +#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ +#define OCEXEC 32 /* or'ed in, close on exec */ +#define ORCLOSE 64 /* or'ed in, remove on close */ + +#define NCONT 0 /* continue after note */ +#define NDFLT 1 /* terminate after note */ + +typedef struct Qid Qid; +typedef struct Dir Dir; +typedef struct Waitmsg Waitmsg; + +#define ERRLEN 64 +#define DIRLEN 116 +#define NAMELEN 28 + +struct Qid +{ + ulong path; + ulong vers; +}; + +struct Dir +{ + char name[NAMELEN]; + char uid[NAMELEN]; + char gid[NAMELEN]; + Qid qid; + ulong mode; + long atime; + long mtime; + vlong length; + short type; + short dev; +}; + +struct Waitmsg +{ + int pid; /* of loved one */ + int status; /* unused; a placeholder */ + ulong time[3]; /* of loved one */ + char msg[ERRLEN]; +}; +#define nelem(x) (sizeof(x)/sizeof((x)[0])) diff --git a/os/boot/rpcg/libg.h b/os/boot/rpcg/libg.h new file mode 100644 index 00000000..5c2cec26 --- /dev/null +++ b/os/boot/rpcg/libg.h @@ -0,0 +1,418 @@ +#pragma src "/sys/src/libg" +#pragma lib "libg.a" + +enum /* constants for I/O to devgraphics */ +{ + Tilehdr = 40, + Tilesize = 8000 +}; + +/* + * you may think it's a blit, but it's gnot + */ +enum +{ + EMAXMSG = 128+8192, /* size of 9p header+data */ +}; + +/* + * Types + */ + +typedef struct Bitmap Bitmap; +typedef struct Display Display; +typedef struct Point Point; +typedef struct Rectangle Rectangle; +typedef struct Cursor Cursor; +typedef struct Mouse Mouse; +typedef struct Menu Menu; +typedef struct Font Font; +typedef struct Fontchar Fontchar; +typedef struct Subfont Subfont; +typedef struct Cachefont Cachefont; +typedef struct Cacheinfo Cacheinfo; +typedef struct Cachesubf Cachesubf; +typedef struct Event Event; +typedef struct Slave Slave; +typedef struct Ebuf Ebuf; +typedef struct RGB RGB; +typedef struct Linedesc Linedesc; +typedef struct DRefret DRefret; + +struct DRefret +{ + int n; /* number of bytes */ + int dy; /* number of lines */ + uchar *dp; /* pointer to data */ +}; + +struct Point +{ + int x; + int y; +}; + +struct Rectangle +{ + Point min; + Point max; +}; + +typedef DRefret DRefresh(Display*, int, Rectangle, uchar*, uchar*, int); + +struct Bitmap +{ + Rectangle r; /* rectangle in data area, local coords */ + Rectangle clipr; /* clipping region */ + int ldepth; /* log base 2 of number of bits per pixel */ + ulong *base; /* pointer to start of data */ + int zero; /* base+zero=&word containing (0,0) */ + ulong width; /* width in words of total data area */ + Display *display; /* if present */ +}; + +struct Display +{ + uchar *data; /* transfer buffer */ + Rectangle r; + int ldepth; + Rectangle bb; /* bounding box of changes */ + int waste; /* unused part of bb */ + Rectangle bound; /* memory for boundin/boundout */ + Bitmap *image; /* owner */ + int id; + int fd; + int ctlfd; + int local; + int bytewidth; + void *drdata1; /* storage for drefresh() */ + void *drdata2; /* storage for drefresh() */ + DRefresh *drefresh; +}; + + +struct Mouse +{ + int buttons; /* bit array: LMR=124 */ + Point xy; + ulong msec; +}; + +struct Cursor +{ + Point offset; + uchar clr[2*16]; + uchar set[2*16]; +}; + +struct Menu +{ + char **item; + char *(*gen)(int); + int lasthit; +}; + +struct Linedesc +{ + int x0; + int y0; + char xmajor; + char slopeneg; + long dminor; + long dmajor; +}; + +/* + * Subfonts + * + * given char c, Subfont *f, Fontchar *i, and Point p, one says + * i = f->info+c; + * bitblt(b, Pt(p.x+i->left,p.y+i->top), + * bitmap, Rect(i->x,i->top,(i+1)->x,i->bottom), + * fc); + * p.x += i->width; + * where bitmap b is the repository of the images. + * + */ + +struct Fontchar +{ + short x; /* left edge of bits */ + uchar top; /* first non-zero scan-line */ + uchar bottom; /* last non-zero scan-line + 1 */ + char left; /* offset of baseline */ + uchar width; /* width of baseline */ +}; + +struct Subfont +{ + short n; /* number of chars in font */ + uchar height; /* height of bitmap */ + char ascent; /* top of bitmap to baseline */ + Fontchar *info; /* n+1 character descriptors */ + Bitmap *bits; /* of font */ +}; + +enum +{ + /* starting values */ + LOG2NFCACHE = 6, + NFCACHE = (1<>8)) +#define BPLONG(p, v) (BPSHORT(p, (v)), BPSHORT(p+2, (v)>>16)) + +ulong *wordaddr(Bitmap*, Point); +uchar *byteaddr(Bitmap*, Point); +int dfree(Display*); +int dwritectl(Display*, char*, int); +int dreadctl(Display*, char*, int); +int dinfo(Display*, int, int*, Rectangle*); +void* dinit(Display*, Bitmap*, int, int); +int ddelete(Display*); +void dfreemem(Display*); +int dreadctl(Display*, char*, int); +int dwritectl(Display*, char*, int); +void dbound(Display*, Rectangle); +void bload(Bitmap*, Rectangle, uchar*); +ulong bunload(Bitmap*, Rectangle, uchar*); +void drefresh(Display*, Rectangle); +Display *dopen(char*, int, DRefresh*); +Bitmap* dbitmap(Display*, DRefresh*, int); +void dclose(Display*); +void dflush(Display*); +void _bltinit(void); +Bitmap* battach(Bitmap*, int, int); +int readmouse(Mouse*); +int atomouse(Mouse*, char*, int); + +/* + * Refresh functions + */ +DRefresh drtexture; +DRefresh drbackstore; diff --git a/os/boot/rpcg/main.c b/os/boot/rpcg/main.c new file mode 100644 index 00000000..10600eef --- /dev/null +++ b/os/boot/rpcg/main.c @@ -0,0 +1,527 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "dosfs.h" + +typedef struct Type Type; +typedef struct Medium Medium; +typedef struct Mode Mode; + +enum { + Dany = -1, + Nmedia = 16, + + /* DS1 switch options */ + Sflashfs = 1<<0, /* take local fs from flash */ + Snotflash = 1<<1, /* don't boot from flash */ +}; + +enum { /* type */ + Tflash, + Tuart, + Tether, + Thard, + + Tany = -1, +}; + +enum { /* flag and name */ + Fnone = 0x00, + + Fdos = 0x01, + Ndos = 0x00, + Fboot = 0x02, + Nboot = 0x01, + Fbootp = 0x04, + Nbootp = 0x02, + Fflash = 0x08, + Fuart = 0x10, + NName = 0x03, + + Fany = Fbootp|Fboot|Fdos|Fflash|Fuart, + + Fini = 0x10, + Fprobe = 0x80, +}; + +enum { /* mode */ + Mauto = 0x00, + Mlocal = 0x01, + Manual = 0x02, + NMode = 0x03, +}; + +typedef struct Type { + int type; + char *cname; + int flag; + int (*init)(void); + long (*read)(int, void*, long); + long (*seek)(int, long); + Partition* (*setpart)(int, char*); + char* name[NName]; + + int mask; + Medium* media; +} Type; + +typedef struct Medium { + Type* type; + int flag; + Partition* partition; + Dos; + + Medium* next; +} Medium; + +typedef struct Mode { + char* name; + int mode; +} Mode; + +static Type types[] = { + { Tflash, "flash", + Fflash, + flashinit, 0, 0, 0, + { 0, "F", 0, } + }, +/* + { Tuart, "uart", + Fuart|Fboot, + uartinit, uartread, uartseek, setuartpart, + { 0, "u", 0, } + }, +*/ + { Tether, "ether", + Fbootp, + etherinit, 0, 0, 0, + { 0, 0, "e", }, + }, + { Thard, "ata", + Fini|Fboot|Fdos, + 0, 0, 0, 0, /* not used now, will be later with PCMCIA */ + { "hd", "h", 0, }, + }, + {-1}, +}; + +static Medium media[Nmedia]; +static Medium *curmedium = media; + +static Mode modes[NMode+1] = { + [Mauto] { "auto", Mauto, }, + [Mlocal] { "local", Mlocal, }, + [Manual] { "manual", Manual, }, +}; + +static char *inis[] = { + "inferno/inferno.ini", + "inferno.ini", + "plan9/plan9.ini", + "plan9.ini", + 0, +}; +char **ini; +int predawn; + +static int +parse(char *line, int *type, int *flag, int *dev, char *file) +{ + Type *tp; + char buf[2*NAMELEN], *v[4], *p; + int i; + + strcpy(buf, line); + switch(getcfields(buf, v, 4, "!")){ + + case 3: + break; + + case 2: + v[2] = ""; + break; + + default: + return 0; + } + + *flag = 0; + for(tp = types; tp->cname; tp++){ + for(i = 0; i < NName; i++){ + + if(tp->name[i] == 0 || strcmp(v[0], tp->name[i])) + continue; + *type = tp->type; + *flag |= 1<type->name[Nbootp], mp->dev); + return bootp(mp->dev, file); + } + + if(flag & Fflash){ + if(mp->flag & Fflash && flashbootable(0)) + flashboot(mp->dev); + } + + if(flag & Fboot){ + + if(mp->flag & Fini){ + (*mp->type->setpart)(mp->dev, "disk"); + plan9ini(mp, nil); + } + if(file == 0 || *file == 0) + file = mp->partition->name; + (*mp->type->setpart)(mp->dev, file); + sprint(BOOTLINE, "%s!%d!%s", mp->type->name[Nboot], mp->dev, file); + r = plan9boot(mp->dev, mp->seek, mp->read); + uartsetboot(0); + return r; + } + + if(flag & Fdos){ + if(mp->type->setpart) + (*mp->type->setpart)(mp->dev, "disk"); + if(mp->flag & Fini) + plan9ini(mp, nil); + if(file == 0 || *file == 0){ + strcpy(ixdos, *ini); + if(p = strrchr(ixdos, '/')) + p++; + else + p = ixdos; + strcpy(p, "impc"); + if(dosstat(mp, ixdos, &df) <= 0) + return -1; + } + else + strcpy(ixdos, file); + sprint(BOOTLINE, "%s!%d!%s", mp->type->name[Ndos], mp->dev, ixdos); + return dosboot(mp, ixdos); + } + + return -1; +} + +static Medium* +allocm(Type *tp) +{ + Medium **l; + + if(curmedium >= &media[Nmedia]) + return 0; + + for(l = &tp->media; *l; l = &(*l)->next) + ; + *l = curmedium++; + return *l; +} + +Medium* +probe(int type, int flag, int dev) +{ + Type *tp; + int dombr, i, start; + Medium *mp; + Dosfile df; + Partition *pp; + + for(tp = types; tp->cname; tp++){ + if(type != Tany && type != tp->type || tp->init == 0) + continue; + + if(flag != Fnone){ + for(mp = tp->media; mp; mp = mp->next){ + if((flag & mp->flag) && (dev == Dany || dev == mp->dev)) + return mp; + } + } + if((tp->flag & Fprobe) == 0){ + tp->flag |= Fprobe; + tp->mask = (*tp->init)(); + } + + for(i = 0; tp->mask; i++){ + if((tp->mask & (1<mask &= ~(1<dev = i; + mp->flag = tp->flag; + mp->seek = tp->seek; + mp->read = tp->read; + mp->type = tp; + + if(mp->flag & Fboot){ + if((mp->partition = (*tp->setpart)(i, "boot")) == 0) + mp->flag &= ~Fboot; + if((mp->flag & (Fflash|Fuart)) == 0) + (*tp->setpart)(i, "disk"); + } + + if(mp->flag & Fdos){ + start = 0; + dombr = 1; + if(mp->type->setpart){ + if(pp = (*mp->type->setpart)(i, "dos")){ + if(start = pp->start) + dombr = 0; + } + (*tp->setpart)(i, "disk"); + } + if(dosinit(mp, start, dombr) < 0) + mp->flag &= ~(Fini|Fdos); + else + print("dos init failed\n"); + } + + if(mp->flag & Fini){ + mp->flag &= ~Fini; + for(ini = inis; *ini; ini++){ + if(dosstat(mp, *ini, &df) <= 0) + continue; + mp->flag |= Fini; + break; + } + } + + if((flag & mp->flag) && (dev == Dany || dev == i)) + return mp; + } + } + + return 0; +} + +void +main(void) +{ + Medium *mp; + int dev, flag, i, mode, tried, type, options; + char def[2*NAMELEN], file[2*NAMELEN], line[80], *p; + Type *tp; + + machinit(); + archinit(); + meminit(); + cpminit(); + trapinit(); + consinit(); /* screen and keyboard initially */ +/* screeninit(); */ + cpuidprint(); + alarminit(); + clockinit(); +print("predawn\n"); + predawn = 0; + spllo(); +print("dawn\n"); + options = archoptionsw(); +print("options=#%ux\n", options); + + mp = 0; + for(tp = types; tp->cname; tp++){ + if(tp->type == Tether) + continue; + if((mp = probe(tp->type, Fini, Dany)) && (mp->flag & Fini)){ + plan9ini(mp, nil); + break; + } + } + + if(mp == 0 || (mp->flag & Fini) == 0) + plan9ini(nil, flashconfig(0)); + + //consinit(); /* establish new console location */ + + if((options & Snotflash) == 0 && flashbootable(0)){ + print("Flash boot\n"); + flashboot(0); + } + + tried = 0; + mode = Mauto; + p = getconf("bootfile"); + flag = 0; + + if(p != 0) { + mode = Manual; + for(i = 0; i < NMode; i++){ + if(strcmp(p, modes[i].name) == 0){ + mode = modes[i].mode; + goto done; + } + } + if(parse(p, &type, &flag, &dev, file) == 0) { + print("Bad bootfile syntax: %s\n", p); + goto done; + } + mp = probe(type, flag, dev); + if(mp == 0) { + print("Cannot access device: %s\n", p); + goto done; + } + tried = boot(mp, flag, file); + } +done: + if(tried == 0 && mode != Manual){ + flag = Fany; + if(mode == Mlocal) + flag &= ~Fbootp; + if(options & Snotflash) + flag &= ~Fflash; + if((mp = probe(Tany, flag, Dany)) != 0) + boot(mp, flag & mp->flag, 0); + } + + def[0] = 0; + probe(Tany, Fnone, Dany); + + flag = 0; + for(tp = types; tp->cname; tp++){ + for(mp = tp->media; mp; mp = mp->next){ + if(flag == 0){ + flag = 1; + print("Boot devices:"); + } + + if(mp->flag & Fbootp) + print(" %s!%d", mp->type->name[Nbootp], mp->dev); + if(mp->flag & Fdos) + print(" %s!%d", mp->type->name[Ndos], mp->dev); + if(mp->flag & (Fflash|Fuart) || mp->flag & Fboot) + print(" %s!%d", mp->type->name[Nboot], mp->dev); + } + } + if(flag) + print("\n"); + + for(;;){ + if(getstr("boot from", line, sizeof(line), def) >= 0){ + if(parse(line, &type, &flag, &dev, file)){ + if(mp = probe(type, flag, dev)) + boot(mp, flag, file); + } + } + def[0] = 0; + } +} + +void +machinit(void) +{ + memset(m, 0, sizeof(*m)); + m->delayloop = 20000; + m->cpupvr = getpvr(); + m->iomem = KADDR(INTMEM); +} + +int +getcfields(char* lp, char** fields, int n, char* sep) +{ + int i; + + for(i = 0; lp && *lp && i < n; i++){ + while(*lp && strchr(sep, *lp) != 0) + *lp++ = 0; + if(*lp == 0) + break; + fields[i] = lp; + while(*lp && strchr(sep, *lp) == 0){ + if(*lp == '\\' && *(lp+1) == '\n') + *lp++ = ' '; + lp++; + } + } + + return i; +} + +static Map memv[512]; +static RMap rammap = {"physical memory"}; + +void +meminit(void) +{ + ulong e; + + mapinit(&rammap, memv, sizeof(memv)); + e = PADDR(&end); + mapfree(&rammap, e, 4*1024*1024-e); /* fixed 4Mbytes is plenty for bootstrap */ +} + +void* +ialloc(ulong n, int align) +{ + ulong a; + int s; + + if(align <= 0) + align = 4; + s = splhi(); + a = mapalloc(&rammap, 0, n, align); + splx(s); + if(a == 0) + panic("ialloc"); + return memset(KADDR(a), 0, n); +} + +void* +malloc(ulong n) +{ + ulong *p; + + n = ((n+sizeof(int)-1)&~(sizeof(int)-1))+2*sizeof(int); + p = ialloc(n, sizeof(int)); + *p++ = 0xcafebeef; + *p++ = n; + return p; +} + +void +free(void *ap) +{ + int s; + ulong *p; + + p = ap; + if(p){ + if(*(p -= 2) != 0xcafebeef) + panic("free"); + s = splhi(); + mapfree(&rammap, (ulong)p, p[1]); + splx(s); + } +} + +void +sched(void) +{ +} diff --git a/os/boot/rpcg/mem.c b/os/boot/rpcg/mem.c new file mode 100644 index 00000000..e69de29b diff --git a/os/boot/rpcg/mem.h b/os/boot/rpcg/mem.h new file mode 100644 index 00000000..f31e5622 --- /dev/null +++ b/os/boot/rpcg/mem.h @@ -0,0 +1,94 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ + +/* + * Sizes + */ +#define BI2BY 8 /* bits per byte */ +#define BI2WD 32 /* bits per word */ +#define BY2WD 4 /* bytes per word */ +#define BY2PG 4096 /* bytes per page */ +#define WD2PG (BY2PG/BY2WD) /* words per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define PGROUND(s) (((s)+(BY2PG-1))&~(BY2PG-1)) + +#define MAXMACH 1 /* max # cpus system can run */ +#define CACHELINELOG 4 +#define CACHELINESZ (1<$target + +f.mx: qbrom$CONF + ms2 -S 0x100 -a 0xFFC20100 -3 -p 4 $prereq >$target + +g.mx: qbrom$CONF + ms2 -S 0x100 -a 0x10000 -3 -p 4 $prereq >$target + +%.$O: %.s + $AS -Dconf$CONF $stem.s + +%.$O: %.c + $CC $CFLAGS $stem.c + +%.$O: $HFILES + +lib%.a:V: $SHELLTYPE-lib%.a + +rc-lib%.a nt-lib%.a:VQ: + echo '@{builtin cd ' $ROOT/lib$stem ';mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install}' + @{builtin cd $ROOT/lib$stem ;mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install} + +sh-lib%.a:VQ: + echo "(cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install)" + (cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install) + +clock.$O floppy.$O trap.$O: ureg.h +conf.$O dosboot.$O main.$O: dosfs.h +ether.$O etherscc.$O: etherif.h +bootp.$O: ip.h + +clean:V: + rm -f *.[$OS] [$OS].out y.tab.? y.debug y.output $TARG qboot k.mx f.mx romboot + +nuke-sh:QV: + for i in $LIBDIRS + do + echo "(cd $ROOT/lib$i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE nuke)" + (cd $ROOT/lib$i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE nuke) + done + +nuke-rc nuke-nt:QV: + for (i in $LIBDIRS) + { + echo '@{cd $ROOT/lib$i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE nuke}' + @{cd $ROOT/lib$i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE nuke} + } + +nuke:V: clean nuke-$SHELLTYPE diff --git a/os/boot/rpcg/ms2.c b/os/boot/rpcg/ms2.c new file mode 100644 index 00000000..ce96df78 --- /dev/null +++ b/os/boot/rpcg/ms2.c @@ -0,0 +1,179 @@ +#include +#include +#include +#include + +void record(uchar*, int); +void usage(void); +void dosegment(long, int); +void trailer(ulong); + +enum +{ + Recordsize = 32, +}; + +int dsegonly; +int supressend; +int binary; +int addr4; +ulong addr; +ulong psize = 4096; +ulong startaddr = 0x030000; +Biobuf stdout; +Biobuf bio; + +void +main(int argc, char **argv) +{ + Dir dir; + Fhdr f; + int fd; + + ARGBEGIN{ + case 'd': + dsegonly++; + break; + case 's': + supressend++; + break; + case 'a': + addr = strtoul(ARGF(), 0, 0); + break; + case 'p': + psize = strtoul(ARGF(), 0, 0); + break; + case 'b': + binary++; + break; + case 'S': + startaddr = strtoul(ARGF(), 0, 0); + break; + case '4': + addr4++; + break; + default: + usage(); + }ARGEND + + if(argc != 1) + usage(); + + Binit(&stdout, 1, OWRITE); + + fd = open(argv[0], OREAD); + if(fd < 0) { + fprint(2, "ms2: open %s: %r\n", argv[0]); + exits("open"); + } + + if(binary) { + if(dirfstat(fd, &dir) < 0) { + fprint(2, "ms2: stat failed %r"); + exits("dirfstat"); + } + Binit(&bio, fd, OREAD); + dosegment(0, dir.length); + if(supressend == 0) + trailer(startaddr); + Bterm(&stdout); + Bterm(&bio); + exits(0); + } + + if(crackhdr(fd, &f) == 0){ + fprint(2, "ms2: bad magic: %r\n"); + exits("magic"); + } + seek(fd, 0, 0); + + Binit(&bio, fd, OREAD); + + if(dsegonly) + dosegment(f.datoff, f.datsz); + else { + dosegment(f.txtoff, f.txtsz); + addr = (addr+(psize-1))&~(psize-1); + dosegment(f.datoff, f.datsz); + } + + if(supressend == 0) + trailer(startaddr); + + Bterm(&stdout); + Bterm(&bio); + exits(0); +} + +void +dosegment(long foff, int len) +{ + int l, n; + uchar buf[2*Recordsize]; + + Bseek(&bio, foff, 0); + for(;;) { + l = len; + if(l > Recordsize) + l = Recordsize; + n = Bread(&bio, buf, l); + if(n == 0) + break; + if(n < 0) { + fprint(2, "ms2: read error: %r\n"); + exits("read"); + } + record(buf, l); + len -= l; + } +} + +void +record(uchar *s, int l) +{ + int i; + ulong cksum; + + if(addr4 || addr & (0xFF<<24)){ + Bprint(&stdout, "S3%.2X%.8luX", l+5, addr); + cksum = l+5; + cksum += (addr>>24)&0xff; + }else{ + Bprint(&stdout, "S2%.2X%.6X", l+4, addr); + cksum = l+4; + } + cksum += addr&0xff; + cksum += (addr>>8)&0xff; + cksum += (addr>>16)&0xff; + + for(i = 0; i < l; i++) { + cksum += *s; + Bprint(&stdout, "%.2X", *s++); + } + Bprint(&stdout, "%.2X\n", (~cksum)&0xff); + addr += l; +} + +void +trailer(ulong a) +{ + ulong cksum; + + cksum = 0; + if(addr4 || a & (0xFF<<24)){ + Bprint(&stdout, "S7%.8luX", a); + cksum += (a>>24)&0xff; + }else + Bprint(&stdout, "S9%.6X", a); + cksum += a&0xff; + cksum += (a>>8)&0xff; + cksum += (a>>16)&0xff; + Bprint(&stdout, "%.2X\n", (~cksum)&0xff); +} + +void +usage(void) +{ + fprint(2, "usage: ms2 [-ds] [-a address] [-p pagesize] ?.out\n"); + exits("usage"); +} diff --git a/os/boot/rpcg/plan9boot.c b/os/boot/rpcg/plan9boot.c new file mode 100644 index 00000000..047474e3 --- /dev/null +++ b/os/boot/rpcg/plan9boot.c @@ -0,0 +1,96 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +char *premature = "premature EOF\n"; + +/* + * read in a segment + */ +static long +readseg(int dev, long (*read)(int, void*, long), long len, long addr) +{ + char *a; + long n, sofar; + + a = (char *)addr; + for(sofar = 0; sofar < len; sofar += n){ + n = 8*1024; + if(len - sofar < n) + n = len - sofar; + n = (*read)(dev, a + sofar, n); + if(n <= 0) + break; + print("."); + } + return sofar; +} + +/* + * boot + */ +int +plan9boot(int dev, long (*seek)(int, long), long (*read)(int, void*, long)) +{ + long n; + long addr; + void (*b)(void); + Exec *ep; + + if((*seek)(dev, 0) < 0) + return -1; + + /* + * read header + */ + ep = (Exec *) ialloc(sizeof(Exec), 0); + n = sizeof(Exec); + if(readseg(dev, read, n, (long) ep) != n){ + print(premature); + return -1; + } + if(GLLONG(ep->magic) != Q_MAGIC){ + print("bad magic 0x%lux not a plan 9 executable!\n", GLLONG(ep->magic)); + return -1; + } + + /* + * read text + */ + addr = PADDR(GLLONG(ep->entry)); + n = GLLONG(ep->text); + print("%d", n); + if(readseg(dev, read, n, addr) != n){ + print(premature); + return -1; + } + + /* + * read data (starts at first page after kernel) + */ + addr = PGROUND(addr+n); + n = GLLONG(ep->data); + print("+%d@%8.8lux", n, addr); + if(readseg(dev, read, n, addr) != n){ + print(premature); + return -1; + } + + /* + * bss and entry point + */ + print("+%d\nstart at 0x%lux\n", GLLONG(ep->bss), GLLONG(ep->entry)); + uartwait(); + scc2stop(); + splhi(); + + /* + * Go to new code. It's up to the program to get its PC relocated to + * the right place. + */ + b = (void (*)(void))(PADDR(GLLONG(ep->entry))); + (*b)(); + return 0; +} diff --git a/os/boot/rpcg/qbromrpcg b/os/boot/rpcg/qbromrpcg new file mode 100755 index 00000000..8b2fe7d8 Binary files /dev/null and b/os/boot/rpcg/qbromrpcg differ diff --git a/os/boot/rpcg/qio.c b/os/boot/rpcg/qio.c new file mode 100644 index 00000000..e014433e --- /dev/null +++ b/os/boot/rpcg/qio.c @@ -0,0 +1,128 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +struct Queue { + Block* first; + Block* last; + void (*kick)(void*); + void* arg; + long len; +}; + +Block * +iallocb(int n) +{ + Block *b; + + b = (Block*)malloc(sizeof(Block)+n); + b->data = (uchar*)b + sizeof(Block); + b->rp = b->wp = b->data; + b->lim = b->data + n; + b->next = 0; + b->magic = 0xcafebee0; + return b; +} + +void +freeb(Block *b) +{ + if(b){ + if(b->magic != 0xcafebee0) + panic("freeb"); + b->magic = 0; + b->next = (Block*)0xdeadbabe; + free(b); + } +} + +Queue * +qopen(int limit, int msg, void (*kick)(void*), void *arg) +{ + Queue *q; + + USED(limit, msg); + q = (Queue*)malloc(sizeof(Queue)); + q->first = q->last = 0; + q->kick = kick; + q->arg = arg; + q->len = 0; + return q; +} + +Block * +qget(Queue *q) +{ + int s; + Block *b; + + s = splhi(); + if((b = q->first) != 0){ + q->first = b->next; + b->next = 0; + q->len -= BLEN(b); + if(q->len < 0) + panic("qget"); + } + splx(s); + return b; +} + +void +qbwrite(Queue *q, Block *b) +{ + int s; + + s = splhi(); + b->next = 0; + if(q->first == 0) + q->first = b; + else + q->last->next = b; + q->last = b; + q->len += BLEN(b); + splx(s); + if(q->kick) + q->kick(q->arg); +} + +long +qlen(Queue *q) +{ + return q->len; +} + +int +qbgetc(Queue *q) +{ + Block *b; + int s, c; + + c = -1; + s = splhi(); + while(c < 0 && (b = q->first) != nil){ + if(b->rp < b->wp){ + c = *b->rp++; + q->len--; + } + if(b->rp >= b->wp){ + q->first = b->next; + b->next = nil; + } + } + splx(s); + return c; +} + +void +qbputc(Queue *q, int c) +{ + Block *b; + + b = iallocb(1); + *b->wp++ = c; + qbwrite(q, b); +} diff --git a/os/boot/rpcg/rmap.c b/os/boot/rpcg/rmap.c new file mode 100644 index 00000000..8b8a0bef --- /dev/null +++ b/os/boot/rpcg/rmap.c @@ -0,0 +1,104 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +void +mapinit(RMap *rmap, Map *map, int size) +{ + lock(rmap); + rmap->map = map; + rmap->mapend = map+(size/sizeof(Map)); + unlock(rmap); +} + +void +mapfree(RMap* rmap, ulong addr, int size) +{ + Map *mp; + ulong t; + + if(size <= 0) + return; + + lock(rmap); + for(mp = rmap->map; mp->addr <= addr && mp->size; mp++) + ; + + if(mp > rmap->map && (mp-1)->addr+(mp-1)->size == addr){ + (mp-1)->size += size; + if(addr+size == mp->addr){ + (mp-1)->size += mp->size; + while(mp->size){ + mp++; + (mp-1)->addr = mp->addr; + (mp-1)->size = mp->size; + } + } + } + else{ + if(addr+size == mp->addr && mp->size){ + mp->addr -= size; + mp->size += size; + } + else do{ + if(mp >= rmap->mapend){ + print("mapfree: %s: losing 0x%uX, %d\n", + rmap->name, addr, size); + break; + } + t = mp->addr; + mp->addr = addr; + addr = t; + t = mp->size; + mp->size = size; + mp++; + }while(size = t); + } + unlock(rmap); +} + +ulong +mapalloc(RMap* rmap, ulong addr, int size, int align) +{ + Map *mp; + ulong maddr, oaddr; + + lock(rmap); + for(mp = rmap->map; mp->size; mp++){ + maddr = mp->addr; + + if(addr){ + if(maddr > addr) + continue; + if(addr+size > maddr+mp->size) + break; + maddr = addr; + } + + if(align > 0) + maddr = ((maddr+align-1)/align)*align; + if(mp->addr+mp->size-maddr < size) + continue; + + oaddr = mp->addr; + mp->addr = maddr+size; + mp->size -= maddr-oaddr+size; + if(mp->size == 0){ + do{ + mp++; + (mp-1)->addr = mp->addr; + }while((mp-1)->size = mp->size); + } + + unlock(rmap); + if(oaddr != maddr) + mapfree(rmap, oaddr, maddr-oaddr); + + return maddr; + } + unlock(rmap); + + return 0; +} diff --git a/os/boot/rpcg/screen.c b/os/boot/rpcg/screen.c new file mode 100644 index 00000000..ec420ee9 --- /dev/null +++ b/os/boot/rpcg/screen.c @@ -0,0 +1,242 @@ +#include "all.h" +#include +#include + +enum { + Colldepth = 3, + Colmaxx = 640, + Colmaxxvis = 640, + Colmaxy = 480, +}; + +#define MINX 8 + +extern GSubfont defont0; + +struct{ + Point pos; + int bwid; +}out; + +typedef struct Mode Mode; +struct Mode { + int x; + int y; + int d; + char* aperture; + int apsize; +}; + +GBitmap gscreen; +Point gchar(GBitmap*, Point, GFont*, int, Fcode); +int setcolor(ulong, ulong, ulong, ulong); +static void lcdinit(Mode*); + +void +screeninit(void) +{ + Mode m; + + m.x = Colmaxx; + m.y = Colmaxy; + m.d = Colldepth; + m.aperture = 0; + lcdinit(&m); + if(m.aperture == 0) + return; + gscreen.ldepth = 3; + gscreen.base = (ulong*)m.aperture; + gscreen.width = Colmaxx/BY2WD; + gscreen.r = Rect(0, 0, Colmaxxvis, Colmaxy); + gscreen.clipr = gscreen.r; + /* + * For now, just use a fixed colormap: + * 0 == white and 255 == black + */ + setcolor(0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF); + setcolor(255, 0x00000000, 0x00000000, 0x00000000); + + gbitblt(&gscreen, Pt(0, 0), &gscreen, gscreen.r, Zero); + out.pos.x = MINX; + out.pos.y = 0; + out.bwid = defont0.info[' '].width; +} + +void +screenputc(int c) +{ + Fontchar *i; + Point p; + + if(gscreen.base == nil) + return; + switch(c){ + case '\n': + out.pos.x = MINX; + out.pos.y += defont0.height; + if(out.pos.y > gscreen.r.max.y-defont0.height) + out.pos.y = gscreen.r.min.y; + gbitblt(&gscreen, Pt(0, out.pos.y), &gscreen, + Rect(0, out.pos.y, gscreen.r.max.x, out.pos.y+2*defont0.height), + Zero); + break; + case '\t': + out.pos.x += (8-((out.pos.x-MINX)/out.bwid&7))*out.bwid; + if(out.pos.x >= gscreen.r.max.x) + screenputc('\n'); + break; + case '\b': + if(out.pos.x >= out.bwid+MINX){ + out.pos.x -= out.bwid; + screenputc(' '); + out.pos.x -= out.bwid; + } + break; + default: + if(out.pos.x >= gscreen.r.max.x-out.bwid) + screenputc('\n'); + c &= 0x7f; + if(c <= 0 || c >= defont0.n) + break; + i = defont0.info + c; + p = out.pos; + gbitblt(&gscreen, Pt(p.x+i->left, p.y), defont0.bits, + Rect(i[0].x, 0, i[1].x, defont0.height), + S); + out.pos.x = p.x + i->width; + break; + } +} + +void +screenputs(char *s, int n) +{ + while(n-- > 0) + screenputc(*s++); +} + +/* + * See section 5.2.1 (page 5-6) of the MPC823 manual + */ +static uchar lcdclock[17] = { /* (a<<2)|b => divisor of (1<iomem; + mode->y = ROWS; + mode->x = COLS; + mode->d = LDEPTH; + mode->aperture = ialloc(mode->x*mode->y, 16); + mode->apsize = mode->x*mode->y; + + io->sdcr &= ~LAM; /* MPC823 errata: turn off LAM before disabling controller */ + io->lcfaa = PADDR(mode->aperture); + io->lccr = (((mode->x*mode->y*(1<lcdmap[i] = i; + break; + case 2: + /* 4-bit grey scale map */ + for(i=0; i<16; i++) + io->lcdmap[0] = (i<<8)|(i<<4)|i; + break; + case 3: + /* 8-bit linear map */ + for(i=0; i<256; i++) + io->lcdmap[i] = (i<<8)|(i<<4)|i; + break; + } + + io->lcvcr = (mode->y << 11) | (1<<28) | 33; /* 2 line vsync pulse, 34 line wait between frames */ + io->lchcr = (mode->x<<10) | BigEndian | 228; /* clock cycles between lines */ + + hz = m->cpuhz; + d = hz/LCDFREQ; + if(hz/d > LCDFREQ) + d++; + if(d >= 16) + d = 16; + + /* + * enable LCD outputs + */ + io->pddat = 0; + io->pdpar = 0x1fff; +io->pdpar &= ~SIBIT(6); /* 823 bug fix? */ + io->pddir = 0x1fff; + io->pbpar |= IBIT(31) | IBIT(19) | IBIT(17); + io->pbdir |= IBIT(31) | IBIT(19) | IBIT(17); + io->pbodr &= ~(IBIT(31) | IBIT(19) | IBIT(17)); + + eieio(); + io->sccrk = KEEP_ALIVE_KEY; + eieio(); + io->sccr = (io->sccr & ~0x1F) | lcdclock[d]; + eieio(); + io->sccrk = ~KEEP_ALIVE_KEY; + eieio(); + gscreen.width = gscreen.width; /* access external memory before enabling (mpc823 errata) */ + io->lcsr = 7; /* clear status */ + eieio(); + io->lccr |= Enable; + archbacklight(1); +} + +int +setcolor(ulong p, ulong r, ulong g, ulong b) +{ + r >>= 28; + g >>= 28; + b >>= 28; + m->iomem->lcdmap[~p&0xFF] = (r<<8) | (g<<4) | b; /* TO DO: it's a function of the ldepth */ + return 1; +} diff --git a/os/boot/rpcg/sload b/os/boot/rpcg/sload new file mode 100755 index 00000000..0e2a3458 Binary files /dev/null and b/os/boot/rpcg/sload differ diff --git a/os/boot/rpcg/sload.c b/os/boot/rpcg/sload.c new file mode 100644 index 00000000..0eb02805 --- /dev/null +++ b/os/boot/rpcg/sload.c @@ -0,0 +1,71 @@ +/* + * send S records to rpcg + */ + +#include +#include +#include + +static int dbg; +static char buf[2048]; +static int run=1; +static void stuffbym(char*, int, int); +static void getdot(void); + +void +main(int argc, char **argv) +{ + int n; + char *l; + Biobuf *f; + static int p; + + ARGBEGIN{ + case 'd': dbg++; break; + case 'n': run=0; break; + }ARGEND + + f = Bopen(*argv? *argv: "k.mx", OREAD); + if(f == 0) { + fprint(2, "sload: cannot open k.mx: %r\n"); + exits("sload"); + } + getdot(); + while((l = Brdline(f, '\n')) != 0) { + l[Blinelen(f)-1] = '\r'; + stuffbym(l, Blinelen(f), 16); + getdot(); + if(++p % 25 == 0) + write(2, ".", 1); + } + exits(0); +} + +static void +stuffbym(char *l, int n, int m) +{ + int nr, ns; + + while(n > 0) { + ns = n; + if(ns > m) + ns = m; + write(1, l, ns); + l += ns; + n -= ns; + } +} + +static void +getdot(void) +{ + char c; + + for(;;){ + if(read(0, &c, 1) != 1) + exits("bang"); + write(2, &c, 1); + if(c == '.') + break; + } +} diff --git a/os/boot/rpcg/squeeze.h b/os/boot/rpcg/squeeze.h new file mode 100644 index 00000000..b06c1b79 --- /dev/null +++ b/os/boot/rpcg/squeeze.h @@ -0,0 +1,34 @@ + +/* + * squeezed file format: + * Sqhdr + * original Exec header + * two Squeeze tables + * squeezed segment + * unsqueezed segment, if any + */ +#define SQMAGIC (ulong)0xFEEF0F1E + +typedef struct Sqhdr Sqhdr; +struct Sqhdr { + uchar magic[4]; /* SQMAGIC */ + uchar text[4]; /* squeezed length of text (excluding tables) */ + uchar data[4]; /* squeezed length of data (excluding tables) */ + uchar asis[4]; /* length of unsqueezed segment */ + uchar toptxt[4]; /* value for 0 encoding in text */ + uchar topdat[4]; /* value for 0 encoding in data */ + uchar sum[4]; /* simple checksum of unsqueezed data */ + uchar flags[4]; +}; +#define SQHDRLEN (8*4) + +/* + * certain power instruction types are rearranged by sqz + * so as to move the variable part of the instruction word to the + * low order bits. note that the mapping is its own inverse. + */ +#define QREMAP(X)\ + switch((X)>>26){\ + case 19: case 31: case 59: case 63:\ + (X) = (((X) & 0xFC00F801) | (((X)>>15)&0x7FE) | (((X)&0x7FE)<<15));\ + } diff --git a/os/boot/rpcg/trap.c b/os/boot/rpcg/trap.c new file mode 100644 index 00000000..3440ee1f --- /dev/null +++ b/os/boot/rpcg/trap.c @@ -0,0 +1,233 @@ +#include "boot.h" + +enum +{ + Maxhandler= 32+16, /* max number of interrupt handlers */ +}; + +typedef struct Handler Handler; +struct Handler +{ + void (*r)(Ureg*, void*); + void *arg; + Handler *next; + int edge; +}; + +struct +{ + Handler *ivec[128]; + Handler h[Maxhandler]; + int free; +} halloc; + +char *excname[] = { + "reserved 0", + "system reset", + "machine check", + "data access", + "instruction access", + "external interrupt", + "alignment", + "program exception", + "floating-point unavailable", + "decrementer", + "reserved A", + "reserved B", + "system call", + "trace trap", + "floating point assist", + "reserved F", + "software emulation", + "ITLB miss", + "DTLB miss", + "ITLB error", + "DTLB error", +}; + +char *regname[]={ + "CAUSE", "SRR1", + "PC", "GOK", + "LR", "CR", + "XER", "CTR", + "R0", "R1", + "R2", "R3", + "R4", "R5", + "R6", "R7", + "R8", "R9", + "R10", "R11", + "R12", "R13", + "R14", "R15", + "R16", "R17", + "R18", "R19", + "R20", "R21", + "R22", "R23", + "R24", "R25", + "R26", "R27", + "R28", "R29", + "R30", "R31", +}; + +static void intr(Ureg*); + +void +sethvec(int v, void (*r)(void)) +{ + ulong *vp, pa, o; + + if((ulong)r & 3) + panic("sethvec"); + vp = (ulong*)KADDR(v); + vp[0] = 0x7c1043a6; /* MOVW R0, SPR(SPRG0) */ + vp[1] = 0x7c0802a6; /* MOVW LR, R0 */ + vp[2] = 0x7c1243a6; /* MOVW R0, SPR(SPRG2) */ + pa = PADDR(r); + o = pa >> 25; + if(o != 0 && o != 0x7F){ + /* a branch too far: running from ROM */ + vp[3] = (15<<26)|(pa>>16); /* MOVW $r&~0xFFFF, R0 */ + vp[4] = (24<<26)|(pa&0xFFFF); /* OR $r&0xFFFF, R0 */ + vp[5] = 0x7c0803a6; /* MOVW R0, LR */ + vp[6] = 0x4e800021; /* BL (LR) */ + }else + vp[3] = (18<<26)|(pa&0x3FFFFFC)|3; /* bla */ +} + +#define LEV(n) (((n)<<1)|1) +#define IRQ(n) (((n)<<1)|0) + +void +setvec(int v, void (*r)(Ureg*, void*), void *arg) +{ + Handler *h; + IMM *io; + + if(halloc.free >= Maxhandler) + panic("out of interrupt handlers"); + v -= VectorPIC; + h = &halloc.h[halloc.free++]; + h->next = halloc.ivec[v]; + h->r = r; + h->arg = arg; + halloc.ivec[v] = h; + + /* + * enable corresponding interrupt in SIU/CPM + */ + + io = m->iomem; + if(v >= VectorCPIC){ + v -= VectorCPIC; + io->cimr |= 1<<(v&0x1F); + } + else if(v >= VectorIRQ) + io->simask |= 1<<(31-IRQ(v&7)); + else + io->simask |= 1<<(31-LEV(v)); +} + +void +trapinit(void) +{ + int i; + IMM *io; + + io = m->iomem; + io->sypcr &= ~(3<<2); /* disable watchdog (821/823) */ + io->simask = 0; /* mask all */ + io->siel = ~0; /* edge sensitive, wake on all */ + io->cicr = 0; /* disable CPM interrupts */ + io->cipr = ~0; /* clear all interrupts */ + io->cimr = 0; /* mask all events */ + io->cicr = (0xE1<<16)|(CPIClevel<<13)|(0x1F<<8); + io->cicr |= 1 << 7; /* enable */ + io->tbscrk = KEEP_ALIVE_KEY; + io->tbscr = 1; /* TBE */ + io->simask |= 1<<(31-LEV(CPIClevel)); /* CPM's level */ + io->tbk = KEEP_ALIVE_KEY; + eieio(); + putdec(~0); + + /* + * set all exceptions to trap + */ + for(i = 0x0; i < 0x3000; i += 0x100) + sethvec(i, exception); +} + +void +dumpregs(Ureg *ur) +{ + int i; + ulong *l; + l = &ur->cause; + for(i=0; icause >> 8; + switch(c){ + default: + {extern int predawn; predawn = 1;} + if(c < 0 || c >= nelem(excname)) + print("exception/interrupt #%x\n", c); + else + print("exception %s\n", excname[c]); + dumpregs(ur); + /* spllo(); */ + print("^P to reset\n"); + for(;;) + ; + + case 0x09: /* decrementer */ + clockintr(ur, 0); + return; + + case 0x05: /* external interrupt */ + intr(ur); + break; + } +} + +static void +intr(Ureg *ur) +{ + int b, v; + Handler *h; + IMM *io; + + io = m->iomem; + b = io->sivec>>2; + v = b>>1; + if(b & 1) { + if(v == CPIClevel){ + io->civr = 1; + eieio(); + v = VectorCPIC+(io->civr>>11); + } + }else + v += VectorIRQ; + h = halloc.ivec[v]; + if(h == nil){ + for(;;) + ; + //print("unknown interrupt %d pc=0x%lux\n", v, ur->pc); + return; + } + if(h->edge) + io->sipend |= 1<<(31-b); + /* + * call the interrupt handlers + */ + do { + (*h->r)(ur, h->arg); + h = h->next; + } while(h != nil); + if(v >= VectorCPIC) + io->cisr |= 1<<(v-VectorCPIC); +} diff --git a/os/boot/rpcg/uartboot.c b/os/boot/rpcg/uartboot.c new file mode 100644 index 00000000..0b11b5d5 --- /dev/null +++ b/os/boot/rpcg/uartboot.c @@ -0,0 +1,189 @@ +#include "boot.h" + +/* + * this doesn't yet use the crc + */ + +typedef struct Uboot Uboot; +struct Uboot { + Queue* iq; + Block* partial; + ulong csum; + long bno; + uchar buf[64]; + int nleft; + int ntimeout; +}; + +static Uboot uboot; +ulong crc32(void *buf, int n, ulong crc); + +static void +uartbrecv(uchar *p, int n) +{ + Uboot *ub; + Block *b; + + ub = &uboot; + if(n > 0 && ub->iq != nil){ + b = iallocb(n); + memmove(b->wp, p, n); + b->wp += n; + qbwrite(ub->iq, b); + } +} + +int +uartinit(void) +{ + return 1<<0; +} + +Partition* +setuartpart(int, char *s) +{ + static Partition pp[1]; + + if(strcmp(s, "boot") != 0 && strcmp(s, "disk") != 0) + return 0; + pp[0].start = 0; + pp[0].end = 2*1024*1024; + strcpy(pp[0].name, "boot"); + return pp; +} + +long +uartseek(int, long) +{ + /* start the boot */ + if(uboot.iq == nil) + uboot.iq = qopen(64*1024, 0, 0, 0); + if(uboot.partial){ + freeb(uboot.partial); + uboot.partial = 0; + } + print("uart: start transmission\n"); + uartsetboot(uartbrecv); + uboot.csum = ~0; + uboot.bno = 0; + uboot.nleft = 0; + uboot.ntimeout = 0; + return 0; +} + +static long +uartreadn(void *buf, int nb) +{ + ulong start; + Uboot *ub; + int l; + Block *b; + uchar *p; + + p = buf; + ub = &uboot; + start = m->ticks; + while(nb > 0){ + b = ub->partial; + ub->partial = nil; + if(b == nil){ + ub->ntimeout = 0; + while((b = qget(ub->iq)) == 0){ + if(TK2MS(m->ticks - start) >= 15*1000){ + if(++ub->ntimeout >= 3){ + print("uart: timeout\n"); + return 0; + } + uartputs("n", 1); + } + } + } + l = BLEN(b); + if(l > nb) + l = nb; + memmove(p, b->rp, l); + b->rp += l; + if(b->rp >= b->wp) + freeb(b); + else + ub->partial = b; + nb -= l; + p += l; + } + return p-(uchar*)buf; +} + +long +uartread(int, void *buf, long n) +{ + uchar *p; + int l; + static uchar lbuf[64]; + + p = buf; + if((l = uboot.nleft) > 0){ + if(l > n) + l = n; + uboot.nleft -= l; + memmove(p, uboot.buf, l); + p += l; + n -= l; + } + while(n > 0){ + l = uartreadn(lbuf, sizeof(lbuf)); + if(l < sizeof(lbuf)) + return 0; + if(l > n){ + uboot.nleft = l-n; + memmove(uboot.buf, lbuf+n, uboot.nleft); + l = n; + } + memmove(p, lbuf, l); + n -= l; + p += l; + uboot.bno++; + uartputs("y", 1); + } + return p-(uchar*)buf; +} + +/* + * from Rob Warnock + */ +static ulong crc32tab[256]; /* initialised on first call to crc32 */ + +enum { + CRC32POLY = 0x04c11db7 /* AUTODIN II, Ethernet, & FDDI */ +}; + +/* + * Build auxiliary table for parallel byte-at-a-time CRC-32. + */ +static void +initcrc32(void) +{ + int i, j; + ulong c; + + for(i = 0; i < 256; i++) { + for(c = i << 24, j = 8; j > 0; j--) + if(c & (1<<31)) + c = (c<<1) ^ CRC32POLY; + else + c <<= 1; + crc32tab[i] = c; + } +} + +ulong +crc32(void *buf, int n, ulong crc) +{ + uchar *p; + + if(crc32tab[1] == 0) + initcrc32(); + crc = ~crc; + for(p = buf; --n >= 0;) + crc = (crc << 8) ^ crc32tab[(crc >> 24) ^ *p++]; + return ~crc; +} diff --git a/os/boot/rpcg/ureg.h b/os/boot/rpcg/ureg.h new file mode 100644 index 00000000..7ccdb492 --- /dev/null +++ b/os/boot/rpcg/ureg.h @@ -0,0 +1,43 @@ +struct Ureg +{ + ulong cause; + union { ulong srr1; ulong status;}; + ulong pc; /* SRR0 */ + ulong pad; + ulong lr; + ulong cr; + ulong xer; + ulong ctr; + ulong r0; + union{ ulong r1; ulong sp; ulong usp; }; + ulong r2; + ulong r3; + ulong r4; + ulong r5; + ulong r6; + ulong r7; + ulong r8; + ulong r9; + ulong r10; + ulong r11; + ulong r12; + ulong r13; + ulong r14; + ulong r15; + ulong r16; + ulong r17; + ulong r18; + ulong r19; + ulong r20; + ulong r21; + ulong r22; + ulong r23; + ulong r24; + ulong r25; + ulong r26; + ulong r27; + ulong r28; + ulong r29; + ulong r30; + ulong r31; +}; diff --git a/os/boot/rpcg/zqs.c b/os/boot/rpcg/zqs.c new file mode 100644 index 00000000..b6296786 --- /dev/null +++ b/os/boot/rpcg/zqs.c @@ -0,0 +1,234 @@ +#include "boot.h" +#include "squeeze.h" + +/* + * for details of `unsqueeze' see: + * + * %A Mark Taunton + * %T Compressed Executables: An Exercise in Thinking Small + * %P 385-404 + * %I USENIX + * %B USENIX Conference Proceedings + * %D Summer 1991 + * %C Nashville, TN + * + * several of the unimplemented improvements described in the paper + * have been implemented here + * + * there is a further transformation on the powerpc (QFLAG!=0) to shuffle bits + * in certain instructions so as to push the fixed bits to the top of the word. + */ + +#define EXECHDRLEN (8*4) + +typedef struct Squeeze Squeeze; +struct Squeeze { + int n; + ulong tab[7*256]; +}; + +#define GET4(p) (((((((p)[0]<<8)|(p)[1])<<8)|(p)[2])<<8)|(p)[3]) + +/* + * for speed of unsqueezing from Flash, certain checks are + * not done inside the loop (as they would be in the unsqueeze program zqs), + * but instead the checksum is expected to catch corrupted files. + * in fact the Squeeze array bounds can't be exceeded in practice + * because the tables are always full for a squeezed kernel. + */ +enum { + QFLAG = 1, /* invert powerpc-specific code transformation */ + CHECK = 0, /* check precise bounds in Squeeze array (otherwise checksum detects error) */ +}; + +static ulong chksum; +static int rdtab(Block*, Squeeze*, int); +static ulong* unsqueeze(ulong*, uchar*, uchar*, Squeeze*, Squeeze*, ulong); +static uchar* unsqzseg(uchar*, Block*, long, long, char*); +static Alarm* unsqzal; + +int +issqueezed(uchar *b) +{ + return GET4(b) == SQMAGIC? GET4(b+SQHDRLEN): 0; +} + +static void +unsqzdot(Alarm*) +{ + unsqzal = alarm(500, unsqzdot, nil); + print("."); +} + +long +unsqueezef(Block *b, ulong *entryp) +{ + uchar *loada, *wp; + ulong toptxt, topdat, oldsum; + long asis, nst, nsd; + Sqhdr *sqh; + Exec *ex; + + if(BLEN(b) < SQHDRLEN+EXECHDRLEN) + return -1; + sqh = (Sqhdr*)b->rp; + if(GET4(sqh->magic) != SQMAGIC) + return -1; + chksum = 0; + toptxt = GET4(sqh->toptxt); + topdat = GET4(sqh->topdat); + oldsum = GET4(sqh->sum); + asis = GET4(sqh->asis); + nst = GET4(sqh->text); + nsd = GET4(sqh->data); + b->rp += SQHDRLEN; + ex = (Exec*)b->rp; + if(GET4(ex->magic) != Q_MAGIC){ + print("zqs: not powerPC executable\n"); + return -1; + } + *entryp = GET4(ex->entry); + b->rp += EXECHDRLEN; + loada = KADDR(PADDR(*entryp)); + wp = unsqzseg(loada, b, nst, toptxt, "text"); + if(wp == nil){ + print("zqs: format error\n"); + return -1; + } + if(nsd){ + wp = (uchar*)PGROUND((ulong)wp); + wp = unsqzseg(wp, b, nsd, topdat, "data"); + if(wp == nil){ + print("zqs: format error\n"); + return -1; + } + } + if(asis){ + memmove(wp, b->rp, asis); + wp += asis; + b->rp += asis; + } + if(chksum != oldsum){ + print("\nsqueezed kernel: checksum error: %8.8lux need %8.8lux\n", chksum, oldsum); + return -1; + } + return wp-loada; +} + +static uchar * +unsqzseg(uchar *wp, Block *b, long ns, long top, char *what) +{ + static Squeeze sq3, sq4; + + print("unpack %s %8.8lux %lud:", what, wp, ns); + if(ns == 0) + return wp; + if(rdtab(b, &sq3, 0) < 0) + return nil; + if(rdtab(b, &sq4, 8) < 0) + return nil; + if(BLEN(b) < ns){ + print(" **size error\n"); + return nil; + } + unsqzal = alarm(500, unsqzdot, nil); + wp = (uchar*)unsqueeze((ulong*)wp, b->rp, b->rp+ns, &sq3, &sq4, top); + cancel(unsqzal); + unsqzal = nil; + print("\n"); + if(wp == nil){ + print("zqs: corrupt squeezed data stream\n"); + return nil; + } + b->rp += ns; + return wp; +} + +static ulong* +unsqueeze(ulong *wp, uchar *rp, uchar *ep, Squeeze *sq3, Squeeze *sq4, ulong top) +{ + ulong nx, csum; + int code, n; + + if(QFLAG){ + QREMAP(top); /* adjust top just once, outside the loop */ + } + csum = chksum; + while(rp < ep){ + /* no function calls within this loop for speed */ + code = *rp; + rp++; + n = 0; + nx = code>>4; + do{ + if(nx == 0){ + nx = top; + }else{ + if(nx==1){ + nx = (((((rp[3]<<8)|rp[2])<<8)|rp[1])<<8)|rp[0]; + rp += 4; + }else if(nx <= 8){ /* 2 to 8 */ + nx = ((nx-2)<<8) | rp[0]; + if(CHECK && nx >= sq4->n) + return nil; /* corrupted file */ + nx = sq4->tab[nx] | rp[1]; + rp += 2; + }else{ /* 9 to 15 */ + nx = ((nx-9)<<8) | rp[0]; + if(CHECK && nx >= sq3->n) + return nil; /* corrupted file */ + nx = sq3->tab[nx]; + rp++; + } + if(rp > ep) + return nil; /* corrupted file */ + if(QFLAG){ + QREMAP(nx); + } + } + *wp = nx; + wp++; + csum += nx; + nx = code & 0xF; + }while(++n == 1); + } + chksum = csum; + return wp; +} + +static int +rdtab(Block *b, Squeeze *sq, int shift) +{ + uchar *p, *ep; + ulong v, w; + int i; + + if(BLEN(b) < 2) + return -1; + i = (b->rp[0]<<8) | b->rp[1]; + if(1) + print(" T%d", i); + b->rp += 2; + if((i -= 2) > 0){ + if(BLEN(b) < i) + return -1; + } + sq->n = 0; + p = b->rp; + ep = b->rp+i; + b->rp += i; + v = 0; + while(p < ep){ + w = 0; + do{ + if(p >= ep) + return -1; + w = (w<<7) | (*p & 0x7F); + }while(*p++ & 0x80); + v += w; + if(0) + print("%d %8.8lux %8.8lux\n", sq->n, v, w); + sq->tab[sq->n++] = v< + +#include "../port/netif.h" +#include "etherif.h" +#include "../port/flashif.h" + +enum { + /* Cerf GPIO assignment */ + LED0 = 1<<0, /* active high */ + LED1 = 1<<1, + LED2 = 1<<2, + LED3 = 1<<3, + /* 4 to 15 appear on J2 */ + CFBVD1 = 1<<20, + CFBVD2 = 1<<19, + CFReset = 1<<21, /* active low for IDE mode; active high for IO or memory mode */ + CFRdypin = 22, + CFRdy = 1< card inserted */ + EnableRS232In = 1<<24, + EnableRS232Out = 1<<25, + /* CS8900 interrupt on 26, active high */ + /* CS8900 nHWSLEEP on 27 */ +}; + +void +archreset(void) +{ + GpioReg *g = GPIOREG; + + g->grer = 0; + g->gfer = 0; + g->gedr = g->gedr; + g->gpdr = 0; + + g->gpdr = EnableRS232In | EnableRS232Out | CFReset; + g->gpsr = EnableRS232In | EnableRS232Out; + + GPCLKREG->gpclkr0 |= 1; /* SUS=1 for uart on serial 1 */ +} + +void +archconfinit(void) +{ + int w; + + conf.topofmem = 0xC0000000+32*MB; + w = PMGRREG->ppcr & 0x1f; + m->cpuhz = CLOCKFREQ*(w*4+16); + + conf.useminicache = 1; + conf.portrait = 1; /* should take from param flash or allow dynamic change */ +} + +void +archconsole(void) +{ + uartspecial(0, 38400, 'n', &kbdq, &printq, kbdcr2nl); +} + +void +archuartpower(int, int) +{ +} + +void +kbdinit(void) +{ +} + +void +archreboot(void) +{ + dcflushall(); + GPIOREG->gedr = 1<<0; + mmuputctl(mmugetctl() & ~CpCaltivec); /* restore bootstrap's vectors */ + RESETREG->rsrr = 1; /* software reset */ + for(;;) + spllo(); +} + +void +archflashwp(Flash*, int) +{ +} + +/* + * for devflash.c:/^flashreset + * retrieve flash type, virtual base and length and return 0; + * return -1 on error (no flash) + */ +int +archflashreset(int bank, Flash *f) +{ + if(bank != 0) + return -1; + f->type = "cfi16"; + f->addr = KADDR(FLASHMEM); + f->size = 0; + f->width = 2; + return 0; +} + +/* + * pcmcia + */ +int +pcmpowered(int slotno) +{ + if(slotno) + return 0; + return 3; +} + +void +pcmpower(int slotno, int on) +{ + USED(slotno, on); +} + +void +pcmreset(int slot) +{ + if(slot != 0) + return; + GPIOREG->gpsr = CFReset; + delay(100); + GPIOREG->gpcr = CFReset; +} + +int +pcmpin(int slot, int type) +{ + if(slot) + return -1; + switch(type){ + case PCMready: + return CFRdypin; + case PCMeject: + return CFnCDxpin; + case PCMstschng: + return -1; + } +} + +void +pcmsetvpp(int slot, int vpp) +{ + USED(slot, vpp); +} + +/* + * set ether parameters: the contents should be derived from EEPROM or NVRAM + */ +int +archether(int ctlno, Ether *ether) +{ + if(ctlno > 0) + return -1; + sprint(ether->type, "CS8900"); + ether->nopt = 0; + ether->irq = 26; /* GPIO */ + ether->itype = BusGPIOrising; + return 1; +} diff --git a/os/cerf1110/cerf b/os/cerf1110/cerf new file mode 100644 index 00000000..24002405 --- /dev/null +++ b/os/cerf1110/cerf @@ -0,0 +1,159 @@ +dev + root + cons archcerf + env + gpio + mnt + pipe + prog + rtc + srv + dup + ssl + cap + sign +# draw screen +# pointer + uart + ip bootp ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium netaux + flash + ftl + pcmcia cis + ata + ether netif netaux ethermedium + + cerf +# kprof + +ip + il + tcp + udp +# rudp +# igmp + ipifc + icmp + icmp6 +# ipmux + +link + flashcfi16 + ether8900 + +lib + interp +# tk +# draw +# memlayer +# memdraw + keyring + sec + mp + math + kern + +mod + math + sys +# draw +# tk + keyring + +port + alarm + alloc + allocb + chan + dev + dial + dis + discall + exception + exportfs + inferno + latin1 + nocache + nodynld + parse + pgrp + print + proc + qio + qlock + random + sysfile + taslock + xalloc + +code + int kernel_pool_pcnt = 10; + int main_pool_pcnt = 40; + int heap_pool_pcnt = 40; + int image_pool_pcnt = 0; + int cflag = 0; /* for JIT */ + + int consoleprint = 1; + int redirectconsole = 1; + char debug_keys = 1; + int panicreset = 0; + void screeninit(void){} + +init + cerfinit + +root + /chan / + /dev / + /dis + /env / + /fd / + /net / + /net.alt / + /nvfs / + /prog / + /dis/lib + /dis/disk + /osinit.dis + +# dos file system + /dis/dossrv.dis + /dis/lib/arg.dis + /dis/lib/styx.dis + /dis/lib/string.dis + /dis/lib/daytime.dis + + /dis/disk/format.dis + +# For development work: + /dis/sh.dis /dis/tiny/sh.dis + /dis/ls.dis + /dis/cat.dis + /dis/bind.dis + /dis/mount.dis + /dis/pwd.dis + /dis/echo.dis + /dis/cd.dis + /dis/xd.dis + /dis/cp.dis + /dis/mkdir.dis + /dis/rm.dis + /dis/p.dis + /dis/ps.dis + /dis/lib/readdir.dis + /dis/lib/workdir.dis + /dis/lib/daytime.dis + /dis/lib/auth.dis + /dis/lib/ssl.dis + /dis/lib/bufio.dis + /dis/lib/string.dis +# /dis/pcmcia.dis /usr/forsyth/pcmcia.dis + + /n/remote + /n/local + /n/client + /n/rdbg + /n/dump + /n/disk + /n/kfs +# Authentication + /nvfs/default /usr/inferno/keyring/default diff --git a/os/cerf1110/dat.h b/os/cerf1110/dat.h new file mode 100644 index 00000000..553c0cb0 --- /dev/null +++ b/os/cerf1110/dat.h @@ -0,0 +1,135 @@ +typedef struct Conf Conf; +typedef struct Dma Dma; +typedef struct FPU FPU; +typedef struct FPenv FPenv; +typedef struct Label Label; +typedef struct Lock Lock; +typedef struct Mach Mach; +typedef struct Ureg Ureg; +typedef struct ISAConf ISAConf; +typedef struct PCMmap PCMmap; +typedef struct PCMslot PCMslot; + +typedef ulong Instr; + +struct Conf +{ + ulong nmach; /* processors */ + ulong nproc; /* processes */ + ulong npage0; /* total physical pages of memory */ + ulong npage1; /* total physical pages of memory */ + ulong topofmem; /* highest physical address + 1 */ + ulong npage; /* total physical pages of memory */ + ulong base0; /* base of bank 0 */ + ulong base1; /* base of bank 1 */ + ulong ialloc; /* max interrupt time allocation in bytes */ + + int useminicache; /* use mini cache: screen.c/lcd.c */ + int textwrite; /* writeable text segment, for debug */ + int portrait; /* display orientation */ +}; + +#define NISAOPT 8 +struct ISAConf { + char type[KNAMELEN]; + ulong port; + ulong irq; + int itype; + ulong dma; + ulong mem; + ulong size; + ulong freq; + + int nopt; + char *opt[NISAOPT]; +}; + +/* + * FPenv.status + */ +enum +{ + FPINIT, + FPACTIVE, + FPINACTIVE, +}; + +struct FPenv +{ + ulong status; + ulong control; + ushort fpistate; /* emulated fp */ + ulong regs[8][3]; /* emulated fp */ +}; + +/* + * This structure must agree with fpsave and fprestore asm routines + */ +struct FPU +{ + FPenv env; +}; + +struct Label +{ + ulong sp; + ulong pc; +}; + +struct Lock +{ + ulong key; + ulong sr; + ulong pc; + int pri; +}; + +#include "../port/portdat.h" + +/* + * machine dependent definitions not used by ../port/portdat.h + */ +struct Mach +{ + /* OFFSETS OF THE FOLLOWING KNOWN BY l.s */ + ulong splpc; /* pc of last caller to splhi */ + + /* ordering from here on irrelevant */ + + int machno; /* physical id of processor */ + ulong ticks; /* of the clock since boot time */ + Proc *proc; /* current process on this processor */ + Label sched; /* scheduler wakeup */ + Lock alarmlock; /* access to alarm list */ + void *alarm; /* alarms bound to this clock */ + ulong cpuhz; + + /* stacks for exceptions */ + ulong fiqstack[4]; + ulong irqstack[4]; + ulong abtstack[4]; + ulong undstack[4]; + + int stack[1]; +}; + +#define MACHP(n) (n == 0 ? (Mach*)(MACHADDR) : (Mach*)0) + +extern Mach *m; +extern Proc *up; + +typedef struct MemBank { + uint pbase; + uint plimit; + uint vbase; + uint vlimit; +} MemBank; + +/* + * Layout at virtual address 0. + */ +typedef struct Vectorpage { + void (*vectors[8])(void); + uint vtable[8]; +} Vectorpage; +extern Vectorpage *page0; diff --git a/os/cerf1110/devata.c b/os/cerf1110/devata.c new file mode 100644 index 00000000..bca9cda3 --- /dev/null +++ b/os/cerf1110/devata.c @@ -0,0 +1,1200 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define DPRINT if(0)iprint + +typedef struct Drive Drive; +typedef struct Ident Ident; +typedef struct Controller Controller; +typedef struct Partition Partition; +typedef struct Repl Repl; + +enum +{ + /* ports */ + Pbase= 0x1F0, + Pdata= 0, /* data port (16 bits) */ + Perror= 1, /* error port (read) */ + Pprecomp= 1, /* buffer mode port (write) */ + Pcount= 2, /* sector count port */ + Psector= 3, /* sector number port */ + Pcyllsb= 4, /* least significant byte cylinder # */ + Pcylmsb= 5, /* most significant byte cylinder # */ + Pdh= 6, /* drive/head port */ + Pstatus= 7, /* status port (read) */ + Sbusy= (1<<7), + Sready= (1<<6), + Sdrq= (1<<3), + Serr= (1<<0), + Pcmd= 7, /* cmd port (write) */ + + /* commands */ + Crecal= 0x10, + Cread= 0x20, + Cwrite= 0x30, + Cident= 0xEC, + Cident2= 0xFF, /* pseudo command for post Cident interrupt */ + Csetbuf= 0xEF, + Cinitparam= 0x91, + + /* conner specific commands */ + Cstandby= 0xE2, + Cidle= 0xE1, + Cpowerdown= 0xE3, + + /* disk states */ + Sspinning, + Sstandby, + Sidle, + Spowerdown, + + /* something we have to or into the drive/head reg */ + DHmagic= 0xA0, + + /* file types */ + Qdir= 0, + Qfile, + + Maxxfer= BY2PG, /* maximum transfer size/cmd */ + Npart= 8+2, /* 8 sub partitions, disk, and partition */ + Nrepl= 64, /* maximum replacement blocks */ +}; +#define PART(x) (((x)>>1)&0xF) +#define DRIVE(x) (((x)>>5)&0x7) +#define MKQID(d,p) (((d)<<5) | ((p)<<1) | Qfile) + +struct Partition +{ + ulong start; + ulong end; + char name[KNAMELEN+1]; +}; + +struct Repl +{ + Partition *p; + int nrepl; + ulong blk[Nrepl]; +}; + +#define PARTMAGIC "plan9 partitions" +#define REPLMAGIC "block replacements" + +/* + * an ata drive + */ +struct Drive +{ + QLock; + + Controller *cp; + int drive; + int confused; /* needs to be recalibrated (or worse) */ + int online; + int npart; /* number of real partitions */ + Partition p[Npart]; + Repl repl; + ulong usetime; + int state; + char vol[KNAMELEN]; + + ulong cap; /* total bytes */ + int bytes; /* bytes/sector */ + int sectors; /* sectors/track */ + int heads; /* heads/cyl */ + long cyl; /* cylinders/drive */ + + char lba; /* true if drive has logical block addressing */ + char multi; /* non-zero if drive does multiple block xfers */ +}; + +/* + * a controller for 2 drives + */ +struct Controller +{ + QLock; /* exclusive access to the controller */ + ISAConf; /* interface to pcmspecial */ + + Lock reglock; /* exclusive access to the registers */ + + int confused; /* needs to be recalibrated (or worse) */ + ulong pbase; /* base port (copied from ISAConf) */ + + /* + * current operation + */ + int cmd; /* current command */ + int lastcmd; /* debugging info */ + Rendez r; /* wait here for command termination */ + char *buf; /* xfer buffer */ + int nsecs; /* length of transfer (sectors) */ + int sofar; /* sectors transferred so far */ + int status; + int error; + Drive *dp; /* drive being accessed */ +}; + +Controller *atac; +Drive *ata; +static char* ataerr; +static int nhard; +static int spindowntime; + +static void ataintr(Ureg*, void*); +static long ataxfer(Drive*, Partition*, int, long, long, char*); +static void ataident(Drive*); +static void atasetbuf(Drive*, int); +static void ataparams(Drive*); +static void atapart(Drive*); +static int ataprobe(Drive*, int, int, int); + +static int +atagen(Chan *c, char*, Dirtab*, int, int s, Dir *dirp) +{ + Qid qid; + int drive; + Drive *dp; + Partition *pp; + ulong l; + + if(s == DEVDOTDOT){ + mkqid(&qid, 0, 0, QTDIR); + sprint(up->genbuf, "#%C", devtab[c->type]->dc); + devdir(c, qid, up->genbuf, 0, eve, 0555, dirp); + return 1; + } + qid.vers = 0; + qid.type = QTFILE; + drive = s/Npart; + s = s % Npart; + if(drive >= nhard) + return -1; + dp = &ata[drive]; + + if(dp->online == 0 || s >= dp->npart) + return 0; + + pp = &dp->p[s]; + sprint(up->genbuf, "%s%s", dp->vol, pp->name); + qid.path = MKQID(drive, s); + l = (pp->end - pp->start) * dp->bytes; + devdir(c, qid, up->genbuf, l, eve, 0660, dirp); + return 1; +} + +static void +atainit(void) +{ + Drive *dp; + Controller *cp; + uchar equip; + int pcmslot; + + if (atac) + return; /* already done */ + + equip = 0x10; /* hard coded */ + + cp = malloc(sizeof(*cp)); + if (!cp) + error(Enomem); + + cp->port = Pbase; + cp->irq = 14; + cp->nopt = 1; + cp->opt[0] = "index=1"; + + if((pcmslot = pcmspecial("ATA FLASH", cp)) < 0) { + DPRINT("No ATA card\n"); + free(cp); + ataerr = Enoifc; + return; + } + ata = malloc(2 * sizeof(*ata)); + if(ata == nil) { + pcmspecialclose(pcmslot); + free(cp); + error(Enomem); + } + + atac = cp; + cp->buf = 0; + cp->lastcmd = cp->cmd; + cp->cmd = 0; + cp->pbase = cp->port; + intrenable(cp->irq, ataintr, cp, cp->itype, "ata"); + + dp = ata; + if(equip & 0xf0){ + dp->drive = 0; + dp->online = 0; + dp->cp = cp; + dp++; + } + if((equip & 0x0f)){ + dp->drive = 1; + dp->online = 0; + dp->cp = cp; + dp++; + } + nhard = dp - ata; + + spindowntime = 1; +} + + +/* + * Get the characteristics of each drive. Mark unresponsive ones + * off line. + */ +static Chan* +ataattach(char *spec) +{ + Drive *dp; + + atainit(); + if (!ata) + error(ataerr ? ataerr : Enoifc); + for(dp = ata; dp < &ata[nhard]; dp++){ + if(waserror()){ + dp->online = 0; + qunlock(dp); + continue; + } + qlock(dp); + if(!dp->online){ + /* + * Make sure ataclock() doesn't + * interfere. + */ + dp->usetime = m->ticks; + ataparams(dp); + dp->online = 1; + atasetbuf(dp, 1); + } + + /* + * read Plan 9 partition table + */ + atapart(dp); + qunlock(dp); + poperror(); + } + return devattach('H', spec); +} + +static Walkqid* +atawalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, atagen); +} + +static int +atastat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, 0, 0, atagen); +} + +static Chan* +ataopen(Chan *c, int omode) +{ + return devopen(c, omode, 0, 0, atagen); +} + +static void +ataclose(Chan *c) +{ + Drive *d; + Partition *p; + + if(c->mode != OWRITE && c->mode != ORDWR) + return; + + d = &ata[DRIVE(c->qid.path)]; + p = &d->p[PART(c->qid.path)]; + if(strcmp(p->name, "partition") != 0) + return; + + if(waserror()){ + qunlock(d); + nexterror(); + } + qlock(d); + atapart(d); + qunlock(d); + poperror(); +} + +static long +ataread(Chan *c, void *a, long n, vlong offset) +{ + Drive *dp; + long rv, i; + int skip; + uchar *aa = a; + Partition *pp; + char *buf; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, atagen); + + buf = smalloc(Maxxfer); + if(waserror()){ + free(buf); + nexterror(); + } + + dp = &ata[DRIVE(c->qid.path)]; + pp = &dp->p[PART(c->qid.path)]; + + skip = offset % dp->bytes; + for(rv = 0; rv < n; rv += i){ + i = ataxfer(dp, pp, Cread, offset+rv-skip, n-rv+skip, buf); + if(i == 0) + break; + i -= skip; + if(i > n - rv) + i = n - rv; + memmove(aa+rv, buf + skip, i); + skip = 0; + } + + free(buf); + poperror(); + + return rv; +} + +static long +atawrite(Chan *c, void *a, long n, vlong offset) +{ + Drive *dp; + long rv, i, partial; + uchar *aa = a; + Partition *pp; + char *buf; + + if(c->qid.type & QTDIR) + error(Eisdir); + + dp = &ata[DRIVE(c->qid.path)]; + pp = &dp->p[PART(c->qid.path)]; + buf = smalloc(Maxxfer); + if(waserror()){ + free(buf); + nexterror(); + } + + /* + * if not starting on a sector boundary, + * read in the first sector before writing + * it out. + */ + partial = offset % dp->bytes; + if(partial){ + ataxfer(dp, pp, Cread, offset-partial, dp->bytes, buf); + if(partial+n > dp->bytes) + rv = dp->bytes - partial; + else + rv = n; + memmove(buf+partial, aa, rv); + ataxfer(dp, pp, Cwrite, offset-partial, dp->bytes, buf); + } else + rv = 0; + + /* + * write out the full sectors + */ + partial = (n - rv) % dp->bytes; + n -= partial; + for(; rv < n; rv += i){ + i = n - rv; + if(i > Maxxfer) + i = Maxxfer; + memmove(buf, aa+rv, i); + i = ataxfer(dp, pp, Cwrite, offset+rv, i, buf); + if(i == 0) + break; + } + + /* + * if not ending on a sector boundary, + * read in the last sector before writing + * it out. + */ + if(partial){ + ataxfer(dp, pp, Cread, offset+rv, dp->bytes, buf); + memmove(buf, aa+rv, partial); + ataxfer(dp, pp, Cwrite, offset+rv, dp->bytes, buf); + rv += partial; + } + + free(buf); + poperror(); + + return rv; +} + +/* + * did an interrupt happen? + */ +static int +cmddone(void *a) +{ + Controller *cp = a; + + return cp->cmd == 0; +} + +/* + * Wait for the controller to be ready to accept a command. + * This is protected from interference by ataclock() by + * setting dp->usetime before it is called. + */ +static void +cmdreadywait(Drive *dp) +{ + long start; + int period; + Controller *cp = dp->cp; + + /* give it 2 seconds to spin down and up */ + if(dp->state == Sspinning) + period = 10; + else + period = 2000; + + start = m->ticks; + while((inb(cp->pbase+Pstatus) & (Sready|Sbusy)) != Sready) + if(TK2MS(m->ticks - start) > period){ + DPRINT("cmdreadywait failed\n"); + error(Eio); + } +} + +static void +atarepl(Drive *dp, long bblk) +{ + int i; + + if(dp->repl.p == 0) + return; + for(i = 0; i < dp->repl.nrepl; i++){ + if(dp->repl.blk[i] == bblk) + DPRINT("found bblk %ld at offset %d\n", bblk, i); + } +} + +static void +atasleep(Controller *cp, int ms) +{ + tsleep(&cp->r, cmddone, cp, ms); + if(cp->cmd && cp->cmd != Cident2){ + DPRINT("ata: cmd 0x%uX timeout, status=%ux\n", + cp->cmd, inb(cp->pbase+Pstatus)); + error("ata drive timeout"); + } +} + +/* + * transfer a number of sectors. ataintr will perform all the iterative + * parts. + */ +static long +ataxfer(Drive *dp, Partition *pp, int cmd, long start, long len, char *buf) +{ + Controller *cp; + long lblk; + int cyl, sec, head; + int loop, stat; + + if(dp->online == 0) + error(Eio); + + /* + * cut transfer size down to disk buffer size + */ + start = start / dp->bytes; + if(len > Maxxfer) + len = Maxxfer; + len = (len + dp->bytes - 1) / dp->bytes; + if(len == 0) + return 0; + + /* + * calculate physical address + */ + lblk = start + pp->start; + if(lblk >= pp->end) + return 0; + if(lblk+len > pp->end) + len = pp->end - lblk; + if(dp->lba){ + sec = lblk & 0xff; + cyl = (lblk>>8) & 0xffff; + head = (lblk>>24) & 0xf; + } else { + cyl = lblk/(dp->sectors*dp->heads); + sec = (lblk % dp->sectors) + 1; + head = ((lblk/dp->sectors) % dp->heads); + } + + DPRINT("<%s %ld>", (cmd == Cwrite) ? "W" : "R", lblk); + cp = dp->cp; + qlock(cp); + if(waserror()){ + cp->buf = 0; + qunlock(cp); + nexterror(); + } + + /* + * Make sure hardclock() doesn't + * interfere. + */ + dp->usetime = m->ticks; + cmdreadywait(dp); + + ilock(&cp->reglock); + cp->sofar = 0; + cp->buf = buf; + cp->nsecs = len; + cp->cmd = cmd; + cp->dp = dp; + cp->status = 0; + + outb(cp->pbase+Pcount, cp->nsecs); + outb(cp->pbase+Psector, sec); + outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4) | (dp->lba<<6) | head); + outb(cp->pbase+Pcyllsb, cyl); + outb(cp->pbase+Pcylmsb, cyl>>8); + outb(cp->pbase+Pcmd, cmd); + + if(cmd == Cwrite){ + loop = 0; + microdelay(1); + while((stat = inb(cp->pbase+Pstatus) & (Serr|Sdrq)) == 0) + if(++loop > 10000) + panic("ataxfer"); + outss(cp->pbase+Pdata, cp->buf, dp->bytes/2); + } else + stat = 0; + iunlock(&cp->reglock); + + if(stat & Serr) + error(Eio); + + /* + * wait for command to complete. if we get a note, + * remember it but keep waiting to let the disk finish + * the current command. + */ + loop = 0; + while(waserror()){ + DPRINT("interrupted ataxfer\n"); + if(loop++ > 10){ + print("ata disk error\n"); + nexterror(); + } + } + atasleep(cp, 3000); + dp->state = Sspinning; + dp->usetime = m->ticks; + poperror(); + if(loop) + nexterror(); + + if(cp->status & Serr){ + DPRINT("hd%ld err: lblk %ld status %ux, err %ux\n", + dp-ata, lblk, cp->status, cp->error); + DPRINT("\tcyl %d, sec %d, head %d\n", cyl, sec, head); + DPRINT("\tnsecs %d, sofar %d\n", cp->nsecs, cp->sofar); + atarepl(dp, lblk+cp->sofar); + error(Eio); + } + cp->buf = 0; + len = cp->sofar*dp->bytes; + qunlock(cp); + poperror(); + + return len; +} + +/* + * set read ahead mode + */ +static void +atasetbuf(Drive *dp, int on) +{ + Controller *cp = dp->cp; + + qlock(cp); + if(waserror()){ + qunlock(cp); + nexterror(); + } + + cmdreadywait(dp); + + ilock(&cp->reglock); + cp->cmd = Csetbuf; + outb(cp->pbase+Pprecomp, on ? 0xAA : 0x55); /* read look ahead */ + outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4)); + outb(cp->pbase+Pcmd, Csetbuf); + iunlock(&cp->reglock); + + atasleep(cp, 5000); + +/* if(cp->status & Serr) + DPRINT("hd%d setbuf err: status %lux, err %lux\n", + dp-ata, cp->status, cp->error);/**/ + + poperror(); + qunlock(cp); +} + +/* + * ident sector from drive. this is from ANSI X3.221-1994 + */ +struct Ident +{ + ushort config; /* general configuration info */ + ushort cyls; /* # of cylinders (default) */ + ushort reserved0; + ushort heads; /* # of heads (default) */ + ushort b2t; /* unformatted bytes/track */ + ushort b2s; /* unformated bytes/sector */ + ushort s2t; /* sectors/track (default) */ + ushort reserved1[3]; +/* 10 */ + ushort serial[10]; /* serial number */ + ushort type; /* buffer type */ + ushort bsize; /* buffer size/512 */ + ushort ecc; /* ecc bytes returned by read long */ + ushort firm[4]; /* firmware revision */ + ushort model[20]; /* model number */ +/* 47 */ + ushort s2i; /* number of sectors/interrupt */ + ushort dwtf; /* double word transfer flag */ + ushort capabilities; + ushort reserved2; + ushort piomode; + ushort dmamode; + ushort cvalid; /* (cvald&1) if next 4 words are valid */ + ushort ccyls; /* current # cylinders */ + ushort cheads; /* current # heads */ + ushort cs2t; /* current sectors/track */ + ushort ccap[2]; /* current capacity in sectors */ + ushort cs2i; /* current number of sectors/interrupt */ +/* 60 */ + ushort lbasecs[2]; /* # LBA user addressable sectors */ + ushort dmasingle; + ushort dmadouble; +/* 64 */ + ushort reserved3[64]; + ushort vendor[32]; /* vendor specific */ + ushort reserved4[96]; +}; + +/* + * get parameters from the drive + */ +static void +ataident(Drive *dp) +{ + Controller *cp; + char *buf; + Ident *ip; + char id[21]; + + cp = dp->cp; + buf = smalloc(Maxxfer); + qlock(cp); + if(waserror()){ + cp->buf = 0; + qunlock(cp); + free(buf); + nexterror(); + } + + cmdreadywait(dp); + + ilock(&cp->reglock); + cp->nsecs = 1; + cp->sofar = 0; + cp->cmd = Cident; + cp->dp = dp; + cp->buf = buf; + outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4)); + outb(cp->pbase+Pcmd, Cident); + iunlock(&cp->reglock); + + atasleep(cp, 5000); + if(cp->status & Serr){ + DPRINT("bad disk ident status\n"); + error(Eio); + } + ip = (Ident*)buf; + + /* + * this function appears to respond with an extra interrupt after + * the ident information is read, except on the safari. The following + * delay gives this extra interrupt a chance to happen while we are quiet. + * Otherwise, the interrupt may come during a subsequent read or write, + * causing a panic and much confusion. + */ + if (cp->cmd == Cident2) + tsleep(&cp->r, return0, 0, 10); + + memmove(id, ip->model, sizeof(id)-1); + id[sizeof(id)-1] = 0; + + if(ip->capabilities & (1<<9)){ + dp->lba = 1; + dp->sectors = (ip->lbasecs[0]) | (ip->lbasecs[1]<<16); + dp->cap = dp->bytes * dp->sectors; +/*print("\nata%d model %s with %d lba sectors\n", dp->drive, id, dp->sectors);/**/ + } else { + dp->lba = 0; + + /* use default (unformatted) settings */ + dp->cyl = ip->cyls; + dp->heads = ip->heads; + dp->sectors = ip->s2t; +/*print("\nata%d model %s with default %d cyl %d head %d sec\n", dp->drive, + id, dp->cyl, dp->heads, dp->sectors);/**/ + + if(ip->cvalid&(1<<0)){ + /* use current settings */ + dp->cyl = ip->ccyls; + dp->heads = ip->cheads; + dp->sectors = ip->cs2t; +/*print("\tchanged to %d cyl %d head %d sec\n", dp->cyl, dp->heads, dp->sectors);/**/ + } + dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors; + } + cp->lastcmd = cp->cmd; + cp->cmd = 0; + cp->buf = 0; + free(buf); + poperror(); + qunlock(cp); +} + +/* + * probe the given sector to see if it exists + */ +static int +ataprobe(Drive *dp, int cyl, int sec, int head) +{ + Controller *cp; + char *buf; + int rv; + + cp = dp->cp; + buf = smalloc(Maxxfer); + qlock(cp); + if(waserror()){ + free(buf); + qunlock(cp); + nexterror(); + } + + cmdreadywait(dp); + + ilock(&cp->reglock); + cp->cmd = Cread; + cp->dp = dp; + cp->status = 0; + cp->nsecs = 1; + cp->sofar = 0; + + outb(cp->pbase+Pcount, 1); + outb(cp->pbase+Psector, sec+1); + outb(cp->pbase+Pdh, DHmagic | head | (dp->lba<<6) | (dp->drive<<4)); + outb(cp->pbase+Pcyllsb, cyl); + outb(cp->pbase+Pcylmsb, cyl>>8); + outb(cp->pbase+Pcmd, Cread); + iunlock(&cp->reglock); + + atasleep(cp, 5000); + + if(cp->status & Serr) + rv = -1; + else + rv = 0; + + cp->buf = 0; + free(buf); + poperror(); + qunlock(cp); + return rv; +} + +/* + * figure out the drive parameters + */ +static void +ataparams(Drive *dp) +{ + int i, hi, lo; + + /* + * first try the easy way, ask the drive and make sure it + * isn't lying. + */ + dp->bytes = 512; + ataident(dp); + if(dp->lba){ + i = dp->sectors - 1; + if(ataprobe(dp, (i>>8)&0xffff, (i&0xff)-1, (i>>24)&0xf) == 0) + return; + } else { + if(ataprobe(dp, dp->cyl-1, dp->sectors-1, dp->heads-1) == 0) + return; + } + + /* + * the drive lied, determine parameters by seeing which ones + * work to read sectors. + */ + dp->lba = 0; + for(i = 0; i < 32; i++) + if(ataprobe(dp, 0, 0, i) < 0) + break; + dp->heads = i; + for(i = 0; i < 128; i++) + if(ataprobe(dp, 0, i, 0) < 0) + break; + dp->sectors = i; + for(i = 512; ; i += 512) + if(ataprobe(dp, i, dp->sectors-1, dp->heads-1) < 0) + break; + lo = i - 512; + hi = i; + for(; hi-lo > 1;){ + i = lo + (hi - lo)/2; + if(ataprobe(dp, i, dp->sectors-1, dp->heads-1) < 0) + hi = i; + else + lo = i; + } + dp->cyl = lo + 1; + dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors; +} + +/* + * Read block replacement table. + * The table is just ascii block numbers. + */ +static void +atareplinit(Drive *dp) +{ + char *line[Nrepl+1]; + char *field[1]; + ulong n; + int i; + char *buf; + + /* + * check the partition is big enough + */ + if(dp->repl.p->end - dp->repl.p->start < Nrepl+1){ + dp->repl.p = 0; + return; + } + + buf = smalloc(Maxxfer); + if(waserror()){ + free(buf); + nexterror(); + } + + /* + * read replacement table from disk, null terminate + */ + ataxfer(dp, dp->repl.p, Cread, 0, dp->bytes, buf); + buf[dp->bytes-1] = 0; + + /* + * parse replacement table. + */ + n = getfields(buf, line, Nrepl+1, 1, "\n"); + if(strncmp(line[0], REPLMAGIC, sizeof(REPLMAGIC)-1)){ + dp->repl.p = 0; + } else { + for(dp->repl.nrepl = 0, i = 1; i < n; i++, dp->repl.nrepl++){ + if(getfields(line[i], field, 1, 1, " ") != 1) + break; + dp->repl.blk[dp->repl.nrepl] = strtoul(field[0], 0, 0); + if(dp->repl.blk[dp->repl.nrepl] <= 0) + break; + } + } + free(buf); + poperror(); +} + +/* + * read partition table. The partition table is just ascii strings. + */ +static void +atapart(Drive *dp) +{ + Partition *pp; + char *line[Npart+1]; + char *field[3]; + ulong n; + int i; + char *buf; + + sprint(dp->vol, "hd%ld", dp - ata); + + /* + * we always have a partition for the whole disk + * and one for the partition table + */ + pp = &dp->p[0]; + strcpy(pp->name, "disk"); + pp->start = 0; + pp->end = dp->cap / dp->bytes; + pp++; + strcpy(pp->name, "partition"); + pp->start = dp->p[0].end - 1; + pp->end = dp->p[0].end; + pp++; + dp->npart = 2; + + /* + * initialise the bad-block replacement info + */ + dp->repl.p = 0; + + buf = smalloc(Maxxfer); + if(waserror()){ + free(buf); + nexterror(); + } + + /* + * read last sector from disk, null terminate. This used + * to be the sector we used for the partition tables. + * However, this sector is special on some PC's so we've + * started to use the second last sector as the partition + * table instead. To avoid reconfiguring all our old systems + * we first look to see if there is a valid partition + * table in the last sector. If so, we use it. Otherwise + * we switch to the second last. + */ + ataxfer(dp, dp->p+1, Cread, 0, dp->bytes, buf); + buf[dp->bytes-1] = 0; + n = getfields(buf, line, Npart+1, 1, "\n"); + if(n == 0 || strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1)){ + dp->p[0].end--; + dp->p[1].start--; + dp->p[1].end--; + ataxfer(dp, dp->p+1, Cread, 0, dp->bytes, buf); + buf[dp->bytes-1] = 0; + n = getfields(buf, line, Npart+1, 1, "\n"); + } + + /* + * parse partition table. + */ + if(n > 0 && strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1) == 0){ + for(i = 1; i < n; i++){ + switch(getfields(line[i], field, 3, 1, " ")) { + case 2: + if(strcmp(field[0], "unit") == 0) + strncpy(dp->vol, field[1], KNAMELEN); + break; + case 3: + strncpy(pp->name, field[0], KNAMELEN); + if(strncmp(pp->name, "repl", KNAMELEN) == 0) + dp->repl.p = pp; + pp->start = strtoul(field[1], 0, 0); + pp->end = strtoul(field[2], 0, 0); + if(pp->start > pp->end || pp->end > dp->p[0].end) + break; + dp->npart++; + pp++; + } + } + } + free(buf); + poperror(); + + if(dp->repl.p) + atareplinit(dp); +} + +enum +{ + Maxloop= 10000, +}; + +/* + * we get an interrupt for every sector transferred + */ +static void +ataintr(Ureg*, void *arg) +{ + Controller *cp; + Drive *dp; + long loop; + char *addr; + + cp = arg; + dp = cp->dp; + + ilock(&cp->reglock); + + loop = 0; + while((cp->status = inb(cp->pbase+Pstatus)) & Sbusy){ + if(++loop > Maxloop) { + DPRINT("cmd=%ux status=%ux\n", + cp->cmd, inb(cp->pbase+Pstatus)); + panic("ataintr: wait busy"); + } + } + + switch(cp->cmd){ + case Cwrite: + if(cp->status & Serr){ + cp->lastcmd = cp->cmd; + cp->cmd = 0; + cp->error = inb(cp->pbase+Perror); + wakeup(&cp->r); + break; + } + cp->sofar++; + if(cp->sofar < cp->nsecs){ + loop = 0; + while(((cp->status = inb(cp->pbase+Pstatus)) & Sdrq) == 0) + if(++loop > Maxloop) { + DPRINT("cmd=%ux status=%ux\n", + cp->cmd, inb(cp->pbase+Pstatus)); + panic("ataintr: write"); + } + addr = cp->buf; + if(addr){ + addr += cp->sofar*dp->bytes; + outss(cp->pbase+Pdata, addr, dp->bytes/2); + } + } else{ + cp->lastcmd = cp->cmd; + cp->cmd = 0; + wakeup(&cp->r); + } + break; + case Cread: + case Cident: + loop = 0; + while((cp->status & (Serr|Sdrq)) == 0){ + if(++loop > Maxloop) { + DPRINT("cmd=%ux status=%ux\n", + cp->cmd, inb(cp->pbase+Pstatus)); + panic("ataintr: read/ident"); + } + cp->status = inb(cp->pbase+Pstatus); + } + if(cp->status & Serr){ + cp->lastcmd = cp->cmd; + cp->cmd = 0; + cp->error = inb(cp->pbase+Perror); + wakeup(&cp->r); + break; + } + addr = cp->buf; + if(addr){ + addr += cp->sofar*dp->bytes; + inss(cp->pbase+Pdata, addr, dp->bytes/2); + } + cp->sofar++; + if(cp->sofar > cp->nsecs) + print("ataintr %d %d\n", cp->sofar, cp->nsecs); + if(cp->sofar >= cp->nsecs){ + cp->lastcmd = cp->cmd; + if (cp->cmd == Cread) + cp->cmd = 0; + else + cp->cmd = Cident2; + wakeup(&cp->r); + } + break; + case Cinitparam: + case Csetbuf: + case Cidle: + case Cstandby: + case Cpowerdown: + cp->lastcmd = cp->cmd; + cp->cmd = 0; + wakeup(&cp->r); + break; + case Cident2: + cp->lastcmd = cp->cmd; + cp->cmd = 0; + break; + default: + print("weird disk interrupt, cmd=%.2ux, lastcmd= %.2ux status=%.2ux\n", + cp->cmd, cp->lastcmd, cp->status); + break; + } + + iunlock(&cp->reglock); +} + +void +hardclock(void) +{ + int drive; + Drive *dp; + Controller *cp; + int diff; + + if(spindowntime <= 0) + return; + + for(drive = 0; drive < nhard; drive++){ + dp = &ata[drive]; + cp = dp->cp; + + diff = TK2SEC(m->ticks - dp->usetime); + if((dp->state == Sspinning) && (diff >= spindowntime)){ + ilock(&cp->reglock); + cp->cmd = Cstandby; + outb(cp->pbase+Pcount, 0); + outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4) | 0); + outb(cp->pbase+Pcmd, cp->cmd); + iunlock(&cp->reglock); + dp->state = Sstandby; + } + } +} + +Dev atadevtab = { + 'H', + "ata", + + devreset, + atainit, + devshutdown, + ataattach, + atawalk, + atastat, + ataopen, + devcreate, + ataclose, + ataread, + devbread, + atawrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/cerf1110/devcerf.c b/os/cerf1110/devcerf.c new file mode 100644 index 00000000..3c0fc999 --- /dev/null +++ b/os/cerf1110/devcerf.c @@ -0,0 +1,128 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "io.h" + + +enum{ + Qdir, + Qled, +}; + +static +Dirtab cerftab[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "cerfled", {Qled, 0}, 0, 0660, +}; + +static void +cerfinit(void) /* default in dev.c */ +{ + int s; + + s = splhi(); + GPIOREG->gpdr |= 0xF; + GPIOREG->gpsr = 1<<0; /* we're here */ + splx(s); +} + +static Chan* +cerfattach(char* spec) +{ + return devattach('T', spec); +} + +static Walkqid* +cerfwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, cerftab, nelem(cerftab), devgen); +} + +static int +cerfstat(Chan* c, uchar *db, int n) +{ + return devstat(c, db, n, cerftab, nelem(cerftab), devgen); +} + +static Chan* +cerfopen(Chan* c, int omode) +{ + return devopen(c, omode, cerftab, nelem(cerftab), devgen); +} + +static void +cerfclose(Chan* c) +{ + USED(c); +} + +static long +cerfread(Chan* c, void* a, long n, vlong offset) +{ + char buf[16]; + + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, cerftab, nelem(cerftab), devgen); + case Qled: + snprint(buf, sizeof(buf), "%2.2lux", GPIOREG->gplr&0xF); + return readstr(offset, a, n, buf); + default: + n=0; + break; + } + return n; +} + +static long +cerfwrite(Chan* c, void* a, long n, vlong) +{ + char buf[16]; + ulong v; + + switch((ulong)c->qid.path){ + case Qled: + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = 0; + v = GPIOREG->gplr & 0xF; + if(buf[0] == '+') + v |= strtoul(buf+1, nil, 0); + else if(buf[0] == '-') + v &= ~strtoul(buf+1, nil, 0); + else + v = strtoul(buf, nil, 0); + GPIOREG->gpsr = v & 0xF; + GPIOREG->gpcr = ~v & 0xF; + break; + default: + error(Ebadusefd); + } + return n; +} + +Dev cerfdevtab = { + 'T', + "cerf", + + devreset, + cerfinit, + devshutdown, + cerfattach, + cerfwalk, + cerfstat, + cerfopen, + devcreate, + cerfclose, + cerfread, + devbread, + cerfwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/cerf1110/ether8900.c b/os/cerf1110/ether8900.c new file mode 100644 index 00000000..fc9ea12e --- /dev/null +++ b/os/cerf1110/ether8900.c @@ -0,0 +1,702 @@ +/* + * Crystal CS8900 ethernet controller + * + * Todo: + * - promiscuous + * + * Copyright © 1998 Vita Nuova Limited. All rights reserved. + * Revisions Copyright © 2000,2003 Vita Nuova Holdings Limited. All rights reserved. + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/netif.h" + +#include "etherif.h" + +typedef struct Ctlr Ctlr; + +/* + * The CS8900 can be addressed from either ISA I/O space + * or ISA memory space at the following virtual addresses, + * depending on the hardware's wiring. MEMORY controls + * use of memory space. + * The cs8900 address pins are shifted by 1 relative to the CPU. + */ +enum {//18000000 + IsaIOBase = 0x08000000, + IsaMemBase = 0xe0000000, + + IOBase = 0x300, + MemBase = 0xc0000, + + MEMORY = 0, /* set non-zero if memory mode to be used */ + DORESET = 1, /* send soft-reset during initialisation */ + DEBUG = 0, +}; + +#define IOSHIFT 0 /* was 2 */ +#define IOREG(r) (IsaIOBase+((IOBase+(r))<>1; --ns >= 0;) + *d++ = in16(RxTxData); + if(len & 1) + *(uchar*)d = in16(RxTxData); + return; + } + d = ad; + s = (ushort*)IsaMemBase + MemBase + RxFrame; + for(ns = len>>1; --ns >= 0;){ + *d++ = *s; + s += 2; + } + if(len & 1) + *(uchar*)d = *s; +} + +static void +copypktout(void *as, int len) +{ + ushort *s, *d; + int ns; + + if(!MEMORY){ + s = as; + ns = (len+1)>>1; + while(--ns >= 0) + out16(RxTxData, *s++); + return; + } + s = as; + d = (ushort*)IsaMemBase + MemBase + TxFrame; + ns = (len+1)>>1; + while(--ns >= 0){ + *d = *s++; + d += 2; + } +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + Ctlr *ctlr; + char *p; + int len; + + if(n == 0) + return 0; + + ctlr = ether->ctlr; + p = malloc(READSTR); + len = snprint(p, READSTR, "Overflow: %ud\n", ether->overflows); + len += snprint(p+len, READSTR-len, "CRC Error: %ud\n", ether->crcs); + snprint(p+len, READSTR-len, "Collision Seen: %lud\n", ctlr->collisions); + + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static void +promiscuous(void* arg, int on) +{ + USED(arg, on); +} + +static void +attach(Ether *ether) +{ + int reg; + + USED(ether); + /* enable transmit and receive */ + reg = regr(BusCtl); + regw(BusCtl, reg|EnableIRQ); + reg = regr(LineCtl); + regw(LineCtl, reg|SerRxOn|SerTxOn); + if(DEBUG){ + iprint("bus=%4.4ux line=%4.4ux\n", regr(BusCtl), regr(LineCtl)); + iprint("rc=%4.4ux tc=%4.4ux bc=%4.4ux\n", regr(RxCfg), regr(TxCfg), regr(BufCfg)); + } +} + +static void +txstart(Ether *ether, int dowait) +{ + int len, status; + Ctlr *ctlr; + Block *b; + + ctlr = ether->ctlr; + for(;;){ + if((b = ctlr->waiting) == nil){ + if((b = qget(ether->oq)) == nil) + break; + }else{ + if(!dowait) + break; + ctlr->waiting = nil; + } + len = BLEN(b); + if(MEMORY){ + regw(TxCmd, TxSt381); + regw(TxLen, len); + }else{ + out16(TxCmdIO, TxStAll); + out16(TxLenIO, len); + } + status = regr(BusSt); + if((status & Rdy4TxNOW) == 0) { + ctlr->waiting = b; + break; + } + /* + * Copy the packet to the transmit buffer. + */ + copypktout(b->rp, len); + freeb(b); + } +} + +static void +transmit(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + txstart(ether, 0); + iunlock(ctlr); +} + +static void +interrupt(Ureg*, void *arg) +{ + Ether *ether; + Ctlr *ctlr; + int len, events, status; + Block *b; + + ether = arg; + ctlr = ether->ctlr; + ilock(ctlr); + while((events = (MEMORY?regr(Isq):in16(IsqIO))) != 0) { + status = events&RegContent; + if(DEBUG) + iprint("status %4.4ux event %4.4ux\n", status, events); + switch(events&Regnum) { + + case IsqBufEvent: + if(status&Rdy4Tx) { + if((b = ctlr->waiting) != nil){ + ctlr->waiting = nil; + copypktout(b->rp, BLEN(b)); + freeb(b); + /* wait for IsqTxEvent to send remaining packets in txstart */ + }else + txstart(ether, 0); + } + break; + + case IsqRxEvent: + if(status&RxOK) { + len = regr(RxLen); + if(DEBUG) + iprint("rxlen=%d\n", len); + if((b = iallocb(len)) != 0) { + copypktin(b->wp, len); + b->wp += len; + etheriq(ether, b, 1); + } + } + break; + + case IsqTxEvent: + if(status&TxOK) + txstart(ether, 1); + break; + + case IsqRxMiss: + ether->overflows++; + break; + + case IsqTxCol: + ctlr->collisions++; + break; + } + } + iunlock(ctlr); +} + +static int +eepromwait(void) +{ + int i; + + for(i=0; i<100000; i++) + if((regIOr(SelfSt) & SIBUSY) == 0) + return 0; + return -1; +} + +static int +eepromrd(void *buf, int off, int n) +{ + int i; + ushort *p; + + p = buf; + n /= 2; + for(i=0; i=100){ + iprint("failed init: reg(0xA): %4.4ux, should be 0x3000\n", in16(PpPtr)); + return -1; + } + } +iprint("8900: %4.4ux (selfst) %4.4ux (linest)\n", regIOr(SelfSt), regIOr(LineSt)); +iprint("8900: %4.4ux %4.4ux\n", regIOr(Ern), regIOr(Pic)); + + /* + * Identify the chip by reading the Pic register. + * The EISA registration number is in the low word + * and the product identification code in the high code. + * The ERN for Crystal Semiconductor is 0x630e. + * Bits 0-7 and 13-15 of the Pic should be zero for a CS8900. + */ + if(regIOr(Ern) != 0x630e || (regIOr(Pic) & 0xe0ff) != 0) + return -1; + + if(ether->ctlr == nil) + ether->ctlr = malloc(sizeof(Ctlr)); + ctlr = ether->ctlr; + + reg = regIOr(Pic); + ctlr->model = reg>>14; + ctlr->rev = (reg >> 8) & 0x1F; + + ether->mbps = 10; + + memset(ea, 0, Eaddrlen); + easet = memcmp(ea, ether->ea, Eaddrlen); + memset(buf, 0, sizeof(buf)); + if(regIOr(SelfSt) & EepromPresent) { /* worth a look */ + if(eepromrd(buf, Edataoff, sizeof(buf)) >= 0){ + for(i=0; i<3; i++){ + ether->ea[2*i] = buf[i]; + ether->ea[2*i+1] = buf[i] >> 8; + } + easet = 1; + }else + iprint("cs8900: can't read EEPROM\n"); + } + if(!easet){ + iprint("cs8900: ethernet address not configured\n"); + return -1; + } + memmove(ea, ether->ea, Eaddrlen); + + if(DORESET){ + /* + * Reset the chip and ensure 16-bit mode operation + */ + regIOw(SelfCtl, RESET); + delay(10); + i=in8(PpPtr); USED(i); + i=in8(PpPtr+1); USED(i); + i=in8(PpPtr); USED(i); + i=in8(PpPtr+1); USED(i); + + /* + * Wait for initialisation and EEPROM reads to complete + */ + i=0; + for(;;) { + short st = regIOr(SelfSt); + if((st&SIBUSY) == 0 && st&INITD) + break; + if(i++ > 1000000) + panic("cs8900: initialisation failed"); + } + } + + if(MEMORY){ + /* + * Enable memory mode operation. + */ + regIOw(Mba, MemBase & 0xffff); + regIOw(Mba+2, MemBase >> 16); + regIOw(BusCtl, MemoryE|UseSA); + } + + /* + * Enable 10BASE-T half duplex, transmit in interrupt mode + */ + reg = regr(LineCtl); + regw(LineCtl, reg&~Iface); + reg = regr(TestCtl); + if(ether->fullduplex) + regw(TestCtl, reg|FDX); + else + regw(TestCtl, reg&~FDX); + regw(BufCfg, Rdy4TxiE|TxUnderruniE); + regw(TxCfg, TxOKiE|AnycolliE|LossofCRSiE|Coll16iE); + regw(RxCfg, RxOKiE|CRCerroriE|RuntiE|ExtradataiE); + regw(RxCtl, RxOKA|IndividualA|BroadcastA); + + for(i=0; iattach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = ifstat; + + ether->arg = ether; + ether->promiscuous = promiscuous; + + ether->itype = BusGPIOrising; /* TO DO: this shouldn't be done here */ + + return 0; +} + +void +ether8900link(void) +{ + addethercard("CS8900", reset); +} diff --git a/os/cerf1110/fns.h b/os/cerf1110/fns.h new file mode 100644 index 00000000..2ad379e4 --- /dev/null +++ b/os/cerf1110/fns.h @@ -0,0 +1,179 @@ +#include "../port/portfns.h" + +ulong aifinit(uchar *aifarr); +void aamloop(int); +int archaudiopower(int); +void archaudiomute(int); +void archaudioamp(int); +int archaudiospeed(int, int); +void archcodecreset(void); +void archconfinit(void); +void archconsole(void); +int archflash12v(int); +int archhooksw(int); +long archkprofmicrosecondspertick(void); +void archkprofenable(int); +void archlcdenable(int); +void archpowerdown(void); +void archpowerup(void); +void archreboot(void); +void archreset(void); +vlong archrdtsc(void); +ulong archrdtsc32(void); +void archuartpower(int, int); +void blankscreen(int); +ulong call_apcs(ulong addr, int nargs, ...); +ulong call_apcs0(ulong addr); +ulong call_apcs1(ulong addr, ulong a1); +ulong call_apcs2(ulong addr, ulong a1, ulong a2); +ulong call_apcs3(ulong addr, ulong a1, ulong a2, ulong a3); +void cisread(int slotno, void (*f)(int, uchar *)); +void clockcheck(void); +void clockinit(void); +void clockpoll(void); +#define coherence() /* nothing to do for cache coherence for uniprocessor */ +void cursorhide(void); +void cursorunhide(void); +void dcflush(void*, ulong); +void dcflushall(void); +void dcinval(void); +int dmaidle(Dma*); +Dma* dmasetup(int device, int direction, int bigend, void(*)(void*,ulong), void*); +int dmastart(Dma*, void*, int); +int dmacontinue(Dma*, void*, int); +void dmastop(Dma*); +int dmaerror(Dma*); +void dmafree(Dma*); +void dmareset(void); +void dmawait(Dma*); +void dumplongs(char *, ulong *, int); +void dumpregs(Ureg* ureg); +void dumpstack(void); +int fpiarm(Ureg*); +void fpinit(void); +ulong getcallerpc(void*); +ulong getcpsr(void); +ulong getcpuid(void); +ulong getspsr(void); +void gotopc(ulong); + +void icflushall(void); +void _idlemode(void); +void (*idle)(void); +void idlehands(void); +int inb(ulong); +int ins(ulong); +ulong inl(ulong); +void outb(ulong, int); +void outs(ulong, int); +void outl(ulong, ulong); +void inss(ulong, void*, int); +void outss(ulong, void*, int); +void insb(ulong, void*, int); +void outsb(ulong, void*, int); +void intrdisable(int, void (*)(Ureg*, void*), void*, int, char*); +void intrenable(int, void (*)(Ureg*, void*), void*, int, char*); +void iofree(int); +#define iofree(x) +void ioinit(void); +int iounused(int, int); +int ioalloc(int, int, int, char*); +#define ioalloc(a,b,c,d) 0 +int iprint(char*, ...); +void installprof(void (*)(Ureg *, int)); +int isvalid_va(void*); +void kbdinit(void); +void L3init(void); +int L3read(int, void*, int); +int L3write(int, void*, int); +void lcd_setbacklight(int); +void lcd_sethz(int); +void lights(ulong); +void links(void); +ulong mcpgettfreq(void); +void mcpinit(void); +void mcpsettfreq(ulong tfreq); +void mcpspeaker(int, int); +void mcptelecomsetup(ulong hz, int adm, int xint, int rint); +ushort mcpadcread(int ts); +void mcptouchsetup(int ts); +void mcptouchintrenable(void); +void mcptouchintrdisable(void); +void mcpgpiowrite(ushort mask, ushort data); +void mcpgpiosetdir(ushort mask, ushort dir); +ushort mcpgpioread(void); +void* minicached(void*); +void minidcflush(void); +void mmuenable(ulong); +ulong mmugetctl(void); +ulong mmugetdac(void); +ulong mmugetfar(void); +ulong mmugetfsr(void); +void mmuinit(void); +void* mmuphysmap(ulong, ulong); +void mmuputctl(ulong); +void mmuputdac(ulong); +void mmuputfsr(ulong); +void mmuputttb(ulong); +void mmureset(void); +void mouseinit(void); +void nowriteSeg(void *, void *); +void* pa2va(ulong); +void pcmcisread(PCMslot*); +int pcmcistuple(int, int, int, void*, int); +PCMmap* pcmmap(int, ulong, int, int); +void pcmunmap(int, PCMmap*); +int pcmpin(int slot, int type); +void pcmpower(int slotno, int on); +int pcmpowered(int); +void pcmreset(int); +void pcmsetvcc(int, int); +void pcmsetvpp(int, int); +int pcmspecial(char *idstr, ISAConf *isa); +void pcmspecialclose(int slotno); +void pcmintrenable(int, void (*)(Ureg*, void*), void*); +void powerenable(void (*)(int)); +void powerdisable(void (*)(int)); +void powerdown(void); +void powerinit(void); +void powersuspend(void); +#define procsave(p) +#define procrestore(p) +long rtctime(void); +void screeninit(void); +void (*screenputs)(char*, int); +int segflush(void*, ulong); +void setpanic(void); +void setr13(int, void*); +int splfhi(void); +int splflo(void); +void _suspendcode(void); +void tlbinvalidate(void); +void tlbinvalidateaddr(void*); +void trapinit(void); +void trapstacks(void); +void trapspecial(int (*)(Ureg *, uint)); +int uartprint(char*, ...); +void uartspecial(int, int, char, Queue**, Queue**, int (*)(Queue*, int)); +ulong va2pa(void*); +void vectors(void); +void vtable(void); +#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) +int wasbusy(int); + +#define KADDR(p) ((void *) p) +#define PADDR(v) va2pa((void*)(v)) + +// #define timer_start() (*OSCR) +// #define timer_ticks(t) (*OSCR - (ulong)(t)) +ulong timer_start(void); +ulong timer_ticks(ulong); +int timer_devwait(ulong *adr, ulong mask, ulong val, int ost); +void timer_setwatchdog(int ost); +void timer_delay(int ost); +ulong ms2tmr(int ms); +int tmr2ms(ulong t); +void delay(int ms); +ulong us2tmr(int us); +int tmr2us(ulong t); +void microdelay(int us); diff --git a/os/cerf1110/io.h b/os/cerf1110/io.h new file mode 100644 index 00000000..4e8cbc39 --- /dev/null +++ b/os/cerf1110/io.h @@ -0,0 +1 @@ +#include "../sa1110/sa1110io.h" diff --git a/os/cerf1110/main.c b/os/cerf1110/main.c new file mode 100644 index 00000000..58bd792f --- /dev/null +++ b/os/cerf1110/main.c @@ -0,0 +1,255 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" +#include "version.h" + +Mach *m = (Mach*)MACHADDR; +Proc *up = 0; +Vectorpage *page0 = (Vectorpage*)KZERO; /* doubly-mapped to AIVECADDR */ +Conf conf; + +extern ulong kerndate; +extern int cflag; +extern int main_pool_pcnt; +extern int heap_pool_pcnt; +extern int image_pool_pcnt; +ulong cpuidlecount; + +static void +poolsizeinit(void) +{ + ulong nb; + + nb = conf.npage*BY2PG; + poolsize(mainmem, (nb*main_pool_pcnt)/100, 0); + poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0); + poolsize(imagmem, (nb*image_pool_pcnt)/100, 1); +} + +void +main(void) +{ + memset(edata, 0, end-edata); /* clear the BSS */ + memset(m, 0, sizeof(Mach)); /* clear the mach struct */ + conf.nmach = 1; + archreset(); + dmareset(); + quotefmtinstall(); + confinit(); + xinit(); + mmuinit(); + poolinit(); + poolsizeinit(); + trapinit(); + clockinit(); + printinit(); + screeninit(); + procinit(); + links(); + chandevreset(); + + eve = strdup("inferno"); + + archconsole(); + kbdinit(); + + print("%ld MHz id %8.8lux\n", (m->cpuhz+500000)/1000000, getcpuid()); + print("\nInferno %s\n", VERSION); + print("Vita Nuova\n"); + print("conf %s (%lud) jit %d\n\n",conffile, kerndate, cflag); + + userinit(); + schedinit(); +} + +void +reboot(void) +{ + exit(0); +} + +void +halt(void) +{ + spllo(); + print("cpu halted\n"); + for(;;){ + /* nothing to do */ + } +} + +void +confinit(void) +{ + ulong base; + + archconfinit(); + + base = PGROUND((ulong)end); + conf.base0 = base; + + conf.base1 = 0; + conf.npage1 = 0; + + conf.npage0 = (conf.topofmem - base)/BY2PG; + + conf.npage = conf.npage0 + conf.npage1; + conf.ialloc = (((conf.npage*(main_pool_pcnt))/100)/2)*BY2PG; + + conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5; + conf.nmach = 1; + +} + +void +init0(void) +{ + Osenv *o; + char buf[2*KNAMELEN]; + + up->nerrlab = 0; + + spllo(); + + if(waserror()) + panic("init0 %r"); + /* + * These are o.k. because rootinit is null. + * Then early kproc's will have a root and dot. + */ + o = up->env; + o->pgrp->slash = namec("#/", Atodir, 0, 0); + cnameclose(o->pgrp->slash->name); + o->pgrp->slash->name = newcname("/"); + o->pgrp->dot = cclone(o->pgrp->slash); + + chandevinit(); + + if(!waserror()){ + ksetenv("cputype", "arm", 0); + snprint(buf, sizeof(buf), "arm %s", conffile); + ksetenv("terminal", buf, 0); + poperror(); + } + + poperror(); + + disinit("/osinit.dis"); +} + +void +userinit(void) +{ + Proc *p; + Osenv *o; + + p = newproc(); + o = p->env; + + o->fgrp = newfgrp(nil); + o->pgrp = newpgrp(); + o->egrp = newegrp(); + kstrdup(&o->user, eve); + + strcpy(p->text, "interp"); + + p->fpstate = FPINIT; + + /* + * Kernel Stack + * + * N.B. The -12 for the stack pointer is important. + * 4 bytes for gotolabel's return PC + */ + p->sched.pc = (ulong)init0; + p->sched.sp = (ulong)p->kstack+KSTACK-8; + + ready(p); +} + +void +exit(int inpanic) +{ + up = 0; + + /* Shutdown running devices */ + chandevshutdown(); + + if(inpanic && 0){ + print("Hit the reset button\n"); + for(;;) + clockpoll(); + } + archreboot(); +} + +static void +linkproc(void) +{ + spllo(); + if (waserror()) + print("error() underflow: %r\n"); + else + (*up->kpfun)(up->arg); + pexit("end proc", 1); +} + +void +kprocchild(Proc *p, void (*func)(void*), void *arg) +{ + p->sched.pc = (ulong)linkproc; + p->sched.sp = (ulong)p->kstack+KSTACK-8; + + p->kpfun = func; + p->arg = arg; +} + +void +idlehands(void) +{ + cpuidlecount++; + INTRREG->iccr = 1; /* only unmasked interrupts will stop idle mode */ + idle(); +} + +/* stubs */ +void +setfsr(ulong) +{ +} + +ulong +getfsr() +{ + return 0; +} + +void +setfcr(ulong) +{ +} + +ulong +getfcr() +{ + return 0; +} + +void +fpinit(void) +{ +} + +void +FPsave(void*) +{ +} + +void +FPrestore(void*) +{ +} diff --git a/os/cerf1110/mem.h b/os/cerf1110/mem.h new file mode 100644 index 00000000..19d55b07 --- /dev/null +++ b/os/cerf1110/mem.h @@ -0,0 +1,200 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ + +/* + * Sizes + */ +#define BI2BY 8 /* bits per byte */ +#define BI2WD 32 /* bits per word */ +#define BY2WD 4 /* bytes per word */ +#define BY2V 8 /* bytes per double word */ +#define BY2PG 4096 /* bytes per page */ +#define WD2PG (BY2PG/BY2WD) /* words per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1)) +#define PGROUND(s) ROUND(s, BY2PG) +#define BIT(n) (1< 4096 descriptors -> 16Kb + * L2: 8-bit index -> 256 descriptors -> 1Kb + * Each L2 descriptor has access permissions for 4 1Kb sub-pages. + * + * TTB + L1Tx gives address of L1 descriptor + * L1 descriptor gives PTBA + * PTBA + L2Tx gives address of L2 descriptor + * L2 descriptor gives PBA + */ +#define MmuSection (1<<20) +#define MmuLargePage (1<<16) +#define MmuSmallPage (1<<12) +#define MmuTTB(pa) ((pa) & ~0x3FFF) /* translation table base */ +#define MmuL1x(pa) (((pa)>>20) & 0xFFF) /* L1 table index */ +#define MmuPTBA(pa) ((pa) & ~0x3FF) /* page table base address */ +#define MmuL2x(pa) (((pa)>>12) & 0xFF) /* L2 table index */ +#define MmuPBA(pa) ((pa) & ~0xFFF) /* page base address */ +#define MmuSBA(pa) ((pa) & ~0xFFFFF) /* section base address */ + +#define MmuL1type 0x03 +#define MmuL1page 0x01 /* descriptor is for L2 pages */ +#define MmuL1section 0x02 /* descriptor is for section */ + +#define MmuL2invalid 0x000 +#define MmuL2large 0x001 /* large */ +#define MmuL2small 0x002 /* small */ +#define MmuWB 0x004 /* data goes through write buffer */ +#define MmuIDC 0x008 /* data placed in cache */ + +#define MmuDAC(d) (((d) & 0xF)<<5) /* L1 domain */ +#define MmuAP(i, v) ((v)<<(((i)*2)+4)) /* access permissions */ +#define MmuL1AP(v) MmuAP(3, (v)) +#define MmuL2AP(v) MmuAP(3, (v))|MmuAP(2, (v))|MmuAP(1, (v))|MmuAP(0, (v)) +#define MmuAPsro 0 /* supervisor ro if S|R */ +#define MmuAPsrw 1 /* supervisor rw */ +#define MmuAPuro 2 /* supervisor rw + user ro */ +#define MmuAPurw 3 /* supervisor rw + user rw */ diff --git a/os/cerf1110/mkfile b/os/cerf1110/mkfile new file mode 100644 index 00000000..693f3248 --- /dev/null +++ b/os/cerf1110/mkfile @@ -0,0 +1,101 @@ +<../../mkconfig +TKSTYLE=std + +#Configurable parameters + +CONF=cerf #default configuration +CONFLIST=cerf + +SYSTARG=$OSTARG +OBJTYPE=arm +INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin #path of directory where kernel is installed +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS + +KTZERO=0xC0008010 + +OBJ=\ + l.$O\ + clock.$O\ + dma.$O\ + fpi.$O\ + fpiarm.$O\ + fpimem.$O\ + main.$O\ + mmu.$O\ + trap.$O\ + $CONF.root.$O\ + $IP\ + $DEVS\ + $ETHERS\ + $LINKS\ + $PORT\ + $MISC\ + $OTHERS\ + +LIBNAMES=${LIBS:%=lib%.a} +LIBDIRS=$LIBS + +HFILES=\ + mem.h\ + dat.h\ + fns.h\ + io.h\ + ../sa1110/sa1110io.h\ + ../sa1110/fpi.h\ + +CFLAGS=-wFV -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp -I../sa1110 +KERNDATE=`{$NDATE} + +default:V: i$CONF i$CONF.p9 + +install:V: $INSTALLDIR/i$CONF + +i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES i$CONF.p9 + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD -s -o $target -H5 -T0xC0008010 -R4 -l $OBJ $CONF.$O $LIBFILES + +i$CONF.p9: $OBJ $CONF.c $CONF.root.h $LIBNAMES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD -o $target -T0xC0008010 -R4 -l $OBJ $CONF.$O $LIBFILES + +<../port/portmkfile + +%.$O: ../sa1110/%.c + $CC $CFLAGS -I. ../sa1110/$stem.c + +%.$O: ../sa1110/%.s + $AS -I. -I../sa1110 ../sa1110/$stem.s + +../init/$INIT.dis: ../init/$INIT.b + cd ../init; mk $INIT.dis + +clock.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +devether.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +devsapcm.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +main.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +trap.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h + +devether.$O $ETHERS: ../sa1110/etherif.h ../port/netif.h +$IP devip.$O: ../ip/ip.h +io.h:N: ../sa1110/sa1110io.h + +dummy:V: + +# to be moved to libinterp +bench.h:D: ../../module/bench.m + rm -f $target && limbo -a -I../../module ../../module/bench.m > $target +benchmod.h:D: ../../module/bench.m + rm -f $target && limbo -t Bench -I../../module ../../module/bench.m > $target +devbench.$O: bench.h benchmod.h + +devaudio.$O: devaudio.c + $CC $CFLAGS devaudio.c + +arch$CONF.$O: ../sa1110/etherif.h + +devuart.$O: ../sa1110/devuart.c + $CC $CFLAGS ../sa1110/devuart.c diff --git a/os/cerf250/NOTICE b/os/cerf250/NOTICE new file mode 100644 index 00000000..7c34dd62 --- /dev/null +++ b/os/cerf250/NOTICE @@ -0,0 +1,2 @@ +Inferno® Copyright © 1996-1999 Lucent Technologies Inc. All rights reserved. +Cerfcube 250 Inferno port Copyright © 2000,2003,2004 Vita Nuova Holdings Limited. All rights reserved. diff --git a/os/cerf250/README b/os/cerf250/README new file mode 100644 index 00000000..ee1ac2d1 --- /dev/null +++ b/os/cerf250/README @@ -0,0 +1,44 @@ +Booting Inferno on a Cerfboard 250 + +This is a preliminary version (work in progress) of Inferno +on an Intrinsyc Cerfboard 250 (without daughterboard[s]). +It and ../pxa were initially the results of a fairly idle `afternoon and an evening' port. +A little work has been done on it since then. +Updates will be available shortly to complete peripheral support +(at least for the Cerfboard 250), and provide suspend mode. + +It allows Inferno to boot up and take resources from the net, +chatting on the console. I2C to the PCF8563 clock and EEPROMs is +also supported. + +Substitute appropriate your own directory's name for /usr/inferno +in the following. + +Build the /usr/inferno/os/cerf250 kernel into /usr/inferno/os/cerf250/icerf: + mk +It uses common PXA25x code in ../pxa, as well as ../port etc. + +Make that icerf file available to the cerf cube by tftp. How you do that depends on +your host system. + +It should then be easy: + +1. Reset the cerf cube (power off/on), and quickly, during `hit a key ...' + hit a key. + +2. type + network.start() + download 10.0.0.1 "/usr/inferno/os/cerf250/icerf" 0xa0020000 + with appropriate substitution for boot server and file name. + +3. on success + jump 0xa0020020 + +it should run. + +once you're happy with it, the kernel image can replace the Linux one in flash. +i plan, however, to use my sqz code to compress it by about 50% with +fast decompression. + +forsyth@vitanuova.com +Fri Mar 19 16:42:07 GMT 2004 diff --git a/os/cerf250/archcerf.c b/os/cerf250/archcerf.c new file mode 100644 index 00000000..4ee9f4e7 --- /dev/null +++ b/os/cerf250/archcerf.c @@ -0,0 +1,266 @@ +/* + * Intrinsyc Cerfboard 250 + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" +#include "draw.h" +#include + +#include "../port/netif.h" +#include "etherif.h" +#include "../port/flashif.h" + +enum { + /* Cerf250 GPIO assignment */ + GPIO_LED0_o = 4, /* active high */ + GPIO_nCFD_i= 14, /* compact flash detect, active low: falling edge */ + + Maxmac= 4, /* number of MAC addresses taken from EEPROM */ +}; + +static uchar macaddrs[Maxmac][Eaddrlen]; + +void +archreset(void) +{ + GpioReg *g = GPIOREG; + + g->grer[0] = 0; + g->grer[1] = 0; + g->grer[2] = 0; + g->gfer[0] = 0; + g->gfer[1] = 0; + g->gfer[2] = 0; + g->gedr[0] = g->gedr[0]; + g->gedr[1] = g->gedr[1]; + g->gedr[2] = g->gedr[2]; + + g->gafr[2] |= GPAF(GPIO_FFRXD_1_i, 1); + g->gafr[2] |= GPAF(GPIO_FFTXD_2_o, 2); + g->gpdr[0] |= GPB(GPIO_LED0_o); + g->gpdr[1] |= GPB(GPIO_FFTXD_2_o); + g->gpsr[0] = GPB(GPIO_LED0_o); + + uartdebuginit(); +} + +void +ledset(int n) +{ + if(n) + GPIOREG->gpsr[0] = GPB(GPIO_LED0_o); + else + GPIOREG->gpcr[0] = GPB(GPIO_LED0_o); +} + +void +archconfinit(void) +{ + int w; + + conf.topofmem = PHYSMEM0+64*MB; +// w = PMGRREG->ppcr & 0x1f; + m->cpuhz = CLOCKFREQ*(27*2*2); +} + +void +archuartpower(int, int) +{ +} + +void +kbdinit(void) +{ +} + +void +archreboot(void) +{ + dcflushall(); + ledset(1); +// GPIOREG->gedr = 1<<0; + mmuputctl(mmugetctl() & ~CpCaltivec); /* restore bootstrap's vectors */ +// RESETREG->rsrr = 1; /* software reset */ + for(;;) + spllo(); +} + +void +archflashwp(Flash*, int) +{ +} + +/* + * for devflash.c:/^flashreset + * retrieve flash type, virtual base and length and return 0; + * return -1 on error (no flash) + */ +int +archflashreset(int bank, Flash *f) +{ + if(bank != 0) + return -1; + f->type = "cfi16"; + f->addr = KADDR(FLASHMEM); + f->size = 0; + f->width = 4; + f->interleave = 1; + return 0; +} + +/* + * set ether parameters: the contents should be derived from EEPROM or NVRAM + */ +int +archether(int ctlno, Ether *ether) +{ + if(ctlno > 0) + return -1; + sprint(ether->type, "91c111"); + ether->mem = PHYSCS1; + ether->nopt = 0; + ether->port = 0x300; /* there isn't an ether EEPROM; use chip's default */ + ether->irq = 21; /* GPIO */ + ether->itype = GPIOrising; /* active high */ +// gpioreserve(ether->irq); +// gpioconfig(ether->irq, Gpio_gpio | Gpio_in); + memmove(ether->ea, macaddrs[ctlno], Eaddrlen); + return 1; +} + +/* + * pcmcia + */ +int +pcmpowered(int slotno) +{ + if(slotno) + return 0; + return 3; +} + +void +pcmpower(int slotno, int on) +{ + USED(slotno, on); +} + +void +pcmreset(int slot) +{ + if(slot != 0) + return; +// GPIOREG->gpsr = CFReset; +// delay(100); +// GPIOREG->gpcr = CFReset; +} + +int +pcmpin(int slot, int type) +{ + if(slot) + return -1; + return -1; +// switch(type){ +// case PCMready: +// return CFRdypin; +// case PCMeject: +// return CFnCDxpin; +// case PCMstschng: +// return -1; +// } +} + +void +pcmsetvpp(int slot, int vpp) +{ + USED(slot, vpp); +} + +/* + * boot environment in eeprom + */ + +enum { + EEpromHdr= 8, /* bytes */ + Envitemsize= 64, + Ekeysize= 48, +}; + +static I2Cdev eedev; +static struct { + uchar buf[Envitemsize]; + int size; +} bootenv; + +static int +eepromitem(uchar *buf, int lim, ulong *off) +{ + int l; + uchar b; + + if(i2crecv(&eedev, &b, 1, (*off)++) != 1) + return -1; + l = b; + if(l & 0x80){ + if(i2crecv(&eedev, &b, 1, (*off)++) != 1) + return -1; + l = ((l & 0x7F)<<8) | b; + } + if(buf == nil) + return l; + if(l > lim) + l = lim; + return i2crecv(&eedev, buf, l, *off); +} + +void +eepromscan(void) +{ + int n, l; + ulong off; + uchar buf[2]; + char *p; + int mac; + + eedev.addr = 0x56; + eedev.salen = 2; + i2csetup(1); + n = i2crecv(&eedev, buf, sizeof(buf), 0); + if(n <= 0){ + iprint("eepromscan: %d\n", n); + return; + } + if(buf[0] != 0xEF || buf[1] != 0xBE){ + iprint("eeprom invalid\n"); + return; + } + bootenv.size = 0; + for(off = EEpromHdr; off < 16384;){ + l = eepromitem(bootenv.buf, sizeof(bootenv.buf), &off); /* key */ + if(l <= 0) + break; + off += l; + if(memcmp(bootenv.buf, "MACAD", 5) == 0){ /* only look for MAC addresses now */ + mac = bootenv.buf[5] - '0'; + if(mac >= 0 && mac < Maxmac){ + l = eepromitem(bootenv.buf, sizeof(bootenv.buf), &off); + if(l < 0) + break; + if(l == Eaddrlen) + memmove(macaddrs[mac], bootenv.buf, Eaddrlen); + off += l+2; + continue; + } + } + l = eepromitem(nil, 0, &off); /* skip value */ + if(l < 0) + break; + off += l+2; /* 2 byte crc */ + } +} diff --git a/os/cerf250/cerf b/os/cerf250/cerf new file mode 100644 index 00000000..20b52181 --- /dev/null +++ b/os/cerf250/cerf @@ -0,0 +1,163 @@ +dev + root + cons archcerf + env + mnt + pipe + prog +# rtc + pcf8563 i2c + srv + dup + ssl + cap + sign + +# draw screen +# pointer + uart + ip bootp ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium netaux + flash +# ftl +# pcmcia cis +# ata + ether netif netaux + +# cerf +# kprof + i2c i2c + +ip + il + tcp + udp +# rudp +# igmp + ipifc + icmp + icmp6 +# ipmux + +link + flashcfi16 + ether91c111 # ethermii + ethermedium + +lib + interp +# tk +# draw +# memlayer +# memdraw + keyring + math + sec + mp + kern + +mod + math + sys +# draw +# tk + keyring + +port + alarm + alloc + allocb + chan + dev + dial + dis + discall + exception + exportfs + inferno + latin1 + nocache + nodynld + parse + pgrp + print + proc + qio + qlock + random + sysfile + taslock + xalloc + +code + int main_pool_pcnt = 50; + int heap_pool_pcnt = 50; + int image_pool_pcnt = 0; + int cflag = 0; /* for JIT */ + + int consoleprint = 1; + char debug_keys = 1; + int panicreset = 0; + void screeninit(void){} + +init + cerfinit + +root + /chan / + /dev / + /env / + /fd / + /net / + /net.alt / + /nvfs / + /prog / + /root / + /nvfs / + /osinit.dis + /tmp / + /dis + /dis/lib + /dis/disk + +# dos file system + /dis/dossrv.dis + /dis/lib/arg.dis + /dis/lib/styx.dis + /dis/lib/string.dis + /dis/lib/daytime.dis + + /dis/disk/format.dis + +# For development work: + /dis/sh.dis /dis/tiny/sh.dis + /dis/ls.dis + /dis/cat.dis + /dis/bind.dis + /dis/mount.dis + /dis/pwd.dis + /dis/echo.dis + /dis/cd.dis + /dis/xd.dis + /dis/cp.dis + /dis/mkdir.dis + /dis/rm.dis + /dis/p.dis + /dis/ps.dis + /dis/lib/readdir.dis + /dis/lib/workdir.dis + /dis/lib/daytime.dis + /dis/lib/auth.dis + /dis/lib/ssl.dis + /dis/lib/bufio.dis + /dis/lib/string.dis +# /dis/pcmcia.dis /usr/forsyth/pcmcia.dis + + /n/remote + /n/local + /n/client + /n/rdbg + /n/dump + /n/disk + /n/kfs +# Authentication + /nvfs/default /usr/inferno/keyring/default diff --git a/os/cerf250/dat.h b/os/cerf250/dat.h new file mode 100644 index 00000000..553c0cb0 --- /dev/null +++ b/os/cerf250/dat.h @@ -0,0 +1,135 @@ +typedef struct Conf Conf; +typedef struct Dma Dma; +typedef struct FPU FPU; +typedef struct FPenv FPenv; +typedef struct Label Label; +typedef struct Lock Lock; +typedef struct Mach Mach; +typedef struct Ureg Ureg; +typedef struct ISAConf ISAConf; +typedef struct PCMmap PCMmap; +typedef struct PCMslot PCMslot; + +typedef ulong Instr; + +struct Conf +{ + ulong nmach; /* processors */ + ulong nproc; /* processes */ + ulong npage0; /* total physical pages of memory */ + ulong npage1; /* total physical pages of memory */ + ulong topofmem; /* highest physical address + 1 */ + ulong npage; /* total physical pages of memory */ + ulong base0; /* base of bank 0 */ + ulong base1; /* base of bank 1 */ + ulong ialloc; /* max interrupt time allocation in bytes */ + + int useminicache; /* use mini cache: screen.c/lcd.c */ + int textwrite; /* writeable text segment, for debug */ + int portrait; /* display orientation */ +}; + +#define NISAOPT 8 +struct ISAConf { + char type[KNAMELEN]; + ulong port; + ulong irq; + int itype; + ulong dma; + ulong mem; + ulong size; + ulong freq; + + int nopt; + char *opt[NISAOPT]; +}; + +/* + * FPenv.status + */ +enum +{ + FPINIT, + FPACTIVE, + FPINACTIVE, +}; + +struct FPenv +{ + ulong status; + ulong control; + ushort fpistate; /* emulated fp */ + ulong regs[8][3]; /* emulated fp */ +}; + +/* + * This structure must agree with fpsave and fprestore asm routines + */ +struct FPU +{ + FPenv env; +}; + +struct Label +{ + ulong sp; + ulong pc; +}; + +struct Lock +{ + ulong key; + ulong sr; + ulong pc; + int pri; +}; + +#include "../port/portdat.h" + +/* + * machine dependent definitions not used by ../port/portdat.h + */ +struct Mach +{ + /* OFFSETS OF THE FOLLOWING KNOWN BY l.s */ + ulong splpc; /* pc of last caller to splhi */ + + /* ordering from here on irrelevant */ + + int machno; /* physical id of processor */ + ulong ticks; /* of the clock since boot time */ + Proc *proc; /* current process on this processor */ + Label sched; /* scheduler wakeup */ + Lock alarmlock; /* access to alarm list */ + void *alarm; /* alarms bound to this clock */ + ulong cpuhz; + + /* stacks for exceptions */ + ulong fiqstack[4]; + ulong irqstack[4]; + ulong abtstack[4]; + ulong undstack[4]; + + int stack[1]; +}; + +#define MACHP(n) (n == 0 ? (Mach*)(MACHADDR) : (Mach*)0) + +extern Mach *m; +extern Proc *up; + +typedef struct MemBank { + uint pbase; + uint plimit; + uint vbase; + uint vlimit; +} MemBank; + +/* + * Layout at virtual address 0. + */ +typedef struct Vectorpage { + void (*vectors[8])(void); + uint vtable[8]; +} Vectorpage; +extern Vectorpage *page0; diff --git a/os/cerf250/devpcf8563.c b/os/cerf250/devpcf8563.c new file mode 100644 index 00000000..f52fe60d --- /dev/null +++ b/os/cerf250/devpcf8563.c @@ -0,0 +1,371 @@ +/* + * Philips PCF8563 real-time clock on I⁲C (and compatibles) + * + * currently this can't coexist with ../pxa/devrtc.c + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "io.h" + +typedef struct Rtc Rtc; +typedef struct Rtcreg Rtcreg; + +struct Rtc +{ + int sec; + int min; + int hour; + int wday; + int mday; + int mon; + int year; +}; + +struct Rtcreg +{ + uchar csr1; + uchar csr2; + uchar sec; /* 00-59 and VL */ + uchar min; /* 00-59 */ + uchar hour; /* 00-23 */ + uchar mday; /* 01-31 */ + uchar wday; /* 0=Sun */ + uchar mon; /* 1-12 and 1900 bit */ + uchar year; + uchar amin; /* minute alarm */ + uchar ahour; + uchar aday; + uchar awday; +}; + +enum{ + Qdir = 0, + Qrtc, + + Rtclen= 0x0C+1, /* bytes read and written to timekeeper */ + VL= 0x80, /* reliable clock data no longer guaranteed */ +}; + +static QLock rtclock; /* mutex on nvram operations */ +static I2Cdev rtdev; + +static Dirtab rtcdir[]={ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "rtc", {Qrtc, 0}, 0, 0664, +}; + +static ulong rtc2sec(Rtc*); +static void sec2rtc(ulong, Rtc*); +static void setrtc(Rtc*); + +static void +rtcreset(void) +{ + rtdev.addr = 0x51; + rtdev.salen = 1; + i2csetup(1); +} + +static Chan* +rtcattach(char *spec) +{ + return devattach('r', spec); +} + +static Walkqid* +rtcwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen); +} + +static int +rtcstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen); +} + +static Chan* +rtcopen(Chan *c, int omode) +{ + omode = openmode(omode); + switch((ulong)c->qid.path){ + case Qrtc: + if(strcmp(up->env->user, eve)!=0 && omode!=OREAD) + error(Eperm); + break; + } + return devopen(c, omode, rtcdir, nelem(rtcdir), devgen); +} + +static void +rtcclose(Chan*) +{ +} + +static long +rtcread(Chan *c, void *buf, long n, vlong offset) +{ + ulong t, ot; + + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen); + + switch((ulong)c->qid.path){ + case Qrtc: + qlock(&rtclock); + t = rtctime(); + do{ + ot = t; + t = rtctime(); /* make sure there's no skew */ + }while(t != ot); + qunlock(&rtclock); + return readnum(offset, buf, n, t, 12); + } + error(Egreg); + return -1; /* never reached */ +} + +static long +rtcwrite(Chan *c, void *buf, long n, vlong off) +{ + Rtc rtc; + ulong secs; + char *cp, sbuf[32]; + ulong offset = off; + + switch((ulong)c->qid.path){ + case Qrtc: + if(offset!=0 || n >= sizeof(sbuf)-1) + error(Ebadarg); + memmove(sbuf, buf, n); + sbuf[n] = '\0'; + /* + * read the time + */ + cp = sbuf; + while(*cp){ + if(*cp>='0' && *cp<='9') + break; + cp++; + } + secs = strtoul(cp, 0, 0); + /* + * convert to bcd + */ + sec2rtc(secs, &rtc); + /* + * write it + */ + setrtc(&rtc); + return n; + } + error(Egreg); + return -1; /* never reached */ +} + +Dev pcf8563devtab = { + 'r', + "pcf8563", + + rtcreset, + devinit, + devshutdown, + rtcattach, + rtcwalk, + rtcstat, + rtcopen, + devcreate, + rtcclose, + rtcread, + devbread, + rtcwrite, + devbwrite, + devremove, + devwstat, +}; + +static int +getbcd(int bcd) +{ + return (bcd&0x0f) + 10 * (bcd>>4); +} + +static int +putbcd(int val) +{ + return (val % 10) | (((val/10) % 10) << 4); +} + +long +rtctime(void) +{ + Rtc rtc; + Rtcreg d; + + if(waserror()){ + iprint("rtc: err %s\n", up->env->errstr); + return 0; + } + if(i2crecv(&rtdev, &d, Rtclen, 0) != Rtclen) + return 0; + poperror(); + rtc.sec = getbcd(d.sec & 0x7F); + rtc.min = getbcd(d.min & 0x7F); + rtc.hour = getbcd(d.hour & 0x3F); + rtc.mday = getbcd(d.mday & 0x3F); + rtc.mon = getbcd(d.mon & 0x1f); + rtc.year = getbcd(d.year); + if(rtc.mon < 1 || rtc.mon > 12) + return 0; + if(d.mon & (1<<7)) + rtc.year += 1900; + else + rtc.year += 2000; + return rtc2sec(&rtc); +} + +static void +setrtc(Rtc *rtc) +{ + Rtcreg d; + + memset(&d, 0, sizeof(d)); + d.year = putbcd(rtc->year % 100); + d.mon = putbcd(rtc->mon); + if(rtc->year < 2000) + d.mon |= 1<<7; + d.wday = rtc->wday+1; + d.mday = putbcd(rtc->mday); + d.hour = putbcd(rtc->hour); + d.min = putbcd(rtc->min); + d.sec = putbcd(rtc->sec); + i2csend(&rtdev, &d, Rtclen, 0); +} + +#define SEC2MIN 60L +#define SEC2HOUR (60L*SEC2MIN) +#define SEC2DAY (24L*SEC2HOUR) + +/* + * days per month plus days/year + */ +static int dmsize[] = +{ + 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; +static int ldmsize[] = +{ + 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +/* + * return the days/month for the given year + */ +static int * +yrsize(int y) +{ + + if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0)) + return ldmsize; + else + return dmsize; +} + +/* + * compute seconds since Jan 1 1970 + */ +static ulong +rtc2sec(Rtc *rtc) +{ + ulong secs; + int i; + int *d2m; + + secs = 0; + + /* + * seconds per year + */ + for(i = 1970; i < rtc->year; i++){ + d2m = yrsize(i); + secs += d2m[0] * SEC2DAY; + } + + /* + * seconds per month + */ + d2m = yrsize(rtc->year); + for(i = 1; i < rtc->mon; i++) + secs += d2m[i] * SEC2DAY; + + secs += (rtc->mday-1) * SEC2DAY; + secs += rtc->hour * SEC2HOUR; + secs += rtc->min * SEC2MIN; + secs += rtc->sec; + + return secs; +} + +/* + * compute rtc from seconds since Jan 1 1970 + */ +static void +sec2rtc(ulong secs, Rtc *rtc) +{ + int d; + long hms, day; + int *d2m; + + /* + * break initial number into days + */ + hms = secs % SEC2DAY; + day = secs / SEC2DAY; + if(hms < 0) { + hms += SEC2DAY; + day -= 1; + } + + /* + * day is the day number. + * generate day of the week. + * The addend is 4 mod 7 (1/1/1970 was Thursday) + */ + + rtc->wday = (day + 7340036L) % 7; + + /* + * generate hours:minutes:seconds + */ + rtc->sec = hms % 60; + d = hms / 60; + rtc->min = d % 60; + d /= 60; + rtc->hour = d; + + /* + * year number + */ + if(day >= 0) + for(d = 1970; day >= *yrsize(d); d++) + day -= *yrsize(d); + else + for (d = 1970; day < 0; d--) + day += *yrsize(d-1); + rtc->year = d; + + /* + * generate month + */ + d2m = yrsize(rtc->year); + for(d = 1; day >= d2m[d]; d++) + day -= d2m[d]; + rtc->mday = day + 1; + rtc->mon = d; +} diff --git a/os/cerf250/ether91c111.c b/os/cerf250/ether91c111.c new file mode 100644 index 00000000..2a404d93 --- /dev/null +++ b/os/cerf250/ether91c111.c @@ -0,0 +1,1056 @@ +/* + * SMsC 91c111 ethernet controller + * Copyright © 2001,2004 Vita Nuova Holdings Limited. All rights reserved. + * + * TO DO: + * - use ethermii + * - use DMA where available + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/netif.h" + +#include "etherif.h" + +/* + * chip definitions + */ + +typedef struct Ctlr Ctlr; + +enum { + SMSC91C11x, + SMSC91C110, + SMSC91C111, + SMSC91C96, +}; + +struct Ctlr { + Lock; + uchar *base; + int type; + int rev; + int hasmii; + int phyad; + int bank; /* currently selected bank */ + Block* waiting; /* waiting for space in FIFO */ + + ulong collisions; + ulong toolongs; + ulong tooshorts; + ulong aligns; + ulong txerrors; + int oddworks; + int bus32bit; +}; + +#define MKREG(bank, off) ((bank << 8) | (off)) + +enum { + /* Bank 0 */ + Tcr= MKREG(0, 0), /* transmit control */ + TcrSwfdup= 1<<15, /* switched full duplex */ + TcrEphLoop= 1<<13, /* internal loopback */ + TcrStpSqet= 1<<12, /* stop transmission on SQET error */ + TcrFduplx= 1<<11, /* enable full duplex */ + TcrMonCsn= 1<<10, /* monitor collision (0 for MII operation) */ + TcrNoCRC= 1<<8, /* don't add CRC */ + TcrPadEn= 1<<7, /* pad short frames */ + TcrForceCol= 1<<2, /* force collision */ + TcrLoop= 1<<1, /* PHY loopback */ + TcrTxena= 1<<0, /* enable transmitter */ + Eph= MKREG(0, 2), /* there are more bits but we don't use them */ + EphLinkOk= 1<<14, + EphCtrRol= 1<<12, /* counter roll over; clear by reading Ecr */ + Rcr= MKREG(0, 4), /* receive control */ + RcrSoftRst= 1<<15, + RcrFiltCar= 1<<14, + RcrAbortEnb= 1<<13, + RcrStripCRC= 1<<9, + RcrRxEn= 1<<8, + RcrAlmul= 1<<2, /* ~=0, accept all multicast frames (=0, match multicast table) */ + RcrPrms= 1<<1, /* promiscuous mode */ + RcrRxAbort= 1<<0, /* set if receive frame longer than 2k bytes */ + Ecr= MKREG(0, 6), /* counter */ + EcrExcDeferred= 0xF<<12, /* excessively deferred Tx */ + EcrDeferred= 0xF<<8, /* deferred Tx */ + EcrMultCol= 0xF<<4, /* multiple collisions */ + EcrCollision= 0xF<<0, /* single collision */ + Mir= MKREG(0, 8), /* memory information */ + Mcr= MKREG(0, 0xA), /* memory config (91cxx) */ + Rpcr= Mcr, /* receive/phy control (91c111) */ + + /* Bank 1 */ + Config= MKREG(1, 0), + CfgMiiSelect= 1<<15, /* 91c110 */ + CfgEphPowerEn= CfgMiiSelect, /* =1, powered (after reset MMU); =0, low power mode (91c111) */ + CfgNoWait= 1<<12, /* don't request additional wait states */ + CfgSetSqlch= 1<<9, /* 91cxx */ + CfgGpcntrl= 1<<9, /* general purpose output (CNTRL), perhaps power-enable (91c111) */ + CfgAuiSelect= 1<<8, /* 91cxx */ + CfgExtPhy= 1<<8, /* enable external PHY/MII (91c111) */ + Cfg16Bit= 1<<7, /* 91cxx */ + BaseAddress= MKREG(1, 2), + Iaddr0_1= MKREG(1, 4), + Iaddr2_3= MKREG(1, 6), + Iaddr4_5= MKREG(1, 8), + Gpr= MKREG(1, 0xA), /* general purpose reg (EEPROM interface) */ + Control= MKREG(1, 0xC), /* control register */ + CtlRcvBad= 1<<14, /* allow bad CRC packets through */ + CtlAutoRelease= 1<<11, /* transmit pages released automatically w/out interrupt */ + CtlLeEnable= 1<<7, /* link error enable */ + CtlCrEnable= 1<<6, /* counter roll over enable */ + CtlTeEnable= 1<<5, /* transmit error enable */ + CtlEeSelect= 1<<2, /* EEPROM select */ + CtlReload= 1<<1, /* read EEPROM and update relevant registers */ + CtlStore= 1<<0, /* store relevant registers in EEPROM */ + + /* Bank 2 */ + Mmucr= MKREG(2, 0), /* MMU command */ + McrAllocTx= 1<<5, /* allocate space for outgoing packet */ + McrReset= 2<<5, /* reset to initial state */ + McrReadFIFO= 3<<5, /* remove frame from top of FIFO */ + McrRemove= 4<<5, /* remove and release top of Rx FIFO */ + McrFreeTx= 5<<5, /* release specific packet (eg, packets done Tx) */ + McrEnqueue= 6<<5, /* enqueue packet number to Tx FIFO */ + McrResetTx= 7<<5, /* reset both Tx FIFOs */ + McrBusy= 1<<0, + ArrPnr= MKREG(2, 2), /* Pnr (low byte), Arr (high byte) */ + ArrFailed= 1<<15, + FifoPorts= MKREG(2, 4), + FifoRxEmpty= 1<<15, + FifoTxEmpty= 1<<7, + Pointer= MKREG(2, 6), + PtrRcv= 1<<15, + PtrAutoIncr= 1<<14, + PtrRead= 1<<13, + PtrEtEn= 1<<12, + PtrNotEmpty= 1<<11, + Data= MKREG(2, 8), + Interrupt= MKREG(2, 0xC), /* status/ack (low byte), mask (high byte) */ + IntMii= 1<<7, /* PHY/MII state change */ + IntErcv= 1<<6, /* early receive interrupt (received > Ercv threshold) */ + IntEph= 1<<5, /* ethernet protocol interrupt */ + IntRxOvrn= 1<<4, /* overrun */ + IntAlloc= 1<<3, /* allocation complete */ + IntTxEmpty= 1<<2, /* TX FIFO now empty */ + IntTx= 1<<1, /* transmit done */ + IntRcv= 1<<0, /* packet received */ + IntrMask= MKREG(2, 0xD), + IntrMaskShift= 8, /* shift for Int... values to mask position in 16-bit register */ + IntrMaskField= 0xFF00, + + /* Bank 3 */ + Mt0_1= MKREG(3, 0), /* multicast table */ + Mt2_3= MKREG(3, 2), + Mt4_5= MKREG(3, 4), + Mt6_7= MKREG(3, 6), + Mgmt= MKREG(3, 8), /* management interface (MII) */ + MgmtMdo= 1<<0, /* MDO pin */ + MgmtMdi= 1<<1, /* MDI pin */ + MgmtMclk= 1<<2, /* drive MDCLK */ + MgmtMdoEn= 1<<3, /* MDO driven when high, tri-stated when low */ + Revision= MKREG(3, 0xA), + Ercv= MKREG(3, 0xC), /* early receive */ + + /* Bank 4 (91cxx only) */ + EcsrEcor= MKREG(4, 0), /* status and option registers */ + + /* all banks */ + BankSelect= MKREG(0, 0xe), +}; + +enum { + /* receive frame status word (p 38) */ + RsAlgnErr= 1<<15, + RsBroadcast= 1<<14, + RsBadCRC= 1<<13, + RsOddFrame= 1<<12, + RsTooLong= 1<<11, + RsTooShort= 1<<10, + RsMulticast= 1<<1, + RsError= RsBadCRC | RsAlgnErr | RsTooLong | RsTooShort, + + Framectlsize= 6, +}; + +static void miiw(Ctlr *ctlr, int regad, int val); +static int miir(Ctlr *ctlr, int regad); + +/* + * architecture dependent section - collected here in case + * we want to port the driver + */ + +#define PHYMIIADDR_91C110 3 +#define PHYMIIADDR_91C111 0 + +#define llregr(ctlr, reg) (*(ushort*)(ctlr->base + (reg))) +#define llregr32(ctlr, reg) (*(ulong*)(ctlr->base + (reg))) +#define llregw(ctlr, reg, val) (*(ushort*)(ctlr->base + (reg)) = (val)) + +static void +adinit(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + // TODO: code to turn on device clocks + ctlr->base = (uchar*)mmuphysmap(PHYSCS1, 0x100000) + ether->port; +iprint("adinit: %8.8lux -> %8.8lux mcs0=%8.8lux\n", (ulong)ctlr->base, PADDR(ctlr->base), MEMCFGREG->msc0); +{ulong v; v = *(ulong*)ctlr->base; iprint("value=%8.8lux\n", v);} + ctlr->bus32bit = 1; +} + +static void +adsetfd(Ctlr *ctlr) +{ + miiw(ctlr, 0x18, miir(ctlr, 0x18) | (1 << 5)); +} + +/* + * architecture independent section + */ + +static ushort +regr(Ctlr *ctlr, int reg) +{ + int bank; + ushort val; + + bank = reg >> 8; + if(ctlr->bank != bank){ + ctlr->bank = bank; + llregw(ctlr, BankSelect, bank); + } + val = llregr(ctlr, reg & 0xff); + return val; +} + +static ulong +regr32(Ctlr *ctlr, int reg) +{ + int bank; + ulong val; + + bank = reg >> 8; + if(ctlr->bank != bank){ + ctlr->bank = bank; + llregw(ctlr, BankSelect, bank); + } + val = llregr32(ctlr, reg & 0xff); + return val; +} + +static void +regw(Ctlr *ctlr, int reg, ushort val) +{ + int bank; + + bank = reg >> 8; + if(ctlr->bank != bank){ + ctlr->bank = bank; + llregw(ctlr, BankSelect, bank); + } + llregw(ctlr, reg & 0xff, val); +} + +static void +regwdatam(Ctlr *ctlr, ushort *data, int ns) +{ + int bank; + ushort *faddr; + + bank = Data >> 8; + if(ctlr->bank != bank){ + ctlr->bank = bank; + llregw(ctlr, BankSelect, bank); + } + faddr = (ushort*)(ctlr->base + (Data & 0xff)); + while(ns-- > 0){ + *faddr = *data; + data++; + } +} + +static void +regrdatam(Ctlr *ctlr, void *data, int nb) +{ + int bank; + ushort *f, *t; + int laps, ns; + + bank = Data >> 8; + if(ctlr->bank != bank){ + ctlr->bank = bank; + llregw(ctlr, BankSelect, bank); + } + + if((ulong)data & 3) + iprint("bad buffer alignment\n"); + + t = data; + f = (ushort*)(ctlr->base + (Data & 0xff)); + ns = nb >> 1; + laps = ns / 8; + switch(ns & 7){ /* Duff's device */ + do { + *t++ = *f; + case 7: *t++ = *f; + case 6: *t++ = *f; + case 5: *t++ = *f; + case 4: *t++ = *f; + case 3: *t++ = *f; + case 2: *t++ = *f; + case 1: *t++ = *f; + case 0: + ; + } while(laps-- > 0); + } +} + +static void +regrdatam32(Ctlr *ctlr, void *data, int nb) +{ + int bank; + ulong *f, *t; + int laps, nw; + + bank = Data >> 8; + if(ctlr->bank != bank){ + ctlr->bank = bank; + llregw(ctlr, BankSelect, bank); + } + + if((ulong)data & 3) + iprint("bad buffer alignment\n"); + + t = data; + f = (ulong*)(ctlr->base + (Data & 0xff)); + nw = nb>>2; + laps = nw / 8; + switch(nw & 7){ /* Duff's device */ + do { + *t++ = *f; + case 7: *t++ = *f; + case 6: *t++ = *f; + case 5: *t++ = *f; + case 4: *t++ = *f; + case 3: *t++ = *f; + case 2: *t++ = *f; + case 1: *t++ = *f; + case 0: + ; + } while(laps-- > 0); + } +} + +static void +regor(Ctlr *ctlr, int reg, ushort val) +{ + int bank; + + bank = reg >> 8; + if(ctlr->bank != bank){ + ctlr->bank = bank; + llregw(ctlr, BankSelect, bank); + } + reg &= 0xff; + llregw(ctlr, reg, llregr(ctlr, reg) | val); +} + +static void +regclear(Ctlr *ctlr, int reg, ushort val) +{ + int bank; + + bank = reg >> 8; + if(ctlr->bank != bank){ + ctlr->bank = bank; + llregw(ctlr, BankSelect, bank); + } + reg &= 0xff; + llregw(ctlr, reg, llregr(ctlr, reg) & ~val); +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + Ctlr *ctlr; + char *p; + int len; + + if(n == 0) + return 0; + + ctlr = ether->ctlr; + p = smalloc(READSTR); + if(waserror()){ + free(p); + nexterror(); + } + len = snprint(p, READSTR, "Overflow: %ud\n", ether->overflows); + len += snprint(p+len, READSTR, "Soft Overflow: %ud\n", ether->soverflows); + len += snprint(p+len, READSTR, "Transmit Error: %lud\n", ctlr->txerrors); + len += snprint(p+len, READSTR-len, "CRC Error: %ud\n", ether->crcs); + len += snprint(p+len, READSTR-len, "Collision: %lud\n", ctlr->collisions); + len += snprint(p+len, READSTR-len, "Align: %lud\n", ctlr->aligns); + len += snprint(p+len, READSTR-len, "Too Long: %lud\n", ctlr->toolongs); + snprint(p+len, READSTR-len, "Too Short: %lud\n", ctlr->tooshorts); + + n = readstr(offset, a, n, p); + poperror(); + free(p); + + return n; +} + +static void +promiscuous(void* arg, int on) +{ + Ether *ether; + Ctlr *ctlr; + int r; + + ether = arg; + ctlr = ether->ctlr; + ilock(ctlr); + r = regr(ctlr, Rcr); + if(on) + r |= RcrPrms; + else + r &= ~RcrPrms; + regw(ctlr, Rcr, r); + iunlock(ctlr); +} + +static void +attach(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + + /* + * enable transmit and receive + */ + regw(ctlr, Interrupt, (IntMii | IntTx | IntRcv | IntRxOvrn)<ctlr; + b = ctlr->waiting; + ctlr->waiting = nil; + if(b == nil) + return; /* shouldn't happen */ + pkt = regr(ctlr, ArrPnr); /* get packet number presumably just allocated */ + if(pkt & 0xC0){ + print("smc91c111: invalid packet number\n"); + freeb(b); + return; + } + + pointtotxpacket(ctlr, pkt, 0); + + lenb = BLEN(b); + odd = lenb & 1; + lenw = lenb >> 1; + regw(ctlr, Data, 0); // status word padding + regw(ctlr, Data, (lenw << 1) + Framectlsize); + regwdatam(ctlr, (ushort*)b->rp, lenw); // put packet into 91cxxx memory + lastw = 0x1000; + if(odd){ + lastw |= 0x2000; /* odd byte flag in control byte */ + lastw |= b->rp[lenb - 1]; + } + regw(ctlr, Data, lastw); + mmucommand(ctlr, McrEnqueue); // chip now owns buff + freeb(b); + regw(ctlr, Interrupt, (regr(ctlr, Interrupt) & IntrMaskField) | (IntTxEmpty << IntrMaskShift)); +} + +static void +txstart(Ether *ether) +{ + Ctlr *ctlr; + int n; + + ctlr = ether->ctlr; + if(ctlr->waiting != nil) /* allocate pending; must wait for that */ + return; + for(;;){ + if((ctlr->waiting = qget(ether->oq)) == nil) + break; + /* ctlr->waiting is a new block to transmit: allocate space */ + n = (BLEN(ctlr->waiting) & ~1) + Framectlsize; /* Framectlsize includes odd byte, if any */ + mmucommand(ctlr, McrAllocTx | (n >> 8)); + if(regr(ctlr, ArrPnr) & ArrFailed){ + regw(ctlr, Interrupt, (regr(ctlr, Interrupt) & IntrMaskField) | (IntAlloc << IntrMaskShift)); + break; + } + txloadpacket(ether); + } +} + +static void +transmit(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + txstart(ether); + iunlock(ctlr); +} + +static void +process(Ether *ether) +{ + Ctlr *ctlr; + int status, intrreg, intr, mask, fifo; + int pkt; + ulong data; + int count, len, alen; + Block *b; + + ctlr = ether->ctlr; + +Recheck: + intrreg = regr(ctlr, Interrupt); + regw(ctlr, Interrupt, 0); + mask = intrreg >> IntrMaskShift; + intr = intrreg & mask; + if(intr == 0){ + regw(ctlr, Interrupt, mask<waiting) + txloadpacket(ether); + mask &= ~IntAlloc; + mask |= IntTxEmpty; + } + + if(intr & IntRxOvrn){ + regw(ctlr, Interrupt, IntRxOvrn); + intr &= ~IntRxOvrn; + ether->overflows++; + } + if(intr & IntRcv){ + fifo = regr(ctlr, FifoPorts); + while((fifo & FifoRxEmpty) == 0){ + ether->inpackets++; + pointtorxpacket(ctlr, 0); + data = regr32(ctlr, Data); + status = data & 0xFFFF; + count = (data>>16) & 0x7FE; + if(status & RsBadCRC) + ether->crcs++; + else if(status & RsAlgnErr) + ether->frames++; + else if(status & (RsTooLong | RsTooShort)) + ether->buffs++; + else { + len = count - Framectlsize; + if(len < 0) + panic("smc:interrupt"); + if(ctlr->type == SMSC91C111 && !ctlr->oddworks) + len++; + else if(status & RsOddFrame) + len++; + alen = (len + 1) & ~1; + if(ctlr->bus32bit) + alen = (alen + 3) & ~3; + b = iallocb(alen); + if(b){ + (ctlr->bus32bit? regrdatam32: regrdatam)(ctlr, b->wp, alen); + b->wp += len; + etheriq(ether, b, 1); + }else + ether->soverflows++; + } + mmucommand(ctlr, McrRemove); + fifo = regr(ctlr, FifoPorts); + } + intr &= ~IntRcv; + } + if(intr & IntTx){ + /* some kind of failure */ + fifo = regr(ctlr, FifoPorts); + ctlr->txerrors++; + if((fifo & FifoTxEmpty) == 0){ + pkt = fifo & 0x3f; + pointtotxpacket(ctlr, pkt, PtrRead); + mmucommand(ctlr, McrFreeTx); + } + regw(ctlr, Interrupt, IntTx); + intr &= ~IntTx; + } + if(intr & IntTxEmpty){ + /* acknowledge and disable TX_EMPTY */ + regw(ctlr, Interrupt, IntTxEmpty); + mask &= ~IntTxEmpty; + intr &= ~IntTxEmpty; + } + if(intr) + panic("91c111: unhandled interrupts %.4ux\n", intr); + regw(ctlr, Interrupt, mask<ctlr; + ilock(ctlr); + bank = llregr(ctlr, BankSelect); + process(ether); + llregw(ctlr, BankSelect, bank); + ctlr->bank = bank; + iunlock(ctlr); +} + +#define MIIDELAY 5 + +static int +miimdi(Ctlr *ctlr, int n) +{ + int data, i; + + /* + * Read n bits from the MII Management Register. + */ + data = 0; + for(i = n - 1; i >= 0; i--){ + if(regr(ctlr, Mgmt) & MgmtMdi) + data |= (1 << i); + microdelay(MIIDELAY); + regw(ctlr, Mgmt, MgmtMclk); + microdelay(MIIDELAY); + regw(ctlr, Mgmt, 0); + microdelay(MIIDELAY); + } + + return data; +} + +static void +miimdo(Ctlr *ctlr, int bits, int n) +{ + int i, mdo; + + /* + * Write n bits to the MII Management Register. + */ + for(i = n - 1; i >= 0; i--){ + if(bits & (1 << i)) + mdo = MgmtMdoEn | MgmtMdo; + else + mdo = MgmtMdoEn; + regw(ctlr, Mgmt, mdo); + microdelay(MIIDELAY); + regw(ctlr, Mgmt, mdo | MgmtMclk); + microdelay(MIIDELAY); + regw(ctlr, Mgmt, mdo); + microdelay(MIIDELAY); + } +} + +static int +miir(Ctlr *ctlr, int regad) +{ + int data; + + /* + * Preamble; + * ST+OP+PHYAD+REGAD; + * TA + 16 data bits. + */ + miimdo(ctlr, 0xFFFFFFFF, 32); + miimdo(ctlr, 0x1800 | (ctlr->phyad << 5) | regad, 14); + data = miimdi(ctlr, 18); + regw(ctlr, Mgmt, 0); + microdelay(MIIDELAY); + + return data & 0xFFFF; +} + +static void +miiw(Ctlr* ctlr, int regad, int data) +{ + /* + * Preamble; + * ST+OP+PHYAD+REGAD+TA + 16 data bits; + * Z. + */ + miimdo(ctlr, 0xFFFFFFFF, 32); + data &= 0xFFFF; + data |= (0x05 << (5 + 5 + 2 + 16)) | (ctlr->phyad << (5 + 2 +16)) | (regad << (2 + 16)) | (0x02 << 16); + miimdo(ctlr, data, 32); + regw(ctlr, Mgmt, 0); + microdelay(MIIDELAY); +} + +static void +miinegostatus(Ctlr *ctlr, int *speed, int *full) +{ + int reg; + + switch(ctlr->type){ + case SMSC91C110: + reg = miir(ctlr, 25); + if((reg & (1<<4)) == 0) + break; + *speed = (reg & (1 << 5))? 100: 10; + *full = (reg & (1 << 6)) != 0; + return; + case SMSC91C111: + reg = miir(ctlr, 18); + *speed = (reg & (1 << 7))? 100: 10; + *full = (reg & (1 << 6)) != 0; + return; + } + *speed = 0; + *full = 0; +} + +void +dump111phyregs(Ctlr *ctlr) +{ + int x; + for(x = 0; x < 6; x++) + iprint("reg%d 0x%.4ux\n", x, miir(ctlr, x)); + for(x = 16; x <= 20; x++) + iprint("reg%d 0x%.4ux\n", x, miir(ctlr, x)); +} + +static void +miireset(Ctlr *ctlr) +{ + miiw(ctlr, 0, 0x8000); + while(miir(ctlr, 0) & 0x8000) + ; + delay(100); +} + +static int +miinegotiate(Ctlr *ctlr, int modes) +{ + ulong now, timeout; + int success; + int reg4; + + // Taken from TRM - don't argue + + miireset(ctlr); + miiw(ctlr, 0, 0); + regw(ctlr, Rpcr, 0x800 | (4 << 2)); + delay(50); + reg4 = miir(ctlr, 4); + reg4 &= ~(0x1f << 5); + reg4 |= ((modes & 0x1f) << 5); + miiw(ctlr, 4, reg4); + miir(ctlr, 18); // clear the status output so we can tell which bits got set... + miiw(ctlr, 0, 0x3300); + now = timer_start(); + timeout = ms2tmr(3000); + success = 0; + while(!success && (timer_start() - now) < timeout){ + ushort status; + status = miir(ctlr, 1); + if(status & (1 << 5)) + success = 1; + if(status & (1 << 4)){ + success = 0; + miiw(ctlr, 0, 0x3300); + } + } + return success; +} + +static int +ether91c111reset(Ether* ether) +{ + int i; + char *p; + uchar ea[Eaddrlen]; + Ctlr *ctlr; + ushort rev; + + if(ether->ctlr == nil){ + ether->ctlr = malloc(sizeof(Ctlr)); + if(ether->ctlr == nil) + return -1; + } + + ctlr = ether->ctlr; + ctlr->bank = -1; + + /* + * do architecture dependent intialisation + */ + adinit(ether); + + regw(ctlr, Rcr, RcrSoftRst); + regw(ctlr, Config, CfgEphPowerEn|CfgNoWait|Cfg16Bit); + delay(4*20); // rkw - (750us for eeprom alone)4x just to be ultra conservative 10 for linux. + regw(ctlr, Rcr, 0); // rkw - now remove reset and let the sig's fly. + regw(ctlr, Tcr, TcrSwfdup); + + regw(ctlr, Control, CtlAutoRelease | CtlTeEnable); + mmucommand(ctlr, McrReset); // rkw - reset the mmu + delay(5); + + /* + * Identify the chip by reading... + * 1) the bank select register - the top byte will be 0x33 + * 2) changing the bank to see if it reads back appropriately + * 3) check revision register for code 9 + */ + if((llregr(ctlr, BankSelect) >> 8) != 0x33){ + gopanic: + free(ctlr); + return -1; + } + + llregw(ctlr, BankSelect, 0xfffb); + if((llregr(ctlr, BankSelect) & 0xff07) != 0x3303) + goto gopanic; + + rev = regr(ctlr, Revision); + + if((rev >> 8) != 0x33) + goto gopanic; + + rev &= 0xff; + switch(rev){ + case 0x40: + /* 91c96 */ + ctlr->type = SMSC91C96; + ctlr->oddworks = 1; + break; + case 0x90: + ctlr->type = SMSC91C11x; + ctlr->hasmii = 1; + /* 91c110/9c111 */ + /* 91c111s are supposed to be revision one, but it's not the case */ + // See man page 112, revision history. rev not incremented till 08/01 + ctlr->oddworks = 0; // dont know if it works at this point + break; + case 0x91: + ctlr->type = SMSC91C111; + ctlr->hasmii = 1; + ctlr->oddworks = 1; + break; + default: + iprint("ether91c111: chip 0x%.1ux detected\n", rev); + goto gopanic; + } + + memset(ea, 0, sizeof(ea)); + if(memcmp(ether->ea, ea, Eaddrlen) == 0) + panic("ethernet address not set"); +#ifdef YYY + if((rev == 0x90) || (rev == 0x91)) // assuming no eeprom setup for these + panic("ethernet address not set in environment"); + for(i = 0; i < Eaddrlen; i += 2){ + ushort w; + w = regr(ctlr, Iaddr0_1 + i); + iprint("0x%.4ux\n", w); + ea[i] = w; + ea[i + 1] = w >> 8; + } + }else{ + for(i = 0; i < 6; i++){ + char buf[3]; + buf[0] = p[i * 2]; + buf[1] = p[i * 2 + 1]; + buf[2] = 0; + ea[i] = strtol(buf, 0, 16); + } + } + memmove(ether->ea, ea, Eaddrlen); +#endif + + /* + * set the local address + */ + for(i=0; iea[i] | (ether->ea[i+1] << 8)); + + /* + * initialise some registers + */ + regw(ctlr, Rcr, RcrRxEn | RcrAbortEnb | RcrStripCRC); // strip can now be used again + + if(rev == 0x90){ // its either a 110 or a 111 rev A at this point + int reg2, reg3; + /* + * how to tell the difference? + * the standard MII dev + */ + ctlr->phyad = PHYMIIADDR_91C110; + ctlr->type = SMSC91C110; + ctlr->oddworks = 1; // assume a 110 + reg2 = miir(ctlr, 2); // check if a 111 RevA + if(reg2 <= 0){ + ctlr->phyad = PHYMIIADDR_91C111; + ctlr->type = SMSC91C111; + reg2 = miir(ctlr, 2); + ctlr->oddworks = 0; // RevA + } + if(reg2 > 0){ + reg3 = miir(ctlr, 3); + iprint("reg2 0x%.4ux reg3 0x%.4ux\n", reg2, reg3); + } + else + panic("ether91c111: can't find phy on MII\n"); + } + + if(ctlr->type == SMSC91C110) + regor(ctlr, Config, CfgMiiSelect); + if(rev == 0x40){ + regor(ctlr, Config, CfgSetSqlch); + regclear(ctlr, Config, CfgAuiSelect); + regor(ctlr, Config, Cfg16Bit); + } + + if(ctlr->type == SMSC91C111){ + int modes; + char *ethermodes; + + miiw(ctlr, 0, 0x1000); /* clear MII_DIS and enable AUTO_NEG */ +// miiw(ctlr, 16, miir(ctlr, 16) | 0x8000); + // Rpcr set in INIT. + ethermodes=nil; /* was getconf("ethermodes"); */ + if(ethermodes == nil) + modes = 0xf; + else { + char *s; + char *args[10]; + int nargs; + int x; + + s = strdup(ethermodes); + if(s == nil) + panic("ether91c111reset: no memory for ethermodes"); + nargs = getfields(s, args, nelem(args), 1, ","); + modes = 0; + for(x = 0; x < nargs; x++){ + if(cistrcmp(args[x], "10HD") == 0) + modes |= 1; + else if(cistrcmp(args[x], "10FD") == 0) + modes |= 2; + else if(cistrcmp(args[x], "100HD") == 0) + modes |= 4; + else if(cistrcmp(args[x], "100FD") == 0) + modes |= 8; + } + free(s); + } + if(!miinegotiate(ctlr, modes)){ + iprint("ether91c111: negotiation timed out\n"); + return -1; + } + } + + if(ctlr->hasmii) + miinegostatus(ctlr, ðer->mbps, ðer->fullduplex); + else if(regr(ctlr, Eph) & EphLinkOk){ + ether->mbps = 10; + ether->fullduplex = 0; + } + else { + ether->mbps = 0; + ether->fullduplex = 0; + } + + if(ether->fullduplex && ctlr->type == SMSC91C110){ + // application note 79 + regor(ctlr, Tcr, TcrFduplx); + // application note 85 + adsetfd(ctlr); + } + + iprint("91c111 enabled: %dmbps %s\n", ether->mbps, ether->fullduplex ? "FDX" : "HDX"); + if(rev == 0x40){ + iprint("EcsrEcor 0x%.4ux\n", regr(ctlr, EcsrEcor)); + regor(ctlr, EcsrEcor, 1); + } + + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = ifstat; + + ether->arg = ether; + ether->promiscuous = promiscuous; + + return 0; +} + +void +ether91c111link(void) +{ + addethercard("91c111", ether91c111reset); +} diff --git a/os/cerf250/fns.h b/os/cerf250/fns.h new file mode 100644 index 00000000..7b798ecc --- /dev/null +++ b/os/cerf250/fns.h @@ -0,0 +1,168 @@ +#include "../port/portfns.h" + +ulong aifinit(uchar *aifarr); +int archaudiopower(int); +void archaudiomute(int); +void archaudioamp(int); +int archaudiospeed(int, int); +void archconfinit(void); +void archconsole(void); +int archflash12v(int); +long archkprofmicrosecondspertick(void); +void archkprofenable(int); +void archlcdenable(int); +void archpowerdown(void); +void archpowerup(void); +void archreboot(void); +void archreset(void); +vlong archrdtsc(void); +ulong archrdtsc32(void); +void archuartpower(int, int); +void blankscreen(int); +ulong call_apcs(ulong addr, int nargs, ...); +ulong call_apcs0(ulong addr); +ulong call_apcs1(ulong addr, ulong a1); +ulong call_apcs2(ulong addr, ulong a1, ulong a2); +ulong call_apcs3(ulong addr, ulong a1, ulong a2, ulong a3); +void cisread(int slotno, void (*f)(int, uchar *)); +void clockcheck(void); +void clockinit(void); +void clockpoll(void); +#define coherence() /* nothing to do for cache coherence for uniprocessor */ +void cursorhide(void); +void cursorunhide(void); +void dcflush(void*, ulong); +void dcflushall(void); +void dcinval(void); +int dmaidle(Dma*); +Dma* dmasetup(int device, void(*)(void*,ulong), void*, ulong); +int dmastart(Dma*, void*, void*, int); +int dmacontinue(Dma*, void*, int); +void dmastop(Dma*); +int dmaerror(Dma*); +void dmafree(Dma*); +void dmareset(void); +void dmawait(Dma*); +void dumplongs(char *, ulong *, int); +void dumpregs(Ureg* ureg); +void dumpstack(void); +int fpiarm(Ureg*); +void fpinit(void); +ulong getcallerpc(void*); +ulong getcclkcfg(void); +ulong getcpsr(void); +ulong getcpuid(void); +ulong getspsr(void); +void gotopc(ulong); + +void icflush(void*, ulong); +void icflushall(void); +void idle(void); +void idlehands(void); +int inb(ulong); +int ins(ulong); +ulong inl(ulong); +void outb(ulong, int); +void outs(ulong, int); +void outl(ulong, ulong); +void inss(ulong, void*, int); +void outss(ulong, void*, int); +void insb(ulong, void*, int); +void outsb(ulong, void*, int); +void intrdisable(int, int, void (*)(Ureg*, void*), void*, char*); +void intrenable(int, int, void (*)(Ureg*, void*), void*, char*); +void iofree(int); +#define iofree(x) +void ioinit(void); +int iounused(int, int); +int ioalloc(int, int, int, char*); +#define ioalloc(a,b,c,d) 0 +int iprint(char*, ...); +void installprof(void (*)(Ureg *, int)); +int isvalid_va(void*); +void kbdinit(void); +void lcd_setbacklight(int); +void lcd_sethz(int); +void ledset(int); +void lights(ulong); +void links(void); +void* minicached(void*); +void minidcflush(void); +void mmuenable(ulong); +ulong mmugetctl(void); +ulong mmugetdac(void); +ulong mmugetfar(void); +ulong mmugetfsr(void); +void mmuinit(void); +void* mmuphysmap(ulong, ulong); +void mmuputctl(ulong); +void mmuputdac(ulong); +void mmuputfsr(ulong); +void mmuputttb(ulong); +void mmureset(void); +void mouseinit(void); +void* pa2va(ulong); +void pcmcisread(PCMslot*); +int pcmcistuple(int, int, int, void*, int); +PCMmap* pcmmap(int, ulong, int, int); +void pcmunmap(int, PCMmap*); +int pcmpin(int slot, int type); +void pcmpower(int slotno, int on); +int pcmpowered(int); +void pcmreset(int); +void pcmsetvcc(int, int); +void pcmsetvpp(int, int); +int pcmspecial(char *idstr, ISAConf *isa); +void pcmspecialclose(int slotno); +void pcmintrenable(int, void (*)(Ureg*, void*), void*); +void powerenable(void (*)(int)); +void powerdisable(void (*)(int)); +void powerdown(void); +void powerinit(void); +void powersuspend(void); +#define procsave(p) +#define procrestore(p) +void putcclkcfg(ulong); +long rtctime(void); +void screeninit(void); +void (*screenputs)(char*, int); +int segflush(void*, ulong); +void setpanic(void); +void setr13(int, void*); +int splfhi(void); +int splflo(void); +void _suspendcode(void); +void tlbinvalidate(void); +void tlbinvalidateaddr(void*); +void trapinit(void); +void trapstacks(void); +void trapspecial(int (*)(Ureg *, uint)); +void uartdebuginit(void); +void uartinstall(void); +int uartprint(char*, ...); +void uartspecial(int, int, Queue**, Queue**, int (*)(Queue*, int)); +ulong va2pa(void*); +void vectors(void); +void vtable(void); +#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) +int wasbusy(int); + +#define KADDR(p) ((void *) p) +#define PADDR(v) va2pa((void*)(v)) + +ulong timer_start(void); +ulong timer_ticks(ulong); +int timer_devwait(ulong *adr, ulong mask, ulong val, int ost); +void timer_setwatchdog(int ost); +void timer_delay(int ost); +ulong ms2tmr(int ms); +int tmr2ms(ulong t); +void delay(int ms); +ulong us2tmr(int us); +int tmr2us(ulong t); +void microdelay(int us); + +#define archuartclock(p,rate) 14745600 + +/* debugging */ +void xdelay(int); diff --git a/os/cerf250/io.h b/os/cerf250/io.h new file mode 100644 index 00000000..6a4a9b74 --- /dev/null +++ b/os/cerf250/io.h @@ -0,0 +1 @@ +#include "../pxa/pxaio.h" diff --git a/os/cerf250/main.c b/os/cerf250/main.c new file mode 100644 index 00000000..2d5c791f --- /dev/null +++ b/os/cerf250/main.c @@ -0,0 +1,351 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" +#include "version.h" + +#define MAXCONF 32 + +Mach *m = (Mach*)MACHADDR; +Proc *up = 0; +Vectorpage *page0 = (Vectorpage*)KZERO; /* doubly-mapped to AIVECADDR */ +Conf conf; + +extern ulong kerndate; +extern int cflag; +extern int main_pool_pcnt; +extern int heap_pool_pcnt; +extern int image_pool_pcnt; +ulong cpuidlecount; + +char *confname[MAXCONF]; +char *confval[MAXCONF]; +int nconf; + +void addconf(char *, char *); +void eepromscan(void); +char* getconf(char*); + +void +doc(char *m) +{ + USED(m); + print("%s...\n", m); +} + +void +idoc(char *m) +{ + uartputs(m, strlen(m)); //xdelay(1); +} + +static void +poolsizeinit(void) +{ + ulong nb; + + nb = conf.npage*BY2PG; + poolsize(mainmem, (nb*main_pool_pcnt)/100, 0); + poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0); + poolsize(imagmem, (nb*image_pool_pcnt)/100, 1); +} + +static void +serialconsole(void) +{ + char *p; + int port, baud; + + p = getconf("console"); + if(p == nil) + p = "0"; + if(p != nil){ + port = strtol(p, nil, 0); + baud = 38400; + p = getconf("baud"); + if(p != nil){ + baud = strtol(p, nil, 0); + if(baud < 38400) + baud = 38400; + } + uartspecial(port, baud, &kbdq, &printq, kbdcr2nl); + } +} + +static char *hello = "Inferno\n"; + +void +main(void) +{ + memset(edata, 0, end-edata); /* clear the BSS */ + memset(m, 0, sizeof(Mach)); /* clear the mach struct */ + conf.nmach = 1; + archreset(); + idoc(hello); +if(0){ + putcclkcfg(0); /* leave turbo mode */ + COREREG->cccr = (COREREG->cccr & ~(7<<7)) | (4<<7); /* turbo mode multiplier=2 */ + putcclkcfg(1); /* enter turbo mode */ +} + quotefmtinstall(); + idoc("confinit...\n"); + confinit(); + idoc("xinit...\n"); + xinit(); + idoc("mmuinit...\n"); + mmuinit(); + poolsizeinit(); + poolinit(); + idoc("trapinit...\n"); + trapinit(); +// dmareset(); + idoc("printinit...\n"); + printinit(); + idoc("uartinstall...\n"); + uartinstall(); + eepromscan(); + doc("clockinit"); + clockinit(); + doc("screeninit"); +// screeninit(); + doc("procinit"); + procinit(); +// cpuidprint(); + doc("links"); + links(); + doc("chandevreset"); + chandevreset(); + + eve = strdup("inferno"); + + serialconsole(); + kbdinit(); + + print("%ld MHz id %8.8lux\n", (m->cpuhz+500000)/1000000, getcpuid()); + print("\nInferno %s\n", VERSION); + print("Vita Nuova\n"); + print("conf %s (%lud) jit %d\n\n",conffile, kerndate, cflag); +print("pmcr=%8.8lux pwer=%8.8lux prer=%8.8lux pfer=%8.8lux pedr=%8.8lux pcfr=%8.8lux rcsr=%8.8lux\n", + PMGRREG->pmcr, PMGRREG->pwer, PMGRREG->prer, PMGRREG->pfer, PMGRREG->pedr, PMGRREG->pcfr, + PMGRREG->rcsr); +print("cccr=%8.8lux cken=%8.8lux oscc=%8.8lux\n", COREREG->cccr, COREREG->cken, COREREG->oscc); +print("msc0=%8.8lux mcs1=%8.8lux mcs2=%8.8lux\n", MEMCFGREG->msc0, MEMCFGREG->msc1, MEMCFGREG->msc2); +print("clkcfg=%8.8lux\n", getcclkcfg()); + + userinit(); + schedinit(); +} + +void +reboot(void) +{ + exit(0); +} + +void +halt(void) +{ + spllo(); + print("cpu halted\n"); + for(;;){ + /* nothing to do */ + } +} + +Conf conf; + +void +addconf(char *name, char *val) +{ + if(nconf >= MAXCONF) + return; + confname[nconf] = name; + confval[nconf] = val; + nconf++; +} + +char* +getconf(char *name) +{ + int i; + + for(i = 0; i < nconf; i++) + if(cistrcmp(confname[i], name) == 0) + return confval[i]; + return 0; +} + +void +confinit(void) +{ + ulong base; + + archconfinit(); + + base = PGROUND((ulong)end); + conf.base0 = base; + + conf.base1 = 0; + conf.npage1 = 0; + + conf.npage0 = (conf.topofmem - base)/BY2PG; + + conf.npage = conf.npage0 + conf.npage1; + conf.ialloc = (((conf.npage*(main_pool_pcnt))/100)/2)*BY2PG; + + conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5; + conf.nmach = 1; + +} + +void +init0(void) +{ + Osenv *o; + char buf[2*KNAMELEN]; + + up->nerrlab = 0; + + spllo(); + + if(waserror()) + panic("init0 %r"); + /* + * These are o.k. because rootinit is null. + * Then early kproc's will have a root and dot. + */ + o = up->env; + o->pgrp->slash = namec("#/", Atodir, 0, 0); + cnameclose(o->pgrp->slash->name); + o->pgrp->slash->name = newcname("/"); + o->pgrp->dot = cclone(o->pgrp->slash); + + chandevinit(); + + if(!waserror()){ + ksetenv("cputype", "arm", 0); + snprint(buf, sizeof(buf), "arm %s", conffile); + ksetenv("terminal", buf, 0); + poperror(); + } + + poperror(); + + disinit("/osinit.dis"); +} + +void +userinit(void) +{ + Proc *p; + Osenv *o; + + p = newproc(); + o = p->env; + + o->fgrp = newfgrp(nil); + o->pgrp = newpgrp(); + o->egrp = newegrp(); + kstrdup(&o->user, eve); + + strcpy(p->text, "interp"); + + p->fpstate = FPINIT; + + /* + * Kernel Stack + * + * N.B. The -12 for the stack pointer is important. + * 4 bytes for gotolabel's return PC + */ + p->sched.pc = (ulong)init0; + p->sched.sp = (ulong)p->kstack+KSTACK-8; + + ready(p); +} + +void +exit(int inpanic) +{ + up = 0; + + /* Shutdown running devices */ + chandevshutdown(); + + if(inpanic && 0){ + print("Hit the reset button\n"); + for(;;) + clockpoll(); + } + archreboot(); +} + +static void +linkproc(void) +{ + spllo(); + if (waserror()) + print("error() underflow: %r\n"); + else + (*up->kpfun)(up->arg); + pexit("end proc", 1); +} + +void +kprocchild(Proc *p, void (*func)(void*), void *arg) +{ + p->sched.pc = (ulong)linkproc; + p->sched.sp = (ulong)p->kstack+KSTACK-8; + + p->kpfun = func; + p->arg = arg; +} + +void +idlehands(void) +{ + cpuidlecount++; + INTRREG->iccr = 1; /* only unmasked interrupts will stop idle mode */ + idle(); +} + +/* stubs */ +void +setfsr(ulong) +{ +} + +ulong +getfsr() +{ + return 0; +} + +void +setfcr(ulong) +{ +} + +ulong +getfcr() +{ + return 0; +} + +void +fpinit(void) +{ +} + +void +FPsave(void*) +{ +} + +void +FPrestore(void*) +{ +} diff --git a/os/cerf250/mem.h b/os/cerf250/mem.h new file mode 100644 index 00000000..625232ab --- /dev/null +++ b/os/cerf250/mem.h @@ -0,0 +1,174 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ + +/* + * Sizes + */ +#define BI2BY 8 /* bits per byte */ +#define BI2WD 32 /* bits per word */ +#define BY2WD 4 /* bytes per word */ +#define BY2V 8 /* bytes per double word */ +#define BY2PG 4096 /* bytes per page */ +#define WD2PG (BY2PG/BY2WD) /* words per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1)) +#define PGROUND(s) ROUND(s, BY2PG) +#define BIT(n) (1<= 0){ + if(*data == '\n') + uartputc('\r'); + uartputc(*data++); + } + splx(s); +} + +void +uartwait(void) +{ +} diff --git a/os/cerf405/NOTICE b/os/cerf405/NOTICE new file mode 100644 index 00000000..0a613c43 --- /dev/null +++ b/os/cerf405/NOTICE @@ -0,0 +1,3 @@ +Inferno® Copyright © 1996-1999 Lucent Technologies Inc. All rights reserved. +PowerPC support Copyright © 1995-1997,2002,2003 C H Forsyth (forsyth@terzarima.net). All rights reserved. +405EP Inferno PowerPC port Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved. diff --git a/os/cerf405/README b/os/cerf405/README new file mode 100644 index 00000000..c526927b --- /dev/null +++ b/os/cerf405/README @@ -0,0 +1,45 @@ +Cerfcube 405EP + +This is the basis of a port to a range of IBM PowerPC 405 devices. +At some point all the common code will move to ../ppc, as is done +for SA1110, MPC8xx, and others. + +Currently untested: + - both EMAC0 and EMAC1 are initialised, but EMAC1 hasn't been tested (i need to get a cable made); + the PHY for it is detected and initialised, so i think it should work + - pci has not been tested since i haven't yet got the mini-PCI card i need + +Will change: + - devuart.c/uart.h will be replaced by ../port/devuart.c and uart405.c eventually + - power control will be extended + +mk in this directory should produce a file `icerfhd', which is icerf parcelled as ppcboot/uboot expects it. +put it where your tftp server can find it. + +on the machine to be used as file server, run inferno and start svc/net +to serve Styx + +To boot Inferno: + 1. set up a dhcp/tftp server for the EMAC0 MAC address, referring to the icerfhd file as the bootfile. + 2. connect a b115200 l8 pn serial port to talk to the ppcboot/uboot bootstrap + 3. reset the 405, and interrupt the automatic boot to get to the bootstrap's prompt + 4. type + bootp + 5. if dhcp is set correctly, that should load the Inferno boot image into memory at 0x300000 + 6. type + bootm + to boot that image. + +To put Inferno kernel image (icerfhd) on NAND flash: + - the boot area is 0 to 0x100000 on NAND (perhaps a bit more, but 0x100000 is currently fine) + 1. get to the bootstrap's prompt. + 2. nand erase 0 0x100000 + 3. bootp # loads the image to 0x300000 by tftp + 4. nand write 0x300000 0 0x100000 + 5. a subsequent {nand; bootm} (as for auto boot) should run that kernel. + +To initialise a file system on the NAND flash: + 1. set logfsformat=yes in the environment + 2. boot inferno + 3. choose remote file system or kernel file system when offered + 4. the empty flash file system should be in /n/local diff --git a/os/cerf405/cerf b/os/cerf405/cerf new file mode 100644 index 00000000..64265586 --- /dev/null +++ b/os/cerf405/cerf @@ -0,0 +1,139 @@ +# Intrinsyc Cerf Cube 405EP +dev + root + cons + env + mnt + pipe + prog + rtc iic + srv + dup + ssl + cap + sign + + ip ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium netaux + ether netif netaux + uart + flash + + logfs + i2c iic + pci pci inb + + +ip + il + tcp + udp +# rudp +# igmp + ipifc + icmp + icmp6 + ipmux + +lib + interp + keyring + sec + mp + math + kern + logfs + nandfs + +link + etheremac ethermii + flashamd29f0x0 + flashnand nand + ethermedium + +mod + math + sys + keyring + +port + alarm + alloc + allocb + chan + dev + dial + dis + discall + exception + exportfs + inferno + latin1 + nocache + nodynld + parse + pgrp + print + proc + qio + qlock + random + sysfile + taslock + xalloc + +code + int cflag = 0; + int consoleprint = 1; + int panicreset = 0; + int main_pool_pcnt = 50; + int heap_pool_pcnt = 50; + int image_pool_pcnt = 0; + void screeninit(void){} + +init + cerf405 + +root + /chan / + /dev / + /boot / + /env / + /fd / + /net / + /prog / + /root / + /nvfs / + /osinit.dis + /tmp / + +# files used by osinit.dis during bootstrap + /boot/n / + /boot/n/local / + /boot/n/remote / +# authentication + /boot/nvfs/default /usr/inferno/keyring/default + /boot/dis/lib/auth.dis /dis/lib/auth.dis + /boot/dis/lib/ssl.dis /dis/lib/ssl.dis +# dhcp + /boot/dis/lib/dhcpclient.dis /dis/lib/dhcpclient.dis + /boot/dis/lib/ip.dis /dis/lib/ip.dis + +# and other files used to poke round during development + /boot/dis/cat.dis /dis/cat.dis + /boot/dis/echo.dis /dis/echo.dis + /boot/dis/lib/arg.dis /dis/lib/arg.dis + + /boot/dis/sh.dis /dis/sh.dis + /boot/dis/lib/bufio.dis /dis/lib/bufio.dis + /boot/dis/lib/filepat.dis /dis/lib/filepat.dis + /boot/dis/lib/readdir.dis /dis/lib/readdir.dis + /boot/dis/lib/string.dis /dis/lib/string.dis + + /boot/dis/cd.dis /dis/cd.dis + /boot/dis/bind.dis /dis/bind.dis + /boot/dis/dd.dis /dis/dd.dis + /boot/dis/p.dis /dis/p.dis + /boot/dis/ls.dis /dis/ls.dis + /boot/dis/lib/daytime.dis /dis/lib/daytime.dis + /boot/dis/time.dis /dis/time.dis + /boot/dis/xd.dis /dis/xd.dis diff --git a/os/cerf405/clock.c b/os/cerf405/clock.c new file mode 100644 index 00000000..d830c18c --- /dev/null +++ b/os/cerf405/clock.c @@ -0,0 +1,159 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +#include +#include + +typedef struct Clock0link Clock0link; +typedef struct Clock0link { + void (*clock)(void); + Clock0link* link; +} Clock0link; + +static Clock0link *clock0link; +static Lock clock0lock; +ulong clkrelinq; +void (*kproftick)(ulong); /* set by devkprof.c when active */ +void (*archclocktick)(void); /* set by arch*.c when desired */ + +Timer* +addclock0link(void (*clock)(void), int) +{ + Clock0link *lp; + + if((lp = malloc(sizeof(Clock0link))) == 0){ + print("addclock0link: too many links\n"); + return nil; + } + ilock(&clock0lock); + lp->clock = clock; + lp->link = clock0link; + clock0link = lp; + iunlock(&clock0lock); + return nil; +} + +void +delay(int l) +{ + ulong i, j; + + j = m->delayloop; + while(l-- > 0) + for(i=0; i < j; i++) + ; +} + +void +microdelay(int l) +{ + ulong i; + + l *= m->delayloop; + l /= 1000; + if(l <= 0) + l = 1; + for(i = 0; i < l; i++) + ; +} + +enum { + Timebase = 1, /* system clock cycles per time base cycle */ + + Wp17= 0<<30, /* watchdog period (2^x clocks) */ + Wp21= 1<<30, + Wp25= 2<<30, + Wp29= 3<<30, + Wrnone= 0<<28, /* no watchdog reset */ + Wrcore= 1<<28, /* core reset */ + Wrchip= 2<<28, /* chip reset */ + Wrsys= 3<<28, /* system reset */ + Wie= 1<<27, /* watchdog interrupt enable */ + Pie= 1<<26, /* enable PIT interrupt */ + Fit9= 0<<24, /* fit period (2^x clocks) */ + Fit13= 1<<24, + Fit17= 2<<24, + Fit21= 3<<24, + Fie= 1<<23, /* fit interrupt enable */ + Are= 1<<22, /* auto reload enable */ + + /* dcr */ + Boot= 0x0F1, + Epctl= 0x0F3, + Pllmr0= 0x0F0, + Pllmr1= 0x0F4, + Ucr= 0x0F5, +}; + +void +clockinit(void) +{ + long x; + + m->delayloop = m->cpuhz/1000; /* initial estimate */ + do { + x = gettbl(); + delay(10); + x = gettbl() - x; + } while(x < 0); + + /* + * fix count + */ + m->delayloop = ((vlong)m->delayloop*(10*(vlong)m->clockgen/1000))/(x*Timebase); + if((int)m->delayloop <= 0) + m->delayloop = 20000; + + x = (m->clockgen/Timebase)/HZ; + putpit(x); +iprint("pit value=%.8lux [%lud]\n", x, x); + puttsr(~0); + puttcr(Pie|Are); +iprint("boot=%.8lux epctl=%.8lux pllmr0=%.8lux pllmr1=%.8lux ucr=%.8lux\n", + getdcr(Boot), getdcr(Epctl), getdcr(Pllmr0), getdcr(Pllmr1), getdcr(Ucr)); +} + +void +clockintr(Ureg *ur) +{ + Clock0link *lp; + + /* PIT was set to reload automatically */ + puttsr(~0); + m->ticks++; + + if(up) + up->pc = ur->pc; + + if(archclocktick != nil) + archclocktick(); + checkalarms(); + if(m->machno == 0) { + if(kproftick != nil) + (*kproftick)(ur->pc); + lock(&clock0lock); + for(lp = clock0link; lp; lp = lp->link) + lp->clock(); + unlock(&clock0lock); + } + + if(up && up->state == Running){ + if(cflag && up->type == Interp && tready(nil)) + ur->cr |= 1; /* set flag in condition register for ../../interp/comp-power.c:/^schedcheck */ + if(anyready()) + sched(); + } +} + +uvlong +fastticks(uvlong *hz) +{ + if(hz) + *hz = HZ; + return m->ticks; +} diff --git a/os/cerf405/compile.c b/os/cerf405/compile.c new file mode 100644 index 00000000..da9976f2 --- /dev/null +++ b/os/cerf405/compile.c @@ -0,0 +1,34 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#define MAXDCR 0x220 + +#define DCRF(n) ((((n)>>5)&0x1F)|(((n)&0x1F)<<5)) +#define MTDCR(s,n) ((31<<26)|((s)<<21)|(DCRF(n)<<11)|(451<<1)) +#define MFDCR(n,t) ((31<<26)|((t)<<21)|(DCRF(n)<<11)|(323<<1)) +#define RETURN 0x4e800020 +ulong _getdcr[MAXDCR][2]; +ulong _putdcr[MAXDCR][2]; + +void +compiledcr(void) +{ + ulong *p; + int i; + + for(i=0; imachno (unused) */ + + /* ordering from here on irrelevant */ + ulong ticks; /* of the clock since boot time */ + Proc *proc; /* current process on this processor */ + Label sched; /* scheduler wakeup */ + Lock alarmlock; /* access to alarm list */ + void *alarm; /* alarms bound to this clock */ + int nrdy; + int speed; /* general system clock in MHz */ + long oscclk; /* oscillator frequency (MHz) */ + long cpuhz; /* general system clock (cycles) */ + long clockgen; /* clock generator frequency (cycles) */ + long vcohz; + long pllhz; + long plbhz; + long opbhz; + long epbhz; + long pcihz; + int cputype; + ulong delayloop; + + /* MUST BE LAST */ + int stack[1]; +}; +extern Mach mach0; + + +/* + * a parsed .ini line + */ +#define NISAOPT 8 + +struct ISAConf { + char* type; + ulong port; + ulong irq; + ulong mem; + int dma; + ulong size; + ulong freq; + uchar bus; + + int nopt; + char* opt[NISAOPT]; +}; + +struct Map { + int size; + ulong addr; +}; + +struct RMap { + char* name; + Map* map; + Map* mapend; + + Lock; +}; + +struct Power { + Dev* dev; + int (*powerdown)(Power*); + int (*powerup)(Power*); + int state; + void* arg; +}; + +extern register Mach *m; +extern register Proc *up; diff --git a/os/cerf405/devboot.c b/os/cerf405/devboot.c new file mode 100644 index 00000000..126fe6e6 --- /dev/null +++ b/os/cerf405/devboot.c @@ -0,0 +1,132 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +enum{ + Qdir, + Qboot, + Qmem, +}; + +Dirtab bootdir[]={ + ".", {Qdir,0,QTDIR}, 0, 0555, + "boot", {Qboot}, 0, 0666, + "mem", {Qmem}, 0, 0666, +}; + +#define NBOOT (sizeof bootdir/sizeof(Dirtab)) + +static void +bootreset(void) +{ +} + +static Chan* +bootattach(char *spec) +{ + return devattach('B', spec); +} + +static Walkqid* +bootwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, bootdir, NBOOT, devgen); +} + +static int +bootstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, bootdir, NBOOT, devgen); +} + +static Chan* +bootopen(Chan *c, int omode) +{ + return devopen(c, omode, bootdir, NBOOT, devgen); +} + +static void +bootclose(Chan*) +{ +} + +static long +bootread(Chan *c, void *buf, long n, vlong off) +{ + ulong offset = off; + + switch((ulong)c->qid.path){ + + case Qdir: + return devdirread(c, buf, n, bootdir, NBOOT, devgen); + + case Qmem: + /* kernel memory */ + if(offset>=KZERO && offset KZERO+conf.npage*BY2PG) + n = KZERO+conf.npage*BY2PG - offset; + memmove(buf, (char*)offset, n); + return n; + } + error(Ebadarg); + } + + error(Egreg); + return 0; /* not reached */ +} + +static long +bootwrite(Chan *c, void *buf, long n, vlong off) +{ + ulong offset = off; + ulong pc; + uchar *p; + + switch((ulong)c->qid.path){ + case Qmem: + /* kernel memory */ + if(offset>=KZERO && offset KZERO+conf.npage*BY2PG) + n = KZERO+conf.npage*BY2PG - offset; + memmove((char*)offset, buf, n); + segflush((void*)offset, n); + return n; + } + error(Ebadarg); + + case Qboot: + p = (uchar*)buf; + pc = (((((p[0]<<8)|p[1])<<8)|p[2])<<8)|p[3]; + if(pc < KZERO || pc >= KZERO+conf.npage*BY2PG) + error(Ebadarg); + splhi(); + segflush((void*)pc, 64*1024); + gotopc(pc); + } + error(Ebadarg); + return 0; /* not reached */ +} + +Dev bootdevtab = { + 'B', + "boot", + + bootreset, + devinit, + devshutdown, + bootattach, + bootwalk, + bootstat, + bootopen, + devcreate, + bootclose, + bootread, + devbread, + bootwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/cerf405/devether.c b/os/cerf405/devether.c new file mode 100644 index 00000000..eb4ed283 --- /dev/null +++ b/os/cerf405/devether.c @@ -0,0 +1,617 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +static Ether *etherxx[MaxEther]; + +Chan* +etherattach(char* spec) +{ + ulong ctlrno; + char *p; + Chan *chan; + Ether *ether; + + ctlrno = 0; + if(spec && *spec){ + ctlrno = strtoul(spec, &p, 0); + if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther)) + error(Ebadarg); + } + if((ether = etherxx[ctlrno]) == 0) + error(Enodev); + rlock(ether); + if(waserror()){ + runlock(ether); + nexterror(); + } + chan = devattach('l', spec); + chan->dev = ctlrno; + if(ether->attach) + ether->attach(etherxx[ctlrno]); + poperror(); + runlock(ether); + return chan; +} + +static void +ethershutdown(void) +{ + Ether *ether; + int i; + + for(i=0; idetach != nil) + ether->detach(ether); + } +} + +static Walkqid* +etherwalk(Chan* chan, Chan *nchan, char **name, int nname) +{ + Walkqid *wq; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + wq = netifwalk(etherxx[chan->dev], chan, nchan, name, nname); + poperror(); + runlock(ether); + return wq; +} + +static int +etherstat(Chan* chan, uchar* dp, int n) +{ + int s; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + s = netifstat(ether, chan, dp, n); + poperror(); + runlock(ether); + return s; +} + +static Chan* +etheropen(Chan* chan, int omode) +{ + Chan *c; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + c = netifopen(ether, chan, omode); + poperror(); + runlock(ether); + return c; +} + +static void +ethercreate(Chan*, char*, int, ulong) +{ +} + +static void +etherclose(Chan* chan) +{ + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + netifclose(ether, chan); + poperror(); + runlock(ether); +} + +static long +etherread(Chan* chan, void* buf, long n, vlong off) +{ + Ether *ether; + ulong offset = off; + long r; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + if((chan->qid.type & QTDIR) == 0 && ether->ifstat){ + /* + * With some controllers it is necessary to reach + * into the chip to extract statistics. + */ + if(NETTYPE(chan->qid.path) == Nifstatqid){ + r = ether->ifstat(ether, buf, n, offset); + goto out; + } + if(NETTYPE(chan->qid.path) == Nstatqid) + ether->ifstat(ether, buf, 0, offset); + } + r = netifread(ether, chan, buf, n, offset); +out: + poperror(); + runlock(ether); + return r; +} + +static Block* +etherbread(Chan* chan, long n, ulong offset) +{ + Block *b; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + b = netifbread(ether, chan, n, offset); + poperror(); + runlock(ether); + return b; +} + +static int +etherwstat(Chan* chan, uchar* dp, int n) +{ + Ether *ether; + int r; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + r = netifwstat(ether, chan, dp, n); + poperror(); + runlock(ether); + return r; +} + +static void +etherrtrace(Netfile* f, Etherpkt* pkt, int len) +{ + int i, n; + Block *bp; + + if(qwindow(f->in) <= 0) + return; + if(len > 58) + n = 58; + else + n = len; + bp = iallocb(64); + if(bp == nil) + return; + memmove(bp->wp, pkt->d, n); + i = TK2MS(MACHP(0)->ticks); + bp->wp[58] = len>>8; + bp->wp[59] = len; + bp->wp[60] = i>>24; + bp->wp[61] = i>>16; + bp->wp[62] = i>>8; + bp->wp[63] = i; + bp->wp += 64; + qpass(f->in, bp); +} + +Block* +etheriq(Ether* ether, Block* bp, int fromwire) +{ + Etherpkt *pkt; + ushort type; + int len, multi, tome, fromme; + Netfile **ep, *f, **fp, *fx; + Block *xbp; + + ether->inpackets++; + + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + type = (pkt->type[0]<<8)|pkt->type[1]; + fx = 0; + ep = ðer->f[Ntypes]; + + multi = pkt->d[0] & 1; + /* check for valid multcast addresses */ + if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){ + if(!activemulti(ether, pkt->d, sizeof(pkt->d))){ + if(fromwire){ + freeb(bp); + bp = 0; + } + return bp; + } + } + + /* is it for me? */ + tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0; + + /* + * Multiplex the packet to all the connections which want it. + * If the packet is not to be used subsequently (fromwire != 0), + * attempt to simply pass it into one of the connections, thereby + * saving a copy of the data (usual case hopefully). + */ + for(fp = ether->f; fp < ep; fp++){ + if((f = *fp) && (f->type == type || f->type < 0)) + if(tome || multi || f->prom){ + /* Don't want to hear bridged packets */ + if(f->bridge && !fromwire && !fromme) + continue; + if(!f->headersonly){ + if(fromwire && fx == 0) + fx = f; + else if(xbp = iallocb(len)){ + memmove(xbp->wp, pkt, len); + xbp->wp += len; + if(qpass(f->in, xbp) < 0) + ether->soverflows++; + } + else + ether->soverflows++; + } + else + etherrtrace(f, pkt, len); + } + } + + if(fx){ + if(qpass(fx->in, bp) < 0) + ether->soverflows++; + return 0; + } + if(fromwire){ + freeb(bp); + return 0; + } + + return bp; +} + +static int +etheroq(Ether* ether, Block* bp) +{ + int len, loopback, s; + Etherpkt *pkt; + + ether->outpackets++; + + /* + * Check if the packet has to be placed back onto the input queue, + * i.e. if it's a loopback or broadcast packet or the interface is + * in promiscuous mode. + * If it's a loopback packet indicate to etheriq that the data isn't + * needed and return, etheriq will pass-on or free the block. + * To enable bridging to work, only packets that were originated + * by this interface are fed back. + */ + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){ + s = splhi(); + etheriq(ether, bp, 0); + splx(s); + } + + if(!loopback){ + qbwrite(ether->oq, bp); + if(ether->transmit != nil) + ether->transmit(ether); + }else + freeb(bp); + + return len; +} + +static long +etherwrite(Chan* chan, void* buf, long n, vlong) +{ + Ether *ether; + Block *bp; + int onoff; + Cmdbuf *cb; + long l; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + if(NETTYPE(chan->qid.path) != Ndataqid) { + l = netifwrite(ether, chan, buf, n); + if(l >= 0) + goto out; + cb = parsecmd(buf, n); + if(strcmp(cb->f[0], "nonblocking") == 0){ + if(cb->nf <= 1) + onoff = 1; + else + onoff = atoi(cb->f[1]); + qnoblock(ether->oq, onoff); + free(cb); + goto out; + } + free(cb); + if(ether->ctl!=nil){ + l = ether->ctl(ether,buf,n); + goto out; + } + error(Ebadctl); + } + + if(n > ether->maxmtu) + error(Etoobig); + if(n < ether->minmtu) + error(Etoosmall); + bp = allocb(n); + if(waserror()){ + freeb(bp); + nexterror(); + } + memmove(bp->rp, buf, n); + memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen); + bp->wp += n; + poperror(); + + l = etheroq(ether, bp); +out: + poperror(); + runlock(ether); + return l; +} + +static long +etherbwrite(Chan* chan, Block* bp, ulong) +{ + Ether *ether; + long n; + + n = BLEN(bp); + if(NETTYPE(chan->qid.path) != Ndataqid){ + if(waserror()) { + freeb(bp); + nexterror(); + } + n = etherwrite(chan, bp->rp, n, 0); + poperror(); + freeb(bp); + return n; + } + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + if(n > ether->maxmtu){ + freeb(bp); + error(Etoobig); + } + if(n < ether->minmtu){ + freeb(bp); + error(Etoosmall); + } + n = etheroq(ether, bp); + poperror(); + runlock(ether); + return n; +} + +static struct { + char* type; + int (*reset)(Ether*); +} cards[MaxEther+1]; + +void +addethercard(char* t, int (*r)(Ether*)) +{ + static int ncard; + + if(ncard == MaxEther) + panic("too many ether cards"); + cards[ncard].type = t; + cards[ncard].reset = r; + ncard++; +} + +int +parseether(uchar *to, char *from) +{ + char nip[4]; + char *p; + int i; + + p = from; + for(i = 0; i < Eaddrlen; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +static void +etherreset(void) +{ + Ether *ether; + int i, n, ctlrno; + char name[KNAMELEN], buf[128]; + + for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){ + if(ether == 0) + ether = malloc(sizeof(Ether)); + memset(ether, 0, sizeof(Ether)); + ether->ctlrno = ctlrno; + ether->mbps = 10; + ether->minmtu = ETHERMINTU; + ether->maxmtu = ETHERMAXTU; + ether->tbdf = BUSUNKNOWN; + + if(archether(ctlrno, ether) <= 0) + continue; + + for(n = 0; cards[n].type; n++){ + if(cistrcmp(cards[n].type, ether->type)) + continue; + for(i = 0; i < ether->nopt; i++){ + if(cistrncmp(ether->opt[i], "ea=", 3) == 0){ + if(parseether(ether->ea, ðer->opt[i][3]) == -1) + memset(ether->ea, 0, Eaddrlen); + }else if(cistrcmp(ether->opt[i], "fullduplex") == 0 || + cistrcmp(ether->opt[i], "10BASE-TFD") == 0) + ether->fullduplex = 1; + else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0) + ether->mbps = 100; + } + if(cards[n].reset(ether)) + break; + snprint(name, sizeof(name), "ether%d", ctlrno); + + if(ether->interrupt != nil) + intrenable(ether->irq, ether->interrupt, ether, ether->tbdf, name); + + i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %lud", + ctlrno, ether->type, ether->mbps, ether->port, ether->irq); + if(ether->mem) + i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem)); + if(ether->size) + i += sprint(buf+i, " size 0x%luX", ether->size); + i += sprint(buf+i, ": %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX", + ether->ea[0], ether->ea[1], ether->ea[2], + ether->ea[3], ether->ea[4], ether->ea[5]); + sprint(buf+i, "\n"); + print(buf); + + if(ether->mbps == 100){ + netifinit(ether, name, Ntypes, 256*1024); + if(ether->oq == 0) + ether->oq = qopen(256*1024, Qmsg, 0, 0); + } + else{ + netifinit(ether, name, Ntypes, 64*1024); + if(ether->oq == 0) + ether->oq = qopen(64*1024, Qmsg, 0, 0); + } + if(ether->oq == 0) + panic("etherreset %s", name); + ether->alen = Eaddrlen; + memmove(ether->addr, ether->ea, Eaddrlen); + memset(ether->bcast, 0xFF, Eaddrlen); + + etherxx[ctlrno] = ether; + ether = 0; + break; + } + } + if(ether) + free(ether); +} + +static void +etherpower(int on) +{ + int i; + Ether *ether; + + for(i = 0; i < MaxEther; i++){ + if((ether = etherxx[i]) == nil || ether->power == nil) + continue; + if(on){ + if(canrlock(ether)) + continue; + if(ether->power != nil) + ether->power(ether, on); + wunlock(ether); + }else{ + if(!canrlock(ether)) + continue; + wlock(ether); + if(ether->power != nil) + ether->power(ether, on); + /* Keep locked until power goes back on */ + } + } +} + +#define POLY 0xedb88320 + +/* really slow 32 bit crc for ethers */ +ulong +ethercrc(uchar *p, int len) +{ + int i, j; + ulong crc, b; + + crc = 0xffffffff; + for(i = 0; i < len; i++){ + b = *p++; + for(j = 0; j < 8; j++){ + crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0); + b >>= 1; + } + } + return crc; +} + +Dev etherdevtab = { + 'l', + "ether", + + etherreset, + devinit, + ethershutdown, + etherattach, + etherwalk, + etherstat, + etheropen, + ethercreate, + etherclose, + etherread, + etherbread, + etherwrite, + etherbwrite, + devremove, + etherwstat, + etherpower, +}; diff --git a/os/cerf405/devrtc.c b/os/cerf405/devrtc.c new file mode 100644 index 00000000..f743f1e0 --- /dev/null +++ b/os/cerf405/devrtc.c @@ -0,0 +1,369 @@ +/* + * DS1339 Timekeeper (on I2C) + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "io.h" + +typedef struct Rtc Rtc; +typedef struct Rtcreg Rtcreg; + +struct Rtc +{ + int sec; + int min; + int hour; + int wday; + int mday; + int mon; + int year; +}; + +struct Rtcreg +{ + uchar sec; + uchar min; + uchar hour; + uchar wday; /* 1=Sun */ + uchar mday; /* 00-31 */ + uchar mon; /* 1-12 */ + uchar year; +}; + +enum{ + Qdir = 0, + Qrtc, + + Rtclen= 7, /* bytes read and written to timekeeper */ +}; + +static QLock rtclock; /* mutex on nvram operations */ +static I2Cdev rtdev; + +static Dirtab rtcdir[]={ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "rtc", {Qrtc, 0}, 0, 0664, +}; + +static ulong rtc2sec(Rtc*); +static void sec2rtc(ulong, Rtc*); +static void setrtc(Rtc*); + +static void +rtcreset(void) +{ + rtdev.addr = 0x68; + rtdev.salen = 1; + i2csetup(1); +} + +static Chan* +rtcattach(char *spec) +{ + return devattach('r', spec); +} + +static Walkqid* +rtcwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen); +} + +static int +rtcstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen); +} + +static Chan* +rtcopen(Chan *c, int omode) +{ + omode = openmode(omode); + switch((ulong)c->qid.path){ + case Qrtc: + if(strcmp(up->env->user, eve)!=0 && omode!=OREAD) + error(Eperm); + break; + } + return devopen(c, omode, rtcdir, nelem(rtcdir), devgen); +} + +static void +rtcclose(Chan*) +{ +} + +static long +rtcread(Chan *c, void *buf, long n, vlong offset) +{ + ulong t, ot; + + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen); + + switch((ulong)c->qid.path){ + case Qrtc: + qlock(&rtclock); + t = rtctime(); + do{ + ot = t; + t = rtctime(); /* make sure there's no skew */ + }while(t != ot); + qunlock(&rtclock); + return readnum(offset, buf, n, t, 12); + } + error(Egreg); + return -1; /* never reached */ +} + +static long +rtcwrite(Chan *c, void *buf, long n, vlong off) +{ + Rtc rtc; + ulong secs; + char *cp, sbuf[32]; + ulong offset = off; + + switch((ulong)c->qid.path){ + case Qrtc: + if(offset!=0 || n >= sizeof(sbuf)-1) + error(Ebadarg); + memmove(sbuf, buf, n); + sbuf[n] = '\0'; + /* + * read the time + */ + cp = sbuf; + while(*cp){ + if(*cp>='0' && *cp<='9') + break; + cp++; + } + secs = strtoul(cp, 0, 0); + /* + * convert to bcd + */ + sec2rtc(secs, &rtc); + /* + * write it + */ + setrtc(&rtc); + return n; + } + error(Egreg); + return -1; /* never reached */ +} + +Dev rtcdevtab = { + 'r', + "rtc", + + rtcreset, + devinit, + devshutdown, + rtcattach, + rtcwalk, + rtcstat, + rtcopen, + devcreate, + rtcclose, + rtcread, + devbread, + rtcwrite, + devbwrite, + devremove, + devwstat, +}; + +static int +getbcd(int bcd) +{ + return (bcd&0x0f) + 10 * (bcd>>4); +} + +static int +putbcd(int val) +{ + return (val % 10) | (((val/10) % 10) << 4); +} + +long +rtctime(void) +{ + Rtc rtc; + Rtcreg d; + int h; + + if(waserror()){ + iprint("rtc: err %s\n", up->env->errstr); + return 0; + } + if(i2crecv(&rtdev, &d, Rtclen, 0) != Rtclen) + return 0; + poperror(); + rtc.sec = getbcd(d.sec); + rtc.min = getbcd(d.min); + if(d.hour & (1<<6)){ /* 12 hour clock */ + h = d.hour & 0x1F; + if(d.hour & (1<<5)) + h += 0x12; + rtc.hour = getbcd(h); + }else + rtc.hour = getbcd(d.hour); + rtc.mday = getbcd(d.mday); + rtc.mon = getbcd(d.mon & 0x7f); + rtc.year = getbcd(d.year); + if(rtc.mon < 1 || rtc.mon > 12) + return 0; + if(d.mon & (1<<7)) + rtc.year += 2000; + else + rtc.year += 1900; + return rtc2sec(&rtc); +} + +static void +setrtc(Rtc *rtc) +{ + Rtcreg d; + + memset(&d, 0, sizeof(d)); + d.year = putbcd(rtc->year % 100); + d.mon = putbcd(rtc->mon); + if(rtc->year >= 2000) + d.mon |= 1<<7; + d.wday = rtc->wday+1; + d.mday = putbcd(rtc->mday); + d.hour = putbcd(rtc->hour); + d.min = putbcd(rtc->min); + d.sec = putbcd(rtc->sec); + i2csend(&rtdev, &d, Rtclen, 0); +} + +#define SEC2MIN 60L +#define SEC2HOUR (60L*SEC2MIN) +#define SEC2DAY (24L*SEC2HOUR) + +/* + * days per month plus days/year + */ +static int dmsize[] = +{ + 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; +static int ldmsize[] = +{ + 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +/* + * return the days/month for the given year + */ +static int * +yrsize(int y) +{ + + if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0)) + return ldmsize; + else + return dmsize; +} + +/* + * compute seconds since Jan 1 1970 + */ +static ulong +rtc2sec(Rtc *rtc) +{ + ulong secs; + int i; + int *d2m; + + secs = 0; + + /* + * seconds per year + */ + for(i = 1970; i < rtc->year; i++){ + d2m = yrsize(i); + secs += d2m[0] * SEC2DAY; + } + + /* + * seconds per month + */ + d2m = yrsize(rtc->year); + for(i = 1; i < rtc->mon; i++) + secs += d2m[i] * SEC2DAY; + + secs += (rtc->mday-1) * SEC2DAY; + secs += rtc->hour * SEC2HOUR; + secs += rtc->min * SEC2MIN; + secs += rtc->sec; + + return secs; +} + +/* + * compute rtc from seconds since Jan 1 1970 + */ +static void +sec2rtc(ulong secs, Rtc *rtc) +{ + int d; + long hms, day; + int *d2m; + + /* + * break initial number into days + */ + hms = secs % SEC2DAY; + day = secs / SEC2DAY; + if(hms < 0) { + hms += SEC2DAY; + day -= 1; + } + + /* + * day is the day number. + * generate day of the week. + * The addend is 4 mod 7 (1/1/1970 was Thursday) + */ + + rtc->wday = (day + 7340036L) % 7; + + /* + * generate hours:minutes:seconds + */ + rtc->sec = hms % 60; + d = hms / 60; + rtc->min = d % 60; + d /= 60; + rtc->hour = d; + + /* + * year number + */ + if(day >= 0) + for(d = 1970; day >= *yrsize(d); d++) + day -= *yrsize(d); + else + for (d = 1970; day < 0; d--) + day += *yrsize(d-1); + rtc->year = d; + + /* + * generate month + */ + d2m = yrsize(rtc->year); + for(d = 1; day >= d2m[d]; d++) + day -= d2m[d]; + rtc->mday = day + 1; + rtc->mon = d; +} diff --git a/os/cerf405/devuart.c b/os/cerf405/devuart.c new file mode 100644 index 00000000..eba92cba --- /dev/null +++ b/os/cerf405/devuart.c @@ -0,0 +1,1064 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "../port/netif.h" + +/* + * Driver for the uart. + */ +enum +{ + /* + * register numbers + */ + Data= 0, /* xmit/rcv buffer */ + Iena= 1, /* interrupt enable */ + Ircv= (1<<0), /* for char rcv'd */ + Ixmt= (1<<1), /* for xmit buffer empty */ + Irstat=(1<<2), /* for change in rcv'er status */ + Imstat=(1<<3), /* for change in modem status */ + Istat= 2, /* interrupt flag (read) */ + Ipend= 1, /* interrupt pending (not) */ + Fenabd=(3<<6), /* on if fifo's enabled */ + Fifoctl=2, /* fifo control (write) */ + Fena= (1<<0), /* enable xmit/rcv fifos */ + Fdma= (1<<3), /* dma on */ + Ftrig= (1<<6), /* trigger after 4 input characters */ + Fclear=(3<<1), /* clear xmit & rcv fifos */ + Format= 3, /* byte format */ + Bits8= (3<<0), /* 8 bits/byte */ + Stop2= (1<<2), /* 2 stop bits */ + Pena= (1<<3), /* generate parity */ + Peven= (1<<4), /* even parity */ + Pforce=(1<<5), /* force parity */ + Break= (1<<6), /* generate a break */ + Dra= (1<<7), /* address the divisor */ + Mctl= 4, /* modem control */ + Dtr= (1<<0), /* data terminal ready */ + Rts= (1<<1), /* request to send */ + Ri= (1<<2), /* ring */ + Inton= (1<<3), /* turn on interrupts */ + Loop= (1<<4), /* loop back */ + Lstat= 5, /* line status */ + Inready=(1<<0), /* receive buffer full */ + Oerror=(1<<1), /* receiver overrun */ + Perror=(1<<2), /* receiver parity error */ + Ferror=(1<<3), /* rcv framing error */ + Berror=(1<<4), /* break alarm */ + Outready=(1<<5), /* output buffer full */ + Mstat= 6, /* modem status */ + Ctsc= (1<<0), /* clear to send changed */ + Dsrc= (1<<1), /* data set ready changed */ + Rire= (1<<2), /* rising edge of ring indicator */ + Dcdc= (1<<3), /* data carrier detect changed */ + Cts= (1<<4), /* complement of clear to send line */ + Dsr= (1<<5), /* complement of data set ready line */ + Ringl= (1<<6), /* complement of ring indicator line */ + Dcd= (1<<7), /* complement of data carrier detect line */ + Scratch=7, /* scratchpad */ + Dlsb= 0, /* divisor lsb */ + Dmsb= 1, /* divisor msb */ + + CTLS= 023, + CTLQ= 021, + + Stagesize= 1024, + Nuart= 2, /* max per machine */ +}; + +typedef struct Uart Uart; +struct Uart +{ + QLock; + int opens; + + int enabled; + Uart *elist; /* next enabled interface */ + char name[KNAMELEN]; + + uchar sticky[8]; /* sticky write register values */ + void* regs; + ulong port; + ulong freq; /* clock frequency */ + uchar mask; /* bits/char */ + int dev; + int baud; /* baud rate */ + + uchar istat; /* last istat read */ + int frame; /* framing errors */ + int overrun; /* rcvr overruns */ + + /* buffers */ + int (*putc)(Queue*, int); + Queue *iq; + Queue *oq; + + Lock flock; /* fifo */ + uchar fifoon; /* fifo's enabled */ + uchar nofifo; /* earlier chip version with nofifo */ + + Lock rlock; /* receive */ + uchar istage[Stagesize]; + uchar *ip; + uchar *ie; + + int haveinput; + + Lock tlock; /* transmit */ + uchar ostage[Stagesize]; + uchar *op; + uchar *oe; + + int modem; /* hardware flow control on */ + int xonoff; /* software flow control on */ + int blocked; + int cts, dsr, dcd; /* keep track of modem status */ + int ctsbackoff; + int hup_dsr, hup_dcd; /* send hangup upstream? */ + int dohup; + + Rendez r; +}; + +static Uart *uart[Nuart]; +static int nuart; + +struct Uartalloc { + Lock; + Uart *elist; /* list of enabled interfaces */ +} uartalloc; + +static void uartintr(Uart*); + +/* + * pick up architecture specific routines and definitions + */ +#include "uart.h" + +/* + * set the baud rate by calculating and setting the baudrate + * generator constant. This will work with fairly non-standard + * baud rates. + */ +static void +uartsetbaud(Uart *p, int rate) +{ + ulong brconst; + + if(rate <= 0) + return; + + p->freq = archuartclock(p->port, rate); + if(p->freq == 0) + return; + + brconst = (p->freq+8*rate-1)/(16*rate); + + uartwrreg(p, Format, Dra); + uartwr(p, Dmsb, (brconst>>8) & 0xff); + uartwr(p, Dlsb, brconst & 0xff); + uartwrreg(p, Format, 0); + + p->baud = rate; +} + +/* + * decide if we should hangup when dsr or dcd drops. + */ +static void +uartdsrhup(Uart *p, int n) +{ + p->hup_dsr = n; +} + +static void +uartdcdhup(Uart *p, int n) +{ + p->hup_dcd = n; +} + +static void +uartparity(Uart *p, char type) +{ + switch(type){ + case 'e': + p->sticky[Format] |= Pena|Peven; + break; + case 'o': + p->sticky[Format] &= ~Peven; + p->sticky[Format] |= Pena; + break; + default: + p->sticky[Format] &= ~(Pena|Peven); + break; + } + uartwrreg(p, Format, 0); +} + +/* + * set bits/character, default 8 + */ +void +uartbits(Uart *p, int bits) +{ + if(bits < 5 || bits > 8) + error(Ebadarg); + + p->sticky[Format] &= ~3; + p->sticky[Format] |= bits-5; + + uartwrreg(p, Format, 0); +} + + +/* + * toggle DTR + */ +void +uartdtr(Uart *p, int n) +{ + if(n) + p->sticky[Mctl] |= Dtr; + else + p->sticky[Mctl] &= ~Dtr; + + uartwrreg(p, Mctl, 0); +} + +/* + * toggle RTS + */ +void +uartrts(Uart *p, int n) +{ + if(n) + p->sticky[Mctl] |= Rts; + else + p->sticky[Mctl] &= ~Rts; + + uartwrreg(p, Mctl, 0); +} + +/* + * send break + */ +static void +uartbreak(Uart *p, int ms) +{ + if(ms == 0) + ms = 200; + + uartwrreg(p, Format, Break); + tsleep(&up->sleep, return0, 0, ms); + uartwrreg(p, Format, 0); +} + +static void +uartfifoon(Uart *p) +{ + ulong i, x; + + if(p->nofifo || uartrdreg(p, Istat) & Fenabd) + return; + + x = splhi(); + + /* reset fifos */ + p->sticky[Fifoctl] = 0; + uartwrreg(p, Fifoctl, Fclear); + + /* empty buffer and interrupt conditions */ + for(i = 0; i < 16; i++){ + if(uartrdreg(p, Istat)){ + /* nothing to do */ + } + if(uartrdreg(p, Data)){ + /* nothing to do */ + } + } + + /* turn on fifo */ + p->fifoon = 1; + p->sticky[Fifoctl] = Fena|Ftrig; + uartwrreg(p, Fifoctl, 0); + p->istat = uartrdreg(p, Istat); + if((p->istat & Fenabd) == 0) { + /* didn't work, must be an earlier chip type */ + p->nofifo = 1; + } + + splx(x); +} + +/* + * modem flow control on/off (rts/cts) + */ +static void +uartmflow(Uart *p, int n) +{ + ilock(&p->tlock); + if(n){ + p->sticky[Iena] |= Imstat; + uartwrreg(p, Iena, 0); + p->modem = 1; + p->cts = uartrdreg(p, Mstat) & Cts; + } else { + p->sticky[Iena] &= ~Imstat; + uartwrreg(p, Iena, 0); + p->modem = 0; + p->cts = 1; + } + iunlock(&p->tlock); + +// ilock(&p->flock); +// if(1) +// /* turn on fifo's */ +// uartfifoon(p); +// else { +// /* turn off fifo's */ +// p->fifoon = 0; +// p->sticky[Fifoctl] = 0; +// uartwrreg(p, Fifoctl, Fclear); +// } +// iunlock(&p->flock); +} + +/* + * turn on a port's interrupts. set DTR and RTS + */ +static void +uartenable(Uart *p) +{ + Uart **l; + + if(p->enabled) + return; + + uartportpower(p, 1); + + p->hup_dsr = p->hup_dcd = 0; + p->cts = p->dsr = p->dcd = 0; + + /* + * turn on interrupts + */ + p->sticky[Iena] = Ircv | Ixmt | Irstat; + uartwrreg(p, Iena, 0); + + /* + * turn on DTR and RTS + */ + uartdtr(p, 1); + uartrts(p, 1); + + uartfifoon(p); + + /* + * assume we can send + */ + ilock(&p->tlock); + p->cts = 1; + p->blocked = 0; + iunlock(&p->tlock); + + /* + * set baud rate to the last used + */ + uartsetbaud(p, p->baud); + + lock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p) + break; + } + if(*l == 0){ + p->elist = uartalloc.elist; + uartalloc.elist = p; + } + p->enabled = 1; + unlock(&uartalloc); +} + +/* + * turn off a port's interrupts. reset DTR and RTS + */ +static void +uartdisable(Uart *p) +{ + Uart **l; + + /* + * turn off interrupts + */ + p->sticky[Iena] = 0; + uartwrreg(p, Iena, 0); + + /* + * revert to default settings + */ + p->sticky[Format] = Bits8; + uartwrreg(p, Format, 0); + + /* + * turn off DTR, RTS, hardware flow control & fifo's + */ + uartdtr(p, 0); + uartrts(p, 0); + uartmflow(p, 0); + ilock(&p->tlock); + p->xonoff = p->blocked = 0; + iunlock(&p->tlock); + + uartportpower(p, 0); + + lock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p){ + *l = p->elist; + break; + } + } + p->enabled = 0; + unlock(&uartalloc); +} + +/* + * put some bytes into the local queue to avoid calling + * qconsume for every character + */ +static int +stageoutput(Uart *p) +{ + int n; + + n = qconsume(p->oq, p->ostage, Stagesize); + if(n <= 0) + return 0; + p->op = p->ostage; + p->oe = p->ostage + n; + return n; +} + +/* + * (re)start output + */ +static void +uartkick0(Uart *p) +{ + int i; + if((p->modem && (p->cts == 0)) || p->blocked) + return; + + /* + * 128 here is an arbitrary limit to make sure + * we don't stay in this loop too long. If the + * chips output queue is longer than 128, too + * bad -- presotto + */ + for(i = 0; i < 128; i++){ + if(!(uartrdreg(p, Lstat) & Outready)) + break; + if(p->op >= p->oe && stageoutput(p) == 0) + break; + uartwr(p, Data, *p->op++); + } +} + +static void +uartkick(void *v) +{ + Uart *p; + + p = v; + ilock(&p->tlock); + uartkick0(p); + iunlock(&p->tlock); +} + +/* + * restart input if it's off + */ +static void +uartflow(void *v) +{ + Uart *p; + + p = v; + if(p->modem) + uartrts(p, 1); + ilock(&p->rlock); + p->haveinput = 1; + iunlock(&p->rlock); +} + +/* + * default is 9600 baud, 1 stop bit, 8 bit chars, no interrupts, + * transmit and receive enabled, interrupts disabled. + */ +static void +uartsetup0(Uart *p) +{ + memset(p->sticky, 0, sizeof(p->sticky)); + /* + * set rate to 9600 baud. + * 8 bits/character. + * 1 stop bit. + * interrupts enabled. + */ + p->sticky[Format] = Bits8; + uartwrreg(p, Format, 0); + p->sticky[Mctl] |= Inton; + uartwrreg(p, Mctl, 0x0); + + uartsetbaud(p, 9600); + + p->iq = qopen(4*1024, 0, uartflow, p); + p->oq = qopen(4*1024, 0, uartkick, p); + if(p->iq == nil || p->oq == nil) + panic("uartsetup0"); + + p->ip = p->istage; + p->ie = &p->istage[Stagesize]; + p->op = p->ostage; + p->oe = p->ostage; +} + +/* + * called by uartinstall() to create a new duart + */ +void +uartsetup(ulong port, void *regs, ulong freq, char *name) +{ + Uart *p; + + if(nuart >= Nuart) + return; + + p = xalloc(sizeof(Uart)); + uart[nuart] = p; + strcpy(p->name, name); + p->dev = nuart; + nuart++; + p->port = port; + p->regs = regs; + p->freq = freq; + uartsetup0(p); +} + +/* + * called by main() to configure a duart port as a console or a mouse + */ +void +uartspecial(int port, int baud, Queue **in, Queue **out, int (*putc)(Queue*, int)) +{ + Uart *p = uart[port]; + uartenable(p); + if(baud) + uartsetbaud(p, baud); + p->putc = putc; + if(in) + *in = p->iq; + if(out) + *out = p->oq; + p->opens++; +} + +/* + * handle an interrupt to a single uart + */ +static void +uartintr(Uart *p) +{ + uchar ch; + int s, l; + + for (s = uartrdreg(p, Istat); !(s&Ipend); s = uartrdreg(p, Istat)) { + switch(s&0x3f){ + case 4: /* received data available */ + case 6: /* receiver line status (alarm or error) */ + case 12: /* character timeout indication */ + while ((l = uartrdreg(p, Lstat)) & Inready) { + if(l & Ferror) + p->frame++; + if(l & Oerror) + p->overrun++; + ch = uartrdreg(p, Data) & 0xff; + if (l & (Berror|Perror|Ferror)) { + /* ch came with break, parity or framing error - consume */ + continue; + } + if (ch == CTLS || ch == CTLQ) { + ilock(&p->tlock); + if(p->xonoff){ + if(ch == CTLS) + p->blocked = 1; + else + p->blocked = 0; /* clock gets output going again */ + } + iunlock(&p->tlock); + } + if(p->putc) + p->putc(p->iq, ch); + else { + ilock(&p->rlock); + if(p->ip < p->ie) + *p->ip++ = ch; + else + p->overrun++; + p->haveinput = 1; + iunlock(&p->rlock); + } + } + break; + + case 2: /* transmitter not full */ + uartkick(p); + break; + + case 0: /* modem status */ + ch = uartrdreg(p, Mstat); + if(ch & Ctsc){ + ilock(&p->tlock); + l = p->cts; + p->cts = ch & Cts; + if(l == 0 && p->cts) + p->ctsbackoff = 2; /* clock gets output going again */ + iunlock(&p->tlock); + } + if (ch & Dsrc) { + l = ch & Dsr; + if(p->hup_dsr && p->dsr && !l){ + ilock(&p->rlock); + p->dohup = 1; + iunlock(&p->rlock); + } + p->dsr = l; + } + if (ch & Dcdc) { + l = ch & Dcd; + if(p->hup_dcd && p->dcd && !l){ + ilock(&p->rlock); + p->dohup = 1; + iunlock(&p->rlock); + } + p->dcd = l; + } + break; + + default: + iprint("weird uart interrupt #%2.2ux\n", s); + break; + } + } + p->istat = s; +} + +/* + * we save up input characters till clock time + * + * There's also a bit of code to get a stalled print going. + * It shouldn't happen, but it does. Obviously I don't + * understand something. Since it was there, I bundled a + * restart after flow control with it to give some hysteresis + * to the hardware flow control. This makes compressing + * modems happier but will probably bother something else. + * -- presotto + */ +void +uartclock(void) +{ + int n; + Uart *p; + + for(p = uartalloc.elist; p; p = p->elist){ + + /* this amortizes cost of qproduce to many chars */ + if(p->haveinput){ + ilock(&p->rlock); + if(p->haveinput){ + n = p->ip - p->istage; + if(n > 0 && p->iq){ + if(n > Stagesize) + panic("uartclock"); + if(qproduce(p->iq, p->istage, n) < 0) + uartrts(p, 0); + else + p->ip = p->istage; + } + p->haveinput = 0; + } + iunlock(&p->rlock); + } + if(p->dohup){ + ilock(&p->rlock); + if(p->dohup){ + qhangup(p->iq, 0); + qhangup(p->oq, 0); + } + p->dohup = 0; + iunlock(&p->rlock); + } + + /* this adds hysteresis to hardware flow control */ + if(p->ctsbackoff){ + ilock(&p->tlock); + if(p->ctsbackoff){ + if(--(p->ctsbackoff) == 0) + uartkick0(p); + } + iunlock(&p->tlock); + } + } +} + +Dirtab *uartdir; +int ndir; + +static void +setlength(int i) +{ + Uart *p; + + if(i >= 0){ + p = uart[i]; + if(p && p->opens && p->iq) + uartdir[1+3*i].length = qlen(p->iq); + } else for(i = 0; i < nuart; i++){ + p = uart[i]; + if(p && p->opens && p->iq) + uartdir[1+3*i].length = qlen(p->iq); + } +} + +/* + * all uarts must be uartsetup() by this point or inside of uartinstall() + */ +static void +uartreset(void) +{ + int i; + Dirtab *dp; + uartinstall(); /* architecture specific */ + + ndir = 1+3*nuart; + uartdir = xalloc(ndir * sizeof(Dirtab)); + dp = uartdir; + strcpy(dp->name, "."); + mkqid(&dp->qid, 0, 0, QTDIR); + dp->length = 0; + dp->perm = DMDIR|0555; + dp++; + for(i = 0; i < nuart; i++){ + /* 3 directory entries per port */ + strcpy(dp->name, uart[i]->name); + dp->qid.path = NETQID(i, Ndataqid); + dp->perm = 0666; + dp++; + sprint(dp->name, "%sctl", uart[i]->name); + dp->qid.path = NETQID(i, Nctlqid); + dp->perm = 0666; + dp++; + sprint(dp->name, "%sstatus", uart[i]->name); + dp->qid.path = NETQID(i, Nstatqid); + dp->perm = 0444; + dp++; + } +} + +static Chan* +uartattach(char *spec) +{ + return devattach('t', spec); +} + +static Walkqid* +uartwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, uartdir, ndir, devgen); +} + +static int +uartstat(Chan *c, uchar *dp, int n) +{ + if(NETTYPE(c->qid.path) == Ndataqid) + setlength(NETID(c->qid.path)); + return devstat(c, dp, n, uartdir, ndir, devgen); +} + +static Chan* +uartopen(Chan *c, int omode) +{ + Uart *p; + + c = devopen(c, omode, uartdir, ndir, devgen); + + switch(NETTYPE(c->qid.path)){ + case Nctlqid: + case Ndataqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(p->opens++ == 0){ + uartenable(p); + qreopen(p->iq); + qreopen(p->oq); + } + qunlock(p); + break; + } + + return c; +} + +static void +uartclose(Chan *c) +{ + Uart *p; + + if(c->qid.type & QTDIR) + return; + if((c->flag & COPEN) == 0) + return; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + case Nctlqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(--(p->opens) == 0){ + uartdisable(p); + qclose(p->iq); + qclose(p->oq); + p->ip = p->istage; + p->dcd = p->dsr = p->dohup = 0; + } + qunlock(p); + break; + } +} + +static long +uartstatus(Chan*, Uart *p, void *buf, long n, long offset) +{ + uchar mstat, fstat, istat, tstat; + char str[256]; + + str[0] = 0; + tstat = p->sticky[Mctl]; + mstat = uartrdreg(p, Mstat); + istat = p->sticky[Iena]; + fstat = p->sticky[Format]; + snprint(str, sizeof str, + "b%d c%d d%d e%d l%d m%d p%c r%d s%d\n" + "%d %d %d%s%s%s%s%s\n", + + p->baud, + p->hup_dcd, + (tstat & Dtr) != 0, + p->hup_dsr, + (fstat & Bits8) + 5, + (istat & Imstat) != 0, + (fstat & Pena) ? ((fstat & Peven) ? 'e' : 'o') : 'n', + (tstat & Rts) != 0, + (fstat & Stop2) ? 2 : 1, + + p->dev, + p->frame, + p->overrun, + uartrdreg(p, Istat) & Fenabd ? " fifo" : "", + (mstat & Cts) ? " cts" : "", + (mstat & Dsr) ? " dsr" : "", + (mstat & Dcd) ? " dcd" : "", + (mstat & Ringl) ? " ring" : "" + ); + return readstr(offset, buf, n, str); +} + +static long +uartread(Chan *c, void *buf, long n, vlong off) +{ + Uart *p; + ulong offset = off; + + if(c->qid.type & QTDIR){ + setlength(-1); + return devdirread(c, buf, n, uartdir, ndir, devgen); + } + + p = uart[NETID(c->qid.path)]; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qread(p->iq, buf, n); + case Nctlqid: + return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE); + case Nstatqid: + return uartstatus(c, p, buf, n, offset); + } + + return 0; +} + +static void +uartctl(Uart *p, char *cmd) +{ + int i, n; + + /* let output drain for a while */ + for(i = 0; i < 16 && qlen(p->oq); i++) + tsleep(&p->r, (int(*)(void*))qlen, p->oq, 125); + + if(strncmp(cmd, "break", 5) == 0){ + uartbreak(p, 0); + return; + } + + + n = atoi(cmd+1); + switch(*cmd){ + case 'B': + case 'b': + uartsetbaud(p, n); + break; + case 'C': + case 'c': + uartdcdhup(p, n); + break; + case 'D': + case 'd': + uartdtr(p, n); + break; + case 'E': + case 'e': + uartdsrhup(p, n); + break; + case 'f': + case 'F': + qflush(p->oq); + break; + case 'H': + case 'h': + qhangup(p->iq, 0); + qhangup(p->oq, 0); + break; + case 'L': + case 'l': + uartbits(p, n); + break; + case 'm': + case 'M': + uartmflow(p, n); + break; + case 'n': + case 'N': + qnoblock(p->oq, n); + break; + case 'P': + case 'p': + uartparity(p, *(cmd+1)); + break; + case 'K': + case 'k': + uartbreak(p, n); + break; + case 'R': + case 'r': + uartrts(p, n); + break; + case 'Q': + case 'q': + qsetlimit(p->iq, n); + qsetlimit(p->oq, n); + break; + case 'W': + case 'w': + /* obsolete */ + break; + case 'X': + case 'x': + ilock(&p->tlock); + p->xonoff = n; + iunlock(&p->tlock); + break; + } +} + +static long +uartwrite(Chan *c, void *buf, long n, vlong) +{ + Uart *p; + char cmd[32]; + + if(c->qid.type & QTDIR) + error(Eperm); + + p = uart[NETID(c->qid.path)]; + + /* + * The fifo's turn themselves off sometimes. + * It must be something I don't understand. -- presotto + */ + lock(&p->flock); + if((p->istat & Fenabd) == 0 && p->fifoon && p->nofifo == 0) + uartfifoon(p); + unlock(&p->flock); + + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qwrite(p->oq, buf, n); + case Nctlqid: + if(n >= sizeof(cmd)) + n = sizeof(cmd)-1; + memmove(cmd, buf, n); + cmd[n] = 0; + uartctl(p, cmd); + return n; + } +} + +static int +uartwstat(Chan *c, uchar *dp, int n) +{ + Dir d; + Dirtab *dt; + + if(!iseve()) + error(Eperm); + if(c->qid.type & QTDIR) + error(Eperm); + if(NETTYPE(c->qid.path) == Nstatqid) + error(Eperm); + + dt = &uartdir[1+3 * NETID(c->qid.path)]; + n = convM2D(dp, n, &d, nil); + if(n == 0) + error(Eshortstat); + if(d.mode != ~0UL){ + d.mode &= 0666; + dt[0].perm = dt[1].perm = d.mode; + } + return n; +} + +Dev uartdevtab = { + 't', + "uart", + + uartreset, + devinit, + devshutdown, + uartattach, + uartwalk, + uartstat, + uartopen, + devcreate, + uartclose, + uartread, + devbread, + uartwrite, + devbwrite, + devremove, + uartwstat, +}; diff --git a/os/cerf405/etheremac.c b/os/cerf405/etheremac.c new file mode 100644 index 00000000..484c4205 --- /dev/null +++ b/os/cerf405/etheremac.c @@ -0,0 +1,829 @@ +/* + * ethernet + */ + +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "ethermii.h" +#include "etherif.h" +#include "ureg.h" + +/* + * TO DO: + * - test EMAC1 + */ + +#define DBG if(0)iprint +#define MIIDBG if(0)iprint + +enum { + Nrdre = 64, /* receive descriptor ring entries */ + Ntdre = 32, /* transmit descriptor ring entries */ + Nrxchan = 2, + Ntxchan = 2, /* there are actually 4 but we only use 2 now */ + + Rbsize = ETHERMAXTU, /* ring buffer size */ + Bufsize = (Rbsize+CACHELINESZ-1)&~(CACHELINESZ-1), /* aligned */ +}; + +enum { + /* emac-specific Rx BD bits */ + RxOverrun= 1<<9, /* not enough empty space in FIFO */ + RxPause= 1<<8, /* control pause packet */ + RxBad= 1<<7, /* packet error */ + RxRunt= 1<<6, + RxShort= 1<<5, + RxAlign= 1<<4, + RxFCS= 1<<3, + RxLong= 1<<2, + RxRange= 1<<1, /* out of range error */ + RxInRange= 1<<0, /* in range error */ + RxError= (0x3FF & ~RxPause), /* error flags */ + + /* emac-specific Tx BD bits */ + /* write access */ + TxFCS= 1<<9, /* generate FCS */ + TxPad= 1<<8, /* pad short frames */ + TxInsSA= 1<<7, /* insert source address */ + TxRepSA= 1<<6, /* replace source address */ + TxInsVLAN= 1<<5, /* insert VLAN tag */ + TxRepVLAN= 1<<4, /* replace VLAN tag */ + + /* read access (status) */ + TxBadFCS= 1<<9, + TxBadPrev= 1<<8, /* bad previous packet in dependent mode */ + TxLostCarrier= 1<<7, + TxEDef= 1<<6, /* excessive deferral */ + TxECol= 1<<5, /* excessive collisions */ + TxLateCol= 1<<4, /* late collision (half-duplex only) */ + TxManyCol= 1<<3, /* more than 1 but less than 16 collisions */ + TxCollision= 1<<2, /* single collision */ + TxUnderrun= 1<<1, /* didn't fill FIFO in time */ + TxSQE= 1<<0, /* signal quality test failed (10mbit half-duplex only) */ + TxError= 0x3FF, /* error flags */ +}; + +typedef struct Emac Emac; +struct Emac { + ulong mr0; /* mode register 0 [see 19-48] */ + ulong mr1; /* mode register 1 [Reset] */ + ulong tmr0; /* transmit mode register 0 [see 19-28] */ + ulong tmr1; /* transmit mode register 1 [see 19-28] */ + ulong rmr; /* receive mode register [Reset] */ + ulong isr; /* interrupt status register [Always] */ + ulong iser; /* interrupt status enable register [Reset] */ + ulong iahr; /* individual address high [Reset, R, T]*/ + ulong ialr; /* individual address low [Reset, R, T] */ + ulong vtpid; /* VLAN Tag Protocol Identifier [Reset, R, T] */ + ulong vtci; /* VLAN Tag Control Information [Reset, R, T] */ + ulong ptr; /* pause timer [Reset, T] */ + ulong iaht[4]; /* individual address hash table [Reset, R] */ + ulong gaht[4]; /* group address hash table [Reset, R] */ + ulong lsah; /* last source address high */ + ulong lsal; /* last source address low */ + ulong ipgvr; /* inter-packet gap value [Reset, T] */ + ulong stacr; /* STA control register [see 19-41] */ + ulong trtr; /* transmit request threshold register [see 19-42] */ + ulong rwmr; /* receive low/high water mark [Reset] */ + ulong octx; /* bytes transmitted */ + ulong ocrx; /* bytes received */ +}; + +enum { + /* mode register 0 */ + Mr0Rxi= 1<<31, /* receive MAC idle */ + Mr0Txi= 1<<30, /* transmit MAC idle */ + Mr0Srst= 1<<29, /* soft reset; soft reset in progress */ + Mr0Txe= 1<<28, /* tx MAC enable */ + Mr0Rxe= 1<<27, /* rx MAC enable */ + Mr0Wke= 1<<26, /* enable wake-up packets */ + + /* mode register 1 */ + Mr1Fde= 1<<31, /* full-duplex enable */ + Mr1Ile= 1<<30, /* internal loop-back enable */ + Mr1Vle= 1<<29, /* VLAN enable */ + Mr1Eifc= 1<<28, /* enable integrated flow control */ + Mr1App= 1<<27, /* allow pause packets */ + Mr1Ist= 1<<24, /* ignore sqe test (all but half-duplex 10m/bit) */ + Mr1Mf10= 0<<22, /* medium [MII] frequency is 10 mbps */ + Mr1Mf100= 1<<22, /* medium frequency is 100 mbps */ + Mr1Rfs512= 0<<20, /* RX FIFO size (512 bytes) */ + Mr1Rfs1024= 1<<20, + Mr1Rfs2048= 2<<20, + Mr1Rfs4096= 3<<20, + Mr1Tfs1024= 1<<18, /* TX FIFO size (1024 bytes) */ + Mr1Tfs2048= 2<<18, + Mr1Tr0sp= 0<<15, /* transmit request 0: single packet */ + Mr1Tr0mp= 1<<15, /* multiple packets */ + Mr1Tr0dm= 2<<15, /* dependent mode */ + Mr1Tr1sp= 0<<13, /* transmit request 1: single packet */ + Mr1Tr1mp= 1<<13, /* multiple packets */ + Mr1Tr1dm= 2<<13, /* dependent mode */ + + /* transmit mode register 0 */ + Tmr0Gnp0= 1<<31, /* get new packet channel 0 */ + Tmr0Gnp1= 1<<30, /* get new packet channel 1 */ + Tmr0Gnpd= 1<<29, /* get new packet dependent mode */ + Tmr0Fc= 1<<28, /* first channel (dependent mode) */ + + /* transmit mode register 1 */ + Tmr1Trl_s= 27, /* transmit low request (shift) */ + Tmr1Tur_s= 16, /* transmit urgent request (shift) */ + + /* receive mode register */ + RmrSp= 1<<31, /* strip pad/FCS bytes */ + RmrSfcs= 1<<30, /* strip FCS */ + RmrRrp= 1<<29, /* receive runt packets */ + RmrRfp= 1<<28, /* receive packets with FCS error */ + RmrRop= 1<<27, /* receive oversize packets */ + RmrRpir= 1<<26, /* receive packets with in range error */ + RmrPpp= 1<<25, /* propagate pause packet */ + RmrPme= 1<<24, /* promiscuous mode enable */ + RmrPmme= 1<<23, /* promiscuous mode multicast enable */ + RmrIae= 1<<22, /* individual address enable */ + RmrMiae= 1<<21, /* multiple individual address enable */ + RmrBae= 1<<20, /* broadcast address enable */ + RmrMae= 1<<19, /* multicast address enable */ + + /* interrupt status register */ + IsrOvr= 1<<25, /* overrun error */ + IsrPp= 1<<24, /* pause packet */ + IsrBp= 1<<23, /* bad packet */ + IsrRp= 1<<22, /* runt packet */ + IsrSe= 1<<21, /* short event */ + IsrAle= 1<<20, /* alignment error */ + IsrBfcs= 1<<19, /* bad FCS */ + IsrPtle= 1<<18, /* packet too long error */ + IsrOre= 1<<17, /* out of range error */ + IsrIre= 1<<16, /* in range error */ + IsrDbdm= 1<<9, /* dead bit dependent mode */ + IsrDb0= 1<<8, /* dead bit 0 */ + IsrSe0= 1<<7, /* sqe 0 */ + IsrTe0= 1<<6, /* tx error 0 */ + IsrDb1= 1<<5, /* dead bit 1 */ + IsrSe1= 1<<4, /* sqe 1 */ + IsrTe1= 1<<3, /* tx error 1 */ + IsrMos= 1<<1, /* MMA operation succeeded */ + IsrMof= 1<<0, /* MMA operation failed */ + + /* STA control register */ + StaOc= 1<<15, /* operation complete */ + StaPhye= 1<<14, /* PHY error */ + StaRead= 1<<12, /* STA read */ + StaWrite= 2<<12, /* STA write */ + StaOpb50= 0<<10, /* OPB frequency */ + StaOpb66= 1<<10, + StaOpb83= 2<<10, + StaOpb100= 3<<10, + + /* transmit request threshold */ + TrtrTrt_s= 27, /* threshold (shift) -- and the value is (threshold/64)-1 */ + + /* receive low/high water mark register */ + RwmrRlwm_s= 23, /* low water mark (shift) */ + RwmrRhwm_s= 7, /* high water mark (shift) */ +}; + +typedef struct { + Lock; + int port; + int init; + int active; + Emac *regs; + Emac *miiregs; + Mal* rx; + Mal* tx; + + Mii *mii; + + Ring; + + ulong interrupts; /* statistics */ + ulong deferred; + ulong heartbeat; + ulong latecoll; + ulong retrylim; + ulong underrun; + ulong overrun; + ulong carrierlost; + ulong retrycount; +} Ctlr; + +static void dumpemac(Emac*); + +static void +attach(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + if(!ctlr->active){ + malrxenable(ctlr->rx); + maltxenable(ctlr->tx); + eieio(); + ctlr->regs->mr0 = Mr0Txe | Mr0Rxe; + eieio(); + ctlr->active = 1; + } + iunlock(ctlr); +} + +static void +closed(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + if(ctlr->active){ + ilock(ctlr); +iprint("ether closed\n"); + ctlr->regs->mr0 &= ~(Mr0Txe | Mr0Rxe); /* reset enable bits */ + /* TO DO: reset ring */ + /* TO DO: could wait */ + ctlr->active = 0; + iunlock(ctlr); + } +} + +static void +promiscuous(void* arg, int on) +{ + Ether *ether; + Ctlr *ctlr; + + ether = (Ether*)arg; + ctlr = ether->ctlr; + + ilock(ctlr); + if(on || ether->nmaddr) + ctlr->regs->rmr |= RmrPme | RmrPmme; + else + ctlr->regs->rmr &= ~(RmrPme | RmrPmme); + iunlock(ctlr); +} + +static void +multicast(void* arg, uchar *addr, int on) +{ + Ether *ether; + Ctlr *ctlr; + + USED(addr, on); /* if on, could SetGroupAddress; if !on, it's hard */ + + ether = (Ether*)arg; + ctlr = ether->ctlr; + + ilock(ctlr); + if(ether->prom || ether->nmaddr) + ctlr->regs->rmr |= RmrPmme; + else + ctlr->regs->rmr &= ~RmrPmme; + iunlock(ctlr); +} + +static void +txstart(Ether *ether) +{ + int len; + Ctlr *ctlr; + Block *b; + BD *dre; + + ctlr = ether->ctlr; + while(ctlr->ntq < ctlr->ntdre-1){ + b = qget(ether->oq); + if(b == 0) + break; + + dre = &ctlr->tdr[ctlr->tdrh]; + if(dre->status & BDReady) + panic("ether: txstart"); + + /* + * Give ownership of the descriptor to the chip, increment the + * software ring descriptor pointer and tell the chip to poll. + */ + len = BLEN(b); + if(ctlr->txb[ctlr->tdrh] != nil) + panic("etheremac: txstart"); + ctlr->txb[ctlr->tdrh] = b; + dre->addr = PADDR(b->rp); + dre->length = len; + dcflush(b->rp, len); + eieio(); + dre->status = (dre->status & BDWrap) | BDReady|BDInt|BDLast|TxFCS|TxPad; + eieio(); + ctlr->regs->tmr0 = Tmr0Gnp0; /* TO DO: several channels */ + eieio(); + ctlr->ntq++; + ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdre); + } +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + txstart(ether); + iunlock(ctlr); +} + +/* + * allocate receive buffer space on cache-line boundaries + */ +static Block* +clallocb(void) +{ + Block *b; + + b = iallocb(Bufsize+CACHELINESZ-1); + if(b == nil) + return b; + dcflush(b->base, BALLOC(b)); + b->wp = b->rp = (uchar*)(((ulong)b->base + CACHELINESZ - 1) & ~(CACHELINESZ-1)); + return b; +} + + +static void +rxring(Ureg*, void *arg) +{ + Ether *ether; + ulong status; + Ctlr *ctlr; + BD *dre; + Block *b, *rb; + + ether = arg; + ctlr = ether->ctlr; + ctlr->interrupts++; + + /* + * Receiver interrupt: run round the descriptor ring logging + * errors and passing valid receive data up to the higher levels + * until we encounter a descriptor still owned by the chip. + * We rely on the descriptor accesses being uncached. + */ + dre = &ctlr->rdr[ctlr->rdrx]; + while(((status = dre->status) & BDEmpty) == 0){ + if(status & RxError || (status & (BDFirst|BDLast)) != (BDFirst|BDLast)){ + if(status & (RxShort|RxLong)) + ether->buffs++; + if(status & (RxBad|RxAlign|RxRange|RxInRange)) + ether->frames++; + if(status & RxFCS) + ether->crcs++; + if(status & RxOverrun) + ether->overflows++; + iprint("eth rx: %lux\n", status); + }else if((status & RxPause) == 0){ + /* + * We have a packet. Read it in. + */ + b = clallocb(); + if(b != nil){ + rb = ctlr->rxb[ctlr->rdrx]; + rb->wp += dre->length; + ctlr->rxb[ctlr->rdrx] = b; + ctlr->rdr[ctlr->rdrx].addr = PADDR(b->wp); + etheriq(ether, rb, 1); + }else + ether->soverflows++; + } + + /* + * Finished with this descriptor, reinitialise it, + * give it back to the chip, then on to the next... + */ + dre->status = (status & BDWrap) | BDEmpty | BDInt; + eieio(); + + ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdre); + dre = &ctlr->rdr[ctlr->rdrx]; + } +} + +static void +txring(Ureg*, void *arg) +{ + Ether *ether; + ulong status; + Ctlr *ctlr; + BD *dre; + Block *b; + + ether = arg; + ctlr = ether->ctlr; + ctlr->interrupts++; + + /* + * Transmitter interrupt: handle anything queued for a free descriptor. + */ + lock(ctlr); + while(ctlr->ntq){ + dre = &ctlr->tdr[ctlr->tdri]; + status = dre->status; + if(status & BDReady) + break; + if(status & TxEDef) + ctlr->deferred++; + if(status & TxLateCol) + ctlr->latecoll++; + if(status & TxECol) + ctlr->retrylim++; + if(status & TxUnderrun) + ctlr->underrun++; + if(status & (TxManyCol|TxCollision)) + ctlr->retrycount++; + b = ctlr->txb[ctlr->tdri]; + if(b == nil) + panic("etheremac: bufp"); + ctlr->txb[ctlr->tdri] = nil; + freeb(b); + ctlr->ntq--; + ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdre); + } + txstart(ether); + unlock(ctlr); +} + +static void +interrupt(Ureg*, void *arg) +{ + Ether *ether; + ulong events; + Ctlr *ctlr; + + ether = arg; + ctlr = ether->ctlr; + + events = ctlr->regs->isr; + eieio(); + ctlr->regs->isr = events; + eieio(); + ctlr->interrupts++; +//iprint("eth: %8.8lux\n", events); + if(!ctlr->active || events == 0) + return; + + if(events & IsrOvr) + ctlr->overrun++; + if(events & (IsrTe0|IsrTe1)) + ether->oerrs++; + + rxring(nil, arg); + txring(nil, arg); + ctlr->interrupts -= 2; + + /* TO DO: restart tx/rx on error */ +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + char *p; + int len; + Ctlr *ctlr; + + if(n == 0) + return 0; + + ctlr = ether->ctlr; + + p = malloc(READSTR); + len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts); + len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", ctlr->carrierlost); + len += snprint(p+len, READSTR-len, "heartbeat: %lud\n", ctlr->heartbeat); + len += snprint(p+len, READSTR-len, "retrylimit: %lud\n", ctlr->retrylim); + len += snprint(p+len, READSTR-len, "retrycount: %lud\n", ctlr->retrycount); + len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", ctlr->latecoll); + len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", ctlr->overrun); + len += snprint(p+len, READSTR-len, "txunderruns: %lud\n", ctlr->underrun); + snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->deferred); + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static QLock miilock; /* the PHY are both on EMAC0's MII bus */ + +static int +miird(Mii *mii, int pa, int ra) +{ + Ctlr *ctlr; + Emac *em; + ulong r; + int i; + + if(up) + qlock(&miilock); + ctlr = mii->ctlr; + em = ctlr->miiregs; + MIIDBG("r: %x.%x:", pa, ra); + if((em->stacr & StaOc) == 0) + iprint("mii-not oc\n"); + em->stacr = StaRead | StaOpb66 | (pa<<5) | ra; + for(i=0; i<100 && (em->stacr & StaOc) == 0; i++) + microdelay(1); + r = em->stacr; + if(up) + qunlock(&miilock); + if((r & StaOc) == 0) + iprint("mii'-not oc\n"); + if(r & StaPhye) + return -1; + MIIDBG(" %8.8lux\n", r); + return r >> 16; +} + +static int +miiwr(Mii *mii, int pa, int ra, int v) +{ + Ctlr *ctlr; + Emac *em; + ulong r; + int i; + + if(up) + qlock(&miilock); + ctlr = mii->ctlr; + em = ctlr->miiregs; + if((em->stacr & StaOc) == 0) + iprint("miiw-not oc\n"); + em->stacr = (v<<16) | StaWrite | StaOpb66 | (pa<<5) | ra; + for(i=0; i<100 && (em->stacr & StaOc) == 0; i++) + microdelay(1); + r = em->stacr; + if(up) + qunlock(&miilock); + if((r & StaOc) == 0) + iprint("miiw'-not oc\n"); + if(r & StaPhye) + return -1; + MIIDBG("w: %x.%x: %8.8lux\n", pa, ra, r); + return 0; +} + +static int +emacmii(Ctlr *ctlr) +{ + MiiPhy *phy; + int i; + + MIIDBG("mii\n"); + if((ctlr->mii = malloc(sizeof(Mii))) == nil) + return -1; + ctlr->mii->ctlr = ctlr; + ctlr->mii->mir = miird; + ctlr->mii->miw = miiwr; + + if(mii(ctlr->mii, 1<<(ctlr->port+1)) == 0 || (phy = ctlr->mii->curphy) == nil){ + free(ctlr->mii); + ctlr->mii = nil; + return -1; + } + + iprint("oui %X phyno %d\n", phy->oui, phy->phyno); + if(miistatus(ctlr->mii) < 0){ + + miireset(ctlr->mii); + MIIDBG("miireset\n"); + if(miiane(ctlr->mii, ~0, 0, ~0) < 0){ + iprint("miiane failed\n"); + return -1; + } + MIIDBG("miistatus...\n"); + miistatus(ctlr->mii); + if(miird(ctlr->mii, phy->phyno, Bmsr) & BmsrLs){ + for(i=0;; i++){ + if(i > 600){ + iprint("emac%d: autonegotiation failed\n", ctlr->port); + break; + } + if(miird(ctlr->mii, phy->phyno, Bmsr) & BmsrAnc) + break; + delay(10); + } + if(miistatus(ctlr->mii) < 0) + iprint("miistatus failed\n"); + }else{ + iprint("emac%d: no link\n", ctlr->port); + phy->speed = 10; /* simple default */ + } + } + + iprint("emac%d mii: fd=%d speed=%d tfc=%d rfc=%d\n", ctlr->port, phy->fd, phy->speed, phy->tfc, phy->rfc); + + MIIDBG("mii done\n"); + + return 0; +} + +static void +emacsetup(Ctlr *ctlr, Ether *ether) +{ + int i; + Emac *em; + ulong mode; + MiiPhy *phy; + + /* apparently don't need to set any Alt1 in GPIO */ + + em = ctlr->regs; + + /* errata emac_8 */ + if(em->mr0 & Mr0Rxe){ /* probably never happens in our config */ + em->mr0 &= ~Mr0Rxe; + eieio(); + for(i=0; (em->mr0 & Mr0Rxi) == 0; i++){ + if(i > 100){ + iprint("ethermac: Rxe->Rxi timed out\n"); + break; /* we'll try soft reset anyway */ + } + microdelay(100); + } + } + + /* soft reset */ + em->mr0 = Mr0Srst; + eieio(); + for(i=0; em->mr0 & Mr0Srst; i++){ + if(i > 20){ + iprint("ethermac: reset (PHY clocks not running?)"); + i=0; + } + microdelay(100); + } +iprint("%d: rx=%8.8lux tx=%8.8lux\n", ctlr->port, PADDR(ctlr->rdr), PADDR(ctlr->tdr)); +//if(ctlr->port)return; + + malrxinit(ctlr->rx, ctlr, Bufsize/16); + maltxinit(ctlr->tx, ctlr); + malrxreset(ctlr->rx); + maltxreset(ctlr->tx); + + em->mr0 = 0; + mode = Mr1Rfs4096 | Mr1Tfs2048 | Mr1Tr0mp; + if(ctlr->mii != nil && (phy = ctlr->mii->curphy) != nil){ + if(phy->speed == 10){ + mode |= Mr1Mf10; + if(phy->fd) + mode |= Mr1Ist; + }else + mode |= Mr1Mf100 | Mr1Ist; + if(phy->fd) + mode |= Mr1Fde; + /* errata emac_9 suggests not using integrated flow control (it's broken); so don't negotiate it */ + if(0 && (phy->rfc || phy->tfc)) + mode |= Mr1App | Mr1Eifc; + ether->mbps = phy->speed; + ether->fullduplex = phy->fd; + }else{ + iprint("mii: didn't work: default 100FD\n"); + mode |= Mr1Mf100 | Mr1Ist | Mr1Fde; + ether->mbps = 100; + ether->fullduplex = 1; + } + + em->mr1 = mode; + em->tmr1 = (9<rmr = RmrSp | RmrSfcs | RmrIae | RmrBae; + em->iahr = (ether->ea[0]<<8) | ether->ea[1]; + em->ialr = (ether->ea[2]<<24) | (ether->ea[3]<<16) | (ether->ea[4]<<8) | ether->ea[5]; + em->vtpid = 0; + em->vtci = 0; + em->ptr = 1; /* pause timer [Reset, T] */ + for(i=0; i<4; i++){ + em->iaht[i] = 0; /* individual address hash table */ + em->gaht[i] = 0; /* group address hash table */ + } + em->ipgvr = (96/8)/3; /* minimise bit times between packets */ + em->trtr = ((256/64)-1)<rwmr = (32<isr = em->isr; /* clear all events */ + eieio(); + em->iser = IsrOvr | IsrBp | IsrSe | IsrSe0 | IsrTe0 | IsrSe1 | IsrTe1; /* enable various error interrupts */ + /* packet tx/rx interrupts come from MAL */ + eieio(); + + /* tx/rx enable is deferred until attach */ +} + +static int +reset(Ether* ether) +{ + uchar ea[Eaddrlen]; + Ctlr *ctlr; + int i; + + ioringreserve(Nrxchan, Nrdre, Ntxchan, Ntdre); + + /* + * Insist that the platform-specific code provide the Ethernet address + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + print("no ether address"); + return -1; + } + + ctlr = malloc(sizeof(*ctlr)); + ctlr->port = ether->port; + + switch(ether->port){ + case 0: + ctlr->regs = KADDR(PHYSEMAC0); + ctlr->miiregs = ctlr->regs; + ctlr->rx = malchannel(0, 0, rxring, ether); + ctlr->tx = malchannel(0, 1, txring, ether); + ether->irq = VectorEMAC0; + break; + case 1: + ctlr->regs = KADDR(PHYSEMAC1); + ctlr->miiregs = KADDR(PHYSEMAC0); /* p. 19-41: ``only the MDIO interface for EMAC0 is pinned out'' */ + ctlr->rx = malchannel(1, 0, rxring, ether); + ctlr->tx = malchannel(2, 1, txring, ether); + ether->irq = VectorEMAC1; + break; + default: + print("%s ether: no port %lud\n", ether->type, ether->port); + free(ctlr); + return -1; + } + + if(emacmii(ctlr) < 0){ + free(ctlr); + return -1; + } + + ether->ctlr = ctlr; + + if(ioringinit(ctlr, Nrdre, Ntdre) < 0) /* TO DO: there are two transmit rings*/ + panic("etheremac initring"); + + for(i = 0; i < ctlr->nrdre; i++){ + ctlr->rxb[i] = clallocb(); + ctlr->rdr[i].addr = PADDR(ctlr->rxb[i]->wp); + } + + emacsetup(ctlr, ether); + + ether->attach = attach; + ether->closed = closed; + ether->transmit = transmit; + ether->interrupt = interrupt; /* oddly, it's only error interrupts; see malchannel call above for tx/rx */ + ether->ifstat = ifstat; + + ether->arg = ether; + ether->promiscuous = promiscuous; + ether->multicast = multicast; + + return 0; +} + +void +etheremaclink(void) +{ + addethercard("EMAC", reset); +} + +static void +dumpemac(Emac *r) +{ + iprint("mr0=%8.8lux\n", r->mr0); /* mode register 0 [see 19-48] */ + iprint("mr1=%8.8lux\n", r->mr1); /* mode register 1 [Reset] */ + iprint("tmr0=%8.8lux\n", r->tmr0); /* transmit mode register 0 [see 19-28] */ + iprint("tmr1=%8.8lux\n", r->tmr1); /* transmit mode register 1 [see 19-28] */ + iprint("rmr=%8.8lux\n", r->rmr); /* receive mode register [Reset] */ + iprint("isr=%8.8lux\n", r->isr); /* interrupt status register [Always] */ + iprint("iser=%8.8lux\n", r->iser); /* interrupt status enable register [Reset] */ + iprint("iahr=%8.8lux\n", r->iahr); /* individual address high [Reset, R, T]*/ + iprint("ialr=%8.8lux\n", r->ialr); /* individual address low [Reset, R, T] */ + iprint("vtpid=%8.8lux\n", r->vtpid); /* VLAN Tag Protocol Identifier [Reset, R, T] */ + iprint("vtci=%8.8lux\n", r->vtci); /* VLAN Tag Control Information [Reset, R, T] */ + iprint("ptr=%8.8lux\n", r->ptr); /* pause timer [Reset, T] */ + iprint("lsah=%8.8lux\n", r->lsah); /* last source address high */ + iprint("lsal=%8.8lux\n", r->lsal); /* last source address low */ + iprint("ipgvr=%8.8lux\n", r->ipgvr); /* inter-packet gap value [Reset, T] */ + iprint("stacr=%8.8lux\n", r->stacr); /* STA control register [see 19-41] */ + iprint("trtr=%8.8lux\n", r->trtr); /* transmit request threshold register [see 19-42] */ + iprint("rwmr=%8.8lux\n", r->rwmr); /* receive low/high water mark [Reset] */ + iprint("octx=%8.8lux\n", r->octx); /* bytes transmitted */ + iprint("ocrx=%8.8lux\n", r->ocrx); /* bytes received */ +} diff --git a/os/cerf405/etherif.h b/os/cerf405/etherif.h new file mode 100644 index 00000000..26f0a764 --- /dev/null +++ b/os/cerf405/etherif.h @@ -0,0 +1,37 @@ +enum { + MaxEther = 4, + Ntypes = 8, +}; + +typedef struct Ether Ether; +struct Ether { +RWlock; /* TO DO */ + ISAConf; /* hardware info */ + int ctlrno; + int tbdf; /* type+busno+devno+funcno */ + int minmtu; + int maxmtu; + uchar ea[Eaddrlen]; + int encry; + + void (*attach)(Ether*); /* filled in by reset routine */ + void (*closed)(Ether*); + void (*detach)(Ether*); + void (*transmit)(Ether*); + void (*interrupt)(Ureg*, void*); + long (*ifstat)(Ether*, void*, long, ulong); + long (*ctl)(Ether*, void*, long); /* custom ctl messages */ + void (*power)(Ether*, int); /* power on/off */ + void (*shutdown)(Ether*); /* shutdown hardware before reboot */ + void *ctlr; + int pcmslot; /* PCMCIA */ + int fullduplex; /* non-zero if full duplex */ + + Queue* oq; + + Netif; +}; + +extern Block* etheriq(Ether*, Block*, int); +extern void addethercard(char*, int(*)(Ether*)); +extern int archether(int, Ether*); diff --git a/os/cerf405/fns.h b/os/cerf405/fns.h new file mode 100644 index 00000000..4c6a3fa1 --- /dev/null +++ b/os/cerf405/fns.h @@ -0,0 +1,147 @@ +#include "../port/portfns.h" + +void addpower(Power*); +void archbacklight(int); +void archconfinit(void); +int archconfval(char**, char**, int); +void archdisableuart(int); +void archdisableusb(void); +void archdisablevideo(void); +void archenableuart(int, int); +void archenableusb(int, int); +void archenablevideo(void); +void archkbdinit(void); +void archresetvideo(void); +void archinit(void); +int archoptionsw(void); +void archreboot(void); +ulong archuartclock(int, int); +void archuartdma(int, int); +void clockcheck(void); +void clockinit(void); +void clockintr(Ureg*); +void clrfptrap(void); +#define coherence() /* nothing needed for uniprocessor */ +void compiledcr(void); +void cpuidprint(void); +void* dcflush(void*, ulong); +void dcinval(void*, ulong); +void delay(int); +void dtlbmiss(void); +void dumplongs(char*, ulong*, int); +void dumpregs(Ureg*); +void eieio(void); +void firmware(int); +void fpinit(void); +int fpipower(Ureg*); +void fpoff(void); +void fprestore(FPU*); +void fpsave(FPU*); +ulong fpstatus(void); +char* getconf(char*); +ulong getccr0(void); +ulong getdar(void); +ulong getdcr(int); +ulong getdear(void); +ulong getdepn(void); +ulong getdsisr(void); +ulong getesr(void); +ulong getimmr(void); +ulong getmsr(void); +ulong getpit(void); +ulong getpvr(void); +ulong gettbl(void); +ulong gettbu(void); +ulong gettsr(void); +void gotopc(ulong); +void icflush(void*, ulong); +void idle(void); +void idlehands(void); +int inb(int); +ulong inl(int); +int ins(int); +void insb(int, void*, int); +void insl(int, void*, int); +void inss(int, void*, int); +void intr(Ureg*); +void intrenable(int, void (*)(Ureg*, void*), void*, int, char*); +void intrdisable(int, void (*)(Ureg*, void*), void*, int, char*); +int intrstats(char*, int); +void intrvec(void); +void intrcvec(void); +void ioinit(void); +void ioreset(void); +int isaconfig(char*, int, ISAConf*); +int isvalid_va(void*); +void itlbmiss(void); +void kbdinit(void); +void kbdreset(void); +void* kmapphys(void*, ulong, ulong, ulong, ulong); +void lcdpanel(int); +void links(void); +void mapfree(RMap*, ulong, int); +void mapinit(RMap*, Map*, int); +void mathinit(void); +void mmuinit(void); +void* mmucacheinhib(void*, ulong); +ulong mmumapsize(ulong); +void pcimapinit(void); +int pciscan(int, Pcidev **); +ulong pcibarsize(Pcidev *, int); +int pcicfgr8(Pcidev*, int); +int pcicfgr16(Pcidev*, int); +int pcicfgr32(Pcidev*, int); +void pcicfgw8(Pcidev*, int, int); +void pcicfgw16(Pcidev*, int, int); +void pcicfgw32(Pcidev*, int, int); +void pciclrbme(Pcidev*); +void pcihinv(Pcidev*); +uchar pciipin(Pcidev *, uchar); +Pcidev* pcimatch(Pcidev*, int, int); +Pcidev* pcimatchtbdf(int); +void pcireset(void); +void pcisetbme(Pcidev*); +void procsave(Proc*); +void procsetup(Proc*); +void putdcr(int, ulong); +void putesr(ulong); +void putevpr(ulong); +void putmsr(ulong); +void putpit(ulong); +void puttcr(ulong); +void puttsr(ulong); +void puttwb(ulong); +ulong rmapalloc(RMap*, ulong, int, int); +long rtctime(void); +void screeninit(void); +int screenprint(char*, ...); /* debugging */ +void (*screenputs)(char*, int); +int segflush(void*, ulong); +void toggleled(int); +void setpanic(void); +ulong _tas(ulong*); +ulong tlbrehi(int); +ulong tlbrelo(int); +int tlbsxcc(void*); +void tlbwehi(int, ulong); +void tlbwelo(int, ulong); +void trapinit(void); +void trapvec(void); +void trapcvec(void); +void uartinstall(void); +void uartspecial(int, int, Queue**, Queue**, int (*)(Queue*, int)); +void uartwait(void); /* debugging */ +void wbflush(void); + +#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) +ulong getcallerpc(void*); + +#define isphys(a) (((ulong)(a)&KSEGM)!=KSEG0 && ((ulong)(a)&KSEGM)!=KSEG1) +#define KADDR(a) ((void*)((ulong)(a)|KZERO)) +#define PADDR(a) (isphys(a)?(ulong)(a):((ulong)(a)&~KSEGM)) + +/* IBM bit field order */ +#define IBIT(b) (((ulong)(1<<31))>>(b)) +#define SIBIT(n) ((ushort)1<<(15-(n))) +#define CIBIT(n) ((uchar)1<<(7-(n))) + diff --git a/os/cerf405/fpi.h b/os/cerf405/fpi.h new file mode 100644 index 00000000..dfb9b1df --- /dev/null +++ b/os/cerf405/fpi.h @@ -0,0 +1,61 @@ +typedef long Word; +typedef unsigned long Single; +typedef struct { + unsigned long h; + unsigned long l; +} Double; + +enum { + FractBits = 28, + CarryBit = 0x10000000, + HiddenBit = 0x08000000, + MsBit = HiddenBit, + NGuardBits = 3, + GuardMask = 0x07, + LsBit = (1<e >= ExpInfinity) +#define IsInfinity(n) (IsWeird(n) && (n)->h == HiddenBit && (n)->l == 0) +#define SetInfinity(n) ((n)->e = ExpInfinity, (n)->h = HiddenBit, (n)->l = 0) +#define IsNaN(n) (IsWeird(n) && (((n)->h & ~HiddenBit) || (n)->l)) +#define SetQNaN(n) ((n)->s = 0, (n)->e = ExpInfinity, \ + (n)->h = HiddenBit|(LsBit<<1), (n)->l = 0) +#define IsZero(n) ((n)->e == 1 && (n)->h == 0 && (n)->l == 0) +#define SetZero(n) ((n)->e = 1, (n)->h = 0, (n)->l = 0) + +/* + * fpi.c + */ +extern void fpiround(Internal *); +extern void fpiadd(Internal *, Internal *, Internal *); +extern void fpisub(Internal *, Internal *, Internal *); +extern void fpimul(Internal *, Internal *, Internal *); +extern void fpidiv(Internal *, Internal *, Internal *); +extern int fpicmp(Internal *, Internal *); +extern void fpinormalise(Internal*); + +/* + * fpimem.c + */ +extern void fpis2i(Internal *, void *); +extern void fpid2i(Internal *, void *); +extern void fpiw2i(Internal *, void *); +extern void fpii2s(void *, Internal *); +extern void fpii2d(void *, Internal *); +extern void fpii2w(Word *, Internal *); diff --git a/os/cerf405/fpipower.c b/os/cerf405/fpipower.c new file mode 100644 index 00000000..ec4363b2 --- /dev/null +++ b/os/cerf405/fpipower.c @@ -0,0 +1,970 @@ +/* + * this doesn't attempt to implement Power architecture floating-point properties + * that aren't visible in the Inferno environment. + * all arithmetic is done in double precision. + * the FP trap status isn't updated. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "ureg.h" + +#include "fpi.h" + +#define REG(x) (*(long*)(((char*)em->ur)+roff[(x)])) +#define FR(x) (*(Internal*)em->fr[(x)&0x1F]) +#define REGSP 1 /* stack pointer */ + +/* BUG: check fetch (not worthwhile in Inferno) */ +#define getulong(a) (*(ulong*)(a)) + +enum { + CRLT = 1<<31, + CRGT = 1<<30, + CREQ = 1<<29, + CRSO = 1<<28, + CRFU = CRSO, + + CRFX = 1<<27, + CRFEX = 1<<26, + CRVX = 1<<25, + CROX = 1<<24, +}; + +#define getCR(x,w) (((w)>>(28-(x*4)))&0xF) +#define mkCR(x,v) (((v)&0xF)<<(28-(x*4))) + +#define simm(xx, ii) xx = (short)(ii&0xFFFF); +#define getairr(i) rd = (i>>21)&0x1f; ra = (i>>16)&0x1f; simm(imm,i) +#define getarrr(i) rd = (i>>21)&0x1f; ra = (i>>16)&0x1f; rb = (i>>11)&0x1f; +#define getop(i) ((i>>26)&0x3F) +#define getxo(i) ((i>>1)&0x3FF) + +#define FPS_FX (1<<31) /* exception summary (sticky) */ +#define FPS_EX (1<<30) /* enabled exception summary */ +#define FPS_VX (1<<29) /* invalid operation exception summary */ +#define FPS_OX (1<<28) /* overflow exception OX (sticky) */ +#define FPS_UX (1<<27) /* underflow exception UX (sticky) */ +#define FPS_ZX (1<<26) /* zero divide exception ZX (sticky) */ +#define FPS_XX (1<<25) /* inexact exception XX (sticky) */ +#define FPS_VXSNAN (1<<24) /* invalid operation exception for SNaN (sticky) */ +#define FPS_VXISI (1<<23) /* invalid operation exception for ∞-∞ (sticky) */ +#define FPS_VXIDI (1<<22) /* invalid operation exception for ∞/∞ (sticky) */ +#define FPS_VXZDZ (1<<21) /* invalid operation exception for 0/0 (sticky) */ +#define FPS_VXIMZ (1<<20) /* invalid operation exception for ∞*0 (sticky) */ +#define FPS_VXVC (1<<19) /* invalid operation exception for invalid compare (sticky) */ +#define FPS_FR (1<<18) /* fraction rounded */ +#define FPS_FI (1<<17) /* fraction inexact */ +#define FPS_FPRF (1<<16) /* floating point result class */ +#define FPS_FPCC (0xF<<12) /* <, >, =, unordered */ +#define FPS_VXCVI (1<<8) /* enable exception for invalid integer convert (sticky) */ +#define FPS_VE (1<<7) /* invalid operation exception enable */ +#define FPS_OE (1<<6) /* enable overflow exceptions */ +#define FPS_UE (1<<5) /* enable underflow */ +#define FPS_ZE (1<<4) /* enable zero divide */ +#define FPS_XE (1<<3) /* enable inexact exceptions */ +#define FPS_RN (3<<0) /* rounding mode */ + +typedef struct Emreg Emreg; + +struct Emreg { + Ureg* ur; + ulong (*fr)[3]; + FPenv* ufp; + ulong ir; + char* name; +}; + +int fpemudebug = 0; + +#undef OFR +#define OFR(X) ((ulong)&((Ureg*)0)->X) + +static int roff[] = { + OFR(r0), OFR(r1), OFR(r2), OFR(r3), + OFR(r4), OFR(r5), OFR(r6), OFR(r7), + OFR(r8), OFR(r9), OFR(r10), OFR(r11), + OFR(r12), OFR(r13), OFR(r14), OFR(r15), + OFR(r16), OFR(r17), OFR(r18), OFR(r19), + OFR(r20), OFR(r21), OFR(r22), OFR(r23), + OFR(r24), OFR(r25), OFR(r26), OFR(r27), + OFR(r28), OFR(r29), OFR(r30), OFR(r31), +}; + +/* + * initial FP register values assumed by qc's code + */ +static Internal fpreginit[] = { + /* s, e, l, h */ + {0, 0x400, 0x00000000, 0x08000000}, /* F31=2.0 */ + {0, 0x3FF, 0x00000000, 0x08000000}, /* F30=1.0 */ + {0, 0x3FE, 0x00000000, 0x08000000}, /* F29=0.5 */ + {0, 0x1, 0x00000000, 0x00000000}, /* F28=0.0 */ + {0, 0x433, 0x00000000, 0x08000040}, /* F27=FREGCVI */ +}; + +static void +fadd(Emreg *em, Internal *d, int ra, int rb) +{ + Internal a, b; + + a = FR(ra); + b = FR(rb); + (a.s == b.s? fpiadd: fpisub)(&b, &a, d); +} + +static void +fsub(Emreg *em, Internal *d, int ra, int rb) +{ + Internal a, b; + + a = FR(ra); + b = FR(rb); + b.s ^= 1; + (b.s == a.s? fpiadd: fpisub)(&b, &a, d); +} + +static void +fmul(Emreg *em, Internal *d, int ra, int rb) +{ + Internal a, b; + + a = FR(ra); + b = FR(rb); + fpimul(&b, &a, d); +} + +static void +fdiv(Emreg *em, Internal *d, int ra, int rb) +{ + Internal a, b; + + a = FR(ra); + b = FR(rb); + fpidiv(&b, &a, d); +} + +static void +fmsub(Emreg *em, Internal *d, int ra, int rc, int rb) +{ + Internal a, c, b, t; + + a = FR(ra); + c = FR(rc); + b = FR(rb); + fpimul(&a, &c, &t); + b.s ^= 1; + (b.s == t.s? fpiadd: fpisub)(&b, &t, d); +} + +static void +fmadd(Emreg *em, Internal *d, int ra, int rc, int rb) +{ + Internal a, c, b, t; + + a = FR(ra); + c = FR(rc); + b = FR(rb); + fpimul(&a, &c, &t); + (t.s == b.s? fpiadd: fpisub)(&b, &t, d); +} + +static ulong setfpscr(Emreg*); +static void setfpcc(Emreg*, int); + +static void +unimp(Emreg *em, ulong op) +{ + char buf[60]; + + snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", em->ur->pc, op); + if(fpemudebug) + print("FPE: %s\n", buf); + error(buf); + /* no return */ +} + +/* + * floating load/store + */ + +static void +fpeairr(Emreg *em, ulong ir, void **eap, int *rdp) +{ + ulong ea; + long imm; + int ra, rd, upd; + + getairr(ir); + ea = imm; + upd = (ir&(1L<<26))!=0; + if(ra) { + ea += REG(ra); + if(upd){ + if(ra == REGSP) + panic("fpemu: r1 update"); /* can't do it because we're running on the same stack */ + REG(ra) = ea; + } + } else { + if(upd) + unimp(em, ir); + } + *rdp = rd; + *eap = (void*)ea; + if(fpemudebug) + print("%8.8lux %s\tf%d,%ld(r%d) ea=%lux upd=%d\n", em->ur->pc, em->name, rd, imm, ra, ea, upd); +} + +static void +fpearrr(Emreg *em, ulong ir, int upd, void **eap, int *rdp) +{ + ulong ea; + int ra, rb, rd; + + getarrr(ir); + ea = REG(rb); + if(ra){ + ea += REG(ra); + if(upd){ + if(ra == REGSP) + panic("fpemu: r1 update"); + REG(ra) = ea; + } + if(fpemudebug) + print("%8.8lux %s\tf%d,(r%d+r%d) ea=%lux upd=%d\n", em->ur->pc, em->name, rd, ra, rb, ea, upd); + } else { + if(upd) + unimp(em, ir); + if(fpemudebug) + print("%8.8lux %s\tf%d,(r%d) ea=%lux\n", em->ur->pc, em->name, rd, rb, ea); + } + *eap = (void*)ea; + *rdp = rd; +} + +static void +lfs(Emreg *em, ulong ir) +{ + void *ea; + int rd; + + em->name = "lfs"; + fpeairr(em, ir, &ea, &rd); + fpis2i(&FR(rd), (void*)ea); +} + +static void +lfsx(Emreg *em, ulong ir) +{ + void *ea; + int rd; + + em->name = "lfsx"; + fpearrr(em, ir, ((ir>>1)&0x3FF)==567, &ea, &rd); + fpis2i(&FR(rd), (void*)ea); +} + +static void +lfd(Emreg *em, ulong ir) +{ + void *ea; + int rd; + + em->name = "lfd"; + fpeairr(em, ir, &ea, &rd); + fpid2i(&FR(rd), (void*)ea); +} + +static void +lfdx(Emreg *em, ulong ir) +{ + void *ea; + int rd; + + em->name = "lfdx"; + fpearrr(em, ir, ((ir>>1)&0x3FF)==631, &ea, &rd); + fpid2i(&FR(rd), (void*)ea); +} + +static void +stfs(Emreg *em, ulong ir) +{ + void *ea; + int rd; + Internal tmp; + + em->name = "stfs"; + fpeairr(em, ir, &ea, &rd); + tmp = FR(rd); + fpii2s(ea, &tmp); +} + +static void +stfsx(Emreg *em, ulong ir) +{ + void *ea; + int rd; + Internal tmp; + + em->name = "stfsx"; + fpearrr(em, ir, getxo(ir)==695, &ea, &rd); + tmp = FR(rd); + fpii2s(ea, &tmp); +} + +static void +stfd(Emreg *em, ulong ir) +{ + void *ea; + int rd; + Internal tmp; + + em->name = "stfd"; + fpeairr(em, ir, &ea, &rd); + tmp = FR(rd); + fpii2d(ea, &tmp); +} + +static void +stfdx(Emreg *em, ulong ir) +{ + void *ea; + int rd; + Internal tmp; + + em->name = "stfdx"; + fpearrr(em, ir, ((ir>>1)&0x3FF)==759, &ea, &rd); + tmp = FR(rd); + fpii2d(ea, &tmp); +} + +static void +mcrfs(Emreg *em, ulong ir) +{ + int rd, ra, rb; + static ulong fpscr0[] ={ + FPS_FX|FPS_OX, + FPS_UX|FPS_ZX|FPS_XX|FPS_VXSNAN, + FPS_VXISI|FPS_VXIDI|FPS_VXZDZ|FPS_VXIMZ, + FPS_VXVC, + 0, + FPS_VXCVI, + }; + + getarrr(ir); + if(rb || ra&3 || rd&3) + unimp(em, ir); + ra >>= 2; + rd >>= 2; + em->ur->cr = (em->ur->cr & ~mkCR(rd, 0xF)) | mkCR(rd, getCR(ra, em->ufp->fpscr)); + em->ufp->fpscr &= ~fpscr0[ra]; + if(fpemudebug) + print("%8.8lux mcrfs\tcrf%d,crf%d\n", em->ur->pc, rd, ra); +} + +static void +mffs(Emreg *em, ulong ir) +{ + int rd, ra, rb; + Double dw; + + getarrr(ir); + if(ra || rb) + unimp(em, ir); + dw.h = 0; + dw.l = ((uvlong)0xFFF8000L<<16)|em->ufp->fpscr; + fpid2i(&FR(rd), &dw); + /* it's anyone's guess how CR1 should be set when ir&1 */ + em->ur->cr &= ~mkCR(1, 0xE); /* leave SO, reset others */ + if(fpemudebug) + print("%8.8lux mffs%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd); +} + +static void +mtfsb1(Emreg *em, ulong ir) +{ + int rd, ra, rb; + + getarrr(ir); + if(ra || rb) + unimp(em, ir); + em->ufp->fpscr |= (1L << (31-rd)); + /* BUG: should set summary bits */ + if(ir & 1) + em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ + if(fpemudebug) + print("%8.8lux mtfsb1%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd); +} + +static void +mtfsb0(Emreg *em, ulong ir) +{ + int rd, ra, rb; + + getarrr(ir); + if(ra || rb) + unimp(em, ir); + em->ufp->fpscr &= ~(1L << (31-rd)); + if(ir & 1) + em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ + if(fpemudebug) + print("%8.8lux mtfsb0%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd); +} + +static void +mtfsf(Emreg *em, ulong ir) +{ + int fm, rb, i; + ulong v; + Internal b; + Double db; + + if(ir & ((1L << 25)|(1L << 16))) + unimp(em, ir); + rb = (ir >> 11) & 0x1F; + fm = (ir >> 17) & 0xFF; + b = FR(rb); + fpii2d(&db, &b); /* reconstruct hi/lo format to recover low word */ + v = db.l; + for(i=0; i<8; i++) + if(fm & (1 << (7-i))) + em->ufp->fpscr = (em->ufp->fpscr & ~mkCR(i, 0xF)) | mkCR(i, getCR(i, v)); + /* BUG: should set FEX and VX `according to the usual rule' */ + if(ir & 1) + em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ + if(fpemudebug) + print("%8.8lux mtfsf%s\t#%.2x,fr%d\n", em->ur->pc, ir&1?".":"", fm, rb); +} + +static void +mtfsfi(Emreg *em, ulong ir) +{ + int imm, rd; + + if(ir & ((0x7F << 16)|(1L << 11))) + unimp(em, ir); + rd = (ir >> 23) & 0xF; + imm = (ir >> 12) & 0xF; + em->ufp->fpscr = (em->ufp->fpscr & ~mkCR(rd, 0xF)) | mkCR(rd, imm); + /* BUG: should set FEX and VX `according to the usual rule' */ + if(ir & 1) + em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ + if(fpemudebug) + print("%8.8lux mtfsfi%s\tcrf%d,#%x\n", em->ur->pc, ir&1?".":"", rd, imm); +} + +static void +fcmp(Emreg *em, ulong ir) +{ + int fc, rd, ra, rb, sig, i; + + getarrr(ir); + if(rd & 3) + unimp(em, ir); + rd >>= 2; + sig = 0; + switch(getxo(ir)) { + default: + unimp(em, ir); + case 32: + if(fpemudebug) + print("%8.8lux fcmpo\tcr%d,f%d,f%d\n", em->ur->pc, rd, ra, rb); + sig = 1; + break; + case 0: + if(fpemudebug) + print("%8.8lux fcmpu\tcr%d,f%d,f%d\n", em->ur->pc, rd, ra, rb); + break; + } + if(IsWeird(&FR(ra)) || IsWeird(&FR(rb))) { + if(sig){ + ; /* BUG: should trap if not masked ... */ + } + fc = CRFU; + } else { + i = fpicmp(&FR(ra), &FR(rb)); + if(i > 0) + fc = CRGT; + else if(i == 0) + fc = CREQ; + else + fc = CRLT; + } + fc >>= 28; + em->ur->cr = (em->ur->cr & ~mkCR(rd,~0)) | mkCR(rd, fc); + em->ufp->fpscr = (em->ufp->fpscr & ~0xF800) | (fc<<11); + /* BUG: update FX, VXSNAN, VXVC */ +} + +static void +fariths(Emreg *em, ulong ir) +{ + int rd, ra, rb, rc, fmt; + char *cc, *n; + ulong fpscr; + Internal *d; + + fmt = 0; + rc = (ir>>6)&0x1F; + getarrr(ir); + d = &FR(rd); + switch(getxo(ir)&0x1F) { /* partial XO decode */ + case 22: /* fsqrts */ + case 24: /* fres */ + default: + unimp(em, ir); + return; + case 18: + if(IsZero(&FR(rb))) { + em->ufp->fpscr |= FPS_ZX | FPS_FX; + error("sys: fp: zero divide"); + } + fdiv(em, d, ra, rb); + n = "fdivs"; + break; + case 20: + fsub(em, d, ra, rb); + n = "fsubs"; + break; + case 21: + fadd(em, d, ra, rb); + n = "fadds"; + break; + case 25: + fmul(em, d, ra, rc); + rb = rc; + n = "fmuls"; + break; + case 28: + fmsub(em, d, ra, rc, rb); + fmt = 2; + n = "fmsubs"; + break; + case 29: + fmadd(em, d, ra, rc, rb); + fmt = 2; + n = "fmadds"; + break; + case 30: + fmsub(em, d, ra, rc, rb); + d->s ^= 1; + fmt = 2; + n = "fnmsubs"; + break; + case 31: + fmadd(em, d, ra, rc, rb); + d->s ^= 1; + fmt = 2; + n = "fnmadds"; + break; + } + if(fmt==1 && ra) + unimp(em, ir); + fpscr = setfpscr(em); + setfpcc(em, rd); + cc = ""; + if(ir & 1) { + cc = "."; + em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28)); + } + if(fpemudebug) { + switch(fmt) { + case 0: + print("%8.8lux %s%s\tfr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rb); + break; + case 1: + print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb); + break; + case 2: + print("%8.8lux %s%s\tfr%d,fr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rc, rb); + break; + } + } +} + +static void +farith(Emreg *em, ulong ir) +{ + Word w; + Double dv; + int rd, ra, rb, rc, fmt; + char *cc, *n; + ulong fpscr; + int nocc; + Internal *d; + + fmt = 0; + nocc = 0; + rc = (ir>>6)&0x1F; + getarrr(ir); + d = &FR(rd); + switch(getxo(ir)&0x1F) { /* partial XO decode */ + case 22: /* frsqrt */ + case 23: /* fsel */ + case 26: /* fsqrte */ + default: + unimp(em, ir); + return; + case 12: /* frsp */ + *d = FR(rb); /* BUG: doesn't round to single precision */ + fmt = 1; + n = "frsp"; + break; + case 14: /* fctiw */ /* BUG: ignores rounding mode */ + case 15: /* fctiwz */ + fpii2w(&w, &FR(rb)); + dv.h = 0; + dv.l = w; + fpid2i(d, &dv); + fmt = 1; + nocc = 1; + n = "fctiw"; + break; + case 18: + if(IsZero(&FR(rb))) { + em->ufp->fpscr |= FPS_ZX | FPS_FX; + error("sys: fp: zero divide"); + } + fdiv(em, d, ra, rb); + n = "fdiv"; + break; + case 20: + fsub(em, d, ra, rb); + n = "fsub"; + break; + case 21: + fadd(em, d, ra, rb); + n = "fadd"; + break; + case 25: + fmul(em, d, ra, rc); + rb = rc; + n = "fmul"; + break; + case 28: + fmsub(em, d, ra, rc, rb); + fmt = 2; + n = "fmsub"; + break; + case 29: + fmadd(em, d, ra, rc, rb); + fmt = 2; + n = "fmadd"; + break; + case 30: + fmsub(em, d, ra, rc, rb); + d->s ^= 1; + fmt = 2; + n = "fnmsub"; + break; + case 31: + fmadd(em, d, ra, rc, rb); + d->s ^= 1; + fmt = 2; + n = "fnmadd"; + break; + } + if(fmt==1 && ra) + unimp(em, ir); + fpscr = setfpscr(em); + if(nocc == 0) + setfpcc(em, rd); + cc = ""; + if(ir & 1) { + cc = "."; + em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28)); + } + if(fpemudebug) { + switch(fmt) { + case 0: + print("%8.8lux %s%s\tfr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rb); + break; + case 1: + print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb); + break; + case 2: + print("%8.8lux %s%s\tfr%d,fr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rc, rb); + break; + } + } +} + +static void +farith2(Emreg *em, ulong ir) +{ + int rd, ra, rb; + char *cc, *n; + ulong fpscr; + Internal *d, *b; + + getarrr(ir); + if(ra) + unimp(em, ir); + d = &FR(rd); + b = &FR(rb); + switch(getxo(ir)) { /* full XO decode */ + default: + unimp(em, ir); + case 40: + *d = *b; + d->s ^= 1; + n = "fneg"; + break; + case 72: + *d = *b; + n = "fmr"; + break; + case 136: + *d = *b; + d->s = 1; + n = "fnabs"; + break; + case 264: + *d = *b; + d->s = 0; + n = "fabs"; + break; + } + fpscr = setfpscr(em); + setfpcc(em, rd); + cc = ""; + if(ir & 1) { + cc = "."; + em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28)); + } + if(fpemudebug) + print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb); +} + +static ulong +setfpscr(Emreg *em) +{ + ulong fps, fpscr; + + fps = 0; /* BUG: getfsr() */ + fpscr = em->ufp->fpscr; + if(fps & FPAOVFL) + fpscr |= FPS_OX; + if(fps & FPAINEX) + fpscr |= FPS_XX; + if(fps & FPAUNFL) + fpscr |= FPS_UX; + if(fps & FPAZDIV) + fpscr |= FPS_ZX; + if(fpscr != em->ufp->fpscr) { + fpscr |= FPS_FX; + em->ufp->fpscr = fpscr; + } + return fpscr; +} + +static void +setfpcc(Emreg *em, int r) +{ + int c; + Internal *d; + + d = &FR(r); + c = 0; + if(IsZero(d)) + c |= 2; + else if(d->s == 1) + c |= 4; + else + c |= 8; + if(IsNaN(d)) + c |= 1; + em->ufp->fpscr = (em->ufp->fpscr & ~0xF800) | (0<<15) | (c<<11); /* unsure about class bit */ +} + +static uchar op63flag[32] = { +[12] 1, [14] 1, [15] 1, [18] 1, [20] 1, [21] 1, [22] 1, +[23] 1, [25] 1, [26] 1, [28] 1, [29] 1, [30] 1, [31] 1, +}; + +/* + * returns the number of FP instructions emulated + */ +int +fpipower(Ureg *ur) +{ + ulong op; + int xo; + Emreg emreg, *em; + FPenv *ufp; + int n; + + ufp = &up->env->fpu; /* because all the state is in Osenv, it need not be saved/restored */ + em = &emreg; + em->ur = ur; + em->fr = ufp->emreg; + em->ufp = ufp; + em->name = nil; + if(em->ufp->fpistate != FPACTIVE) { + em->ufp->fpistate = FPACTIVE; + em->ufp->fpscr = 0; /* TO DO */ + for(n = 0; n < nelem(fpreginit); n++) + FR(31-n) = fpreginit[n]; + } + for(n=0;;n++){ + op = getulong(ur->pc); + em->ir = op; + if(fpemudebug > 1) + print("%8.8lux %8.8lux: ", ur->pc, op); + switch(op>>26){ + default: + return n; + case 48: /* lfs */ + case 49: /* lfsu */ + lfs(em, op); + break; + case 50: /* lfd */ + case 51: /* lfdu */ + lfd(em, op); + break; + case 52: /* stfs */ + case 53: /* stfsu */ + stfs(em, op); + break; + case 54: /* stfd */ + case 55: /* stfdu */ + stfd(em, op); + break; + case 31: /* indexed load/store */ + xo = getxo(op); + if((xo & 0x300) != 0x200) + return n; + switch(xo){ + default: + return n; + case 535: /* lfsx */ + case 567: /* lfsux */ + lfsx(em, op); + break; + case 599: /* lfdx */ + case 631: /* lfdux */ + lfdx(em, op); + break; + case 663: /* stfsx */ + case 695: /* stfsux */ + stfsx(em, op); + break; + case 727: /* stfdx */ + case 759: /* stfdux */ + stfdx(em, op); + break; + } + break; + case 63: /* double precision */ + xo = getxo(op); + if(op63flag[xo & 0x1F]){ + farith(em, op); + break; + } + switch(xo){ + default: + return n; + case 0: /* fcmpu */ + case 32: /* fcmpo */ + fcmp(em, op); + break; + case 40: /* fneg */ + case 72: /* fmr */ + case 136: /* fnabs */ + case 264: /* fabs */ + farith2(em, op); + break; + case 38: + mtfsb1(em, op); + break; + case 64: + mcrfs(em, op); + break; + case 70: + mtfsb0(em, op); + break; + case 134: + mtfsfi(em, op); + break; + case 583: + mffs(em, op); + break; + case 711: + mtfsf(em, op); + break; + } + break; + case 59: /* single precision */ + fariths(em, op); + break; + } + ur->pc += 4; + if(anyhigher()) + sched(); + } + return n; +} + +/* +50: lfd frD,d(rA) +51: lfdu frD,d(rA) +31,631: lfdux frD,rA,rB +31,599: lfdx frD,rA,rB +48: lfs frD,d(rA) +49: lfsu frD,d(rA) +31,567: lfsux frD,rA,rB +31,535: lfsx frD,rA,rB + +54: stfd frS,d(rA) +55: stfdu frS,d(rA) +31,759: stfdux frS,rA,rB +31,727: stfdx frS,rA,rB +52: stfs frS,d(rA) +53: stfsu frS,d(rA) +31,695: stfsux frS,rA,rB +31,663: stfsx frS,rA,rB + +63,64: mcrfs crfD,crfS +63,583: mffs[.] frD +63,70: mtfsb0[.] crbD +63,38: mtfsb1[.] crbD +63,711: mtfsf[.] FM,frB +63,134: mtfsfi[.] crfD,IMM +*/ + +/* +float to int: + FMOVD g+0(SB),F1 + FCTIWZ F1,F4 + FMOVD F4,.rathole+0(SB) + MOVW .rathole+4(SB),R7 + MOVW R7,l+0(SB) +*/ + +/* +int to float: + MOVW $1127219200,R9 + MOVW l+0(SB),R7 + MOVW R9,.rathole+0(SB) + XOR $-2147483648,R7,R6 + MOVW R6,.rathole+4(SB) + FMOVD .rathole+0(SB),F0 + FSUB F27,F0 + +unsigned to float: + MOVW ul+0(SB),R5 + MOVW R9,.rathole+0(SB) + XOR $-2147483648,R5,R4 + MOVW R4,.rathole+4(SB) + FMOVD .rathole+0(SB),F3 + FSUB F27,F3 + FCMPU F3,F28 + BGE ,3(PC) + FMOVD $4.29496729600000000e+09,F2 + FADD F2,F3 + FMOVD F3,g+0(SB) +*/ diff --git a/os/cerf405/gpio.c b/os/cerf405/gpio.c new file mode 100644 index 00000000..a4821b1c --- /dev/null +++ b/os/cerf405/gpio.c @@ -0,0 +1,106 @@ +#include "u.h" +#include "mem.h" +#include "../port/lib.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#define GPIOREGS ((Gpioregs*)KADDR(PHYSGPIO)) + +static ulong gpioreserved; +static Lock gpiolock; + +void +gpioreserve(ulong mask) +{ + ilock(&gpiolock); + if(gpioreserved & mask) + panic("gpioreserve: duplicate use of 0x%.8lux", gpioreserved & mask); + gpioreserved |= mask; + iunlock(&gpiolock); +} + +/* + * expand each of the bottom 16 bits into a two bit field + * with the bit as low order bit in the field + */ +static ulong +inflate(ulong m) +{ + m = ((m & 0xFF00) << 8) | (m & 0x00FF); + m = ((m << 4) | m) & 0x0F0F0F0F; + m = ((m << 2) | m) & 0x33333333; + return ((m << 1) | m) & 0x55555555; +} + +/* + * set tcr, osr[hl], tsr[hl], odr, isr1[hl] for gpio bits in m, + * following the configuration bits in cfg. when setting + * a gpio pin as output, set the right output value in OR first. + */ +void +gpioconfig(ulong m, ulong cfg) +{ + Gpioregs *g; + ulong h, hm, l, lm; + + h = inflate(m>>16); + hm = h | (h<<1); + l = inflate(m); + lm = l | (l<<1); + ilock(&gpiolock); + g = GPIOREGS; + /* + * tsr has a setting ``Alt1 three-state source'' but + * table 23-7 sets it to zero (use TCR) and sets TCR. + * thus, it seems never really to be needed. + */ + g->tsrh &= ~hm; + g->tsrl &= ~lm; + /* always select pin input (don't care for outputs) */ + g->isr1h = (g->isr1h & ~hm) | h; + g->isr1l = (g->isr1l & ~lm) | l; + if(cfg & Gpio_Alt1){ /* table 23-7 */ + g->osrh = (g->osrh & ~hm) | h; /* alt1 source */ + g->osrl = (g->osrl & ~lm) | l; + }else{ + g->osrh &= ~hm; /* GPIO_OR source */ + g->osrl &= ~lm; + } + if(cfg & Gpio_OD) + g->odr |= m; + else + g->odr &= ~m; + if(cfg & Gpio_in || cfg & Gpio_Tri) + g->tcr &= ~m; + else + g->tcr |= m; + iunlock(&gpiolock); +} + +ulong +gpioget(ulong mask) +{ + return GPIOREGS->ir & mask; +} + +void +gpioset(ulong mask, ulong out) +{ + Gpioregs *g; + + ilock(&gpiolock); + g = GPIOREGS; + g->or = (g->or & ~mask) | (out & mask); + iunlock(&gpiolock); +} + +void +gpiorelease(ulong mask) +{ + ilock(&gpiolock); + if((gpioreserved & mask) != mask) + panic("gpiorelease: unexpected release of 0x%.8lux", ~gpioreserved & mask); + gpioreserved &= ~mask; + iunlock(&gpiolock); +} diff --git a/os/cerf405/iic.c b/os/cerf405/iic.c new file mode 100644 index 00000000..22d5e51f --- /dev/null +++ b/os/cerf405/iic.c @@ -0,0 +1,605 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* + * basic read/write interface to 4xx IIC bus (master mode) + * ``referred to as IIC to distinguish it from the Phillips I⁲C bus [itself]'' + * + * TO DO: + * power management ref count + * power up/power down on timer? + */ + +typedef struct Ctlr Ctlr; +typedef struct IICregs IICregs; + +struct IICregs { + uchar mdbuf; /* master data buffer */ + uchar rsvd0; + uchar sdbuf; /* slave data buffer */ + uchar rsvd1; + uchar lmadr; /* low master address */ + uchar hmadr; /* high master address */ + uchar cntl; /* control */ + uchar mdcntl; /* mode control */ + uchar sts; /* status */ + uchar extsts; /* extended status */ + uchar lsadr; /* low slave address */ + uchar hsadr; /* high slave address */ + uchar clkdiv; /* clock divide */ + uchar intrmsk; /* interrupt mask */ + uchar xfrcnt; /* transfer count */ + uchar xtcntlss; /* extended control and slave status */ + uchar directcntl; /* direct control */ +}; + +enum { + /* cntl */ + Hmt= 1<<7, /* halt master transfer */ + Amd10= 1<<6, /* =0, 7-bit; =1, 10-bit addressing */ + /* 5,4: two bit transfer count (n-1)&3 bytes */ + Rpst= 1<<3, /* =0, normal start; =1, repeated Start, transfer should be followed by another start */ + Cht= 1<<2, /* chain transfer; not the last */ + Write= 0<<1, /* transfer is a write */ + Read= 1<<1, /* transfer is a read */ + Pt= 1<<0, /* =0, most recent transfer complete; =1, start transfer if bus free */ + + /* mdcntl */ + Fsdb= 1<<7, /* flush slave data buffer */ + Fmdb= 1<<6, /* flush master data buffer */ + Fsm= 1<<4, /* =0, 100 kHz standard mode; =1, 400 Khz fast mode */ + Esm= 1<<3, /* enable slave mode */ + Eint= 1<<2, /* enable interrupt */ + Eubs= 1<<1, /* exit unknown bus state */ + Hscl= 1<<0, /* hold IIC serial clock low */ + + /* sts */ + Sss= 1<<7, /* slave status set (slave operation in progress) */ + Slpr= 1<<6, /* sleep mode */ + Mdbs= 1<<5, /* master data buffer has data */ + Mdbf= 1<<4, /* master data buffer is full */ + Scmp= 1<<3, /* stop complete */ + Err= 1<<2, /* error set in extsts */ + Irqa= 1<<1, /* IRQ active */ + /* Pt as above */ + + /* extsts */ + Irqp= 1<<7, /* IRQ pending */ + Bcs= 7<<4, + Bcs_ssel= 1<<4, /* slave-selected state */ + Bcs_sio= 2<<4, /* slave transfer state */ + Bcs_mio= 3<<4, /* master transfer state */ + Bcs_free= 4<<4, /* bus is free */ + Bcs_busy= 5<<4, /* bus is busy */ + Bcs_gok= 6<<4, /* unknown state */ + Irqd= 1<<3, /* IRQ on deck */ + La= 1<<2, /* lost arbitration */ + Ict= 1<<1, /* incomplete transfer */ + Xfra= 1<<0, /* transfer aborted */ + + /* intrmsk */ + Eirc= 1<<7, /* slave read complete */ + Eirs= 1<<6, /* slave read needs service */ + Eiwc= 1<<5, /* slave write complete */ + Eiws= 1<<4, /* slave write needs service */ + Eihe= 1<<3, /* halt executed */ + Eiic= 1<<2, /* incomplete transfer */ + Eita= 1<<1, /* transfer aborted */ + Eimtc= 1<<0, /* master transfer complete */ + + /* xtcntlss */ + Src= 1<<7, /* slave read complete; =1, NAK or Stop, or repeated Start ended op */ + Srs= 1<<6, /* slave read needs service */ + Swc= 1<<5, /* slave write complete */ + Sws= 1<<4, /* slave write needs service */ + Sdbd= 1<<3, /* slave buffer has data */ + Sdbf= 1<<2, /* slave buffer is full */ + Epi= 1<<1, /* enable pulsed IRQ on transfer aborted */ + Srst= 1<<0, /* soft reset */ + + /* directcntl */ + Sdac= 1<<3, /* SDA output */ + Scc= 1<<2, /* SCL output */ + Msda= 1<<1, /* SDA input */ + Msc= 1<<0, /* SCL input */ + + /* others */ + Rbit = 1<<0, /* bit in address byte denoting read */ + FIFOsize= 4, /* most to be written at once */ + + MaxIO = 8192, /* largest transfer done at once (can change) */ + MaxSA= 2, /* largest subaddress; could be FIFOsize */ + Bufsize = MaxIO, /* subaddress bytes don't go in buffer */ + Freq = 100000, + I2Ctimeout = 125, /* msec (can change) */ + + Chatty = 0, +}; + +#define DPRINT if(Chatty)print + +/* + * I2C software structures + */ + +struct Ctlr { + Lock; + QLock io; + int init; + int polling; /* eg, when running before system set up */ + IICregs* regs; /* hardware registers */ + + /* controller state (see below) */ + int status; + int phase; + Rendez r; + + /* transfer parameters */ + int cntl; /* everything but transfer length */ + int rdcount; /* requested read transfer size */ + Block* b; +}; + +enum { + /* Ctlr.state */ + Idle, + Done, + Failed, + Busy, + Halting, +}; + +static Ctlr iicctlr[1]; + +static void interrupt(Ureg*, void*); +static int readyxfer(Ctlr*); +static void rxstart(Ctlr*); +static void txstart(Ctlr*); +static void stopxfer(Ctlr*); +static void txoffset(Ctlr*, ulong, int); +static int idlectlr(Ctlr*); + +static void +iicdump(char *t, IICregs *iic) +{ + iprint("iic %s: lma=%.2ux hma=%.2ux im=%.2ux mdcntl=%.2ux sts=%.2ux ests=%.2ux cntl=%.2ux\n", + t, iic->lmadr, iic->hmadr, iic->intrmsk, iic->mdcntl, iic->sts, iic->extsts, iic->cntl); +} + +static void +initialise(IICregs *iic, int intrmsk) +{ + int d; + + d = (m->opbhz-1000000)/10000000; + if(d <= 0) + d = 1; /* just in case OPB freq < 20 Mhz */ + /* initialisation (see 22.4, p. 22-23) */ + iic->lmadr = 0; + iic->hmadr = 0; + iic->sts = Scmp|Irqa; + iic->extsts = Irqp | Irqd | La | Ict | Xfra; + iic->clkdiv = d; + iic->intrmsk = 0; /* see below */ + iic->xfrcnt = 0; + iic->xtcntlss = Src | Srs | Swc | Sws; + iic->mdcntl = Fsdb | Fmdb | Eubs; /* reset; standard mode */ + iic->cntl = 0; + eieio(); + iic->mdcntl = 0; + eieio(); + if(intrmsk){ + iic->intrmsk = intrmsk; + iic->mdcntl = Eint; + } +} + +/* + * called by the reset routine of any driver using the IIC + */ +void +i2csetup(int polling) +{ + IICregs *iic; + Ctlr *ctlr; + + ctlr = iicctlr; + ctlr->polling = polling; + iic = (IICregs*)KADDR(PHYSIIC); + ctlr->regs = iic; + if(!polling){ + if(ctlr->init == 0){ + initialise(iic, Eihe | Eiic | Eita | Eimtc); + ctlr->init = 1; + intrenable(VectorIIC, interrupt, iicctlr, BUSUNKNOWN, "iic"); + } + }else + initialise(iic, 0); +} + +static void +interrupt(Ureg*, void *arg) +{ + int sts, nb, ext, avail; + Ctlr *ctlr; + Block *b; + IICregs *iic; + + ctlr = arg; + iic = ctlr->regs; + if(0) + iicdump("intr", iic); + sts = iic->sts; + if(sts & Pt) + iprint("iic: unexpected status: %.2ux", iic->sts); + ext = iic->extsts; + if(sts & Mdbs) + nb = iic->xfrcnt & 7; + else + nb = 0; + eieio(); + iic->sts = sts; + if(sts & Err && (ext & (La|Xfra)) != 0) + iprint("iic: s=%.2ux es=%.2ux (IO)\n", sts, ext); + ctlr->status = ext; + switch(ctlr->phase){ + default: + iprint("iic: unexpected interrupt: p-%d s=%.2ux es=%.2ux\n", ctlr->phase, sts, ext); + break; + + case Halting: + ctlr->phase = Idle; + break; + + case Busy: + b = ctlr->b; + if(b == nil) + panic("iic: no buffer"); + if(ctlr->cntl & Read){ + /* copy data in from FIFO */ + avail = b->lim - b->wp; + if(nb > avail) + nb = avail; + while(--nb >= 0) + *b->wp++ = iic->mdbuf; /* ``the IIC interface handles the [FIFO] latency'' (22-4) */ + if(sts & Err || ctlr->rdcount <= 0){ + ctlr->phase = Done; + wakeup(&ctlr->r); + break; + } + rxstart(ctlr); + }else{ + /* account for data transmitted */ + if((b->rp += nb) > b->wp) + b->rp = b->wp; + if(sts & Err || BLEN(b) <= 0){ + ctlr->phase = Done; + wakeup(&ctlr->r); + break; + } + txstart(ctlr); + } + } +} + +static int +done(void *a) +{ + return ((Ctlr*)a)->phase < Busy; +} + +static int +i2cerror(char *s) +{ + DPRINT("iic error: %s\n", s); + if(up) + error(s); + /* no current process, don't call error */ + return -1; +} + +static char* +startxfer(I2Cdev *d, int op, void (*xfer)(Ctlr*), Block *b, int n, ulong offset) +{ + IICregs *iic; + Ctlr *ctlr; + int i, cntl, p, s; + + ctlr = iicctlr; + if(up){ + qlock(&ctlr->io); + if(waserror()){ + qunlock(&ctlr->io); + nexterror(); + } + } + ilock(ctlr); + if(!idlectlr(ctlr)){ + iunlock(ctlr); + if(up) + error("bus confused"); + return "bus confused"; + } + if(ctlr->phase >= Busy) + panic("iic: ctlr busy"); + cntl = op | Pt; + if(d->tenbit) + cntl |= Amd10; + ctlr->cntl = cntl; + ctlr->b = b; + ctlr->rdcount = n; + ctlr->phase = Busy; + iic = ctlr->regs; + if(d->tenbit){ + iic->hmadr = 0xF0 | (d->addr>>7); /* 2 higher bits of address, LSB don't care */ + iic->lmadr = d->addr; + }else{ + iic->hmadr = 0; + iic->lmadr = d->addr<<1; /* 7-bit address */ + } + if(d->salen) + txoffset(ctlr, offset, d->salen); + else + (*xfer)(ctlr); + iunlock(ctlr); + + /* wait for it */ + if(ctlr->polling){ + for(i=0; !done(ctlr); i++){ + delay(2); + interrupt(nil, ctlr); + } + }else + tsleep(&ctlr->r, done, ctlr, I2Ctimeout); + + ilock(ctlr); + p = ctlr->phase; + s = ctlr->status; + ctlr->b = nil; + if(ctlr->phase != Done && ctlr->phase != Idle) + stopxfer(ctlr); + iunlock(ctlr); + + if(up){ + poperror(); + qunlock(&ctlr->io); + } + if(p != Done || s & (La|Xfra)){ /* CHECK; time out */ + if(s & La) + return "iic lost arbitration"; + if(s & Xfra) + return "iic transfer aborted"; + if(p != Done) + return "iic timed out"; + sprint(up->genbuf, "iic error: phase=%d estatus=%.2ux", p, s); + return up->genbuf; + } + return nil; +} + +long +i2csend(I2Cdev *d, void *buf, long n, ulong offset) +{ + Block *b; + char *e; + + if(n <= 0) + return 0; + if(n > MaxIO) + n = MaxIO; + + if(up){ + b = allocb(n); + if(b == nil) + error(Enomem); + if(waserror()){ + freeb(b); + nexterror(); + } + }else{ + b = iallocb(n); + if(b == nil) + return -1; + } + memmove(b->wp, buf, n); + b->wp += n; + e = startxfer(d, Write, txstart, b, 0, offset); + if(up) + poperror(); + n -= BLEN(b); /* residue */ + freeb(b); + if(e) + return i2cerror(e); + return n; +} + +long +i2crecv(I2Cdev *d, void *buf, long n, ulong offset) +{ + Block *b; + long nr; + char *e; + + if(n <= 0) + return 0; + if(n > MaxIO) + n = MaxIO; + + if(up){ + b = allocb(n); + if(b == nil) + error(Enomem); + if(waserror()){ + freeb(b); + nexterror(); + } + }else{ + b = iallocb(n); + if(b == nil) + return -1; + } + e = startxfer(d, Read, rxstart, b, n, offset); + nr = BLEN(b); + if(nr > 0) + memmove(buf, b->rp, nr); + if(up) + poperror(); + freeb(b); + if(e) + return i2cerror(e); + return nr; +} + +/* + * the controller must be locked for the following functions + */ + +static int +readyxfer(Ctlr *ctlr) +{ + IICregs *iic; + + iic = ctlr->regs; + iic->sts = Scmp | Err; + if((iic->sts & Pt) != 0){ + ctlr->phase = Failed; + wakeup(&ctlr->r); + return 0; + } + iic->mdcntl |= Fmdb; + return 1; +} + +/* + * start a master transfer to receive the next chunk of data + */ +static void +rxstart(Ctlr *ctlr) +{ + Block *b; + int cntl; + long nb; + + b = ctlr->b; + if(b == nil || (nb = ctlr->rdcount) <= 0){ + ctlr->phase = Done; + wakeup(&ctlr->r); + return; + } + if(!readyxfer(ctlr)) + return; + cntl = ctlr->cntl; + if(nb > FIFOsize){ + nb = FIFOsize; + cntl |= Cht; /* more to come */ + } + ctlr->rdcount -= nb; + ctlr->regs->cntl = cntl | ((nb-1)<<4); +} + +/* + * start a master transfer to send the next chunk of data + */ +static void +txstart(Ctlr *ctlr) +{ + Block *b; + int cntl, i; + long nb; + IICregs *iic; + + b = ctlr->b; + if(b == nil || (nb = BLEN(b)) <= 0){ + ctlr->phase = Done; + wakeup(&ctlr->r); + return; + } + if(!readyxfer(ctlr)) + return; + cntl = ctlr->cntl; + if(nb > FIFOsize){ + nb = FIFOsize; + cntl |= Cht; /* more to come */ + } + iic = ctlr->regs; + for(i=0; imdbuf = *b->rp++; /* load the FIFO */ + iic->cntl = cntl | ((nb-1)<<4); +} + +/* + * start a master transfer to send a sub-addressing offset; + * if subsequently receiving, use Rpst to cause the next transfer to include a Start; + * if subsequently sending, use Cht to chain the transfer without a Start. + */ +static void +txoffset(Ctlr *ctlr, ulong offset, int len) +{ + int i, cntl; + IICregs *iic; + + if(!readyxfer(ctlr)) + return; + iic = ctlr->regs; + for(i=len*8; (i -= 8) >= 0;) + iic->mdbuf = offset>>i; /* load offset bytes into FIFO */ + cntl = ctlr->cntl & Amd10; + if(ctlr->cntl & Read) + cntl |= Rpst; + else + cntl |= Cht; + iic->cntl = cntl | ((len-1)<<4) | Write | Pt; +} + +/* + * stop a transfer if one is in progress + */ +static void +stopxfer(Ctlr *ctlr) +{ + IICregs *iic; + int ext; + + iic = ctlr->regs; + ext = iic->extsts; + eieio(); + iic->sts = Scmp | Irqa; + eieio(); + if((iic->sts & Pt) == 0){ + ctlr->phase = Idle; + return; + } + if((ext & Bcs) == Bcs_mio && ctlr->phase != Halting){ + ctlr->phase = Halting; /* interrupt will clear the state */ + iic->cntl = Hmt; + } +} + +static int +idlectlr(Ctlr *ctlr) +{ + IICregs *iic; + + iic = ctlr->regs; + if((iic->extsts & Bcs) == Bcs_free){ + if((iic->sts & Pt) == 0){ + ctlr->phase = Idle; + return 1; + } + iprint("iic: bus free, ctlr busy: s=%.2ux es=%.2ux\n", iic->sts, iic->extsts); + } + /* hit it with the hammer, soft reset */ + iprint("iic: soft reset\n"); + iic->xtcntlss = Srst; + iunlock(ctlr); + delay(1); + ilock(ctlr); + initialise(iic, Eihe | Eiic | Eita | Eimtc); + ctlr->phase = Idle; + return (iic->extsts & Bcs) == Bcs_free && (iic->sts & Pt) == 0; +} diff --git a/os/cerf405/inb.s b/os/cerf405/inb.s new file mode 100644 index 00000000..b664f85e --- /dev/null +++ b/os/cerf405/inb.s @@ -0,0 +1,127 @@ +#include "mem.h" + +/* + * the tlb entry is configured for little-endian access, + * so byte-reversing loads aren't needed + */ + +#define ISAIO PHYSPCIIO0 + +#define BDNZ BC 16,0, + +TEXT inb(SB), $0 + OR $ISAIO, R3 + EIEIO + MOVBZ (R3), R3 + RETURN + +TEXT insb(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $1, R4 +insb1: + EIEIO + MOVBZ (R3), R7 + MOVBU R7, 1(R4) + BDNZ insb1 + RETURN + +TEXT outb(SB), $0 + MOVW v+4(FP), R4 + OR $ISAIO, R3 + EIEIO + MOVB R4, (R3) + RETURN + +TEXT outsb(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $1, R4 +outsb1: + EIEIO + MOVBZU 1(R4), R7 + MOVB R7, (R3) + BDNZ outsb1 + RETURN + +TEXT ins(SB), $0 + OR $ISAIO, R3 + EIEIO + MOVHZ (R3), R3 + RETURN + +TEXT inss(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $2, R4 +inss1: + EIEIO + MOVHZ (R3), R7 + MOVHU R7, 2(R4) + BDNZ inss1 + RETURN + +TEXT outs(SB), $0 + MOVW v+4(FP), R4 + OR $ISAIO, R3 + EIEIO + MOVH R4, (R3) + RETURN + +TEXT outss(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $2, R4 +outss1: + EIEIO + MOVHZU 2(R4), R7 + MOVH R7, (R3) + BDNZ outss1 + RETURN + +TEXT inl(SB), $0 + OR $ISAIO, R3 + EIEIO + MOVW (R3), R3 + RETURN + +TEXT insl(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $4, R4 +insl1: + EIEIO + MOVW (R3), R7 + MOVWU R7, 4(R4) + BDNZ insl1 + RETURN + +TEXT outl(SB), $0 + MOVW v+4(FP), R4 + OR $ISAIO, R3 + EIEIO + MOVW R4, (R3) + RETURN + +TEXT outsl(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $4, R4 +outsl1: + EIEIO + MOVWU 4(R4), R7 + MOVW R7, (R3) + BDNZ outsl1 + RETURN diff --git a/os/cerf405/io.h b/os/cerf405/io.h new file mode 100644 index 00000000..1f779dfc --- /dev/null +++ b/os/cerf405/io.h @@ -0,0 +1,301 @@ +typedef struct BD BD; +typedef struct Ring Ring; +typedef struct MALdev MALdev; +typedef struct I2Cdev I2Cdev; + +enum +{ + /* 405EP UIC interrupt vectors (IBM bit numbering) */ + VectorUIC= 0, + VectorUART0=VectorUIC, + VectorUART1, + VectorIIC, + VectorPCIECW, + VectorRsvd1, + VectorDMA0, + VectorDMA1, + VectorDMA2, + VectorDMA3, + VectorEtherwake, + VectorMALSERR, + VectorMALTXEOB, + VectorMALRXEOB, + VectorMALTXDE, + VectorMALRXDE, + VectorEMAC0, + VectorPCISERR, + VectorEMAC1, + VectorPCIPM, + VectorGPT0, + VectorGPT1, + VectorGPT2, + VectorGPT3, + VectorGPT4, + /* 1 reserved */ + VectorIRQ= VectorUIC+25, /* IRQ0 to IRQ6 */ + MaxVector= VectorIRQ+7, + + /* some flags to change polarity and sensitivity */ + IRQmask= 0xFF, /* actual vector address */ + IRQactivelow= 1<<8, + IRQedge= 1<<9, + IRQcritical= 1<<10, +}; + +/* + * these are defined to keep the interface compatible with other + * architectures, but only BUSUNKNOWN is currently used + */ +#define MKBUS(t,b,d,f) (((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8)) +#define BUSFNO(tbdf) (((tbdf)>>8)&0x07) +#define BUSDNO(tbdf) (((tbdf)>>11)&0x1F) +#define BUSBNO(tbdf) (((tbdf)>>16)&0xFF) +#define BUSTYPE(tbdf) ((tbdf)>>24) +#define BUSBDF(tbdf) ((tbdf)&0x00FFFF00) +#define BUSUNKNOWN (-1) + +enum { + BusOPB, + BusPLB, + BusPCI, + MaxBus +}; + +/* + * MAL Buffer Descriptors and IO Rings + */ + +struct BD { + ushort status; + ushort length; + ulong addr; +}; +#define MAXIORING 256 /* hardware limit to ring size */ +#define BDBUFLIM (4096-16) /* no MAL buffer larger than this */ + +BD* bdalloc(ulong); +void bdfree(BD*, int); +void dumpbd(char*, BD*, int); + +enum { + /* Rx BDs, bits common to all protocols */ + BDEmpty= 1<<15, + BDWrap= 1<<14, /* end of ring */ + BDContin= 1<<13, /* continuous mode */ + BDLast= 1<<12, /* last buffer in current packet */ + BDFirst= 1<<11, /* first buffer in current packet (set by MAL) */ + BDInt= 1<<10, /* interrupt when done */ + + /* Tx BDs */ + BDReady= 1<<15, /* ready to transmit; set by driver, cleared by MAL */ + /* BDWrap, BDInt, BDLast as above */ +}; + +struct Ring { + BD* rdr; /* receive descriptor ring */ + Block** rxb; /* receive ring buffers */ + int rdrx; /* index into rdr */ + int nrdre; /* length of rdr */ + + BD* tdr; /* transmit descriptor ring */ + Block** txb; /* transmit ring buffers */ + int tdrh; /* host index into tdr */ + int tdri; /* interface index into tdr */ + int ntdre; /* length of tdr */ + int ntq; /* pending transmit requests */ +}; + +#define NEXT(x, l) (((x)+1)%(l)) +#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1) +#define HOWMANY(x, y) (((x)+((y)-1))/(y)) +#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) + +/* + * one per mal channel + */ +typedef struct Mal Mal; +struct Mal { + int n; + int len; + int tx; + ulong mask; + + void* arg; + void (*interrupt)(Ureg*, void*); +}; + +Mal* malchannel(int, int, void (*)(Ureg*, void*), void*); +void maltxreset(Mal*); +void maltxinit(Mal*, Ring*); +void maltxenable(Mal*); +void malrxreset(Mal*); +void malrxinit(Mal*, Ring*, ulong); +void malrxenable(Mal*); +void ioringreserve(int, ulong, int, ulong); +int ioringinit(Ring*, int, int); + +typedef struct Gpioregs Gpioregs; +struct Gpioregs { + ulong or; /* output register */ + ulong tcr; /* tristate control */ + ulong osrh; /* output select high (0-15) */ + ulong osrl; /* output select low (16-31) */ + ulong tsrh; /* tristate select high (0-15) */ + ulong tsrl; /* tristate select low (16-31) */ + ulong odr; /* open drain */ + ulong ir; /* input */ + ulong rr1; /* receive register */ + ulong pad[3]; + ulong isr1h; /* input select 1 high (0-15) */ + ulong isr1l; /* input select 1 low (16-31) */ +}; + +enum { + /* software configuration bits for gpioconfig */ + Gpio_Alt1= 1<<0, /* implies specific settings of all the others, but include in or out */ + Gpio_OD= 1<<1, + Gpio_Tri= 1<<2, + Gpio_in= 1<<4, + Gpio_out= 1<<5, +}; + +void gpioreserve(ulong); +void gpioconfig(ulong, ulong); +ulong gpioget(ulong); +void gpioset(ulong, ulong); +void gpiorelease(ulong); + +/* + * used by ../port/devi2c.c and iic.c + */ +struct I2Cdev { + int addr; + int salen; /* length in bytes of subaddress, if used; 0 otherwise */ + int tenbit; /* 10-bit addresses */ +}; + +long i2crecv(I2Cdev*, void*, long, ulong); +long i2csend(I2Cdev*, void*, long, ulong); +void i2csetup(int); + +/* + * PCI support code. + */ +enum { /* type 0 and type 1 pre-defined header */ + PciVID = 0x00, /* vendor ID */ + PciDID = 0x02, /* device ID */ + PciPCR = 0x04, /* command */ + PciPSR = 0x06, /* status */ + PciRID = 0x08, /* revision ID */ + PciCCRp = 0x09, /* programming interface class code */ + PciCCRu = 0x0A, /* sub-class code */ + PciCCRb = 0x0B, /* base class code */ + PciCLS = 0x0C, /* cache line size */ + PciLTR = 0x0D, /* latency timer */ + PciHDT = 0x0E, /* header type */ + PciBST = 0x0F, /* BIST */ + + PciBAR0 = 0x10, /* base address */ + PciBAR1 = 0x14, + + PciINTL = 0x3C, /* interrupt line */ + PciINTP = 0x3D, /* interrupt pin */ +}; + +enum { /* type 0 pre-defined header */ + PciBAR2 = 0x18, + PciBAR3 = 0x1C, + PciBAR4 = 0x20, + PciBAR5 = 0x24, + PciCIS = 0x28, /* cardbus CIS pointer */ + PciSVID = 0x2C, /* subsystem vendor ID */ + PciSID = 0x2E, /* cardbus CIS pointer */ + PciEBAR0 = 0x30, /* expansion ROM base address */ + PciMGNT = 0x3E, /* burst period length */ + PciMLT = 0x3F, /* maximum latency between bursts */ +}; + +enum { /* type 1 pre-defined header */ + PciPBN = 0x18, /* primary bus number */ + PciSBN = 0x19, /* secondary bus number */ + PciUBN = 0x1A, /* subordinate bus number */ + PciSLTR = 0x1B, /* secondary latency timer */ + PciIBR = 0x1C, /* I/O base */ + PciILR = 0x1D, /* I/O limit */ + PciSPSR = 0x1E, /* secondary status */ + PciMBR = 0x20, /* memory base */ + PciMLR = 0x22, /* memory limit */ + PciPMBR = 0x24, /* prefetchable memory base */ + PciPMLR = 0x26, /* prefetchable memory limit */ + PciPUBR = 0x28, /* prefetchable base upper 32 bits */ + PciPULR = 0x2C, /* prefetchable limit upper 32 bits */ + PciIUBR = 0x30, /* I/O base upper 16 bits */ + PciIULR = 0x32, /* I/O limit upper 16 bits */ + PciEBAR1 = 0x28, /* expansion ROM base address */ + PciBCR = 0x3E, /* bridge control register */ +}; + +enum { /* type 2 pre-defined header */ + PciCBExCA = 0x10, + PciCBSPSR = 0x16, + PciCBPBN = 0x18, /* primary bus number */ + PciCBSBN = 0x19, /* secondary bus number */ + PciCBUBN = 0x1A, /* subordinate bus number */ + PciCBSLTR = 0x1B, /* secondary latency timer */ + PciCBMBR0 = 0x1C, + PciCBMLR0 = 0x20, + PciCBMBR1 = 0x24, + PciCBMLR1 = 0x28, + PciCBIBR0 = 0x2C, /* I/O base */ + PciCBILR0 = 0x30, /* I/O limit */ + PciCBIBR1 = 0x34, /* I/O base */ + PciCBILR1 = 0x38, /* I/O limit */ + PciCBSVID = 0x40, /* subsystem vendor ID */ + PciCBSID = 0x42, /* subsystem ID */ + PciCBLMBAR = 0x44, /* legacy mode base address */ +}; + +typedef struct Pcisiz Pcisiz; +struct Pcisiz +{ + Pcidev* dev; + int siz; + int bar; +}; + +typedef struct Pcidev Pcidev; +struct Pcidev +{ + int tbdf; /* type+bus+device+function */ + ushort vid; /* vendor ID */ + ushort did; /* device ID */ + + uchar rid; + uchar ccrp; + uchar ccru; + uchar ccrb; + + struct { + ulong bar; /* base address */ + int size; + } mem[6]; + + struct { + ulong bar; + int size; + } rom; + uchar intl; /* interrupt line */ + + Pcidev* list; + Pcidev* link; /* next device on this bno */ + + Pcidev* bridge; /* down a bus */ + struct { + ulong bar; + int size; + } ioa, mema; + ulong pcr; +}; + +#define PCIWINDOW 0x80000000 +#define PCIWADDR(va) (PADDR(va)+PCIWINDOW) diff --git a/os/cerf405/l.s b/os/cerf405/l.s new file mode 100644 index 00000000..28990ef6 --- /dev/null +++ b/os/cerf405/l.s @@ -0,0 +1,795 @@ +#include "mem.h" + +#define MB (1024*1024) + +/* + * common ppc special purpose registers + */ +#define DSISR 18 +#define SRR0 26 /* Saved Registers (exception) */ +#define SRR1 27 +#define SPRG0 272 /* Supervisor Private Registers */ +#define SPRG1 273 +#define SPRG2 274 +#define SPRG3 275 +#define TBRU 269 /* Time base Upper/Lower (Reading) */ +#define TBRL 268 +#define TBWU 285 /* Time base Upper/Lower (Writing) */ +#define TBWL 284 +#define PVR 287 /* Processor Version */ + +/* + * 4xx-specific special purpose registers of interest here + */ +#define ICCR 1019 /* instruction cache control */ +#define DCCR 1018 /* data cache control */ +#define DBCR0 1010 /* debug control register 0 */ +#define DCWR 964 /* data cache write-through */ +#define PID 945 /* TLB process ID */ +#define CCR0 947 /* core configuration register 0 */ +#define SLER 955 /* storage little-endian */ +#define SU0R 956 /* storage user-defined 0 */ +#define SRR2 990 +#define SRR3 991 +/* SPRGn up to 7, if needed, on the 400 series */ +#define DEAR 961 /* data error address */ +#define ESR 980 /* exception syndrome */ +#define EVPR 982 /* exception vector prefix */ +#define PIT 987 /* interval timer */ +#define SGR 953 /* storage guarded */ +#define TCR 986 /* timer control */ +#define TSR 984 /* timer status */ +#define ZPR 944 /* zone protection */ + +/* + * 4xx-specific(?) device control registers + */ +#define OCM0_DSCNTL 0x1B /* OCM data-side control register */ + +/* use of SPRG registers in save/restore */ +#define SAVER0 SPRG0 +#define SAVER1 SPRG1 +#define SAVELR SPRG2 +#define SAVEXX SPRG3 + +/* special instruction definitions */ +#define BDNZ BC 16,0, +#define BDNE BC 0,2, +#define TLBIA WORD $((31<<26)|(370<<1)) +#define TLBSYNC WORD $((31<<26)|(566<<1)) +#define MFTB(tbr,d) WORD $((31<<26)|((d)<<21)|((tbr&0x1f)<<16)|(((tbr>>5)&0x1f)<<11)|(371<<1)) + +/* 603/603e specific: load tlb entries */ +#define TLBLD(x) WORD $((31<<26)|(978<<1)|((x&0x1F)<<11)) +#define TLBLI(x) WORD $((31<<26)|(1010<<1)|((x&0x1F)<<11)) + +/* 400 models; perhaps others */ +#define ICCCI(a,b) WORD $((31<<26)|((a)<<16)|((b)<<11)|(966<<1)) +#define DCCCI(a,b) WORD $((31<<26)|((a)<<16)|((b)<<11)|(454<<1)) +/* these follow the source -> dest ordering */ +#define DCREAD(s,t) WORD $((31<<26)|((t)<<21)|((s)<<11)|(486<<1)) +#define DCRF(n) ((((n)>>5)&0x1F)|(((n)&0x1F)<<5)) +#define MTDCR(s,n) WORD $((31<<26)|((s)<<21)|(DCRF(n)<<11)|(451<<1)) +#define MFDCR(n,t) WORD $((31<<26)|((t)<<21)|(DCRF(n)<<11)|(323<<1)) +#define TLBRELO(a,t) WORD $((31<<26)|((t)<<21)|((a)<<16)|(1<<11)|(946<<1)) +#define TLBREHI(a,t) WORD $((31<<26)|((t)<<21)|((a)<<16)|(0<<11)|(946<<1)) +#define TLBWELO(s,a) WORD $((31<<26)|((s)<<21)|((a)<<16)|(1<<11)|(978<<1)) +#define TLBWEHI(s,a) WORD $((31<<26)|((s)<<21)|((a)<<16)|(0<<11)|(978<<1)) +#define TLBSX(a,b,t) WORD $((31<<26)|((t)<<21)|((a)<<16)|((b)<<11)|(914<<1)) +#define TLBSXCC(a,b,t) WORD $((31<<26)|((t)<<21)|((a)<<16)|((b)<<11)|(914<<1)|1) +#define WRTMSR_EE(s) WORD $((31<<26)|((s)<<21)|(131<<1)) +#define WRTMSR_EEI(e) WORD $((31<<26)|((e)<<16)|(163<<1)) + +/* on some models mtmsr doesn't synchronise enough (eg, 603e) */ +#define MSRSYNC SYNC; ISYNC + +/* on the 400 series, the prefetcher madly fetches across RFI, sys call, and others; use BR 0(PC) to stop */ +#define RFI WORD $((19<<26)|(50<<1)); BR 0(PC) +#define RFCI WORD $((19<<26)|(51<<1)); BR 0(PC) + +#define UREGSPACE (UREGSIZE+8) + +/* could define STEP to set an LED to mark progress */ +#define STEP(x) + +/* + * Boot first processor + */ + TEXT start(SB), $-4 + + MOVW MSR, R3 + RLWNM $0, R3, $~MSR_EE, R3 + OR $MSR_ME, R3 + ISYNC + MOVW R3, MSR /* turn off interrupts but enable traps */ + MSRSYNC + MOVW $0, R0 /* except during trap handling, R0 is zero from now on */ + MOVW R0, CR + + MOVW $setSB-KZERO(SB), R2 /* SB until mmu on */ + +/* + * reset the caches and disable them until mmu on + */ + MOVW R0, SPR(ICCR) + ICCCI(0, 2) /* the errata reveals that EA is used; we'll use SB */ + ISYNC + + MOVW $((CACHEWAYSIZE/CACHELINESZ)-1), R3 + MOVW R3, CTR + MOVW R0, R3 +dcinv: + DCCCI(0,3) + ADD $32, R3 + BDNZ dcinv + + /* cache is copy-back, disabled; no user-defined 0; big endian throughout */ + MOVW R0, SPR(DCWR) + MOVW R0, SPR(DCCR) + MOVW R0, SPR(SU0R) + MOVW R0, SPR(SLER) + ISYNC + + /* guard everything above 0x20000000 */ + MOVW $~(0xF000<<16), R3 + MOVW R3, SPR(SGR) + ISYNC + + /* set access to LED */ + MOVW $PHYSGPIO, R4 + MOVW $(1<<31), R6 + MOVW $(0xC000<<16), R5 + MOVW 4(R4), R3 + OR R6, R3 + MOVW R3, 4(R4) /* tcr set */ + MOVW 0x18(R4), R3 + ANDN R6, R3 + MOVW R3, 0x18(R4) /* odr reset */ + MOVW 8(R4), R3 + ANDN R5, R3 + MOVW R3, 8(R4) /* osrh uses or */ + MOVW 0x10(R4), R3 + ANDN R5, R3 + MOVW R3, 0x10(R4) /* tsr uses tcr */ + + MOVW $(1<<31), R4 /* reset MAL */ + MTDCR(0x180, 4) + +/* + MOVW $'H', R3 + BL uartputc(SB) + MOVW $'\n', R3 + BL uartputc(SB) +*/ + +/* + * set other system configuration values + */ + MOVW R0, SPR(PIT) + MOVW $~0, R3 + MOVW R3, SPR(TSR) + +STEP(1) + + BL kernelmmu(SB) + /* now running with correct addresses, mmu on */ + + MOVW $setSB(SB), R2 + + /* enable caches for kernel 128mb in real mode; data is copy-back */ + MOVW R0, SPR(DCWR) + MOVW $(1<<31), R3 + MOVW R3, SPR(DCCR) + MOVW R3, SPR(ICCR) + +/* + BL ledoff(SB) + + MOVW $0x800, R8 + MOVW R8, LR + BL (LR) + BR 0(PC) +*/ + +STEP(2) + /* no kfpinit on 4xx */ + + MOVW $mach0(SB), R(MACH) + ADD $(MACHSIZE-8), R(MACH), R1 + SUB $4, R(MACH), R3 + ADD $4, R1, R4 +clrmach: + MOVWU R0, 4(R3) + CMP R3, R4 + BNE clrmach + + MOVW R0, R(USER) + MOVW R0, 0(R(MACH)) + + MOVW $edata(SB), R3 + MOVW $end(SB), R4 + ADD $4, R4 + SUB $4, R3 +clrbss: + MOVWU R0, 4(R3) + CMP R3, R4 + BNE clrbss + +STEP(3) + BL main(SB) + BR 0(PC) + +TEXT kernelmmu(SB), $-4 + TLBIA + ISYNC + SYNC + + /* make following TLB entries shared, TID=PID=0 */ + MOVW R0, SPR(PID) + + /* all zones are supervisor, access controlled by TLB */ + MOVW R0, SPR(ZPR) + + /* map various things 1:1 */ + MOVW $tlbtab-KZERO(SB), R4 + MOVW $tlbtabe-KZERO(SB), R5 + SUB R4, R5 + MOVW $(2*4), R6 + DIVW R6, R5 + SUB $4, R4 + MOVW R5, CTR + MOVW R0, R3 +ltlb: + MOVWU 4(R4), R5 /* TLBHI */ + TLBWEHI(5,3) + MOVWU 4(R4), R5 /* TLBLO */ + TLBWELO(5,3) + ADD $1, R3 + BDNZ ltlb + + MOVW LR, R3 + OR $KZERO, R3 + MOVW R3, SPR(SRR0) + MOVW MSR, R4 + OR $(MSR_IR|MSR_DR), R4 + MOVW R4, SPR(SRR1) + + RFI /* resume in kernel mode in caller */ + +TEXT ledoff(SB), $0 + MOVW $PHYSGPIO, R4 + MOVW 0(R4), R3 + RLWNM $0, R3, $~(1<<31), R3 /* LED off */ + MOVW R3, 0(R4) + RETURN + +TEXT splhi(SB), $0 + MOVW MSR, R3 + RLWNM $0, R3, $~MSR_EE, R4 + SYNC + MOVW R4, MSR + MSRSYNC + MOVW LR, R31 + MOVW R31, 4(R(MACH)) /* save PC in m->splpc */ + RETURN + +TEXT splx(SB), $0 + MOVW MSR, R4 + RLWMI $0, R3, $MSR_EE, R4 + RLWNMCC $0, R3, $MSR_EE, R5 + BNE splx0 + MOVW LR, R31 + MOVW R31, 4(R(MACH)) /* save PC in m->splpc */ +splx0: + SYNC + MOVW R4, MSR + MSRSYNC + RETURN + +TEXT splxpc(SB), $0 + MOVW MSR, R4 + RLWMI $0, R3, $MSR_EE, R4 + RLWNMCC $0, R3, $MSR_EE, R5 + SYNC + MOVW R4, MSR + MSRSYNC + RETURN + +TEXT spllo(SB), $0 + MFTB(TBRL, 3) + MOVW R3, spltbl(SB) + MOVW MSR, R3 + OR $MSR_EE, R3, R4 + SYNC + MOVW R4, MSR + MSRSYNC + RETURN + +TEXT spldone(SB), $0 + RETURN + +TEXT islo(SB), $0 + MOVW MSR, R3 + RLWNM $0, R3, $MSR_EE, R3 + RETURN + +TEXT setlabel(SB), $-4 + MOVW LR, R31 + MOVW R1, 0(R3) + MOVW R31, 4(R3) + MOVW $0, R3 + RETURN + +TEXT gotolabel(SB), $-4 + MOVW 4(R3), R31 + MOVW R31, LR + MOVW 0(R3), R1 + MOVW $1, R3 + RETURN + +TEXT tlbwelo(SB), $-4 + MOVW v+4(FP), R5 + SYNC + TLBWELO(5, 3) + ISYNC + SYNC + RETURN + +TEXT tlbwehi(SB), $-4 + MOVW v+4(FP), R5 + SYNC + TLBWEHI(5, 3) + ISYNC + SYNC + RETURN + +TEXT tlbrehi(SB), $-4 + TLBREHI(3, 3) + RETURN + +TEXT tlbrelo(SB), $-4 + TLBRELO(3, 3) + RETURN + +TEXT tlbsxcc(SB), $-4 + TLBSXCC(0, 3, 3) + BEQ tlbsxcc0 + MOVW $-1, R3 /* not found */ +tlbsxcc0: + RETURN + +/* + * enter with stack set and mapped. + * on return, SB (R2) has been set, and R3 has the Ureg*, + * the MMU has been re-enabled, kernel text and PC are in KSEG, + * R(MACH) has been set, and R0 contains 0. + * + * this can be simplified in the Inferno regime + */ +TEXT saveureg(SB), $-4 +/* + * save state + */ + MOVMW R2, 48(R1) /* r2:r31 */ + MOVW $setSB(SB), R2 + MOVW SPR(SAVER1), R4 + MOVW R4, 44(R1) + MOVW SPR(SAVER0), R5 + MOVW R5, 40(R1) + MOVW CTR, R6 + MOVW R6, 36(R1) + MOVW XER, R4 + MOVW R4, 32(R1) + MOVW CR, R5 + MOVW R5, 28(R1) + MOVW SPR(SAVELR), R6 /* LR */ + MOVW R6, 24(R1) + /* pad at 20(R1) */ + /* old PC(16) and status(12) saved earlier */ + MOVW SPR(SAVEXX), R0 + MOVW R0, 8(R1) /* cause/vector */ + ADD $8, R1, R3 /* Ureg* */ + STWCCC R3, (R1) /* break any pending reservations */ + MOVW $0, R0 /* compiler/linker expect R0 to be zero */ + + MOVW MSR, R5 + OR $(MSR_IR|MSR_DR), R5 /* enable MMU */ + MOVW R5, SPR(SRR1) + MOVW LR, R31 + OR $KZERO, R31 /* return PC in KSEG0 */ + MOVW R31, SPR(SRR0) + SYNC + ISYNC + RFI /* returns to trap handler */ + +TEXT icflush(SB), $-4 /* icflush(virtaddr, count) */ + MOVW n+4(FP), R4 + CMP R4, R0 + BLE icf1 + RLWNM $0, R3, $~(CACHELINESZ-1), R5 + SUB R5, R3 + ADD R3, R4 + ADD $(CACHELINESZ-1), R4 + SRAW $CACHELINELOG, R4 + MOVW R4, CTR +icf0: ICBI (R5) + ADD $CACHELINESZ, R5 + BDNZ icf0 +icf1: + ISYNC + RETURN + +/* + * flush to store and invalidate globally + */ +TEXT dcflush(SB), $-4 /* dcflush(virtaddr, count) */ + SYNC + MOVW n+4(FP), R4 + RLWNM $0, R3, $~(CACHELINESZ-1), R5 + CMP R4, $0 + BLE dcf1 + SUB R5, R3 + ADD R3, R4 + ADD $(CACHELINESZ-1), R4 + SRAW $CACHELINELOG, R4 + MOVW R4, CTR +dcf0: DCBF (R5) + ADD $CACHELINESZ, R5 + BDNZ dcf0 + SYNC +dcf1: + ISYNC + MOVW R5, R3 /* check its operation */ + RETURN + +/* + * invalidate without flush, globally + */ +TEXT dcinval(SB), $-4 /* dcinval(virtaddr, count) */ + SYNC + MOVW n+4(FP), R4 + RLWNM $0, R3, $~(CACHELINESZ-1), R5 + CMP R4, $0 + BLE dci1 + SUB R5, R3 + ADD R3, R4 + ADD $(CACHELINESZ-1), R4 + SRAW $CACHELINELOG, R4 + MOVW R4, CTR +dci0: DCBI (R5) + ADD $CACHELINESZ, R5 + BDNZ dci0 + SYNC + ISYNC +dci1: + RETURN + +TEXT dccci(SB), $-4 + SYNC + DCCCI(0, 3) + ISYNC + RETURN + +TEXT _tas(SB), $0 + SYNC + MOVW R3, R4 + MOVW $0xdeaddead,R5 +tas1: + DCBF (R4) /* fix for 603x bug */ + LWAR (R4), R3 + CMP R3, $0 + BNE tas0 + STWCCC R5, (R4) + BNE tas1 +tas0: + SYNC + ISYNC + RETURN + +TEXT gettbl(SB), $0 + MFTB(TBRL, 3) + RETURN + +TEXT gettbu(SB), $0 + MFTB(TBRU, 3) + RETURN + +TEXT getpvr(SB), $0 + MOVW SPR(PVR), R3 + RETURN + +TEXT getcallerpc(SB), $-4 + MOVW 0(R1), R3 + RETURN + +TEXT getdear(SB), $0 + MOVW SPR(DEAR), R3 + RETURN + +TEXT getdsisr(SB), $0 + MOVW SPR(DSISR), R3 + RETURN + +TEXT getmsr(SB), $0 + MOVW MSR, R3 + RETURN + +TEXT putmsr(SB), $0 + SYNC + MOVW R3, MSR + MSRSYNC + RETURN + +TEXT putevpr(SB), $0 + MOVW R3, SPR(EVPR) + RETURN + +TEXT getesr(SB), $0 + MOVW SPR(ESR), R3 + RETURN + +TEXT putesr(SB), $0 + MOVW R3, SPR(ESR) + RETURN + +TEXT getpit(SB), $0 + MOVW SPR(PIT), R3 + RETURN + +TEXT putpit(SB), $0 + MOVW R3, SPR(PIT) + RETURN + +TEXT gettsr(SB), $0 + MOVW SPR(TSR), R3 + RETURN + +TEXT puttsr(SB), $0 + MOVW R3, SPR(TSR) + RETURN + +TEXT puttcr(SB), $0 + MOVW R3, SPR(TCR) + RETURN + +TEXT eieio(SB), $0 + EIEIO + RETURN + +TEXT gotopc(SB), $0 + MOVW R3, CTR + MOVW LR, R31 /* for trace back */ + BR (CTR) + +TEXT getccr0(SB), $-4 + MOVW SPR(CCR0), R3 + RETURN + +TEXT dcread(SB), $-4 + MOVW 4(FP), R4 + MOVW SPR(CCR0), R5 + RLWNM $0, R5, $~0xFF, R5 + OR R4, R5 + MOVW R5, SPR(CCR0) + SYNC + ISYNC + DCREAD(3, 3) + RETURN + +TEXT getdcr(SB), $-4 + MOVW $_getdcr(SB), R5 + SLW $3, R3 + ADD R3, R5 + MOVW R5, CTR + BR (CTR) + +TEXT putdcr(SB), $-4 + MOVW $_putdcr(SB), R5 + SLW $3, R3 + ADD R3, R5 + MOVW R5, CTR + MOVW 8(R1), R3 + BR (CTR) + +TEXT firmware(SB), $0 + MOVW $(3<<28), R3 + MOVW R3, SPR(DBCR0) /* system reset */ + BR 0(PC) + +/* + * byte swapping of arrays of long and short; + * could possibly be avoided with more changes to drivers + */ +TEXT swabl(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + SRAW $2, R5, R5 + MOVW R5, CTR + SUB $4, R4 + SUB $4, R3 +swabl1: + ADD $4, R3 + MOVWU 4(R4), R7 + MOVWBR R7, (R3) + BDNZ swabl1 + RETURN + +TEXT swabs(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + SRAW $1, R5, R5 + MOVW R5, CTR + SUB $2, R4 + SUB $2, R3 +swabs1: + ADD $2, R3 + MOVHZU 2(R4), R7 + MOVHBR R7, (R3) + BDNZ swabs1 + RETURN + +TEXT legetl(SB), $0 + MOVWBR (R3), R3 + RETURN + +TEXT lesetl(SB), $0 + MOVW v+4(FP), R4 + MOVWBR R4, (R3) + RETURN + +TEXT legets(SB), $0 + MOVHBR (R3), R3 + RETURN + +TEXT lesets(SB), $0 + MOVW v+4(FP), R4 + MOVHBR R4, (R3) + RETURN + +TEXT itlbmiss(SB), $-4 + BR traps + +TEXT dtlbmiss(SB), $-4 + BR traps + +/* + * traps force memory mapping off. + * this code goes to much effort to restore it; + * (a little more effort than needed for the Inferno environment) + */ +TEXT trapvec(SB), $-4 +traps: + MOVW LR, R0 + +pagefault: + +/* + * map data virtually and make space to save + */ + MOVW R0, SPR(SAVEXX) /* vector */ +trapcomm: + MOVW R1, SPR(SAVER1) + SYNC + ISYNC + MOVW MSR, R0 + OR $(MSR_DR|MSR_ME), R0 /* make data space usable */ + SYNC + MOVW R0, MSR + MSRSYNC + SUB $UREGSPACE, R1 + + MOVW SPR(SRR0), R0 /* save SRR0/SRR1 now, since DLTB might be missing stack page */ + MOVW R0, LR + MOVW SPR(SRR1), R0 + RLWNM $0, R0, $~MSR_WE, R0 /* remove wait state */ + MOVW R0, 12(R1) /* save status: could take DLTB miss here */ + MOVW LR, R0 + MOVW R0, 16(R1) /* old PC */ + BL saveureg(SB) + BL trap(SB) + BR restoreureg + +/* + * critical trap/interrupt + */ +TEXT trapcvec(SB), $-4 + MOVW LR, R0 + /* for now we'll just restore the state to the conventions that trap expects, since we don't use critical intrs yet */ + MOVW R0, SPR(SAVEXX) + MOVW SPR(SRR2), R0 + MOVW R0, SPR(SRR0) + MOVW SPR(SRR3), R0 + MOVW R0, SPR(SRR1) + BR trapcomm + +TEXT intrvec(SB), $-4 + MOVW LR, R0 + +/* + * map data virtually and make space to save + */ + MOVW R0, SPR(SAVEXX) /* vector */ + MOVW R1, SPR(SAVER1) + SYNC + ISYNC + MOVW MSR, R0 + OR $MSR_DR, R0 /* make data space usable */ + SYNC + MOVW R0, MSR + MSRSYNC + SUB $UREGSPACE, R1 + + MFTB(TBRL, 0) + MOVW R0, intrtbl(SB) + + MOVW SPR(SRR0), R0 + MOVW R0, LR + MOVW SPR(SRR1), R0 + RLWNM $0, R0, $~MSR_WE, R0 /* remove wait state */ + MOVW R0, 12(R1) + MOVW LR, R0 + MOVW R0, 16(R1) + BL saveureg(SB) + + MFTB(TBRL, 5) + MOVW R5, isavetbl(SB) + + BL intr(SB) + +/* + * restore state from Ureg and return from trap/interrupt + */ +restoreureg: + MOVMW 48(R1), R2 /* r2:r31 */ + /* defer R1 */ + MOVW 40(R1), R0 + MOVW R0, SPR(SAVER0) + MOVW 36(R1), R0 + MOVW R0, CTR + MOVW 32(R1), R0 + MOVW R0, XER + MOVW 28(R1), R0 + MOVW R0, CR /* CR */ + MOVW 24(R1), R0 + MOVW R0, SPR(SAVELR) /* LR */ + /* pad, skip */ + MOVW 16(R1), R0 + MOVW R0, SPR(SRR0) /* old PC */ + MOVW 12(R1), R0 + MOVW R0, SPR(SRR1) /* old MSR */ + /* cause, skip */ + MOVW 44(R1), R1 /* old SP */ + MOVW SPR(SAVELR), R0 + MOVW R0, LR + MOVW SPR(SAVER0), R0 + RFI + +TEXT mul64fract(SB), $0 + MOVW a0+8(FP), R9 + MOVW a1+4(FP), R10 + MOVW b0+16(FP), R4 + MOVW b1+12(FP), R5 + + MULLW R10, R5, R13 /* c2 = lo(a1*b1) */ + + MULLW R10, R4, R12 /* c1 = lo(a1*b0) */ + MULHWU R10, R4, R7 /* hi(a1*b0) */ + ADD R7, R13 /* c2 += hi(a1*b0) */ + + MULLW R9, R5, R6 /* lo(a0*b1) */ + MULHWU R9, R5, R7 /* hi(a0*b1) */ + ADDC R6, R12 /* c1 += lo(a0*b1) */ + ADDE R7, R13 /* c2 += hi(a0*b1) + carry */ + + MULHWU R9, R4, R7 /* hi(a0*b0) */ + ADDC R7, R12 /* c1 += hi(a0*b0) */ + ADDE R0, R13 /* c2 += carry */ + + MOVW R12, 4(R3) + MOVW R13, 0(R3) + RETURN + +GLOBL mach0+0(SB), $MACHSIZE +GLOBL spltbl+0(SB), $4 +GLOBL intrtbl+0(SB), $4 +GLOBL isavetbl+0(SB), $4 diff --git a/os/cerf405/main.c b/os/cerf405/main.c new file mode 100644 index 00000000..11e172fc --- /dev/null +++ b/os/cerf405/main.c @@ -0,0 +1,719 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../ip/ip.h" +#include "version.h" + +#define MAXCONF 32 + +extern ulong kerndate; +extern int cflag; +int remotedebug; + +extern int main_pool_pcnt; +extern int heap_pool_pcnt; +extern int image_pool_pcnt; + +char *confname[MAXCONF]; +char *confval[MAXCONF]; +int nconf; + +void addconf(char *, char *); +void eepromscan(void); + +static void +options(void) +{ +// nconf = archconfval(confname, confval, sizeof(confname)); +} + +void +doc(char *m) +{ + USED(m); + iprint("%s...\n", m); +} + +void +idoc(char *m) +{ + uartputs(m, strlen(m)); +} + +static void +poolsizeinit(void) +{ + ulong nb; + + nb = conf.npage*BY2PG; + poolsize(mainmem, (nb*main_pool_pcnt)/100, 0); + poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0); + poolsize(imagmem, (nb*image_pool_pcnt)/100, 1); +} + +static void +serialconsole(void) +{ + char *p; + int port, baud; + + p = getconf("console"); + if(p == nil) + p = "0"; + if(p != nil && !remotedebug){ + port = strtol(p, nil, 0); + baud = 115200; + p = getconf("baud"); + if(p != nil){ + baud = strtol(p, nil, 0); + if(baud < 9600) + baud = 9600; + } + uartspecial(port, baud, &kbdq, &printq, kbdcr2nl); + } +} + +void +main(void) +{ + idoc("machinit...\n"); + machinit(); + idoc("options...\n"); + compiledcr(); + options(); +// archinit(); + quotefmtinstall(); + idoc("confinit...\n"); + confinit(); + xinit(); + poolsizeinit(); + poolinit(); + idoc("trapinit...\n"); + trapinit(); + mmuinit(); + ioinit(); + printinit(); + uartinstall(); + serialconsole(); + pcimapinit(); + eepromscan(); + doc("clockinit"); + clockinit(); + doc("procinit"); + procinit(); + cpuidprint(); + doc("links"); + links(); + doc("chandevreset"); + chandevreset(); + + eve = strdup("inferno"); + + print("\nInferno %s\n", VERSION); + print("Vita Nuova\n"); + print("conf %s (%lud) jit %d\n\n",conffile, kerndate, cflag); + + doc("userinit"); + userinit(); + doc("schedinit"); + schedinit(); +} + +//ccdv=1 cbdv=2 opdv=2 epdv=3 mpdv=1 ppdv=2 + +void +machinit(void) +{ + int n; + + n = m->machno; + memset(m, 0, sizeof(Mach)); + m->machno = n; + m->mmask = 1<machno; + m->cputype = getpvr()>>16; + m->delayloop = 20000; /* initial estimate only; set by clockinit */ + m->speed = 266; /* initial estimate only; set by archinit */ + m->cpuhz = 266333333; + m->vcohz = 799000000; + m->pllhz = 266333333; + m->plbhz = 133166666; + m->opbhz = 66600000; + m->epbhz = 44*MHz; + m->pcihz = 66600000; + m->clockgen = m->cpuhz; /* it's the internal cpu clock */ +} + +void +init0(void) +{ + Osenv *o; + int i; + char buf[2*KNAMELEN]; + + up->nerrlab = 0; + + spllo(); + + if(waserror()) + panic("init0"); + /* + * These are o.k. because rootinit is null. + * Then early kproc's will have a root and dot. + */ + o = up->env; + o->pgrp->slash = namec("#/", Atodir, 0, 0); + cnameclose(o->pgrp->slash->name); + o->pgrp->slash->name = newcname("/"); + o->pgrp->dot = cclone(o->pgrp->slash); + + chandevinit(); + + if(!waserror()){ + ksetenv("cputype", "power", 0); + snprint(buf, sizeof(buf), "power %s", conffile); + ksetenv("terminal", buf, 0); + poperror(); + } + for(i = 0; i < nconf; i++) + if(confname[i][0] != '*'){ + if(!waserror()){ + ksetenv(confname[i], confval[i], 0); + poperror(); + } + } + + poperror(); + disinit("/osinit.dis"); +} + +void +userinit(void) +{ + Proc *p; + Osenv *o; + + p = newproc(); + o = p->env; + + o->fgrp = newfgrp(nil); + o->pgrp = newpgrp(); + o->egrp = newegrp(); + kstrdup(&o->user, eve); + + strcpy(p->text, "interp"); + + /* + * Kernel Stack + */ + p->sched.pc = (ulong)init0; + p->sched.sp = (ulong)p->kstack+KSTACK; + + ready(p); +} + +Conf conf; + +void +addconf(char *name, char *val) +{ + if(nconf >= MAXCONF) + return; + confname[nconf] = name; + confval[nconf] = val; + nconf++; +} + +char* +getconf(char *name) +{ + int i; + + for(i = 0; i < nconf; i++) + if(cistrcmp(confname[i], name) == 0) + return confval[i]; + return 0; +} + +void +confinit(void) +{ + char *p; + int pcnt; + + + if(p = getconf("*kernelpercent")) + pcnt = 100 - strtol(p, 0, 0); + else + pcnt = 0; + + archconfinit(); + + conf.npage = conf.npage0 + conf.npage1; + if(pcnt < 10) + pcnt = 70; + conf.ialloc = (((conf.npage*(100-pcnt))/100)/2)*BY2PG; + + conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5; + conf.nmach = MAXMACH; + +} + +static void +twinkle(void) +{ + if(m->ticks%MS2TK(1000) == 0) + ((Gpioregs*)PHYSGPIO)->or ^= 1<<31; +} + +void (*archclocktick)(void) = twinkle; + +void +exit(int ispanic) +{ + up = 0; + spllo(); + print("cpu %d exiting\n", m->machno); + + /* Shutdown running devices */ + chandevshutdown(); + + delay(1000); + splhi(); + if(ispanic) + for(;;); + archreboot(); +} + +void +reboot(void) +{ + exit(0); +} + +void +halt(void) +{ + print("cpu halted\n"); + microdelay(1000); + for(;;) + ; +} + +/* + * kept in case it's needed for PCI/ISA devices + */ +int +isaconfig(char *class, int ctlrno, ISAConf *isa) +{ + char cc[KNAMELEN], *p; + int i; + + snprint(cc, sizeof cc, "%s%d", class, ctlrno); + p = getconf(cc); + if(p == nil) + return 0; + + isa->nopt = tokenize(p, isa->opt, NISAOPT); + for(i = 0; i < isa->nopt; i++){ + p = isa->opt[i]; + if(cistrncmp(p, "type=", 5) == 0) + isa->type = p + 5; + else if(cistrncmp(p, "port=", 5) == 0) + isa->port = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "irq=", 4) == 0) + isa->irq = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "mem=", 4) == 0) + isa->mem = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "size=", 5) == 0) + isa->size = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "freq=", 5) == 0) + isa->freq = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "dma=", 4) == 0) + isa->dma = strtoul(p+4, &p, 0); + } + return 1; +} + +/* + * Save the mach dependent part of the process state. + */ +void +procsave(Proc*) +{ +} + +void +idlehands(void) +{ + putmsr(getmsr() | MSR_WE | MSR_EE | MSR_CE); /* MSR_DE as well? */ +} + +/* stubs */ +void +setfsr(ulong) +{ +} + +ulong +getfsr() +{ + return 0; +} + +void +setfcr(ulong) +{ +} + +ulong +getfcr() +{ + return 0; +} + +/* + * some of this is possibly ice-cube specific + */ + +enum { + Cpc0Pllmr0= 0xF0, /* PLL mode register 0 */ + Cpc0Boot= 0xF1, /* clock status */ + Cpc0Pllmr1= 0xF4, /* PLL mode register 1 */ + Cpc0Srr= 0xF6, /* PCI soft reset */ + Cpc0PCI= 0xF9, /* PCI control */ +}; +/* +00f0 = 00011101 +00f1 = 00000025 +00f2 = 00000000 +00f3 = 00000000 +00f4 = 8085523e +00f5 = 00000017 +00f6 = 00000000 +ccdv=1 cbdv=2 opdv=2 epdv=3 mpdv=1 ppdv=2 +fbmul=8 fwdva=5 fwdvb=5 tun=257 m=40 +*/ +void +archconfinit(void) +{ + ulong ktop; + + conf.npage0 = (32*1024*1024)/BY2PG; + conf.base0 = 0; + ktop = PGROUND((ulong)end); + ktop = PADDR(ktop) - conf.base0; + conf.npage0 -= ktop/BY2PG; + conf.base0 += ktop; + + {int i; for(i=0xF0; i<=0xF6; i++){iprint("%.4ux = %.8lux\n", i, getdcr(i));}} + { + int ccdv, cbdv, opdv, epdv, mpdv, ppdv; + int fbmul, fwdva, fwdvb, tun; + ulong mr0, mr1; + + mr0 = getdcr(Cpc0Pllmr0); + ccdv = ((mr0>>20)&3)+1; + cbdv = ((mr0>>16)&3)+1; + opdv = ((mr0>>12)&3)+1; + epdv = ((mr0>>8)&3)+2; + mpdv = ((mr0>>4)&3)+1; + ppdv = (mr0&3)+1; + iprint("ccdv=%d cbdv=%d opdv=%d epdv=%d mpdv=%d ppdv=%d\n", + ccdv, cbdv, opdv, epdv, mpdv, ppdv); + mr1 = getdcr(Cpc0Pllmr1); + fbmul = (mr1>>20) & 0xF; + if(fbmul == 0) + fbmul = 16; + fwdva = (mr1>>16) & 7; + if(fwdva == 0) + fwdva = 8; + fwdvb = (mr1>>12) & 7; + if(fwdvb == 0) + fwdvb = 8; + tun = mr0 & 0x3FF; + iprint("fbmul=%d fwdva=%d fwdvb=%d tun=%d m=%d\n", + fbmul, fwdva, fwdvb, tun, fbmul*fwdva); + } +} + +void +archreboot(void) +{ + putevpr(~0); + firmware(0); + for(;;); +} + +void +clockcheck(void) +{ +} + +void +cpuidprint(void) +{ + iprint("PowerPC 405EP pvr=%8.8lux\n", getpvr()); + /* TO DO */ +} + +#include "../port/flashif.h" + +/* + * for devflash.c:/^flashreset + * retrieve flash type, virtual base and length and return 0; + * return -1 on error (no flash) + */ +int +archflashreset(int bank, Flash *f) +{ + switch(bank){ + case 0: + f->type = "AMD29F0x0"; /* not right, but will do for now */ + f->addr = (void*)PHYSFLASH; + f->size = FLASHSIZE; + f->width = 2; + return 0; + case 1: + f->type = "nand"; + f->addr = (void*)PHYSNAND; + f->size = 0; /* done by probe */ + f->width = 1; + return 0; + default: + return -1; + } +} + +void +archflashwp(Flash*, int) +{ +} + +#include "../port/netif.h" +#include "etherif.h" + +enum { + /* EMAC-PHY control, tucked away in CPC0 */ + Cpc0Epctl= 0xF3, /* EMAC-PHY ctl */ + + E0Nf= 1<<31, /* Emac0 noise filter enable */ + E1Nf= 1<<30, /* Emac1 noise filter enable */ + E1pr= 1<<7, /* Emac1 packet reject is active high */ + E0pr= 1<<6, /* Emac 0 packet reject is active high */ + E1rm= 1<<5, /* enable Emac 1 packet removal */ + E0rm= 1<<4, /* enable Emac 0 packet removal */ + E1pci= 1<<1, /* Emac 1 clock source is Tx clock output (loopback) */ + E0pci= 1<<0, /* Emac 0 clock source is Tx clock output (loopback) */ +}; + +int +archether(int ctlno, Ether *ether) +{ + char name[KNAMELEN], *p; + int s; + + if(ctlno > 1) + return -1; + ether->type = "EMAC"; + ether->port = ctlno; + if(ctlno != 0) + snprint(name, sizeof(name), "eth%daddr", ctlno); + else + strcpy(name, "ethaddr"); + p = getconf(name); + if(p == 0){ + iprint("ether%d: no %s in EEPROM env\n", ctlno, name); + return -1; + } + parsemac(ether->ea, p, Eaddrlen); + s = splhi(); + putdcr(Cpc0Epctl, getdcr(Cpc0Epctl) | (ctlno?E1Nf:E0Nf)); + splx(s); + return 1; +} + +enum { + /* UART control */ + Cpc0Ucr= 0xF5, /* UART control register */ + + U0Dc= 1<<21, /* UART0 DMA clear enable */ + U0Dt= 1<<20, /* enable UART0 DMA transmit channel */ + U0Dr= 1<<19, /* enable UART0 DMA receive channel */ + U1Dc= 1<<18, /* UART1 DMA clear enable */ + U1Dt= 1<<17, /* enable UART1 DMA transmit channel */ + U1Dr= 1<<16, /* enable UART1 DMA receive channel */ + U1Div_s= 8, /* UART1 serial clock divisor (shift) */ + U1Stop= 1<<8, + U0Div_s= 0, /* UART0 serial clock divisor (shift) */ + U0Stop= 1<<0, + UDiv_m= 0x7F, /* UARTx divisor mask */ +}; + +static ulong +findserialclock(int rate, ulong *freq) +{ + ulong d, b; + ulong serialclock; + int actual, e, beste, bestd; + + *freq = 0; + if(rate == 0) + return 0; + d = ((m->pllhz+m->opbhz-1)/m->opbhz)*2; /* double to allow for later rounding */ + beste = 0; + bestd = -1; + for(; d<=128; d++){ + serialclock = (2*m->pllhz)/d; + b = ((serialclock+8*rate-1)/(rate*16))>>1; + actual = ((serialclock+8*b-1)/(b*16))>>1; + e = rate-actual; + if(e < 0) + e = -e; + if(bestd < 0 || e < beste || e == beste && (bestd&1) && (d&1)==0){ + beste = e; + bestd = d; + } + } + if(bestd > 0) + *freq = m->pllhz/bestd; + return bestd; +} + +/* + * return a value for UARTn's baud rate generator, and + * set a corresponding divsor in the UARTn clock generator + * (between 2 and 128) + */ +ulong +archuartclock(int n, int rate) +{ + int d, s; + ulong m, freq; + + d = findserialclock(rate, &freq); + if(d <= 0) + d = U0Stop; + m = UDiv_m; + if(n){ + d <<= U1Div_s; + m <<= U1Div_s; + } + s = splhi(); + putdcr(Cpc0Ucr, (getdcr(Cpc0Ucr) & ~m) | d); + splx(s); + return freq; +} + +void +archuartdma(int n, int on) +{ + ulong r; + int s; + + r = n? (U1Dc|U1Dt|U1Dr): (U0Dc|U0Dt|U0Dr); + if(on){ + s = splhi(); + putdcr(Cpc0Ucr, getdcr(Cpc0Ucr) | r); + splx(s); + }else{ + s = splhi(); + putdcr(Cpc0Ucr, getdcr(Cpc0Ucr) & ~r); + splx(s); + } +} + +/* + * boot environment in eeprom + */ + +enum { + EEpromHdr= 8, /* bytes */ + Envsize= 0x400, +}; + +static I2Cdev eedev; +static struct { + uchar buf[Envsize]; + int size; +} bootenv; + +static int +eepromitem(uchar *buf, int lim, ulong *off) +{ + int l; + uchar b; + + if(i2crecv(&eedev, &b, 1, (*off)++) != 1) + return -1; + l = b; + if(l & 0x80){ + if(i2crecv(&eedev, &b, 1, (*off)++) != 1) + return -1; + l = ((l & 0x7F)<<8) | b; + } + if(buf == nil) + return l; + if(l > lim) + l = lim; + return i2crecv(&eedev, buf, l, *off); +} + +void +eepromscan(void) +{ + int n, l; + ulong off; + uchar buf[2]; + char *p, *ep, *v; + + eedev.addr = 0x50; + eedev.salen = 2; + i2csetup(1); + n = i2crecv(&eedev, buf, sizeof(buf), 0); + if(n <= 0){ + iprint("eepromscan: %d\n", n); + return; + } + if(buf[0] != 0xEF || buf[1] != 0xBE){ + iprint("eeprom invalid\n"); + return; + } + bootenv.size = 0; + for(off = EEpromHdr; off < 16384;){ + l = eepromitem(bootenv.buf, sizeof(bootenv.buf), &off); /* key */ + if(l <= 0) + break; + off += l; + if(l == 7 && memcmp(bootenv.buf, "PPCBOOT", 7) == 0){ /* intrinsyc key */ + bootenv.size = eepromitem(bootenv.buf, sizeof(bootenv.buf), &off); + break; + } + l = eepromitem(nil, 0, &off); /* skip value */ + if(l < 0) + break; + off += l+2; /* 2 byte crc */ + } + p = (char*)bootenv.buf+4; /* skip crc */ + ep = p+bootenv.size; + for(; p < ep && *p; p += l){ + l = strlen(p)+1; + v = strchr(p, '='); + if(v != nil) + *v++ = 0; + else + v = ""; + addconf(p, v); + if(0) + iprint("%q = %q\n", p, v); + } +} + +ulong +logfsnow(void) +{ + return rtctime(); +} diff --git a/os/cerf405/mal.c b/os/cerf405/mal.c new file mode 100644 index 00000000..8a5018d3 --- /dev/null +++ b/os/cerf405/mal.c @@ -0,0 +1,334 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +/* + * on the 405EP the MAL is used only by the Ethernet + * but we keep it separate even so + */ + +enum { + Nrxchan= 2, + Ntxchan= 4, + Maxchan = 4 +}; + +enum { + /* device control registers */ + Cfg= 0x180, /* configuration register */ + Esr= 0x181, /* error status register */ + Ier= 0x182, /* interrupt enable register */ + Txcasr= 0x184, /* transmit channel active set register */ + Txcarr= 0x185, /* transmit channel active reset register */ + Txeobisr= 0x186, /* transmit end of buffer interrupt status register */ + Txdeir= 0x187, /* transmit descriptor error interrupt register */ + Rxcasr= 0x190, /* receive channel active set register */ + Rxcarr= 0x191, /* receive channel active reset register */ + Rxeobisr= 0x192, /* receive channel descriptor error interrupt register */ + Rxdeir= 0x193, /* receive descriptor error interrupt register */ +}; + +#define TXCTPR(n) (0x1A0+(n)) /* transmit channel table pointer register */ +#define RXCTPR(n) (0x1C0+(n)) /* receive channel table pointer register */ +#define RCBS(n) (0x1E0+(n)) /* receive channel buffer size register */ + +enum { + /* configuration */ + CfgSr= 1<<31, /* software reset */ + CfgPlbp0= 0<<22, /* PLB priority (0=lowest) */ + CfgPlbp1= 1<<22, + CfgPlbp2= 2<<22, + CfgPlbp3= 3<<22, + CfgGa= 1<<21, /* guarded */ + CfgOa= 1<<20, /* ordered */ + CfgPlble= 1<<19, /* lock error */ + CfgPlbt_f= 0xF<<15, /* latency timer field */ + CfgPlbt_s= 15, /* latency timer (shift) */ + CfgPlbb= 1<<14, /* burst enable */ + CfgOpbbl= 1<<7, /* OPB locked */ + CfgOepie= 1<<2, /* interrupt on every end of packet */ + CfgLea= 1<<1, /* locked error active */ + CfgSd= 1<<0, /* scroll to next packet on early termination */ + + /* error status */ + EsrEvb= 1<<31, /* error valid bit */ + EsrCid_f= 0x7F<<25, /* field: channel ID causing lock error */ + EsrDe= 1<<20, /* descriptor error */ + EsrOne= 1<<19, /* OPB non-fullword error */ + EsrOte= 1<<18, /* OPB timeout error */ + EsrOse= 1<<17, /* OPB slave error */ + EsrPein= 1<<16, /* PLB bus error indication */ + EsrDei= 1<<4, /* descriptor error interrupt */ + EsrOnei= 1<<3, /* OPB non-fulword error interrupt */ + EsrOtei= 1<<2, /* OPB timeout error interrupt */ + EsrOsei= 1<<1, /* OPB slave error interrupt */ + EsrPbei= 1<<0, /* OPB bus error interrupt */ + +}; + +typedef struct Malmem Malmem; +struct Malmem { + Lock; + BD* base; + BD* limit; + BD* avail; +}; + +static Malmem malmem; + +static Mal* malchans[2][Maxchan]; + +static void +errorintr(Ureg*, void*) +{ + ulong esr, rxdeir, txdeir; + + /* mal de tête */ + esr = getdcr(Esr); + txdeir = getdcr(Txdeir); + rxdeir = getdcr(Rxdeir); + iprint("mal: esr=%8.8lux txdeir=%8.8lux rxdeir=%8.8lux\n", esr, txdeir, rxdeir); + putdcr(Rxdeir, rxdeir); + putdcr(Txdeir, txdeir); + putdcr(Esr, esr); +} + +static void +scanintr(Ureg *ur, ulong ir, Mal *chans[]) +{ + Mal *ml; + int i; + + for(i=0; ir != 0 && i < Maxchan; i++) + if(ir & IBIT(i)){ + ir &= ~IBIT(i); + ml = chans[i]; + if(ml != nil && ml->interrupt != nil) + ml->interrupt(ur, ml->arg); + /* unexpected interrupt otherwise */ + } +} + +static void +txinterrupt(Ureg *ur, void*) +{ + ulong ir; + + ir = getdcr(Txeobisr); + putdcr(Txeobisr, ir); + scanintr(ur, ir, malchans[1]); +} + +static void +rxinterrupt(Ureg *ur, void*) +{ + ulong ir; + + ir = getdcr(Rxeobisr); + putdcr(Rxeobisr, ir); + scanintr(ur, ir, malchans[0]); +} + +void +ioinit(void) +{ + int i; + + putdcr(Txcarr, ~0); + putdcr(Rxcarr, ~0); + + /* reset */ + putdcr(Cfg, CfgSr); + while(getdcr(Cfg) & CfgSr) + ; /* at most one system clock */ + + /* clear these out whilst we're at it */ + for(i=0; in = n; + ml->tx = tx; + ml->len = 1; + ml->arg = arg; + ml->interrupt = intr; + return ml; +} + +void +maltxreset(Mal *ml) +{ + putdcr(Txcarr, IBIT(ml->n)); +} + +void +maltxinit(Mal *ml, Ring *r) +{ + putdcr(TXCTPR(ml->n), PADDR(r->tdr)); +} + +void +maltxenable(Mal *ml) +{ + putdcr(Txcasr, getdcr(Txcasr) | IBIT(ml->n)); +} + +void +malrxreset(Mal *ml) +{ + putdcr(Rxcarr, IBIT(ml->n)); +} + +void +malrxinit(Mal *ml, Ring *r, ulong limit) +{ + putdcr(RXCTPR(ml->n), PADDR(r->rdr)); + putdcr(RCBS(ml->n), limit); +} + +void +malrxenable(Mal *ml) +{ + putdcr(Rxcasr, getdcr(Rxcasr) | IBIT(ml->n)); +} + +/* + * initialise receive and transmit buffer rings + * to use both Emacs, or two channels per emac, we'll need + * to allocate all rx descriptors at once, and all tx descriptors at once, + * in a region where all addresses have the same bits 0-12(!); + * see p 20-34. of the MAL chapter. + * + * the ring entries must be aligned on sizeof(BD) boundaries + * rings must be uncached, and buffers must align with cache lines since the cache doesn't snoop + * + * thus, we initialise it once for all, then hand it out as requested. + */ +void +ioringreserve(int nrx, ulong nrb, int ntx, ulong ntb) +{ + ulong nb, nbd; + + lock(&malmem); + if(malmem.base == nil){ + nbd = nrx*nrb + ntx*ntb; + nb = mmumapsize(nbd*sizeof(BD)); + /* + * the data sheet says in the description of buffer tables that they must be on a 4k boundary, + * but the pointer register descriptions say 8 bytes; it seems to be the latter. + */ + malmem.base = mmucacheinhib(xspanalloc(nb, nb, 1<<19), nb); + malmem.limit = malmem.base + nbd; + malmem.avail = malmem.base; + if((PADDR(malmem.base)&~0x7FFFF) != (PADDR(malmem.base)&~0x7FFFF)) + print("mal: trouble ahead?\n"); + } + unlock(&malmem); + if(malmem.base == nil) + panic("ioringreserve"); +} + +BD* +bdalloc(ulong nd) +{ + BD *b; + + lock(&malmem); + b = malmem.avail; + if(b+nd > malmem.limit) + b = nil; + else + malmem.avail = b+nd; + unlock(&malmem); + return b; +} + +int +ioringinit(Ring* r, int nrdre, int ntdre) +{ + int i; + + /* buffers must align with cache lines since the cache doesn't snoop */ + r->nrdre = nrdre; + if(r->rdr == nil) + r->rdr = bdalloc(nrdre); + if(r->rxb == nil) + r->rxb = malloc(nrdre*sizeof(Block*)); + if(r->rdr == nil || r->rxb == nil) + return -1; + for(i = 0; i < nrdre; i++){ + r->rxb[i] = nil; + r->rdr[i].length = 0; + r->rdr[i].addr = 0; + r->rdr[i].status = BDEmpty|BDInt; + } + r->rdr[i-1].status |= BDWrap; + r->rdrx = 0; + + r->ntdre = ntdre; + if(r->tdr == nil) + r->tdr = bdalloc(ntdre); + if(r->txb == nil) + r->txb = malloc(ntdre*sizeof(Block*)); + if(r->tdr == nil || r->txb == nil) + return -1; + for(i = 0; i < ntdre; i++){ + r->txb[i] = nil; + r->tdr[i].addr = 0; + r->tdr[i].length = 0; + r->tdr[i].status = 0; + } + r->tdr[i-1].status |= BDWrap; + r->tdrh = 0; + r->tdri = 0; + r->ntq = 0; + return 0; +} + +void +dumpmal(void) +{ + int i; + + iprint("Cfg=%8.8lux\n", getdcr(Cfg)); + iprint("Esr=%8.8lux\n", getdcr(Esr)); + iprint("Ier=%8.8lux\n", getdcr(Ier)); + iprint("Txcasr=%8.8lux\n", getdcr(Txcasr)); + iprint("Txcarr=%8.8lux\n", getdcr(Txcarr)); + iprint("Txeobisr=%8.8lux\n", getdcr(Txeobisr)); + iprint("Txdeir=%8.8lux\n", getdcr(Txdeir)); + iprint("Rxcasr=%8.8lux\n", getdcr(Rxcasr)); + iprint("Rxcarr=%8.8lux\n", getdcr(Rxcarr)); + iprint("Rxeobisr=%8.8lux\n", getdcr(Rxeobisr)); + iprint("Rxdeir=%8.8lux\n", getdcr(Rxdeir)); + for(i=0; i */ +#define USER 29 /* R29 is up-> */ + +/* + * Fundamental addresses + */ + +#define UREGSIZE ((8+32)*4) + +/* + * MMU + */ + +/* TLBHI */ +#define TLBEPN(x) ((x) & ~0x3FF) +#define TLB1K (0<<7) +#define TLB4K (1<<7) +#define TLB16K (2<<7) +#define TLB64K (3<<7) +#define TLB256K (4<<7) +#define TLB1MB (5<<7) +#define TLB4MB (6<<7) +#define TLB16MB (7<<7) +#define TLBVALID (1<<6) +#define TLBLE (1<<5) /* little-endian */ +#define TLBU0 (1<<4) /* user-defined attribute */ + +/* TLBLO */ +#define TLBRPN(x) ((x) & ~0x3FF) +#define TLBEX (1<<9) /* execute enable */ +#define TLBWR (1<<8) /* write enable */ +#define TLBZONE(x) ((x)<<4) +#define TLBW (1<<3) /* write-through */ +#define TLBI (1<<2) /* cache inhibit */ +#define TLBM (1<<1) /* memory coherent */ +#define TLBG (1<<0) /* guarded */ + +/* + * Address spaces + */ + +#define KUSEG 0x00000000 +#define KSEG0 0x20000000 +#define KSEG1 0x60000000 /* uncached alias for KSEG0 */ +#define KSEGM 0xE0000000 /* mask to check which seg */ + +#define KZERO KSEG0 /* base of kernel address space */ +#define KTZERO (KZERO+0x3000) /* first address in kernel text */ +#define KSTACK 8192 /* Size of kernel stack */ + +#define OCMZERO 0x40000000 /* on-chip memory (virtual and physical--see p 5-1) */ + +/* + * Exception codes (trap vectors) + */ +#define CRESET 0x01 +#define CMCHECK 0x02 +#define CDSI 0x03 +#define CISI 0x04 +#define CEI 0x05 +#define CALIGN 0x06 +#define CPROG 0x07 +/* 0x08 (fpu) not used */ +/* 0x09 (dec) not used */ +#define CSYSCALL 0x0C +/* 0x0D (trace) not used */ +/* 0x0E (fpa) not used */ +#define CPIT 0x10 +/* FIT is 0x1010 */ +/* WDT is 0x1020 */ +#define CDMISS 0x11 +#define CIMISS 0x12 +#define CDEBUG 0x20 + +/* + * exception syndrome register + */ +#define ESR_MCI 0x80000000 /* instruction machine check */ +#define ESR_PIL 0x08000000 /* program interrupt: illegal instruction */ +#define ESR_PPR 0x04000000 /* program interrupt: privileged */ +#define ESR_PTR 0x02000000 /* program intterupt: trap with successful compare */ +#define ESR_DST 0x00800000 /* data storage interrupt: store fault */ +#define ESR_DIZ 0x00400000 /* data/instruction storage interrupt: zone fault */ +#define ESR_U0F 0x00008000 /* data storage interrupt: u0 fault */ + +#include "physmem.h" + +/* cerf-cube specific */ +#define PHYSDRAM 0 +#define PHYSFLASH 0xFFE00000 +#define FLASHSIZE 0x200000 +#define PHYSNAND 0x60000000 diff --git a/os/cerf405/mkfile b/os/cerf405/mkfile new file mode 100644 index 00000000..f43b9232 --- /dev/null +++ b/os/cerf405/mkfile @@ -0,0 +1,104 @@ +SYSTARG=Inferno +OBJTYPE=power +<../../mkconfig + +#Configurable parameters + +CONF=cerf #default configuration +CONFLIST=cerf +KZERO=0x20003020 + +SYSTARG=$OSTARG +OBJTYPE=power +INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin #path of directory where kernel is installed +#INSTALLDIR=/$OBJTYPE + +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS + +OBJ=\ + l.$O\ + tlb.$O\ + nofp.$O\ + clock.$O\ + compile.$O\ + fpi.$O\ + fpimem.$O\ + fpipower.$O\ + gpio.$O\ + main.$O\ + mal.$O\ + mmu.$O\ + rmap.$O\ + trap.$O\ + $CONF.root.$O\ + $IP\ + $DEVS\ + $ETHERS\ + $LINKS\ + $VGAS\ + $PORT\ + $MISC\ + $OTHERS\ + +LIBNAMES=${LIBS:%=lib%.a} + +HFILES=\ + mem.h\ + physmem.h\ + dat.h\ + fns.h\ + io.h\ + +CFLAGS=-wFV -I. -I../port -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp +KERNDATE=`{$NDATE} + +#default:V: i$CONF.sq +default:V: i${CONF}hd + +i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD -o $target -T$KZERO -l -R4 $OBJ $CONF.$O $LIBFILES + $KSIZE $target + +i$CONF.sq: i$CONF + sqz -w i$CONF >$target + +out=i${CONF}hd + +i${CONF}hd: i$CONF + mkppcimage i$CONF $out + +install:V: i$CONF # i$CONF.sq + cp i$CONF $INSTALLDIR/i$CONF + #cp i$CONF.sq $INSTALLDIR/i$CONF.sq + +uninstall:V: + rm -f $ROOT/$OBJDIR/bin/i$CONF + rm -f $ROOT/$OBJDIR/bin/i$CONF.sq + +<../port/portmkfile + +../init/$INIT.dis: ../init/$INIT.b + cd ../init; mk $INIT.dis + +clock.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +devether.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +faultpower.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +main.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +trap.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h + +devether.$O $ETHERS: etherif.h ../port/netif.h +archipe.$O: screen.h archipe.h + +#$VGAS: screen.h vga.h +$IP devip.$O: ../ip/ip.h + +devboot.$O: devboot.c + $CC $CFLAGS devboot.c + +devuart.$O: devuart.c + $CC $CFLAGS devuart.c diff --git a/os/cerf405/mmu.c b/os/cerf405/mmu.c new file mode 100644 index 00000000..ae0e0be9 --- /dev/null +++ b/os/cerf405/mmu.c @@ -0,0 +1,122 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +extern ulong tlbtab[], tlbtabe[]; +static int tlbx; /* index of next free entry in TLB */ + +enum +{ + /* on-chip memory dcr */ + Isarc= 0x018, /* instruction-side address range compare */ + Iscntl= 0x019, /* instruction-side control register */ + Isen= 1<<31, /* enable */ + Dsarc= 0x01A, /* data-side address range compare register */ + Dscntl= 0x01B, /* data-side control register */ + Dsen= 1<<31, /* enable */ + Dof= 1<<30, /* must be one (p. 5-7) */ +}; + +void +mmuinit(void) +{ + int i; + + /* + * the l.s initial TLB settings do nearly all that is needed initially. + * clear invalid entries (just for clarity) and record the address + * of the first available + */ + tlbx = -1; + for(i = 0; i < 64; i++) + if((tlbrehi(i) & TLBVALID) == 0){ + if(tlbx < 0) + tlbx = i; + tlbwelo(i, 0); + tlbwehi(i, 0); + } + + iprint("ccr0=%8.8lux\n", getccr0()); + + /* + * set OCM mapping, assuming: + * caches were invalidated earlier; + * and we aren't currently using it + * must also set a tlb entry that validates the virtual address but + * the translation is not used (see p. 5-2) + */ + putdcr(Isarc, OCMZERO); + putdcr(Dsarc, OCMZERO); + putdcr(Iscntl, Isen); + putdcr(Iscntl, Dsen|Dof); + tlbwelo(tlbx, OCMZERO|TLBZONE(0)|TLBWR|TLBEX|TLBI); + tlbwehi(tlbx, OCMZERO|TLB4K|TLBVALID); + tlbx++; +} + +int +segflush(void *a, ulong n) +{ + /* flush dcache then invalidate icache */ + dcflush(a, n); + icflush(a, n); + return 0; +} + +/* + * return required size and alignment to map n bytes in a tlb entry + */ +ulong +mmumapsize(ulong n) +{ + ulong size; + int i; + + size = 1024; + for(i = 0; i < 8 && size < n; i++) + size <<= 2; + return size; +} + +/* + * map a physical addresses at pa to va, with the given attributes. + * the virtual address must not be mapped already. + * if va is nil, map it at pa in virtual space. + */ +void* +kmapphys(void *va, ulong pa, ulong nb, ulong attr, ulong le) +{ + int s, i; + ulong size; + + if(va == nil) + va = (void*)pa; /* simplest is to use a 1-1 map */ + size = 1024; + for(i = 0; i < 8 && size < nb; i++) + size <<= 2; + if(i >= 8) + return nil; + s = splhi(); + tlbwelo(tlbx, pa | TLBZONE(0) | attr); + tlbwehi(tlbx, (ulong)va | (i<<7) | TLBVALID | le); + tlbx++; + splx(s); + return va; +} + +/* + * return an uncached alias for the memory at a + */ +void* +mmucacheinhib(void *a, ulong nb) +{ + ulong p; + + if(a == nil) + return nil; + dcflush(a, nb); + p = PADDR(a); + return kmapphys((void*)(KSEG1|p), p, nb, TLBWR | TLBI | TLBG, 0); +} diff --git a/os/cerf405/nand.c b/os/cerf405/nand.c new file mode 100644 index 00000000..5567574c --- /dev/null +++ b/os/cerf405/nand.c @@ -0,0 +1,96 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "flashif.h" + +/* + * Cerf405-specific NAND flash interface + */ + +#define BE(n) (1<<(31-(n))) /* big-endian bit numbering */ + +enum { + /* GPIO lines */ + Gpio_CLE_o_b= 31, + Gpio_ALE_o_b= 30, + Gpio_NCE_o_b= 24, /* CE#, active low */ + Gpio_RDY_i_b= 23, + + /* bit masks */ + Gpio_CLE_o= BE(Gpio_CLE_o_b), + Gpio_ALE_o= BE(Gpio_ALE_o_b), + Gpio_NCE_o= BE(Gpio_NCE_o_b), + Gpio_RDY_i= BE(Gpio_RDY_i_b), + + Gpio_NAND_o= Gpio_CLE_o | Gpio_ALE_o | Gpio_NCE_o, + + CS_NAND= 1, + Gpio_PerCS1_o= BE(10), +}; + +void +archnand_init(Flash*) +{ + gpioreserve(Gpio_NAND_o | Gpio_RDY_i); + gpioset(Gpio_NAND_o, Gpio_NCE_o); + gpioconfig(Gpio_NAND_o, Gpio_out); + gpioconfig(Gpio_RDY_i, Gpio_in); +} + +void +archnand_claim(Flash*, int claim) +{ + gpioset(Gpio_NCE_o, claim? 0: Gpio_NCE_o); +} + +void +archnand_setCLEandALE(Flash*, int cle, int ale) +{ + ulong v; + + v = 0; + if(cle) + v |= Gpio_CLE_o; + if(ale) + v |= Gpio_ALE_o; + gpioset(Gpio_CLE_o | Gpio_ALE_o, v); +} + +/* + * could unroll the loops + */ + +void +archnand_read(Flash *f, void *buf, int len) +{ + uchar *p, *bp; + + p = f->addr; + if(buf != nil){ + bp = buf; + while(--len >= 0) + *bp++ = *p; + }else{ + int junk; + while(--len >= 0){ + junk = *p; + USED(junk); + } + } +} + +void +archnand_write(Flash *f, void *buf, int len) +{ + uchar *p, *bp; + + p = f->addr; + bp = buf; + while(--len >= 0) + *p = *bp++; +} diff --git a/os/cerf405/nofp.s b/os/cerf405/nofp.s new file mode 100644 index 00000000..f23d49b0 --- /dev/null +++ b/os/cerf405/nofp.s @@ -0,0 +1,31 @@ +/* + * stubs when no floating-point hardware + */ + +TEXT kfpinit(SB), $0 + RETURN + +TEXT getfpscr(SB), $8 + MOVW $0, R3 + RETURN + +TEXT fpsave(SB), $0 + RETURN + +TEXT fprestore(SB), $0 + RETURN + +TEXT clrfptrap(SB), $0 + RETURN + +TEXT fpinit(SB), $0 + RETURN + +TEXT fpoff(SB), $0 + RETURN + +TEXT FPsave(SB), 1, $0 + RETURN + +TEXT FPrestore(SB), 1, $0 + RETURN diff --git a/os/cerf405/pci.c b/os/cerf405/pci.c new file mode 100644 index 00000000..30908dc0 --- /dev/null +++ b/os/cerf405/pci.c @@ -0,0 +1,981 @@ +/* + * PCI support code. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define DBG if(0) pcilog + +typedef struct Pcicfg Pcicfg; +struct Pcicfg { + ulong addr; + union { + ulong l; + uchar b[4]; + ushort s[2]; + } data; +}; + +static Pcicfg* pcicfg; +static ulong* pciack; +static ulong* pcimem; + +struct +{ + char output[16384]; + int ptr; +}PCICONS; + +int +pcilog(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + + memmove(PCICONS.output+PCICONS.ptr, buf, n); + PCICONS.ptr += n; + return n; +} + +enum +{ /* configuration mechanism #1 */ + MaxFNO = 7, + MaxUBN = 255, +}; + +enum +{ /* command register */ + IOen = (1<<0), + MEMen = (1<<1), + MASen = (1<<2), + MemWrInv = (1<<4), + PErrEn = (1<<6), + SErrEn = (1<<8), +}; + +static Lock pcicfglock; +static QLock pcicfginitlock; +static int pcicfgmode = -1; +static int pcimaxbno = 7; +static int pcimaxdno; +static Pcidev* pciroot; +static Pcidev* pcilist; +static Pcidev* pcitail; + +static int pcicfgrw32(int, int, int, int); +static int pcicfgrw8(int, int, int, int); + +static char* bustypes[] = { +[BusOPB] "OPB", +[BusPLB] "PLB", +[BusPCI] "PCI", +}; + +#pragma varargck type "T" int + +static int +tbdffmt(Fmt* fmt) +{ + char *p; + int l, r, type, tbdf; + + if((p = malloc(READSTR)) == nil) + return fmtstrcpy(fmt, "(tbdfconv)"); + + switch(fmt->r){ + case 'T': + tbdf = va_arg(fmt->args, int); + type = BUSTYPE(tbdf); + if(type < nelem(bustypes)) + l = snprint(p, READSTR, bustypes[type]); + else + l = snprint(p, READSTR, "%d", type); + snprint(p+l, READSTR-l, ".%d.%d.%d", + BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf)); + break; + + default: + snprint(p, READSTR, "(tbdfconv)"); + break; + } + r = fmtstrcpy(fmt, p); + free(p); + + return r; +} + +ulong +pcibarsize(Pcidev *p, int rno) +{ + ulong v, size; + + v = pcicfgrw32(p->tbdf, rno, 0, 1); + pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0); + size = pcicfgrw32(p->tbdf, rno, 0, 1); + if(v & 1) + size |= 0xFFFF0000; + pcicfgrw32(p->tbdf, rno, v, 0); + + return -(size & ~0x0F); +} + +static int +pcisizcmp(void *a, void *b) +{ + Pcisiz *aa, *bb; + + aa = a; + bb = b; + return aa->siz - bb->siz; +} + +static ulong +pcimask(ulong v) +{ + ulong m; + + m = BI2BY*sizeof(v); + for(m = 1<<(m-1); m != 0; m >>= 1) { + if(m & v) + break; + } + + m--; + if((v & m) == 0) + return v; + + v |= m; + return v+1; +} + +static void +pcibusmap(Pcidev *root, ulong *pmema, ulong *pioa, int wrreg) +{ + Pcidev *p; + int ntb, i, size, rno, hole; + ulong v, mema, ioa, sioa, smema, base, limit; + Pcisiz *table, *tptr, *mtb, *itb; + extern void qsort(void*, long, long, int (*)(void*, void*)); + + ioa = *pioa; + mema = *pmema; + + DBG("pcibusmap wr=%d %T mem=%luX io=%luX\n", + wrreg, root->tbdf, mema, ioa); + + ntb = 0; + for(p = root; p != nil; p = p->link) + ntb++; + + ntb *= (PciCIS-PciBAR0)/4; + table = malloc(2*ntb*sizeof(Pcisiz)); + itb = table; + mtb = table+ntb; + + /* + * Build a table of sizes + */ + for(p = root; p != nil; p = p->link) { + if(p->ccrb == 0x06) { + if(p->ccru == 0x04 && p->bridge != nil) { + sioa = ioa; + smema = mema; + pcibusmap(p->bridge, &smema, &sioa, 0); + + hole = pcimask(smema-mema); + if(hole < (1<<20)) + hole = 1<<20; + p->mema.size = hole; + + hole = pcimask(sioa-ioa); + if(hole < (1<<12)) + hole = 1<<12; + + p->ioa.size = hole; + + itb->dev = p; + itb->bar = -1; + itb->siz = p->ioa.size; + itb++; + + mtb->dev = p; + mtb->bar = -1; + mtb->siz = p->mema.size; + mtb++; + } + if((pcicfgr8(p, PciHDT)&0x7f) != 0) + continue; + } + + for(i = 0; i <= 5; i++) { + rno = PciBAR0 + i*4; + v = pcicfgrw32(p->tbdf, rno, 0, 1); + size = pcibarsize(p, rno); + if(size == 0) + continue; + + if(v & 1) { + itb->dev = p; + itb->bar = i; + itb->siz = size; + itb++; + } + else { + mtb->dev = p; + mtb->bar = i; + mtb->siz = size; + mtb++; + } + + p->mem[i].size = size; + } + } + + /* + * Sort both tables IO smallest first, Memory largest + */ + qsort(table, itb-table, sizeof(Pcisiz), pcisizcmp); + tptr = table+ntb; + qsort(tptr, mtb-tptr, sizeof(Pcisiz), pcisizcmp); + + /* + * Allocate IO address space on this bus + */ + for(tptr = table; tptr < itb; tptr++) { + hole = tptr->siz; + if(tptr->bar == -1) + hole = 1<<12; + ioa = (ioa+hole-1) & ~(hole-1); + + p = tptr->dev; + if(tptr->bar == -1) + p->ioa.bar = ioa; + else { + p->pcr |= IOen; + p->mem[tptr->bar].bar = ioa|1; + if(wrreg) + pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), ioa|1, 0); + } + + ioa += tptr->siz; + } + + /* + * Allocate Memory address space on this bus + */ + for(tptr = table+ntb; tptr < mtb; tptr++) { + hole = tptr->siz; + if(tptr->bar == -1) + hole = 1<<20; + mema = (mema+hole-1) & ~(hole-1); + + p = tptr->dev; + if(tptr->bar == -1) + p->mema.bar = mema; + else { + p->pcr |= MEMen; + p->mem[tptr->bar].bar = mema; + if(wrreg) + pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), mema, 0); + } + mema += tptr->siz; + } + + *pmema = mema; + *pioa = ioa; + free(table); + + if(wrreg == 0) + return; + + /* + * Finally set all the bridge addresses & registers + */ + for(p = root; p != nil; p = p->link) { + if(p->bridge == nil) { + pcicfgrw8(p->tbdf, PciLTR, 64, 0); + + p->pcr |= MASen; + pcicfgrw32(p->tbdf, PciPCR, p->pcr, 0); + continue; + } + + base = p->ioa.bar; + limit = base+p->ioa.size-1; + v = pcicfgrw32(p->tbdf, PciBAR3, 0, 1); + v = (v&0xFFFF0000)|(limit & 0xF000)|((base & 0xF000)>>8); + pcicfgrw32(p->tbdf, PciBAR3, v, 0); + v = (limit & 0xFFFF0000)|(base>>16); + pcicfgrw32(p->tbdf, 0x30, v, 0); + + base = p->mema.bar; + limit = base+p->mema.size-1; + v = (limit & 0xFFF00000)|((base & 0xFFF00000)>>16); + pcicfgrw32(p->tbdf, PciBAR4, v, 0); + + /* + * Disable memory prefetch + */ + pcicfgrw32(p->tbdf, PciBAR5, 0x0000FFFF, 0); + pcicfgrw8(p->tbdf, PciLTR, 64, 0); + + /* + * Enable the bridge + */ + v = 0xFFFF0000 | IOen | MEMen | MASen; + pcicfgrw32(p->tbdf, PciPCR, v, 0); + + sioa = p->ioa.bar; + smema = p->mema.bar; + pcibusmap(p->bridge, &smema, &sioa, 1); + } +} + +static int +pcilscan(int bno, Pcidev** list) +{ + Pcidev *p, *head, *tail; + int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn; + + maxubn = bno; + head = nil; + tail = nil; + for(dno = 0; dno <= pcimaxdno; dno++){ + maxfno = 0; + for(fno = 0; fno <= maxfno; fno++){ + /* + * For this possible device, form the + * bus+device+function triplet needed to address it + * and try to read the vendor and device ID. + * If successful, allocate a device struct and + * start to fill it in with some useful information + * from the device's configuration space. + */ + tbdf = MKBUS(BusPCI, bno, dno, fno); + l = pcicfgrw32(tbdf, PciVID, 0, 1); + if(l == 0xFFFFFFFF || l == 0) + continue; + p = malloc(sizeof(*p)); + p->tbdf = tbdf; + p->vid = l; + p->did = l>>16; + + if(pcilist != nil) + pcitail->list = p; + else + pcilist = p; + pcitail = p; + + p->rid = pcicfgr8(p, PciRID); + p->ccrp = pcicfgr8(p, PciCCRp); + p->ccru = pcicfgr8(p, PciCCRu); + p->ccrb = pcicfgr8(p, PciCCRb); + p->pcr = pcicfgr32(p, PciPCR); + + p->intl = pcicfgr8(p, PciINTL); + + /* + * If the device is a multi-function device adjust the + * loop count so all possible functions are checked. + */ + hdt = pcicfgr8(p, PciHDT); + if(hdt & 0x80) + maxfno = MaxFNO; + + /* + * If appropriate, read the base address registers + * and work out the sizes. + */ + switch(p->ccrb) { + case 0x01: /* mass storage controller */ + case 0x02: /* network controller */ + case 0x03: /* display controller */ + case 0x04: /* multimedia device */ + case 0x06: /* bridge device */ + case 0x07: /* simple comm. controllers */ + case 0x08: /* base system peripherals */ + case 0x09: /* input devices */ + case 0x0A: /* docking stations */ + case 0x0B: /* processors */ + case 0x0C: /* serial bus controllers */ + if((hdt & 0x7F) != 0) + break; + rno = PciBAR0 - 4; + for(i = 0; i < nelem(p->mem); i++) { + rno += 4; + p->mem[i].bar = pcicfgr32(p, rno); + p->mem[i].size = pcibarsize(p, rno); + } + break; + + case 0x00: + case 0x05: /* memory controller */ + default: + break; + } + + if(head != nil) + tail->link = p; + else + head = p; + tail = p; + } + } + + *list = head; + for(p = head; p != nil; p = p->link){ + /* + * Find PCI-PCI bridges and recursively descend the tree. + */ + if(p->ccrb != 0x06 || p->ccru != 0x04) + continue; + + /* + * If the secondary or subordinate bus number is not + * initialised try to do what the PCI BIOS should have + * done and fill in the numbers as the tree is descended. + * On the way down the subordinate bus number is set to + * the maximum as it's not known how many buses are behind + * this one; the final value is set on the way back up. + */ + sbn = pcicfgr8(p, PciSBN); + ubn = pcicfgr8(p, PciUBN); + + if(sbn == 0 || ubn == 0) { + sbn = maxubn+1; + /* + * Make sure memory, I/O and master enables are + * off, set the primary, secondary and subordinate + * bus numbers and clear the secondary status before + * attempting to scan the secondary bus. + * + * Initialisation of the bridge should be done here. + */ + pcicfgw32(p, PciPCR, 0xFFFF0000); + l = (MaxUBN<<16)|(sbn<<8)|bno; + pcicfgw32(p, PciPBN, l); + pcicfgw16(p, PciSPSR, 0xFFFF); + maxubn = pcilscan(sbn, &p->bridge); + l = (maxubn<<16)|(sbn<<8)|bno; + + pcicfgw32(p, PciPBN, l); + } + else { + maxubn = ubn; + pcilscan(sbn, &p->bridge); + } + } + + return maxubn; +} + +int +pciscan(int bno, Pcidev **list) +{ + int ubn; + + qlock(&pcicfginitlock); + ubn = pcilscan(bno, list); + qunlock(&pcicfginitlock); + return ubn; +} + +static void +pcicfginit(void) +{ + char *p; + int bno; + Pcidev **list; + ulong mema, ioa; + + qlock(&pcicfginitlock); + if(pcicfgmode != -1) + goto out; + + //pcimmap(); + + pcicfgmode = 1; + pcimaxdno = 31; + +// fmtinstall('T', tbdffmt); + + if(p = getconf("*pcimaxbno")) + pcimaxbno = strtoul(p, 0, 0); + if(p = getconf("*pcimaxdno")) + pcimaxdno = strtoul(p, 0, 0); + + + list = &pciroot; + for(bno = 0; bno <= pcimaxbno; bno++) { + int sbno = bno; + bno = pcilscan(bno, list); + + while(*list) + list = &(*list)->link; + + if (sbno == 0) { + Pcidev *pci; + + /* + * If we have found a PCI-to-Cardbus bridge, make sure + * it has no valid mappings anymore. + */ + pci = pciroot; + while (pci) { + if (pci->ccrb == 6 && pci->ccru == 7) { + ushort bcr; + + /* reset the cardbus */ + bcr = pcicfgr16(pci, PciBCR); + pcicfgw16(pci, PciBCR, 0x40 | bcr); + delay(50); + } + pci = pci->link; + } + } + } + + if(pciroot == nil) + goto out; + + /* + * Work out how big the top bus is + */ + mema = 0; + ioa = 0; + pcibusmap(pciroot, &mema, &ioa, 0); + + DBG("Sizes: mem=%8.8lux size=%8.8lux io=%8.8lux\n", + mema, pcimask(mema), ioa); + + /* + * Align the windows and map it + */ + ioa = 0x1000; + mema = 0; + + pcilog("Mask sizes: mem=%lux io=%lux\n", mema, ioa); + + pcibusmap(pciroot, &mema, &ioa, 1); + DBG("Sizes2: mem=%lux io=%lux\n", mema, ioa); + +out: + qunlock(&pcicfginitlock); +} + +static int +pcicfgrw8(int tbdf, int rno, int data, int read) +{ + int o, x; + + if(pcicfgmode == -1) + pcicfginit(); + + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + o = rno & 0x03; + rno &= ~0x03; + pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno; + eieio(); + if(read) + x = pcicfg->data.b[o]; /* TO DO: perhaps o^3 */ + else + pcicfg->data.b[o] = data; + eieio(); + pcicfg->addr = 0; + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr8(Pcidev* pcidev, int rno) +{ + return pcicfgrw8(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw8(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw8(pcidev->tbdf, rno, data, 0); +} + +static int +pcicfgrw16(int tbdf, int rno, int data, int read) +{ + int o, x; + + if(pcicfgmode == -1) + pcicfginit(); + + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + o = (rno >> 1) & 1; + rno &= ~0x03; + pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno; + eieio(); + if(read) + x = pcicfg->data.s[o]; + else + pcicfg->data.s[o] = data; + eieio(); + pcicfg->addr = 0; + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr16(Pcidev* pcidev, int rno) +{ + return pcicfgrw16(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw16(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw16(pcidev->tbdf, rno, data, 0); +} + +static int +pcicfgrw32(int tbdf, int rno, int data, int read) +{ + int x; + + if(pcicfgmode == -1) + pcicfginit(); + + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + rno &= ~0x03; + pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno; + eieio(); + if(read) + x = pcicfg->data.l; + else + pcicfg->data.l = data; + eieio(); + pcicfg->addr = 0; + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr32(Pcidev* pcidev, int rno) +{ + return pcicfgrw32(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw32(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw32(pcidev->tbdf, rno, data, 0); +} + +Pcidev* +pcimatch(Pcidev* prev, int vid, int did) +{ + if(pcicfgmode == -1) + pcicfginit(); + + if(prev == nil) + prev = pcilist; + else + prev = prev->list; + + while(prev != nil){ + if((vid == 0 || prev->vid == vid) + && (did == 0 || prev->did == did)) + break; + prev = prev->list; + } + return prev; +} + +Pcidev* +pcimatchtbdf(int tbdf) +{ + Pcidev *pcidev; + + if(pcicfgmode == -1) + pcicfginit(); + + for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) { + if(pcidev->tbdf == tbdf) + break; + } + return pcidev; +} + +uchar +pciipin(Pcidev *pci, uchar pin) +{ + if (pci == nil) + pci = pcilist; + + while (pci) { + uchar intl; + + if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff) + return pci->intl; + + if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0) + return intl; + + pci = pci->list; + } + return 0; +} + +static void +pcilhinv(Pcidev* p) +{ + int i; + Pcidev *t; + + if(p == nil) { + putstrn(PCICONS.output, PCICONS.ptr); + p = pciroot; + print("bus dev type vid did intl memory\n"); + } + for(t = p; t != nil; t = t->link) { + print("%d %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d ", + BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf), + t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl); + + for(i = 0; i < nelem(p->mem); i++) { + if(t->mem[i].size == 0) + continue; + print("%d:%.8lux %d ", i, + t->mem[i].bar, t->mem[i].size); + } + if(t->ioa.bar || t->ioa.size) + print("ioa:%.8lux %d ", t->ioa.bar, t->ioa.size); + if(t->mema.bar || t->mema.size) + print("mema:%.8lux %d ", t->mema.bar, t->mema.size); + if(t->bridge) + print("->%d", BUSBNO(t->bridge->tbdf)); + print("\n"); + } + while(p != nil) { + if(p->bridge != nil) + pcilhinv(p->bridge); + p = p->link; + } +} + +void +pcihinv(Pcidev* p) +{ + if(pcicfgmode == -1) + pcicfginit(); + qlock(&pcicfginitlock); + pcilhinv(p); + qunlock(&pcicfginitlock); +} + +void +pcishutdown(void) +{ + Pcidev *p; + + if(pcicfgmode == -1) + pcicfginit(); + + for(p = pcilist; p != nil; p = p->list){ + /* don't mess with the bridges */ + if(p->ccrb == 0x06) + continue; + pciclrbme(p); + } +} + +void +pcisetbme(Pcidev* p) +{ + int pcr; + + pcr = pcicfgr16(p, PciPCR); + pcr |= MASen; + pcicfgw16(p, PciPCR, pcr); +} + +void +pciclrbme(Pcidev* p) +{ + int pcr; + + pcr = pcicfgr16(p, PciPCR); + pcr &= ~MASen; + pcicfgw16(p, PciPCR, pcr); +} + +/* + * 405EP specific + */ + +typedef struct Pciplbregs Pciplbregs; +struct Pciplbregs { + struct { + ulong la; + ulong ma; + ulong pcila; + ulong pciha; + } pmm[3]; + struct { + ulong ms; + ulong la; + } ptm[2]; +}; + +enum { /* mask/attribute registers */ + Pre= 1<<1, /* enable prefetching (PMM only) */ + Ena= 1<<0, /* enable PLB to PCI map (PMM); enable PCI to PLB (PTM) */ +}; + +enum { /* DCR */ + Cpc0Srr= 0xF6, /* PCI soft reset */ + Rpci= 1<<18, /* reset PCI bridge */ + Cpc0PCI= 0xF9, /* PCI control */ + Spe= 1<<4, /* PCIINT/WE select */ + Hostcfgen= 1<<3, /* enable host config */ + Arben= 1<<1, /* enable internal arbiter */ +}; + +enum { + /* PciPCR */ + Se= 1<<8, /* enable PCISErr# when parity error detected as target */ + Per= 1<<6, /* enable PERR# on parity errors */ + Me= 1<<2, /* enable bridge-to-master cycles */ + Ma= 1<<1, /* enable memory access (when PCI memory target) */ + + /* PciPSR */ + Depe= 1<<15, /* parity error */ + Sse= 1<<14, /* signalled system error */ + Rma= 1<<13, /* received master abort */ + Rta= 1<<12, /* received target abort */ + Sta= 1<<11, /* signalled target abort */ + Dpe= 1<<8, /* data parity error */ + F66C= 1<<5, /* 66MHz capable */ + + /* PciBARn */ + Pf= 1<<3, /* prefetchable */ +}; + +/* +pmm 0: la=00000080 ma=010000c0 pcila=00000080 pciha=00000000 +pmm 1: la=00000000 ma=00000000 pcila=00000000 pciha=00000000 +pmm 2: la=00000000 ma=00000000 pcila=00000000 pciha=00000000 +ptm 0: ms=01000080 la=00000000 +ptm 1: ms=00000000 la=00000000 +*/ + +static void +pcidumpdev(ulong tbdf) +{ + int i; + + for(i=0; i<0x68; i+=4) + iprint("[%.2x]=%.8ux\n", i, pcicfgrw32(tbdf, i, 0, 1)); +} + +void +pcimapinit(void) +{ + Pciplbregs *pm; + int i, bridge, psr; + + pm = kmapphys(nil, PHYSPCIBCFG, sizeof(Pciplbregs), TLBWR | TLBI | TLBG, TLBLE); + if(0){ + /* see what the bootstrap left */ + iprint("PCI:\n"); + for(i=0; i<3; i++) + iprint("pmm %d: la=%.8lux ma=%.8lux pcila=%.8lux pciha=%.8lux\n", + i, pm->pmm[i].la, pm->pmm[i].ma, pm->pmm[i].pcila, pm->pmm[i].pciha); + for(i=0; i<2; i++) + iprint("ptm %d: ms=%.8lux la=%.8lux\n", + i, pm->ptm[i].ms, pm->ptm[i].la); + } + putdcr(Cpc0Srr, Rpci); + delay(1); + putdcr(Cpc0Srr, 0); + + kmapphys((void*)PHYSPCIIO0, PHYSPCIIO0, 64*1024, TLBWR | TLBI | TLBG, TLBLE); + pcicfg = kmapphys(nil, PHYSPCIADDR, sizeof(Pcicfg), TLBWR | TLBI | TLBG, TLBLE); + pciack = kmapphys(nil, PHYSPCIACK, sizeof(ulong), TLBWR | TLBI | TLBG, TLBLE); + eieio(); + + /* + * PLB addresses between PHYSPCIBRIDGE and PHYSPCIBRIDGE+64Mb + * are mapped to PCI memory at 0. + */ + pm->pmm[0].ma = 0; /* disable during update */ + eieio(); + pm->pmm[0].la = PHYSPCIBRIDGE; + pm->pmm[0].pcila = 0; + pm->pmm[0].pciha = 0; + eieio(); + pm->pmm[0].ma = 0xFC000000 | Ena; /* enable prefetch? */ + for(i=1; i<3; i++) + pm->pmm[i].ma = 0; /* disable the others */ + eieio(); + pcimem = kmapphys((void*)PHYSPCIBRIDGE, PHYSPCIBRIDGE, 0x4000000, TLBWR | TLBI | TLBG, TLBLE); + + /* + * addresses presented by a PCI device between PCIWINDOW and PCIWINDOW+1Gb + * are mapped to physical memory. + */ + pm->ptm[0].ms = 0; + eieio(); + pm->ptm[0].la = PCIWINDOW; + eieio(); + pm->ptm[0].ms = 0xC0000000 | Ena; /* always enabled by hardware */ + pm->ptm[1].ms = 0; + eieio(); + + iprint("cpc0pci=%.8lux\n", getdcr(Cpc0PCI)); + + /* + * the 405ep's pci bridge contains IBM vendor & devid, but + * ppcboot rather annoyingly clears them; put them back. + */ + pcicfgmode = 1; + pcimaxdno = 31; + + bridge = MKBUS(BusPCI, 0, 0, 0); + pcicfgrw16(bridge, PciPCR, Me | Ma, 0); + pcicfgrw16(bridge, PciVID, 0x1014, 0); + pcicfgrw16(bridge, PciDID, 0x0156, 0); + pcicfgrw8(bridge, PciCCRb, 0x06, 0); /* bridge */ + pcicfgrw32(bridge, PciBAR1, Pf, 0); + psr = pcicfgrw16(bridge, PciPSR, 0, 1); + if(m->pcihz >= 66000000) + psr |= F66C; /* 66 MHz */ + pcicfgrw16(bridge, PciPSR, psr, 0); /* reset error status */ + if(0){ + iprint("pci bridge':\n"); + pcidumpdev(bridge); + } + + pcicfgmode = -1; +} diff --git a/os/cerf405/physmem.h b/os/cerf405/physmem.h new file mode 100644 index 00000000..9fde7920 --- /dev/null +++ b/os/cerf405/physmem.h @@ -0,0 +1,22 @@ +/* + * Memory-mapped IO + */ + +#define PHYSPCIBRIDGE 0x80000000 +#define PHYSMMIO 0xEF600000 +#define MMIO(i) (PHYSMMIO+(i)*0x100) +#define PHYSGPT MMIO(0) +#define PHYSUART0 MMIO(3) +#define PHYSUART1 MMIO(4) +#define PHYSIIC MMIO(5) +#define PHYSOPB MMIO(6) +#define PHYSGPIO MMIO(7) +#define PHYSEMAC0 MMIO(8) +#define PHYSEMAC1 MMIO(9) + +#define PHYSPCIIO0 0xE8000000 /* for 64M */ +#define PHYSPCIMEM 0x80000000 +#define PHYSPCIADDR 0xEEC00000 /* for 8 bytes */ +#define PHYSPCIDATA 0xEEC00004 +#define PHYSPCIACK 0xEED00000 /* interrupt acknowledge */ +#define PHYSPCIBCFG 0xEF400000 /* bridge configuration registers */ diff --git a/os/cerf405/powerbreak.c b/os/cerf405/powerbreak.c new file mode 100644 index 00000000..fc5970ef --- /dev/null +++ b/os/cerf405/powerbreak.c @@ -0,0 +1,123 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "ureg.h" + +extern int (*breakhandler)(Ureg *ur, Proc*); /* trap.c */ +extern Instr BREAK; /* trap.c */ +extern void portbreakinit(void); + +#define getop(i) ((i>>26)&0x3F) +#define getxo(i) ((i>>1)&0x3FF) +#define getbobi(i) bo = (i>>21)&0x1f; bi = (i>>16)&0x1f; xx = (i>>11)&0x1f; + +void +machbreakinit(void) +{ + portbreakinit(); + breakhandler = breakhit; +} + +Instr +machinstr(ulong addr) +{ + if (addr < KTZERO) + error(Ebadarg); + return *(Instr*)addr; +} + +void +machbreakset(ulong addr) +{ + if (addr < KTZERO) + error(Ebadarg); + *(Instr*)addr = BREAK; + segflush((void*)addr, sizeof(Instr)); +} + +void +machbreakclear(ulong addr, Instr i) +{ + if (addr < KTZERO) + error(Ebadarg); + *(Instr*)addr = i; + segflush((void*)addr, sizeof(Instr)); +} + +static int +condok(Ureg *ur, ulong ir, int ctrok) +{ + int bo, bi, xx; + ulong ctrval; + + ctrval = ur->ctr; + getbobi(ir); + if(xx) + return 0; /* illegal */ + if((bo & 0x4) == 0) { + if(!ctrok) + return 0; /* illegal */ + ctrval--; + } + if(bo & 0x4 || (ctrval!=0)^((bo>>1)&1)) { + if(bo & 0x10 || (((ur->cr & (1L<<(31-bi))!=0)==((bo>>3)&1)))) + return 1; + } + return 0; +} + +/* + * Return the address of the instruction that will be executed after the + * instruction at ur->pc, accounting for current branch conditions. + */ +ulong +machnextaddr(Ureg *ur) +{ + long imm; + ulong ir; + + ir = *(ulong*)ur->pc; + switch(getop(ir)) { + case 18: /* branch */ + imm = ir & 0x03FFFFFC; + if(ir & 0x02000000) + imm |= 0xFC000000; /* sign extended */ + if((ir & 2) == 0) /* relative address */ + return ur->pc + imm; + return imm; + + case 16: /* conditional branch */ + if(condok(ur, ir&0xFFFF0000, 1)){ + imm = ir & 0xFFFC; + if(ir & 0x08000) + imm |= 0xFFFF0000; /* sign extended */ + if((ir & 2) == 0) /* relative address */ + return ur->pc + imm; + return imm; + } + break; + + case 19: /* conditional branch to register */ + switch(getxo(ir)){ + case 528: /* bcctr */ + if(condok(ur, ir, 0)) + return ur->ctr & ~3; + break; + case 16: /* bclr */ + if(condok(ur, ir, 1)) + return ur->lr & ~3; + break; + } + break; + } + return ur->pc+4; /* next instruction */ +} + +int +isvalid_va(void *v) +{ + return (ulong)v >= KTZERO; +} diff --git a/os/cerf405/rmap.c b/os/cerf405/rmap.c new file mode 100644 index 00000000..f521c24d --- /dev/null +++ b/os/cerf405/rmap.c @@ -0,0 +1,106 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +void +mapinit(RMap *rmap, Map *map, int size) +{ + lock(rmap); + rmap->map = map; + rmap->mapend = map+(size/sizeof(Map)); + unlock(rmap); +} + +void +mapfree(RMap* rmap, ulong addr, int size) +{ + Map *mp; + ulong t; + + if(size <= 0) + return; + + lock(rmap); + for(mp = rmap->map; mp->addr <= addr && mp->size; mp++) + ; + + if(mp > rmap->map && (mp-1)->addr+(mp-1)->size == addr){ + (mp-1)->size += size; + if(addr+size == mp->addr){ + (mp-1)->size += mp->size; + while(mp->size){ + mp++; + (mp-1)->addr = mp->addr; + (mp-1)->size = mp->size; + } + } + } + else{ + if(addr+size == mp->addr && mp->size){ + mp->addr -= size; + mp->size += size; + } + else do{ + if(mp >= rmap->mapend){ + print("mapfree: %s: losing 0x%luX, %d\n", + rmap->name, addr, size); + break; + } + t = mp->addr; + mp->addr = addr; + addr = t; + t = mp->size; + mp->size = size; + mp++; + }while(size = t); + } + unlock(rmap); +} + +ulong +rmapalloc(RMap* rmap, ulong addr, int size, int align) +{ + Map *mp; + ulong maddr, oaddr; + + lock(rmap); + for(mp = rmap->map; mp->size; mp++){ + maddr = mp->addr; + + if(addr){ + if(maddr > addr) + break; + if(maddr+mp->size < addr) + continue; + if(addr+size > maddr+mp->size) + break; + maddr = addr; + } + + if(align > 0) + maddr = ((maddr+align-1)/align)*align; + if(mp->addr+mp->size-maddr < size) + continue; + + oaddr = mp->addr; + mp->addr = maddr+size; + mp->size -= maddr-oaddr+size; + if(mp->size == 0){ + do{ + mp++; + (mp-1)->addr = mp->addr; + }while((mp-1)->size = mp->size); + } + + unlock(rmap); + if(oaddr != maddr) + mapfree(rmap, oaddr, maddr-oaddr); + + return maddr; + } + unlock(rmap); + + return 0; +} diff --git a/os/cerf405/tlb.s b/os/cerf405/tlb.s new file mode 100644 index 00000000..98abd3fd --- /dev/null +++ b/os/cerf405/tlb.s @@ -0,0 +1,30 @@ +#include "mem.h" +#define MB (1024*1024) + +/* + * TLB prototype entries, loaded once-for-all at startup, + * remaining unchanged thereafter. + * Limit the table size to ensure it fits in small TLBs. + */ +#define TLBE(hi, lo) WORD $(hi); WORD $(lo) + +TEXT tlbtab(SB), $-4 + + /* tlbhi tlblo */ + + /* DRAM, 32MB */ + TLBE(KZERO|PHYSDRAM|TLB16MB|TLBVALID, PHYSDRAM|TLBZONE(0)|TLBWR|TLBEX) + TLBE(KZERO|(PHYSDRAM+16*MB)|TLB16MB|TLBVALID, (PHYSDRAM+16*MB)|TLBZONE(0)|TLBWR|TLBEX) + + /* memory-mapped IO, 4K */ + TLBE(PHYSMMIO|TLB4K|TLBVALID, PHYSMMIO|TLBZONE(0)|TLBWR|TLBI|TLBG) + + /* NAND flash access (4K?) */ + TLBE(PHYSNAND|TLB4K|TLBVALID, PHYSNAND|TLBZONE(0)|TLBWR|TLBI|TLBG) + + /* NOR flash, 2MB */ + TLBE(PHYSFLASH|TLB1MB|TLBVALID, PHYSFLASH|TLBZONE(0)|TLBWR|TLBEX) + TLBE((PHYSFLASH+MB)|TLB1MB|TLBVALID, (PHYSFLASH+MB)|TLBZONE(0)|TLBWR|TLBEX) + +TEXT tlbtabe(SB), $-4 + RETURN diff --git a/os/cerf405/trap.c b/os/cerf405/trap.c new file mode 100644 index 00000000..846cc097 --- /dev/null +++ b/os/cerf405/trap.c @@ -0,0 +1,581 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" +#include "io.h" +#include "../port/error.h" + +enum +{ + Maxhandler= MaxVector /* max number of interrupt handlers, assuming none shared */ +}; + +enum { + /* UIC registers (p. 10-4) */ + Usr = 0xC0, /* status */ + Uer = 0xC2, /* enable */ + Ucr = 0xC3, /* critical interrupts */ + Upr = 0xC4, /* priority */ + Utr = 0xC5, /* trigger */ + Umsr = 0xC6, /* masked status */ + Uvr = 0xC7, /* vector */ + Uvcr = 0xC8, /* vector configuration */ +}; + +typedef struct Handler Handler; +struct Handler +{ + void (*r)(Ureg*, void*); + void *arg; + char name[KNAMELEN]; + Handler *next; + int edge; + ulong nintr; + ulong ticks; + int maxtick; +}; + +static Lock veclock; + +static struct +{ + Handler *ivec[MaxVector]; + Handler h[Maxhandler]; + int free; + Handler* freelist; +} halloc; + +Instr BREAK = 0x7fe00008; +int (*breakhandler)(Ureg*, Proc*); + +void kernfault(Ureg*, int); + +char *excname[] = +{ + "reserved 0", + "system reset", + "machine check", + "data access", + "instruction access", + "external interrupt", + "alignment", + "program exception", + "floating-point unavailable", + "decrementer", + "i/o controller interface error", + "reserved B", + "system call", + "trace trap", + "floating point assist", + "reserved F", + "software emulation", + "ITLB miss", + "DTLB miss", + "ITLB error", + "DTLB error", + "reserved 15", + "reserved 16", + "reserved 17", + "reserved 18", + "reserved 19", + "reserved 1A", + "reserved 1B", + "data breakpoint", + "instruction breakpoint", + "peripheral breakpoint", + "development port", + /* the following are made up on a program exception */ + "floating point exception", /* 20: FPEXC */ + "illegal instruction", /* 21 */ + "privileged instruction", /* 22 */ + "trap", /* 23 */ + "illegal operation", /* 24 */ + "breakpoint", /* 25 */ +}; + +char *fpcause[] = +{ + "inexact operation", + "division by zero", + "underflow", + "overflow", + "invalid operation", +}; +char *fpexcname(Ureg*, ulong, char*); +#define FPEXPMASK 0xfff80300 /* Floating exception bits in fpscr */ + +char *regname[]={ + "CAUSE", "SRR1", + "PC", "GOK", + "LR", "CR", + "XER", "CTR", + "R0", "R1", + "R2", "R3", + "R4", "R5", + "R6", "R7", + "R8", "R9", + "R10", "R11", + "R12", "R13", + "R14", "R15", + "R16", "R17", + "R18", "R19", + "R20", "R21", + "R22", "R23", + "R24", "R25", + "R26", "R27", + "R28", "R29", + "R30", "R31", +}; + +void +sethvec(int v, void (*r)(void)) +{ + ulong *vp, pa, o; + + vp = (ulong*)KADDR(v); + vp[0] = 0x7c1043a6; /* MOVW R0, SPR(SPRG0) */ + vp[1] = 0x7c0802a6; /* MOVW LR, R0 */ + vp[2] = 0x7c1243a6; /* MOVW R0, SPR(SPRG2) */ + pa = PADDR(r); + o = pa >> 25; + if(o != 0 && o != 0x7F){ + /* a branch too far: running from ROM */ + vp[3] = (15<<26)|(pa>>16); /* MOVW $r&~0xFFFF, R0 */ + vp[4] = (24<<26)|(pa&0xFFFF); /* OR $r&0xFFFF, R0 */ + vp[5] = 0x7c0803a6; /* MOVW R0, LR */ + vp[6] = 0x4e800021; /* BL (LR) */ + }else + vp[3] = (18<<26)|(pa&0x3FFFFFC)|3; /* bla */ + dcflush(vp, 8*sizeof(ulong)); +} + +void +sethvec2(int v, void (*r)(void)) +{ + ulong *vp; + + vp = (ulong*)KADDR(v); + vp[0] = (18<<26)|((ulong)r&~KSEGM)|2; /* ba */ + dcflush(vp, sizeof(*vp)); +} + +static void +faultpower(Ureg *ur, ulong addr, int read) +{ + char buf[ERRMAX]; + + if(up == nil){ + dumpregs(ur); + panic("kernel fault"); + } + + up->dbgreg = ur; /* For remote ACID */ + spllo(); + + sprint(buf, "trap: fault %s pc=0x%lux addr=0x%lux", + read? "read": "write", ur->pc, addr); + if(up->type == Interp){ + if(addr == ~0) + disfault(ur, "dereference of nil"); + disfault(ur, buf); + } + dumpregs(ur); + panic("fault: %s\n", buf); +} + +void +trap(Ureg *ur) +{ + int ecode, s; + ulong w, esr; + char buf[ERRMAX]; + + ecode = ur->cause >> 8; + if(ecode < 0 || ecode >= 0x1F) + ecode = 0x1F; + esr = getesr(); + putesr(0); + switch(ecode){ + case CPIT: + clockintr(ur); + preemption(1); + break; + + case CMCHECK: + if(esr & ESR_MCI){ + faultpower(ur, ur->pc, 1); + break; + } + /* FALL THROUGH */ + case CDSI: + faultpower(ur, getdear(), !(esr&ESR_DST)); + break; + + case CDMISS: + faultpower(ur, getdear(), 1); + break; + + case CISI: + case CIMISS: + faultpower(ur, ur->pc, 1); + break; + + case CPROG: + if(esr & ESR_PIL){ + if(up == nil) + goto Default; + if((ulong)(ur+1) != ur->r1) + panic("fp emu stack"); + spllo(); + if(waserror()){ + if(up->type == Interp) + disfault(ur, up->env->errstr); + panic("%s", up->env->errstr); + } + if(fpipower(ur) == 0){ + splhi(); + poperror(); + print("pc=#%lux op=#%8.8lux\n", ur->pc, *(ulong*)ur->pc); /* temporary */ + goto Default; + } + poperror(); + break; + } + /* TO DO: 4xx variant for the following */ + if(ur->status & (1<<19)) { + ecode = 0x20; + w = ur->pc; + if(ur->status & (1<<16)) + w += 4; + if(*(ulong*)w == 0x7fe00008){ /* tw 31,0,0 */ + if(breakhandler){ + s = (*breakhandler)(ur, up); + if(s == BrkSched){ + if(up){ + up->preempted = 0; + sched(); + splhi(); + } + }else if(s == BrkNoSched){ + if(up){ + up->preempted = 1; /* stop it being preempted until next instruction */ + up->dbgreg = 0; + } + } + break; + } + ecode = 0x1D; /* breakpoint */ + } + } + if(ur->status & (1<<18)) + ecode = 0x21; + if(ur->status & (1<<17)) + ecode = 0x22; + /* FALL THROUGH */ + + Default: + default: + if(up && up->type == Interp) { + spllo(); + snprint(buf, sizeof buf, "sys: trap: %s pc=0x%lux", excname[ecode], ur->pc); + error(buf); + break; + } + print("kernel %s pc=0x%lux\n", excname[ecode], ur->pc); + dumpregs(ur); + dumpstack(); + if(m->machno == 0) + spllo(); + exit(1); + } + + splhi(); +} + +void +trapinit(void) +{ + int i; + + putdcr(Uer, 0); /* none enabled */ + putdcr(Ucr, 0); /* none are critical by default */ + putdcr(Upr, ~IBIT(VectorPCISERR)); /* default is active high (except PCISERR) */ + putdcr(Utr, 0); /* all are level sensitive by default */ + putdcr(Usr, getdcr(Usr)); /* reset interrupts */ + putdcr(Uvcr, 0); /* 31 is highest priority */ + eieio(); + + /* + * set all exceptions to trap + */ + for(i = 0x0; i < 0x3000; i += 0x100) + sethvec(i, trapvec); + + /* on the 405, several traps are critical interrupts with different SRRs */ + sethvec(0x0100, trapcvec); + sethvec(0x0200, trapcvec); + + sethvec(CEI<<8, intrvec); + /* TO DO: FIT and WDT */ + //sethvec2(CIMISS<<8, itlbmiss); + //sethvec2(CDMISS<<8, dtlbmiss); + + putevpr(0); /* use our vectors */ +} + +void +intrenable(int v, void (*r)(Ureg*, void*), void *arg, int, char *name) +{ + Handler *h; + ulong w, f, bit; + + f = v; + v &= IRQmask; + bit = IBIT(v); + if(v < 0 || v >= nelem(halloc.ivec)) + panic("intrenable(%d)", v); + ilock(&veclock); + if((h = halloc.freelist) == nil){ + if(halloc.free >= Maxhandler){ + iunlock(&veclock); + panic("out of interrupt handlers"); + } + h = &halloc.h[halloc.free++]; + }else + halloc.freelist = h->next; + h->r = r; + h->arg = arg; + strncpy(h->name, name, KNAMELEN-1); + h->name[KNAMELEN-1] = 0; + h->next = halloc.ivec[v]; + halloc.ivec[v] = h; + + /* + * enable corresponding interrupt in UIC + */ + w = getdcr(Ucr); + if(f & IRQcritical) + putdcr(Ucr, w | bit); + else + putdcr(Ucr, w & ~bit); + if(v >= VectorIRQ){ + /* (only) these have got choice of polarity, etc. */ + w = getdcr(Utr); + h->edge = (f & IRQedge) != 0; + if(h->edge) + putdcr(Utr, w | bit); + else + putdcr(Utr, w & ~bit); + w = getdcr(Upr); + if(f & IRQactivelow) + putdcr(Upr, w | bit); + else + putdcr(Upr, w & ~bit); + } + eieio(); + putdcr(Uer, getdcr(Uer) | bit); + eieio(); + iunlock(&veclock); +} + +static void +irqdisable(int v) +{ + putdcr(Uer, getdcr(Uer) & ~IBIT(v)); +} + +void +intrdisable(int v, void (*r)(Ureg*, void*), void *arg, int, char *name) +{ + Handler *h, **hp; + + v &= IRQmask; + if(v < 0 || v >= nelem(halloc.ivec)) + panic("intrdisable(%d)", v); + ilock(&veclock); + for(hp = &halloc.ivec[v]; (h = *hp) != nil; hp = &h->next) + if(h->r == r && h->arg == arg && strcmp(h->name, name) == 0){ + *hp = h->next; + h->next = halloc.freelist; + halloc.freelist = h; + break; + } + if(halloc.ivec[v] == nil) + irqdisable(v); + iunlock(&veclock); +} + +/* + * called directly by l.s:/intrvec. on a multiprocessor we'd need to lock veclock. + */ +void +intr(Ureg *ur) +{ + ulong msr, b; + int v; + Handler *h; + long t0; + Proc *oup; + + oup = up; + up = nil; /* no process at interrupt level */ + while((msr = getdcr(Umsr)) != 0){ + for(v=0; msr!=0 && v<32; v++){ + b = IBIT(v); + if((msr & b) == 0) + continue; + msr &= ~b; + ur->cause = (CEI<<8) | v; + h = halloc.ivec[v]; + if(h == nil){ + iprint("unknown interrupt %d pc=0x%lux\n", v, ur->pc); + irqdisable(v); + continue; + } + + /* + * call the interrupt handlers + */ + do { + if(h->edge) + putdcr(Usr, b); + h->nintr++; + t0 = getpit(); + (*h->r)(ur, h->arg); + t0 -= getpit(); + h->ticks += t0; + if(h->maxtick < t0) + h->maxtick = t0; + if(!h->edge) + putdcr(Usr, b); + h = h->next; + } while(h != nil); + } + } + up = oup; + preemption(0); +} + +int +intrstats(char *buf, int bsize) +{ + Handler *h; + int i, n; + + n = 0; + for(i=0; inintr) + n += snprint(buf+n, bsize-n, "%3d %lud %lud %ud\n", i, h->nintr, h->ticks, h->maxtick); + return n; +} + +char* +fpexcname(Ureg *ur, ulong fpscr, char *buf) +{ + int i; + char *s; + ulong fppc; + + fppc = ur->pc; + s = 0; + fpscr >>= 3; /* trap enable bits */ + fpscr &= (fpscr>>22); /* anded with exceptions */ + for(i=0; i<5; i++) + if(fpscr & (1<status, ur->pc, ur->sp); + dumpregs(ur); + l.sp = ur->sp; + l.pc = ur->pc; + dumpstack(); + setpri(PriBackground); /* Let the debugger in */ + for(;;) + sched(); +} + +void +dumpstack(void) +{ + ulong l, v; + int i; + + if(up == 0) + return; + i = 0; + for(l=(ulong)&l; l<(ulong)(up->kstack+KSTACK); l+=4){ + v = *(ulong*)l; + if(KTZERO < v && v < (ulong)etext){ + print("%lux=%lux, ", l, v); + if(i++ == 4){ + print("\n"); + i = 0; + } + } + } +} + +void +dumpregs(Ureg *ur) +{ + int i; + ulong *l; + if(up) { + print("registers for %s %ld\n", up->text, up->pid); + if(ur->usp < (ulong)up->kstack || + ur->usp > (ulong)up->kstack+KSTACK) + print("invalid stack ptr\n"); + } + else + print("registers for kernel\n"); + + l = &ur->cause; + for(i=0; ikpfun)(up->arg); + pexit("", 0); +} + +void +kprocchild(Proc *p, void (*func)(void*), void *arg) +{ + p->sched.pc = (ulong)linkproc; + p->sched.sp = (ulong)p->kstack+KSTACK; + + p->kpfun = func; + p->arg = arg; +} + +void +setpanic(void) +{ + consoleprint = 1; +iprint("panic\n"); +} + +void +dumplongs(char*, ulong*, int) +{ +} diff --git a/os/cerf405/uart.c b/os/cerf405/uart.c new file mode 100644 index 00000000..3d90f5ad --- /dev/null +++ b/os/cerf405/uart.c @@ -0,0 +1,139 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +/* + * uart for initial port + */ + +typedef struct Uartregs Uartregs; +struct Uartregs { + uchar rbr; +#define thr rbr +#define dll rbr + uchar ier; +#define dlm ier + uchar fcr; +#define iir fcr + uchar lcr; + uchar mcr; + uchar lsr; + uchar msr; + uchar scr; +}; + +#define UARTREGS(n) ((Uartregs*)(PHYSUART0+(n)*0x100)) + +enum { + /* ier */ + Edssi= 1<<3, + Elsi= 1<<2, + Etbei= 1<<1, + Erbfi= 1<<0, + + /* iir */ + Fci0= 0<<4, + Fci3= 3<<4, + Ipl= 7<<1, + Ip= 1<<0, + + /* fcr */ + Rftl1= 0<<6, /* receiver trigger level 1, 16, 32, 56 */ + Rftl16= 1<<6, + Rftl32= 2<<6, + Rftl56= 3<<6, + Dms= 1<<3, /* =0, single transfer; =1, multiple transfers */ + Tfr= 1<<2, /* transmitter fifo reset */ + Rfr= 1<<1, /* receiver fifo reset */ + Fifoe= 1<<0, /* =0, disable fifos; =1, enable fifos */ + + /* lcr */ + Dlab= 1<<7, /* =0, normal; =1, dll/dlm visible */ + Sb= 1<<6, /* set break */ + Sp= 1<<5, /* =1, enable sticky parity */ + Eps= 1<<4, /* =1, generate even parity */ + Pen= 1<<3, /* =1, enable parity checking */ + Sbs= 1<<2, /* =0, 1 stop bit; =1, 1.5 or 2 stop bits (see Wls) */ + Wls= 3<<0, /* set to nbit-5 for nbit characters */ + + /* mcr */ + Afc= 1<<5, /* =1, auto flow control enabled */ + Loop= 1<<4, /* =1, loop back mode enabled */ + Out2= 1<<3, /* =0, OUT2# active */ + Out1= 1<<2, /* =0, OUT1# active */ + Rts= 1<<1, /* =0, RTS# inactive (1); =1, RTS# active (0) */ + Dtr= 1<<0, /* =0, DTS# inactive; =1, DTS# active */ + + /* lsr */ + Rfe= 1<<7, /* error instances in fifo */ + Temt= 1<<6, /* transmitter empty */ + Thre= 1<<5, /* transmitter holding register/fifo empty */ + Be= 1<<4, /* break (interrupt) */ + Fe= 1<<3, /* framing error */ + Pe= 1<<2, /* parity error */ + Oe= 1<<1, /* overrun error */ + Dr= 1<<0, /* receiver data ready */ + + /* msr */ + Dcd= 1<<7, /* follows Out2 */ + Ri= 1<<6, /* follows Out1 */ + Dsr= 1<<5, /* follows Dtr */ + Cts= 1<<4, /* follows Rts */ + Ddcd= 1<<3, /* Dcd input changed */ + Teri= 1<<2, /* Ri changed from 0 to 1 */ + Ddsr= 1<<1, /* Dsr input changed */ + Dcts= 1<<0, /* Cts input changed */ +}; + +void (*serwrite)(char*, int) = uartputs; + +void +uartinstall(void) +{ +} + +void +uartspecial(int, int, Queue**, Queue**, int (*)(Queue*, int)) +{ +} + +void +uartputc(int c) +{ + Uartregs *r; + + if(c == 0) + return; + r = UARTREGS(0); + while((r->lsr & Thre) == 0) + {} + r->thr = c; + if(c == '\n') + while((r->lsr & Thre) == 0) /* flush xmit fifo */ + {} +} + +void +uartputs(char *data, int len) +{ + int s; + +// if(!uartspcl && !redirectconsole) +// return; + s = splhi(); + while(--len >= 0){ + if(*data == '\n') + uartputc('\r'); + uartputc(*data++); + } + splx(s); +} + +void +uartwait(void) +{ +} diff --git a/os/cerf405/uart.h b/os/cerf405/uart.h new file mode 100644 index 00000000..28037473 --- /dev/null +++ b/os/cerf405/uart.h @@ -0,0 +1,101 @@ +/* + * 405EP specific code for its uart. + */ + +#define UR(p,r) ((uchar*)(p))[r] +#define uartwr(u,r,v) (UR(u->regs,r) = (v)) +#define uartwrreg(u,r,v) (UR(u->regs,r)= (u)->sticky[r] | (v)) +#define uartrdreg(u,r) UR(u->regs,r) + +extern void uartsetup(ulong, void*, ulong, char*); +extern void uartclock(void); + +static void +uartportpower(Uart*, int) +{ + /* TO DO: power control */ +} + +/* + * handle an interrupt to a single uart + */ +static void +uartintrx(Ureg*, void* arg) +{ + uartintr(arg); +} + +/* + * install the uarts (called by reset) + */ +void +uartinstall(void) +{ + static int already; + + if(already) + return; + already = 1; + + /* first two ports are always there */ + uartsetup(0, (void*)PHYSUART0, 0, "eia0"); + intrenable(VectorUART0, uartintrx, uart[0], BUSUNKNOWN, "uart0"); + uartsetup(1, (void*)PHYSUART1, 0, "eia1"); + intrenable(VectorUART1, uartintrx, uart[1], BUSUNKNOWN, "uart1"); + addclock0link(uartclock, 22); +} + +/* + * If the UART's receiver can be connected to a DMA channel, + * this function does what is necessary to create the + * connection and returns the DMA channel number. + * If the UART's receiver cannot be connected to a DMA channel, + * a -1 is returned. + */ +char +uartdmarcv(int dev) +{ + + USED(dev); + return -1; +} + +void +uartputc(int c) +{ + uchar *p; + + if(c == 0) + return; + p = (uchar*)PHYSUART0; + while((UR(p,Lstat) & Outready) == 0){ + ; + } + UR(p,Data) = c; + eieio(); + if(c == '\n') + while((UR(p,Lstat) & Outready) == 0){ /* let fifo drain */ + ; + } +} + +void +uartputs(char *data, int len) +{ + int s; + +// if(!uartspcl && !redirectconsole) +// return; + s = splhi(); + while(--len >= 0){ + if(*data == '\n') + uartputc('\r'); + uartputc(*data++); + } + splx(s); +} + +void +uartwait(void) +{ +} diff --git a/os/fads/NOTICE b/os/fads/NOTICE new file mode 100644 index 00000000..95589fda --- /dev/null +++ b/os/fads/NOTICE @@ -0,0 +1,4 @@ +Inferno® Copyright © 1996-1999 Lucent Technologies Inc. All rights reserved. +PowerPC support Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net). All rights reserved. +MPC8xx Inferno PowerPC port Copyright © 1998-2003 Vita Nuova Holdings Limited. All rights reserved. +FADS Inferno PowerPC port Copyright © 1998-2003 Vita Nuova Holdings Limited. All rights reserved. diff --git a/os/fads/archfads.c b/os/fads/archfads.c new file mode 100644 index 00000000..4ceb02a1 --- /dev/null +++ b/os/fads/archfads.c @@ -0,0 +1,619 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "../port/netif.h" +#include "../mpc/etherif.h" +#include "../port/flashif.h" + +#include +#include +#include +#include "screen.h" + +#include "archfads.h" + +/* + * board-specific support for the 8xxFADS (including 860/21 development system) + */ + +enum { + /* CS assignment on FADS boards */ + BOOTCS = 0, + BCSRCS = 1, + DRAM1 = 2, + DRAM2 = 3, + SDRAM = 4, + + /* sccr */ + RTSEL = IBIT(8), /* =0, select main oscillator (OSCM); =1, select external crystal (EXTCLK) */ + RTDIV = IBIT(7), /* =0, divide by 4; =1, divide by 512 */ + CRQEN = IBIT(9), /* =1, switch to high frequency when CPM active */ + PRQEN = IBIT(10), /* =1, switch to high frequency when interrupt pending */ + + /* plprcr */ + CSRC = IBIT(21), /* =0, clock is DFNH; =1, clock is DFNL */ +}; + +/* + * called early in main.c, after machinit: + * using board and architecture specific registers, initialise + * 8xx registers that need it and complete initialisation of the Mach structure. + */ +void +archinit(void) +{ + IMM *io; + int mf; + + m->bcsr = KADDR(PHYSBCSR); + m->bcsr[1] |= DisableRS232a | DisableIR | DisableEther | DisablePCMCIA | DisableRS232b; + m->bcsr[1] &= ~(DisableDRAM|DisableFlash); + m->bcsr[1] &= ~EnableSDRAM; + m->bcsr[4] &= ~EnableVideoClock; + m->bcsr[4] |= DisableVideoLamp; + io = m->iomem; /* run by reset code: no need to lock */ + if(1 || (io->sccr & RTDIV) != 0){ + /* oscillator frequency can't be determined independently: check a switch */ + if((m->bcsr[2]>>19)&(1<<2)) + m->clockgen = 5*MHz; + else + m->clockgen = 4*MHz; + } else + m->clockgen = 32768; + m->oscclk = m->clockgen/MHz; /* TO DO: 32k clock */ + io->plprcrk = KEEP_ALIVE_KEY; + io->plprcr &= ~CSRC; /* general system clock is DFNH */ + mf = (io->plprcr >> 20)+1; /* use timing set by bootstrap */ + io->plprcrk = ~KEEP_ALIVE_KEY; + io->sccrk = KEEP_ALIVE_KEY; + io->sccr |= CRQEN | PRQEN; + io->sccr |= RTSEL; /* select EXTCLK */ + io->sccrk = ~KEEP_ALIVE_KEY; + m->cpuhz = m->clockgen*mf; + m->speed = m->cpuhz/MHz; +} + +static ulong +banksize(int x, ulong *pa) +{ + IMM *io; + + io = m->iomem; + if((io->memc[x].base & 1) == 0) + return 0; /* bank not valid */ + *pa = io->memc[x].base & ~0x7FFF; + return -(io->memc[x].option&~0x7FFF); +} + +/* + * initialise the kernel's memory configuration: + * there are two banks (base0, npage0) and (base1, npage1). + * initialise any other values in conf that are board-specific. + */ +void +archconfinit(void) +{ + ulong nbytes, pa, ktop; + + conf.nscc = 2; + conf.nocts2 = 1; /* not connected on the FADS board */ + + conf.npage0 = 0; + if((m->bcsr[1] & DisableDRAM) == 0){ + nbytes = banksize(DRAM1, &pa); + if(nbytes){ + conf.npage0 = nbytes/BY2PG; + conf.base0 = pa; + } + } + + conf.npage1 = 0; + if(m->bcsr[1] & EnableSDRAM){ + nbytes = banksize(SDRAM, &pa); + if(nbytes){ + conf.npage1 = nbytes/BY2PG; + conf.base1 = pa; + } + } + + /* the following assumes the kernel text and/or data is in bank 0 */ + ktop = PGROUND((ulong)end); + ktop = PADDR(ktop) - conf.base0; + conf.npage0 -= ktop/BY2PG; + conf.base0 += ktop; +} + +static void +archidprint(void) +{ + int f, i; + ulong v; + + /* 8xx and FADS specific */ + print("IMMR: "); + v = getimmr() & 0xFFFF; + switch(v>>8){ + case 0x00: print("MPC860/821"); break; + case 0x20: print("MPC823"); break; + case 0x21: print("MPC823A"); break; + default: print("Type #%lux", v>>8); break; + } + print(", mask #%lux\n", v&0xFF); + v = m->bcsr[3]>>16; + print("MPC8xxFADS rev %lud, DB: ", ((v>>4)&8)|((v>>1)&4)|(v&3)); + f = (v>>8)&0x3F; + switch(f){ + default: print("ID#%x", f); break; + case 0x00: print("MPC860/821"); break; + case 0x01: print("MPC813"); break; + case 0x02: print("MPC821"); break; + case 0x03: print("MPC823"); break; + case 0x20: print("MPC801"); break; + case 0x21: print("MPC850"); break; + case 0x22: print("MPC860"); break; + case 0x23: print("MPC860SAR"); break; + case 0x24: print("MPC860T"); break; + } + print("ADS, rev #%lux\n", (m->bcsr[2]>>16)&7); + for(i=0; i<=4; i++) + print("BCSR%d: %8.8lux\n", i, m->bcsr[i]); + v = m->bcsr[2]; + f = (v>>28)&0xF; + switch(f){ + default: print("Unknown"); break; + case 4: print("SM732A2000/SM73228 - 8M SIMM"); break; + case 5: print("SM732A1000A/SM73218 - 4M SIMM"); break; + case 6: print("MCM29080 - 8M SIMM"); break; + case 7: print("MCM29040 - 4M SIMM"); break; + case 8: print("MCM29020 - 2M SIMM"); break; + } + switch((m->bcsr[3]>>20)&7){ + default: i = 0; break; + case 1: i = 150; break; + case 2: i = 120; break; + case 3: i = 90; break; + } + print(" flash, %dns\n", i); + f = (v>>23)&0xF; + switch(f&3){ + case 0: i = 4; break; + case 1: i = 32; break; + case 2: i = 16; break; + case 3: i = 8; break; + } + print("%dM SIMM, ", i); + switch(f>>2){ + default: i = 0; break; + case 2: i = 70; break; + case 3: i = 60; break; + } + print("%dns\n", i); + print("options: #%lux\n", (m->bcsr[2]>>19)&0xF); + print("plprcr=%8.8lux sccr=%8.8lux\n", m->iomem->plprcr, m->iomem->sccr); +} + +void +cpuidprint(void) +{ + print("PVR: "); + switch(m->cputype){ + case 0x01: print("MPC601"); break; + case 0x03: print("MPC603"); break; + case 0x04: print("MPC604"); break; + case 0x06: print("MPC603e"); break; + case 0x07: print("MPC603e-v7"); break; + case 0x50: print("MPC8xx"); break; + default: print("PowerPC version #%x", m->cputype); break; + } + print(", revision #%lux\n", getpvr()&0xffff); + archidprint(); + print("%lud MHz system\n", m->cpuhz/MHz); + print("\n"); +} + +/* + * provide value for #r/switch (devrtc.c) + */ +int +archoptionsw(void) +{ + return (m->bcsr[2]>>19)&0xF; /* value of switch DS1 */ +} + +/* + * invoked by clock.c:/^clockintr + */ +static void +twinkle(void) +{ + if(m->ticks%MS2TK(1000) == 0) + m->bcsr[4] ^= DisableLamp; +} + +void (*archclocktick)(void) = twinkle; + +/* + * invoked by ../port/taslock.c:/^ilock: + * reset watchdog timer here, if there is one and it is enabled + * (qboot currently disables it on the FADS board) + */ +void +clockcheck(void) +{ +} + +/* + * for devflash.c:/^flashreset + * retrieve flash type, virtual base and length and return 0; + * return -1 on error (no flash) + */ +int +archflashreset(int bank, Flash *f) +{ + char *t; + int mbyte; + + if(bank != 0) + return -1; + switch((m->bcsr[2]>>28)&0xF){ + default: return -1; /* unknown or not there */ + case 4: mbyte=8; t = "SM732x8"; break; + case 5: mbyte=4; t = "SM732x8"; break; + case 6: mbyte=8; t = "AMD29F0x0"; break; + case 7: mbyte=4; t = "AMD29F0x0"; break; + case 8: mbyte=2; t = "AMD29F0x0"; break; + } + f->type = t; + f->addr = KADDR(PHYSFLASH); + f->size = mbyte*1024*1024; + f->width = 4; + f->interleave = 3; + return 0; +} + +void +archflashwp(Flash*, int) +{ +} + +int +archether(int ctlrno, Ether *ether) +{ + if(isaconfig("ether", ctlrno, ether) == 0) + return -1; + return 1; +} + +/* + * enable the clocks for the given SCC ether and reveal them to the caller. + * do anything else required to prepare the transceiver (eg, set full-duplex, reset loopback). + */ +int +archetherenable(int cpmid, int *rcs, int *tcs, int mbps, int fullduplex) +{ + IMM *io; + + USED(mbps, fullduplex); /* TO DO */ + switch(cpmid){ + default: + /* no other SCCs are wired on the FADS board */ + return -1; + + case CPscc2: /* assume 8xxFADS board with 823DABS */ + io = ioplock(); + m->bcsr[1] |= DisableIR|DisableRS232b; + m->bcsr[1] &= ~DisableEther; + io->papar |= SIBIT(6)|SIBIT(5); /* enable CLK2 and CLK3 */ + io->padir &= ~(SIBIT(6)|SIBIT(5)); + /* ETHLOOP etc reset in BCSR elsewhere */ + iopunlock(); + *rcs = CLK2; + *tcs = CLK3; + break; + + case CPscc1: /* assume 860/21 development board */ + io = ioplock(); + m->bcsr[1] |= DisableIR|DisableRS232b; /* TO DO: might not be shared with RS232b */ + m->bcsr[1] &= ~DisableEther; + io->papar |= SIBIT(6)|SIBIT(7); /* enable CLK2 and CLK1 */ + io->padir &= ~(SIBIT(6)|SIBIT(7)); + + /* settings peculiar to 860/821 development board */ + io->pcpar &= ~(SIBIT(4)|SIBIT(5)|SIBIT(6)); /* ETHLOOP, TPFULDL~, TPSQEL~ */ + io->pcdir |= SIBIT(4)|SIBIT(5)|SIBIT(6); + io->pcdat &= ~SIBIT(4); + io->pcdat |= SIBIT(5)|SIBIT(6); + iopunlock(); + *rcs = CLK2; + *tcs = CLK1; + break; + } + return 0; +} + +/* + * do anything extra required to enable the UART on the given CPM port + */ +void +archenableuart(int id, int irda) +{ + switch(id){ + case CPsmc1: + m->bcsr[1] &= ~DisableRS232a; + break; + case CPscc2: + m->bcsr[1] |= DisableEther|DisableIR|DisableRS232b; + if(irda) + m->bcsr[1] &= ~DisableIR; + else + m->bcsr[1] &= ~DisableRS232b; + break; + default: + /* nothing special */ + break; + } +} + +/* + * do anything extra required to disable the UART on the given CPM port + */ +void +archdisableuart(int id) +{ + switch(id){ + case CPsmc1: + m->bcsr[1] |= DisableRS232a; + break; + case CPscc2: + m->bcsr[1] |= DisableIR|DisableRS232b; + break; + default: + /* nothing special */ + break; + } +} + +/* + * enable the external USB transceiver + * speed is 12MHz if highspeed is non-zero; 1.5MHz if zero + * master is non-zero if the node is acting as USB Host and should provide power + */ +void +archenableusb(int highspeed, int master) +{ + if(highspeed) + m->bcsr[4] |= USBFullSpeed; + else + m->bcsr[4] &= ~USBFullSpeed; + if(master) + m->bcsr[4] &= ~DisableUSBVcc; + else + m->bcsr[4] |= DisableUSBVcc; + eieio(); + m->bcsr[4] &= ~DisableUSB; +} + +/* + * shut down the USB transceiver + */ +void +archdisableusb(void) +{ + m->bcsr[4] |= DisableUSBVcc | DisableUSB; +} + +/* + * set the external infrared transceiver to the given speed + */ +void +archsetirxcvr(int highspeed) +{ + if(!highspeed){ + /* force low edge after enable to put TFDS6000 xcvr in low-speed mode (see 4.9.2.1 in FADS manual) */ + m->bcsr[1] |= DisableIR; + microdelay(2); + } + m->bcsr[1] &= ~DisableIR; +} + +/* + * force hardware reset/reboot + */ +void +archreboot(void) +{ + IMM *io; + + io = m->iomem; + io->plprcrk = KEEP_ALIVE_KEY; + io->plprcr |= 1<<7; /* checkstop reset enable */ + io->plprcrk = ~KEEP_ALIVE_KEY; + m->iomem->padat &= ~SIBIT(4); /* drop backlight */ + eieio(); + io->sdcr = 1; + eieio(); + io->lccr = 0; + eieio(); + firmware(0); +} + +/* + * board-specific PCMCIA support: assumes slot B on 82xFADS + */ + +int +pcmslotavail(int slotno) +{ + return slotno == 1; +} + +void +pcmenable(void) +{ + ioplock(); + m->bcsr[1] = (m->bcsr[1] | PCCVPPHiZ) & ~PCCVPP5V; + m->bcsr[1] |= PCCVCC0V; + m->bcsr[1] &= ~DisablePCMCIA; + m->bcsr[1] &= ~PCCVCC5V; /* apply Vcc */ + iopunlock(); +} + +int +pcmpowered(int) +{ + ulong r; + + r = ~m->bcsr[1]&PCCVCCMask; /* active low */ + if(r == PCCVCC5V) + return 5; + if(r == PCCVCC3V) + return 3; + return 0; +} + +void +pcmsetvcc(int, int v) +{ + if(v == 5) + v = PCCVCC5V; + else if(v == 3) + v = PCCVCC3V; + else + v = 0; + ioplock(); + m->bcsr[1] = (m->bcsr[1] | PCCVCCMask) & ~v; /* active low */ + iopunlock(); +} + +void +pcmsetvpp(int, int v) +{ + if(v == 5) + v = PCCVPP5V; + else if(v == 12) + v = PCCVPP12V; + else if(v == 0) + v = PCCVPP0V; + else + v = 0; /* Hi-Z */ + ioplock(); + m->bcsr[1] = (m->bcsr[1] | PCCVPPHiZ) & ~v; /* active low */ + iopunlock(); +} + +void +pcmpower(int slotno, int on) +{ + if(!on){ + pcmsetvcc(slotno, 0); /* turn off card power */ + pcmsetvpp(slotno, -1); /* turn off programming voltage (Hi-Z) */ + }else + pcmsetvcc(slotno, 5); +} + +/* + * enable/disable the LCD panel's backlight via + * York touch panel interface (does no harm without it) + */ +void +archbacklight(int on) +{ + IMM *io; + + delay(2); + io = ioplock(); + io->papar &= ~SIBIT(4); + io->padir |= SIBIT(4); + if(on) + io->padat |= SIBIT(4); + else + io->padat &= ~SIBIT(4); + iopunlock(); +} + +/* + * set parameters to describe the screen + */ +int +archlcdmode(Mode *m) +{ + m->x = 640; + m->y = 480; + m->d = 3; + m->lcd.freq = 25000000; + m->lcd.ac = 0; + m->lcd.vpw = 2; + m->lcd.wbf = 34; + m->lcd.wbl = 106; + m->lcd.flags = IsColour | IsTFT | OELow | HsyncLow | VsyncLow; + m->lcd.notpdpar = SIBIT(6); + return 0; +} + +/* + * reset 823 video port for devvid.c + */ +void +archresetvideo(void) +{ + ioplock(); + m->bcsr[4] &= ~DisableVideoLamp; + m->bcsr[4] |= EnableVideoPort; + eieio(); + m->bcsr[4] &= ~EnableVideoPort; /* falling edge to reset */ + iopunlock(); + delay(6); + ioplock(); + m->bcsr[4] |= EnableVideoPort; + iopunlock(); + delay(6); +} + +/* + * enable 823 video port and clock + */ +void +archenablevideo(void) +{ + ioplock(); + m->bcsr[4] |= EnableVideoClock|EnableVideoPort; /* enable AFTER pdpar/pddir to avoid damage */ + iopunlock(); +} + +/* + * disable 823 video port and clock + */ +void +archdisablevideo(void) +{ + ioplock(); + m->bcsr[4] &= ~(EnableVideoClock|EnableVideoPort); + m->bcsr[4] |= DisableVideoLamp; + iopunlock(); +} + +/* + * allocate a frame buffer for the video, aligned on 16 byte boundary + */ +uchar* +archvideobuffer(long nbytes) +{ + /* we shall use the on-board SDRAM if the kernel hasn't grabbed it */ + if((m->bcsr[1] & EnableSDRAM) == 0){ + m->bcsr[1] |= EnableSDRAM; + return KADDR(PHYSSDRAM); + } + return xspanalloc(nbytes, 16, 0); +} + +/* + * there isn't a hardware keyboard port + */ +void +archkbdinit(void) +{ +} diff --git a/os/fads/archfads.h b/os/fads/archfads.h new file mode 100644 index 00000000..a35230f9 --- /dev/null +++ b/os/fads/archfads.h @@ -0,0 +1,33 @@ + +enum { + /* BCSR1 bits */ + DisableFlash= IBIT(0), + DisableDRAM= IBIT(1), + DisableEther= IBIT(2), + DisableIR= IBIT(3), + DisableRS232a= IBIT(7), + DisablePCMCIA= IBIT(8), + PCCVCCMask= IBIT(9)|IBIT(15), + PCCVPPMask= IBIT(10)|IBIT(11), + DisableRS232b= IBIT(13), + EnableSDRAM= IBIT(14), + + PCCVCC0V= IBIT(15)|IBIT(9), + PCCVCC5V= IBIT(9), /* active low */ + PCCVCC3V= IBIT(15), /* active low */ + PCCVPP0V= IBIT(10)|IBIT(11), /* active low */ + PCCVPP5V= IBIT(10), /* active low */ + PCCVPP12V= IBIT(11), /* active low */ + PCCVPPHiZ= IBIT(10)|IBIT(11), + + /* BCSR4 bits */ + DisableTPDuplex= IBIT(1), + DisableLamp= IBIT(3), + DisableUSB= IBIT(4), + USBFullSpeed= IBIT(5), + DisableUSBVcc= IBIT(6), + DisableVideoLamp= IBIT(8), + EnableVideoClock= IBIT(9), + EnableVideoPort= IBIT(10), + DisableModem= IBIT(11), +}; diff --git a/os/fads/dat.h b/os/fads/dat.h new file mode 100644 index 00000000..19d7c560 --- /dev/null +++ b/os/fads/dat.h @@ -0,0 +1,162 @@ +typedef struct Conf Conf; +typedef struct FPU FPU; +typedef struct FPenv FPenv; +typedef struct IMM IMM; +typedef struct Irqctl Irqctl; +typedef struct ISAConf ISAConf; +typedef struct Label Label; +typedef struct Lock Lock; +typedef struct Mach Mach; +typedef struct Map Map; +typedef struct Power Power; +typedef struct RMap RMap; +typedef struct Ureg Ureg; + +typedef ulong Instr; + +#define MACHP(n) (n==0? &mach0 : *(Mach**)0) + +struct Lock +{ + ulong key; + ulong pc; + ulong sr; + int pri; +}; + +struct Label +{ + ulong sp; + ulong pc; +}; + +/* + * Proc.fpstate + */ +enum +{ + FPINIT, + FPACTIVE, + FPINACTIVE, +}; + +/* + * This structure must agree with FPsave and FPrestore asm routines + */ +struct FPenv +{ + union { + double fpscrd; + struct { + ulong pad; + ulong fpscr; + }; + }; + int fpistate; /* emulated fp */ + ulong emreg[32][3]; /* emulated fp */ +}; +/* + * This structure must agree with fpsave and fprestore asm routines + */ +struct FPU +{ + double fpreg[32]; + FPenv env; +}; + +struct Conf +{ + ulong nmach; /* processors */ + ulong nproc; /* processes */ + ulong npage0; /* total physical pages of memory */ + ulong npage1; /* total physical pages of memory */ + ulong npage; /* total physical pages of memory */ + ulong base0; /* base of bank 0 */ + ulong base1; /* base of bank 1 */ + ulong ialloc; /* max interrupt time allocation in bytes */ + + int nscc; /* number of SCCs implemented */ + ulong smcuarts; /* bits for SMCs to define as eiaN */ + ulong sccuarts; /* bits for SCCs to define as eiaN */ + int nocts2; /* CTS2 and CD2 aren't connected */ + uchar* nvrambase; /* virtual address of nvram */ + ulong nvramsize; /* size in bytes */ +}; + +#include "../port/portdat.h" + +/* + * machine dependent definitions not used by ../port/dat.h + */ + +struct Mach +{ + /* OFFSETS OF THE FOLLOWING KNOWN BY l.s */ + int machno; /* physical id of processor (unused) */ + ulong splpc; /* pc of last caller to splhi (unused) */ + int mmask; /* 1<machno (unused) */ + + /* ordering from here on irrelevant */ + ulong ticks; /* of the clock since boot time */ + Proc *proc; /* current process on this processor */ + Label sched; /* scheduler wakeup */ + Lock alarmlock; /* access to alarm list */ + void *alarm; /* alarms bound to this clock */ + int nrdy; + int speed; /* general system clock in MHz */ + long oscclk; /* oscillator frequency (MHz) */ + long cpuhz; /* general system clock (cycles) */ + long clockgen; /* clock generator frequency (cycles) */ + int cputype; + ulong delayloop; + ulong* bcsr; + IMM* iomem; /* MPC8xx internal i/o control memory */ + + /* MUST BE LAST */ + int stack[1]; +}; +extern Mach mach0; + + +/* + * a parsed .ini line + */ +#define NISAOPT 8 + +struct ISAConf { + char* type; + ulong port; + ulong irq; + ulong mem; + int dma; + ulong size; + ulong freq; + uchar bus; + + int nopt; + char* opt[NISAOPT]; +}; + +struct Map { + int size; + ulong addr; +}; + +struct RMap { + char* name; + Map* map; + Map* mapend; + + Lock; +}; + +struct Power { + Dev* dev; + int (*powerdown)(Power*); + int (*powerup)(Power*); + int state; + void* arg; +}; + +extern register Mach *m; +extern register Proc *up; diff --git a/os/fads/fads b/os/fads/fads new file mode 100644 index 00000000..1f1a2959 --- /dev/null +++ b/os/fads/fads @@ -0,0 +1,121 @@ +# fads board with remote file system on ether or ppp +dev + root + cons archfads screen + env + mnt + pipe + prog + rtc + srv + dup + ssl + cap + + draw + pointer + + ip bootp ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium + ether netif netaux + uart + flash +# usb + touch spi +# pcmcia cis +# ata inb + + ftl +# kfs chk kcon console dat dentry fcall fs fswren iobuf kfs sub uid +# kprof + +# vid i2c + i2c i2c + +ip + il + tcp + udp + ipifc + icmp + icmp6 + ipmux + +lib + interp + tk + draw + memlayer + memdraw + keyring + sec + mp + math + kern + +link + etherscc + ethermedium + flashamd29f0x0 +# pppmedium ppp compress + +mod + sys + draw + tk + math + keyring + +port + alarm + alloc + allocb + chan + dev + dial + dis + discall + exception + exportfs + inferno + latin1 + nocache + nodynld + parse + pgrp + print + proc + qio + qlock + random + sysfile + taslock + xalloc + +code + int cflag = 0; + int consoleprint = 1; + int panicreset = 0; + int kernel_pool_pcnt = 10; + int main_pool_pcnt = 40; + int heap_pool_pcnt = 20; + int image_pool_pcnt = 40; + +init + mpcinit + +root + /chan + /dev + /dis + /env + /fd / + /n + /net + /nvfs / + /prog + /icons + /osinit.dis + /dis/lib/auth.dis + /dis/lib/ssl.dis + /n/local / + /nvfs/default /usr/inferno/keyring/default diff --git a/os/fads/fns.h b/os/fads/fns.h new file mode 100644 index 00000000..c4892a82 --- /dev/null +++ b/os/fads/fns.h @@ -0,0 +1,117 @@ +#include "../port/portfns.h" + +void addpower(Power*); +void archbacklight(int); +void archconfinit(void); +void archdisableuart(int); +void archdisableusb(void); +void archdisablevideo(void); +void archenableuart(int, int); +void archenableusb(int, int); +void archenablevideo(void); +void archkbdinit(void); +void archresetvideo(void); +int archetherenable(int, int*, int*, int, int); +void archinit(void); +int archoptionsw(void); +void archreboot(void); +void archsetirxcvr(int); +uchar* archvideobuffer(long); +ulong baudgen(int, int); +int brgalloc(void); +void brgfree(int); +int cistrcmp(char*, char*); +int cistrncmp(char*, char*, int); +void clockcheck(void); +void clockinit(void); +void clockintr(Ureg*); +void clrfptrap(void); +#define coherence() /* nothing needed for uniprocessor */ +void cpminit(void); +void cpuidprint(void); +void dcflush(void*, ulong); +void dcinval(void*, ulong); +void delay(int); +void dtlbmiss(void); +void dumplongs(char*, ulong*, int); +void dumpregs(Ureg*); +void eieio(void); +void faultpower(Ureg*); +void firmware(int); +void fpinit(void); +int fpipower(Ureg*); +void fpoff(void); +void fprestore(FPU*); +void fpsave(FPU*); +ulong fpstatus(void); +char* getconf(char*); +ulong getdar(void); +ulong getdec(void); +ulong getdepn(void); +ulong getdsisr(void); +ulong getimmr(void); +ulong getmsr(void); +ulong getpvr(void); +ulong gettbl(void); +ulong gettbu(void); +void gotopc(ulong); +void icflush(void*, ulong); +void idle(void); +#define idlehands() /* nothing to do in the runproc */ +void intr(Ureg*); +void intrenable(int, void (*)(Ureg*, void*), void*, int, char*); +void intrdisable(int, void (*)(Ureg*, void*), void*, int, char*); +int intrstats(char*, int); +void intrvec(void); +int isaconfig(char*, int, ISAConf*); +int isvalid_va(void*); +void itlbmiss(void); +void kbdinit(void); +void kbdreset(void); +void lcdpanel(int); +void links(void); +void mapfree(RMap*, ulong, int); +void mapinit(RMap*, Map*, int); +void mathinit(void); +void mmuinit(void); +ulong* mmuwalk(ulong*, ulong, int); +void pcmenable(void); +void pcmintrenable(int, void (*)(Ureg*, void*), void*); +int pcmpin(int slot, int type); +void pcmpower(int, int); +int pcmpowered(int); +void pcmsetvcc(int, int); +void pcmsetvpp(int, int); +int pcmslotsavail(int); +void procsave(Proc*); +void procsetup(Proc*); +void putdec(ulong); +void putmsr(ulong); +void puttwb(ulong); +ulong rmapalloc(RMap*, ulong, int, int); +void screeninit(void); +int screenprint(char*, ...); /* debugging */ +void screenputs(char*, int); +int segflush(void*, ulong); +void setpanic(void); +long spioutin(void*, long, void*); +void spireset(void); +ulong _tas(ulong*); +void trapinit(void); +void trapvec(void); +void uartinstall(void); +void uartspecial(int, int, Queue**, Queue**, int (*)(Queue*, int)); +void uartwait(void); /* debugging */ +void videoreset(void); +void videotest(void); +void wbflush(void); + +#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) +ulong getcallerpc(void*); + +#define KADDR(a) ((void*)((ulong)(a)|KZERO)) +#define PADDR(a) ((((ulong)(a)&KSEGM)!=KSEG0)?(ulong)(a):((ulong)(a)&~KZERO)) + +/* IBM bit field order */ +#define IBIT(b) ((ulong)1<<(31-(b))) +#define SIBIT(n) ((ushort)1<<(15-(n))) diff --git a/os/fads/io.h b/os/fads/io.h new file mode 100644 index 00000000..30312c68 --- /dev/null +++ b/os/fads/io.h @@ -0,0 +1 @@ +#include "../mpc/800io.h" diff --git a/os/fads/main.c b/os/fads/main.c new file mode 100644 index 00000000..93ba7f95 --- /dev/null +++ b/os/fads/main.c @@ -0,0 +1,392 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "version.h" + +/* where b.com or qboot leaves configuration info */ +#define BOOTARGS ((char*)CONFADDR) +#define BOOTARGSLEN 1024 +#define MAXCONF 32 + +extern ulong kerndate; +extern int cflag; +int remotedebug; + +extern int main_pool_pcnt; +extern int heap_pool_pcnt; +extern int image_pool_pcnt; + +char bootargs[BOOTARGSLEN+1]; +char bootdisk[KNAMELEN]; +char *confname[MAXCONF]; +char *confval[MAXCONF]; +int nconf; + +extern void addconf(char *, char *); + +/* + * arguments passed to initcode and /boot + */ +char argbuf[128]; + +static void +options(void) +{ + long i, n; + char *cp, *line[MAXCONF], *p, *q; + + /* + * parse configuration args from bootstrap + */ + memmove(bootargs, BOOTARGS, BOOTARGSLEN); /* where b.com leaves its config */ + cp = bootargs; + cp[BOOTARGSLEN-1] = 0; + + /* + * Strip out '\r', change '\t' -> ' '. + */ + p = cp; + for(q = cp; *q; q++){ + if(*q == '\r') + continue; + if(*q == '\t') + *q = ' '; + *p++ = *q; + } + *p = 0; + + n = getfields(cp, line, MAXCONF, 1, "\n"); + for(i = 0; i < n; i++){ + if(*line[i] == '#') + continue; + cp = strchr(line[i], '='); + if(cp == 0) + continue; + *cp++ = 0; + confname[nconf] = line[i]; + confval[nconf] = cp; + nconf++; + } +} + +void +doc(char *m) +{ + USED(m); + print("%s...\n", m); uartwait(); +} + +static void +poolsizeinit(void) +{ + ulong nb; + + nb = conf.npage*BY2PG; + poolsize(mainmem, (nb*main_pool_pcnt)/100, 0); + poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0); + poolsize(imagmem, (nb*image_pool_pcnt)/100, 1); +} + +static void +serialconsole(void) +{ + char *p; + int port, baud; + + p = getconf("console"); + if(p == nil) + p = "0"; + if(p != nil && !remotedebug){ + port = strtol(p, nil, 0); + baud = 9600; + p = getconf("baud"); + if(p != nil){ + baud = strtol(p, nil, 0); + if(baud < 9600) + baud = 9600; + } + uartspecial(port, baud, &kbdq, &printq, kbdcr2nl); + } +} + +void +main(void) +{ + machinit(); + options(); + archinit(); + quotefmtinstall(); + confinit(); + cpminit(); + xinit(); + poolsizeinit(); + trapinit(); + mmuinit(); + printinit(); + uartinstall(); + serialconsole(); + doc("screeninit"); + screeninit(); + doc("kbdinit"); + kbdinit(); + doc("clockinit"); + clockinit(); + doc("procinit"); + procinit(); + cpuidprint(); + doc("links"); + links(); + doc("chandevreset"); + chandevreset(); + + eve = strdup("inferno"); + + print("\nInferno %s\n", VERSION); + print("Vita Nuova\n"); + print("conf %s (%lud) jit %d\n\n",conffile, kerndate, cflag); + + doc("userinit"); + userinit(); + doc("schedinit"); + schedinit(); +} + +void +machinit(void) +{ + int n; + + n = m->machno; + memset(m, 0, sizeof(Mach)); + m->machno = n; + m->mmask = 1<machno; + m->iomem = KADDR(getimmr() & ~0xFFFF); + m->cputype = getpvr()>>16; + m->delayloop = 20000; /* initial estimate only; set by clockinit */ + m->speed = 50; /* initial estimate only; set by archinit */ +} + +void +init0(void) +{ + Osenv *o; + int i; + char buf[2*KNAMELEN]; + + up->nerrlab = 0; + + spllo(); + + if(waserror()) + panic("init0"); + /* + * These are o.k. because rootinit is null. + * Then early kproc's will have a root and dot. + */ + o = up->env; + o->pgrp->slash = namec("#/", Atodir, 0, 0); + cnameclose(o->pgrp->slash->name); + o->pgrp->slash->name = newcname("/"); + o->pgrp->dot = cclone(o->pgrp->slash); + + chandevinit(); + + if(!waserror()){ + ksetenv("cputype", "power", 0); + snprint(buf, sizeof(buf), "power %s", conffile); + ksetenv("terminal", buf, 0); + poperror(); + } + for(i = 0; i < nconf; i++) + if(confname[i][0] != '*'){ + if(!waserror()){ + ksetenv(confname[i], confval[i], 0); + poperror(); + } + } + + poperror(); + disinit("/osinit.dis"); +} + +void +userinit(void) +{ + Proc *p; + Osenv *o; + + p = newproc(); + o = p->env; + + o->fgrp = newfgrp(nil); + + o->pgrp = newpgrp(); + o->egrp = newegrp(); + kstrdup(&o->user, eve); + + strcpy(p->text, "interp"); + + /* + * Kernel Stack + */ + p->sched.pc = (ulong)init0; + p->sched.sp = (ulong)p->kstack+KSTACK; + + ready(p); +} + +Conf conf; + +void +addconf(char *name, char *val) +{ + if(nconf >= MAXCONF) + return; + confname[nconf] = name; + confval[nconf] = val; + nconf++; +} + +char* +getconf(char *name) +{ + int i; + + for(i = 0; i < nconf; i++) + if(cistrcmp(confname[i], name) == 0) + return confval[i]; + return 0; +} + +void +confinit(void) +{ + char *p; + int pcnt; + + if(p = getconf("*kernelpercent")) + pcnt = 100 - strtol(p, 0, 0); + else + pcnt = 0; + + conf.nscc = 4; + conf.smcuarts = 1<<0; /* SMC1 (usual console) */ + conf.sccuarts = 1<<1; /* SCC2 available by default */ + + archconfinit(); + + conf.npage = conf.npage0 + conf.npage1; + if(pcnt < 10) + pcnt = 70; + conf.ialloc = (((conf.npage*(100-pcnt))/100)/2)*BY2PG; + + conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5; + conf.nmach = MAXMACH; +} + +void +exit(int ispanic) +{ + up = 0; + spllo(); + print("cpu %d exiting\n", m->machno); + + /* Shutdown running devices */ + chandevshutdown(); + + delay(1000); + splhi(); + if(ispanic) + for(;;); + archreboot(); +} + +void +reboot(void) +{ + exit(0); +} + +void +halt(void) +{ + print("cpu halted\n"); + microdelay(1000); + for(;;) + ; +} + +int +isaconfig(char *class, int ctlrno, ISAConf *isa) +{ + char cc[KNAMELEN], *p; + int i; + + snprint(cc, sizeof cc, "%s%d", class, ctlrno); + p = getconf(cc); + if(p == nil) + return 0; + + isa->nopt = tokenize(p, isa->opt, NISAOPT); + for(i = 0; i < isa->nopt; i++){ + p = isa->opt[i]; + if(cistrncmp(p, "type=", 5) == 0) + isa->type = p + 5; + else if(cistrncmp(p, "port=", 5) == 0) + isa->port = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "irq=", 4) == 0) + isa->irq = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "mem=", 4) == 0) + isa->mem = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "size=", 5) == 0) + isa->size = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "freq=", 5) == 0) + isa->freq = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "dma=", 4) == 0) + isa->dma = strtoul(p+4, &p, 0); + } + return 1; +} + +/* + * Save the mach dependent part of the process state. + */ +void +procsave(Proc*) +{ +} + +void +uartputs(char *s, int n) +{ +// screenputs(buf, n); + putstrn(s, n); + uartwait(); +} + +/* stubs */ +void +setfsr(ulong) +{ +} + +ulong +getfsr() +{ + return 0; +} + +void +setfcr(ulong) +{ +} + +ulong +getfcr() +{ + return 0; +} diff --git a/os/fads/mem.h b/os/fads/mem.h new file mode 100644 index 00000000..02cf8f43 --- /dev/null +++ b/os/fads/mem.h @@ -0,0 +1,157 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ + +/* + * Sizes + */ + +#define BI2BY 8 /* bits per byte */ +#define BI2WD 32 /* bits per word */ +#define BY2WD 4 /* bytes per word */ +#define BY2V 8 /* bytes per double word */ +#define BY2PG 4096 /* bytes per page */ +#define WD2PG (BY2PG/BY2WD) /* words per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1)) +#define PGROUND(s) ROUND(s, BY2PG) +#define CACHELINELOG 4 +#define CACHELINESZ (1< */ +#define USER 29 /* R29 is up-> */ +#define IOMEMR 28 /* R28 will be iomem-> */ + +/* + * Fundamental addresses + */ + +#define UREGSIZE ((8+32)*4) + +/* + * MMU + */ + +/* L1 table entry and Mx_TWC flags */ +#define PTEVALID (1<<0) +#define PTEWT (1<<1) /* write through */ +#define PTE4K (0<<2) +#define PTE512K (1<<2) +#define PTE8MB (3<<2) +#define PTEG (1<<4) /* guarded */ + +/* L2 table entry and Mx_RPN flags (also PTEVALID) */ +#define PTECI (1<<1) /* cache inhibit */ +#define PTESH (1<<2) /* page is shared; ASID ignored */ +#define PTELPS (1<<3) /* large page size */ +#define PTEWRITE 0x9F0 + +/* TLB and MxEPN flag */ +#define TLBVALID (1<<9) + +/* + * Address spaces + */ + +#define KUSEG 0x00000000 +#define KSEG0 0x20000000 +#define KSEGM 0xE0000000 /* mask to check which seg */ + +#define KZERO KSEG0 /* base of kernel address space */ +#define KTZERO (KZERO+0x3000) /* first address in kernel text */ +#define KSTACK 8192 /* Size of kernel stack */ + +#define CONFADDR (KZERO|0x200000) /* where qboot leaves configuration info */ + +/* + * Exception codes (trap vectors) + */ +#define CRESET 0x01 +#define CMCHECK 0x02 +#define CDSI 0x03 +#define CISI 0x04 +#define CEI 0x05 +#define CALIGN 0x06 +#define CPROG 0x07 +#define CFPU 0x08 +#define CDEC 0x09 +#define CSYSCALL 0x0C +#define CTRACE 0x0D +#define CFPA 0x0E +/* rest are power-implementation dependent (8xx) */ +#define CEMU 0x10 +#define CIMISS 0x11 +#define CDMISS 0x12 +#define CITLBE 0x13 +#define CDTLBE 0x14 +#define CDBREAK 0x1C +#define CIBREAK 0x1D +#define CPBREAK 0x1E +#define CDPORT 0x1F + +/* + * MPC8xx physical addresses + */ + +/* those encouraged by mpc8bug */ +#define PHYSDRAM 0x00000000 +#define PHYSBCSR 0x02100000 +#define PHYSIMM 0x02200000 +#define PHYSFLASH 0x02800000 + +/* remaining ones are our choice */ +#define PHYSSDRAM 0x03000000 +#define PHYSPCMCIA 0x04000000 +#define PCMCIALEN (8*MB) /* chosen to allow mapping by single TLB entry */ +#define ISAIO (KZERO|PHYSPCMCIA) /* for inb.s */ + +/* + * MPC8xx dual-ported CPM memory physical addresses + */ +#define PHYSDPRAM (PHYSIMM+0x2000) +#define DPLEN1 0x200 +#define DPLEN2 0x400 +#define DPLEN3 0x800 +#define DPBASE (PHYSDPRAM+DPLEN1) + +#define KEEP_ALIVE_KEY 0x55ccaa33 /* clock and rtc register key */ diff --git a/os/fads/mkfile b/os/fads/mkfile new file mode 100644 index 00000000..afc241a1 --- /dev/null +++ b/os/fads/mkfile @@ -0,0 +1,107 @@ +SYSTARG=Inferno +OBJTYPE=power +<../../mkconfig + +#Configurable parameters + +CONF=fads #default configuration +CONFLIST=fads fadskfs fads2 +CLEANCONFLIST=paq +KZERO=0x20003020 + +SYSTARG=$OSTARG +OBJTYPE=power +INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin #path of directory where kernel is installed +#INSTALLDIR=/$OBJTYPE + +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS + +OBJ=\ + l.$O\ + tlb.$O\ + nofp.$O\ + clock.$O\ + cpm.$O\ + faultpower.$O\ + fpi.$O\ + fpimem.$O\ + fpipower.$O\ + kbd.$O\ + main.$O\ + mmu.$O\ + rmap.$O\ + trap.$O\ + $CONF.root.$O\ + $IP\ + $DEVS\ + $ETHERS\ + $LINKS\ + $VGAS\ + $PORT\ + $MISC\ + $OTHERS\ + +LIBNAMES=${LIBS:%=lib%.a} + +HFILES=\ + mem.h\ + dat.h\ + fns.h\ + io.h\ + ../mpc/800io.h\ + ../mpc/screen.h\ + +CFLAGS=-wFV -I. -I../mpc -I../port -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp +KERNDATE=`{$NDATE} + +#default:V: i$CONF.sq +default:V: i$CONF + +i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD -o $target -T$KZERO -l $OBJ $CONF.$O $LIBFILES + $KSIZE $target + +i$CONF.sq: i$CONF + sqz -w i$CONF >$target + +install:V: i$CONF # i$CONF.sq + cp i$CONF $INSTALLDIR/i$CONF + #cp i$CONF.sq $INSTALLDIR/i$CONF.sq + +uninstall:V: + rm -f $ROOT/$OBJDIR/bin/i$CONF + rm -f $ROOT/$OBJDIR/bin/i$CONF.sq + +<../port/portmkfile + +../init/$INIT.dis: ../init/$INIT.b + cd ../init; mk $INIT.dis + +%.$O: ../mpc/%.c + $CC $CFLAGS -I. -I../mpc ../mpc/$stem.c + +%.$O: ../mpc/%.s + $AS -I. -I../mpc ../mpc/$stem.s + +clock.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +devether.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +faultpower.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +main.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +trap.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +screen.h:NV: ../mpc/screen.h + +devether.$O $ETHERS: ../mpc/etherif.h ../port/netif.h + +#$VGAS: screen.h vga.h +$IP devip.$O: ../ip/ip.h + +devboot.$O: devboot.c + $CC $CFLAGS devboot.c + +devuart.$O: ../mpc/devuart.c + $CC $CFLAGS ../mpc/devuart.c diff --git a/os/fads/mmu.c b/os/fads/mmu.c new file mode 100644 index 00000000..c9cd758b --- /dev/null +++ b/os/fads/mmu.c @@ -0,0 +1,20 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +void +mmuinit(void) +{ + /* the l.s initial TLB settings do all that's required */ +} + +int +segflush(void *a, ulong n) +{ + /* flush dcache then invalidate icache */ + dcflush(a, n); + icflush(a, n); + return 0; +} diff --git a/os/fads/tlb.s b/os/fads/tlb.s new file mode 100644 index 00000000..68ebf4e7 --- /dev/null +++ b/os/fads/tlb.s @@ -0,0 +1,23 @@ +#include "mem.h" + +#define MB (1024*1024) + +/* + * TLB prototype entries, loaded once-for-all at startup, + * remaining unchanged thereafter. + * Limit the table to at most 8 entries to ensure + * it works on the 823 (other 8xx processors allow up to 32 TLB entries). + */ +#define TLBE(epn,rpn,twc) WORD $(epn); WORD $(twc); WORD $(rpn) + +TEXT tlbtab(SB), $-4 + + /* epn, rpn, twc */ + TLBE(KZERO|PHYSDRAM|TLBVALID, PHYSDRAM|PTEWRITE|PTELPS|PTESH|PTEVALID, PTE8MB|/*PTEWT|*/PTEVALID) /* DRAM, 8M */ + TLBE(KZERO|PHYSBCSR|TLBVALID, PHYSBCSR|PTEWRITE|PTESH|PTECI|PTEVALID, PTE4K|PTEWT|PTEVALID) /* Board CSR, 4K */ + TLBE(KZERO|PHYSIMM|TLBVALID, PHYSIMM|PTEWRITE|PTELPS|PTESH|PTECI|PTEVALID, PTE4K|PTEWT|PTEVALID) /* IMMR, 16K */ + TLBE(KZERO|PHYSFLASH|TLBVALID, PHYSFLASH|PTEWRITE|PTELPS|PTESH|PTECI|PTEVALID, PTE8MB|PTEWT|PTEVALID) /* Flash, 8M */ + TLBE(KZERO|PHYSSDRAM|TLBVALID, PHYSSDRAM|PTEWRITE|PTELPS|PTESH|PTEVALID, PTE8MB|/*PTEWT|*/PTEVALID) /* SDRAM, 8M */ + TLBE(KZERO|PHYSPCMCIA|TLBVALID, PHYSPCMCIA|PTEWRITE|PTELPS|PTESH|PTECI|PTEVALID, PTE8MB|PTEWT|PTEG|PTEVALID) /* PCMCIA, 8M */ +TEXT tlbtabe(SB), $-4 + RETURN diff --git a/os/init/README b/os/init/README new file mode 100644 index 00000000..da86706f --- /dev/null +++ b/os/init/README @@ -0,0 +1,5 @@ +initialisation programs used by various native kernels to set up +the right environment to invoke a shell, a window system, mux, or +any specialised application. + +even so, there are really more than needed here just now. diff --git a/os/init/bootinit.b b/os/init/bootinit.b new file mode 100644 index 00000000..0fcd58ef --- /dev/null +++ b/os/init/bootinit.b @@ -0,0 +1,628 @@ +# +# Generalized boot Inferno +# + +implement Init; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + +include "keyring.m"; + kr: Keyring; + +include "security.m"; + auth: Auth; + random: Random; + +include "tftp.m"; + +Bootpreadlen: con 128; + +Init: module +{ + init: fn(); +}; + +ip: string; +mask: string; +fsip: string; +bootprotocol: string; +bootserver: string; +bootfile: string; + +debug: con 0; + +init() +{ + ipavailable: int; + sys = load Sys Sys->PATH; + + kexecfd := sys->open("#B/kexec", Sys->OWRITE); + if (kexecfd == nil) + fatal(sys->sprint("opening #B/kexec: %r")); + + ipavailable = 0; + if (dobind("#l", "/net", sys->MREPL) && dobind("#I", "/net", sys->MAFTER)) + ipavailable = 1; + + dobind("#c", "/dev", sys->MAFTER); # console device + + if (!ipavailable) + fatal("no IP stack available"); + cfd := sys->open("/net/ipifc/clone", sys->ORDWR); + if(cfd == nil) + fatal(sys->sprint("open /net/ipifc/clone: %r")); + + if (sys->fprint(cfd, "bind ether ether0") < 0) + fatal(sys->sprint("binding ether0: %r")); + + fsready := 0; + + fsip = ipconfig(cfd); + + bootstring := getenvdefault("bootpath", "tftp"); + + (bootprotocol, bootserver, bootfile) = parsebootstring(bootstring); + + if (bootprotocol == nil) + fatal(bootstring + ": unrecognised syntax"); + + # Run dhcp if necessary + if (bootprotocol == "tftp" && (bootserver == nil || bootfile == nil)) + dhcp(); + + # determine server + if (bootprotocol == "net" && bootserver == nil) + bootserver = fsip; + + if (bootserver == nil) + fatal("couldn't determine boot server"); + + if (bootfile == nil) + fatal("couldn't determine boot file"); + + if (bootprotocol == nil) + fatal("couldn't determine boot protocol"); + + sys->print("loading %s!%s!%s\n", bootprotocol, bootserver, bootfile); + + if (bootprotocol == "net") { + sys->print("Attempting remote mount\n"); + if (netfs(bootserver) == 0) + sys->print("Remote mount successful\n"); + else + fatal(sys->sprint("Remote mount failed: %r")); + fd := sys->open("/n/remote" + bootfile, Sys->OREAD); + if (fd == nil) + fatal(sys->sprint("%s:/n/remote%s: %r", bootserver, bootfile)); + if (sys->stream(fd, kexecfd, 4096) < 0) + fatal(sys->sprint("copying %s: %r", bootfile)); + } + else if (bootprotocol == "tftp") { + tftp := load Tftp Tftp->PATH; + if (tftp == nil) + fatal("can't load tftp module"); + tftp->init(1); + errstr := tftp->receive(bootserver, bootfile, kexecfd); + if (errstr != nil) + fatal("tftp: " + errstr); + } + else + fatal("protocol " + bootprotocol + " not supported"); + sys->print("Launching new kernel\n"); + kexecfd = nil; +} + +parsebootstring(s: string): (string, string, string) +{ + proto, server, file: string; + (n, l) := sys->tokenize(s, "!"); + if (n > 3) + return (nil, nil, nil); + proto = hd l; + l = tl l; + if (l != nil) { + server = hd l; + l = tl l; + } + if (l != nil) + file = hd l; + case proto { + "tftp" => + ; + "net" => + # can't have a default file, so n must be 3 + if (n != 3) + return (nil, nil, nil); + * => + return (nil, nil, nil); + } + return (proto, server, file); +} + +dobind(f, t: string, flags: int): int +{ + if(sys->bind(f, t, flags) < 0) { + err(sys->sprint("can't bind %s on %s: %r", f, t)); + return 0; + } + return 1; +} + +err(s: string) +{ + sys->fprint(sys->fildes(2), "bootinit: %s\n", s); +} + +hang() +{ + <-(chan of int); +} + +fatal(s: string) +{ + err(s); + hang(); +} + +envlist: list of string; + +getenv(name: string): string +{ + if (envlist == nil) { + fd := sys->open("/dev/sysenv", Sys->OREAD); + if (fd != nil) { + ntok: int; + buf := array[1024] of byte; + nr := sys->read(fd, buf, len buf); + if(nr > 0) + (ntok, envlist) = sys->tokenize(string buf, "\n"); + } + } + ls := envlist; + while(ls != nil) { + (ntok2, ls2) := sys->tokenize(hd ls, "="); + if(hd ls2 == name) + return hd tl ls2; + ls = tl ls; + } + return nil; +} + +getenvdefault(name: string, default: string): string +{ + rv := getenv(name); + if (rv == nil) + return default; + return rv; +} + +ipconfig(cfd: ref sys->FD): string +{ + ip = getenv("wireip"); + if (ip == nil) + ip = getenv("ip"); + mask = getenv("ipmask"); + fsip = getenv("fsip"); + if (ip != nil && mask != nil) { + sys->print("ip %s %s\n", ip, mask); + sys->fprint(cfd, "add %s %s", ip, mask); + gwip := getenv("gwip"); + if (gwip != nil) { + sys->print("gwip %s\n", gwip); + rfd := sys->open("/net/iproute", Sys->ORDWR); + if (rfd == nil || sys->fprint(rfd, "add 0.0.0.0 0.0.0.0 %s", gwip) < 0) + err(sys->sprint("failed to add default route: %r")); + } + } + if (ip == nil || mask == nil) + return bootp(cfd); + return fsip; +} + +bootpdone: int; + +bootp(cfd: ref sys->FD): string +{ + if (bootpdone == 1) + return fsip; + + bootpdone = 1; + + sys->print("bootp ..."); + + if (sys->fprint(cfd, "bootp") < 0) { + sys->print("init: bootp: %r"); + return nil; + } + + fd := sys->open("/net/bootp", sys->OREAD); + if(fd == nil) { + err(sys->sprint("open /net/bootp: %r")); + return nil; + } + + buf := array[Bootpreadlen] of byte; + nr := sys->read(fd, buf, len buf); + fd = nil; + if(nr <= 0) { + err(sys->sprint("read /net/bootp: %r")); + return nil; + } + (ntok, ls) := sys->tokenize(string buf, " \t\n"); + while(ls != nil) { + name := hd ls; + ls = tl ls; + if (ls == nil) + break; + value := hd ls; + ls = tl ls; + if (name == "fsip") + fsip = value; + else if (name == "ipaddr") + ip = value; + else if (name == "ipmask") + mask = value; + } + return fsip; +} + +netfs(server: string): int +{ + auth = load Auth Auth->PATH; + if (auth != nil) + auth->init(); + + kr = load Keyring Keyring->PATH; + sys->print("dial..."); + (ok, c) := sys->dial("tcp!" + server + "!6666", nil); + if(ok < 0) + return -1; + + if(kr != nil && auth != nil){ + err: string; + sys->print("Authenticate ..."); + ai := kr->readauthinfo("/nvfs/default"); + if(ai == nil){ + sys->print("readauthinfo /nvfs/default failed: %r\n"); + sys->print("trying mount as `nobody'\n"); + } + (c.dfd, err) = auth->client("none", ai, c.dfd); + if(c.dfd == nil){ + sys->print("authentication failed: %s\n", err); + return -1; + } + } + + sys->print("mount ..."); + + c.cfd = nil; + n := sys->mount(c.dfd, nil, "/n/remote", sys->MREPL, ""); + if(n > 0) + return 0; + return -1; +} + +# +# +# DHCP +# +# + +Dhcp: adt { + op: int; + htype: int; + hops: int; + xid: int; + secs: int; + flags: int; + ciaddr: int; + yiaddr: int; + siaddr: int; + giaddr: int; + chaddr: array of byte; + sname: string; + file: string; +}; + +nboputl(buf: array of byte, val: int) +{ + buf[0] = byte (val >> 24); + buf[1] = byte (val >> 16); + buf[2] = byte (val >> 8); + buf[3] = byte val; +} + +nboputs(buf: array of byte, val: int) +{ + buf[0] = byte (val >> 8); + buf[1] = byte val; +} + +nbogets(buf: array of byte): int +{ + return (int buf[0] << 8) | int buf[1]; +} + +nbogetl(buf: array of byte): int +{ + return (int buf[0] << 24) | (int buf[1] << 16) | (int buf[2] << 8) | int buf[3]; +} + +stringget(buf: array of byte): string +{ + for (x := 0; x < len buf; x++) + if (buf[x] == byte 0) + break; + if (x == 0) + return nil; + return string buf[0 : x]; +} + +memcmp(b1: array of byte, b2: array of byte): int +{ + l := len b1; + if (l < len b2) + return int -b2[l]; + if (l > len b2) + return int b1[l]; + for (i := 0; i < l; i++) { + d := int b1[i] - int b2[i]; + if (d != 0) + return d; + } + return 0; +} + +memncpy(out: array of byte, in: array of byte) +{ + if (in == nil) + return; + l := len in; + if (l > len out) + l = len out; + out[0 :] = in[0 : l]; +} + +memset(out: array of byte, val: byte) +{ + for (l := 0; l < len out; l++) + out[l] = val; +} + +dhcpsend(dfd: ref Sys->FD, dhcp: ref Dhcp) +{ + buf := array[576] of byte; + buf[0] = byte dhcp.op; + buf[1] = byte dhcp.htype; + buf[2] = byte len dhcp.chaddr; + buf[3] = byte dhcp.hops; + nboputl(buf[4 : 8], dhcp.xid); + nboputs(buf[8 : 10], dhcp.secs); + nboputs(buf[10 : 12], dhcp.flags); + nboputl(buf[12 : 16], dhcp.ciaddr); + nboputl(buf[16 : 20], dhcp.yiaddr); + nboputl(buf[20 : 24], dhcp.siaddr); + nboputl(buf[24 : 28], dhcp.giaddr); + memset(buf[28 :], byte 0); + memncpy(buf[28 : 44], dhcp.chaddr); + memncpy(buf[44 : 108], array of byte dhcp.sname); + memncpy(buf[108 : 236], array of byte dhcp.file); + sys->write(dfd, buf, len buf); +} + +kill(pid: int) +{ + fd := sys->open("#p/" + string pid + "/ctl", sys->OWRITE); + if (fd == nil) + return; + + msg := array of byte "kill"; + sys->write(fd, msg, len msg); +} + +ipfmt(ipaddr: int): string +{ + return sys->sprint("%ud.%ud.%ud.%ud", + (ipaddr >> 24) & 16rff, + (ipaddr >> 16) & 16rff, + (ipaddr >> 8) & 16rff, + ipaddr & 16rff); +} + +dumpdhcp(dhcp: ref Dhcp) +{ + sys->print("op %d htype %d hops %d xid %ud\n", dhcp.op, dhcp.htype, dhcp.hops, dhcp.xid); + sys->print("secs %d flags 0x%.4ux\n", dhcp.secs, dhcp.flags); + sys->print("ciaddr %s\n", ipfmt(dhcp.ciaddr)); + sys->print("yiaddr %s\n", ipfmt(dhcp.yiaddr)); + sys->print("siaddr %s\n", ipfmt(dhcp.siaddr)); + sys->print("giaddr %s\n", ipfmt(dhcp.giaddr)); + sys->print("chaddr "); + for (x := 0; x < len dhcp.chaddr; x++) + sys->print("%.2ux", int dhcp.chaddr[x]); + sys->print("\n"); + if (dhcp.sname != nil) + sys->print("sname %s\n", dhcp.sname); + if (dhcp.file != nil) + sys->print("file %s\n", dhcp.file); +} + +dhcplisten(pidc: chan of int, fd: ref Sys->FD, dc: chan of ref Dhcp) +{ + pid := sys->pctl(0, nil); + pidc <-= pid; + buf := array [576] of byte; + while (1) { + n := sys->read(fd, buf, len buf); + dhcp := ref Dhcp; + dhcp.op = int buf[0]; + dhcp.htype = int buf[1]; + hlen := int buf[2]; + dhcp.hops = int buf[3]; + dhcp.xid = nbogetl(buf[4 : 8]); + dhcp.secs = nbogets(buf[8 : 10]); + dhcp.flags = nbogets(buf[10 : 12]); + dhcp.ciaddr = nbogetl(buf[12 : 16]); + dhcp.yiaddr = nbogetl(buf[16 : 20]); + dhcp.siaddr = nbogetl(buf[20 : 24]); + dhcp.giaddr = nbogetl(buf[24: 28]); + dhcp.chaddr = buf[28 : 28 + hlen]; + dhcp.sname = stringget(buf[44 : 108]); + dhcp.file = stringget(buf[108 : 236]); + dc <-= dhcp; + } +} + +timeoutproc(pid: chan of int, howlong: int, c: chan of string) +{ + pid <-= sys->pctl(0, nil); + + sys->sleep(howlong); + + # send timeout + c <-= "timed out"; +} + +tpid := -1; +tc: chan of string; + +timeoutcancel() +{ + if (tpid >= 0) { + kill(tpid); + tpid = -1; + } +} + +timeoutstart(howlong: int): (chan of string) +{ + timeoutcancel(); + pidc := chan of int; + tc = chan of string; + spawn timeoutproc(pidc, howlong, tc); + tpid = <- pidc; + return tc; +} + +atohn(b: byte): int +{ + if (b >= byte '0' && b <= byte '9') + return int (b - byte '0'); + if (b >= byte 'A' && b <= byte 'F') + return int b - 'A' + 10; + if (b >= byte 'a' && b <= byte 'f') + return int b - 'a' + 10; + return -1; +} + +atohb(buf: array of byte): int +{ + tn := atohn(buf[0]); + bn := atohn(buf[1]); + if (tn < 0 || bn < 0) + return -1; + return tn * 16 + bn; +} + +gethaddr(dhcp: ref Dhcp): int +{ + fd := sys->open("#l/ether0/addr", Sys->OREAD); + if (fd == nil) + return 0; + buf := array [100] of byte; + n := sys->read(fd, buf, len buf); + if (n < 0) + return 0; + dhcp.htype = 1; + hlen := n / 2; + dhcp.chaddr = array [hlen] of byte; + for (i := 0; i < hlen; i++) + dhcp.chaddr[i] = byte atohb(buf[i * 2 : i * 2 + 2]); + return 1; +} + +parsedq(dq: string): (int, int) +{ + (c, l) := sys->tokenize(dq, "."); + if (c != 4) + return (0, 0); + a := hd l; + l = tl l; + b := hd l; + l = tl l; + d := hd l; + l = tl l; + addr := (int a << 24) | (int b << 16) | (int d << 8) | int hd l; + return (1, addr); +} + +dhcp() +{ + ok: int; + conn: Sys->Connection; + rdhcp: ref Dhcp; + + if (random == nil) + random = load Random Random->PATH; + + (ok, conn) = sys->dial("udp!255.255.255.255!67", "68"); + if (!ok) + fatal(sys->sprint("failed to dial udp broadcast: %r")); + + pidc := chan of int; + dc := chan of ref Dhcp; + spawn dhcplisten(pidc, conn.dfd, dc); + dhcppid := <- pidc; + dhcp := ref Dhcp; + dhcp.op = 1; + dhcp.htype = 1; + gethaddr(dhcp); + dhcp.hops = 0; + dhcp.xid = random->randomint(Random->NotQuiteRandom); + dhcp.secs = 0; + dhcp.flags = 0; + (ok, dhcp.ciaddr) = parsedq(ip); + dhcp.yiaddr = 0; + dhcp.siaddr = 0; + dhcp.giaddr = 0; + if (bootfile != "bootp") + dhcp.file = bootfile; + else + dhcp.file = nil; + ok = 0; + for (count := 0; !ok && count < 5; count++) { + mtc := timeoutstart(3000); + dhcpsend(conn.dfd, dhcp); + timedout := 0; + do { + alt { + <- mtc => + timedout = 1; + rdhcp = <- dc => + if (debug) + dumpdhcp(rdhcp); + if (rdhcp.ciaddr != dhcp.ciaddr || rdhcp.xid != dhcp.xid + || memcmp(rdhcp.chaddr, dhcp.chaddr) != 0) { + break; + } + if (rdhcp.file != nil) { + ok = 1; + timeoutcancel(); + } + } + } while (!timedout && !ok); + dhcp.xid++; + } + if (ok) { + if (bootfile == nil) + bootfile = rdhcp.file; + if (bootserver == nil) + bootserver = ipfmt(rdhcp.siaddr); + } + else + err("bootp timed out"); + kill(dhcppid); +} diff --git a/os/init/cerf405.b b/os/init/cerf405.b new file mode 100644 index 00000000..b02dd5a2 --- /dev/null +++ b/os/init/cerf405.b @@ -0,0 +1,598 @@ +# +# Intrinsyc Cerf cube 405EP, also Manga switch +# +# this encrusted version will be simplified shortly +# + +implement Init; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + +include "keyring.m"; + kr: Keyring; + +include "security.m"; + auth: Auth; + +include "dhcp.m"; + dhcpclient: Dhcpclient; + Bootconf: import dhcpclient; + +include "sh.m"; + +Init: module +{ + init: fn(); +}; + +Bootpreadlen: con 128; +Microsec: con 1000000; +Notime: con big 800000000 * big Microsec; # fairly arbitrary time in 1995 to check validity + +# conventional Inferno NAND flash partitions +nandparts := array[] of { + # bootstrap from 0 to 0x210000 + "add boot 0 0x210000", + "add fs 0x210000 end" +}; + +userdefault := array[] of { + "inferno inferno", + "sys sys" +}; + +ethername := "/net/ether0"; + +# +# initialise flash translation +# mount flash file system +# add devices +# start a shell or window manager +# + +init() +{ + sys = load Sys Sys->PATH; + kr = load Keyring Keyring->PATH; + + sys->bind("/", "/", Sys->MREPL); + if(sys->bind("/boot", "/", Sys->MAFTER) < 0) + sys->print("can't bind /boot after /: %r\n"); + sys->bind("/boot/nvfs", "/nvfs", Sys->MREPL); + + auth = load Auth Auth->PATH; + if(auth != nil) + auth->init(); + + localok := 0; + if(lfs() >= 0){ + # let's just take a closer look + sys->bind("/n/local/nvfs", "/nvfs", Sys->MBEFORE|Sys->MCREATE); + (rc, nil) := sys->stat("/n/local/dis/sh.dis"); + if(rc >= 0) + localok = 1; + else + err("local file system unusable"); + } + + netok := sys->bind("#l", "/net", Sys->MREPL) >= 0; + if(!netok){ + netok = sys->bind("#l1", "/net", Sys->MREPL) >= 0; + if(netok) + ethername = "/net/ether1"; + } + if(netok) + configether(); + + dobind("#I", "/net", sys->MAFTER); # IP + dobind("#p", "/prog", sys->MREPL); # prog + sys->bind("#d", "/fd", Sys->MREPL); + dobind("#c", "/dev", sys->MREPL); # console + dobind("#t", "/dev", sys->MAFTER); # serial line + drawok := sys->bind("#i", "/dev", sys->MAFTER) >= 0; # draw + sys->bind("#m", "/dev", sys->MAFTER); # pointer + sys->bind("#e", "/env", sys->MREPL|sys->MCREATE); # environment + sys->bind("#A", "/dev", Sys->MAFTER); # optional audio + sys->bind("#ʟ", "/dev", Sys->MAFTER); # logfs + + timefile: string; + rootsource: string; + cfd := sys->open("/dev/consctl", Sys->OWRITE); + if(cfd != nil) + sys->fprint(cfd, "rawon"); + for(;;){ + (rootsource, timefile) = askrootsource(localok, netok); + if(rootsource == nil) + break; # internal + (rc, nil) := sys->stat(rootsource+"/dis/sh.dis"); + if(rc < 0) + err("%s has no shell"); + else if(sys->bind(rootsource, "/", Sys->MAFTER) < 0) + sys->print("can't bind %s on /: %r\n", rootsource); + else{ + sys->bind("/n/local", rootsource+"/n/local", Sys->MREPL|Sys->MCREATE); + sys->unmount("#//./boot", "/"); + sys->bind(rootsource+"/dis", "/dis", Sys->MBEFORE|Sys->MCREATE); + break; + } + } + cfd = nil; + + setsysname("cerf"); # set system name + + rtc := big rf("#r/rtc", "0") * big Microsec; + now := big 0; + if(timefile != nil){ # synchronise with remote time if it's valid + now = big rf(timefile, "0"); + if(now < Notime && rootsource != nil) + now = big filetime(rootsource) * big Microsec; # try the time of the root directory + if(now >= Notime){ + setclock("#r/rtc", now/big Microsec); + rtc = now; + } + } + if(now < Notime) + now = rtc; + setclock("/dev/time", now); + + sys->chdir("/"); + if(netok){ + start("ndb/dns", nil); + start("ndb/cs", nil); + } + startup := "/nvfs/startup"; + if(sys->open(startup, Sys->OREAD) != nil){ + shell := load Command Sh->PATH; + if(shell != nil){ + sys->print("Running %s\n", startup); + shell->init(nil, "sh" :: startup :: nil); + } + } + user := rdenv("user", "inferno"); + (ok, nil) := sys->stat("/dis/wm/wm.dis"); + if(drawok && ok >= 0) + (ok, nil) = sys->stat("/dis/wm/logon.dis"); + if(drawok && ok >= 0 && userok(user)){ + wm := load Command "/dis/wm/wm.dis"; + if(wm != nil){ + fd := sys->open("/nvfs/user", Sys->OWRITE); + if(fd != nil){ + sys->fprint(fd, "%s", user); + fd = nil; + } + spawn wm->init(nil, list of {"wm/wm", "wm/logon", "-l", "-u", user}); + exit; + } + sys->print("init: can't load wm/logon: %r"); + } + sh := load Command Sh->PATH; + if(sh == nil){ + err(sys->sprint("can't load %s: %r", Sh->PATH)); + hang(); + } + spawn sh->init(nil, "sh" :: nil); +} + +start(cmd: string, args: list of string) +{ + disfile := cmd; + if(disfile[0] != '/') + disfile = "/dis/"+disfile+".dis"; + (ok, nil) := sys->stat(disfile); + if(ok >= 0){ + dis := load Command disfile; + if(dis == nil) + sys->print("init: can't load %s: %r\n", disfile); + else + spawn dis->init(nil, cmd :: args); + } +} + +dobind(f, t: string, flags: int) +{ + if(sys->bind(f, t, flags) < 0) + err(sys->sprint("can't bind %s on %s: %r", f, t)); +} + +# +# Set system name from nvram if possible +# +setsysname(def: string) +{ + v := array of byte def; + fd := sys->open("/nvfs/ID", sys->OREAD); + if(fd == nil) + fd = sys->open("/env/sysname", sys->OREAD); + if(fd != nil){ + buf := array[Sys->NAMEMAX] of byte; + nr := sys->read(fd, buf, len buf); + while(nr > 0 && buf[nr-1] == byte '\n') + nr--; + if(nr > 0) + v = buf[0:nr]; + } + fd = sys->open("/dev/sysname", sys->OWRITE); + if(fd != nil) + sys->write(fd, v, len v); +} + +filetime(name: string): int +{ + (ok, dir) := sys->stat(name); + if(ok < 0) + return 0; + return dir.atime; +} + +setclock(timefile: string, now: big) +{ + fd := sys->open(timefile, sys->OWRITE); + if(fd == nil) + sys->print("init: can't open %s: %r\n", timefile); + else if(sys->fprint(fd, "%bud", now) < 0) + sys->print("init: can't write to %s: %r\n", timefile); +} + +err(s: string) +{ + sys->fprint(sys->fildes(2), "init: %s\n", s); +} + +hang() +{ + <-chan of int; +} + +tried := 0; + +askrootsource(localok: int, netok: int): (string, string) +{ + stdin := sys->fildes(0); + sources := "kernel" :: nil; + if(netok) + sources = "remote" :: sources; + if(localok){ + sources = "local" :: sources; + if(netok) + sources = "local+remote" :: sources; + } + for(;;){ + s := ""; + if(tried == 0 && (s = rdenv("rootsource", nil)) != nil){ + tried = 1; + sys->print("rootsource: root from %s\n", s); + } else { + sys->print("root from ("); + cm := ""; + for(l := sources; l != nil; l = tl l){ + sys->print("%s%s", cm, hd l); + cm = ","; + } + sys->print(")[%s] ", hd sources); + + s = getline(stdin, hd sources); # default + } + (nil, choice) := sys->tokenize(s, "\t "); + if(choice == nil) + choice = sources; + opt := hd choice; + case opt { + * => + sys->print("\ninvalid boot option: '%s'\n", opt); + "kernel" => + return (nil, nil); + "local" => + return ("/n/local", nil); + "local+remote" => + if(netfs("/n/remote") >= 0) + return ("/n/local", nil); + "remote" => + if(netfs("/n/remote") >= 0) + return ("/n/remote", "/n/remote/dev/time"); + } + } +} + +getline(fd: ref Sys->FD, default: string): string +{ + result := ""; + buf := array[10] of byte; + i := 0; + for(;;){ + n := sys->read(fd, buf[i:], len buf - i); + if(n < 1) + break; + i += n; + while(i >0 && (nutf := sys->utfbytes(buf, i)) > 0){ + s := string buf[0:nutf]; + for(j := 0; j < len s; j++) + case s[j] { + '\b' => + if(result != nil) + result = result[0:len result-1]; + 'u'&16r1F => + sys->print("^U\n"); + result = ""; + '\r' => + ; + * => + sys->print("%c", s[j]); + if(s[j] == '\n' || s[j] >= 16r80){ + if(s[j] != '\n') + result[len result] = s[j]; + if(result == nil) + return default; + return result; + } + result[len result] = s[j]; + } + buf[0:] = buf[nutf:i]; + i -= nutf; + } + } + return default; +} + +# +# serve local file system using logfs +# +lfs(): int +{ + if(!flashpart("#F1/flash1/flashctl", nandparts)) + return -1; + if(!logfsinit("#F1/flash1/fs")) + return -1; + mfd := sys->open("/dev/logfsmain", Sys->ORDWR); + if(mfd == nil){ + sys->print("can't open /dev/logfsmain: %r\n"); + return -1; + } + if(sys->mount(mfd, nil, "/n/local", Sys->MREPL|Sys->MCREATE, nil) < 0){ + sys->print("can't mount /dev/logfsmain on /n/local: %r\n"); + return -1; + } + return 0; +} + +# +# partition flash +# +flashdone := 0; + +flashpart(ctl: string, parts: array of string): int +{ + if(flashdone) + return 1; + cfd := sys->open(ctl, Sys->ORDWR); + if(cfd == nil){ + sys->print("can't open %s: %r\n", ctl); + return 0; + } + for(i := 0; i < len parts; i++) + if(sys->fprint(cfd, "%s", parts[i]) < 0){ + sys->print("can't %q to %s: %r\n", parts[i], ctl); + return 0; + } + flashdone = 1; + return 1; +} + +# +# set up logfs +# +logfsdone := 0; + +logfsinit(flashmem: string): int +{ + if(logfsdone) + return 1; + fd := sys->open("/dev/logfsctl", Sys->OWRITE); + if(fd == nil){ + if(sys->bind("#ʟ", "/dev", Sys->MBEFORE) < 0) + return -1; + fd = sys->open("/dev/logfsctl", Sys->OWRITE); + if(fd == nil){ + sys->print("can't open /dev/logfsctl: %r\n"); + return -1; + } + } + sys->print("Set logfs main on %s...\n", flashmem); + if(!ctlw(fd, "logfs", "fsys main config "+flashmem)) + return -1; + if(!ctlw(fd, "logfs", "fsys main")) + return -1; + cm := rf("#e/logfsformat", nil); + if(cm == "yes"){ + if(!ctlw(fd, "logfs", "format 0")) + return -1; + } + cf := rf("#e/logfsopen", nil); + if(cf == nil) + cf = "open"; + if(!ctlw(fd, "logfs", cf)) + return -1; + for(i := 0; i < len userdefault; i++) + ctlw(fd, "logfs", "uname "+userdefault[i]); + logfsdone = 1; + return 1; +} + +ctlw(fd: ref Sys->FD, w: string, cmd: string): int +{ + if(sys->fprint(fd, "%s", cmd) < 0){ + sys->print("%s ctl %q: %r\n", w, cmd); + return 0; + } + return 1; +} + +configether() +{ + if(ethername == nil) + return; + fd := sys->open("/nvfs/etherparams", Sys->OREAD); + if(fd == nil) + return; + ctl := sys->open(ethername+"/clone", Sys->OWRITE); + if(ctl == nil){ + sys->print("init: can't open %s/clone: %r\n", ethername); + return; + } + b := array[1024] of byte; + n := sys->read(fd, b, len b); + if(n <= 0) + return; + for(i := 0; i < n;){ + for(e := i; e < n && b[e] != byte '\n'; e++) + ; + s := string b[i:e]; + if(sys->fprint(ctl, "%s", s) < 0) + sys->print("init: ctl write to %s/clone: %s: %r\n", ethername, s); + i = e+1; + } +} + +donebind := 0; +server: string; + +# +# set up network mount +# +netfs(mountpt: string): int +{ + fd: ref Sys->FD; + if(!donebind){ + fd = sys->open("/net/ipifc/clone", sys->OWRITE); + if(fd == nil){ + sys->print("init: open /net/ipifc/clone: %r\n"); + return -1; + } + if(sys->fprint(fd, "bind ether %s", ethername) < 0){ + sys->print("could not bind %s interface: %r\n", ethername); + return -1; + } + donebind = 1; + }else{ + fd = sys->open("/net/ipifc/0/ctl", Sys->OWRITE); + if(fd == nil){ + sys->print("init: can't reopen /net/ipifc/0/ctl: %r\n"); + return -1; + } + } + server = rdenv("fsip", nil); + if((ip := rdenv("ip", nil)) != nil){ + sys->print("**using %s\n", ip); + sys->fprint(fd, "bind ether /net/ether0"); + s := rdenv("ipmask", nil); + if(s == nil) + s = rdenv("netmask", nil); # alternative name used by some bootstraps + sys->fprint(fd, "add %s %s", ip, s); + gate := rdenv("ipgw", nil); + if(gate == nil) + gate = rdenv("gateway", nil); + if(gate != nil){ + rfd := sys->open("/net/iproute", Sys->OWRITE); + if(rfd != nil){ + sys->fprint(rfd, "add 0 0 %s", gate); + sys->print("set gateway %s\n", gate); + } + } + }else if(server == nil){ + sys->print("dhcp..."); + dhcpclient = load Dhcpclient Dhcpclient->PATH; + if(dhcpclient == nil){ + sys->print("can't load dhcpclient: %r\n"); + return -1; + } + dhcpclient->init(); + (cfg, nil, e) := dhcpclient->dhcp("/net", fd, "/net/ether0/addr", nil, nil); + if(e != nil){ + sys->print("dhcp: %s\n", e); + return -1; + } + if(server == nil) + server = cfg.getip(Dhcpclient->OP9fs); + dhcpclient = nil; + } + if(server == nil || server == "0.0.0.0"){ + sys->print("no file server address\n"); + return -1; + } + sys->print("fs=%s\n", server); + + net := "tcp"; # how to specify il? + svcname := net + "!" + server + "!6666"; + + sys->print("dial %s...", svcname); + + (ok, c) := sys->dial(svcname, nil); + if(ok < 0){ + sys->print("can't dial %s: %r\n", svcname); + return -1; + } + + sys->print("\nConnected ...\n"); + if(kr != nil){ + err: string; + sys->print("Authenticate ..."); + ai := kr->readauthinfo("/nvfs/default"); + if(ai == nil){ + sys->print("readauthinfo /nvfs/default failed: %r\n"); + sys->print("trying mount as `none'\n"); + } + (c.dfd, err) = auth->client("none", ai, c.dfd); + if(c.dfd == nil){ + sys->print("authentication failed: %s\n", err); + return -1; + } + } + + sys->print("mount %s...", mountpt); + + c.cfd = nil; + n := sys->mount(c.dfd, nil, mountpt, Sys->MREPL, ""); + if(n > 0) + return 0; + if(n < 0) + sys->print("%r"); + return -1; +} + +username(def: string): string +{ + return rdenv("user", def); +} + +userok(user: string): int +{ + (ok, d) := sys->stat("/usr/"+user); + return ok >= 0 && (d.mode & Sys->DMDIR) != 0; +} + +rdenv(name: string, def: string): string +{ + s := rf("#e/"+name, nil); + if(s != nil) + return s; + return rf("/nvfs/"+name, def); +} + +rf(file: string, default: string): string +{ + fd := sys->open(file, Sys->OREAD); + if(fd != nil){ + buf := array[128] of byte; + nr := sys->read(fd, buf, len buf); + if(nr > 0){ + s := string buf[0:nr]; + while(s != nil && ((c := s[len s-1]) == '\n' || c == '\r')) + s = s[0: len s-1]; + if(s != nil) + return s; + } + } + return default; +} diff --git a/os/init/cerfinit.b b/os/init/cerfinit.b new file mode 100644 index 00000000..96d2b784 --- /dev/null +++ b/os/init/cerfinit.b @@ -0,0 +1,612 @@ +# +# Intrinsyc Cerf cube +# + +implement Init; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + +include "keyring.m"; + kr: Keyring; + +include "security.m"; + auth: Auth; + +include "sh.m"; + +Init: module +{ + init: fn(); +}; + +Bootpreadlen: con 128; + +# standard flash partitions + +flashparts := array[] of { + # bootstrap at 0x0 to 0x20000 + "add script 0x20000 0x40000", + "add kernel 0x100000 0x200000", + "add fs 0x200000 end", +}; + +ethername := "ether0"; + +# +# initialise flash translation +# mount flash file system +# add devices +# start a shell or window manager +# + +init() +{ + sys = load Sys Sys->PATH; + kr = load Keyring Keyring->PATH; + auth = load Auth Auth->PATH; + if(auth != nil) + auth->init(); + + sys->bind("/", "/", Sys->MREPL); + + localok := 0; + if(lfs() >= 0){ + # let's just take a closer look + sys->bind("/n/local/nvfs", "/nvfs", Sys->MREPL|Sys->MCREATE); + (rc, nil) := sys->stat("/n/local/dis/sh.dis"); + if(rc >= 0) + localok = 1; + else + err("local file system unusable"); + } + netok := sys->bind("#l", "/net", Sys->MREPL) >= 0; + if(!netok){ + netok = sys->bind("#l1", "/net", Sys->MREPL) >= 0; + if(netok) + ethername = "ether1"; + } + if(netok) + configether(); + dobind("#I", "/net", sys->MAFTER); # IP + dobind("#p", "/prog", sys->MREPL); # prog + sys->bind("#d", "/fd", Sys->MREPL); + dobind("#c", "/dev", sys->MREPL); # console + dobind("#t", "/dev", sys->MAFTER); # serial line + drawok := sys->bind("#i", "/dev", sys->MAFTER) >= 0; # draw + sys->bind("#m", "/dev", sys->MAFTER); # pointer + sys->bind("#e", "/env", sys->MREPL|sys->MCREATE); # environment + sys->bind("#A", "/dev", Sys->MAFTER); # optional audio + timefile: string; + rootsource: string; + scale := 1; + cfd := sys->open("/dev/consctl", Sys->OWRITE); + if(cfd != nil) + sys->fprint(cfd, "rawon"); + for(;;){ + (rootsource, timefile, scale) = askrootsource(localok, netok); + if(rootsource == nil) + break; # internal + (rc, nil) := sys->stat(rootsource+"/dis/sh.dis"); + if(rc < 0) + err("%s has no shell"); + else if(sys->bind(rootsource, "/", Sys->MAFTER) < 0) + sys->print("can't bind %s on /: %r\n", rootsource); + else{ + sys->bind(rootsource+"/dis", "/dis", Sys->MBEFORE|Sys->MCREATE); + break; + } + } + cfd = nil; + + setsysname("cerf"); # set system name + + now := getclock(timefile, rootsource); + if(scale == 1) + now *= big 1000000; + setclock("/dev/time", now); + if(timefile != "#r/rtc") + setclock("#r/rtc", now/big 1000000); + + sys->chdir("/"); + if(netok){ + start("ndb/dns", nil); + start("ndb/cs", nil); + } + startup := "/nvfs/startup"; + if(sys->open(startup, Sys->OREAD) != nil){ + shell := load Command Sh->PATH; + if(shell != nil){ + sys->print("Running %s\n", startup); + shell->init(nil, "sh" :: startup :: nil); + } + } + user := username("inferno"); + (ok, nil) := sys->stat("/dis/wm/wm.dis"); + if(drawok && ok >= 0) + (ok, nil) = sys->stat("/dis/wm/logon.dis"); + if(drawok && ok >= 0 && userok(user)){ + wm := load Command "/dis/wm/wm.dis"; + if(wm != nil){ + fd := sys->open("/nvfs/user", Sys->OWRITE); + if(fd != nil){ + sys->fprint(fd, "%s", user); + fd = nil; + } + spawn wm->init(nil, list of {"wm/wm", "wm/logon", "-l", "-u", user}); + exit; + } + sys->print("init: can't load wm/logon: %r"); + } + sh := load Command Sh->PATH; + if(sh == nil){ + err(sys->sprint("can't load %s: %r", Sh->PATH)); + hang(); + } + spawn sh->init(nil, "sh" :: nil); +} + +start(cmd: string, args: list of string) +{ + disfile := cmd; + if(disfile[0] != '/') + disfile = "/dis/"+disfile+".dis"; + (ok, nil) := sys->stat(disfile); + if(ok >= 0){ + dis := load Command disfile; + if(dis == nil) + sys->print("init: can't load %s: %r\n", disfile); + else + spawn dis->init(nil, cmd :: args); + } +} + +dobind(f, t: string, flags: int) +{ + if(sys->bind(f, t, flags) < 0) + err(sys->sprint("can't bind %s on %s: %r", f, t)); +} + +# +# Set system name from nvram if possible +# +setsysname(def: string) +{ + v := array of byte def; + fd := sys->open("/nvfs/ID", sys->OREAD); + if(fd == nil) + fd = sys->open("/env/sysname", sys->OREAD); + if(fd != nil){ + buf := array[Sys->NAMEMAX] of byte; + nr := sys->read(fd, buf, len buf); + while(nr > 0 && buf[nr-1] == byte '\n') + nr--; + if(nr > 0) + v = buf[0:nr]; + } + fd = sys->open("/dev/sysname", sys->OWRITE); + if(fd != nil) + sys->write(fd, v, len v); +} + +getclock(timefile: string, timedir: string): big +{ + now := big 0; + if(timefile != nil){ + fd := sys->open(timefile, Sys->OREAD); + if(fd != nil){ + b := array[64] of byte; + n := sys->read(fd, b, len b-1); + if(n > 0){ + now = big string b[0:n]; + if(now <= big 16r20000000) + now = big 0; # remote itself is not initialised + } + } + } + if(now == big 0){ + if(timedir != nil){ + (ok, dir) := sys->stat(timedir); + if(ok < 0) { + sys->print("init: stat %s: %r", timedir); + return big 0; + } + now = big dir.atime; + }else{ + now = big 993826747000000; + sys->print("time warped\n"); + } + } + return now; +} + +setclock(timefile: string, now: big) +{ + fd := sys->open(timefile, sys->OWRITE); + if (fd == nil) { + sys->print("init: can't open %s: %r", timefile); + return; + } + + b := sys->aprint("%ubd", now); + if (sys->write(fd, b, len b) != len b) + sys->print("init: can't write to %s: %r", timefile); +} + +srv() +{ + sys->print("remote debug srv..."); + fd := sys->open("/dev/eia0ctl", Sys->OWRITE); + if(fd != nil) + sys->fprint(fd, "b115200"); + + fd = sys->open("/dev/eia0", Sys->ORDWR); + if (fd == nil){ + err(sys->sprint("can't open /dev/eia0: %r")); + return; + } + if (sys->export(fd, "/", Sys->EXPASYNC) < 0){ + err(sys->sprint("can't export on serial port: %r")); + return; + } +} + +err(s: string) +{ + sys->fprint(sys->fildes(2), "init: %s\n", s); +} + +hang() +{ + <-chan of int; +} + +tried := 0; + +askrootsource(localok: int, netok: int): (string, string, int) +{ + stdin := sys->fildes(0); + sources := "kernel" :: nil; + if(netok) + sources = "remote" :: sources; + if(localok){ + sources = "local" :: sources; + if(netok) + sources = "local+remote" :: sources; + } + for(;;) { + s := ""; + if (tried == 0 && (s = rf("/nvfs/rootsource", nil)) != nil) { + tried = 1; + if (s[len s - 1] == '\n') + s = s[:len s - 1]; + sys->print("/nvfs/rootsource: root from %s\n", s); + } else { + sys->print("root from ("); + cm := ""; + for(l := sources; l != nil; l = tl l){ + sys->print("%s%s", cm, hd l); + cm = ","; + } + sys->print(")[%s] ", hd sources); + + s = getline(stdin, hd sources); # default + } + (nil, choice) := sys->tokenize(s, "\t "); + if(choice == nil) + choice = sources; + opt := hd choice; + case opt { + * => + sys->print("\ninvalid boot option: '%s'\n", opt); + "kernel" => + return (nil, "#r/rtc", 1); + "local" => + return ("/n/local", "#r/rtc", 1); + "local+remote" => + if(netfs("/n/remote") >= 0) + return ("/n/local", "/n/remote/dev/time", 1000000); + "remote" => + if(netfs("/n/remote") >= 0) + return ("/n/remote", "/n/remote/dev/time", 1000000); + } + } +} + +getline(fd: ref Sys->FD, default: string): string +{ + result := ""; + buf := array[10] of byte; + i := 0; + for(;;) { + n := sys->read(fd, buf[i:], len buf - i); + if(n < 1) + break; + i += n; + while(i >0 && (nutf := sys->utfbytes(buf, i)) > 0){ + s := string buf[0:nutf]; + for (j := 0; j < len s; j++) + case s[j] { + '\b' => + if(result != nil) + result = result[0:len result-1]; + 'u'&16r1F => + sys->print("^U\n"); + result = ""; + '\r' => + ; + * => + sys->print("%c", s[j]); + if(s[j] == '\n' || s[j] >= 16r80){ + if(s[j] != '\n') + result[len result] = s[j]; + if(result == nil) + return default; + return result; + } + result[len result] = s[j]; + } + buf[0:] = buf[nutf:i]; + i -= nutf; + } + } + return default; +} + +# +# serve local DOS file system using flash translation layer +# +lfs(): int +{ + if(!flashpart("#F/flash/flashctl", flashparts)) + return -1; + if(!ftlinit("#F/flash/fs")) + return -1; + c := chan of string; + spawn startfs(c, "/dis/dossrv.dis", "dossrv" :: "-f" :: "#X/ftldata" :: "-m" :: "/n/local" :: nil); + if(<-c != nil) + return -1; + return 0; +} + +startfs(c: chan of string, file: string, args: list of string) +{ + fs := load Command file; + if(fs == nil){ + sys->print("can't load %s: %r\n", file); + c <-= "load failed"; + } + { + fs->init(nil, args); + }exception e { + "*" => + c <-= "failed"; + exit; + * => + c <-= "unknown exception"; + exit; + } + c <-= nil; +} + +# +# partition flash +# +flashdone := 0; + +flashpart(ctl: string, parts: array of string): int +{ + if(flashdone) + return 1; + cfd := sys->open(ctl, Sys->ORDWR); + if(cfd == nil){ + sys->print("can't open %s: %r\n", ctl); + return 0; + } + for(i := 0; i < len parts; i++) + if(sys->fprint(cfd, "%s", parts[i]) < 0){ + sys->print("can't %q to %s: %r\n", parts[i], ctl); + return 0; + } + flashdone = 1; + return 1; +} + +# +# set up flash translation layer +# +ftldone := 0; + +ftlinit(flashmem: string): int +{ + if(ftldone) + return 1; + sys->print("Set flash translation of %s...\n", flashmem); + fd := sys->open("#X/ftlctl", Sys->OWRITE); + if(fd == nil){ + sys->print("can't open #X/ftlctl: %r\n"); + return 0; + } + if(sys->fprint(fd, "init %s", flashmem) <= 0){ + sys->print("can't init flash translation: %r\n"); + return 0; + } + ftldone = 1; + return 1; +} + +configether() +{ + if(ethername == nil) + return; + fd := sys->open("/nvfs/etherparams", Sys->OREAD); + if(fd == nil) + return; + ctl := sys->open("/net/"+ethername+"/clone", Sys->OWRITE); + if(ctl == nil){ + sys->print("init: can't open %s's clone: %r\n", ethername); + return; + } + b := array[1024] of byte; + n := sys->read(fd, b, len b); + if(n <= 0) + return; + for(i := 0; i < n;){ + for(e := i; e < n && b[e] != byte '\n'; e++) + ; + s := string b[i:e]; + if(sys->fprint(ctl, "%s", s) < 0) + sys->print("init: ctl write to %s: %s: %r\n", ethername, s); + i = e+1; + } +} + +donebind := 0; + +# +# set up network mount +# +netfs(mountpt: string): int +{ + sys->print("bootp ..."); + + fd: ref Sys->FD; + if(!donebind){ + fd = sys->open("/net/ipifc/clone", sys->OWRITE); + if(fd == nil) { + sys->print("init: open /net/ipifc/clone: %r\n"); + return -1; + } + if(sys->fprint(fd, "bind ether %s", ethername) < 0) { + sys->print("could not bind %s interface: %r\n", ethername); + return -1; + } + donebind = 1; + }else{ + fd = sys->open("/net/ipifc/0/ctl", Sys->OWRITE); + if(fd == nil){ + sys->print("init: can't reopen /net/ipifc/0/ctl: %r\n"); + return -1; + } + } + if ((ip := rf("/nvfs/ip", nil)) != nil) { + sys->print("**using %s\n", ip); + sys->fprint(fd, "bind ether /net/ether0"); + sys->fprint(fd, "add %s ", ip); + } else { + { + if(sys->fprint(fd, "bootp") < 0) + sys->print("could not bootp: %r\n"); + } exception e { + "*" => + sys->print("could not bootp: %s\n", e); + } + } + server := rf("/nvfs/fsip", nil); + if (server != nil) { + if (server[len server - 1] == '\n') + server = server[:len server - 1]; + sys->print("/nvfs/fsip: server=%s\n", server); + } else + server = bootp(); + if(server == nil || server == "0.0.0.0") + return -1; + + net := "tcp"; # how to specify il? + svcname := net + "!" + server + "!6666"; + + sys->print("dial %s...", svcname); + + (ok, c) := sys->dial(svcname, nil); + if(ok < 0){ + sys->print("can't dial %s: %r\n", svcname); + return -1; + } + + sys->print("\nConnected ...\n"); + if(kr != nil){ + err: string; + sys->print("Authenticate ..."); + ai := kr->readauthinfo("/nvfs/default"); + if(ai == nil){ + sys->print("readauthinfo /nvfs/default failed: %r\n"); + sys->print("trying mount as `nobody'\n"); + } + (c.dfd, err) = auth->client("none", ai, c.dfd); + if(c.dfd == nil){ + sys->print("authentication failed: %s\n", err); + return -1; + } + } + + sys->print("mount %s...", mountpt); + + c.cfd = nil; + n := sys->mount(c.dfd, nil, mountpt, sys->MREPL, ""); + if(n > 0) + return 0; + if(n < 0) + sys->print("%r"); + return -1; +} + +bootp(): string +{ + fd := sys->open("/net/bootp", sys->OREAD); + if(fd == nil) { + sys->print("init: can't open /net/bootp: %r"); + return nil; + } + + buf := array[Bootpreadlen] of byte; + nr := sys->read(fd, buf, len buf); + fd = nil; + if(nr <= 0) { + sys->print("init: read /net/bootp: %r"); + return nil; + } + + (ntok, ls) := sys->tokenize(string buf, " \t\n"); + while(ls != nil) { + if(hd ls == "fsip"){ + ls = tl ls; + break; + } + ls = tl ls; + } + if(ls == nil) { + sys->print("init: server address not in bootp read"); + return nil; + } + + srv := hd ls; + + sys->print("%s\n", srv); + + return srv; +} + +username(def: string): string +{ + return rf("/nvfs/user", def); +} + +userok(user: string): int +{ + (ok, d) := sys->stat("/usr/"+user); + return ok >= 0 && (d.mode & Sys->DMDIR) != 0; +} + +rf(file: string, default: string): string +{ + fd := sys->open(file, Sys->OREAD); + if(fd != nil){ + buf := array[128] of byte; + nr := sys->read(fd, buf, len buf); + if(nr > 0) + return string buf[0:nr]; + } + return default; +} diff --git a/os/init/evalinit.b b/os/init/evalinit.b new file mode 100644 index 00000000..1e33c6f9 --- /dev/null +++ b/os/init/evalinit.b @@ -0,0 +1,119 @@ +implement Init; + +# +# ARM evaluator 7t +# + +include "sys.m"; +sys: Sys; +FD, Connection, sprint, Dir: import sys; +print, fprint, open, bind, mount, dial, sleep, read: import sys; + +include "draw.m"; +include "sh.m"; +draw: Draw; +Context: import draw; + +Init: module +{ + init: fn(); +}; + +Logon: module +{ + init: fn(ctxt: ref Context, argv: list of string); +}; + +Bootpreadlen: con 128; + +init() +{ + sys = load Sys Sys->PATH; +# kr = load Keyring Keyring->PATH; +# auth = load Auth Auth->PATH; +# if(auth != nil) +# auth->init(); + + sys->print("**\n** Inferno\n** Vita Nuova\n**\n"); + +# sys->print("Setup boot net services ...\n"); + + # + # Setup what we need to call a server and + # Authenticate + # +# bind("#l", "/net", sys->MREPL); +# bind("#I", "/net", sys->MAFTER); + bind("#c", "/dev", sys->MAFTER); + bind("#r", "/dev", sys->MAFTER); +# nvramfd := sys->open("#r/nvram", sys->ORDWR); +# if(nvramfd != nil){ +# spec = "#Fnvram"; +# if(bind(spec, "/nvfs", sys->MAFTER) < 0) +# print("init: bind %s: %r\n", spec); +# } + +# setsysname(); + + # + # default namespace + # + bind("#c", "/dev", sys->MREPL); # console + bind("#t", "/dev", sys->MAFTER); # serial port + bind("#r", "/dev", sys->MAFTER); # RTC +# if(spec != nil) +# bind(spec, "/nvfs", sys->MBEFORE|sys->MCREATE); # our keys +# bind("#l", "/net", sys->MBEFORE); # ethernet +# bind("#I", "/net", sys->MBEFORE); # TCP/IP + bind("#p", "/prog", sys->MREPL); # prog device + sys->bind("#d", "/fd", Sys->MREPL); + + sys->print("clock...\n"); + setclock(); + + sys->print("logon...\n"); + +# sys->chdir("/usr/inferno"); +# logon := load Logon "/dis/sh.dis"; +# spawn logon->init(dc, nil); + ts := load Sh "/dis/sh.dis"; + ts->init(nil, nil); +} + +setclock() +{ + (ok, dir) := sys->stat("/"); + if (ok < 0) { + print("init: stat /: %r"); + return; + } + + fd := sys->open("/dev/time", sys->OWRITE); + if (fd == nil) { + print("init: open /dev/time: %r"); + return; + } + + # Time is kept as microsecs, atime is in secs + b := array of byte sprint("%d000000", dir.atime); + if (sys->write(fd, b, len b) != len b) + print("init: write /dev/time: %r"); +} + +# +# Set system name from nvram +# +setsysname() +{ + fd := open("/nvfs/ID", sys->OREAD); + if(fd == nil) + return; + fds := open("/dev/sysname", sys->OWRITE); + if(fds == nil) + return; + buf := array[128] of byte; + nr := sys->read(fd, buf, len buf); + if(nr <= 0) + return; + sys->write(fds, buf, nr); +} diff --git a/os/init/geninit.b b/os/init/geninit.b new file mode 100644 index 00000000..7e63d24f --- /dev/null +++ b/os/init/geninit.b @@ -0,0 +1,93 @@ +implement Init; +# +# init program for native inferno, generic pc version +# +include "sys.m"; +sys: Sys; +FD, Connection, sprint, Dir: import sys; +print, fprint, open, bind, mount, dial, sleep, read, chdir: import sys; + +include "draw.m"; +draw: Draw; +Context: import draw; + +include "keyring.m"; +kr: Keyring; + +Init: module +{ + init: fn(); +}; + +Shell: module +{ + init: fn(ctxt: ref Context, argv: list of string); +}; + +init() +{ + + sys = load Sys Sys->PATH; + stdin := sys->fildes(0); + kr = load Keyring Keyring->PATH; + + sys->print("**\n** Inferno\n** Vita Nuova\n**\n"); + + sys->print("Setup boot net services ...\n"); + + # + # Setup what we need to call a server and + # Authenticate + # + sys->print("Bind console ...\n"); + bind("#c", "/dev", sys->MAFTER); + + setsysname(); + print("Standalone mode\n"); + # + # default namespace + # + sys->unmount(nil, "/dev"); + bind("#p", "/prog", sys->MREPL); # prog device + sys->bind("#d", "/fd", Sys->MREPL); + bind("#c", "/dev", sys->MBEFORE); # console + bind("#m", "/dev", sys->MAFTER); # mouse setup device + bind("#t", "/dev", sys->MAFTER); # serial device + + mouse := load Shell "/dis/mouse.dis"; + if (mouse != nil) { + print("Setting up mouse\n"); + mouse->init(nil, "/dis/mouse.dis" :: nil); + mouse = nil; + } + + # create fake nameserver db that can be written to later + ramfile := load Shell "/dis/ramfile.dis"; + if (ramfile != nil) { + ramfile->init(nil, "/dis/ramfile.dis" :: "/services/dns/db" :: "" :: nil); + ramfile = nil; + } + + print("Console...\n"); + shell := load Shell "/dis/sh.dis"; + if(shell == nil) { + print("init: load /dis/sh.dis: %r\n"); + exit; + } + print("starting shell\n"); + shell->init(nil, "/dis/sh.dis" :: nil); + print("shell exited, bye bye\n"); +} + + +# +# Set system name from nvram +# +setsysname() +{ + fds := open("/dev/sysname", sys->OWRITE); + if(fds == nil) + return; + buf := array of byte "genericpc"; + sys->write(fds, buf, len buf); +} diff --git a/os/init/i4e.b b/os/init/i4e.b new file mode 100644 index 00000000..f670e8d5 --- /dev/null +++ b/os/init/i4e.b @@ -0,0 +1,210 @@ +implement Init; + +# +# init program for Inferno 4thEd demo box +# + +include "sys.m"; + sys: Sys; + FD, Connection, Dir: import sys; + +include "draw.m"; + +include "keyring.m"; + kr: Keyring; + +include "security.m"; + auth: Auth; + +include "dhcp.m"; + dhcpclient: Dhcpclient; + Bootconf: import dhcpclient; + +I4EBOOT: con "/lib/boot.sh"; + +Init: module +{ + init: fn(); +}; + +Command: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +Bootpreadlen: con 128; + +init() +{ + sys = load Sys Sys->PATH; + kr = load Keyring Keyring->PATH; + auth = load Auth Auth->PATH; + if(auth != nil) + auth->init(); + + sys->print("**\n** Inferno\n** Vita Nuova\n**\n"); + + sys->print("Setup boot net services ...\n"); + + # + # Setup what we need to call a server and + # Authenticate + # + dobind("#l", "/net", Sys->MREPL); + dobind("#I", "/net", Sys->MAFTER); + dobind("#c", "/dev", Sys->MAFTER); + + + fd := sys->open("/net/ipifc/clone", sys->OWRITE); + if(fd == nil) + fail(sys->sprint("iopen /net/ipifc/clone: %r")); + + if(sys->fprint(fd, "bind ether /net/ether0") < 0) + fail(sys->sprint("could not bind interface: %r")); + + fsip: string; + + dhcpclient = load Dhcpclient Dhcpclient->PATH; + if(dhcpclient == nil) + fail(sys->sprint("can't load dhcpclient: %r")); + + sys->print("dhcp..."); + dhcpclient->init(); + (cfg, nil, e) := dhcpclient->dhcp("/net", fd, "/net/ether0/addr", nil, nil); + if(e != nil) + fail(sys->sprint("dhcp: %s", e)); + fsip = cfg.getip(Dhcpclient->OP9fs); + if(fsip == nil) + fail("server address not in bootp/dhcp reply"); + dhcpclient = nil; + + infd := sys->open("/dev/cons", Sys->OREAD); + if(infd == nil) + sys->print("warning: no kbd\n"); + + err := rootfs(fsip); + if(err != nil) + fail(err); + + # + # default namespace + # + dobind("#c", "/dev", Sys->MREPL); # console + dobind("#p", "/prog", Sys->MREPL); # prog device + sys->bind("#d", "/fd", Sys->MREPL); + sys->pctl(Sys->NEWENV, nil); + dobind("#e", "/env", Sys->MREPL|Sys->MCREATE); # env device + + sys->print("clock...\n"); + setclock(); + + sys->print("boot...\n"); + sys->pctl(Sys->NEWFD, 0::1::2::nil); + done := chan of string; + spawn boot(done); + err = <- done; + if(err != nil) + fail("boot script failed: "+err); + fail("boot script exit"); +} + +rootfs(server: string): string +{ + ok: int; + c: Connection; + + sys->print("readauthinfo...\n"); + ai := kr->readauthinfo("/keydb/mutual"); + if(ai == nil) + return sys->sprint("readauthinfo /keydb/mutual failed: %r"); + + addr := "tcp!" + server + "!9999"; + for(gap := 3;; gap *= 2){ + sys->print("Connect (%s)...", addr); + (ok, c) = sys->dial(addr, nil); + if(ok != -1) + break; + sys->print("failed: %r\n"); + if(gap > 60) + gap = 60; + sys->sleep(gap*1000); + } + + sys->print("\nConnected ..."); + if(kr != nil && auth != nil){ + err: string; + sys->print("Authenticate ..."); + (c.dfd, err) = auth->client("none", ai, c.dfd); + if(c.dfd == nil) + return sys->sprint("authentication failed: %s", err); + } + sys->print("mount ..."); + + c.cfd = nil; + sys->pctl(Sys->NEWNS, nil); + if(sys->mount(c.dfd, nil, "/", sys->MREPL, "") < 0) # TO DO: would be better to mount behind + return sys->sprint("mount failed: %r"); + sys->chdir("/"); + return nil; +} + +boot(done: chan of string) +{ + { + shell := load Command "/dis/sh.dis"; + if(shell == nil){ + done <-= sys->sprint("load /dis/sh.dis: %r"); + exit; + } + shell->init(nil, "/dis/sh.dis"::I4EBOOT::nil); + } exception e { + "*" => + done <-= e; + exit; + } + done <-= nil; +} + +setclock() +{ + (ok, dir) := sys->stat("/"); + if(ok < 0){ + sys->print("stat /: %r"); + return; + } + + fd := sys->open("/dev/time", sys->OWRITE); + if(fd == nil){ + sys->print("open /dev/time: %r"); + return; + } + + # Time is kept as microsecs, atime is in secs + b := array of byte sys->sprint("%d000000", dir.atime); + if(sys->write(fd, b, len b) != len b) + sys->print("write /dev/time: %r"); +} + +# +# Bind wrapper which reports errors +# + +dobind(f, t: string, flags: int) +{ + if(sys->bind(f, t, flags) < 0) + sys->print("bind(%s, %s, %d) failed: %r\n", f, t, flags); +} + +fail(msg: string) +{ + sys->print("%s\n", msg); + sys->bind("/", "#//n/remote", Sys->MREPL); + sys->bind("#//", "/", Sys->MREPL); + shell := load Command "#//dis/sh.dis"; + if(shell == nil){ + sys->print("cannot load shell: %r\n"); + exit; + } + shell->init(nil, "/dis/sh.dis"::"-i"::nil); + exit; +} diff --git a/os/init/init.b b/os/init/init.b new file mode 100644 index 00000000..655b0cf3 --- /dev/null +++ b/os/init/init.b @@ -0,0 +1,613 @@ +implement Init; + +include "sys.m"; +sys: Sys; +FD, Connection, sprint, Dir: import sys; +print, fprint, open, bind, mount, dial, sleep, read: import sys; + +include "draw.m"; +draw: Draw; +Context, Display, Font, Rect, Point, Image, Screen: import draw; + +include "prefab.m"; +prefab: Prefab; +Environ, Element, Compound, Style: import prefab; + +include "mpeg.m"; + +include "ir.m"; +tirc: chan of int; # translated remote input (from irslave) +irstopc: chan of int; # channel to irslave + +include "keyring.m"; +kr: Keyring; +IPint: import kr; + +Init: module +{ + init: fn(); +}; + +Shell: module +{ + init: fn(ctxt: ref Context, argv: list of string); +}; + +Signon: con "Dialing Local Service Provider\nWait a moment ..."; +Login: con "Connected to Service Provider"; +Intro: con "/mpeg/youwill2"; +Garden: con "The Garden of Delights\nHieronymus Bosch"; + +rootfs(server: string): int +{ + ok, n: int; + c: Connection; + err: string; + + (ok, c) = dial("tcp!" + server + "!6666", nil); + if(ok < 0) + return -1; + + if(kr != nil){ + ai := kr->readauthinfo("/nvfs/default"); + if(ai == nil){ + (ai, err) = register(server); + if(err != nil){ + status("registration failed: "+err+"\nPress a key on your remote\ncontrol to continue."); + # register() may have failed before Ir loaded. + if(tirc!=nil){ + <-tirc; + irstopc <-= 1; + } + } + statusbox = nil; + } + (id_or_err, secret) := kr->auth(c.dfd, ai, 0); + if(secret == nil){ + status("authentication failed: "+err); + sys->sleep(2000); + statusbox = nil; + (ai, err) = register(server); + if(err != nil){ + status("registration failed: "+err+"\nPress a key on your remote\ncontrol to continue."); + # register() may have failed before Ir loaded. + if(tirc!=nil){ + <-tirc; + irstopc <-= 1; + } + } + statusbox = nil; + } else { + # no line encryption + algbuf := array of byte "none"; + kr->sendmsg(c.dfd, algbuf, len algbuf); + } + } + + c.cfd = nil; + n = mount(c.dfd, nil, "/", sys->MREPL, ""); + if(n > 0) + return 0; + return -1; +} + +ones: ref Image; +screen: ref Screen; +menuenv, tvenv: ref Environ; +Bootpreadlen: con 128; +textfont: ref Font; +disp: ref Display; +env: ref Environ; +statusbox: ref Compound; + +init() +{ + shell: Shell; + nr, ntok: int; + c: ref Compound; + ls: list of string; + le, te, xe: ref Element; + spec: string; + + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + prefab = load Prefab Prefab->PATH; + kr = load Keyring Keyring->PATH; + + disp = Display.allocate(nil); + ones = disp.ones; + + textfont = Font.open(disp, "*default*"); + screencolor := disp.rgb(161, 195, 209); + + menustyle := ref Style( + textfont, # titlefont + textfont, # textfont + disp.color(16r55), # elemcolor + disp.color(draw->Black), # edgecolor + disp.color(draw->Yellow), # titlecolor + disp.color(draw->Black), # textcolor + disp.color(draw->White)); # highlightcolor + + screen = Screen.allocate(disp.image, screencolor, 0); + screen.image.draw(screen.image.r, screencolor, ones, (0, 0)); + menuenv = ref Environ(screen, menustyle); + + logo := disp.open("/lucent"); + phone := disp.open("/phone"); + if(phone == nil || logo == nil) { + print("open: /phone or /lucent: %r\n"); + exit; + } + + # + # Setup what we need to call a server and + # Authenticate + # + bind("#l", "/net", sys->MREPL); + bind("#I", "/net", sys->MAFTER); + bind("#c", "/dev", sys->MAFTER); + bind("#H", "/dev", sys->MAFTER); + nvramfd := sys->open("#H/hd0nvram", sys->ORDWR); + if(nvramfd != nil){ + spec = sys->sprint("#Fhd0nvram", nvramfd.fd); + if(bind(spec, "/nvfs", sys->MAFTER|sys->MCREATE) < 0) + print("init: bind %s: %r\n", spec); + } + + setsysname(); # set up system name + + fd := open("/net/ipifc", sys->OWRITE); + if(fd == nil) { + print("init: open /net/ipifc: %r"); + exit; + } + fprint(fd, "bootp /net/ether0"); + + fd = open("/net/bootp", sys->OREAD); + if(fd == nil) { + print("init: open /net/bootp: %r"); + exit; + } + + buf := array[Bootpreadlen] of byte; + nr = read(fd, buf, len buf); + fd = nil; + if(nr <= 0) { + print("init: read /net/bootp: %r"); + exit; + } + + (ntok, ls) = sys->tokenize(string buf, " \t\n"); + while(ls != nil) { + if(hd ls == "fsip"){ + ls = tl ls; + break; + } + ls = tl ls; + } + if(ls == nil) { + print("init: server address not in bootp read"); + exit; + } + + zr := Rect((0,0), (0,0)); + + le = Element.icon(menuenv, logo.r, logo, ones); + le = Element.elist(menuenv, le, Prefab->EVertical); + xe = Element.icon(menuenv, phone.r, phone, ones); + xe = Element.elist(menuenv, xe, Prefab->EHorizontal); + te = Element.text(menuenv, Signon, zr, Prefab->EText); + xe.append(te); + xe.adjust(Prefab->Adjpack, Prefab->Adjleft); + le.append(xe); + le.adjust(Prefab->Adjpack, Prefab->Adjup); + c = Compound.box(menuenv, (150, 100), + Element.text(menuenv, "Inferno", zr, Prefab->ETitle), le); + c.draw(); + + while(rootfs(hd ls) < 0) + sleep(1000); + + # + # default namespace + # + bind("#c", "/dev", sys->MBEFORE); # console + bind("#H", "/dev", sys->MAFTER); + if(spec != nil) + bind(spec, "/nvfs", sys->MBEFORE|sys->MCREATE); # our keys + bind("#E", "/dev", sys->MBEFORE); # mpeg + bind("#l", "/net", sys->MBEFORE); # ethernet + bind("#I", "/net", sys->MBEFORE); # TCP/IP + bind("#V", "/dev", sys->MAFTER); # hauppauge TV + bind("#p", "/prog", sys->MREPL); # prog device + sys->bind("#d", "/fd", Sys->MREPL); + + setclock(); + + le = Element.icon(menuenv, logo.r, logo, ones); + le = Element.elist(menuenv, le, Prefab->EVertical); + xe = Element.text(menuenv, Login, zr, Prefab->EText); + le.append(xe); + + i := disp.newimage(Rect((0, 0), (320, 240)), 3, 0, 0); + i.draw(i.r, menustyle.elemcolor, ones, i.r.min); + xe = Element.icon(menuenv, i.r, i, ones); + le.append(xe); + + le.adjust(Prefab->Adjpack, Prefab->Adjup); + c = Compound.box(menuenv, (160, 50), + Element.text(menuenv, "Inferno", zr, Prefab->ETitle), le); + c.draw(); + + xc: chan of string; + mpeg := load Mpeg Mpeg->PATH; + if(mpeg != nil) { + xc = chan of string; + r := (hd tl tl c.contents.kids).r; + s := mpeg->play(disp, c.image, 1, r, Intro, xc); + if(s != "") { + print("mpeg: %s\n", s); + xc = nil; + } + } + + i2 := disp.open("/icons/delight.bit"); + i.draw(i.r, i2, ones, i2.r.min); + i2 = nil; + if(xc != nil) + <-xc; + + le.append(Element.text(menuenv, Garden, le.r, Prefab->EText)); + le.adjust(Prefab->Adjpack, Prefab->Adjup); + c = Compound.box(menuenv, (160, 50), + Element.text(menuenv, "Inferno", zr, Prefab->ETitle), le); + c.draw(); + + sleep(5000); + + # Do a bind to force applications to use IR module built + # into the kernel. + if(bind("#/./ir", Ir->PATH, sys->MREPL) < 0) + print("init: bind ir: %r\n"); + # Uncomment the next line to load sh.dis. +# shell = load Shell "/dis/sh.dis"; + dc : ref Context; + # Comment the next 2 lines to load sh.dis. + shell = load Shell "/dis/mux/mux.dis"; + dc = ref Context(screen, disp, nil, nil, nil, nil, nil); + if(shell == nil) { + print("init: load /dis/sh.dis: %r"); + exit; + } + shell->init(dc, nil); +} + +setclock() +{ + (ok, dir) := sys->stat("/"); + if (ok < 0) { + print("init: stat /: %r"); + return; + } + + fd := sys->open("/dev/time", sys->OWRITE); + if (fd == nil) { + print("init: open /dev/time: %r"); + return; + } + + # Time is kept as microsecs, atime is in secs + b := array of byte sprint("%d000000", dir.atime); + if (sys->write(fd, b, len b) != len b) + print("init: write /dev/time: %r"); +} + +register(signer: string): (ref Keyring->Authinfo, string) +{ + + # get box id + fd := sys->open("/nvfs/ID", sys->OREAD); + if(fd == nil){ + fd = sys->create("/nvfs/ID", sys->OWRITE, 8r664); + if(fd == nil) + return (nil, "can't create /nvfs/ID"); + if(sys->fprint(fd, "LT%d", randomint()) < 0) + return (nil, "can't write /nvfs/ID"); + fd = sys->open("/nvfs/ID", sys->OREAD); + } + if(fd == nil) + return (nil, "can't open /nvfs/ID"); + + buf := array[64] of byte; + n := sys->read(fd, buf, (len buf) - 1); + if(n <= 0) + return (nil, "can't read /nvfs/ID"); + + boxid := string buf[0:n]; + fd = nil; + buf = nil; + + # Set-up for user input via remote control. + tirc = chan of int; + irstopc = chan of int; + spawn irslave(tirc, irstopc); + case dialogue("Register with your service provider?", "yes\nno") { + 0 => + ; + * => + return (nil, "registration not desired"); + } + + # a holder + info := ref Keyring->Authinfo; + + # contact signer +# status("looking for signer"); +# signer := virgil->virgil("$SIGNER"); +# if(signer == nil) +# return (nil, "can't find signer"); + status("dialing tcp!"+signer+"!6671"); + (ok, c) := sys->dial("tcp!"+signer+"!6671", nil); + if(!ok) + return (nil, "can't contact signer"); + + # get signer's public key and diffie helman parameters + status("getting signer's key"); + spkbuf := kr->getmsg(c.dfd); + if(spkbuf == nil) + return (nil, "can't read signer's key"); + info.spk = kr->strtopk(string spkbuf); + if(info.spk == nil) + return (nil, "bad key from signer"); + alphabuf := kr->getmsg(c.dfd); + if(alphabuf == nil) + return (nil, "can't read dh alpha"); + info.alpha = IPint.b64toip(string alphabuf); + pbuf := kr->getmsg(c.dfd); + if(pbuf == nil) + return (nil, "can't read dh mod"); + info.p = IPint.b64toip(string pbuf); + + # generate our key from system parameters + status("generating our key"); + info.mysk = kr->genSKfromPK(info.spk, boxid); + if(info.mysk == nil) + return (nil, "can't generate our own key"); + info.mypk = kr->sktopk(info.mysk); + + # send signer our public key + mypkbuf := array of byte kr->pktostr(info.mypk); + kr->sendmsg(c.dfd, mypkbuf, len mypkbuf); + + # get blind certificate + status("getting blinded certificate"); + certbuf := kr->getmsg(c.dfd); + if(certbuf == nil) + return (nil, "can't read signed key"); + + # verify we've got the right stuff + if(!verify(boxid, spkbuf, mypkbuf, certbuf)) + return (nil, "verification failed, try again"); + + # contact counter signer + status("dialing tcp!"+signer+"!6672"); + (ok, c) = sys->dial("tcp!"+signer+"!6672", nil); + if(!ok) + return (nil, "can't contact countersigner"); + + # send boxid + buf = array of byte boxid; + kr->sendmsg(c.dfd, buf, len buf); + + # get blinding mask + status("unblinding certificate"); + mask := kr->getmsg(c.dfd); + if(len mask != len certbuf) + return (nil, "bad mask length"); + for(i := 0; i < len mask; i++) + certbuf[i] = certbuf[i] ^ mask[i]; + info.cert = kr->strtocert(string certbuf); + + status("verifying certificate"); + state := kr->sha(mypkbuf, len mypkbuf, nil, nil); + if(kr->verify(info.spk, info.cert, state) == 0) + return (nil, "bad certificate"); + + status("storing keys"); + kr->writeauthinfo("/nvfs/default", info); + + status("Congratulations, you are registered.\nPress a key to continue."); + <-tirc; + irstopc <-= 1; + + return (info, nil); +} + +dialogue(expl: string, selection: string): int +{ + c := Compound.textbox(menuenv, ((100, 100), (100, 100)), expl, selection); + c.draw(); + for(;;){ + (key, index, nil) := c.select(c.contents, 0, tirc); + case key { + Ir->Select => + return index; + Ir->Enter => + return -1; + } + } +} + +status(expl: string) +{ +# title := Element.text(menuenv, "registration\nstatus", ((0,0),(0,0)), Prefab->ETitle); +# msg := Element.text(menuenv, expl, ((0,0),(0,0)), Prefab->EText); +# c := Compound.box(menuenv, (100, 100), title, msg); + + c := Compound.textbox(menuenv, ((100, 100),(100,100)), "Registration status", expl); + c.draw(); + statusbox = c; +} + +pro:= array[] of { + "alpha", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", + "hotel", "india", "juliet", "kilo", "lima", "mike", "nancy", "oscar", + "poppa", "quebec", "romeo", "sierra", "tango", "uniform", + "victor", "whiskey", "xray", "yankee", "zulu" +}; + +# +# prompt for acceptance +# +verify(boxid: string, hispk, mypk, cert: array of byte): int +{ + s: string; + + # hash the string + state := kr->md5(hispk, len hispk, nil, nil); + kr->md5(mypk, len mypk, nil, state); + digest := array[Keyring->MD5dlen] of byte; + kr->md5(cert, len cert, digest, state); + + title := Element.elist(menuenv, nil, Prefab->EVertical); + subtitle := Element.text(menuenv, "Telephone your service provider\n to register. You will need\nthe following:\n", ((0,0),(0,0)), Prefab->ETitle); + title.append(subtitle); + + line := Element.text(menuenv, "boxid is '"+boxid+"'.", ((0,0),(0,0)), Prefab->ETitle); + title.append(line); + for(i := 0; i < len digest; i++){ + line = Element.elist(menuenv, nil, Prefab->EHorizontal); + s = (string (2*i)) + ": " + pro[((int digest[i])>>4)%len pro]; + line.append(Element.text(menuenv, s, ((0,0),(0,0)), Prefab->ETitle)); + + s = (string (2*i+1)) + ": " + pro[(int digest[i])%len pro] + "\n"; + line.append(Element.text(menuenv, s, ((0,0),(200,0)), Prefab->ETitle)); + + line.adjust(Prefab->Adjequal, Prefab->Adjleft); + title.append(line); + } + title.adjust(Prefab->Adjpack, Prefab->Adjleft); + + le := Element.elist(menuenv, nil, Prefab->EHorizontal); + le.append(Element.text(menuenv, " accept ", ((0, 0), (0, 0)), Prefab->EText)); + le.append(Element.text(menuenv, " reject ", ((0, 0), (0, 0)), Prefab->EText)); + le.adjust(Prefab->Adjpack, Prefab->Adjleft); + + c := Compound.box(menuenv, (50, 50), title, le); + c.draw(); + + for(;;){ + (key, index, nil) := c.select(c.contents, 0, tirc); + case key { + Ir->Select => + if(index == 0) + return 1; + return 0; + Ir->Enter => + return 0; + } + } + + return 0; +} + +randomint(): int +{ + fd := sys->open("/dev/random", sys->OREAD); + if(fd == nil) + return 0; + buf := array[4] of byte; + sys->read(fd, buf, 4); + rand := 0; + for(i := 0; i < 4; i++) + rand = (rand<<8) | int buf[i]; + return rand; +} + +# Reads real (if possible) or simulated remote, returns Ir events on irc. +# Must be a separate thread to be able to 1) read raw Ir input channel +# and 2) write translated Ir input data on output channel. +irslave(irc, stopc: chan of int) +{ + in, irpid: int; + buf: list of int; + outc: chan of int; + + irchan := chan of int; # Untranslated Ir input channel. + irpidch := chan of int; # Ir reader pid channel. + irmod := load Ir "#/./ir"; # Module built into kernel. + + if(irmod==nil){ + print("irslave: failed to load #/./ir"); + return; + } + if(irmod->init(irchan, irpidch)<0){ + print("irslave: failed to initialize ir"); + return; + } + irpid =<-irpidch; + + hdbuf := 0; + dummy := chan of int; + for(;;){ + if(buf == nil){ + outc = dummy; + }else{ + outc = irc; + hdbuf = hd buf; + } + alt{ + in = <-irchan => + buf = append(buf, in); + outc <-= irmod->translate(hdbuf) => + buf = tl buf; + <-stopc =>{ + killir(irpid); + return; + } + } + } +} + +append(l: list of int, i: int): list of int +{ + if(l == nil) + return i :: nil; + return hd l :: append(tl l, i); +} + +killir(irpid: int) +{ + pid := sys->sprint("%d", irpid); + fd := sys->open("#p/"+pid+"/ctl", sys->OWRITE); + if(fd==nil) { + print("init: process %s: %r\n", pid); + return; + } + + msg := array of byte "kill"; + n := sys->write(fd, msg, len msg); + if(n < 0) { + print("init: message for %s: %r\n", pid); + return; + } +} + +# +# Set system name from nvram +# +setsysname() +{ + fd := open("/nvfs/ID", sys->OREAD); + if(fd == nil) + return; + fds := open("/dev/sysname", sys->OWRITE); + if(fds == nil) + return; + buf := array[128] of byte; + nr := sys->read(fd, buf, len buf); + if(nr <= 0) + return; + sys->write(fds, buf, nr); +} diff --git a/os/init/ipaqinit.b b/os/init/ipaqinit.b new file mode 100644 index 00000000..40ddd529 --- /dev/null +++ b/os/init/ipaqinit.b @@ -0,0 +1,697 @@ +# +# ipaq +# +# TO DO: read params from params flash +# + +implement Init; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + +include "keyring.m"; + kr: Keyring; + +include "security.m"; + auth: Auth; + +include "dhcp.m"; + dhcpclient: Dhcpclient; + Bootconf: import dhcpclient; + +include "keyboard.m"; + +include "sh.m"; + +Init: module +{ + init: fn(); +}; + +Bootpreadlen: con 128; + +ethername := "ether0"; + +# standard Inferno flash partitions + +flashparts := array[] of { + # bootstrap at 0x0 to 0x40000, don't touch + "add params 0x40000 0x80000", + "add kernel 0x80000 0x140000", + "add fs 0x140000 end", +}; + +# +# initialise flash translation +# mount flash file system +# add devices +# start a shell or window manager +# + +init() +{ + sys = load Sys Sys->PATH; + kr = load Keyring Keyring->PATH; + auth = load Auth Auth->PATH; + if(auth != nil) + auth->init(); + + sys->bind("/", "/", Sys->MREPL); + + lightup(); + + localok := 0; + if(lfs() >= 0){ + # let's just take a closer look + sys->bind("/n/local/nvfs", "/nvfs", Sys->MREPL|Sys->MCREATE); + (rc, nil) := sys->stat("/n/local/dis/sh.dis"); + if(rc >= 0) + localok = 1; + else + err("local file system unusable"); + } + netok := sys->bind("#l", "/net", Sys->MREPL) >= 0; + if(!netok){ + netok = sys->bind("#l1", "/net", Sys->MREPL) >= 0; + if(netok) + ethername = "ether1"; + } + if(netok) + configether(); + + dobind("#I", "/net", sys->MAFTER); # IP + dobind("#p", "/prog", sys->MREPL); # prog + dobind("#c", "/dev", sys->MREPL); # console + sys->bind("#d", "/fd", Sys->MREPL); + dobind("#t", "/dev", sys->MAFTER); # serial line + dobind("#i", "/dev", sys->MAFTER); # draw + dobind("#m", "/dev", Sys->MAFTER); # pointer + sys->bind("#e", "/env", sys->MREPL|sys->MCREATE); # environment + sys->bind("#A", "/dev", Sys->MAFTER); # optional audio + dobind("#T","/dev",sys->MAFTER); # touch screen and other ipaq devices + + timefile: string; + rootsource: string; + cfd := sys->open("/dev/consctl", Sys->OWRITE); + if(cfd != nil) + sys->fprint(cfd, "rawon"); + for(;;){ + (rootsource, timefile) = askrootsource(localok, netok); + if(rootsource == nil) + break; # internal + (rc, nil) := sys->stat(rootsource+"/dis/sh.dis"); + if(rc < 0) + err("%s has no shell"); + else if(sys->bind(rootsource, "/", Sys->MAFTER) < 0) + sys->print("can't bind %s on /: %r\n", rootsource); + else{ + sys->bind(rootsource+"/dis", "/dis", Sys->MBEFORE|Sys->MCREATE); + break; + } + } + cfd = nil; + + setsysname("ipaq"); # set system name + + now := getclock(timefile, rootsource); + setclock("/dev/time", now); + if(timefile != "#r/rtc") + setclock("#r/rtc", now/big 1000000); + + sys->chdir("/"); + if(netok){ + start("ndb/dns", nil); + start("ndb/cs", nil); + } + calibrate(); + startup := "/nvfs/startup"; + if(sys->open(startup, Sys->OREAD) != nil){ + shell := load Command Sh->PATH; + if(shell != nil){ + sys->print("Running %s\n", startup); + shell->init(nil, "sh" :: startup :: nil); + } + } + user := rdenv("user", "inferno"); + (ok, nil) := sys->stat("/dis/wm/wm.dis"); + if(ok >= 0) + (ok, nil) = sys->stat("/dis/wm/logon.dis"); + if(ok >= 0 && userok(user)){ + wm := load Command "/dis/wm/wm.dis"; + if(wm != nil){ + fd := sys->open("/nvfs/user", Sys->OWRITE); + if(fd != nil){ + sys->fprint(fd, "%s", user); + fd = nil; + } + spawn wm->init(nil, list of {"wm/wm", "wm/logon", "-l", "-n", "lib/ipaqns", "-u", user}); + exit; + } + sys->print("init: can't load wm/logon: %r"); + } + sh := load Command Sh->PATH; + if(sh == nil){ + err(sys->sprint("can't load %s: %r", Sh->PATH)); + hang(); + } + spawn sh->init(nil, "sh" :: nil); +} + +start(cmd: string, args: list of string) +{ + disfile := cmd; + if(disfile[0] != '/') + disfile = "/dis/"+disfile+".dis"; + (ok, nil) := sys->stat(disfile); + if(ok >= 0){ + dis := load Command disfile; + if(dis == nil) + sys->print("init: can't load %s: %r\n", disfile); + else + spawn dis->init(nil, cmd :: args); + } +} + +dobind(f, t: string, flags: int) +{ + if(sys->bind(f, t, flags) < 0) + err(sys->sprint("can't bind %s on %s: %r", f, t)); +} + +lightup() +{ + # backlight + fd := sys->open("#T/ipaqctl", Sys->OWRITE); + if(fd != nil) + sys->fprint(fd, "light 1 1 0x80"); +} + +# +# Set system name from nvram if possible +# +setsysname(def: string) +{ + v := array of byte def; + fd := sys->open("/nvfs/ID", sys->OREAD); + if(fd == nil) + fd = sys->open("/env/sysname", sys->OREAD); + if(fd != nil){ + buf := array[Sys->NAMEMAX] of byte; + nr := sys->read(fd, buf, len buf); + while(nr > 0 && buf[nr-1] == byte '\n') + nr--; + if(nr > 0) + v = buf[0:nr]; + } + fd = sys->open("/dev/sysname", sys->OWRITE); + if(fd != nil) + sys->write(fd, v, len v); +} + +getclock(timefile: string, timedir: string): big +{ + now := big 0; + if(timefile != nil){ + fd := sys->open(timefile, Sys->OREAD); + if(fd != nil){ + b := array[64] of byte; + n := sys->read(fd, b, len b-1); + if(n > 0){ + now = big string b[0:n]; + if(now <= big 16r20000000) + now = big 0; # remote itself is not initialised + } + } + } + if(now == big 0){ + if(timedir != nil){ + (ok, dir) := sys->stat(timedir); + if(ok < 0) { + sys->print("init: stat %s: %r", timedir); + return big 0; + } + now = big dir.atime; + }else{ + now = big 993826747000000; + sys->print("time warped\n"); + } + } + return now; +} + +setclock(timefile: string, now: big) +{ + fd := sys->open(timefile, sys->OWRITE); + if (fd == nil) { + sys->print("init: can't open %s: %r", timefile); + return; + } + + b := sys->aprint("%ubd", now); + if (sys->write(fd, b, len b) != len b) + sys->print("init: can't write to %s: %r", timefile); +} + +srv() +{ + sys->print("remote debug srv..."); + fd := sys->open("/dev/eia0ctl", Sys->OWRITE); + if(fd != nil) + sys->fprint(fd, "b115200"); + + fd = sys->open("/dev/eia0", Sys->ORDWR); + if (fd == nil){ + err(sys->sprint("can't open /dev/eia0: %r")); + return; + } + if (sys->export(fd, "/", Sys->EXPASYNC) < 0){ + err(sys->sprint("can't export on serial port: %r")); + return; + } +} + +err(s: string) +{ + sys->fprint(sys->fildes(2), "init: %s\n", s); +} + +hang() +{ + <-(chan of int); +} + +tried := 0; + +askrootsource(localok: int, netok: int): (string, string) +{ + stdin := sys->fildes(0); + sources := "kernel" :: nil; + if(netok) + sources = "remote" :: sources; + if(localok){ + sources = "local" :: sources; + if(netok) + sources = "local+remote" :: sources; + } +Query: + for(;;) { + s := ""; + if (tried == 0 && (s = rdenv("rootsource", nil)) != nil) { + tried = 1; + if (s[len s - 1] == '\n') + s = s[:len s - 1]; + sys->print("/nvfs/rootsource: root from %s\n", s); + } else { + sys->print("root from ("); + cm := ""; + for(l := sources; l != nil; l = tl l){ + sys->print("%s%s", cm, hd l); + cm = ","; + } + sys->print(")[%s] ", hd sources); + + s = getline(stdin, hd sources); # default + } + case s[0] { + Keyboard->Right or Keyboard->Left => + sources = append(hd sources, tl sources); + sys->print("\n"); + continue Query; + Keyboard->Down => + s = hd sources; + sys->print(" %s\n", s); + } + (nil, choice) := sys->tokenize(s, "\t "); + if(choice == nil) + choice = sources; + opt := hd choice; + case opt { + * => + sys->print("\ninvalid boot option: '%s'\n", opt); + "kernel" => + return (nil, "#r/rtc"); + "local" => + return ("/n/local", "#r/rtc"); + "local+remote" => + if(netfs("/n/remote") >= 0) + return ("/n/local", "/n/remote/dev/time"); + "remote" => + if(netfs("/n/remote") >= 0) + return ("/n/remote", "/n/remote/dev/time"); + } + } +} + +getline(fd: ref Sys->FD, default: string): string +{ + result := ""; + buf := array[10] of byte; + i := 0; + for(;;) { + n := sys->read(fd, buf[i:], len buf - i); + if(n < 1) + break; + i += n; + while(i >0 && (nutf := sys->utfbytes(buf, i)) > 0){ + s := string buf[0:nutf]; + for (j := 0; j < len s; j++) + case s[j] { + '\b' => + if(result != nil) + result = result[0:len result-1]; + 'u'&16r1F => + sys->print("^U\n"); + result = ""; + '\r' => + ; + * => + sys->print("%c", s[j]); + if(s[j] == '\n' || s[j] >= 16r80){ + if(s[j] != '\n') + result[len result] = s[j]; + if(result == nil) + return default; + return result; + } + result[len result] = s[j]; + } + buf[0:] = buf[nutf:i]; + i -= nutf; + } + } + return default; +} + +append(v: string, l: list of string): list of string +{ + if(l == nil) + return v :: nil; + return hd l :: append(v, tl l); +} + +# +# serve local DOS or kfs file system using flash translation layer +# +lfs(): int +{ + if(!flashpart("#F/flash/flashctl", flashparts)) + return -1; + if(!ftlinit("#F/flash/fs")) + return -1; + if(iskfs("#X/ftldata")) + return lkfs("#X/ftldata"); + c := chan of string; + spawn startfs(c, "/dis/dossrv.dis", "dossrv" :: "-f" :: "#X/ftldata" :: "-m" :: "/n/local" :: nil, nil); + if(<-c != nil) + return -1; + return 0; +} + +wmagic := "kfs wren device\n"; + +iskfs(file: string): int +{ + fd := sys->open(file, Sys->OREAD); + if(fd == nil) + return 0; + buf := array[512] of byte; + n := sys->read(fd, buf, len buf); + if(n < len buf) + return 0; + if(string buf[256:256+len wmagic] != wmagic) + return 0; + RBUFSIZE := int string buf[256+len wmagic:256+len wmagic+12]; + if(RBUFSIZE % 512) + return 0; # bad block size + return 1; +} + +lkfs(file: string): int +{ + p := array[2] of ref Sys->FD; + if(sys->pipe(p) < 0) + return -1; + c := chan of string; + spawn startfs(c, "/dis/disk/kfs.dis", "disk/kfs" :: "-A" :: "-n" :: "main" :: file :: nil, p[0]); + if(<-c != nil) + return -1; + p[0] = nil; + return sys->mount(p[1], nil, "/n/local", Sys->MREPL|Sys->MCREATE, nil); +} + +startfs(c: chan of string, file: string, args: list of string, fd: ref Sys->FD) +{ + if(fd != nil){ + sys->pctl(Sys->NEWFD, fd.fd :: 1 :: 2 :: nil); + sys->dup(fd.fd, 0); + } + fs := load Command file; + if(fs == nil){ + sys->print("can't load %s: %r\n", file); + c <-= "load failed"; + } + { + fs->init(nil, args); + c <-= nil; + }exception { + "*" => + c <-= "failed"; + * => + c <-= "unknown exception"; + } +} + +# +# partition flash +# +flashdone := 0; + +flashpart(ctl: string, parts: array of string): int +{ + if(flashdone) + return 1; + cfd := sys->open(ctl, Sys->ORDWR); + if(cfd == nil){ + sys->print("can't open %s: %r\n", ctl); + return 0; + } + for(i := 0; i < len parts; i++) + if(sys->fprint(cfd, "%s", parts[i]) < 0){ + sys->print("can't %q to %s: %r\n", parts[i], ctl); + return 0; + } + flashdone = 1; + return 1; +} + +# +# set up flash translation layer +# +ftldone := 0; + +ftlinit(flashmem: string): int +{ + if(ftldone) + return 1; + sys->print("Set flash translation of %s...\n", flashmem); + fd := sys->open("#X/ftlctl", Sys->OWRITE); + if(fd == nil){ + sys->print("can't open #X/ftlctl: %r\n"); + return 0; + } + if(sys->fprint(fd, "init %s", flashmem) <= 0){ + sys->print("can't init flash translation: %r\n"); + return 0; + } + ftldone = 1; + return 1; +} + +configether() +{ + if(ethername == nil) + return; + fd := sys->open("/nvfs/etherparams", Sys->OREAD); + if(fd == nil) + return; + ctl := sys->open("/net/"+ethername+"/clone", Sys->OWRITE); + if(ctl == nil){ + sys->print("init: can't open %s's clone: %r\n", ethername); + return; + } + b := array[1024] of byte; + n := sys->read(fd, b, len b); + if(n <= 0) + return; + for(i := 0; i < n;){ + for(e := i; e < n && b[e] != byte '\n'; e++) + ; + s := string b[i:e]; + if(sys->fprint(ctl, "%s", s) < 0) + sys->print("init: ctl write to %s: %s: %r\n", ethername, s); + i = e+1; + } +} + +donebind := 0; + +# +# set up network mount +# +netfs(mountpt: string): int +{ + fd: ref Sys->FD; + if(!donebind){ + fd = sys->open("/net/ipifc/clone", sys->OWRITE); + if(fd == nil) { + sys->print("init: open /net/ipifc/clone: %r\n"); + return -1; + } + if(sys->fprint(fd, "bind ether %s", ethername) < 0) { + sys->print("could not bind ether0 interface: %r\n"); + return -1; + } + donebind = 1; + }else{ + fd = sys->open("/net/ipifc/0/ctl", Sys->OWRITE); + if(fd == nil){ + sys->print("init: can't reopen /net/ipifc/0/ctl: %r\n"); + return -1; + } + } + server := rdenv("fsip", nil); + if((ip := rdenv("ip", nil)) != nil) { + sys->print("**using %s\n", ip); + sys->fprint(fd, "bind ether /net/ether0"); + sys->fprint(fd, "add %s ", ip); + if((ipgw := rdenv("ipgw", nil)) != nil){ + rfd := sys->open("/net/iproute", Sys->OWRITE); + if(rfd != nil){ + sys->fprint(rfd, "add 0 0 %s", ipgw); + sys->print("**using ipgw=%s\n", ipgw); + } + } + }else if(server == nil){ + sys->print("dhcp..."); + dhcpclient = load Dhcpclient Dhcpclient->PATH; + if(dhcpclient == nil){ + sys->print("can't load dhcpclient: %r\n"); + return -1; + } + dhcpclient->init(); + (cfg, nil, e) := dhcpclient->dhcp("/net", fd, "/net/ether0/addr", nil, nil); + if(e != nil){ + sys->print("dhcp: %s\n", e); + return -1; + } + if(server == nil) + server = cfg.getip(Dhcpclient->OP9fs); + dhcpclient = nil; + } + if(server == nil || server == "0.0.0.0"){ + sys->print("no file server address\n"); + return -1; + } + sys->print("fs=%s\n", server); + + net := "tcp"; # how to specify il? + svcname := net + "!" + server + "!6666"; + + sys->print("dial %s...", svcname); + + (ok, c) := sys->dial(svcname, nil); + if(ok < 0){ + sys->print("can't dial %s: %r\n", svcname); + return -1; + } + + sys->print("\nConnected ...\n"); + if(kr != nil){ + err: string; + sys->print("Authenticate ..."); + ai := kr->readauthinfo("/nvfs/default"); + if(ai == nil){ + sys->print("readauthinfo /nvfs/default failed: %r\n"); + sys->print("trying mount as `nobody'\n"); + } + (c.dfd, err) = auth->client("none", ai, c.dfd); + if(c.dfd == nil){ + sys->print("authentication failed: %s\n", err); + return -1; + } + } + + sys->print("mount %s...", mountpt); + + c.cfd = nil; + n := sys->mount(c.dfd, nil, mountpt, sys->MREPL, ""); + if(n > 0) + return 0; + if(n < 0) + sys->print("%r"); + return -1; +} + +calibrate() +{ + val := rf("/nvfs/calibrate", nil); + if(val != nil){ + fd := sys->open("/dev/touchctl", Sys->OWRITE); + if(fd != nil && sys->fprint(fd, "%s", val) >= 0) + return; + } + done := chan of int; + spawn docal(done); + <-done; +} + +docal(done: chan of int) +{ + sys->pctl(Sys->FORKFD, nil); + ofd := sys->create("/nvfs/calibrate", Sys->OWRITE, 8r644); + if(ofd != nil) + sys->dup(ofd.fd, 1); + cal := load Command "/dis/touchcal.dis"; + if(cal != nil){ + { + cal->init(nil, "touchcal" :: nil); + }exception{ + "fail:*" => + ; + } + } + done <-= 1; +} + +userok(user: string): int +{ + (ok, d) := sys->stat("/usr/"+user); + return ok >= 0 && (d.mode & Sys->DMDIR) != 0; +} + +rdenv(name: string, def: string): string +{ + s := rf("#e/"+name, nil); + if(s != nil) + return s; + s = rf("/nvfs/"+name, def); + while(s != nil && ((c := s[len s-1]) == '\n' || c == '\r')) + s = s[0: len s-1]; + if(s != nil) + return s; + return def; +} + +rf(file: string, default: string): string +{ + fd := sys->open(file, Sys->OREAD); + if(fd != nil){ + buf := array[128] of byte; + nr := sys->read(fd, buf, len buf); + if(nr > 0) + return string buf[0:nr]; + } + return default; +} diff --git a/os/init/ipeinit.b b/os/init/ipeinit.b new file mode 100644 index 00000000..7182ded3 --- /dev/null +++ b/os/init/ipeinit.b @@ -0,0 +1,620 @@ +# +# ipEngine-1 +# + +implement Init; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + +include "keyring.m"; + kr: Keyring; + +include "security.m"; + auth: Auth; + +include "sh.m"; + +Init: module +{ + init: fn(); +}; + +Bootpreadlen: con 128; + +# standard flash partitions + +flashparts := array[] of { + # bootstrap at 0x0 to 0x6000 + "add boot 0 0x6000", + "add param 0x6000 0x10000", + "add kernel 0x10000 0x110000", + "add fs 0x110000 end", +}; + +ethername := "ether0"; + +# +# initialise flash translation +# mount flash file system +# add devices +# start a shell or window manager +# + +init() +{ + sys = load Sys Sys->PATH; + kr = load Keyring Keyring->PATH; + auth = load Auth Auth->PATH; + if(auth != nil) + auth->init(); + + sys->bind("/", "/", Sys->MREPL); + + localok := 0; + if(lfs() >= 0){ + # let's just take a closer look + sys->bind("/n/local/nvfs", "/nvfs", Sys->MREPL|Sys->MCREATE); + (rc, nil) := sys->stat("/n/local/dis/sh.dis"); + if(rc >= 0) + localok = 1; + else + err("local file system unusable"); + } + netok := sys->bind("#l", "/net", Sys->MREPL) >= 0; + if(!netok){ + netok = sys->bind("#l1", "/net", Sys->MREPL) >= 0; + if(netok) + ethername = "ether1"; + } + if(netok) + configether(); + dobind("#I", "/net", sys->MAFTER); # IP + dobind("#p", "/prog", sys->MREPL); # prog + sys->bind("#d", "/fd", Sys->MREPL); + dobind("#c", "/dev", sys->MREPL); # console + dobind("#t", "/dev", sys->MAFTER); # serial line + drawok := sys->bind("#i", "/dev", sys->MAFTER) >= 0; # draw + sys->bind("#m", "/dev", sys->MAFTER); # pointer + sys->bind("#e", "/env", sys->MREPL|sys->MCREATE); # environment + sys->bind("#A", "/dev", Sys->MAFTER); # optional audio + timefile: string; + rootsource: string; + cfd := sys->open("/dev/consctl", Sys->OWRITE); + if(cfd != nil) + sys->fprint(cfd, "rawon"); + for(;;){ + (rootsource, timefile) = askrootsource(localok, netok); + if(rootsource == nil) + break; # internal + (rc, nil) := sys->stat(rootsource+"/dis/sh.dis"); + if(rc < 0) + err("%s has no shell"); + else if(sys->bind(rootsource, "/", Sys->MAFTER) < 0) + sys->print("can't bind %s on /: %r\n", rootsource); + else{ + sys->bind(rootsource+"/dis", "/dis", Sys->MBEFORE|Sys->MCREATE); + break; + } + } + cfd = nil; + + setsysname("ipe"); # set system name + + now := getclock(timefile, rootsource); + setclock("/dev/time", now); + if(timefile != "#r/rtc") + setclock("#r/rtc", now/big 1000000); + + sys->chdir("/"); + if(netok){ + start("ndb/dns", nil); + start("ndb/cs", nil); + } + startup := "/nvfs/startup"; + if(sys->open(startup, Sys->OREAD) != nil){ + shell := load Command Sh->PATH; + if(shell != nil){ + sys->print("Running %s\n", startup); + shell->init(nil, "sh" :: startup :: nil); + } + } + user := username("inferno"); + (ok, nil) := sys->stat("/dis/wm/wm.dis"); + if(drawok && ok >= 0) + (ok, nil) = sys->stat("/dis/wm/logon.dis"); + if(drawok && ok >= 0 && userok(user)){ + wm := load Command "/dis/wm/wm.dis"; + if(wm != nil){ + fd := sys->open("/nvfs/user", Sys->OWRITE); + if(fd != nil){ + sys->fprint(fd, "%s", user); + fd = nil; + } + spawn wm->init(nil, list of {"wm/wm", "wm/logon", "-l", "-u", user}); + exit; + } + sys->print("init: can't load wm/logon: %r"); + } + sh := load Command Sh->PATH; + if(sh == nil){ + err(sys->sprint("can't load %s: %r", Sh->PATH)); + hang(); + } + spawn sh->init(nil, "sh" :: nil); +} + +start(cmd: string, args: list of string) +{ + disfile := cmd; + if(disfile[0] != '/') + disfile = "/dis/"+disfile+".dis"; + (ok, nil) := sys->stat(disfile); + if(ok >= 0){ + dis := load Command disfile; + if(dis == nil) + sys->print("init: can't load %s: %r\n", disfile); + else + spawn dis->init(nil, cmd :: args); + } +} + +dobind(f, t: string, flags: int) +{ + if(sys->bind(f, t, flags) < 0) + err(sys->sprint("can't bind %s on %s: %r", f, t)); +} + +# +# Set system name from nvram if possible +# +setsysname(def: string) +{ + v := array of byte def; + fd := sys->open("/nvfs/ID", sys->OREAD); + if(fd == nil) + fd = sys->open("/env/sysname", sys->OREAD); + if(fd != nil){ + buf := array[Sys->NAMEMAX] of byte; + nr := sys->read(fd, buf, len buf); + while(nr > 0 && buf[nr-1] == byte '\n') + nr--; + if(nr > 0) + v = buf[0:nr]; + } + fd = sys->open("/dev/sysname", sys->OWRITE); + if(fd != nil) + sys->write(fd, v, len v); +} + +getclock(timefile: string, timedir: string): big +{ + now := big 0; + if(timefile != nil){ + fd := sys->open(timefile, Sys->OREAD); + if(fd != nil){ + b := array[64] of byte; + n := sys->read(fd, b, len b-1); + if(n > 0){ + now = big string b[0:n]; + if(now <= big 16r20000000) + now = big 0; # remote itself is not initialised + } + } + } + if(now == big 0){ + if(timedir != nil){ + (ok, dir) := sys->stat(timedir); + if(ok < 0) { + sys->print("init: stat %s: %r", timedir); + return big 0; + } + now = big dir.atime; + }else{ + now = big 993826747000000; + sys->print("time warped\n"); + } + } + return now; +} + +setclock(timefile: string, now: big) +{ + fd := sys->open(timefile, sys->OWRITE); + if (fd == nil) { + sys->print("init: can't open %s: %r", timefile); + return; + } + + b := sys->aprint("%ubd", now); + if (sys->write(fd, b, len b) != len b) + sys->print("init: can't write to %s: %r", timefile); +} + +srv() +{ + sys->print("remote debug srv..."); + fd := sys->open("/dev/eia0ctl", Sys->OWRITE); + if(fd != nil) + sys->fprint(fd, "b115200"); + + fd = sys->open("/dev/eia0", Sys->ORDWR); + if (fd == nil){ + err(sys->sprint("can't open /dev/eia0: %r")); + return; + } + if (sys->export(fd, "/", Sys->EXPASYNC) < 0){ + err(sys->sprint("can't export on serial port: %r")); + return; + } +} + +err(s: string) +{ + sys->fprint(sys->fildes(2), "init: %s\n", s); +} + +hang() +{ + <-chan of int; +} + +tried := 0; + +askrootsource(localok: int, netok: int): (string, string) +{ + stdin := sys->fildes(0); + sources := "kernel" :: nil; + if(netok) + sources = "remote" :: sources; + if(localok){ + sources = "local" :: sources; + if(netok) + sources = "local+remote" :: sources; + } + for(;;) { + s := ""; + if (tried == 0 && (s = rf("/nvfs/rootsource", nil)) != nil) { + tried = 1; + if (s[len s - 1] == '\n') + s = s[:len s - 1]; + sys->print("/nvfs/rootsource: root from %s\n", s); + } else { + sys->print("root from ("); + cm := ""; + for(l := sources; l != nil; l = tl l){ + sys->print("%s%s", cm, hd l); + cm = ","; + } + sys->print(")[%s] ", hd sources); + + s = getline(stdin, hd sources); # default + } + (nil, choice) := sys->tokenize(s, "\t "); + if(choice == nil) + choice = sources; + opt := hd choice; + case opt { + * => + sys->print("\ninvalid boot option: '%s'\n", opt); + "kernel" => + return (nil, "#r/rtc"); + "local" => + return ("/n/local", "#r/rtc"); + "local+remote" => + if(netfs("/n/remote") >= 0) + return ("/n/local", "/n/remote/dev/time"); + "remote" => + if(netfs("/n/remote") >= 0) + return ("/n/remote", "/n/remote/dev/time"); + } + } +} + +getline(fd: ref Sys->FD, default: string): string +{ + result := ""; + buf := array[10] of byte; + i := 0; + for(;;) { + n := sys->read(fd, buf[i:], len buf - i); + if(n < 1) + break; + i += n; + while(i >0 && (nutf := sys->utfbytes(buf, i)) > 0){ + s := string buf[0:nutf]; + for (j := 0; j < len s; j++) + case s[j] { + '\b' => + if(result != nil) + result = result[0:len result-1]; + 'u'&16r1F => + sys->print("^U\n"); + result = ""; + '\r' => + ; + * => + sys->print("%c", s[j]); + if(s[j] == '\n' || s[j] >= 16r80){ + if(s[j] != '\n') + result[len result] = s[j]; + if(result == nil) + return default; + return result; + } + result[len result] = s[j]; + } + buf[0:] = buf[nutf:i]; + i -= nutf; + } + } + return default; +} + +# +# serve local DOS file system using flash translation layer +# +lfs(): int +{ + if(!flashpart("#F/flash/flashctl", flashparts)) + return -1; + if(!ftlinit("#F/flash/fs")) + return -1; + c := chan of string; + spawn startfs(c, "/dis/dossrv.dis", "dossrv" :: "-f" :: "#X/ftldata" :: "-m" :: "/n/local" :: nil); + if(<-c != nil) + return -1; + return 0; +} + +startfs(c: chan of string, file: string, args: list of string) +{ + fs := load Command file; + if(fs == nil){ + sys->print("can't load %s: %r\n", file); + c <-= "load failed"; + } + { + fs->init(nil, args); + }exception e { + "*" => + c <-= "failed"; + exit; + * => + c <-= "unknown exception"; + exit; + } + c <-= nil; +} + +# +# partition flash +# +flashdone := 0; + +flashpart(ctl: string, parts: array of string): int +{ + if(flashdone) + return 1; + cfd := sys->open(ctl, Sys->ORDWR); + if(cfd == nil){ + sys->print("can't open %s: %r\n", ctl); + return 0; + } + for(i := 0; i < len parts; i++) + if(sys->fprint(cfd, "%s", parts[i]) < 0){ + sys->print("can't %q to %s: %r\n", parts[i], ctl); + return 0; + } + flashdone = 1; + return 1; +} + +# +# set up flash translation layer +# +ftldone := 0; + +ftlinit(flashmem: string): int +{ + if(ftldone) + return 1; + sys->print("Set flash translation of %s...\n", flashmem); + fd := sys->open("#X/ftlctl", Sys->OWRITE); + if(fd == nil){ + sys->print("can't open #X/ftlctl: %r\n"); + return 0; + } + if(sys->fprint(fd, "init %s", flashmem) <= 0){ + sys->print("can't init flash translation: %r\n"); + return 0; + } + ftldone = 1; + return 1; +} + +configether() +{ + if(ethername == nil) + return; + fd := sys->open("/nvfs/etherparams", Sys->OREAD); + if(fd == nil) + return; + ctl := sys->open("/net/"+ethername+"/clone", Sys->OWRITE); + if(ctl == nil){ + sys->print("init: can't open %s's clone: %r\n", ethername); + return; + } + b := array[1024] of byte; + n := sys->read(fd, b, len b); + if(n <= 0) + return; + for(i := 0; i < n;){ + for(e := i; e < n && b[e] != byte '\n'; e++) + ; + s := string b[i:e]; + if(sys->fprint(ctl, "%s", s) < 0) + sys->print("init: ctl write to %s: %s: %r\n", ethername, s); + i = e+1; + } +} + +donebind := 0; + +# +# set up network mount +# +netfs(mountpt: string): int +{ + sys->print("bootp ..."); + + fd: ref Sys->FD; + if(!donebind){ + fd = sys->open("/net/ipifc/clone", sys->OWRITE); + if(fd == nil) { + sys->print("init: open /net/ipifc/clone: %r\n"); + return -1; + } + if(sys->fprint(fd, "bind ether %s", ethername) < 0) { + sys->print("could not bind ether0 interface: %r\n"); + return -1; + } + donebind = 1; + }else{ + fd = sys->open("/net/ipifc/0/ctl", Sys->OWRITE); + if(fd == nil){ + sys->print("init: can't reopen /net/ipifc/0/ctl: %r\n"); + return -1; + } + } + if ((ip := rf("/nvfs/ip", nil)) != nil || (ip = rf("#e/myip", nil)) != nil) { + sys->print("**using %s\n", ip); + sys->fprint(fd, "bind ether /net/ether0"); + sys->fprint(fd, "add %s %s", ip, rf("#e/netmask", nil)); + gate := rf("#e/gateway", nil); + if(gate != nil){ + rfd := sys->open("/net/iproute", Sys->OWRITE); + if(rfd != nil){ + sys->fprint(rfd, "add 0 0 %s", gate); + sys->print("set gateway %s\n", gate); + } + } + } else { + { + if(sys->fprint(fd, "bootp") < 0) + sys->print("could not bootp: %r\n"); + } exception e { + "*" => + sys->print("could not bootp: %s\n", e); + } + } + server := rf("/nvfs/fsip", nil); + if(server == nil) + server = rf("#e/fsip", nil); + if (server != nil) { + if (server[len server - 1] == '\n') + server = server[:len server - 1]; + sys->print("/nvfs/fsip: server=%s\n", server); + } else + server = bootp(); + if(server == nil || server == "0.0.0.0") + return -1; + + net := "tcp"; # how to specify il? + svcname := net + "!" + server + "!6666"; + + sys->print("dial %s...", svcname); + + (ok, c) := sys->dial(svcname, nil); + if(ok < 0){ + sys->print("can't dial %s: %r\n", svcname); + return -1; + } + + sys->print("\nConnected ...\n"); + if(kr != nil){ + err: string; + sys->print("Authenticate ..."); + ai := kr->readauthinfo("/nvfs/default"); + if(ai == nil){ + sys->print("readauthinfo /nvfs/default failed: %r\n"); + sys->print("trying mount as `nobody'\n"); + } + (c.dfd, err) = auth->client("none", ai, c.dfd); + if(c.dfd == nil){ + sys->print("authentication failed: %s\n", err); + return -1; + } + } + + sys->print("mount %s...", mountpt); + + c.cfd = nil; + n := sys->mount(c.dfd, nil, mountpt, sys->MREPL, ""); + if(n > 0) + return 0; + if(n < 0) + sys->print("%r"); + return -1; +} + +bootp(): string +{ + fd := sys->open("/net/bootp", sys->OREAD); + if(fd == nil) { + sys->print("init: can't open /net/bootp: %r"); + return nil; + } + + buf := array[Bootpreadlen] of byte; + nr := sys->read(fd, buf, len buf); + fd = nil; + if(nr <= 0) { + sys->print("init: read /net/bootp: %r"); + return nil; + } + + (ntok, ls) := sys->tokenize(string buf, " \t\n"); + while(ls != nil) { + if(hd ls == "fsip"){ + ls = tl ls; + break; + } + ls = tl ls; + } + if(ls == nil) { + sys->print("init: server address not in bootp read"); + return nil; + } + + srv := hd ls; + + sys->print("%s\n", srv); + + return srv; +} + +username(def: string): string +{ + return rf("/nvfs/user", def); +} + +userok(user: string): int +{ + (ok, d) := sys->stat("/usr/"+user); + return ok >= 0 && (d.mode & Sys->DMDIR) != 0; +} + +rf(file: string, default: string): string +{ + fd := sys->open(file, Sys->OREAD); + if(fd != nil){ + buf := array[128] of byte; + nr := sys->read(fd, buf, len buf); + if(nr > 0) + return string buf[0:nr]; + } + return default; +} diff --git a/os/init/jsinit.b b/os/init/jsinit.b new file mode 100644 index 00000000..a8c9cc68 --- /dev/null +++ b/os/init/jsinit.b @@ -0,0 +1,213 @@ +implement Init; +# +# init program for standalone wm using TK +# +include "sys.m"; +sys: Sys; + FD, Connection, sprint, Dir: import sys; + print, fprint, open, bind, mount, dial, sleep, read: import sys; + +include "draw.m"; + draw: Draw; + Context: import draw; + +include "keyring.m"; + kr: Keyring; + +include "security.m"; + auth: Auth; + +Init: module +{ + init: fn(); +}; + +Logon: module +{ + init: fn(ctxt: ref Context, argv: list of string); +}; + +rootfs(server: string): int +{ + ok, n: int; + c: Connection; + + (ok, c) = dial("tcp!" + server + "!6666", nil); + if(ok < 0) + return -1; + + sys->print("Connected ..."); + if(kr != nil && auth != nil){ + err: string; + sys->print("Authenticate ..."); + ai := kr->readauthinfo("/nvfs/default"); + if(ai == nil){ + sys->print("readauthinfo /nvfs/default failed: %r\n"); + sys->print("trying mount as `nobody'\n"); + } + (c.dfd, err) = auth->client("none", ai, c.dfd); + if(c.dfd == nil){ + sys->print("authentication failed: %s\n", err); + return -1; + } + } + + sys->print("mount ..."); + + c.cfd = nil; + n = mount(c.dfd, nil, "/", sys->MREPL, ""); + if(n > 0) + return 0; + return -1; +} + +Bootpreadlen: con 128; + +init() +{ + spec: string; + + sys = load Sys Sys->PATH; + kr = load Keyring Keyring->PATH; + auth = load Auth Auth->PATH; + if(auth != nil) + auth->init(); + + sys->print("**\n** Inferno\n** Vita Nuova\n**\n"); + + sys->print("Setup boot net services ...\n"); + + # + # Setup what we need to call a server and + # Authenticate + # + bind("#l", "/net", sys->MREPL); + bind("#I", "/net", sys->MAFTER); + bind("#c", "/dev", sys->MAFTER); + bind("#r", "/dev", sys->MAFTER); + nvramfd := sys->open("#r/nvram", sys->ORDWR); + if(nvramfd != nil){ + spec = "#Fnvram"; + if(bind(spec, "/nvfs", sys->MAFTER) < 0) + print("init: bind %s: %r\n", spec); + nvramfd = nil; + } + + setsysname(); + + sys->print("bootp..."); + + fd := open("/net/ipifc/clone", sys->OWRITE); + if(fd == nil) { + print("init: open /net/ipifc/clone: %r\n"); + exit; + } + cfg := array of byte "bind ether ether"; + if(sys->write(fd, cfg, len cfg) != len cfg) { + sys->print("could not bind interface: %r\n"); + exit; + } + cfg = array of byte "bootp"; + if(sys->write(fd, cfg, len cfg) != len cfg) { + sys->print("could not bootp: %r\n"); + exit; + } + + fd = open("/net/bootp", sys->OREAD); + if(fd == nil) { + print("init: open /net/bootp: %r"); + exit; + } + + buf := array[Bootpreadlen] of byte; + nr := read(fd, buf, len buf); + fd = nil; + if(nr <= 0) { + print("init: read /net/bootp: %r"); + exit; + } + + (ntok, ls) := sys->tokenize(string buf, " \t\n"); + while(ls != nil) { + if(hd ls == "fsip"){ + ls = tl ls; + break; + } + ls = tl ls; + } + if(ls == nil) { + print("init: server address not in bootp read"); + exit; + } + + srv := hd ls; + sys->print("server %s\nConnect ...\n", srv); + + while(rootfs(srv) < 0) + sleep(1000); + + sys->print("done\n"); + + # + # default namespace + # + bind("#c", "/dev", sys->MBEFORE); # console + bind("#r", "/dev", sys->MAFTER); + if(spec != nil) + bind(spec, "/nvfs", sys->MBEFORE|sys->MCREATE); # our keys + bind("#l", "/net", sys->MBEFORE); # ethernet + bind("#I", "/net", sys->MBEFORE); # TCP/IP + bind("#p", "/prog", sys->MREPL); # prog device + sys->bind("#d", "/fd", Sys->MREPL); + + sys->print("clock...\n"); + setclock(); + + sys->print("logon...\n"); + + logon := load Logon "/dis/wm/logon.dis"; + if(logon == nil) { + print("init: load /dis/wm/logon.dis: %r"); + exit; + } + dc: ref Context; + spawn logon->init(dc, nil); +} + +setclock() +{ + (ok, dir) := sys->stat("/"); + if (ok < 0) { + print("init: stat /: %r"); + return; + } + + fd := sys->open("/dev/time", sys->OWRITE); + if (fd == nil) { + print("init: open /dev/time: %r"); + return; + } + + # Time is kept as microsecs, atime is in secs + b := array of byte sprint("%d000000", dir.atime); + if (sys->write(fd, b, len b) != len b) + print("init: write /dev/time: %r"); +} + +# +# Set system name from nvram +# +setsysname() +{ + fd := open("/nvfs/ID", sys->OREAD); + if(fd == nil) + return; + fds := open("/dev/sysname", sys->OWRITE); + if(fds == nil) + return; + buf := array[128] of byte; + nr := sys->read(fd, buf, len buf); + if(nr <= 0) + return; + sys->write(fds, buf, nr); +} diff --git a/os/init/mkfile b/os/init/mkfile new file mode 100644 index 00000000..e027809d --- /dev/null +++ b/os/init/mkfile @@ -0,0 +1,27 @@ +<../../mkconfig + +OBJ=\ + wminit.dis\ + +all:V: $OBJ +install:V: all +installall:V: all + +clean nuke:V: + rm -f *.dis *.sbl + +INCLD=\ + -I$ROOT/module\ + +%.dis: %.b + limbo $INCLD -gw $stem.b + +%.s: %.b + limbo $INCLD -w -G -S $stem.b + +ir%.dis: ../../appl/lib/ir%.b + limbo $INCLD -gw $prereq + +ir%.s: ../../appl/lib/ir%.b + limbo $INCLD -w -G -S $prereq + diff --git a/os/init/mpcinit.b b/os/init/mpcinit.b new file mode 100644 index 00000000..bef89fa2 --- /dev/null +++ b/os/init/mpcinit.b @@ -0,0 +1,383 @@ +implement Init; +# +# init program for Motorola 800 series (serial console only) +# +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + +include "keyring.m"; + kr: Keyring; + +include "security.m"; + auth: Auth; + +Init: module +{ + init: fn(); +}; + +Shell: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +Bootpreadlen: con 128; + +# option switches +UseLocalFS: con 1<<0; +EtherBoot: con 1<<1; +Prompting: con 1<<3; + +lfs(): int +{ + if(!ftlinit("#F/flash/flash", 1024*1024, 1024*1024)) + return -1; + if(mountkfs("#X/ftldata", "main", "flash") < 0) + return -1; + if(sys->bind("#Kmain", "/n/local", sys->MREPL) < 0){ + sys->print("can't bind #Kmain to /n/local: %r\n"); + return -1; + } + if(sys->bind("/n/local", "/", Sys->MCREATE|Sys->MREPL) < 0){ + sys->print("can't bind /n/local after /: %r\n"); + return -1; + } + return 0; +} + +donebind := 0; + +# +# set up network mount +# +netfs(mountpt: string): int +{ + sys->print("bootp ..."); + + fd: ref Sys->FD; + if(!donebind){ + fd = sys->open("/net/ipifc/clone", sys->OWRITE); + if(fd == nil) { + sys->print("init: open /net/ipifc/clone: %r\n"); + return -1; + } + if(sys->fprint(fd, "bind ether %s", "/net/ether0") < 0) { + sys->print("could not bind ether0 interface: %r\n"); + return -1; + } + donebind = 1; + }else{ + fd = sys->open("/net/ipifc/0/ctl", Sys->OWRITE); + if(fd == nil){ + sys->print("init: can't reopen /net/ipifc/0/ctl: %r\n"); + return -1; + } + } + if(sys->fprint(fd, "bootp") < 0){ + sys->print("init: bootp failed: %r\n"); + return -1; + } + + server := bootp(); + if(server == nil) + return -1; + + net := "tcp"; # how to specify il? + svcname := net + "!" + server + "!6666"; + + sys->print("dial %s...", svcname); + + (ok, c) := sys->dial(svcname, nil); + if(ok < 0){ + sys->print("can't dial %s: %r\n", svcname); + return -1; + } + + sys->print("\nConnected ...\n"); + if(kr != nil){ + err: string; + sys->print("Authenticate ..."); + ai := kr->readauthinfo("/nvfs/default"); + if(ai == nil){ + sys->print("readauthinfo /nvfs/default failed: %r\n"); + sys->print("trying mount as `nobody'\n"); + } + (c.dfd, err) = auth->client("none", ai, c.dfd); + if(c.dfd == nil){ + sys->print("authentication failed: %s\n", err); + return -1; + } + } + + sys->print("mount %s...", mountpt); + + c.cfd = nil; + n := sys->mount(c.dfd, nil, mountpt, sys->MREPL, ""); + if(n > 0) + return 0; + if(n < 0) + sys->print("%r"); + return -1; +} + +init() +{ + sys = load Sys Sys->PATH; + kr = load Keyring Keyring->PATH; + auth = load Auth Auth->PATH; + if(auth != nil) + auth->init(); + + sys->print("**\n** Inferno\n** Vita Nuova\n**\n"); + + optsw := options(); + sys->print("Switch options: 0x%ux\n", optsw); + + # + # Setup what we need to call a server and + # Authenticate + # + sys->bind("#l", "/net", sys->MREPL); + sys->bind("#I", "/net", sys->MAFTER); + sys->bind("#c", "/dev", sys->MAFTER); + + fsready := 0; + mountpt := "/"; + usertc := 0; + + if((optsw & Prompting) == 0){ + if(optsw & UseLocalFS){ + sys->print("Option: use local file system\n"); + if(lfs() == 0){ + fsready = 1; + mountpt = "/n/remote"; + } + } + + if(optsw & EtherBoot){ + sys->print("Attempting remote mount\n"); + if(netfs(mountpt) == 0) + fsready = 1; + } + } + + if(fsready == 0){ + + sys->print("\n\n"); + + stdin := sys->fildes(0); + buf := array[128] of byte; + sources := "fs" :: "net" :: nil; + + loop: for(;;) { + sys->print("root from ("); + cm := ""; + for(l := sources; l != nil; l = tl l){ + sys->print("%s%s", cm, hd l); + cm = ","; + } + sys->print(")[%s] ", hd sources); + + n := sys->read(stdin, buf, len buf); + if(n <= 0) + continue; + if(buf[n-1] == byte '\n') + n--; + + (nil, choice) := sys->tokenize(string buf[0:n], "\t "); + + if(choice == nil) + choice = sources; + opt := hd choice; + case opt { + * => + sys->print("\ninvalid boot option: '%s'\n", opt); + break; + "fs" or "" => + if(lfs() == 0){ + usertc = 1; + break loop; + } + "net" => + if(netfs("/") == 0) + break loop; + } + } + } + + # + # default namespace + # + sys->unmount(nil, "/dev"); + sys->bind("#c", "/dev", sys->MBEFORE); # console + sys->bind("#l", "/net", sys->MBEFORE); # ethernet + sys->bind("#I", "/net", sys->MBEFORE); # TCP/IP + sys->bind("#p", "/prog", sys->MREPL); # prog device + sys->bind("#d", "/fd", Sys->MREPL); + + setsysname(); + + sys->print("clock...\n"); + setclock(usertc, mountpt); + + sys->print("Console...\n"); + + shell := load Shell "/dis/sh.dis"; + if(shell == nil) { + sys->print("init: load /dis/sh.dis: %r"); + exit; + } + dc: ref Draw->Context; + shell->init(dc, nil); +} + +setclock(usertc: int, timedir: string) +{ + now := 0; + if(usertc){ + fd := sys->open("#r/rtc", Sys->OREAD); + if(fd != nil){ + b := array[64] of byte; + n := sys->read(fd, b, len b-1); + if(n > 0){ + b[n] = byte 0; + now = int string b; + if(now <= 16r20000000) + now = 0; # rtc itself is not initialised + } + } + } + if(now == 0){ + (ok, dir) := sys->stat(timedir); + if (ok < 0) { + sys->print("init: stat %s: %r", timedir); + return; + } + now = dir.atime; + } + fd := sys->open("/dev/time", sys->OWRITE); + if (fd == nil) { + sys->print("init: can't open /dev/time: %r"); + return; + } + + # Time is kept as microsecs, atime is in secs + b := array of byte sys->sprint("%ud000000", now); + if (sys->write(fd, b, len b) != len b) + sys->print("init: can't write /dev/time: %r"); +} + +# +# Set system name from nvram +# +setsysname() +{ + fd := sys->open("/nvfs/ID", sys->OREAD); + if(fd == nil) + return; + fds := sys->open("/dev/sysname", sys->OWRITE); + if(fds == nil) + return; + buf := array[128] of byte; + nr := sys->read(fd, buf, len buf); + if(nr <= 0) + return; + sys->write(fds, buf, nr); +} + +# +# fetch options from switch DS2 +# +options(): int +{ + fd := sys->open("#r/switch", Sys->OREAD); + if(fd == nil){ + sys->print("can't open #r/switch: %r\n"); + return 0; + } + b := array[20] of byte; + n := sys->read(fd, b, len b); + s := string b[0:n]; + return int s; +} + +bootp(): string +{ + fd := sys->open("/net/bootp", sys->OREAD); + if(fd == nil) { + sys->print("init: can't open /net/bootp: %r"); + return nil; + } + + buf := array[Bootpreadlen] of byte; + nr := sys->read(fd, buf, len buf); + fd = nil; + if(nr <= 0) { + sys->print("init: read /net/bootp: %r"); + return nil; + } + + (ntok, ls) := sys->tokenize(string buf, " \t\n"); + while(ls != nil) { + if(hd ls == "fsip"){ + ls = tl ls; + break; + } + ls = tl ls; + } + if(ls == nil) { + sys->print("init: server address not in bootp read"); + return nil; + } + + srv := hd ls; + + sys->print("%s\n", srv); + + return srv; +} + +# +# set up flash translation layer +# +ftldone := 0; + +ftlinit(flashmem: string, offset: int, length: int): int +{ + if(ftldone) + return 1; + sys->print("Set flash translation of %s at offset %d (%d bytes)\n", flashmem, offset, length); + fd := sys->open("#X/ftlctl", Sys->OWRITE); + if(fd == nil){ + sys->print("can't open #X/ftlctl: %r\n"); + return 0; + } + if(sys->fprint(fd, "init %s %ud %ud", flashmem, offset, length) <= 0){ + sys->print("can't init flash translation: %r"); + return 0; + } + ftldone = 1; + return 1; +} + +# +# Mount kfs filesystem +# +mountkfs(devname: string, fsname: string, options: string): int +{ + fd := sys->open("#Kcons/kfsctl", sys->OWRITE); + if(fd == nil) { + sys->print("could not open #Kcons/kfsctl: %r\n"); + return -1; + } + if(sys->fprint(fd, "filsys %s %s %s", fsname, devname, options) <= 0){ + sys->print("could not write #Kcons/kfsctl: %r\n"); + return -1; + } + if(options == "ro") + sys->fprint(fd, "cons flashwrite"); + return 0; +} diff --git a/os/init/pcdemo.b b/os/init/pcdemo.b new file mode 100644 index 00000000..5a7ec76b --- /dev/null +++ b/os/init/pcdemo.b @@ -0,0 +1,273 @@ +# +# Init shell ``with the kitchen sink'', for development purposes. +# +implement InitShell; + +include "sys.m"; +include "draw.m"; + +sys: Sys; +FD, Connection, sprint, Dir: import sys; +print, fprint, open, bind, mount, dial, sleep, read: import sys; + +stderr: ref sys->FD; # standard error FD + +InitShell: module +{ + init: fn(nil: ref Draw->Context, nil: list of string); +}; + +Sh: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +init(nil: ref Draw->Context, nil: list of string) +{ + sys = load Sys Sys->PATH; + stderr = sys->fildes(2); + + sys->print("Welcome to Inferno...\n"); + + sys->pctl(Sys->NEWNS, nil); + if (imount("/n/remote")) { + bind("/n/remote", "/", sys->MAFTER); + bind("/n/remote/dis", "/dis", sys->MBEFORE); + mountkfs("#W/flash0fs", "fs", "/n/local", sys->MREPL); + } + else { + # bind("#U/pcdemo", "/", sys->MREPL); + # mountkfs("#U/pcdemo.kfs", "fs", "/", sys->MBEFORE); + # bind("#U/pcdemo/usr", "/usr", sys->MAFTER); + mountkfs("#R/ramdisk", "fs", "/", sys->MBEFORE); + bind("/services", "/data", sys->MREPL|sys->MCREATE); + } + + namespace(); + srv(); + + if (1) { + bind("/icons/oldlogon.bit", "/icons/logon.bit", sys->MREPL); + bind("/icons/tk/oldinferno.bit", "/icons/tk/inferno.bit", sys->MREPL); + } + + sys->print("starting shell (type wm/logon or wm/wmcp)\n"); + shell := sysenv("shell"); + if (shell == nil) + shell = "/dis/sh.dis"; + sh := load Sh shell; + spawn sh->init(nil, nil); +} + +namespace() +{ + # Bind anything useful we can get our hands on. Ignore errors. + sys->print("namespace...\n"); + sys->bind("#I", "/net", sys->MAFTER); # IP + sys->bind("#I1", "/net.alt", sys->MREPL); # second IP for PPP tests + sys->bind("#p", "/prog", sys->MREPL); # prog device + sys->bind("#d", "/fd", Sys->MREPL); + sys->bind("#i", "/dev", sys->MREPL); # draw device + sys->bind("#t", "/dev", sys->MAFTER); # serial line + sys->bind("#c", "/dev", sys->MAFTER); # console device + sys->bind("#W", "/dev", sys->MAFTER); # Flash + sys->bind("#O", "/dev", sys->MAFTER); # Modem + sys->bind("#T", "/dev", sys->MAFTER); # Touchscreen + sys->bind("#H", "/dev", sys->MAFTER); # Ata disk device + sys->bind("#b", "/dev", sys->MAFTER); # debug device + sys->bind("#c", "/chan", sys->MREPL); + sys->bind("/data", "/usr/inferno", sys->MREPL|sys->MCREATE); + sys->bind("/data", "/usr/charon", sys->MREPL|sys->MCREATE); + sys->bind("/data", "/usr/shaggy", sys->MREPL|sys->MCREATE); +} + +mountkfs(devname: string, fsname: string, where: string, flags: int): int +{ + sys->print("mount kfs...\n"); + fd := sys->open("#Kcons/kfsctl", sys->OWRITE); + if (fd == nil) { + sys->fprint(stderr, "could not open #Kcons/kfsctl: %r\n"); + return 0; + } + kfsopt := ""; + kfsrw := sysenv("kfsrw"); +# if (kfsrw != "1") +# kfsopt = " ronly"; + b := array of byte ("filsys " + fsname + " " + devname + kfsopt); + if (sys->write(fd, b, len b) < 0) { + sys->fprint(stderr, "could not write #Kcons/kfsctl: %r\n"); + return 0; + } + if (sys->bind("#K" + fsname, where, flags) < 0) { + sys->fprint(stderr, "could not bind %s to %s: %r\n", "#K" + fsname, where); + return 0; + } + return 1; +} + +dialfs(server: string, where: string): int +{ + ok, n: int; + c: Connection; + + (ok, c) = dial("tcp!" + server + "!6666", nil); + if(ok < 0) + return 0; + + sys->print("mount..."); + + c.cfd = nil; + n = mount(c.dfd, where, sys->MREPL, ""); + if(n > 0) + return 1; + sys->print("mount failed: %r\n"); + return 0; +} + +Bootpreadlen: con 128; + +imount(where: string): int +{ + sys->print("bootp..."); + if (sys->bind("#I", "/net", sys->MREPL) < 0) { + sys->fprint(stderr, "could not bind ip device: %r\n"); + return 0; + } + if (sys->bind("#l", "/net", sys->MAFTER) < 0) { + sys->fprint(stderr, "could not bind ether device: %r\n"); + return 0; + } + + fd := sys->open("/net/ipifc/clone", sys->OWRITE); + if(fd == nil) { + sys->print("init: open /net/ipifc: %r"); + return 0; + } + + cfg := array of byte "bind ether ether0"; + if(sys->write(fd, cfg, len cfg) != len cfg) { + sys->fprint(stderr, "could not bind interface: %r\n"); + return 0; + } + cfg = array of byte "bootp"; + if(sys->write(fd, cfg, len cfg) != len cfg) { + sys->fprint(stderr, "could not bootp: %r\n"); + return 0; + } + + bfd := sys->open("/net/bootp", sys->OREAD); + if(bfd == nil) { + sys->print("init: can't open /net/bootp: %r"); + return 0; + } + + buf := array[Bootpreadlen] of byte; + nr := sys->read(bfd, buf, len buf); + bfd = nil; + if(nr <= 0) { + sys->print("init: read /net/bootp: %r"); + return 0; + } + + (ntok, ls) := sys->tokenize(string buf, " \t\n"); + while(ls != nil) { + if(hd ls == "fsip"){ + ls = tl ls; + break; + } + ls = tl ls; + } + if(ls == nil) { + sys->print("init: server address not in bootp read"); + return 0; + } + + server := hd ls; + sys->print("imount: server %s\n", server); + + return dialfs(server, where); +} + +srv() +{ + remotedebug := sysenv("remotedebug"); + if(remotedebug != "1") + return; + remotespeed := sysenv("remotespeed"); + if (remotespeed == nil) + remotespeed = "38400"; + + sys->print("srv..."); + if(echoto("#t/eia0ctl", "b" + remotespeed) < 0) + return; + + fd := sys->open("/dev/eia0", Sys->ORDWR); + if (fd == nil) { + sys->print("eia data open: %r\n"); + return; + } + if (sys->export(fd, Sys->EXPASYNC) < 0) { + sys->print("export: %r\n"); + return; + } + sys->print("ok\n"); +} + +sysenv(param: string): string +{ + fd := sys->open("#c/sysenv", sys->OREAD); + if (fd == nil) + return(nil); + buf := array[4096] of byte; + nb := sys->read(fd, buf, len buf); + (nfl,fl) := sys->tokenize(string buf, "\n"); + while (fl != nil) { + pair := hd fl; + (npl, pl) := sys->tokenize(pair, "="); + if (npl > 1) { + if ((hd pl) == param) + return hd tl pl; + } + fl = tl fl; + } + return nil; +} + +echoto(fname, str: string): int +{ + fd := sys->open(fname, Sys->OWRITE); + if(fd == nil) { + sys->print("%s: %r\n", fname); + return -1; + } + x := array of byte str; + if(sys->write(fd, x, len x) == -1) { + sys->print("write: %r\n"); + return -1; + } + return 0; +} + +hang() +{ + c := chan of int; + <- c; +} + +# +# Set system name from nvram +# +setsysname() +{ + fd := open("/nvfs/ID", sys->OREAD); + if(fd == nil) + return; + fds := open("/dev/sysname", sys->OWRITE); + if(fds == nil) + return; + buf := array[128] of byte; + nr := sys->read(fd, buf, len buf); + if(nr <= 0) + return; + sys->write(fds, buf, nr); +} diff --git a/os/init/pcinit.b b/os/init/pcinit.b new file mode 100644 index 00000000..98899b45 --- /dev/null +++ b/os/init/pcinit.b @@ -0,0 +1,405 @@ +implement Init; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + +include "keyring.m"; + kr: Keyring; + +include "security.m"; + auth: Auth; + +include "styx.m"; + + dosfs : Dosfs; + +PROMPT: con 1; # boot from prompt? (0 means boot from fs) +SHELL: con 0; # Start a Shell, not Logon +INIT: con "/init"; # file to read init commands from + +startip := 0; + +Bootpreadlen: con 128; + +Init: module +{ + init: fn(); +}; + +Logon: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +Sh: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +lfs(dev: string): int +{ + (ok, dir) := sys->stat(dev); + if (ok < 0) { + sys->print("init: stat %s: %r\n", dev); + return -1; + } + pipefd := array[2] of ref Sys->FD; + dosfs = load Dosfs "#/./dosfs"; + if(dosfs == nil) { + sys->fprint(sys->fildes(2),"load #/.dosfs: %r\n"); + return -1; + } + + dosfs->init(dev, "", 0); + if(sys->pipe(pipefd) < 0){ + sys->fprint(sys->fildes(2),"pipe %r\n"); + exit; + } + spawn dosfs->dossrv(pipefd[1]); + + n := sys->mount(pipefd[0], "/", sys->MREPL|sys->MCREATE, ""); + if(n<0) { + sys->print("couldn't mount. %r\n"); + return -1; + } + + dosfs->setup(); + + sys->print("mounted %s at /\n", dev); + + return 0; +} + +ipinit() +{ + fd := sys->open("/nvfs/IP", sys->OREAD); + if(fd == nil) + return; + + buf := array[128] of byte; + nr := sys->read(fd, buf, len buf); + if(nr <= 0) + return; + + cfd := sys->open("/net/ipifc/clone", sys->ORDWR); + if(cfd == nil) { + sys->print("init: open /net/ipifc/clone: %r"); + exit; + } + + sys->fprint(cfd, "bind ether ether0"); + sys->fprint(cfd, "%s", string buf[0:nr]); +} + +netfs(): int +{ + cfd := sys->open("/net/ipifc/clone", sys->ORDWR); + if(cfd == nil) { + sys->print("init: open /net/ipifc/clone: %r"); + exit; + } + sys->fprint(cfd, "bind ether ether0"); + + server:= bootp(cfd); + sys->print("dial..."); + (ok, c) := sys->dial("tcp!" + server + "!6666", nil); + if(ok < 0) + return -1; + + if(kr != nil && auth != nil){ + err: string; + sys->print("Authenticate ..."); + ai := kr->readauthinfo("/nvfs/default"); + if(ai == nil){ + sys->print("readauthinfo /nvfs/default failed: %r\n"); + sys->print("trying mount as `nobody'\n"); + } + (c.dfd, err) = auth->client("none", ai, c.dfd); + if(c.dfd == nil){ + sys->print("authentication failed: %s\n", err); + return -1; + } + } + + sys->print("mount ..."); + + c.cfd = nil; + n := sys->mount(c.dfd, "/", sys->MREPL, ""); + if(n > 0) + return 0; + + return -1; +} + +init() +{ + spec: string; + + sys = load Sys Sys->PATH; + kr = load Keyring Keyring->PATH; + auth = load Auth Auth->PATH; + if(auth != nil) + auth->init(); + + sys->print("**\n** Inferno\n** Vita Nuova\n**\n\n\n"); + + # + # Setup what we need to call a server and + # Authenticate + # + sys->bind("#l", "/net", sys->MREPL); + sys->bind("#I", "/net", sys->MAFTER); + sys->bind("#c", "/dev", sys->MAFTER); + + sys->print("Non-volatile ram read ...\n"); + + nvramfd := sys->open("#H/hd0nvram", sys->ORDWR); + if(nvramfd != nil) { + spec = "#Fhd0nvram"; + if(sys->bind(spec, "/nvfs", sys->MAFTER) < 0) + sys->print("init: bind %s: %r\n", spec); + sys->print("mounted tinyfs"); + nvramfd = nil; + } + + sys->print("\n\n"); + + if(!PROMPT) { + if(lfs("#H/hd0fs") == 0) + startip = 1; + else + bootfrom(); + } else + bootfrom(); + + sys->bind("#l", "/net", sys->MBEFORE); + sys->bind("#I", "/net", sys->MBEFORE); + sys->bind("#c", "/dev", sys->MBEFORE); + + if(startip) + ipinit(); + + setsysname(); + + sys->print("clock...\n"); + setclock(); + + if(SHELL) { + sys->print("shell...\n"); + + logon := load Logon "/dis/sh.dis"; + if(logon == nil) { + sys->print("init: load /dis/wm/logon.dis: %r"); + exit; + } + dc: ref Draw->Context; + spawn logon->init(dc, nil); + exit; + } + + runprogs(); +} + +bootfrom() +{ + buf := array[128] of byte; + stdin := sys->fildes(0); + + fsdev := "#H/hd0disk"; + + loop: for(;;) { + sys->print("boot from [fs, net]: "); + + n := sys->read(stdin, buf, len buf); + if(n <= 0) + continue; + if(buf[n-1] == byte '\n') + n--; + + (nil, choice) := sys->tokenize(string buf[:n], "\t "); + if(choice == nil) + continue; + + opt := hd choice; + choice = tl choice; + + case opt { + * => + sys->print("\ninvalid boot option: '%s'\n", opt); + break; + "fs" or "" => + if(choice != nil) + fsdev = hd choice; + if(lfs(fsdev) == 0) { + startip = 1; + break loop; + } + "net" => + if(netfs() == 0) + break loop; + } + } +} + +runprogs() +{ + fd:= sys->open(INIT, Sys->OREAD); + if(fd == nil) { + sys->print("open %s: %r\n", INIT); + return; + } + + dc := ref Draw->Context; + dc.ctomux = chan of int; + + for(l:=1;;l++) { + (e, line):= getline(fd); + if(e != nil) { + sys->print(INIT+":%d: %s\n", l, e); + return; + } + if(line == nil) + break; + if(line == "\n" || line[0] == '#') + continue; + if(line[len line-1] == '\n') + line = line[:len line-1]; + (n, f):= sys->tokenize(line, " \t"); + if(n < 0) { + sys->print(INIT+":%d: tokenize: %r\n", l); + return; + } + if(n < 2) { + sys->print(INIT+":%d: not enough fields\n", l); + continue; + } + e = run(dc, f); + if(e != nil) + sys->print(INIT+":%d: %s\n", l, e); + } +} + +run(dc: ref Draw->Context, argv: list of string): string +{ + c:= hd argv; + argv = tl argv; + prog:= hd argv; + ext:= ".dis"; + if(prog[len prog-4:] == ".dis") + ext = ""; + sh:= load Sh prog+ext; + if(sh == nil) + sh = load Sh "/dis/"+prog+ext; + if(sh == nil) + return sys->sprint("%s: load: %r", prog); + + case c { + "run" => + e:= ref Sys->Exception; + if(sys->rescue("fail:*", e)) + return prog+": "+e.name; + sh->init(dc, argv); + return nil; + "spawn" => + spawn sh->init(dc, argv); + return nil; + } + return c+": unknown command"; +} + +getline(fd: ref Sys->FD): (string, string) +{ + s:= ""; + buf:= array[1] of byte; + for(;;) { + n:= sys->read(fd, buf, 1); + if(n < 0) + return (sys->sprint("getline: read: %r\n"), nil); + if(n == 0) + return (nil, s); + s += string buf; + if(buf[0] == byte '\n') + return (nil, s); + } +} + +setclock() +{ + (ok, dir) := sys->stat("/"); + if (ok < 0) { + sys->print("init: stat /: %r"); + return; + } + + fd := sys->open("/dev/time", sys->OWRITE); + if (fd == nil) { + sys->print("init: open /dev/time: %r\n"); + return; + } + + # Time is kept as microsecs, atime is in secs + b := array of byte sys->sprint("%d000000", dir.atime); + if (sys->write(fd, b, len b) != len b) + sys->print("init: write /dev/time: %r"); +} + +# +# Set system name from nvram +# +setsysname() +{ + fd := sys->open("/nvfs/ID", sys->OREAD); + if(fd == nil) + return; + fds := sys->open("/dev/sysname", sys->OWRITE); + if(fds == nil) + return; + buf := array[128] of byte; + nr := sys->read(fd, buf, len buf); + if(nr <= 0) + return; + sys->write(fds, buf, nr); +} + +bootp(cfd: ref sys->FD): string +{ + sys->print("bootp ..."); + + sys->fprint(cfd, "bootp"); + + fd := sys->open("/net/bootp", sys->OREAD); + if(fd == nil) { + sys->print("init: open /net/bootp: %r"); + exit; + } + + + buf := array[Bootpreadlen] of byte; + nr := sys->read(fd, buf, len buf); + fd = nil; + if(nr <= 0) { + sys->print("init: read /net/bootp: %r"); + exit; + } + (ntok, ls) := sys->tokenize(string buf, " \t\n"); + while(ls != nil) { + if(hd ls == "fsip"){ + ls = tl ls; + break; + } + ls = tl ls; + } + if(ls == nil) { + sys->print("init: server address not in bootp read"); + exit; + } + + srv := hd ls; + + sys->print("(ip=%s)", srv); + + return srv; +} diff --git a/os/init/reminit.b b/os/init/reminit.b new file mode 100644 index 00000000..b87ff996 --- /dev/null +++ b/os/init/reminit.b @@ -0,0 +1,239 @@ +implement Init; +# +# init program for standalone remote pc kernel - for benchmarking +# +include "sys.m"; +sys: Sys; +FD, Connection, sprint, Dir: import sys; +print, fprint, open, bind, mount, dial, sleep, read, chdir: import sys; + +include "draw.m"; +draw: Draw; +Context: import draw; + +include "keyring.m"; +kr: Keyring; + +include "security.m"; +auth: Auth; + +Init: module +{ + init: fn(); +}; + +Shell: module +{ + init: fn(ctxt: ref Context, argv: list of string); +}; + +remotefs(server: string): int +{ + ok, n: int; + c: Connection; + + (ok, c) = dial("tcp!" + server + "!6666", nil); + if(ok < 0) + return -1; + + sys->print("Connected ...\n"); + if(kr != nil){ + err: string; + sys->print("Authenticate ..."); + ai := kr->readauthinfo("/nvfs/default"); + if(ai == nil){ + sys->print("readauthinfo /nvfs/default failed: %r\n"); + sys->print("trying mount as `nobody'\n"); + } + (c.dfd, err) = auth->client("none", ai, c.dfd); + if(c.dfd == nil){ + sys->print("authentication failed: %s\n", err); + return -1; + } + } + + sys->print("mount ...\n"); + + c.cfd = nil; + n = mount(c.dfd, "/n/remote", sys->MREPL, ""); + if(n > 0) + return 0; + return -1; +} + +Bootpreadlen: con 128; + +bootp(): string +{ +# +# BUG: if bootp fails, can't then use "add ether" correctly +# + fd := open("/net/ipifc", sys->OWRITE); + if(fd == nil) { + print("init: open /net/ipifc: %r"); + return nil; + } + fprint(fd, "bootp /net/ether0"); + + fd = open("/net/bootp", sys->OREAD); + if(fd == nil) { + print("init: open /net/bootp: %r\n"); + return nil; + } + + buf := array[Bootpreadlen] of byte; + nr := read(fd, buf, len buf); + fd = nil; + if(nr > 0) { + (ntok, ls) := sys->tokenize(string buf, " \t\n"); + while(ls != nil) { + if(hd ls == "fsip") { + ls = tl ls; + break; + } + ls = tl ls; + } + srv : string; + if(ls == nil || ((srv = hd ls) == "0.0.0.0")) { + print("init: server address not in bootp read\n"); + return nil; + } + return srv; + } + return nil; +} + +init() +{ + + sys = load Sys Sys->PATH; + stdin := sys->fildes(0); + kr = load Keyring Keyring->PATH; + auth = load Auth Auth->PATH; + if(auth != nil) + auth->init(); + + sys->print("**\n** Inferno\n** Vita Nuova\n**\n"); + + sys->print("Setup boot net services ...\n"); + + # + # Setup what we need to call a server and + # Authenticate + # + sys->print("Bind ethernet ...\n"); + bind("#l", "/net", sys->MREPL); + sys->print("Bind IP ...\n"); + bind("#I", "/net", sys->MAFTER); + sys->print("Bind console ...\n"); + bind("#c", "/dev", sys->MAFTER); + + setsysname(); + if (1) { + print("bootp ...\n"); + srv := bootp(); + if (srv != nil) + if (remotefs(srv) < 0) + print("No remote filesystem\n"); + else { + print("Remote filesystem mounted\n"); + bind("/n/remote/dis", "/dis", sys->MBEFORE); + bind("/n/remote/dis/lib", "/dis/lib", sys->MBEFORE); + } + } else { + print("Standalone mode\n"); + fd := open("/net/ipifc", sys->OWRITE); + if(fd == nil) { + print("init: open /net/ipifc: %r"); + exit; + } + fprint(fd, "add ether /net/ether0 %s %s", "200.1.1.60", "255.255.255.0"); + fd = nil; + } + # + # default namespace + # + sys->unmount(nil, "/dev"); + bind("#c", "/dev", sys->MBEFORE); # console + bind("#l", "/net", sys->MBEFORE); # ethernet + bind("#I", "/net", sys->MBEFORE); # TCP/IP + bind("#p", "/prog", sys->MREPL); # prog device + sys->bind("#d", "/fd", Sys->MREPL); + + bind("#T", "/dev", sys->MBEFORE); # kprof device + bind("#x", "/dev", sys->MBEFORE); # bench device + + print("clock...\n"); + setclock(); + + print("Server...\n"); + dc: ref Context; + cs := load Shell "/dis/ndb/cs.dis"; + if(cs == nil) + print("Server not loaded\n"); + else + cs->init(dc, "cs" :: nil); + server := load Shell "/dis/lib/srv.dis"; + if(server == nil) + print("Server not loaded\n"); + else { +# server->init(dc, "srv" :: nil); + } + + print("Console...\n"); +sys->chdir("/n/remote/usr/john/appl/bench"); + shell := load Shell "/dis/sh.dis"; + if(shell == nil) { + print("init: load /dis/sh.dis: %r"); + exit; + } + shell->init(dc, nil); +} + +setclock() +{ + (ok, dir) := sys->stat("/"); + if (ok < 0) { + print("init: stat /: %r"); + return; + } + + fd := sys->open("/dev/time", sys->OWRITE); + if (fd == nil) { + print("init: open /dev/time: %r"); + return; + } + + # Time is kept as microsecs, atime is in secs + b := array of byte sprint("%d000000", dir.atime); + if (sys->write(fd, b, len b) != len b) + print("init: write /dev/time: %r"); +} + +# +# Set system name from nvram +# +setsysname() +{ + fd := open("/nvfs/ID", sys->OREAD); + if(fd == nil) + return; + fds := open("/dev/sysname", sys->OWRITE); + if(fds == nil) + return; + buf := array[128] of byte; + nr := sys->read(fd, buf, len buf); + if(nr <= 0) + return; + sys->write(fds, buf, nr); +} + +readline(fd: ref Sys->FD): string +{ + l := array [128] of byte; + nb := sys->read(fd, l, len l); + if(nb <= 1) + return ""; + return string l[0:nb-1]; +} + diff --git a/os/init/rpcginit.b b/os/init/rpcginit.b new file mode 100644 index 00000000..ce142d84 --- /dev/null +++ b/os/init/rpcginit.b @@ -0,0 +1,610 @@ +# +# RPCG RPX-Lite AW +# + +implement Init; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + +include "keyring.m"; + kr: Keyring; + +include "security.m"; + auth: Auth; + +include "sh.m"; + +Init: module +{ + init: fn(); +}; + +Bootpreadlen: con 128; + +# standard flash partitions + +flashparts := array[] of { + # rpcg monitor at 0x300000 to end + "add params 0 0x20000", + "add boot 0x20000 0x40000", + "add kernel 0x40000 0xC0000", + "add fs 0xC0000 end", +}; + +ethername := "ether0"; + +# +# initialise flash translation +# mount flash file system +# add devices +# start a shell or window manager +# + +init() +{ + sys = load Sys Sys->PATH; + kr = load Keyring Keyring->PATH; + auth = load Auth Auth->PATH; + if(auth != nil) + auth->init(); + + sys->bind("/", "/", Sys->MREPL); + + localok := 0; + if(lfs() >= 0){ + # let's just take a closer look + sys->bind("/n/local/nvfs", "/nvfs", Sys->MREPL|Sys->MCREATE); + (rc, nil) := sys->stat("/n/local/dis/sh.dis"); + if(rc >= 0) + localok = 1; + else + err("local file system unusable"); + } + netok := sys->bind("#l", "/net", Sys->MREPL) >= 0; + if(!netok){ + netok = sys->bind("#l1", "/net", Sys->MREPL) >= 0; + if(netok) + ethername = "ether1"; + } + if(netok) + configether(); + dobind("#I", "/net", sys->MAFTER); # IP + dobind("#p", "/prog", sys->MREPL); # prog + sys->bind("#d", "/fd", Sys->MREPL); + dobind("#c", "/dev", sys->MREPL); # console + dobind("#t", "/dev", sys->MAFTER); # serial line + drawok := sys->bind("#i", "/dev", sys->MAFTER) >= 0; # draw + sys->bind("#m", "/dev", sys->MAFTER); # pointer + sys->bind("#e", "/env", sys->MREPL|sys->MCREATE); # environment + sys->bind("#A", "/dev", Sys->MAFTER); # optional audio + timefile: string; + rootsource: string; + cfd := sys->open("/dev/consctl", Sys->OWRITE); + if(cfd != nil) + sys->fprint(cfd, "rawon"); + for(;;){ + (rootsource, timefile) = askrootsource(localok, netok); + if(rootsource == nil) + break; # internal + (rc, nil) := sys->stat(rootsource+"/dis/sh.dis"); + if(rc < 0) + err("%s has no shell"); + else if(sys->bind(rootsource, "/", Sys->MAFTER) < 0) + sys->print("can't bind %s on /: %r\n", rootsource); + else{ + sys->bind(rootsource+"/dis", "/dis", Sys->MBEFORE|Sys->MCREATE); + break; + } + } + cfd = nil; + + setsysname("rpxlite"); # set system name + + now := getclock(timefile, rootsource); + setclock("/dev/time", now); + if(timefile != "#r/rtc") + setclock("#r/rtc", now/big 1000000); + + sys->chdir("/"); + if(netok){ + start("ndb/dns", nil); + start("ndb/cs", nil); + } + startup := "/nvfs/startup"; + if(sys->open(startup, Sys->OREAD) != nil){ + shell := load Command Sh->PATH; + if(shell != nil){ + sys->print("Running %s\n", startup); + shell->init(nil, "sh" :: startup :: nil); + } + } + user := username("inferno"); + (ok, nil) := sys->stat("/dis/wm/wm.dis"); + if(drawok && ok >= 0) + (ok, nil) = sys->stat("/dis/wm/logon.dis"); + if(drawok && ok >= 0 && userok(user)){ + wm := load Command "/dis/wm/wm.dis"; + if(wm != nil){ + fd := sys->open("/nvfs/user", Sys->OWRITE); + if(fd != nil){ + sys->fprint(fd, "%s", user); + fd = nil; + } + spawn wm->init(nil, list of {"wm/wm", "wm/logon", "-l", "-u", user}); + exit; + } + sys->print("init: can't load wm/logon: %r"); + } + sh := load Command Sh->PATH; + if(sh == nil){ + err(sys->sprint("can't load %s: %r", Sh->PATH)); + hang(); + } + spawn sh->init(nil, "sh" :: nil); +} + +start(cmd: string, args: list of string) +{ + disfile := cmd; + if(disfile[0] != '/') + disfile = "/dis/"+disfile+".dis"; + (ok, nil) := sys->stat(disfile); + if(ok >= 0){ + dis := load Command disfile; + if(dis == nil) + sys->print("init: can't load %s: %r\n", disfile); + else + spawn dis->init(nil, cmd :: args); + } +} + +dobind(f, t: string, flags: int) +{ + if(sys->bind(f, t, flags) < 0) + err(sys->sprint("can't bind %s on %s: %r", f, t)); +} + +# +# Set system name from nvram if possible +# +setsysname(def: string) +{ + v := array of byte def; + fd := sys->open("/nvfs/ID", sys->OREAD); + if(fd == nil) + fd = sys->open("/env/sysname", sys->OREAD); + if(fd != nil){ + buf := array[Sys->NAMEMAX] of byte; + nr := sys->read(fd, buf, len buf); + while(nr > 0 && buf[nr-1] == byte '\n') + nr--; + if(nr > 0) + v = buf[0:nr]; + } + fd = sys->open("/dev/sysname", sys->OWRITE); + if(fd != nil) + sys->write(fd, v, len v); +} + +getclock(timefile: string, timedir: string): big +{ + now := big 0; + if(timefile != nil){ + fd := sys->open(timefile, Sys->OREAD); + if(fd != nil){ + b := array[64] of byte; + n := sys->read(fd, b, len b-1); + if(n > 0){ + now = big string b[0:n]; + if(now <= big 16r20000000) + now = big 0; # remote itself is not initialised + } + } + } + if(now == big 0){ + if(timedir != nil){ + (ok, dir) := sys->stat(timedir); + if(ok < 0) { + sys->print("init: stat %s: %r", timedir); + return big 0; + } + now = big dir.atime; + }else{ + now = big 993826747000000; + sys->print("time warped\n"); + } + } + return now; +} + +setclock(timefile: string, now: big) +{ + fd := sys->open(timefile, sys->OWRITE); + if (fd == nil) { + sys->print("init: can't open %s: %r", timefile); + return; + } + + b := sys->aprint("%ubd", now); + if (sys->write(fd, b, len b) != len b) + sys->print("init: can't write to %s: %r", timefile); +} + +srv() +{ + sys->print("remote debug srv..."); + fd := sys->open("/dev/eia0ctl", Sys->OWRITE); + if(fd != nil) + sys->fprint(fd, "b115200"); + + fd = sys->open("/dev/eia0", Sys->ORDWR); + if (fd == nil){ + err(sys->sprint("can't open /dev/eia0: %r")); + return; + } + if (sys->export(fd, "/", Sys->EXPASYNC) < 0){ + err(sys->sprint("can't export on serial port: %r")); + return; + } +} + +err(s: string) +{ + sys->fprint(sys->fildes(2), "init: %s\n", s); +} + +hang() +{ + <-chan of int; +} + +tried := 0; + +askrootsource(localok: int, netok: int): (string, string) +{ + stdin := sys->fildes(0); + sources := "kernel" :: nil; + if(netok) + sources = "remote" :: sources; + if(localok){ + sources = "local" :: sources; + if(netok) + sources = "local+remote" :: sources; + } + for(;;) { + s := ""; + if (tried == 0 && (s = rf("/nvfs/rootsource", nil)) != nil) { + tried = 1; + if (s[len s - 1] == '\n') + s = s[:len s - 1]; + sys->print("/nvfs/rootsource: root from %s\n", s); + } else { + sys->print("root from ("); + cm := ""; + for(l := sources; l != nil; l = tl l){ + sys->print("%s%s", cm, hd l); + cm = ","; + } + sys->print(")[%s] ", hd sources); + + s = getline(stdin, hd sources); # default + } + (nil, choice) := sys->tokenize(s, "\t "); + if(choice == nil) + choice = sources; + opt := hd choice; + case opt { + * => + sys->print("\ninvalid boot option: '%s'\n", opt); + "kernel" => + return (nil, "#r/rtc"); + "local" => + return ("/n/local", "#r/rtc"); + "local+remote" => + if(netfs("/n/remote") >= 0) + return ("/n/local", "/n/remote/dev/time"); + "remote" => + if(netfs("/n/remote") >= 0) + return ("/n/remote", "/n/remote/dev/time"); + } + } +} + +getline(fd: ref Sys->FD, default: string): string +{ + result := ""; + buf := array[10] of byte; + i := 0; + for(;;) { + n := sys->read(fd, buf[i:], len buf - i); + if(n < 1) + break; + i += n; + while(i >0 && (nutf := sys->utfbytes(buf, i)) > 0){ + s := string buf[0:nutf]; + for (j := 0; j < len s; j++) + case s[j] { + '\b' => + if(result != nil) + result = result[0:len result-1]; + 'u'&16r1F => + sys->print("^U\n"); + result = ""; + '\r' => + ; + * => + sys->print("%c", s[j]); + if(s[j] == '\n' || s[j] >= 16r80){ + if(s[j] != '\n') + result[len result] = s[j]; + if(result == nil) + return default; + return result; + } + result[len result] = s[j]; + } + buf[0:] = buf[nutf:i]; + i -= nutf; + } + } + return default; +} + +# +# serve local DOS file system using flash translation layer +# +lfs(): int +{ + if(!flashpart("#F/flash/flashctl", flashparts)) + return -1; + if(!ftlinit("#F/flash/fs")) + return -1; + c := chan of string; + spawn startfs(c, "/dis/dossrv.dis", "dossrv" :: "-f" :: "#X/ftldata" :: "-m" :: "/n/local" :: nil); + if(<-c != nil) + return -1; + return 0; +} + +startfs(c: chan of string, file: string, args: list of string) +{ + fs := load Command file; + if(fs == nil){ + sys->print("can't load %s: %r\n", file); + c <-= "load failed"; + } + { + fs->init(nil, args); + }exception e { + "*" => + c <-= "failed"; + exit; + * => + c <-= "unknown exception"; + exit; + } + c <-= nil; +} + +# +# partition flash +# +flashdone := 0; + +flashpart(ctl: string, parts: array of string): int +{ + if(flashdone) + return 1; + cfd := sys->open(ctl, Sys->ORDWR); + if(cfd == nil){ + sys->print("can't open %s: %r\n", ctl); + return 0; + } + for(i := 0; i < len parts; i++) + if(sys->fprint(cfd, "%s", parts[i]) < 0){ + sys->print("can't %q to %s: %r\n", parts[i], ctl); + return 0; + } + flashdone = 1; + return 1; +} + +# +# set up flash translation layer +# +ftldone := 0; + +ftlinit(flashmem: string): int +{ + if(ftldone) + return 1; + sys->print("Set flash translation of %s...\n", flashmem); + fd := sys->open("#X/ftlctl", Sys->OWRITE); + if(fd == nil){ + sys->print("can't open #X/ftlctl: %r\n"); + return 0; + } + if(sys->fprint(fd, "init %s", flashmem) <= 0){ + sys->print("can't init flash translation: %r\n"); + return 0; + } + ftldone = 1; + return 1; +} + +configether() +{ + if(ethername == nil) + return; + fd := sys->open("/nvfs/etherparams", Sys->OREAD); + if(fd == nil) + return; + ctl := sys->open("/net/"+ethername+"/clone", Sys->OWRITE); + if(ctl == nil){ + sys->print("init: can't open %s's clone: %r\n", ethername); + return; + } + b := array[1024] of byte; + n := sys->read(fd, b, len b); + if(n <= 0) + return; + for(i := 0; i < n;){ + for(e := i; e < n && b[e] != byte '\n'; e++) + ; + s := string b[i:e]; + if(sys->fprint(ctl, "%s", s) < 0) + sys->print("init: ctl write to %s: %s: %r\n", ethername, s); + i = e+1; + } +} + +donebind := 0; + +# +# set up network mount +# +netfs(mountpt: string): int +{ + sys->print("bootp ..."); + + fd: ref Sys->FD; + if(!donebind){ + fd = sys->open("/net/ipifc/clone", sys->OWRITE); + if(fd == nil) { + sys->print("init: open /net/ipifc/clone: %r\n"); + return -1; + } + if(sys->fprint(fd, "bind ether %s", ethername) < 0) { + sys->print("could not bind ether0 interface: %r\n"); + return -1; + } + donebind = 1; + }else{ + fd = sys->open("/net/ipifc/0/ctl", Sys->OWRITE); + if(fd == nil){ + sys->print("init: can't reopen /net/ipifc/0/ctl: %r\n"); + return -1; + } + } + if ((ip := rf("/nvfs/ip", nil)) != nil) { + sys->print("**using %s\n", ip); + sys->fprint(fd, "bind ether /net/ether0"); + sys->fprint(fd, "add %s ", ip); + } else { + { + if(sys->fprint(fd, "bootp") < 0) + sys->print("could not bootp: %r\n"); + } exception e { + "*" => + sys->print("could not bootp: %s\n", e); + } + } + server := rf("/nvfs/fsip", nil); + if (server != nil) { + if (server[len server - 1] == '\n') + server = server[:len server - 1]; + sys->print("/nvfs/fsip: server=%s\n", server); + } else + server = bootp(); + if(server == nil || server == "0.0.0.0") + return -1; + + net := "tcp"; # how to specify il? + svcname := net + "!" + server + "!6666"; + + sys->print("dial %s...", svcname); + + (ok, c) := sys->dial(svcname, nil); + if(ok < 0){ + sys->print("can't dial %s: %r\n", svcname); + return -1; + } + + sys->print("\nConnected ...\n"); + if(kr != nil){ + err: string; + sys->print("Authenticate ..."); + ai := kr->readauthinfo("/nvfs/default"); + if(ai == nil){ + sys->print("readauthinfo /nvfs/default failed: %r\n"); + sys->print("trying mount as `nobody'\n"); + } + (c.dfd, err) = auth->client("none", ai, c.dfd); + if(c.dfd == nil){ + sys->print("authentication failed: %s\n", err); + return -1; + } + } + + sys->print("mount %s...", mountpt); + + c.cfd = nil; + n := sys->mount(c.dfd, nil, mountpt, sys->MREPL, ""); + if(n > 0) + return 0; + if(n < 0) + sys->print("%r"); + return -1; +} + +bootp(): string +{ + fd := sys->open("/net/bootp", sys->OREAD); + if(fd == nil) { + sys->print("init: can't open /net/bootp: %r"); + return nil; + } + + buf := array[Bootpreadlen] of byte; + nr := sys->read(fd, buf, len buf); + fd = nil; + if(nr <= 0) { + sys->print("init: read /net/bootp: %r"); + return nil; + } + + (ntok, ls) := sys->tokenize(string buf, " \t\n"); + while(ls != nil) { + if(hd ls == "fsip"){ + ls = tl ls; + break; + } + ls = tl ls; + } + if(ls == nil) { + sys->print("init: server address not in bootp read"); + return nil; + } + + srv := hd ls; + + sys->print("%s\n", srv); + + return srv; +} + +username(def: string): string +{ + return rf("/nvfs/user", def); +} + +userok(user: string): int +{ + (ok, d) := sys->stat("/usr/"+user); + return ok >= 0 && (d.mode & Sys->DMDIR) != 0; +} + +rf(file: string, default: string): string +{ + fd := sys->open(file, Sys->OREAD); + if(fd != nil){ + buf := array[128] of byte; + nr := sys->read(fd, buf, len buf); + if(nr > 0) + return string buf[0:nr]; + } + return default; +} diff --git a/os/init/shell.b b/os/init/shell.b new file mode 100644 index 00000000..e0b19ec9 --- /dev/null +++ b/os/init/shell.b @@ -0,0 +1,97 @@ +implement InitShell; + +include "sys.m"; +include "draw.m"; + +sys: Sys; + +InitShell: module +{ + init: fn(nil: ref Draw->Context, nil: list of string); +}; + +Sh: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +init(nil: ref Draw->Context, nil: list of string) +{ + shell := load Sh "/dis/sh.dis"; + + sys = load Sys Sys->PATH; + + if(sys != nil) + sys->print("init: starting shell\n"); + +# sys->bind("#I", "/net", sys->MAFTER); # IP + sys->bind("#p", "/prog", sys->MREPL); # prog device + sys->bind("#d", "/fd", Sys->MREPL); + sys->bind("#i", "/dev", sys->MREPL); # draw device + sys->bind("#t", "/dev", sys->MAFTER); # serial line + sys->bind("#c", "/dev", sys->MAFTER); # console device + sys->bind("#W","/dev",sys->MAFTER); # Flash +# sys->bind("#O", "/dev", sys->MAFTER); # Modem +# sys->bind("#T","/dev",sys->MAFTER); # Touchscreen + + srv(); + + spawn shell->init(nil, nil); +} + +srv() +{ + remotedebug := sysenv("remotedebug"); + if(remotedebug != "1") + return; + + sys->print("srv..."); + if(echoto("#t/eia0ctl", "b38400") < 0) + return; + + fd := sys->open("/dev/eia0", Sys->ORDWR); + if (fd == nil) { + sys->print("eia data open: %r\n"); + return; + } + if (sys->export(fd, "/", Sys->EXPASYNC) < 0) { + sys->print("export: %r\n"); + return; + } + sys->print("ok\n"); +} + +sysenv(param: string): string +{ + fd := sys->open("#c/sysenv", sys->OREAD); + if (fd == nil) + return(nil); + buf := array[4096] of byte; + nb := sys->read(fd, buf, len buf); + (nfl,fl) := sys->tokenize(string buf, "\n"); + while (fl != nil) { + pair := hd fl; + (npl, pl) := sys->tokenize(pair, "="); + if (npl > 1) { + if ((hd pl) == param) + return hd tl pl; + } + fl = tl fl; + } + return nil ; +} + +echoto(fname, str: string): int +{ + fd := sys->open(fname, Sys->OWRITE); + if(fd == nil) { + sys->print("%s: %r\n", fname); + return -1; + } + x := array of byte str; + if(sys->write(fd, x, len x) == -1) { + sys->print("write: %r\n"); + return -1; + } + return 0; +} diff --git a/os/init/soeinit.b b/os/init/soeinit.b new file mode 100644 index 00000000..cae7f5bd --- /dev/null +++ b/os/init/soeinit.b @@ -0,0 +1,613 @@ +# +# Soekris 4501 +# + +implement Init; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + +include "keyring.m"; + kr: Keyring; + +include "security.m"; + auth: Auth; + +include "sh.m"; + +Init: module +{ + init: fn(); +}; + +Bootpreadlen: con 128; + +# standard flash partitions + +#flashparts := array[] of { +# # bootstrap at 0x0 to 0x20000 +# "add script 0x20000 0x40000", +# "add kernel 0x100000 0x200000", +# "add fs 0x200000 end", +#}; +flashparts: array of string; + +ethername := "ether0"; + +# +# initialise flash translation +# mount flash file system +# add devices +# start a shell or window manager +# + +init() +{ + sys = load Sys Sys->PATH; + kr = load Keyring Keyring->PATH; + auth = load Auth Auth->PATH; + if(auth != nil) + auth->init(); + + sys->bind("/", "/", Sys->MREPL); + + localok := 0; + if(lfs() >= 0){ + # let's just take a closer look + sys->bind("/n/local/nvfs", "/nvfs", Sys->MREPL|Sys->MCREATE); + (rc, nil) := sys->stat("/n/local/dis/sh.dis"); + if(rc >= 0) + localok = 1; + else + err("local file system unusable"); + } + netok := sys->bind("#l", "/net", Sys->MREPL) >= 0; + if(!netok){ + netok = sys->bind("#l1", "/net", Sys->MREPL) >= 0; + if(netok) + ethername = "ether1"; + } + if(netok) + configether(); + dobind("#I", "/net", sys->MAFTER); # IP + dobind("#p", "/prog", sys->MREPL); # prog + sys->bind("#d", "/fd", Sys->MREPL); + dobind("#c", "/dev", sys->MREPL); # console + dobind("#t", "/dev", sys->MAFTER); # serial line + drawok := sys->bind("#i", "/dev", sys->MAFTER) >= 0; # draw + sys->bind("#m", "/dev", sys->MAFTER); # pointer + sys->bind("#e", "/env", sys->MREPL|sys->MCREATE); # environment + sys->bind("#A", "/dev", Sys->MAFTER); # optional audio + timefile: string; + rootsource: string; + scale := 1; + cfd := sys->open("/dev/consctl", Sys->OWRITE); + if(cfd != nil) + sys->fprint(cfd, "rawon"); + for(;;){ + (rootsource, timefile, scale) = askrootsource(localok, netok); + if(rootsource == nil) + break; # internal + (rc, nil) := sys->stat(rootsource+"/dis/sh.dis"); + if(rc < 0) + err("%s has no shell"); + else if(sys->bind(rootsource, "/", Sys->MAFTER) < 0) + sys->print("can't bind %s on /: %r\n", rootsource); + else{ + sys->bind(rootsource+"/dis", "/dis", Sys->MBEFORE|Sys->MCREATE); + break; + } + } + cfd = nil; + + setsysname("soe"); # set system name + + now := getclock(timefile, rootsource); + if(scale == 1) + now *= big 1000000; + setclock("/dev/time", now); + if(timefile != "#r/rtc") + setclock("#r/rtc", now/big 1000000); + + sys->chdir("/"); + if(netok){ + start("ndb/dns", nil); + start("ndb/cs", nil); + } + startup := "/nvfs/startup"; + if(sys->open(startup, Sys->OREAD) != nil){ + shell := load Command Sh->PATH; + if(shell != nil){ + sys->print("Running %s\n", startup); + shell->init(nil, "sh" :: startup :: nil); + } + } + user := username("inferno"); + (ok, nil) := sys->stat("/dis/wm/wm.dis"); + if(drawok && ok >= 0) + (ok, nil) = sys->stat("/dis/wm/logon.dis"); + if(drawok && ok >= 0 && userok(user)){ + wm := load Command "/dis/wm/wm.dis"; + if(wm != nil){ + fd := sys->open("/nvfs/user", Sys->OWRITE); + if(fd != nil){ + sys->fprint(fd, "%s", user); + fd = nil; + } + spawn wm->init(nil, list of {"wm/wm", "wm/logon", "-l", "-u", user}); + exit; + } + sys->print("init: can't load wm/logon: %r"); + } + sh := load Command Sh->PATH; + if(sh == nil){ + err(sys->sprint("can't load %s: %r", Sh->PATH)); + hang(); + } + spawn sh->init(nil, "sh" :: nil); +} + +start(cmd: string, args: list of string) +{ + disfile := cmd; + if(disfile[0] != '/') + disfile = "/dis/"+disfile+".dis"; + (ok, nil) := sys->stat(disfile); + if(ok >= 0){ + dis := load Command disfile; + if(dis == nil) + sys->print("init: can't load %s: %r\n", disfile); + else + spawn dis->init(nil, cmd :: args); + } +} + +dobind(f, t: string, flags: int) +{ + if(sys->bind(f, t, flags) < 0) + err(sys->sprint("can't bind %s on %s: %r", f, t)); +} + +# +# Set system name from nvram if possible +# +setsysname(def: string) +{ + v := array of byte def; + fd := sys->open("/nvfs/ID", sys->OREAD); + if(fd == nil) + fd = sys->open("/env/sysname", sys->OREAD); + if(fd != nil){ + buf := array[Sys->NAMEMAX] of byte; + nr := sys->read(fd, buf, len buf); + while(nr > 0 && buf[nr-1] == byte '\n') + nr--; + if(nr > 0) + v = buf[0:nr]; + } + fd = sys->open("/dev/sysname", sys->OWRITE); + if(fd != nil) + sys->write(fd, v, len v); +} + +getclock(timefile: string, timedir: string): big +{ + now := big 0; + if(timefile != nil){ + fd := sys->open(timefile, Sys->OREAD); + if(fd != nil){ + b := array[64] of byte; + n := sys->read(fd, b, len b-1); + if(n > 0){ + now = big string b[0:n]; + if(now <= big 16r20000000) + now = big 0; # remote itself is not initialised + } + } + } + if(now == big 0){ + if(timedir != nil){ + (ok, dir) := sys->stat(timedir); + if(ok < 0) { + sys->print("init: stat %s: %r", timedir); + return big 0; + } + now = big dir.atime; + }else{ + now = big 993826747000000; + sys->print("time warped\n"); + } + } + return now; +} + +setclock(timefile: string, now: big) +{ + fd := sys->open(timefile, sys->OWRITE); + if (fd == nil) { + sys->print("init: can't open %s: %r", timefile); + return; + } + + b := sys->aprint("%ubd", now); + if (sys->write(fd, b, len b) != len b) + sys->print("init: can't write to %s: %r", timefile); +} + +srv() +{ + sys->print("remote debug srv..."); + fd := sys->open("/dev/eia0ctl", Sys->OWRITE); + if(fd != nil) + sys->fprint(fd, "b115200"); + + fd = sys->open("/dev/eia0", Sys->ORDWR); + if (fd == nil){ + err(sys->sprint("can't open /dev/eia0: %r")); + return; + } + if (sys->export(fd, "/", Sys->EXPASYNC) < 0){ + err(sys->sprint("can't export on serial port: %r")); + return; + } +} + +err(s: string) +{ + sys->fprint(sys->fildes(2), "init: %s\n", s); +} + +hang() +{ + <-chan of int; +} + +tried := 0; + +askrootsource(localok: int, netok: int): (string, string, int) +{ + stdin := sys->fildes(0); + sources := "kernel" :: nil; + if(netok) + sources = "remote" :: sources; + if(localok){ + sources = "local" :: sources; + if(netok) + sources = "local+remote" :: sources; + } + for(;;) { + s := ""; + if (tried == 0 && (s = rf("/nvfs/rootsource", nil)) != nil) { + tried = 1; + if (s[len s - 1] == '\n') + s = s[:len s - 1]; + sys->print("/nvfs/rootsource: root from %s\n", s); + } else { + sys->print("root from ("); + cm := ""; + for(l := sources; l != nil; l = tl l){ + sys->print("%s%s", cm, hd l); + cm = ","; + } + sys->print(")[%s] ", hd sources); + + s = getline(stdin, hd sources); # default + } + (nil, choice) := sys->tokenize(s, "\t "); + if(choice == nil) + choice = sources; + opt := hd choice; + case opt { + * => + sys->print("\ninvalid boot option: '%s'\n", opt); + "kernel" => + return (nil, "#r/rtc", 1); + "local" => + return ("/n/local", "#r/rtc", 1); + "local+remote" => + if(netfs("/n/remote") >= 0) + return ("/n/local", "/n/remote/dev/time", 1000000); + "remote" => + if(netfs("/n/remote") >= 0) + return ("/n/remote", "/n/remote/dev/time", 1000000); + } + } +} + +getline(fd: ref Sys->FD, default: string): string +{ + result := ""; + buf := array[10] of byte; + i := 0; + for(;;) { + n := sys->read(fd, buf[i:], len buf - i); + if(n < 1) + break; + i += n; + while(i >0 && (nutf := sys->utfbytes(buf, i)) > 0){ + s := string buf[0:nutf]; + for (j := 0; j < len s; j++) + case s[j] { + '\b' => + if(result != nil) + result = result[0:len result-1]; + 'u'&16r1F => + sys->print("^U\n"); + result = ""; + '\r' => + ; + * => + sys->print("%c", s[j]); + if(s[j] == '\n' || s[j] >= 16r80){ + if(s[j] != '\n') + result[len result] = s[j]; + if(result == nil) + return default; + return result; + } + result[len result] = s[j]; + } + buf[0:] = buf[nutf:i]; + i -= nutf; + } + } + return default; +} + +# +# serve local DOS file system using flash translation layer +# +lfs(): int +{ + if(!flashpart("#F/flash/flashctl", flashparts)) + return -1; + if(!ftlinit("#F/flash/fs")) + return -1; + c := chan of string; + spawn startfs(c, "/dis/dossrv.dis", "dossrv" :: "-f" :: "#X/ftldata" :: "-m" :: "/n/local" :: nil); + if(<-c != nil) + return -1; + return 0; +} + +startfs(c: chan of string, file: string, args: list of string) +{ + fs := load Command file; + if(fs == nil){ + sys->print("can't load %s: %r\n", file); + c <-= "load failed"; + } + { + fs->init(nil, args); + }exception e { + "*" => + c <-= "failed"; + exit; + * => + c <-= "unknown exception"; + exit; + } + c <-= nil; +} + +# +# partition flash +# +flashdone := 0; + +flashpart(ctl: string, parts: array of string): int +{ + if(flashdone) + return 1; + cfd := sys->open(ctl, Sys->ORDWR); + if(cfd == nil){ + sys->print("can't open %s: %r\n", ctl); + return 0; + } + for(i := 0; i < len parts; i++) + if(sys->fprint(cfd, "%s", parts[i]) < 0){ + sys->print("can't %q to %s: %r\n", parts[i], ctl); + return 0; + } + flashdone = 1; + return 1; +} + +# +# set up flash translation layer +# +ftldone := 0; + +ftlinit(flashmem: string): int +{ + if(ftldone) + return 1; + sys->print("Set flash translation of %s...\n", flashmem); + fd := sys->open("#X/ftlctl", Sys->OWRITE); + if(fd == nil){ + sys->print("can't open #X/ftlctl: %r\n"); + return 0; + } + if(sys->fprint(fd, "init %s", flashmem) <= 0){ + sys->print("can't init flash translation: %r\n"); + return 0; + } + ftldone = 1; + return 1; +} + +configether() +{ + if(ethername == nil) + return; + fd := sys->open("/nvfs/etherparams", Sys->OREAD); + if(fd == nil) + return; + ctl := sys->open("/net/"+ethername+"/clone", Sys->OWRITE); + if(ctl == nil){ + sys->print("init: can't open %s's clone: %r\n", ethername); + return; + } + b := array[1024] of byte; + n := sys->read(fd, b, len b); + if(n <= 0) + return; + for(i := 0; i < n;){ + for(e := i; e < n && b[e] != byte '\n'; e++) + ; + s := string b[i:e]; + if(sys->fprint(ctl, "%s", s) < 0) + sys->print("init: ctl write to %s: %s: %r\n", ethername, s); + i = e+1; + } +} + +donebind := 0; + +# +# set up network mount +# +netfs(mountpt: string): int +{ + sys->print("bootp ..."); + + fd: ref Sys->FD; + if(!donebind){ + fd = sys->open("/net/ipifc/clone", sys->OWRITE); + if(fd == nil) { + sys->print("init: open /net/ipifc/clone: %r\n"); + return -1; + } + if(sys->fprint(fd, "bind ether %s", ethername) < 0) { + sys->print("could not bind ether0 interface: %r\n"); + return -1; + } + donebind = 1; + }else{ + fd = sys->open("/net/ipifc/0/ctl", Sys->OWRITE); + if(fd == nil){ + sys->print("init: can't reopen /net/ipifc/0/ctl: %r\n"); + return -1; + } + } + if ((ip := rf("/nvfs/ip", nil)) != nil) { + sys->print("**using %s\n", ip); + sys->fprint(fd, "bind ether /net/ether0"); + sys->fprint(fd, "add %s ", ip); + } else { + { + if(sys->fprint(fd, "bootp") < 0) + sys->print("could not bootp: %r\n"); + } exception e { + "*" => + sys->print("could not bootp: %s\n", e); + } + } + server := rf("/nvfs/fsip", nil); + if (server != nil) { + if (server[len server - 1] == '\n') + server = server[:len server - 1]; + sys->print("/nvfs/fsip: server=%s\n", server); + } else + server = bootp(); + if(server == nil || server == "0.0.0.0") + return -1; + + net := "tcp"; # how to specify il? + svcname := net + "!" + server + "!6666"; + + sys->print("dial %s...", svcname); + + (ok, c) := sys->dial(svcname, nil); + if(ok < 0){ + sys->print("can't dial %s: %r\n", svcname); + return -1; + } + + sys->print("\nConnected ...\n"); + if(kr != nil){ + err: string; + sys->print("Authenticate ..."); + ai := kr->readauthinfo("/nvfs/default"); + if(ai == nil){ + sys->print("readauthinfo /nvfs/default failed: %r\n"); + sys->print("trying mount as `nobody'\n"); + } + (c.dfd, err) = auth->client("none", ai, c.dfd); + if(c.dfd == nil){ + sys->print("authentication failed: %s\n", err); + return -1; + } + } + + sys->print("mount %s...", mountpt); + + c.cfd = nil; + n := sys->mount(c.dfd, nil, mountpt, sys->MREPL, ""); + if(n > 0) + return 0; + if(n < 0) + sys->print("%r"); + return -1; +} + +bootp(): string +{ + fd := sys->open("/net/bootp", sys->OREAD); + if(fd == nil) { + sys->print("init: can't open /net/bootp: %r"); + return nil; + } + + buf := array[Bootpreadlen] of byte; + nr := sys->read(fd, buf, len buf); + fd = nil; + if(nr <= 0) { + sys->print("init: read /net/bootp: %r"); + return nil; + } + + (ntok, ls) := sys->tokenize(string buf, " \t\n"); + while(ls != nil) { + if(hd ls == "fsip"){ + ls = tl ls; + break; + } + ls = tl ls; + } + if(ls == nil) { + sys->print("init: server address not in bootp read"); + return nil; + } + + srv := hd ls; + + sys->print("%s\n", srv); + + return srv; +} + +username(def: string): string +{ + return rf("/nvfs/user", def); +} + +userok(user: string): int +{ + (ok, d) := sys->stat("/usr/"+user); + return ok >= 0 && (d.mode & Sys->DMDIR) != 0; +} + +rf(file: string, default: string): string +{ + fd := sys->open(file, Sys->OREAD); + if(fd != nil){ + buf := array[128] of byte; + nr := sys->read(fd, buf, len buf); + if(nr > 0) + return string buf[0:nr]; + } + return default; +} diff --git a/os/init/srvinit.b b/os/init/srvinit.b new file mode 100644 index 00000000..2c1c1b69 --- /dev/null +++ b/os/init/srvinit.b @@ -0,0 +1,209 @@ +implement Init; +# +# init program for standalone wm using TK +# +include "sys.m"; +sys: Sys; + FD, Connection, sprint, Dir: import sys; + print, fprint, open, bind, mount, dial, sleep, read: import sys; + +include "security.m"; + auth: Auth; + +include "draw.m"; + draw: Draw; + Context: import draw; + +include "keyring.m"; + kr: Keyring; + +Init: module +{ + init: fn(); +}; + +Command: module +{ + init: fn(ctxt: ref Context, argv: list of string); +}; + +rootfs(server: string): int +{ + ok, n: int; + c: Connection; + + (ok, c) = dial("tcp!" + server + "!6666", nil); + if(ok < 0) { + sys->print("cannot connected to %s: %r\n", server); + return -1; + } + + sys->print("Connected ..."); + if(kr != nil && auth != nil){ + err: string; + sys->print("Authenticate ..."); + ai := kr->readauthinfo("/nvfs/default"); + if(ai == nil){ + sys->print("readauthinfo /nvfs/default failed: %r\n"); + sys->print("trying mount as `nobody'\n"); + } + (c.dfd, err) = auth->client("none", ai, c.dfd); + if(c.dfd == nil){ + sys->print("authentication failed: %s\n", err); + return -1; + } + } + + sys->print("mount ..."); + + c.cfd = nil; + n = mount(c.dfd, "/", sys->MREPL, ""); + if(n > 0) + return 0; + return -1; +} + +Bootpreadlen: con 128; + +init() +{ + spec: string; + + sys = load Sys Sys->PATH; + kr = load Keyring Keyring->PATH; + auth = load Auth Auth->PATH; + if(auth != nil) + auth->init(); + + sys->print("**\n** Inferno\n** Vita Nuova\n**\n"); + + sys->print("Setup boot net services ...\n"); + + # + # Setup what we need to call a server and + # Authenticate + # + bind("#l", "/net", sys->MREPL); + bind("#I", "/net", sys->MAFTER); + bind("#c", "/dev", sys->MAFTER); + bind("#H", "/dev", sys->MAFTER); + nvramfd := sys->open("#H/hd0nvram", sys->ORDWR); + if(nvramfd != nil){ + spec = "#Fhd0nvram"; + if(bind(spec, "/nvfs", sys->MAFTER) < 0) + print("init: bind %s: %r\n", spec); + } + + setsysname(); + + sys->print("bootp..."); + + fd := open("/net/ipifc/clone", sys->OWRITE); + if(fd == nil) { + print("init: open /net/ipifc/clone: %r\n"); + exit; + } + cfg := array of byte "bind ether ether0"; + if(sys->write(fd, cfg, len cfg) != len cfg) { + sys->print("could not bind interface: %r\n"); + exit; + } + cfg = array of byte "bootp"; + if(sys->write(fd, cfg, len cfg) != len cfg) { + sys->print("could not bootp: %r\n"); + exit; + } + + fd = open("/net/bootp", sys->OREAD); + if(fd == nil) { + print("init: open /net/bootp: %r"); + exit; + } + + buf := array[Bootpreadlen] of byte; + nr := read(fd, buf, len buf); + fd = nil; + if(nr <= 0) { + print("init: read /net/bootp: %r"); + exit; + } + + (ntok, ls) := sys->tokenize(string buf, " \t\n"); + while(ls != nil) { + if(hd ls == "fsip"){ + ls = tl ls; + break; + } + ls = tl ls; + } + if(ls == nil) { + print("init: server address not in bootp read"); + exit; + } + + srv := hd ls; + sys->print("server %s\nConnect ...\n", srv); + + retrycount := 0; + while(rootfs(srv) < 0 && retrycount++ < 5) + sleep(1000); + + sys->print("done\n"); + + # + # default namespace + # + bind("#c", "/dev", sys->MREPL); # console + bind("#l", "/net", sys->MREPL); # ethernet + bind("#I", "/net", sys->MBEFORE); # TCP/IP + bind("#p", "/prog", sys->MREPL); # prog device + sys->bind("#d", "/fd", Sys->MREPL); + + sys->print("clock...\n"); + setclock(); + + sys->print("sh...\n"); + sh := load Command "/dis/sh.dis"; + if (sh == nil) + print("cannot load /dis/sh.dis: %r\n"); + + sh->init(nil, "sh" :: "-i" :: nil); +} + +setclock() +{ + (ok, dir) := sys->stat("/"); + if (ok < 0) { + print("init: stat /: %r"); + return; + } + + fd := sys->open("/dev/time", sys->OWRITE); + if (fd == nil) { + print("init: open /dev/time: %r"); + return; + } + + # Time is kept as microsecs, atime is in secs + b := array of byte sprint("%d000000", dir.atime); + if (sys->write(fd, b, len b) != len b) + print("init: write /dev/time: %r"); +} + +# +# Set system name from nvram +# +setsysname() +{ + fd := open("/nvfs/ID", sys->OREAD); + if(fd == nil) + return; + fds := open("/dev/sysname", sys->OWRITE); + if(fds == nil) + return; + buf := array[128] of byte; + nr := sys->read(fd, buf, len buf); + if(nr <= 0) + return; + sys->write(fds, buf, nr); +} diff --git a/os/init/wminit.b b/os/init/wminit.b new file mode 100644 index 00000000..89312ec2 --- /dev/null +++ b/os/init/wminit.b @@ -0,0 +1,231 @@ +implement Init; +# +# init program for standalone wm using TK +# +include "sys.m"; +sys: Sys; + FD, Connection, sprint, Dir: import sys; + print, fprint, open, bind, mount, dial, sleep, read: import sys; + +include "security.m"; + auth: Auth; + +include "draw.m"; + draw: Draw; + Context: import draw; + +include "keyring.m"; + kr: Keyring; + +Init: module +{ + init: fn(); +}; + +Command: module +{ + init: fn(ctxt: ref Context, argv: list of string); +}; + +rootfs(server: string): int +{ + ok, n: int; + c: Connection; + + (ok, c) = dial("tcp!" + server + "!6666", nil); + if(ok < 0) + return -1; + + sys->print("Connected ..."); + if(kr != nil && auth != nil){ + err: string; + sys->print("Authenticate ..."); + ai := kr->readauthinfo("/nvfs/default"); + if(ai == nil){ + sys->print("readauthinfo /nvfs/default failed: %r\n"); + sys->print("trying mount as `nobody'\n"); + } + (c.dfd, err) = auth->client("none", ai, c.dfd); + if(c.dfd == nil){ + sys->print("authentication failed: %s\n", err); + return -1; + } + } + + sys->print("mount ..."); + + c.cfd = nil; + n = mount(c.dfd, nil, "/", sys->MREPL, ""); + if(n > 0) + return 0; + return -1; +} + +Bootpreadlen: con 128; + +init() +{ + spec: string; + + sys = load Sys Sys->PATH; + kr = load Keyring Keyring->PATH; + auth = load Auth Auth->PATH; + if(auth != nil) + auth->init(); + + sys->print("**\n** Inferno\n** Vita Nuova\n**\n"); + + sys->print("Setup boot net services ...\n"); + + # + # Setup what we need to call a server and + # Authenticate + # + bind("#l", "/net", sys->MREPL); + bind("#I", "/net", sys->MAFTER); + bind("#c", "/dev", sys->MAFTER); + bind("#H", "/dev", sys->MAFTER); + nvramfd := sys->open("#H/hd0nvram", sys->ORDWR); + if(nvramfd != nil){ + spec = "#Fhd0nvram"; + if(bind(spec, "/nvfs", sys->MAFTER) < 0) + print("init: bind %s: %r\n", spec); + } + + setsysname(); + + sys->print("bootp..."); + + fd := open("/net/ipifc/clone", sys->OWRITE); + if(fd == nil) { + print("init: open /net/ipifc/clone: %r\n"); + exit; + } + cfg := array of byte "bind ether ether0"; + if(sys->write(fd, cfg, len cfg) != len cfg) { + sys->print("could not bind interface: %r\n"); + exit; + } + cfg = array of byte "bootp"; + if(sys->write(fd, cfg, len cfg) != len cfg) { + sys->print("could not bootp: %r\n"); + exit; + } + + fd = open("/net/bootp", sys->OREAD); + if(fd == nil) { + print("init: open /net/bootp: %r"); + exit; + } + + buf := array[Bootpreadlen] of byte; + nr := read(fd, buf, len buf); + fd = nil; + if(nr <= 0) { + print("init: read /net/bootp: %r"); + exit; + } + + (ntok, ls) := sys->tokenize(string buf, " \t\n"); + while(ls != nil) { + if(hd ls == "fsip"){ + ls = tl ls; + break; + } + ls = tl ls; + } + if(ls == nil) { + print("init: server address not in bootp read"); + exit; + } + + srv := hd ls; + sys->print("server %s\nConnect ...\n", srv); + + retrycount := 0; + while(rootfs(srv) < 0 && retrycount++ < 5) + sleep(1000); + + cfd := sys->open("/dev/cons", Sys->OWRITE); + if (cfd != nil) { + sys->dup(cfd.fd, 1); + sys->dup(cfd.fd, 2); + } + cfd = nil; + + sys->print("done\n"); + + # + # default namespace + # + bind("#c", "/dev", sys->MREPL); # console + bind("#t", "/dev", sys->MAFTER); # serial port + bind("#H", "/dev", sys->MAFTER); + if(spec != nil) + bind(spec, "/nvfs", sys->MBEFORE|sys->MCREATE); # our keys + bind("#E", "/dev", sys->MBEFORE); # mpeg + bind("#l", "/net", sys->MBEFORE); # ethernet + bind("#I", "/net", sys->MBEFORE); # TCP/IP + bind("#V", "/dev", sys->MAFTER); # hauppauge TV + bind("#p", "/prog", sys->MREPL); # prog device + sys->bind("#d", "/fd", Sys->MREPL); + if(bind("#m", "/dev", sys->MAFTER) >= 0){ # mouse setup device + mouse := load Command "/dis/mouse.dis"; + if (mouse != nil) { + print("Setting up mouse\n"); + mouse->init(nil, "/dis/mouse.dis" :: nil); + mouse = nil; + } + } + + sys->print("clock...\n"); + setclock(); + + sys->print("logon...\n"); + + logon := load Command "/dis/wm/logon.dis"; + if(logon == nil) { + print("init: load /dis/wm/logon.dis: %r"); + logon = load Command "/dis/sh.dis"; + } + dc: ref Context; + spawn logon->init(dc, nil); +} + +setclock() +{ + (ok, dir) := sys->stat("/"); + if (ok < 0) { + print("init: stat /: %r"); + return; + } + + fd := sys->open("/dev/time", sys->OWRITE); + if (fd == nil) { + print("init: open /dev/time: %r"); + return; + } + + # Time is kept as microsecs, atime is in secs + b := array of byte sprint("%d000000", dir.atime); + if (sys->write(fd, b, len b) != len b) + print("init: write /dev/time: %r"); +} + +# +# Set system name from nvram +# +setsysname() +{ + fd := open("/nvfs/ID", sys->OREAD); + if(fd == nil) + return; + fds := open("/dev/sysname", sys->OWRITE); + if(fds == nil) + return; + buf := array[128] of byte; + nr := sys->read(fd, buf, len buf); + if(nr <= 0) + return; + sys->write(fds, buf, nr); +} diff --git a/os/ip/arp.c b/os/ip/arp.c new file mode 100644 index 00000000..11f4fb1e --- /dev/null +++ b/os/ip/arp.c @@ -0,0 +1,681 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" +#include "ipv6.h" + +/* + * address resolution tables + */ + +enum +{ + NHASH = (1<<6), + NCACHE = 256, + + AOK = 1, + AWAIT = 2, +}; + +char *arpstate[] = +{ + "UNUSED", + "OK", + "WAIT", +}; + +/* + * one per Fs + */ +struct Arp +{ + QLock; + Fs *f; + Arpent *hash[NHASH]; + Arpent cache[NCACHE]; + Arpent *rxmt; + Proc *rxmitp; /* neib sol re-transmit proc */ + Rendez rxmtq; + Block *dropf, *dropl; +}; + +char *Ebadarp = "bad arp"; + +#define haship(s) ((s)[IPaddrlen-1]%NHASH) + +extern int ReTransTimer = RETRANS_TIMER; +static void rxmitproc(void *v); + +void +arpinit(Fs *f) +{ + f->arp = smalloc(sizeof(Arp)); + f->arp->f = f; + f->arp->rxmt = nil; + f->arp->dropf = f->arp->dropl = nil; + kproc("rxmitproc", rxmitproc, f->arp, 0); +} + +/* + * create a new arp entry for an ip address. + */ +static Arpent* +newarp6(Arp *arp, uchar *ip, Ipifc *ifc, int addrxt) +{ + uint t; + Block *next, *xp; + Arpent *a, *e, *f, **l; + Medium *m = ifc->m; + int empty; + + /* find oldest entry */ + e = &arp->cache[NCACHE]; + a = arp->cache; + t = a->utime; + for(f = a; f < e; f++){ + if(f->utime < t){ + t = f->utime; + a = f; + } + } + + /* dump waiting packets */ + xp = a->hold; + a->hold = nil; + + if(isv4(a->ip)){ + while(xp){ + next = xp->list; + freeblist(xp); + xp = next; + } + } + else { // queue icmp unreachable for rxmitproc later on, w/o arp lock + if(xp){ + if(arp->dropl == nil) + arp->dropf = xp; + else + arp->dropl->list = xp; + + for(next = xp->list; next; next = next->list) + xp = next; + arp->dropl = xp; + wakeup(&arp->rxmtq); + } + } + + /* take out of current chain */ + l = &arp->hash[haship(a->ip)]; + for(f = *l; f; f = f->hash){ + if(f == a){ + *l = a->hash; + break; + } + l = &f->hash; + } + + /* insert into new chain */ + l = &arp->hash[haship(ip)]; + a->hash = *l; + *l = a; + + memmove(a->ip, ip, sizeof(a->ip)); + a->utime = NOW; + a->ctime = 0; + a->type = m; + + a->rtime = NOW + ReTransTimer; + a->rxtsrem = MAX_MULTICAST_SOLICIT; + a->ifc = ifc; + a->ifcid = ifc->ifcid; + + /* put to the end of re-transmit chain; addrxt is 0 when isv4(a->ip) */ + if(!ipismulticast(a->ip) && addrxt){ + l = &arp->rxmt; + empty = (*l==nil); + + for(f = *l; f; f = f->nextrxt){ + if(f == a){ + *l = a->nextrxt; + break; + } + l = &f->nextrxt; + } + for(f = *l; f; f = f->nextrxt){ + l = &f->nextrxt; + } + *l = a; + if(empty) + wakeup(&arp->rxmtq); + } + + a->nextrxt = nil; + + return a; +} + +/* called with arp qlocked */ + +void +cleanarpent(Arp *arp, Arpent *a) +{ + Arpent *f, **l; + + a->utime = 0; + a->ctime = 0; + a->type = 0; + a->state = 0; + + /* take out of current chain */ + l = &arp->hash[haship(a->ip)]; + for(f = *l; f; f = f->hash){ + if(f == a){ + *l = a->hash; + break; + } + l = &f->hash; + } + + /* take out of re-transmit chain */ + l = &arp->rxmt; + for(f = *l; f; f = f->nextrxt){ + if(f == a){ + *l = a->nextrxt; + break; + } + l = &f->nextrxt; + } + a->nextrxt = nil; + a->hash = nil; + a->hold = nil; + a->last = nil; + a->ifc = nil; +} + +/* + * fill in the media address if we have it. Otherwise return an + * Arpent that represents the state of the address resolution FSM + * for ip. Add the packet to be sent onto the list of packets + * waiting for ip->mac to be resolved. + */ +Arpent* +arpget(Arp *arp, Block *bp, int version, Ipifc *ifc, uchar *ip, uchar *mac) +{ + int hash; + Arpent *a; + Medium *type = ifc->m; + uchar v6ip[IPaddrlen]; + + if(version == V4){ + v4tov6(v6ip, ip); + ip = v6ip; + } + + qlock(arp); + hash = haship(ip); + for(a = arp->hash[hash]; a; a = a->hash){ + if(memcmp(ip, a->ip, sizeof(a->ip)) == 0) + if(type == a->type) + break; + } + + if(a == nil){ + a = newarp6(arp, ip, ifc, (version != V4)); + a->state = AWAIT; + } + a->utime = NOW; + if(a->state == AWAIT){ + if(bp != nil){ + if(a->hold) + a->last->list = bp; + else + a->hold = bp; + a->last = bp; + bp->list = nil; + } + return a; /* return with arp qlocked */ + } + + memmove(mac, a->mac, a->type->maclen); + + /* remove old entries */ + if(NOW - a->ctime > 15*60*1000) + cleanarpent(arp, a); + + qunlock(arp); + return nil; +} + +/* + * called with arp locked + */ +void +arprelease(Arp *arp, Arpent*) +{ + qunlock(arp); +} + +/* + * Copy out the mac address from the Arpent. Return the + * block waiting to get sent to this mac address. + * + * called with arp locked + */ +Block* +arpresolve(Arp *arp, Arpent *a, Medium *type, uchar *mac) +{ + Block *bp; + Arpent *f, **l; + + if(!isv4(a->ip)){ + l = &arp->rxmt; + for(f = *l; f; f = f->nextrxt){ + if(f == a){ + *l = a->nextrxt; + break; + } + l = &f->nextrxt; + } + } + + memmove(a->mac, mac, type->maclen); + a->type = type; + a->state = AOK; + a->utime = NOW; + bp = a->hold; + a->hold = nil; + qunlock(arp); + + return bp; +} + +void +arpenter(Fs *fs, int version, uchar *ip, uchar *mac, int n, int refresh) +{ + Arp *arp; + Route *r; + Arpent *a, *f, **l; + Ipifc *ifc; + Medium *type; + Block *bp, *next; + uchar v6ip[IPaddrlen]; + + arp = fs->arp; + + if(n != 6){ +// print("arp: len = %d\n", n); + return; + } + + switch(version){ + case V4: + r = v4lookup(fs, ip, nil); + v4tov6(v6ip, ip); + ip = v6ip; + break; + case V6: + r = v6lookup(fs, ip, nil); + break; + default: + panic("arpenter: version %d", version); + return; /* to supress warnings */ + } + + if(r == nil){ +// print("arp: no route for entry\n"); + return; + } + + ifc = r->ifc; + type = ifc->m; + + qlock(arp); + for(a = arp->hash[haship(ip)]; a; a = a->hash){ + if(a->type != type || (a->state != AWAIT && a->state != AOK)) + continue; + + if(ipcmp(a->ip, ip) == 0){ + a->state = AOK; + memmove(a->mac, mac, type->maclen); + + if(version == V6){ + /* take out of re-transmit chain */ + l = &arp->rxmt; + for(f = *l; f; f = f->nextrxt){ + if(f == a){ + *l = a->nextrxt; + break; + } + l = &f->nextrxt; + } + } + + a->ifc = ifc; + a->ifcid = ifc->ifcid; + bp = a->hold; + a->hold = nil; + if(version == V4) + ip += IPv4off; + a->utime = NOW; + a->ctime = a->utime; + qunlock(arp); + + while(bp){ + next = bp->list; + if(ifc != nil){ + if(waserror()){ + runlock(ifc); + nexterror(); + } + rlock(ifc); + if(ifc->m != nil) + ifc->m->bwrite(ifc, bp, version, ip); + else + freeb(bp); + runlock(ifc); + poperror(); + } else + freeb(bp); + bp = next; + } + return; + } + } + + if(refresh == 0){ + a = newarp6(arp, ip, ifc, 0); + a->state = AOK; + a->type = type; + a->ctime = NOW; + memmove(a->mac, mac, type->maclen); + } + + qunlock(arp); +} + +int +arpwrite(Fs *fs, char *s, int len) +{ + int n; + Route *r; + Arp *arp; + Block *bp; + Arpent *a, *fl, **l; + Medium *m; + char *f[4], buf[256]; + uchar ip[IPaddrlen], mac[MAClen]; + + arp = fs->arp; + + if(len == 0) + error(Ebadarp); + if(len >= sizeof(buf)) + len = sizeof(buf)-1; + strncpy(buf, s, len); + buf[len] = 0; + if(len > 0 && buf[len-1] == '\n') + buf[len-1] = 0; + + n = getfields(buf, f, 4, 1, " "); + if(strcmp(f[0], "flush") == 0){ + qlock(arp); + for(a = arp->cache; a < &arp->cache[NCACHE]; a++){ + memset(a->ip, 0, sizeof(a->ip)); + memset(a->mac, 0, sizeof(a->mac)); + a->hash = nil; + a->state = 0; + a->utime = 0; + while(a->hold != nil){ + bp = a->hold->list; + freeblist(a->hold); + a->hold = bp; + } + } + memset(arp->hash, 0, sizeof(arp->hash)); +// clear all pkts on these lists (rxmt, dropf/l) + arp->rxmt = nil; + arp->dropf = nil; + arp->dropl = nil; + qunlock(arp); + } else if(strcmp(f[0], "add") == 0){ + switch(n){ + default: + error(Ebadarg); + case 3: + parseip(ip, f[1]); + if(isv4(ip)) + r = v4lookup(fs, ip+IPv4off, nil); + else + r = v6lookup(fs, ip, nil); + if(r == nil) + error("Destination unreachable"); + m = r->ifc->m; + n = parsemac(mac, f[2], m->maclen); + break; + case 4: + m = ipfindmedium(f[1]); + if(m == nil) + error(Ebadarp); + parseip(ip, f[2]); + n = parsemac(mac, f[3], m->maclen); + break; + } + + if(m->ares == nil) + error(Ebadarp); + + m->ares(fs, V6, ip, mac, n, 0); + } else if(strcmp(f[0], "del") == 0){ + if(n != 2) + error(Ebadarg); + + parseip(ip, f[1]); + qlock(arp); + + l = &arp->hash[haship(ip)]; + for(a = *l; a; a = a->hash){ + if(memcmp(ip, a->ip, sizeof(a->ip)) == 0){ + *l = a->hash; + break; + } + l = &a->hash; + } + + if(a){ + /* take out of re-transmit chain */ + l = &arp->rxmt; + for(fl = *l; fl; fl = fl->nextrxt){ + if(fl == a){ + *l = a->nextrxt; + break; + } + l = &fl->nextrxt; + } + + a->nextrxt = nil; + a->hash = nil; + a->hold = nil; + a->last = nil; + a->ifc = nil; + memset(a->ip, 0, sizeof(a->ip)); + memset(a->mac, 0, sizeof(a->mac)); + } + qunlock(arp); + } else + error(Ebadarp); + + return len; +} + +enum +{ + Alinelen= 90, +}; + +char *aformat = "%-6.6s %-8.8s %-40.40I %-32.32s\n"; + +static void +convmac(char *p, uchar *mac, int n) +{ + while(n-- > 0) + p += sprint(p, "%2.2ux", *mac++); +} + +int +arpread(Arp *arp, char *p, ulong offset, int len) +{ + Arpent *a; + int n; + char mac[2*MAClen+1]; + + if(offset % Alinelen) + return 0; + + offset = offset/Alinelen; + len = len/Alinelen; + + n = 0; + for(a = arp->cache; len > 0 && a < &arp->cache[NCACHE]; a++){ + if(a->state == 0) + continue; + if(offset > 0){ + offset--; + continue; + } + len--; + qlock(arp); + convmac(mac, a->mac, a->type->maclen); + n += sprint(p+n, aformat, a->type->name, arpstate[a->state], a->ip, mac); + qunlock(arp); + } + + return n; +} + +extern int +rxmitsols(Arp *arp) +{ + uint sflag; + Block *next, *xp; + Arpent *a, *b, **l; + Fs *f; + uchar ipsrc[IPaddrlen]; + Ipifc *ifc = nil; + long nrxt; + + qlock(arp); + f = arp->f; + + a = arp->rxmt; + if(a==nil){ + nrxt = 0; + goto dodrops; //return nrxt; + } + nrxt = a->rtime - NOW; + if(nrxt > 3*ReTransTimer/4) + goto dodrops; //return nrxt; + + for(; a; a = a->nextrxt){ + ifc = a->ifc; + assert(ifc != nil); + if((a->rxtsrem <= 0) || !(canrlock(ifc)) || (a->ifcid != ifc->ifcid)){ + xp = a->hold; + a->hold = nil; + + if(xp){ + if(arp->dropl == nil) + arp->dropf = xp; + else + arp->dropl->list = xp; + } + + cleanarpent(arp, a); + } + else + break; + } + if(a == nil) + goto dodrops; + + + qunlock(arp); /* for icmpns */ + if((sflag = ipv6anylocal(ifc, ipsrc)) != SRC_UNSPEC) + icmpns(f, ipsrc, sflag, a->ip, TARG_MULTI, ifc->mac); + + runlock(ifc); + qlock(arp); + + /* put to the end of re-transmit chain */ + l = &arp->rxmt; + for(b = *l; b; b = b->nextrxt){ + if(b == a){ + *l = a->nextrxt; + break; + } + l = &b->nextrxt; + } + for(b = *l; b; b = b->nextrxt){ + l = &b->nextrxt; + } + *l = a; + a->rxtsrem--; + a->nextrxt = nil; + a->rtime = NOW + ReTransTimer; + + a = arp->rxmt; + if(a==nil) + nrxt = 0; + else + nrxt = a->rtime - NOW; + +dodrops: + xp = arp->dropf; + arp->dropf = nil; + arp->dropl = nil; + qunlock(arp); + + for(; xp; xp = next){ + next = xp->list; + icmphostunr(f, ifc, xp, icmp6_adr_unreach, 1); + } + + return nrxt; + +} + +static int +rxready(void *v) +{ + Arp *arp = (Arp *) v; + int x; + + x = ((arp->rxmt != nil) || (arp->dropf != nil)); + + return x; +} + +static void +rxmitproc(void *v) +{ + Arp *arp = v; + long wakeupat; + + arp->rxmitp = up; + //print("arp rxmitproc started\n"); + if(waserror()){ + arp->rxmitp = 0; + pexit("hangup", 1); + } + for(;;){ + wakeupat = rxmitsols(arp); + if(wakeupat == 0) + sleep(&arp->rxmtq, rxready, v); + else if(wakeupat > ReTransTimer/4) + tsleep(&arp->rxmtq, return0, 0, wakeupat); + } +} + diff --git a/os/ip/bootp.c b/os/ip/bootp.c new file mode 100644 index 00000000..b7d3fcda --- /dev/null +++ b/os/ip/bootp.c @@ -0,0 +1,231 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "kernel.h" +#include "ip.h" + +static ulong fsip; +static ulong auip; +static ulong gwip; +static ulong ipmask; +static ulong ipaddr; + +enum +{ + Bootrequest = 1, + Bootreply = 2, +}; + +typedef struct Bootp +{ + /* udp.c oldheader */ + uchar raddr[IPaddrlen]; + uchar laddr[IPaddrlen]; + uchar rport[2]; + uchar lport[2]; + /* bootp itself */ + uchar op; /* opcode */ + uchar htype; /* hardware type */ + uchar hlen; /* hardware address len */ + uchar hops; /* hops */ + uchar xid[4]; /* a random number */ + uchar secs[2]; /* elapsed snce client started booting */ + uchar pad[2]; + uchar ciaddr[4]; /* client IP address (client tells server) */ + uchar yiaddr[4]; /* client IP address (server tells client) */ + uchar siaddr[4]; /* server IP address */ + uchar giaddr[4]; /* gateway IP address */ + uchar chaddr[16]; /* client hardware address */ + uchar sname[64]; /* server host name (optional) */ + uchar file[128]; /* boot file name */ + uchar vend[128]; /* vendor-specific goo */ +} Bootp; + +/* + * bootp returns: + * + * "fsip d.d.d.d + * auip d.d.d.d + * gwip d.d.d.d + * ipmask d.d.d.d + * ipaddr d.d.d.d" + * + * where d.d.d.d is the IP address in dotted decimal notation, and each + * address is followed by a newline. + */ + +static Bootp req; +static Proc* rcvprocp; +static int recv; +static int done; +static Rendez bootpr; +static char rcvbuf[512+2*IPaddrlen+2*2]; + +static void +rcvbootp(void *a) +{ + int n, fd; + Bootp *rp; + char *field[4]; + uchar ip[IPaddrlen]; + + if(waserror()) + pexit("", 0); + rcvprocp = up; /* store for postnote below */ + fd = (int)a; + while(done == 0) { + n = kread(fd, rcvbuf, sizeof(rcvbuf)); + if(n <= 0) + break; + rp = (Bootp*)rcvbuf; + /* currently ignore udp's header */ + if(memcmp(req.chaddr, rp->chaddr, 6) == 0 + && rp->htype == 1 && rp->hlen == 6 + && getfields((char*)rp->vend+4, field, 4, 1, " ") == 4 + && strncmp((char*)rp->vend, "p9 ", 4) == 0){ + if(ipaddr == 0) + ipaddr = nhgetl(rp->yiaddr); + if(ipmask == 0) + ipmask = parseip(ip, field[0]); + if(fsip == 0) + fsip = parseip(ip, field[1]); + if(auip == 0) + auip = parseip(ip, field[2]); + if(gwip == 0) + gwip = parseip(ip, field[3]); + break; + } + } + poperror(); + rcvprocp = nil; + + recv = 1; + wakeup(&bootpr); + pexit("", 0); +} + +static char* +rbootp(Ipifc *ifc) +{ + int cfd, dfd, tries, n; + char ia[5+3*24], im[16], *av[3]; + uchar nipaddr[4], ngwip[4], nipmask[4]; + char dir[Maxpath]; + + av[1] = "0.0.0.0"; + av[2] = "0.0.0.0"; + ipifcadd(ifc, av, 3, 0, nil); + + cfd = kannounce("udp!*!68", dir); + if(cfd < 0) + return "bootp announce failed"; + strcat(dir, "/data"); + if(kwrite(cfd, "headers", 7) < 0){ + kclose(cfd); + return "bootp ctl headers failed"; + } + kwrite(cfd, "oldheaders", 10); + dfd = kopen(dir, ORDWR); + if(dfd < 0){ + kclose(cfd); + return "bootp open data failed"; + } + kclose(cfd); + + + /* create request */ + memset(&req, 0, sizeof(req)); + ipmove(req.raddr, IPv4bcast); + hnputs(req.rport, 67); + req.op = Bootrequest; + req.htype = 1; /* ethernet (all we know) */ + req.hlen = 6; /* ethernet (all we know) */ + + /* Hardware MAC address */ + memmove(req.chaddr, ifc->mac, 6); + /* Fill in the local IP address if we know it */ + ipv4local(ifc, req.ciaddr); + memset(req.file, 0, sizeof(req.file)); + strcpy((char*)req.vend, "p9 "); + + done = 0; + recv = 0; + + kproc("rcvbootp", rcvbootp, (void*)dfd, KPDUPFDG); + + /* + * broadcast bootp's till we get a reply, + * or fixed number of tries + */ + tries = 0; + while(recv == 0) { + if(kwrite(dfd, &req, sizeof(req)) < 0) + print("bootp: write: %s\n", commonerror()); + + tsleep(&bootpr, return0, 0, 1000); + if(++tries > 10) { + print("bootp: timed out\n"); + break; + } + } + kclose(dfd); + done = 1; + if(rcvprocp != nil){ + postnote(rcvprocp, 1, "timeout", 0); + rcvprocp = nil; + } + + av[1] = "0.0.0.0"; + av[2] = "0.0.0.0"; + ipifcrem(ifc, av, 3); + + hnputl(nipaddr, ipaddr); + sprint(ia, "%V", nipaddr); + hnputl(nipmask, ipmask); + sprint(im, "%V", nipmask); + av[1] = ia; + av[2] = im; + ipifcadd(ifc, av, 3, 0, nil); + + if(gwip != 0) { + hnputl(ngwip, gwip); + n = snprint(ia, sizeof(ia), "add 0.0.0.0 0.0.0.0 %V", ngwip); + routewrite(ifc->conv->p->f, nil, ia, n); + } + return nil; +} + +static int +rbootpread(char *bp, ulong offset, int len) +{ + int n; + char *buf; + uchar a[4]; + + buf = smalloc(READSTR); + if(waserror()){ + free(buf); + nexterror(); + } + hnputl(a, fsip); + n = snprint(buf, READSTR, "fsip %15V\n", a); + hnputl(a, auip); + n += snprint(buf + n, READSTR-n, "auip %15V\n", a); + hnputl(a, gwip); + n += snprint(buf + n, READSTR-n, "gwip %15V\n", a); + hnputl(a, ipmask); + n += snprint(buf + n, READSTR-n, "ipmask %15V\n", a); + hnputl(a, ipaddr); + snprint(buf + n, READSTR-n, "ipaddr %15V\n", a); + + len = readstr(offset, bp, len, buf); + poperror(); + free(buf); + return len; +} + +char* (*bootp)(Ipifc*) = rbootp; +int (*bootpread)(char*, ulong, int) = rbootpread; diff --git a/os/ip/compress.c b/os/ip/compress.c new file mode 100644 index 00000000..0a7bd7a3 --- /dev/null +++ b/os/ip/compress.c @@ -0,0 +1,520 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" +#include "ppp.h" + +typedef struct Iphdr Iphdr; +typedef struct Tcphdr Tcphdr; +typedef struct Ilhdr Ilhdr; +typedef struct Hdr Hdr; +typedef struct Tcpc Tcpc; + +struct Iphdr +{ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar ttl; /* Time to live */ + uchar proto; /* Protocol */ + uchar cksum[2]; /* Header checksum */ + ulong src; /* Ip source (byte ordering unimportant) */ + ulong dst; /* Ip destination (byte ordering unimportant) */ +}; + +struct Tcphdr +{ + ulong ports; /* defined as a ulong to make comparisons easier */ + uchar seq[4]; + uchar ack[4]; + uchar flag[2]; + uchar win[2]; + uchar cksum[2]; + uchar urg[2]; +}; + +struct Ilhdr +{ + uchar sum[2]; /* Checksum including header */ + uchar len[2]; /* Packet length */ + uchar type; /* Packet type */ + uchar spec; /* Special */ + uchar src[2]; /* Src port */ + uchar dst[2]; /* Dst port */ + uchar id[4]; /* Sequence id */ + uchar ack[4]; /* Acked sequence */ +}; + +enum +{ + URG = 0x20, /* Data marked urgent */ + ACK = 0x10, /* Aknowledge is valid */ + PSH = 0x08, /* Whole data pipe is pushed */ + RST = 0x04, /* Reset connection */ + SYN = 0x02, /* Pkt. is synchronise */ + FIN = 0x01, /* Start close down */ + + IP_DF = 0x4000, /* Don't fragment */ + + IP_TCPPROTO = 6, + IP_ILPROTO = 40, + IL_IPHDR = 20, +}; + +struct Hdr +{ + uchar buf[128]; + Iphdr *ip; + Tcphdr *tcp; + int len; +}; + +struct Tcpc +{ + uchar lastrecv; + uchar lastxmit; + uchar basexmit; + uchar err; + uchar compressid; + Hdr t[MAX_STATES]; + Hdr r[MAX_STATES]; +}; + +enum +{ /* flag bits for what changed in a packet */ + NEW_U=(1<<0), /* tcp only */ + NEW_W=(1<<1), /* tcp only */ + NEW_A=(1<<2), /* il tcp */ + NEW_S=(1<<3), /* tcp only */ + NEW_P=(1<<4), /* tcp only */ + NEW_I=(1<<5), /* il tcp */ + NEW_C=(1<<6), /* il tcp */ + NEW_T=(1<<7), /* il only */ + TCP_PUSH_BIT = 0x10, +}; + +/* reserved, special-case values of above for tcp */ +#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */ +#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */ +#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U) + +int +encode(void *p, ulong n) +{ + uchar *cp; + + cp = p; + if(n >= 256 || n == 0) { + *cp++ = 0; + cp[0] = n >> 8; + cp[1] = n; + return 3; + } else + *cp = n; + return 1; +} + +#define DECODEL(f) { \ + if (*cp == 0) {\ + hnputl(f, nhgetl(f) + ((cp[1] << 8) | cp[2])); \ + cp += 3; \ + } else { \ + hnputl(f, nhgetl(f) + (ulong)*cp++); \ + } \ +} +#define DECODES(f) { \ + if (*cp == 0) {\ + hnputs(f, nhgets(f) + ((cp[1] << 8) | cp[2])); \ + cp += 3; \ + } else { \ + hnputs(f, nhgets(f) + (ulong)*cp++); \ + } \ +} + +ushort +tcpcompress(Tcpc *comp, Block *b, Fs *) +{ + Iphdr *ip; /* current packet */ + Tcphdr *tcp; /* current pkt */ + ulong iplen, tcplen, hlen; /* header length in bytes */ + ulong deltaS, deltaA; /* general purpose temporaries */ + ulong changes; /* change mask */ + uchar new_seq[16]; /* changes from last to current */ + uchar *cp; + Hdr *h; /* last packet */ + int i, j; + + /* + * Bail if this is not a compressible TCP/IP packet + */ + ip = (Iphdr*)b->rp; + iplen = (ip->vihl & 0xf) << 2; + tcp = (Tcphdr*)(b->rp + iplen); + tcplen = (tcp->flag[0] & 0xf0) >> 2; + hlen = iplen + tcplen; + if((tcp->flag[1] & (SYN|FIN|RST|ACK)) != ACK) + return Pip; /* connection control */ + + /* + * Packet is compressible, look for a connection + */ + changes = 0; + cp = new_seq; + j = comp->lastxmit; + h = &comp->t[j]; + if(ip->src != h->ip->src || ip->dst != h->ip->dst + || tcp->ports != h->tcp->ports) { + for(i = 0; i < MAX_STATES; ++i) { + j = (comp->basexmit + i) % MAX_STATES; + h = &comp->t[j]; + if(ip->src == h->ip->src && ip->dst == h->ip->dst + && tcp->ports == h->tcp->ports) + goto found; + } + + /* no connection, reuse the oldest */ + if(i == MAX_STATES) { + j = comp->basexmit; + j = (j + MAX_STATES - 1) % MAX_STATES; + comp->basexmit = j; + h = &comp->t[j]; + goto raise; + } + } +found: + + /* + * Make sure that only what we expect to change changed. + */ + if(ip->vihl != h->ip->vihl || ip->tos != h->ip->tos || + ip->ttl != h->ip->ttl || ip->proto != h->ip->proto) + goto raise; /* headers changed */ + if(iplen != sizeof(Iphdr) && memcmp(ip+1, h->ip+1, iplen - sizeof(Iphdr))) + goto raise; /* ip options changed */ + if(tcplen != sizeof(Tcphdr) && memcmp(tcp+1, h->tcp+1, tcplen - sizeof(Tcphdr))) + goto raise; /* tcp options changed */ + + if(tcp->flag[1] & URG) { + cp += encode(cp, nhgets(tcp->urg)); + changes |= NEW_U; + } else if(memcmp(tcp->urg, h->tcp->urg, sizeof(tcp->urg)) != 0) + goto raise; + if(deltaS = nhgets(tcp->win) - nhgets(h->tcp->win)) { + cp += encode(cp, deltaS); + changes |= NEW_W; + } + if(deltaA = nhgetl(tcp->ack) - nhgetl(h->tcp->ack)) { + if(deltaA > 0xffff) + goto raise; + cp += encode(cp, deltaA); + changes |= NEW_A; + } + if(deltaS = nhgetl(tcp->seq) - nhgetl(h->tcp->seq)) { + if (deltaS > 0xffff) + goto raise; + cp += encode(cp, deltaS); + changes |= NEW_S; + } + + /* + * Look for the special-case encodings. + */ + switch(changes) { + case 0: + /* + * Nothing changed. If this packet contains data and the last + * one didn't, this is probably a data packet following an + * ack (normal on an interactive connection) and we send it + * compressed. Otherwise it's probably a retransmit, + * retransmitted ack or window probe. Send it uncompressed + * in case the other side missed the compressed version. + */ + if(nhgets(ip->length) == nhgets(h->ip->length) || + nhgets(h->ip->length) != hlen) + goto raise; + break; + case SPECIAL_I: + case SPECIAL_D: + /* + * Actual changes match one of our special case encodings -- + * send packet uncompressed. + */ + goto raise; + case NEW_S | NEW_A: + if (deltaS == deltaA && + deltaS == nhgets(h->ip->length) - hlen) { + /* special case for echoed terminal traffic */ + changes = SPECIAL_I; + cp = new_seq; + } + break; + case NEW_S: + if (deltaS == nhgets(h->ip->length) - hlen) { + /* special case for data xfer */ + changes = SPECIAL_D; + cp = new_seq; + } + break; + } + deltaS = nhgets(ip->id) - nhgets(h->ip->id); + if(deltaS != 1) { + cp += encode(cp, deltaS); + changes |= NEW_I; + } + if (tcp->flag[1] & PSH) + changes |= TCP_PUSH_BIT; + /* + * Grab the cksum before we overwrite it below. Then update our + * state with this packet's header. + */ + deltaA = nhgets(tcp->cksum); + memmove(h->buf, b->rp, hlen); + h->len = hlen; + h->tcp = (Tcphdr*)(h->buf + iplen); + + /* + * We want to use the original packet as our compressed packet. (cp - + * new_seq) is the number of bytes we need for compressed sequence + * numbers. In addition we need one byte for the change mask, one + * for the connection id and two for the tcp checksum. So, (cp - + * new_seq) + 4 bytes of header are needed. hlen is how many bytes + * of the original packet to toss so subtract the two to get the new + * packet size. The temporaries are gross -egs. + */ + deltaS = cp - new_seq; + cp = b->rp; + if(comp->lastxmit != j || comp->compressid == 0) { + comp->lastxmit = j; + hlen -= deltaS + 4; + cp += hlen; + *cp++ = (changes | NEW_C); + *cp++ = j; + } else { + hlen -= deltaS + 3; + cp += hlen; + *cp++ = changes; + } + b->rp += hlen; + hnputs(cp, deltaA); + cp += 2; + memmove(cp, new_seq, deltaS); + return Pvjctcp; + +raise: + /* + * Update connection state & send uncompressed packet + */ + memmove(h->buf, b->rp, hlen); + h->tcp = (Tcphdr*)(h->buf + iplen); + h->len = hlen; + h->ip->proto = j; + comp->lastxmit = j; + return Pvjutcp; +} + +Block* +tcpuncompress(Tcpc *comp, Block *b, ushort type, Fs *f) +{ + uchar *cp, changes; + int i; + int iplen, len; + Iphdr *ip; + Tcphdr *tcp; + Hdr *h; + + if(type == Pvjutcp) { + /* + * Locate the saved state for this connection. If the state + * index is legal, clear the 'discard' flag. + */ + ip = (Iphdr*)b->rp; + if(ip->proto >= MAX_STATES) + goto raise; + iplen = (ip->vihl & 0xf) << 2; + tcp = (Tcphdr*)(b->rp + iplen); + comp->lastrecv = ip->proto; + len = iplen + ((tcp->flag[0] & 0xf0) >> 2); + comp->err = 0; +netlog(f, Logcompress, "uncompressed %d\n", comp->lastrecv); + /* + * Restore the IP protocol field then save a copy of this + * packet header. The checksum is zeroed in the copy so we + * don't have to zero it each time we process a compressed + * packet. + */ + ip->proto = IP_TCPPROTO; + h = &comp->r[comp->lastrecv]; + memmove(h->buf, b->rp, len); + h->tcp = (Tcphdr*)(h->buf + iplen); + h->len = len; + h->ip->cksum[0] = h->ip->cksum[1] = 0; + return b; + } + + cp = b->rp; + changes = *cp++; + if(changes & NEW_C) { + /* + * Make sure the state index is in range, then grab the + * state. If we have a good state index, clear the 'discard' + * flag. + */ + if(*cp >= MAX_STATES) + goto raise; + comp->err = 0; + comp->lastrecv = *cp++; +netlog(f, Logcompress, "newc %d\n", comp->lastrecv); + } else { + /* + * This packet has no state index. If we've had a + * line error since the last time we got an explicit state + * index, we have to toss the packet. + */ + if(comp->err != 0){ + freeblist(b); + return nil; + } +netlog(f, Logcompress, "oldc %d\n", comp->lastrecv); + } + + /* + * Find the state then fill in the TCP checksum and PUSH bit. + */ + h = &comp->r[comp->lastrecv]; + ip = h->ip; + tcp = h->tcp; + len = h->len; + memmove(tcp->cksum, cp, sizeof tcp->cksum); + cp += 2; + if(changes & TCP_PUSH_BIT) + tcp->flag[1] |= PSH; + else + tcp->flag[1] &= ~PSH; + /* + * Fix up the state's ack, seq, urg and win fields based on the + * changemask. + */ + switch (changes & SPECIALS_MASK) { + case SPECIAL_I: + i = nhgets(ip->length) - len; + hnputl(tcp->ack, nhgetl(tcp->ack) + i); + hnputl(tcp->seq, nhgetl(tcp->seq) + i); + break; + + case SPECIAL_D: + hnputl(tcp->seq, nhgetl(tcp->seq) + nhgets(ip->length) - len); + break; + + default: + if(changes & NEW_U) { + tcp->flag[1] |= URG; + if(*cp == 0){ + hnputs(tcp->urg, nhgets(cp+1)); + cp += 3; + }else + hnputs(tcp->urg, *cp++); + } else + tcp->flag[1] &= ~URG; + if(changes & NEW_W) + DECODES(tcp->win) + if(changes & NEW_A) + DECODEL(tcp->ack) + if(changes & NEW_S) + DECODEL(tcp->seq) + break; + } + + /* Update the IP ID */ + if(changes & NEW_I) + DECODES(ip->id) + else + hnputs(ip->id, nhgets(ip->id) + 1); + + /* + * At this point, cp points to the first byte of data in the packet. + * Back up cp by the TCP/IP header length to make room for the + * reconstructed header. + * We assume the packet we were handed has enough space to prepend + * up to 128 bytes of header. + */ + b->rp = cp; + if(b->rp - b->base < len){ + b = padblock(b, len); + b = pullupblock(b, blocklen(b)); + } else + b->rp -= len; + hnputs(ip->length, BLEN(b)); + memmove(b->rp, ip, len); + + /* recompute the ip header checksum */ + ip = (Iphdr*)b->rp; + hnputs(ip->cksum, ipcsum(b->rp)); + return b; + +raise: + netlog(f, Logcompress, "Bad Packet!\n"); + comp->err = 1; + freeblist(b); + return nil; +} + +Tcpc* +compress_init(Tcpc *c) +{ + int i; + Hdr *h; + + if(c == nil){ + c = malloc(sizeof(Tcpc)); + if(c == nil) + return nil; + } + memset(c, 0, sizeof(*c)); + for(i = 0; i < MAX_STATES; i++){ + h = &c->t[i]; + h->ip = (Iphdr*)h->buf; + h->tcp = (Tcphdr*)(h->buf + 10); + h->len = 20; + h = &c->r[i]; + h->ip = (Iphdr*)h->buf; + h->tcp = (Tcphdr*)(h->buf + 10); + h->len = 20; + } + + return c; +} + +ushort +compress(Tcpc *tcp, Block *b, Fs *f) +{ + Iphdr *ip; + + /* + * Bail if this is not a compressible IP packet + */ + ip = (Iphdr*)b->rp; + if((nhgets(ip->frag) & 0x3fff) != 0) + return Pip; + + switch(ip->proto) { + case IP_TCPPROTO: + return tcpcompress(tcp, b, f); + default: + return Pip; + } +} + +int +compress_negotiate(Tcpc *tcp, uchar *data) +{ + if(data[0] != MAX_STATES - 1) + return -1; + tcp->compressid = data[1]; + return 0; +} diff --git a/os/ip/devip.c b/os/ip/devip.c new file mode 100644 index 00000000..8564e987 --- /dev/null +++ b/os/ip/devip.c @@ -0,0 +1,1419 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "../ip/ip.h" + +enum +{ + Qtopdir= 1, /* top level directory */ + Qtopbase, + Qarp= Qtopbase, + Qbootp, + Qndb, + Qiproute, + Qiprouter, + Qipselftab, + Qlog, + + Qprotodir, /* directory for a protocol */ + Qprotobase, + Qclone= Qprotobase, + Qstats, + + Qconvdir, /* directory for a conversation */ + Qconvbase, + Qctl= Qconvbase, + Qdata, + Qerr, + Qlisten, + Qlocal, + Qremote, + Qstatus, + Qsnoop, + + Logtype= 5, + Masktype= (1<> Shiftconv) & Maskconv ) +#define PROTO(x) ( (((ulong)(x).path) >> Shiftproto) & Maskproto ) +#define QID(p, c, y) ( ((p)<<(Shiftproto)) | ((c)<dev]->p[PROTO(c->qid)]->conv[CONV(c->qid)]; + if(cv->owner == nil) + kstrdup(&cv->owner, eve); + mkqid(&q, QID(PROTO(c->qid), CONV(c->qid), i), 0, QTFILE); + + switch(i) { + default: + return -1; + case Qctl: + devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp); + return 1; + case Qdata: + devdir(c, q, "data", qlen(cv->rq), cv->owner, cv->perm, dp); + return 1; + case Qerr: + devdir(c, q, "err", qlen(cv->eq), cv->owner, cv->perm, dp); + return 1; + case Qlisten: + devdir(c, q, "listen", 0, cv->owner, cv->perm, dp); + return 1; + case Qlocal: + p = "local"; + break; + case Qremote: + p = "remote"; + break; + case Qsnoop: + if(strcmp(cv->p->name, "ipifc") != 0) + return -1; + devdir(c, q, "snoop", qlen(cv->sq), cv->owner, 0400, dp); + return 1; + case Qstatus: + p = "status"; + break; + } + devdir(c, q, p, 0, cv->owner, 0444, dp); + return 1; +} + +static int +ip2gen(Chan *c, int i, Dir *dp) +{ + Qid q; + + switch(i) { + case Qclone: + mkqid(&q, QID(PROTO(c->qid), 0, Qclone), 0, QTFILE); + devdir(c, q, "clone", 0, network, 0666, dp); + return 1; + case Qstats: + mkqid(&q, QID(PROTO(c->qid), 0, Qstats), 0, QTFILE); + devdir(c, q, "stats", 0, network, 0444, dp); + return 1; + } + return -1; +} + +static int +ip1gen(Chan *c, int i, Dir *dp) +{ + Qid q; + char *p; + int prot; + int len = 0; + Fs *f; + extern ulong kerndate; + + f = ipfs[c->dev]; + + prot = 0666; + mkqid(&q, QID(0, 0, i), 0, QTFILE); + switch(i) { + default: + return -1; + case Qarp: + p = "arp"; + break; + case Qbootp: + p = "bootp"; + if(bootp == nil) + return 0; + break; + case Qndb: + p = "ndb"; + len = strlen(f->ndb); + q.vers = f->ndbvers; + break; + case Qiproute: + p = "iproute"; + break; + case Qipselftab: + p = "ipselftab"; + prot = 0444; + break; + case Qiprouter: + p = "iprouter"; + break; + case Qlog: + p = "log"; + break; + } + devdir(c, q, p, len, network, prot, dp); + if(i == Qndb && f->ndbmtime > kerndate) + dp->mtime = f->ndbmtime; + return 1; +} + +static int +ipgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp) +{ + Qid q; + Conv *cv; + Fs *f; + + f = ipfs[c->dev]; + + switch(TYPE(c->qid)) { + case Qtopdir: + if(s == DEVDOTDOT){ + mkqid(&q, QID(0, 0, Qtopdir), 0, QTDIR); + sprint(up->genbuf, "#I%lud", c->dev); + devdir(c, q, up->genbuf, 0, network, 0555, dp); + return 1; + } + if(s < f->np) { + if(f->p[s]->connect == nil) + return 0; /* protocol with no user interface */ + mkqid(&q, QID(s, 0, Qprotodir), 0, QTDIR); + devdir(c, q, f->p[s]->name, 0, network, 0555, dp); + return 1; + } + s -= f->np; + return ip1gen(c, s+Qtopbase, dp); + case Qarp: + case Qbootp: + case Qndb: + case Qlog: + case Qiproute: + case Qiprouter: + case Qipselftab: + return ip1gen(c, TYPE(c->qid), dp); + case Qprotodir: + if(s == DEVDOTDOT){ + mkqid(&q, QID(0, 0, Qtopdir), 0, QTDIR); + sprint(up->genbuf, "#I%lud", c->dev); + devdir(c, q, up->genbuf, 0, network, 0555, dp); + return 1; + } + if(s < f->p[PROTO(c->qid)]->ac) { + cv = f->p[PROTO(c->qid)]->conv[s]; + sprint(up->genbuf, "%d", s); + mkqid(&q, QID(PROTO(c->qid), s, Qconvdir), 0, QTDIR); + devdir(c, q, up->genbuf, 0, cv->owner, 0555, dp); + return 1; + } + s -= f->p[PROTO(c->qid)]->ac; + return ip2gen(c, s+Qprotobase, dp); + case Qclone: + case Qstats: + return ip2gen(c, TYPE(c->qid), dp); + case Qconvdir: + if(s == DEVDOTDOT){ + s = PROTO(c->qid); + mkqid(&q, QID(s, 0, Qprotodir), 0, QTDIR); + devdir(c, q, f->p[s]->name, 0, network, 0555, dp); + return 1; + } + return ip3gen(c, s+Qconvbase, dp); + case Qctl: + case Qdata: + case Qerr: + case Qlisten: + case Qlocal: + case Qremote: + case Qstatus: + case Qsnoop: + return ip3gen(c, TYPE(c->qid), dp); + } + return -1; +} + +static void +ipreset(void) +{ + nullmediumlink(); + pktmediumlink(); + + fmtinstall('i', eipfmt); + fmtinstall('I', eipfmt); + fmtinstall('E', eipfmt); + fmtinstall('V', eipfmt); + fmtinstall('M', eipfmt); +} + +static Fs* +ipgetfs(int dev) +{ + extern void (*ipprotoinit[])(Fs*); + Fs *f; + int i; + + if(dev >= Nfs) + return nil; + + qlock(&fslock); + if(ipfs[dev] == nil){ + f = smalloc(sizeof(Fs)); + ip_init(f); + arpinit(f); + netloginit(f); + for(i = 0; ipprotoinit[i]; i++) + ipprotoinit[i](f); + f->dev = dev; + ipfs[dev] = f; + } + qunlock(&fslock); + + return ipfs[dev]; +} + +IPaux* +newipaux(char *owner, char *tag) +{ + IPaux *a; + int n; + + a = smalloc(sizeof(*a)); + kstrdup(&a->owner, owner); + memset(a->tag, ' ', sizeof(a->tag)); + n = strlen(tag); + if(n > sizeof(a->tag)) + n = sizeof(a->tag); + memmove(a->tag, tag, n); + return a; +} + +#define ATTACHER(c) (((IPaux*)((c)->aux))->owner) + +static Chan* +ipattach(char* spec) +{ + Chan *c; + int dev; + + dev = atoi(spec); + if(dev >= Nfs) + error("bad specification"); + + ipgetfs(dev); + c = devattach('I', spec); + mkqid(&c->qid, QID(0, 0, Qtopdir), 0, QTDIR); + c->dev = dev; + + c->aux = newipaux(commonuser(), "none"); + + return c; +} + +static Walkqid* +ipwalk(Chan* c, Chan *nc, char **name, int nname) +{ + IPaux *a = c->aux; + Walkqid* w; + + w = devwalk(c, nc, name, nname, nil, 0, ipgen); + if(w != nil && w->clone != nil) + w->clone->aux = newipaux(a->owner, a->tag); + return w; +} + +static int +ipstat(Chan* c, uchar* db, int n) +{ + return devstat(c, db, n, nil, 0, ipgen); +} + +static int +incoming(void* arg) +{ + Conv *conv; + + conv = arg; + return conv->incall != nil; +} + +static int m2p[] = { + [OREAD] 4, + [OWRITE] 2, + [ORDWR] 6 +}; + +static Chan* +ipopen(Chan* c, int omode) +{ + Conv *cv, *nc; + Proto *p; + int perm; + Fs *f; + + perm = m2p[omode&3]; + + f = ipfs[c->dev]; + + switch(TYPE(c->qid)) { + default: + break; + case Qndb: + if(omode & (OWRITE|OTRUNC) && !iseve()) + error(Eperm); + if((omode & (OWRITE|OTRUNC)) == (OWRITE|OTRUNC)) + f->ndb[0] = 0; + break; + case Qlog: + netlogopen(f); + break; + case Qiprouter: + iprouteropen(f); + break; + case Qiproute: + break; + case Qtopdir: + case Qprotodir: + case Qconvdir: + case Qstatus: + case Qremote: + case Qlocal: + case Qstats: + case Qbootp: + case Qipselftab: + if(omode != OREAD) + error(Eperm); + break; + case Qsnoop: + if(omode != OREAD) + error(Eperm); + p = f->p[PROTO(c->qid)]; + cv = p->conv[CONV(c->qid)]; + if(strcmp(ATTACHER(c), cv->owner) != 0 && !iseve()) + error(Eperm); + incref(&cv->snoopers); + break; + case Qclone: + p = f->p[PROTO(c->qid)]; + qlock(p); + if(waserror()){ + qunlock(p); + nexterror(); + } + cv = Fsprotoclone(p, ATTACHER(c)); + qunlock(p); + poperror(); + if(cv == nil) { + error(Enodev); + break; + } + mkqid(&c->qid, QID(p->x, cv->x, Qctl), 0, QTFILE); + break; + case Qdata: + case Qctl: + case Qerr: + p = f->p[PROTO(c->qid)]; + qlock(p); + cv = p->conv[CONV(c->qid)]; + qlock(cv); + if(waserror()) { + qunlock(cv); + qunlock(p); + nexterror(); + } + if((perm & (cv->perm>>6)) != perm) { + if(strcmp(ATTACHER(c), cv->owner) != 0) + error(Eperm); + if((perm & cv->perm) != perm) + error(Eperm); + + } + cv->inuse++; + if(cv->inuse == 1){ + kstrdup(&cv->owner, ATTACHER(c)); + cv->perm = 0660; + } + qunlock(cv); + qunlock(p); + poperror(); + break; + case Qlisten: + cv = f->p[PROTO(c->qid)]->conv[CONV(c->qid)]; + if((perm & (cv->perm>>6)) != perm) { + if(strcmp(ATTACHER(c), cv->owner) != 0) + error(Eperm); + if((perm & cv->perm) != perm) + error(Eperm); + + } + + if(cv->state != Announced) + error("not announced"); + + if(waserror()){ + closeconv(cv); + nexterror(); + } + qlock(cv); + cv->inuse++; + qunlock(cv); + + nc = nil; + while(nc == nil) { + /* give up if we got a hangup */ + if(qisclosed(cv->rq)) + error("listen hungup"); + + qlock(&cv->listenq); + if(waserror()) { + qunlock(&cv->listenq); + nexterror(); + } + + /* wait for a connect */ + sleep(&cv->listenr, incoming, cv); + + qlock(cv); + nc = cv->incall; + if(nc != nil){ + cv->incall = nc->next; + mkqid(&c->qid, QID(PROTO(c->qid), nc->x, Qctl), 0, QTFILE); + kstrdup(&cv->owner, ATTACHER(c)); + } + qunlock(cv); + + qunlock(&cv->listenq); + poperror(); + } + closeconv(cv); + poperror(); + break; + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static int +ipwstat(Chan *c, uchar *dp, int n) +{ + Dir *d; + Conv *cv; + Fs *f; + Proto *p; + + f = ipfs[c->dev]; + switch(TYPE(c->qid)) { + default: + error(Eperm); + break; + case Qctl: + case Qdata: + break; + } + + d = smalloc(sizeof(*d)+n); + if(waserror()){ + free(d); + nexterror(); + } + n = convM2D(dp, n, d, (char*)&d[1]); + if(n == 0) + error(Eshortstat); + p = f->p[PROTO(c->qid)]; + cv = p->conv[CONV(c->qid)]; + if(!iseve() && strcmp(ATTACHER(c), cv->owner) != 0) + error(Eperm); + if(!emptystr(d->uid)) + kstrdup(&cv->owner, d->uid); + if(d->mode != ~0UL) + cv->perm = d->mode & 0777; + poperror(); + free(d); + return n; +} + +static void +closeconv(Conv *cv) +{ + Conv *nc; + Ipmulti *mp; + + qlock(cv); + + if(--cv->inuse > 0) { + qunlock(cv); + return; + } + + /* close all incoming calls since no listen will ever happen */ + for(nc = cv->incall; nc; nc = cv->incall){ + cv->incall = nc->next; + closeconv(nc); + } + cv->incall = nil; + + kstrdup(&cv->owner, network); + cv->perm = 0660; + + while((mp = cv->multi) != nil) + ipifcremmulti(cv, mp->ma, mp->ia); + + cv->r = nil; + cv->rgen = 0; + cv->p->close(cv); + cv->state = Idle; + qunlock(cv); +} + +static void +ipclose(Chan* c) +{ + Fs *f; + + f = ipfs[c->dev]; + switch(TYPE(c->qid)) { + default: + break; + case Qlog: + if(c->flag & COPEN) + netlogclose(f); + break; + case Qiprouter: + if(c->flag & COPEN) + iprouterclose(f); + break; + case Qdata: + case Qctl: + case Qerr: + if(c->flag & COPEN) + closeconv(f->p[PROTO(c->qid)]->conv[CONV(c->qid)]); + break; + case Qsnoop: + if(c->flag & COPEN) + decref(&f->p[PROTO(c->qid)]->conv[CONV(c->qid)]->snoopers); + break; + } + free(((IPaux*)c->aux)->owner); + free(c->aux); +} + +enum +{ + Statelen= 32*1024, +}; + +static long +ipread(Chan *ch, void *a, long n, vlong off) +{ + Conv *c; + Proto *x; + char *buf, *p; + long rv; + Fs *f; + ulong offset = off; + + f = ipfs[ch->dev]; + + p = a; + switch(TYPE(ch->qid)) { + default: + error(Eperm); + case Qtopdir: + case Qprotodir: + case Qconvdir: + return devdirread(ch, a, n, 0, 0, ipgen); + case Qarp: + return arpread(f->arp, a, offset, n); + case Qbootp: + return bootpread(a, offset, n); + case Qndb: + return readstr(offset, a, n, f->ndb); + case Qiproute: + return routeread(f, a, offset, n); + case Qiprouter: + return iprouterread(f, a, n); + case Qipselftab: + return ipselftabread(f, a, offset, n); + case Qlog: + return netlogread(f, a, offset, n); + case Qctl: + sprint(up->genbuf, "%lud", CONV(ch->qid)); + return readstr(offset, p, n, up->genbuf); + case Qremote: + buf = smalloc(Statelen); + x = f->p[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + if(x->remote == nil) { + sprint(buf, "%I!%d\n", c->raddr, c->rport); + } else { + (*x->remote)(c, buf, Statelen-2); + } + rv = readstr(offset, p, n, buf); + free(buf); + return rv; + case Qlocal: + buf = smalloc(Statelen); + x = f->p[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + if(x->local == nil) { + sprint(buf, "%I!%d\n", c->laddr, c->lport); + } else { + (*x->local)(c, buf, Statelen-2); + } + rv = readstr(offset, p, n, buf); + free(buf); + return rv; + case Qstatus: + buf = smalloc(Statelen); + x = f->p[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + (*x->state)(c, buf, Statelen-2); + rv = readstr(offset, p, n, buf); + free(buf); + return rv; + case Qdata: + c = f->p[PROTO(ch->qid)]->conv[CONV(ch->qid)]; + return qread(c->rq, a, n); + case Qerr: + c = f->p[PROTO(ch->qid)]->conv[CONV(ch->qid)]; + return qread(c->eq, a, n); + case Qsnoop: + c = f->p[PROTO(ch->qid)]->conv[CONV(ch->qid)]; + return qread(c->sq, a, n); + case Qstats: + x = f->p[PROTO(ch->qid)]; + if(x->stats == nil) + error("stats not implemented"); + buf = smalloc(Statelen); + (*x->stats)(x, buf, Statelen); + rv = readstr(offset, p, n, buf); + free(buf); + return rv; + } +} + +static Block* +ipbread(Chan* ch, long n, ulong offset) +{ + Conv *c; + Proto *x; + Fs *f; + + switch(TYPE(ch->qid)){ + case Qdata: + f = ipfs[ch->dev]; + x = f->p[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + return qbread(c->rq, n); + default: + return devbread(ch, n, offset); + } +} + +/* + * set local address to be that of the ifc closest to remote address + */ +static void +setladdr(Conv* c) +{ + findlocalip(c->p->f, c->laddr, c->raddr); +} + +/* + * set a local port making sure the quad of raddr,rport,laddr,lport is unique + */ +static char* +setluniqueport(Conv* c, int lport) +{ + Proto *p; + Conv *xp; + int x; + + p = c->p; + + qlock(p); + for(x = 0; x < p->nc; x++){ + xp = p->conv[x]; + if(xp == nil) + break; + if(xp == c) + continue; + if((xp->state == Connected || xp->state == Announced) + && xp->lport == lport + && xp->rport == c->rport + && ipcmp(xp->raddr, c->raddr) == 0 + && ipcmp(xp->laddr, c->laddr) == 0){ + qunlock(p); + return "address in use"; + } + } + c->lport = lport; + qunlock(p); + return nil; +} + +/* + * pick a local port and set it + */ +static void +setlport(Conv* c) +{ + Proto *p; + ushort *pp; + int x, found; + + p = c->p; + if(c->restricted) + pp = &p->nextrport; + else + pp = &p->nextport; + qlock(p); + for(;;(*pp)++){ + /* + * Fsproto initialises p->nextport to 0 and the restricted + * ports (p->nextrport) to 600. + * Restricted ports must lie between 600 and 1024. + * For the initial condition or if the unrestricted port number + * has wrapped round, select a random port between 5000 and 1<<15 + * to start at. + */ + if(c->restricted){ + if(*pp >= 1024) + *pp = 600; + } + else while(*pp < 5000) + *pp = nrand(1<<15); + + found = 0; + for(x = 0; x < p->nc; x++){ + if(p->conv[x] == nil) + break; + if(p->conv[x]->lport == *pp){ + found = 1; + break; + } + } + if(!found) + break; + } + c->lport = (*pp)++; + qunlock(p); +} + +/* + * set a local address and port from a string of the form + * [address!]port[!r] + */ +static char* +setladdrport(Conv* c, char* str, int announcing) +{ + char *p; + char *rv; + ushort lport; + uchar addr[IPaddrlen]; + + rv = nil; + + /* + * ignore restricted part if it exists. it's + * meaningless on local ports. + */ + p = strchr(str, '!'); + if(p != nil){ + *p++ = 0; + if(strcmp(p, "r") == 0) + p = nil; + } + + c->lport = 0; + if(p == nil){ + if(announcing) + ipmove(c->laddr, IPnoaddr); + else + setladdr(c); + p = str; + } else { + if(strcmp(str, "*") == 0) + ipmove(c->laddr, IPnoaddr); + else { + parseip(addr, str); + if(ipforme(c->p->f, addr)) + ipmove(c->laddr, addr); + else + return "not a local IP address"; + } + } + + /* one process can get all connections */ + if(announcing && strcmp(p, "*") == 0){ + if(!iseve()) + error(Eperm); + return setluniqueport(c, 0); + } + + lport = atoi(p); + if(lport <= 0) + setlport(c); + else + rv = setluniqueport(c, lport); + return rv; +} + +static char* +setraddrport(Conv* c, char* str) +{ + char *p; + + p = strchr(str, '!'); + if(p == nil) + return "malformed address"; + *p++ = 0; + parseip(c->raddr, str); + c->rport = atoi(p); + p = strchr(p, '!'); + if(p){ + if(strstr(p, "!r") != nil) + c->restricted = 1; + } + return nil; +} + +/* + * called by protocol connect routine to set addresses + */ +char* +Fsstdconnect(Conv *c, char *argv[], int argc) +{ + char *p; + + switch(argc) { + default: + return "bad args to connect"; + case 2: + p = setraddrport(c, argv[1]); + if(p != nil) + return p; + setladdr(c); + setlport(c); + break; + case 3: + p = setraddrport(c, argv[1]); + if(p != nil) + return p; + p = setladdrport(c, argv[2], 0); + if(p != nil) + return p; + } + + if((memcmp(c->raddr, v4prefix, IPv4off) == 0 && + memcmp(c->laddr, v4prefix, IPv4off) == 0) + || ipcmp(c->raddr, IPnoaddr) == 0) + c->ipversion = V4; + else + c->ipversion = V6; + + return nil; +} +/* + * initiate connection and sleep till its set up + */ +static int +connected(void* a) +{ + return ((Conv*)a)->state == Connected; +} +static void +connectctlmsg(Proto *x, Conv *c, Cmdbuf *cb) +{ + char *p; + + if(c->state != 0) + error(Econinuse); + c->state = Connecting; + c->cerr[0] = '\0'; + if(x->connect == nil) + error("connect not supported"); + p = x->connect(c, cb->f, cb->nf); + if(p != nil) + error(p); + + qunlock(c); + if(waserror()){ + qlock(c); + nexterror(); + } + sleep(&c->cr, connected, c); + qlock(c); + poperror(); + + if(c->cerr[0] != '\0') + error(c->cerr); +} + +/* + * called by protocol announce routine to set addresses + */ +char* +Fsstdannounce(Conv* c, char* argv[], int argc) +{ + memset(c->raddr, 0, sizeof(c->raddr)); + c->rport = 0; + switch(argc){ + default: + return "bad args to announce"; + case 2: + return setladdrport(c, argv[1], 1); + } + return nil; +} + +/* + * initiate announcement and sleep till its set up + */ +static int +announced(void* a) +{ + return ((Conv*)a)->state == Announced; +} +static void +announcectlmsg(Proto *x, Conv *c, Cmdbuf *cb) +{ + char *p; + + if(c->state != 0) + error(Econinuse); + c->state = Announcing; + c->cerr[0] = '\0'; + if(x->announce == nil) + error("announce not supported"); + p = x->announce(c, cb->f, cb->nf); + if(p != nil) + error(p); + + qunlock(c); + if(waserror()){ + qlock(c); + nexterror(); + } + sleep(&c->cr, announced, c); + qlock(c); + poperror(); + + if(c->cerr[0] != '\0') + error(c->cerr); +} + +/* + * called by protocol bind routine to set addresses + */ +char* +Fsstdbind(Conv* c, char* argv[], int argc) +{ + switch(argc){ + default: + return "bad args to bind"; + case 2: + return setladdrport(c, argv[1], 0); + } + return nil; +} + +static void +bindctlmsg(Proto *x, Conv *c, Cmdbuf *cb) +{ + char *p; + + if(x->bind == nil) + p = Fsstdbind(c, cb->f, cb->nf); + else + p = x->bind(c, cb->f, cb->nf); + if(p != nil) + error(p); +} + +static void +tosctlmsg(Conv *c, Cmdbuf *cb) +{ + if(cb->nf < 2) + c->tos = 0; + else + c->tos = atoi(cb->f[1]); +} + +static void +ttlctlmsg(Conv *c, Cmdbuf *cb) +{ + if(cb->nf < 2) + c->ttl = MAXTTL; + else + c->ttl = atoi(cb->f[1]); +} + +static long +ipwrite(Chan* ch, void *v, long n, vlong off) +{ + Conv *c; + Proto *x; + char *p; + Cmdbuf *cb; + uchar ia[IPaddrlen], ma[IPaddrlen]; + Fs *f; + char *a; + + a = v; + f = ipfs[ch->dev]; + + switch(TYPE(ch->qid)){ + default: + error(Eperm); + case Qdata: + x = f->p[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + + if(c->wq == nil) + error(Eperm); + + qwrite(c->wq, a, n); + break; + case Qarp: + return arpwrite(f, a, n); + case Qiproute: + return routewrite(f, ch, a, n); + case Qlog: + netlogctl(f, a, n); + return n; + case Qndb: + return ndbwrite(f, a, off, n); + case Qctl: + x = f->p[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + cb = parsecmd(a, n); + + qlock(c); + if(waserror()) { + qunlock(c); + free(cb); + nexterror(); + } + if(cb->nf < 1) + error("short control request"); + if(strcmp(cb->f[0], "connect") == 0) + connectctlmsg(x, c, cb); + else if(strcmp(cb->f[0], "announce") == 0) + announcectlmsg(x, c, cb); + else if(strcmp(cb->f[0], "bind") == 0) + bindctlmsg(x, c, cb); + else if(strcmp(cb->f[0], "ttl") == 0) + ttlctlmsg(c, cb); + else if(strcmp(cb->f[0], "tos") == 0) + tosctlmsg(c, cb); + else if(strcmp(cb->f[0], "ignoreadvice") == 0) + c->ignoreadvice = 1; + else if(strcmp(cb->f[0], "addmulti") == 0){ + if(cb->nf < 2) + error("addmulti needs interface address"); + if(cb->nf == 2){ + if(!ipismulticast(c->raddr)) + error("addmulti for a non multicast address"); + parseip(ia, cb->f[1]); + ipifcaddmulti(c, c->raddr, ia); + } else { + parseip(ma, cb->f[2]); + if(!ipismulticast(ma)) + error("addmulti for a non multicast address"); + parseip(ia, cb->f[1]); + ipifcaddmulti(c, ma, ia); + } + } else if(strcmp(cb->f[0], "remmulti") == 0){ + if(cb->nf < 2) + error("remmulti needs interface address"); + if(!ipismulticast(c->raddr)) + error("remmulti for a non multicast address"); + parseip(ia, cb->f[1]); + ipifcremmulti(c, c->raddr, ia); + } else if(x->ctl != nil) { + p = x->ctl(c, cb->f, cb->nf); + if(p != nil) + error(p); + } else + error("unknown control request"); + qunlock(c); + free(cb); + poperror(); + } + return n; +} + +static long +ipbwrite(Chan* ch, Block* bp, ulong offset) +{ + Conv *c; + Proto *x; + Fs *f; + int n; + + switch(TYPE(ch->qid)){ + case Qdata: + f = ipfs[ch->dev]; + x = f->p[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + + if(c->wq == nil) + error(Eperm); + + if(bp->next) + bp = concatblock(bp); + n = BLEN(bp); + qbwrite(c->wq, bp); + return n; + default: + return devbwrite(ch, bp, offset); + } +} + +Dev ipdevtab = { + 'I', + "ip", + + ipreset, + devinit, + devshutdown, + ipattach, + ipwalk, + ipstat, + ipopen, + devcreate, + ipclose, + ipread, + ipbread, + ipwrite, + ipbwrite, + devremove, + ipwstat, +}; + +int +Fsproto(Fs *f, Proto *p) +{ + if(f->np >= Maxproto) + return -1; + + p->f = f; + + if(p->ipproto > 0){ + if(f->t2p[p->ipproto] != nil) + return -1; + f->t2p[p->ipproto] = p; + } + + p->qid.type = QTDIR; + p->qid.path = QID(f->np, 0, Qprotodir); + p->conv = malloc(sizeof(Conv*)*(p->nc+1)); + if(p->conv == nil) + panic("Fsproto"); + + p->x = f->np; + p->nextport = 0; + p->nextrport = 600; + f->p[f->np++] = p; + + return 0; +} + +/* + * return true if this protocol is + * built in + */ +int +Fsbuiltinproto(Fs* f, uchar proto) +{ + return f->t2p[proto] != nil; +} + +/* + * called with protocol locked + */ +Conv* +Fsprotoclone(Proto *p, char *user) +{ + Conv *c, **pp, **ep; + +retry: + c = nil; + ep = &p->conv[p->nc]; + for(pp = p->conv; pp < ep; pp++) { + c = *pp; + if(c == nil){ + c = malloc(sizeof(Conv)); + if(c == nil) + error(Enomem); + qlock(c); + c->p = p; + c->x = pp - p->conv; + if(p->ptclsize != 0){ + c->ptcl = malloc(p->ptclsize); + if(c->ptcl == nil) { + free(c); + error(Enomem); + } + } + *pp = c; + p->ac++; + c->eq = qopen(1024, Qmsg, 0, 0); + (*p->create)(c); + break; + } + if(canqlock(c)){ + /* + * make sure both processes and protocol + * are done with this Conv + */ + if(c->inuse == 0 && (p->inuse == nil || (*p->inuse)(c) == 0)) + break; + + qunlock(c); + } + } + if(pp >= ep) { + if(p->gc != nil && (*p->gc)(p)) + goto retry; + return nil; + } + + c->inuse = 1; + kstrdup(&c->owner, user); + c->perm = 0660; + c->state = Idle; + ipmove(c->laddr, IPnoaddr); + ipmove(c->raddr, IPnoaddr); + c->r = nil; + c->rgen = 0; + c->lport = 0; + c->rport = 0; + c->restricted = 0; + c->ttl = MAXTTL; + c->tos = DFLTTOS; + qreopen(c->rq); + qreopen(c->wq); + qreopen(c->eq); + + qunlock(c); + return c; +} + +int +Fsconnected(Conv* c, char* msg) +{ + if(msg != nil && *msg != '\0') + kstrcpy(c->cerr, msg, sizeof(c->cerr)); + + switch(c->state){ + + case Announcing: + c->state = Announced; + break; + + case Connecting: + c->state = Connected; + break; + } + + wakeup(&c->cr); + return 0; +} + +Proto* +Fsrcvpcol(Fs* f, uchar proto) +{ + if(f->ipmux) + return f->ipmux; + else + return f->t2p[proto]; +} + +Proto* +Fsrcvpcolx(Fs *f, uchar proto) +{ + return f->t2p[proto]; +} + +/* + * called with protocol locked + */ +Conv* +Fsnewcall(Conv *c, uchar *raddr, ushort rport, uchar *laddr, ushort lport, uchar version) +{ + Conv *nc; + Conv **l; + int i; + + qlock(c); + i = 0; + for(l = &c->incall; *l; l = &(*l)->next) + i++; + if(i >= Maxincall) { + qunlock(c); + return nil; + } + + /* find a free conversation */ + nc = Fsprotoclone(c->p, network); + if(nc == nil) { + qunlock(c); + return nil; + } + ipmove(nc->raddr, raddr); + nc->rport = rport; + ipmove(nc->laddr, laddr); + nc->lport = lport; + nc->next = nil; + *l = nc; + nc->state = Connected; + nc->ipversion = version; + + qunlock(c); + + wakeup(&c->listenr); + + return nc; +} + +static long +ndbwrite(Fs *f, char *a, ulong off, int n) +{ + if(off > strlen(f->ndb)) + error(Eio); + if(off+n >= sizeof(f->ndb)-1) + error(Eio); + memmove(f->ndb+off, a, n); + f->ndb[off+n] = 0; + f->ndbvers++; + f->ndbmtime = seconds(); + return n; +} + +ulong +scalednconv(void) +{ + if(conf.npage*BY2PG >= 128*MB) + return Nchans*4; + return Nchans; +} diff --git a/os/ip/dhcp.c b/os/ip/dhcp.c new file mode 100644 index 00000000..639e51bb --- /dev/null +++ b/os/ip/dhcp.c @@ -0,0 +1,447 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "kernel.h" +#include "ip.h" +#include "ppp.h" + +Ipaddr pppdns[2]; + +static ulong fsip; +static ulong auip; +static ulong gwip; +static ulong ipmask; +static ulong ipaddr; +static ulong dns1ip; +static ulong dns2ip; + +int dhcpmsgtype; +int debug=0; +enum +{ + Bootrequest = 1, + Bootreply = 2, +}; + +typedef struct Bootp +{ + /* udp.c oldheader */ + uchar raddr[IPaddrlen]; + uchar laddr[IPaddrlen]; + uchar rport[2]; + uchar lport[2]; + /* bootp itself */ + uchar op; /* opcode */ + uchar htype; /* hardware type */ + uchar hlen; /* hardware address len */ + uchar hops; /* hops */ + uchar xid[4]; /* a random number */ + uchar secs[2]; /* elapsed snce client started booting */ + uchar flags[2]; /* flags */ + uchar ciaddr[4]; /* client IP address (client tells server) */ + uchar yiaddr[4]; /* client IP address (server tells client) */ + uchar siaddr[4]; /* server IP address */ + uchar giaddr[4]; /* gateway IP address */ + uchar chaddr[16]; /* client hardware address */ + uchar sname[64]; /* server host name (optional) */ + uchar file[128]; /* boot file name */ + uchar vend[128]; /* vendor-specific goo 340 */ +} Bootp; + +static Bootp req; +static Proc* rcvprocp; +static int recv; +static int done; +static Rendez bootpr; +static char rcvbuf[512+2*IPaddrlen+2*2]; /* 576 */ +static uchar sid[4]; +static ulong iplease; + +/* + * bootp returns: + * + * "fsip d.d.d.d + * auip d.d.d.d + * gwip d.d.d.d + * ipmask d.d.d.d + * ipaddr d.d.d.d + * dns1ip d.d.d.d + * dns2ip d.d.d.d + * + * where d.d.d.d is the IP address in dotted decimal notation, and each + * address is followed by a newline. + Last change: SUN 13 Sep 2001 4:36 pm + */ + +/* + * Parse the vendor specific fields according to RFC 1084. + * We are overloading the "cookie server" to be the Inferno + * authentication server and the "resource location server" + * to be the Inferno file server. + * + * If the vendor specific field is formatted properly, it + * will being with the four bytes 99.130.83.99 and end with + * an 0xFF byte. + */ +static int +parsevend(uchar* pvend) +{ + uchar *vend=pvend; + int dhcpmsg=0; + /* The field must start with 99.130.83.99 to be compliant */ + if ((vend[0] != 99) || (vend[1] != 130) || (vend[2] != 83) || (vend[3] != 99)){ + print("bad bootp vendor field: %.2x%.2x%.2x%.2x", vend[0], vend[1], vend[2], vend[3]); + return -1; + } + + /* Skip over the magic cookie */ + vend += 4; + + while ((vend[0] != 0) && (vend[0] != 0xFF)) { + int i; +// + if(debug){ + print(">>>Opt[%d] [%d]", vend[0], vend[1]); + for(i=0; i0 && vend[1]%4==0) + gwip = (vend[2]<<24)|(vend[3]<<16)|(vend[4]<<8)|vend[5]; + else + return -1; + break; + case 6: /* domain name server */ + if(vend[1]>0 && vend[1] %4==0){ + dns1ip=(vend[2]<<24)|(vend[3]<<16)|(vend[4]<<8)|vend[5]; + if(vend[1]>4) + dns2ip=(vend[6]<<24)|(vend[7]<<16)|(vend[8]<<8)|vend[9]; + }else + return -1; + break; + + case 8: /* "Cookie server" (auth server) field */ + /* We are only concerned with first address */ + if (vend[1] > 0 && vend[1]%4==0) + auip = (vend[2]<<24)|(vend[3]<<16)|(vend[4]<<8)|vend[5]; + else + return -1; + break; + + case 11: /* "Resource loc server" (file server) field */ + /* We are only concerned with first address */ + if (vend[1] > 0 && vend[1]%4==0) + fsip = (vend[2]<<24)| (vend[3]<<16)| (vend[4]<<8)| vend[5]; + else + return -1; + break; + case 51: /* ip lease time */ + if(vend[1]==4){ + iplease=(vend[2]<<24)|(vend[3]<<16)|(vend[4]<<8)|vend[5]; + }else + return -1; + break; + case 53: /* DHCP message type */ + if(vend[1]==1) + dhcpmsg=vend[2]; + else + return -1; + break; + case 54: /* server identifier */ + if(vend[1]==4){ + memmove(sid, vend+2, 4); + }else + return -1; + break; + + default: /* Everything else stops us */ + break; + } + + /* Skip over the field */ + vend += vend[1] + 2; + } + if(debug) + print(">>>Opt[%d] [%d]\n", vend[0], vend[1]); + return dhcpmsg; +} + +static void +dispvend(uchar* pvend) +{ + uchar *vend=pvend; + + //print("<<chaddr, 6) == 0 && rp->htype == 1 && rp->hlen == 6) { + ipaddr = (rp->yiaddr[0]<<24)| (rp->yiaddr[1]<<16)| (rp->yiaddr[2]<<8)| rp->yiaddr[3]; + if(debug) + print("ipaddr = %2.2x %2.2x %2.2x %2.2x \n", rp->yiaddr[0], rp->yiaddr[1], rp->yiaddr[2], rp->yiaddr[3]); + //memmove(req.siaddr, rp->siaddr, 4); /* siaddr */ + dhcp = parsevend(rp->vend); + + if(dhcpmsgtype < dhcp){ + dhcpmsgtype=dhcp; + recv = 1; + wakeup(&bootpr); + if(dhcp==0 || dhcp ==5 || dhcp == 6 ) + break; + } + } + } + poperror(); + rcvprocp = nil; + + if(debug) + print("rcvbootp exit\n"); + pexit("", 0); +} + +static char* +rbootp(Ipifc *ifc) +{ + int cfd, dfd, tries, n; + char ia[5+3*16], im[16], *av[3]; + uchar nipaddr[4], ngwip[4], nipmask[4]; + char dir[Maxpath]; + static uchar vend_rfc1048[] = { 99, 130, 83, 99 }; + uchar *vend; + + /* + * broadcast bootp's till we get a reply, + * or fixed number of tries + */ + if(debug) + print("dhcp: bootp() called\n"); + tries = 0; + av[1] = "0.0.0.0"; + av[2] = "0.0.0.0"; + ipifcadd(ifc, av, 3, 0, nil); + + cfd = kannounce("udp!*!68", dir); + if(cfd < 0) + return "dhcp announce failed"; + strcat(dir, "/data"); + if(kwrite(cfd, "headers", 7) < 0){ + kclose(cfd); + return "dhcp ctl headers failed"; + } + kwrite(cfd, "oldheaders", 10); + dfd = kopen(dir, ORDWR); + if(dfd < 0){ + kclose(cfd); + return "dhcp open data failed"; + } + kclose(cfd); + + while(tries<1){ + tries++; + memset(sid, 0, 4); + iplease=0; + dhcpmsgtype=-2; +/* DHCPDISCOVER*/ + done = 0; + recv = 0; + kproc("rcvbootp", rcvbootp, (void*)dfd, KPDUPFDG); + /* Prepare DHCPDISCOVER */ + memset(&req, 0, sizeof(req)); + ipmove(req.raddr, IPv4bcast); + hnputs(req.rport, 67); + req.op = Bootrequest; + req.htype = 1; /* ethernet (all we know) */ + req.hlen = 6; /* ethernet (all we know) */ + + memmove(req.chaddr, ifc->mac, 6); /* Hardware MAC address */ + //ipv4local(ifc, req.ciaddr); /* Fill in the local IP address if we know it */ + memset(req.file, 0, sizeof(req.file)); + vend=req.vend; + memmove(vend, vend_rfc1048, 4); vend+=4; + *vend++=53; *vend++=1;*vend++=1; /* dhcp msg type==3, dhcprequest */ + + *vend++=61;*vend++=7;*vend++=1; + memmove(vend, ifc->mac, 6);vend+=6; + *vend=0xff; + + if(debug) + dispvend(req.vend); + for(n=0;n<4;n++){ + if(kwrite(dfd, &req, sizeof(req))<0) /* SEND DHCPDISCOVER */ + print("DHCPDISCOVER: %r"); + + tsleep(&bootpr, return0, 0, 1000); /* wait DHCPOFFER */ + if(debug) + print("[DHCP] DISCOVER: msgtype = %d\n", dhcpmsgtype); + + if(dhcpmsgtype==2) /* DHCPOFFER */ + break; + else if(dhcpmsgtype==0) /* bootp */ + return nil; + else if(dhcpmsgtype== -2) /* time out */ + continue; + else + break; + + } + if(dhcpmsgtype!=2) + continue; + +/* DHCPREQUEST */ + memset(req.vend, 0, sizeof(req.vend)); + vend=req.vend; + memmove(vend, vend_rfc1048, 4);vend+=4; + + *vend++=53; *vend++=1;*vend++=3; /* dhcp msg type==3, dhcprequest */ + + *vend++=50; *vend++=4; /* requested ip address */ + *vend++=(ipaddr >> 24)&0xff; + *vend++=(ipaddr >> 16)&0xff; + *vend++=(ipaddr >> 8) & 0xff; + *vend++=ipaddr & 0xff; + + *vend++=51;*vend++=4; /* lease time */ + *vend++=(iplease>>24)&0xff; *vend++=(iplease>>16)&0xff; *vend++=(iplease>>8)&0xff; *vend++=iplease&0xff; + + *vend++=54; *vend++=4; /* server identifier */ + memmove(vend, sid, 4); vend+=4; + + *vend++=61;*vend++=07;*vend++=01; /* client identifier */ + memmove(vend, ifc->mac, 6);vend+=6; + *vend=0xff; + if(debug) + dispvend(req.vend); + if(kwrite(dfd, &req, sizeof(req))<0){ + print("DHCPREQUEST: %r"); + continue; + } + tsleep(&bootpr, return0, 0, 2000); + if(dhcpmsgtype==5) /* wait for DHCPACK */ + break; + else + continue; + /* CHECK ARP */ + /* DHCPDECLINE */ + } + kclose(dfd); + done = 1; + if(rcvprocp != nil){ + postnote(rcvprocp, 1, "timeout", 0); + rcvprocp = nil; + } + + av[1] = "0.0.0.0"; + av[2] = "0.0.0.0"; + ipifcrem(ifc, av, 3); + + hnputl(nipaddr, ipaddr); + sprint(ia, "%V", nipaddr); + hnputl(nipmask, ipmask); + sprint(im, "%V", nipmask); + av[1] = ia; + av[2] = im; + ipifcadd(ifc, av, 3, 0, nil); + + if(gwip != 0) { + hnputl(ngwip, gwip); + n = sprint(ia, "add 0.0.0.0 0.0.0.0 %V", ngwip); + routewrite(ifc->conv->p->f, nil, ia, n); + } + return nil; +} + +static int +rbootpread(char *bp, ulong offset, int len) +{ + int n, i; + char *buf; + uchar a[4]; + + if(debug) + print("dhcp: bootpread() \n"); + buf = smalloc(READSTR); + if(waserror()){ + free(buf); + nexterror(); + } + + hnputl(a, fsip); + n = snprint(buf, READSTR, "fsip %15V\n", a); + hnputl(a, auip); + n += snprint(buf + n, READSTR-n, "auip %15V\n", a); + hnputl(a, gwip); + n += snprint(buf + n, READSTR-n, "gwip %15V\n", a); + hnputl(a, ipmask); + n += snprint(buf + n, READSTR-n, "ipmask %15V\n", a); + hnputl(a, ipaddr); + n += snprint(buf + n, READSTR-n, "ipaddr %15V\n", a); + n += snprint(buf+n, READSTR-n, "expired %lud\n", iplease); + + n += snprint(buf + n, READSTR-n, "dns"); + if(dns2ip){ + hnputl(a, dns2ip); + n+=snprint(buf + n, READSTR-n, " %15V", a); + } + if(dns1ip){ + hnputl(a, dns1ip); + n += snprint(buf + n, READSTR-n, " %15V", a); + } + + for(i=0; i<2; i++) + if(ipcmp(pppdns[i], IPnoaddr) != 0 && ipcmp(pppdns[i], v4prefix) != 0) + n += snprint(buf + n, READSTR-n, " %15I", pppdns[i]); + + snprint(buf + n, READSTR-n, "\n"); + len = readstr(offset, bp, len, buf); + poperror(); + free(buf); + return len; +} + +char* (*bootp)(Ipifc*) = rbootp; +int (*bootpread)(char*, ulong, int) = rbootpread; diff --git a/os/ip/eipconvtest.c b/os/ip/eipconvtest.c new file mode 100644 index 00000000..06b0f9b5 --- /dev/null +++ b/os/ip/eipconvtest.c @@ -0,0 +1,152 @@ +#include +#include + +enum +{ + Isprefix= 16, +}; + +uchar prefixvals[256] = +{ +[0x00] 0 | Isprefix, +[0x80] 1 | Isprefix, +[0xC0] 2 | Isprefix, +[0xE0] 3 | Isprefix, +[0xF0] 4 | Isprefix, +[0xF8] 5 | Isprefix, +[0xFC] 6 | Isprefix, +[0xFE] 7 | Isprefix, +[0xFF] 8 | Isprefix, +}; + +uchar v4prefix[16] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0xff, 0xff, + 0, 0, 0, 0 +}; + +void +hnputl(void *p, ulong v) +{ + uchar *a; + + a = p; + a[0] = v>>24; + a[1] = v>>16; + a[2] = v>>8; + a[3] = v; +} + +int +eipconv(va_list *arg, Fconv *f) +{ + char buf[8*5]; + static char *efmt = "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux"; + static char *ifmt = "%d.%d.%d.%d"; + uchar *p, ip[16]; + ulong *lp; + ushort s; + int i, j, n, eln, eli; + + switch(f->chr) { + case 'E': /* Ethernet address */ + p = va_arg(*arg, uchar*); + sprint(buf, efmt, p[0], p[1], p[2], p[3], p[4], p[5]); + break; + case 'I': /* Ip address */ + p = va_arg(*arg, uchar*); +common: + if(memcmp(p, v4prefix, 12) == 0) + sprint(buf, ifmt, p[12], p[13], p[14], p[15]); + else { + /* find longest elision */ + eln = eli = -1; + for(i = 0; i < 16; i += 2){ + for(j = i; j < 16; j += 2) + if(p[j] != 0 || p[j+1] != 0) + break; + if(j > i && j - i > eln){ + eli = i; + eln = j - i; + } + } + + /* print with possible elision */ + n = 0; + for(i = 0; i < 16; i += 2){ + if(i == eli){ + n += sprint(buf+n, "::"); + i += eln; + if(i >= 16) + break; + } else if(i != 0) + n += sprint(buf+n, ":"); + s = (p[i]<<8) + p[i+1]; + n += sprint(buf+n, "%ux", s); + } + } + break; + case 'i': /* v6 address as 4 longs */ + lp = va_arg(*arg, ulong*); + for(i = 0; i < 4; i++) + hnputl(ip+4*i, *lp++); + p = ip; + goto common; + case 'V': /* v4 ip address */ + p = va_arg(*arg, uchar*); + sprint(buf, ifmt, p[0], p[1], p[2], p[3]); + break; + case 'M': /* ip mask */ + p = va_arg(*arg, uchar*); + + /* look for a prefix mask */ + for(i = 0; i < 16; i++) + if(p[i] != 0xff) + break; + if(i < 16){ + if((prefixvals[p[i]] & Isprefix) == 0) + goto common; + for(j = i+1; j < 16; j++) + if(p[j] != 0) + goto common; + n = 8*i + (prefixvals[p[i]] & ~Isprefix); + } else + n = 8*16; + + /* got one, use /xx format */ + sprint(buf, "/%d", n); + break; + default: + strcpy(buf, "(eipconv)"); + } + strconv(buf, f); + return sizeof(uchar*); +} + +uchar testvec[11][16] = +{ + { 0,0,0,0, 0,0,0,0, 0,0,0xff,0xff, 1,3,4,5, }, + { 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, }, + { 0xff,0xff,0x80,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, }, + { 0xff,0xff,0xff,0xc0, 0,0,0,0, 0,0,0,0, 0,0,0,0, }, + { 0xff,0xff,0xff,0xff, 0xe0,0,0,0, 0,0,0,0, 0,0,0,0, }, + { 0xff,0xff,0xff,0xff, 0xff,0xf0,0,0, 0,0,0,0, 0,0,0,0, }, + { 0xff,0xff,0xff,0xff, 0xff,0xff,0xf8,0, 0,0,0,0, 0,0,0,0, }, + { 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, }, + { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, }, + { 0,0,0,0, 0,0x11,0,0, 0,0,0,0, 0,0,0,0, }, + { 0,0,0,0x11, 0,0,0,0, 0,0,0,0, 0,0,0,0x12, }, +}; + +void +main(void) +{ + int i; + + fmtinstall('I', eipconv); + fmtinstall('M', eipconv); + for(i = 0; i < 11; i++) + print("%I\n%M\n", testvec[i], testvec[i]); + exits(0); +} diff --git a/os/ip/esp.c b/os/ip/esp.c new file mode 100644 index 00000000..9c9f33f8 --- /dev/null +++ b/os/ip/esp.c @@ -0,0 +1,866 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" + +#include "libsec.h" + +typedef struct Esphdr Esphdr; +typedef struct Esptail Esptail; +typedef struct Userhdr Userhdr; +typedef struct Esppriv Esppriv; +typedef struct Espcb Espcb; +typedef struct Algorithm Algorithm; +typedef struct Esprc4 Esprc4; + +#define DPRINT if(0)print + +enum +{ + IP_ESPPROTO = 50, + EsphdrSize = 28, // includes IP header + IphdrSize = 20, // options have been striped + EsptailSize = 2, // does not include pad or auth data + UserhdrSize = 4, // user visable header size - if enabled +}; + +struct Esphdr +{ + /* ip header */ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar Unused; + uchar espproto; /* Protocol */ + uchar espplen[2]; /* Header plus data length */ + uchar espsrc[4]; /* Ip source */ + uchar espdst[4]; /* Ip destination */ + + /* esp header */ + uchar espspi[4]; /* Security parameter index */ + uchar espseq[4]; /* Sequence number */ +}; + +struct Esptail +{ + uchar pad; + uchar nexthdr; +}; + +/* header as seen by the user */ +struct Userhdr +{ + uchar nexthdr; // next protocol + uchar unused[3]; +}; + +struct Esppriv +{ + ulong in; + ulong inerrors; +}; + +/* + * protocol specific part of Conv + */ +struct Espcb +{ + int incoming; + int header; // user user level header + ulong spi; + ulong seq; // last seq sent + ulong window; // for replay attacks + char *espalg; + void *espstate; // other state for esp + int espivlen; // in bytes + int espblklen; + int (*cipher)(Espcb*, uchar *buf, int len); + char *ahalg; + void *ahstate; // other state for esp + int ahlen; // auth data length in bytes + int ahblklen; + int (*auth)(Espcb*, uchar *buf, int len, uchar *hash); +}; + +struct Algorithm +{ + char *name; + int keylen; // in bits + void (*init)(Espcb*, char* name, uchar *key, int keylen); +}; + + +enum { + RC4forward = 10*1024*1024, // maximum skip forward + RC4back = 100*1024, // maximum look back +}; + +struct Esprc4 +{ + ulong cseq; // current byte sequence number + RC4state current; + + int ovalid; // old is valid + ulong lgseq; // last good sequence + ulong oseq; // old byte sequence number + RC4state old; +}; + +static Conv* convlookup(Proto *esp, ulong spi); +static char *setalg(Espcb *ecb, char **f, int n, Algorithm *alg); +static void nullespinit(Espcb*, char*, uchar *key, int keylen); +static void nullahinit(Espcb*, char*, uchar *key, int keylen); +static void shaahinit(Espcb*, char*, uchar *key, int keylen); +static void md5ahinit(Espcb*, char*, uchar *key, int keylen); +static void desespinit(Espcb *ecb, char *name, uchar *k, int n); +static void rc4espinit(Espcb *ecb, char *name, uchar *k, int n); +static void espkick(void *x); + +static Algorithm espalg[] = +{ + "null", 0, nullespinit, + "des_56_cbc", 64, desespinit, + "rc4_128", 128, rc4espinit, + nil, 0, nil, +}; + +static Algorithm ahalg[] = +{ + "null", 0, nullahinit, + "hmac_sha1_96", 128, shaahinit, + "hmac_md5_96", 128, md5ahinit, + nil, 0, nil, +}; + +static char* +espconnect(Conv *c, char **argv, int argc) +{ + char *p, *pp; + char *e = nil; + ulong spi; + Espcb *ecb = (Espcb*)c->ptcl; + + switch(argc) { + default: + e = "bad args to connect"; + break; + case 2: + p = strchr(argv[1], '!'); + if(p == nil){ + e = "malformed address"; + break; + } + *p++ = 0; + parseip(c->raddr, argv[1]); + findlocalip(c->p->f, c->laddr, c->raddr); + ecb->incoming = 0; + ecb->seq = 0; + if(strcmp(p, "*") == 0) { + qlock(c->p); + for(;;) { + spi = nrand(1<<16) + 256; + if(convlookup(c->p, spi) == nil) + break; + } + qunlock(c->p); + ecb->spi = spi; + ecb->incoming = 1; + qhangup(c->wq, nil); + } else { + spi = strtoul(p, &pp, 10); + if(pp == p) { + e = "malformed address"; + break; + } + ecb->spi = spi; + qhangup(c->rq, nil); + } + nullespinit(ecb, "null", nil, 0); + nullahinit(ecb, "null", nil, 0); + } + Fsconnected(c, e); + + return e; +} + + +static int +espstate(Conv *c, char *state, int n) +{ + return snprint(state, n, "%s", c->inuse?"Open\n":"Closed\n"); +} + +static void +espcreate(Conv *c) +{ + c->rq = qopen(64*1024, Qmsg, 0, 0); + c->wq = qopen(64*1024, Qkick, espkick, c); +} + +static void +espclose(Conv *c) +{ + Espcb *ecb; + + qclose(c->rq); + qclose(c->wq); + qclose(c->eq); + ipmove(c->laddr, IPnoaddr); + ipmove(c->raddr, IPnoaddr); + + ecb = (Espcb*)c->ptcl; + free(ecb->espstate); + free(ecb->ahstate); + memset(ecb, 0, sizeof(Espcb)); +} + +static void +espkick(void *x) +{ + Conv *c = x; + Esphdr *eh; + Esptail *et; + Userhdr *uh; + Espcb *ecb; + Block *bp; + int nexthdr; + int payload; + int pad; + int align; + uchar *auth; + + bp = qget(c->wq); + if(bp == nil) + return; + + qlock(c); + ecb = c->ptcl; + + if(ecb->header) { + /* make sure the message has a User header */ + bp = pullupblock(bp, UserhdrSize); + if(bp == nil) { + qunlock(c); + return; + } + uh = (Userhdr*)bp->rp; + nexthdr = uh->nexthdr; + bp->rp += UserhdrSize; + } else { + nexthdr = 0; // what should this be? + } + + payload = BLEN(bp) + ecb->espivlen; + + /* Make space to fit ip header */ + bp = padblock(bp, EsphdrSize + ecb->espivlen); + + align = 4; + if(ecb->espblklen > align) + align = ecb->espblklen; + if(align % ecb->ahblklen != 0) + panic("espkick: ahblklen is important after all"); + pad = (align-1) - (payload + EsptailSize-1)%align; + + /* + * Make space for tail + * this is done by calling padblock with a negative size + * Padblock does not change bp->wp! + */ + bp = padblock(bp, -(pad+EsptailSize+ecb->ahlen)); + bp->wp += pad+EsptailSize+ecb->ahlen; + + eh = (Esphdr *)(bp->rp); + et = (Esptail*)(bp->rp + EsphdrSize + payload + pad); + + // fill in tail + et->pad = pad; + et->nexthdr = nexthdr; + + ecb->cipher(ecb, bp->rp+EsphdrSize, payload+pad+EsptailSize); + auth = bp->rp + EsphdrSize + payload + pad + EsptailSize; + + // fill in head + eh->vihl = IP_VER4; + hnputl(eh->espspi, ecb->spi); + hnputl(eh->espseq, ++ecb->seq); + v6tov4(eh->espsrc, c->laddr); + v6tov4(eh->espdst, c->raddr); + eh->espproto = IP_ESPPROTO; + eh->frag[0] = 0; + eh->frag[1] = 0; + + ecb->auth(ecb, bp->rp+IphdrSize, (EsphdrSize-IphdrSize)+payload+pad+EsptailSize, auth); + + qunlock(c); + //print("esp: pass down: %uld\n", BLEN(bp)); + ipoput4(c->p->f, bp, 0, c->ttl, c->tos, c); +} + +void +espiput(Proto *esp, Ipifc*, Block *bp) +{ + Esphdr *eh; + Esptail *et; + Userhdr *uh; + Conv *c; + Espcb *ecb; + uchar raddr[IPaddrlen], laddr[IPaddrlen]; + Fs *f; + uchar *auth; + ulong spi; + int payload, nexthdr; + + f = esp->f; + + bp = pullupblock(bp, EsphdrSize+EsptailSize); + if(bp == nil) { + netlog(f, Logesp, "esp: short packet\n"); + return; + } + + eh = (Esphdr*)(bp->rp); + spi = nhgetl(eh->espspi); + v4tov6(raddr, eh->espsrc); + v4tov6(laddr, eh->espdst); + + qlock(esp); + /* Look for a conversation structure for this port */ + c = convlookup(esp, spi); + if(c == nil) { + qunlock(esp); + netlog(f, Logesp, "esp: no conv %I -> %I!%d\n", raddr, + laddr, spi); + icmpnoconv(f, bp); + freeblist(bp); + return; + } + + qlock(c); + qunlock(esp); + + ecb = c->ptcl; + // too hard to do decryption/authentication on block lists + if(bp->next) + bp = concatblock(bp); + + if(BLEN(bp) < EsphdrSize + ecb->espivlen + EsptailSize + ecb->ahlen) { + qunlock(c); + netlog(f, Logesp, "esp: short block %I -> %I!%d\n", raddr, + laddr, spi); + freeb(bp); + return; + } + + eh = (Esphdr*)(bp->rp); + auth = bp->wp - ecb->ahlen; + if(!ecb->auth(ecb, eh->espspi, auth-eh->espspi, auth)) { + qunlock(c); +print("esp: bad auth %I -> %I!%ld\n", raddr, laddr, spi); + netlog(f, Logesp, "esp: bad auth %I -> %I!%d\n", raddr, + laddr, spi); + freeb(bp); + return; + } + + payload = BLEN(bp)-EsphdrSize-ecb->ahlen; + if(payload<=0 || payload%4 != 0 || payload%ecb->espblklen!=0) { + qunlock(c); + netlog(f, Logesp, "esp: bad length %I -> %I!%d payload=%d BLEN=%d\n", raddr, + laddr, spi, payload, BLEN(bp)); + freeb(bp); + return; + } + if(!ecb->cipher(ecb, bp->rp+EsphdrSize, payload)) { + qunlock(c); +print("esp: cipher failed %I -> %I!%ld: %r\n", raddr, laddr, spi); + netlog(f, Logesp, "esp: cipher failed %I -> %I!%d: %r\n", raddr, + laddr, spi); + freeb(bp); + return; + } + + payload -= EsptailSize; + et = (Esptail*)(bp->rp + EsphdrSize + payload); + payload -= et->pad + ecb->espivlen; + nexthdr = et->nexthdr; + if(payload <= 0) { + qunlock(c); + netlog(f, Logesp, "esp: short packet after decrypt %I -> %I!%d\n", raddr, + laddr, spi); + freeb(bp); + return; + } + + // trim packet + bp->rp += EsphdrSize + ecb->espivlen; + bp->wp = bp->rp + payload; + if(ecb->header) { + // assume UserhdrSize < EsphdrSize + bp->rp -= UserhdrSize; + uh = (Userhdr*)bp->rp; + memset(uh, 0, UserhdrSize); + uh->nexthdr = nexthdr; + } + + if(qfull(c->rq)){ + netlog(f, Logesp, "esp: qfull %I -> %I.%uld\n", raddr, + laddr, spi); + freeblist(bp); + }else { +//print("esp: pass up: %uld\n", BLEN(bp)); + qpass(c->rq, bp); + } + + qunlock(c); +} + +char* +espctl(Conv *c, char **f, int n) +{ + Espcb *ecb = c->ptcl; + char *e = nil; + + if(strcmp(f[0], "esp") == 0) + e = setalg(ecb, f, n, espalg); + else if(strcmp(f[0], "ah") == 0) + e = setalg(ecb, f, n, ahalg); + else if(strcmp(f[0], "header") == 0) + ecb->header = 1; + else if(strcmp(f[0], "noheader") == 0) + ecb->header = 0; + else + e = "unknown control request"; + return e; +} + +void +espadvise(Proto *esp, Block *bp, char *msg) +{ + Esphdr *h; + Conv *c; + ulong spi; + + h = (Esphdr*)(bp->rp); + + spi = nhgets(h->espspi); + qlock(esp); + c = convlookup(esp, spi); + if(c != nil) { + qhangup(c->rq, msg); + qhangup(c->wq, msg); + } + qunlock(esp); + freeblist(bp); +} + +int +espstats(Proto *esp, char *buf, int len) +{ + Esppriv *upriv; + + upriv = esp->priv; + return snprint(buf, len, "%lud %lud\n", + upriv->in, + upriv->inerrors); +} + +static int +esplocal(Conv *c, char *buf, int len) +{ + Espcb *ecb = c->ptcl; + int n; + + qlock(c); + if(ecb->incoming) + n = snprint(buf, len, "%I!%uld\n", c->laddr, ecb->spi); + else + n = snprint(buf, len, "%I\n", c->laddr); + qunlock(c); + return n; +} + +static int +espremote(Conv *c, char *buf, int len) +{ + Espcb *ecb = c->ptcl; + int n; + + qlock(c); + if(ecb->incoming) + n = snprint(buf, len, "%I\n", c->raddr); + else + n = snprint(buf, len, "%I!%uld\n", c->raddr, ecb->spi); + qunlock(c); + return n; +} + +static Conv* +convlookup(Proto *esp, ulong spi) +{ + Conv *c, **p; + Espcb *ecb; + + for(p=esp->conv; *p; p++){ + c = *p; + ecb = c->ptcl; + if(ecb->incoming && ecb->spi == spi) + return c; + } + return nil; +} + +static char * +setalg(Espcb *ecb, char **f, int n, Algorithm *alg) +{ + uchar *key; + int i, nbyte, nchar; + int c; + + if(n < 2) + return "bad format"; + for(; alg->name; alg++) + if(strcmp(f[1], alg->name) == 0) + break; + if(alg->name == nil) + return "unknown algorithm"; + + if(n != 3) + return "bad format"; + nbyte = (alg->keylen + 7) >> 3; + nchar = strlen(f[2]); + for(i=0; i= '0' && c <= '9') + f[2][i] -= '0'; + else if(c >= 'a' && c <= 'f') + f[2][i] -= 'a'-10; + else if(c >= 'A' && c <= 'F') + f[2][i] -= 'A'-10; + else + return "bad character in key"; + } + key = smalloc(nbyte); + for(i=0; i>1] |= c; + } + + alg->init(ecb, alg->name, key, alg->keylen); + free(key); + return nil; +} + +static int +nullcipher(Espcb*, uchar*, int) +{ + return 1; +} + +static void +nullespinit(Espcb *ecb, char *name, uchar*, int) +{ + ecb->espalg = name; + ecb->espblklen = 1; + ecb->espivlen = 0; + ecb->cipher = nullcipher; +} + +static int +nullauth(Espcb*, uchar*, int, uchar*) +{ + return 1; +} + +static void +nullahinit(Espcb *ecb, char *name, uchar*, int) +{ + ecb->ahalg = name; + ecb->ahblklen = 1; + ecb->ahlen = 0; + ecb->auth = nullauth; +} + +void +seanq_hmac_sha1(uchar hash[SHA1dlen], uchar *t, long tlen, uchar *key, long klen) +{ + uchar ipad[65], opad[65]; + int i; + DigestState *digest; + uchar innerhash[SHA1dlen]; + + for(i=0; i<64; i++){ + ipad[i] = 0x36; + opad[i] = 0x5c; + } + ipad[64] = opad[64] = 0; + for(i=0; iahstate, 16); + r = memcmp(auth, hash, ecb->ahlen) == 0; + memmove(auth, hash, ecb->ahlen); + return r; +} + +static void +shaahinit(Espcb *ecb, char *name, uchar *key, int klen) +{ + if(klen != 128) + panic("shaahinit: bad keylen"); + klen >>= 8; // convert to bytes + + ecb->ahalg = name; + ecb->ahblklen = 1; + ecb->ahlen = 12; + ecb->auth = shaauth; + ecb->ahstate = smalloc(klen); + memmove(ecb->ahstate, key, klen); +} + +void +seanq_hmac_md5(uchar hash[MD5dlen], uchar *t, long tlen, uchar *key, long klen) +{ + uchar ipad[65], opad[65]; + int i; + DigestState *digest; + uchar innerhash[MD5dlen]; + + for(i=0; i<64; i++){ + ipad[i] = 0x36; + opad[i] = 0x5c; + } + ipad[64] = opad[64] = 0; + for(i=0; iahstate, 16); + r = memcmp(auth, hash, ecb->ahlen) == 0; + memmove(auth, hash, ecb->ahlen); + return r; +} + +static void +md5ahinit(Espcb *ecb, char *name, uchar *key, int klen) +{ + if(klen != 128) + panic("md5ahinit: bad keylen"); + klen >>= 3; // convert to bytes + + + ecb->ahalg = name; + ecb->ahblklen = 1; + ecb->ahlen = 12; + ecb->auth = md5auth; + ecb->ahstate = smalloc(klen); + memmove(ecb->ahstate, key, klen); +} + +static int +descipher(Espcb *ecb, uchar *p, int n) +{ + uchar tmp[8]; + uchar *pp, *tp, *ip, *eip, *ep; + DESstate *ds = ecb->espstate; + + ep = p + n; + if(ecb->incoming) { + memmove(ds->ivec, p, 8); + p += 8; + while(p < ep){ + memmove(tmp, p, 8); + block_cipher(ds->expanded, p, 1); + tp = tmp; + ip = ds->ivec; + for(eip = ip+8; ip < eip; ){ + *p++ ^= *ip; + *ip++ = *tp++; + } + } + } else { + memmove(p, ds->ivec, 8); + for(p += 8; p < ep; p += 8){ + pp = p; + ip = ds->ivec; + for(eip = ip+8; ip < eip; ) + *pp++ ^= *ip++; + block_cipher(ds->expanded, p, 0); + memmove(ds->ivec, p, 8); + } + } + return 1; +} + +static void +desespinit(Espcb *ecb, char *name, uchar *k, int n) +{ + uchar key[8]; + uchar ivec[8]; + int i; + + // bits to bytes + n = (n+7)>>3; + if(n > 8) + n = 8; + memset(key, 0, sizeof(key)); + memmove(key, k, n); + for(i=0; i<8; i++) + ivec[i] = nrand(256); + ecb->espalg = name; + ecb->espblklen = 8; + ecb->espivlen = 8; + ecb->cipher = descipher; + ecb->espstate = smalloc(sizeof(DESstate)); + setupDESstate(ecb->espstate, key, ivec); +} + +static int +rc4cipher(Espcb *ecb, uchar *p, int n) +{ + Esprc4 *esprc4; + RC4state tmpstate; + ulong seq; + long d, dd; + + if(n < 4) + return 0; + + esprc4 = ecb->espstate; + if(ecb->incoming) { + seq = nhgetl(p); + p += 4; + n -= 4; + d = seq-esprc4->cseq; + if(d == 0) { + rc4(&esprc4->current, p, n); + esprc4->cseq += n; + if(esprc4->ovalid) { + dd = esprc4->cseq - esprc4->lgseq; + if(dd > RC4back) + esprc4->ovalid = 0; + } + } else if(d > 0) { +print("missing packet: %uld %ld\n", seq, d); + // this link is hosed + if(d > RC4forward) { + strcpy(up->errstr, "rc4cipher: skipped too much"); + return 0; + } + esprc4->lgseq = seq; + if(!esprc4->ovalid) { + esprc4->ovalid = 1; + esprc4->oseq = esprc4->cseq; + memmove(&esprc4->old, &esprc4->current, sizeof(RC4state)); + } + rc4skip(&esprc4->current, d); + rc4(&esprc4->current, p, n); + esprc4->cseq = seq+n; + } else { +print("reordered packet: %uld %ld\n", seq, d); + dd = seq - esprc4->oseq; + if(!esprc4->ovalid || -d > RC4back || dd < 0) { + strcpy(up->errstr, "rc4cipher: too far back"); + return 0; + } + memmove(&tmpstate, &esprc4->old, sizeof(RC4state)); + rc4skip(&tmpstate, dd); + rc4(&tmpstate, p, n); + return 1; + } + + // move old state up + if(esprc4->ovalid) { + dd = esprc4->cseq - RC4back - esprc4->oseq; + if(dd > 0) { + rc4skip(&esprc4->old, dd); + esprc4->oseq += dd; + } + } + } else { + hnputl(p, esprc4->cseq); + p += 4; + n -= 4; + rc4(&esprc4->current, p, n); + esprc4->cseq += n; + } + return 1; +} + +static void +rc4espinit(Espcb *ecb, char *name, uchar *k, int n) +{ + Esprc4 *esprc4; + + // bits to bytes + n = (n+7)>>3; + esprc4 = smalloc(sizeof(Esprc4)); + memset(esprc4, 0, sizeof(Esprc4)); + setupRC4state(&esprc4->current, k, n); + ecb->espalg = name; + ecb->espblklen = 4; + ecb->espivlen = 4; + ecb->cipher = rc4cipher; + ecb->espstate = esprc4; +} + +void +espinit(Fs *fs) +{ + Proto *esp; + + esp = smalloc(sizeof(Proto)); + esp->priv = smalloc(sizeof(Esppriv)); + esp->name = "esp"; + esp->connect = espconnect; + esp->announce = nil; + esp->ctl = espctl; + esp->state = espstate; + esp->create = espcreate; + esp->close = espclose; + esp->rcv = espiput; + esp->advise = espadvise; + esp->stats = espstats; + esp->local = esplocal; + esp->remote = espremote; + esp->ipproto = IP_ESPPROTO; + esp->nc = Nchans; + esp->ptclsize = sizeof(Espcb); + + Fsproto(fs, esp); +} diff --git a/os/ip/ethermedium.c b/os/ip/ethermedium.c new file mode 100644 index 00000000..18778176 --- /dev/null +++ b/os/ip/ethermedium.c @@ -0,0 +1,792 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" +#include "ipv6.h" +#include "kernel.h" + +typedef struct Etherhdr Etherhdr; +struct Etherhdr +{ + uchar d[6]; + uchar s[6]; + uchar t[2]; +}; + +static uchar ipbroadcast[IPaddrlen] = { + 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, +}; + +static uchar etherbroadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +static void etherread4(void *a); +static void etherread6(void *a); +static void etherbind(Ipifc *ifc, int argc, char **argv); +static void etherunbind(Ipifc *ifc); +static void etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip); +static void etheraddmulti(Ipifc *ifc, uchar *a, uchar *ia); +static void etherremmulti(Ipifc *ifc, uchar *a, uchar *ia); +static Block* multicastarp(Fs *f, Arpent *a, Medium*, uchar *mac); +static void sendarp(Ipifc *ifc, Arpent *a); +static void sendgarp(Ipifc *ifc, uchar*); +static int multicastea(uchar *ea, uchar *ip); +static void recvarpproc(void*); +static void resolveaddr6(Ipifc *ifc, Arpent *a); +static void etherpref2addr(uchar *pref, uchar *ea); + +Medium ethermedium = +{ +.name= "ether", +.hsize= 14, +.mintu= 60, +.maxtu= 1514, +.maclen= 6, +.bind= etherbind, +.unbind= etherunbind, +.bwrite= etherbwrite, +.addmulti= etheraddmulti, +.remmulti= etherremmulti, +.ares= arpenter, +.areg= sendgarp, +.pref2addr= etherpref2addr, +}; + +Medium gbemedium = +{ +.name= "gbe", +.hsize= 14, +.mintu= 60, +.maxtu= 9014, +.maclen= 6, +.bind= etherbind, +.unbind= etherunbind, +.bwrite= etherbwrite, +.addmulti= etheraddmulti, +.remmulti= etherremmulti, +.ares= arpenter, +.areg= sendgarp, +.pref2addr= etherpref2addr, +}; + +typedef struct Etherrock Etherrock; +struct Etherrock +{ + Fs *f; /* file system we belong to */ + Proc *arpp; /* arp process */ + Proc *read4p; /* reading process (v4)*/ + Proc *read6p; /* reading process (v6)*/ + Chan *mchan4; /* Data channel for v4 */ + Chan *achan; /* Arp channel */ + Chan *cchan4; /* Control channel for v4 */ + Chan *mchan6; /* Data channel for v6 */ + Chan *cchan6; /* Control channel for v6 */ +}; + +/* + * ethernet arp request + */ +enum +{ + ETARP = 0x0806, + ETIP4 = 0x0800, + ETIP6 = 0x86DD, + ARPREQUEST = 1, + ARPREPLY = 2, +}; + +typedef struct Etherarp Etherarp; +struct Etherarp +{ + uchar d[6]; + uchar s[6]; + uchar type[2]; + uchar hrd[2]; + uchar pro[2]; + uchar hln; + uchar pln; + uchar op[2]; + uchar sha[6]; + uchar spa[4]; + uchar tha[6]; + uchar tpa[4]; +}; + +static char *nbmsg = "nonblocking"; + +/* + * called to bind an IP ifc to an ethernet device + * called with ifc wlock'd + */ +static void +etherbind(Ipifc *ifc, int argc, char **argv) +{ + Chan *mchan4, *cchan4, *achan, *mchan6, *cchan6; + char addr[Maxpath]; //char addr[2*KNAMELEN]; + char dir[Maxpath]; //char dir[2*KNAMELEN]; + char *buf; + int fd, cfd, n; + char *ptr; + Etherrock *er; + + if(argc < 2) + error(Ebadarg); + + mchan4 = cchan4 = achan = mchan6 = cchan6 = nil; + buf = nil; + if(waserror()){ + if(mchan4 != nil) + cclose(mchan4); + if(cchan4 != nil) + cclose(cchan4); + if(achan != nil) + cclose(achan); + if(mchan6 != nil) + cclose(mchan6); + if(cchan6 != nil) + cclose(cchan6); + if(buf != nil) + free(buf); + nexterror(); + } + + /* + * open ip converstation + * + * the dial will fail if the type is already open on + * this device. + */ + snprint(addr, sizeof(addr), "%s!0x800", argv[2]); + fd = kdial(addr, nil, dir, &cfd); + if(fd < 0) + errorf("dial 0x800 failed: %s", up->env->errstr); + mchan4 = commonfdtochan(fd, ORDWR, 0, 1); + cchan4 = commonfdtochan(cfd, ORDWR, 0, 1); + kclose(fd); + kclose(cfd); + + /* + * make it non-blocking + */ + devtab[cchan4->type]->write(cchan4, nbmsg, strlen(nbmsg), 0); + + /* + * get mac address and speed + */ + snprint(addr, sizeof(addr), "%s/stats", dir); + fd = kopen(addr, OREAD); + if(fd < 0) + errorf("can't open ether stats: %s", up->env->errstr); + + buf = smalloc(512); + n = kread(fd, buf, 511); + kclose(fd); + if(n <= 0) + error(Eio); + buf[n] = 0; + + ptr = strstr(buf, "addr: "); + if(!ptr) + error(Eio); + ptr += 6; + parsemac(ifc->mac, ptr, 6); + + ptr = strstr(buf, "mbps: "); + if(ptr){ + ptr += 6; + ifc->mbps = atoi(ptr); + } else + ifc->mbps = 100; + + /* + * open arp conversation + */ + snprint(addr, sizeof(addr), "%s!0x806", argv[2]); + fd = kdial(addr, nil, nil, nil); + if(fd < 0) + errorf("dial 0x806 failed: %s", up->env->errstr); + achan = commonfdtochan(fd, ORDWR, 0, 1); + kclose(fd); + + /* + * open ip conversation + * + * the dial will fail if the type is already open on + * this device. + */ + snprint(addr, sizeof(addr), "%s!0x86DD", argv[2]); + fd = kdial(addr, nil, dir, &cfd); + if(fd < 0) + errorf("dial 0x86DD failed: %s", up->env->errstr); + mchan6 = commonfdtochan(fd, ORDWR, 0, 1); + cchan6 = commonfdtochan(cfd, ORDWR, 0, 1); + kclose(fd); + kclose(cfd); + + /* + * make it non-blocking + */ + devtab[cchan6->type]->write(cchan6, nbmsg, strlen(nbmsg), 0); + + er = smalloc(sizeof(*er)); + er->mchan4 = mchan4; + er->cchan4 = cchan4; + er->achan = achan; + er->mchan6 = mchan6; + er->cchan6 = cchan6; + er->f = ifc->conv->p->f; + ifc->arg = er; + + free(buf); + poperror(); + + kproc("etherread4", etherread4, ifc, 0); + kproc("recvarpproc", recvarpproc, ifc, 0); + kproc("etherread6", etherread6, ifc, 0); +} + +/* + * called with ifc wlock'd + */ +static void +etherunbind(Ipifc *ifc) +{ + Etherrock *er = ifc->arg; + + if(er->read4p) + postnote(er->read4p, 1, "unbind", 0); + if(er->read6p) + postnote(er->read6p, 1, "unbind", 0); + if(er->arpp) + postnote(er->arpp, 1, "unbind", 0); + + /* wait for readers to die */ + while(er->arpp != 0 || er->read4p != 0 || er->read6p != 0) + tsleep(&up->sleep, return0, 0, 300); + + if(er->mchan4 != nil) + cclose(er->mchan4); + if(er->achan != nil) + cclose(er->achan); + if(er->cchan4 != nil) + cclose(er->cchan4); + if(er->mchan6 != nil) + cclose(er->mchan6); + if(er->cchan6 != nil) + cclose(er->cchan6); + + free(er); +} + +/* + * called by ipoput with a single block to write with ifc rlock'd + */ +static void +etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip) +{ + Etherhdr *eh; + Arpent *a; + uchar mac[6]; + Etherrock *er = ifc->arg; + + /* get mac address of destination */ + a = arpget(er->f->arp, bp, version, ifc, ip, mac); + if(a){ + /* check for broadcast or multicast */ + bp = multicastarp(er->f, a, ifc->m, mac); + if(bp==nil){ + switch(version){ + case V4: + sendarp(ifc, a); + break; + case V6: + resolveaddr6(ifc, a); + break; + default: + panic("etherbwrite: version %d", version); + } + return; + } + } + + /* make it a single block with space for the ether header */ + bp = padblock(bp, ifc->m->hsize); + if(bp->next) + bp = concatblock(bp); + if(BLEN(bp) < ifc->mintu) + bp = adjustblock(bp, ifc->mintu); + eh = (Etherhdr*)bp->rp; + + /* copy in mac addresses and ether type */ + memmove(eh->s, ifc->mac, sizeof(eh->s)); + memmove(eh->d, mac, sizeof(eh->d)); + + switch(version){ + case V4: + eh->t[0] = 0x08; + eh->t[1] = 0x00; + devtab[er->mchan4->type]->bwrite(er->mchan4, bp, 0); + break; + case V6: + eh->t[0] = 0x86; + eh->t[1] = 0xDD; + devtab[er->mchan6->type]->bwrite(er->mchan6, bp, 0); + break; + default: + panic("etherbwrite2: version %d", version); + } + ifc->out++; +} + + +/* + * process to read from the ethernet + */ +static void +etherread4(void *a) +{ + Ipifc *ifc; + Block *bp; + Etherrock *er; + + ifc = a; + er = ifc->arg; + er->read4p = up; /* hide identity under a rock for unbind */ + if(waserror()){ + er->read4p = 0; + pexit("hangup", 1); + } + for(;;){ + bp = devtab[er->mchan4->type]->bread(er->mchan4, ifc->maxtu, 0); + if(!canrlock(ifc)){ + freeb(bp); + continue; + } + if(waserror()){ + runlock(ifc); + nexterror(); + } + ifc->in++; + bp->rp += ifc->m->hsize; + if(ifc->lifc == nil) + freeb(bp); + else + ipiput4(er->f, ifc, bp); + runlock(ifc); + poperror(); + } +} + + +/* + * process to read from the ethernet, IPv6 + */ +static void +etherread6(void *a) +{ + Ipifc *ifc; + Block *bp; + Etherrock *er; + + ifc = a; + er = ifc->arg; + er->read6p = up; /* hide identity under a rock for unbind */ + if(waserror()){ + er->read6p = 0; + pexit("hangup", 1); + } + for(;;){ + bp = devtab[er->mchan6->type]->bread(er->mchan6, ifc->maxtu, 0); + if(!canrlock(ifc)){ + freeb(bp); + continue; + } + if(waserror()){ + runlock(ifc); + nexterror(); + } + ifc->in++; + bp->rp += ifc->m->hsize; + if(ifc->lifc == nil) + freeb(bp); + else + ipiput6(er->f, ifc, bp); + runlock(ifc); + poperror(); + } +} + +static void +etheraddmulti(Ipifc *ifc, uchar *a, uchar *) +{ + uchar mac[6]; + char buf[64]; + Etherrock *er = ifc->arg; + int version; + + version = multicastea(mac, a); + sprint(buf, "addmulti %E", mac); + switch(version){ + case V4: + devtab[er->cchan4->type]->write(er->cchan4, buf, strlen(buf), 0); + break; + case V6: + devtab[er->cchan6->type]->write(er->cchan6, buf, strlen(buf), 0); + break; + default: + panic("etheraddmulti: version %d", version); + } +} + +static void +etherremmulti(Ipifc *ifc, uchar *a, uchar *) +{ + uchar mac[6]; + char buf[64]; + Etherrock *er = ifc->arg; + int version; + + version = multicastea(mac, a); + sprint(buf, "remmulti %E", mac); + switch(version){ + case V4: + devtab[er->cchan4->type]->write(er->cchan4, buf, strlen(buf), 0); + break; + case V6: + devtab[er->cchan6->type]->write(er->cchan6, buf, strlen(buf), 0); + break; + default: + panic("etherremmulti: version %d", version); + } +} + +/* + * send an ethernet arp + * (only v4, v6 uses the neighbor discovery, rfc1970) + */ +static void +sendarp(Ipifc *ifc, Arpent *a) +{ + int n; + Block *bp; + Etherarp *e; + Etherrock *er = ifc->arg; + + /* don't do anything if it's been less than a second since the last */ + if(NOW - a->ctime < 1000){ + arprelease(er->f->arp, a); + return; + } + + /* remove all but the last message */ + while((bp = a->hold) != nil){ + if(bp == a->last) + break; + a->hold = bp->list; + freeblist(bp); + } + + /* try to keep it around for a second more */ + a->ctime = NOW; + arprelease(er->f->arp, a); + + n = sizeof(Etherarp); + if(n < a->type->mintu) + n = a->type->mintu; + bp = allocb(n); + memset(bp->rp, 0, n); + e = (Etherarp*)bp->rp; + memmove(e->tpa, a->ip+IPv4off, sizeof(e->tpa)); + ipv4local(ifc, e->spa); + memmove(e->sha, ifc->mac, sizeof(e->sha)); + memset(e->d, 0xff, sizeof(e->d)); /* ethernet broadcast */ + memmove(e->s, ifc->mac, sizeof(e->s)); + + hnputs(e->type, ETARP); + hnputs(e->hrd, 1); + hnputs(e->pro, ETIP4); + e->hln = sizeof(e->sha); + e->pln = sizeof(e->spa); + hnputs(e->op, ARPREQUEST); + bp->wp += n; + + n = devtab[er->achan->type]->bwrite(er->achan, bp, 0); + if(n < 0) + print("arp: send: %r\n"); +} + +static void +resolveaddr6(Ipifc *ifc, Arpent *a) +{ + int sflag; + Block *bp; + Etherrock *er = ifc->arg; + uchar ipsrc[IPaddrlen]; + + /* don't do anything if it's been less than a second since the last */ + if(NOW - a->ctime < ReTransTimer){ + arprelease(er->f->arp, a); + return; + } + + /* remove all but the last message */ + while((bp = a->hold) != nil){ + if(bp == a->last) + break; + a->hold = bp->list; + freeblist(bp); + } + + /* try to keep it around for a second more */ + a->ctime = NOW; + a->rtime = NOW + ReTransTimer; + if(a->rxtsrem <= 0) { + arprelease(er->f->arp, a); + return; + } + + a->rxtsrem--; + arprelease(er->f->arp, a); + + if(sflag = ipv6anylocal(ifc, ipsrc)) + icmpns(er->f, ipsrc, sflag, a->ip, TARG_MULTI, ifc->mac); +} + +/* + * send a gratuitous arp to refresh arp caches + */ +static void +sendgarp(Ipifc *ifc, uchar *ip) +{ + int n; + Block *bp; + Etherarp *e; + Etherrock *er = ifc->arg; + + /* don't arp for our initial non address */ + if(ipcmp(ip, IPnoaddr) == 0) + return; + + n = sizeof(Etherarp); + if(n < ifc->m->mintu) + n = ifc->m->mintu; + bp = allocb(n); + memset(bp->rp, 0, n); + e = (Etherarp*)bp->rp; + memmove(e->tpa, ip+IPv4off, sizeof(e->tpa)); + memmove(e->spa, ip+IPv4off, sizeof(e->spa)); + memmove(e->sha, ifc->mac, sizeof(e->sha)); + memset(e->d, 0xff, sizeof(e->d)); /* ethernet broadcast */ + memmove(e->s, ifc->mac, sizeof(e->s)); + + hnputs(e->type, ETARP); + hnputs(e->hrd, 1); + hnputs(e->pro, ETIP4); + e->hln = sizeof(e->sha); + e->pln = sizeof(e->spa); + hnputs(e->op, ARPREQUEST); + bp->wp += n; + + n = devtab[er->achan->type]->bwrite(er->achan, bp, 0); + if(n < 0) + print("garp: send: %r\n"); +} + +static void +recvarp(Ipifc *ifc) +{ + int n; + Block *ebp, *rbp; + Etherarp *e, *r; + uchar ip[IPaddrlen]; + static uchar eprinted[4]; + Etherrock *er = ifc->arg; + + ebp = devtab[er->achan->type]->bread(er->achan, ifc->maxtu, 0); + if(ebp == nil) { + print("arp: rcv: %r\n"); + return; + } + + e = (Etherarp*)ebp->rp; + switch(nhgets(e->op)) { + default: + break; + + case ARPREPLY: + /* check for machine using my ip address */ + v4tov6(ip, e->spa); + if(iplocalonifc(ifc, ip) || ipproxyifc(er->f, ifc, ip)){ + if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0){ + print("arprep: 0x%E/0x%E also has ip addr %V\n", + e->s, e->sha, e->spa); + break; + } + } + + /* make sure we're not entering broadcast addresses */ + if(ipcmp(ip, ipbroadcast) == 0 || + !memcmp(e->sha, etherbroadcast, sizeof(e->sha))){ + print("arprep: 0x%E/0x%E cannot register broadcast address %I\n", + e->s, e->sha, e->spa); + break; + } + + arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), 0); + break; + + case ARPREQUEST: + /* don't answer arps till we know who we are */ + if(ifc->lifc == 0) + break; + + /* check for machine using my ip or ether address */ + v4tov6(ip, e->spa); + if(iplocalonifc(ifc, ip) || ipproxyifc(er->f, ifc, ip)){ + if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0){ + if (memcmp(eprinted, e->spa, sizeof(e->spa))){ + /* print only once */ + print("arpreq: 0x%E also has ip addr %V\n", e->sha, e->spa); + memmove(eprinted, e->spa, sizeof(e->spa)); + } + } + } else { + if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) == 0){ + print("arpreq: %V also has ether addr %E\n", e->spa, e->sha); + break; + } + } + + /* refresh what we know about sender */ + arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), 1); + + /* answer only requests for our address or systems we're proxying for */ + v4tov6(ip, e->tpa); + if(!iplocalonifc(ifc, ip)) + if(!ipproxyifc(er->f, ifc, ip)) + break; + + n = sizeof(Etherarp); + if(n < ifc->mintu) + n = ifc->mintu; + rbp = allocb(n); + r = (Etherarp*)rbp->rp; + memset(r, 0, sizeof(Etherarp)); + hnputs(r->type, ETARP); + hnputs(r->hrd, 1); + hnputs(r->pro, ETIP4); + r->hln = sizeof(r->sha); + r->pln = sizeof(r->spa); + hnputs(r->op, ARPREPLY); + memmove(r->tha, e->sha, sizeof(r->tha)); + memmove(r->tpa, e->spa, sizeof(r->tpa)); + memmove(r->sha, ifc->mac, sizeof(r->sha)); + memmove(r->spa, e->tpa, sizeof(r->spa)); + memmove(r->d, e->sha, sizeof(r->d)); + memmove(r->s, ifc->mac, sizeof(r->s)); + rbp->wp += n; + + n = devtab[er->achan->type]->bwrite(er->achan, rbp, 0); + if(n < 0) + print("arp: write: %r\n"); + } + freeb(ebp); +} + +static void +recvarpproc(void *v) +{ + Ipifc *ifc = v; + Etherrock *er = ifc->arg; + + er->arpp = up; + if(waserror()){ + er->arpp = 0; + pexit("hangup", 1); + } + for(;;) + recvarp(ifc); +} + +static int +multicastea(uchar *ea, uchar *ip) +{ + int x; + + switch(x = ipismulticast(ip)){ + case V4: + ea[0] = 0x01; + ea[1] = 0x00; + ea[2] = 0x5e; + ea[3] = ip[13] & 0x7f; + ea[4] = ip[14]; + ea[5] = ip[15]; + break; + case V6: + ea[0] = 0x33; + ea[1] = 0x33; + ea[2] = ip[12]; + ea[3] = ip[13]; + ea[4] = ip[14]; + ea[5] = ip[15]; + break; + } + return x; +} + +/* + * fill in an arp entry for broadcast or multicast + * addresses. Return the first queued packet for the + * IP address. + */ +static Block* +multicastarp(Fs *f, Arpent *a, Medium *medium, uchar *mac) +{ + /* is it broadcast? */ + switch(ipforme(f, a->ip)){ + case Runi: + return nil; + case Rbcast: + memset(mac, 0xff, 6); + return arpresolve(f->arp, a, medium, mac); + default: + break; + } + + /* if multicast, fill in mac */ + switch(multicastea(mac, a->ip)){ + case V4: + case V6: + return arpresolve(f->arp, a, medium, mac); + } + + /* let arp take care of it */ + return nil; +} + +void +ethermediumlink(void) +{ + addipmedium(ðermedium); + addipmedium(&gbemedium); +} + + +static void +etherpref2addr(uchar *pref, uchar *ea) +{ + pref[8] = ea[0] | 0x2; + pref[9] = ea[1]; + pref[10] = ea[2]; + pref[11] = 0xFF; + pref[12] = 0xFE; + pref[13] = ea[3]; + pref[14] = ea[4]; + pref[15] = ea[5]; +} diff --git a/os/ip/gre.c b/os/ip/gre.c new file mode 100644 index 00000000..96106331 --- /dev/null +++ b/os/ip/gre.c @@ -0,0 +1,282 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" + +#define DPRINT if(0)print + +enum +{ + GRE_IPONLY = 12, /* size of ip header */ + GRE_IPPLUSGRE = 12, /* minimum size of GRE header */ + IP_GREPROTO = 47, + + GRErxms = 200, + GREtickms = 100, + GREmaxxmit = 10, +}; + +typedef struct GREhdr +{ + /* ip header */ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar len[2]; /* packet length (including headers) */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar Unused; + uchar proto; /* Protocol */ + uchar cksum[2]; /* checksum */ + uchar src[4]; /* Ip source */ + uchar dst[4]; /* Ip destination */ + + /* gre header */ + uchar flags[2]; + uchar eproto[2]; /* encapsulation protocol */ +} GREhdr; + +typedef struct GREpriv GREpriv; +struct GREpriv +{ + int raw; /* Raw GRE mode */ + + /* non-MIB stats */ + ulong csumerr; /* checksum errors */ + ulong lenerr; /* short packet */ +}; + +static void grekick(void *x, Block *bp); + +static char* +greconnect(Conv *c, char **argv, int argc) +{ + Proto *p; + char *err; + Conv *tc, **cp, **ecp; + + err = Fsstdconnect(c, argv, argc); + if(err != nil) + return err; + + /* make sure noone's already connected to this other sys */ + p = c->p; + qlock(p); + ecp = &p->conv[p->nc]; + for(cp = p->conv; cp < ecp; cp++){ + tc = *cp; + if(tc == nil) + break; + if(tc == c) + continue; + if(tc->rport == c->rport && ipcmp(tc->raddr, c->raddr) == 0){ + err = "already connected to that addr/proto"; + ipmove(c->laddr, IPnoaddr); + ipmove(c->raddr, IPnoaddr); + break; + } + } + qunlock(p); + + if(err != nil) + return err; + Fsconnected(c, nil); + + return nil; +} + +static void +grecreate(Conv *c) +{ + c->rq = qopen(64*1024, Qmsg, 0, c); + c->wq = qbypass(grekick, c); +} + +static int +grestate(Conv *c, char *state, int n) +{ + USED(c); + return snprint(state, n, "%s", "Datagram"); +} + +static char* +greannounce(Conv*, char**, int) +{ + return "pktifc does not support announce"; +} + +static void +greclose(Conv *c) +{ + qclose(c->rq); + qclose(c->wq); + qclose(c->eq); + ipmove(c->laddr, IPnoaddr); + ipmove(c->raddr, IPnoaddr); + c->lport = 0; + c->rport = 0; +} + +int drop; + +static void +grekick(void *x, Block *bp) +{ + Conv *c = x; + GREhdr *ghp; + uchar laddr[IPaddrlen], raddr[IPaddrlen]; + + if(bp == nil) + return; + + /* Make space to fit ip header (gre header already there) */ + bp = padblock(bp, GRE_IPONLY); + if(bp == nil) + return; + + /* make sure the message has a GRE header */ + bp = pullupblock(bp, GRE_IPONLY+GRE_IPPLUSGRE); + if(bp == nil) + return; + + ghp = (GREhdr *)(bp->rp); + ghp->vihl = IP_VER4; + + if(!((GREpriv*)c->p->priv)->raw){ + v4tov6(raddr, ghp->dst); + if(ipcmp(raddr, v4prefix) == 0) + memmove(ghp->dst, c->raddr + IPv4off, IPv4addrlen); + v4tov6(laddr, ghp->src); + if(ipcmp(laddr, v4prefix) == 0){ + if(ipcmp(c->laddr, IPnoaddr) == 0) + findlocalip(c->p->f, c->laddr, raddr); /* pick interface closest to dest */ + memmove(ghp->src, c->laddr + IPv4off, IPv4addrlen); + } + hnputs(ghp->eproto, c->rport); + } + + ghp->proto = IP_GREPROTO; + ghp->frag[0] = 0; + ghp->frag[1] = 0; + + ipoput4(c->p->f, bp, 0, c->ttl, c->tos, nil); +} + +static void +greiput(Proto *gre, Ipifc*, Block *bp) +{ + int len; + GREhdr *ghp; + Conv *c, **p; + ushort eproto; + uchar raddr[IPaddrlen]; + GREpriv *gpriv; + + gpriv = gre->priv; + ghp = (GREhdr*)(bp->rp); + + v4tov6(raddr, ghp->src); + eproto = nhgets(ghp->eproto); + qlock(gre); + + /* Look for a conversation structure for this port and address */ + c = nil; + for(p = gre->conv; *p; p++) { + c = *p; + if(c->inuse == 0) + continue; + if(c->rport == eproto && + (gpriv->raw || ipcmp(c->raddr, raddr) == 0)) + break; + } + + if(*p == nil) { + qunlock(gre); + freeblist(bp); + return; + } + + qunlock(gre); + + /* + * Trim the packet down to data size + */ + len = nhgets(ghp->len) - GRE_IPONLY; + if(len < GRE_IPPLUSGRE){ + freeblist(bp); + return; + } + bp = trimblock(bp, GRE_IPONLY, len); + if(bp == nil){ + gpriv->lenerr++; + return; + } + + /* + * Can't delimit packet so pull it all into one block. + */ + if(qlen(c->rq) > 64*1024) + freeblist(bp); + else{ + bp = concatblock(bp); + if(bp == 0) + panic("greiput"); + qpass(c->rq, bp); + } +} + +int +grestats(Proto *gre, char *buf, int len) +{ + GREpriv *gpriv; + + gpriv = gre->priv; + + return snprint(buf, len, "gre: len %lud\n", gpriv->lenerr); +} + +char* +grectl(Conv *c, char **f, int n) +{ + GREpriv *gpriv; + + gpriv = c->p->priv; + if(n == 1){ + if(strcmp(f[0], "raw") == 0){ + gpriv->raw = 1; + return nil; + } + else if(strcmp(f[0], "cooked") == 0){ + gpriv->raw = 0; + return nil; + } + } + return "unknown control request"; +} + +void +greinit(Fs *fs) +{ + Proto *gre; + + gre = smalloc(sizeof(Proto)); + gre->priv = smalloc(sizeof(GREpriv)); + gre->name = "gre"; + gre->connect = greconnect; + gre->announce = greannounce; + gre->state = grestate; + gre->create = grecreate; + gre->close = greclose; + gre->rcv = greiput; + gre->ctl = grectl; + gre->advise = nil; + gre->stats = grestats; + gre->ipproto = IP_GREPROTO; + gre->nc = 64; + gre->ptclsize = 0; + + Fsproto(fs, gre); +} diff --git a/os/ip/icmp.c b/os/ip/icmp.c new file mode 100644 index 00000000..53eaf372 --- /dev/null +++ b/os/ip/icmp.c @@ -0,0 +1,490 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" + +typedef struct Icmp { + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar ttl; /* Time to live */ + uchar proto; /* Protocol */ + uchar ipcksum[2]; /* Header checksum */ + uchar src[4]; /* Ip source */ + uchar dst[4]; /* Ip destination */ + uchar type; + uchar code; + uchar cksum[2]; + uchar icmpid[2]; + uchar seq[2]; + uchar data[1]; +} Icmp; + +enum { /* Packet Types */ + EchoReply = 0, + Unreachable = 3, + SrcQuench = 4, + Redirect = 5, + EchoRequest = 8, + TimeExceed = 11, + InParmProblem = 12, + Timestamp = 13, + TimestampReply = 14, + InfoRequest = 15, + InfoReply = 16, + AddrMaskRequest = 17, + AddrMaskReply = 18, + + Maxtype = 18, +}; + +enum +{ + MinAdvise = 24, /* minimum needed for us to advise another protocol */ +}; + +char *icmpnames[Maxtype+1] = +{ +[EchoReply] "EchoReply", +[Unreachable] "Unreachable", +[SrcQuench] "SrcQuench", +[Redirect] "Redirect", +[EchoRequest] "EchoRequest", +[TimeExceed] "TimeExceed", +[InParmProblem] "InParmProblem", +[Timestamp] "Timestamp", +[TimestampReply] "TimestampReply", +[InfoRequest] "InfoRequest", +[InfoReply] "InfoReply", +[AddrMaskRequest] "AddrMaskRequest", +[AddrMaskReply ] "AddrMaskReply ", +}; + +enum { + IP_ICMPPROTO = 1, + ICMP_IPSIZE = 20, + ICMP_HDRSIZE = 8, +}; + +enum +{ + InMsgs, + InErrors, + OutMsgs, + CsumErrs, + LenErrs, + HlenErrs, + + Nstats, +}; + +static char *statnames[Nstats] = +{ +[InMsgs] "InMsgs", +[InErrors] "InErrors", +[OutMsgs] "OutMsgs", +[CsumErrs] "CsumErrs", +[LenErrs] "LenErrs", +[HlenErrs] "HlenErrs", +}; + +typedef struct Icmppriv Icmppriv; +struct Icmppriv +{ + ulong stats[Nstats]; + + /* message counts */ + ulong in[Maxtype+1]; + ulong out[Maxtype+1]; +}; + +static void icmpkick(void *x, Block*); + +static void +icmpcreate(Conv *c) +{ + c->rq = qopen(64*1024, Qmsg, 0, c); + c->wq = qbypass(icmpkick, c); +} + +extern char* +icmpconnect(Conv *c, char **argv, int argc) +{ + char *e; + + e = Fsstdconnect(c, argv, argc); + if(e != nil) + return e; + Fsconnected(c, e); + + return nil; +} + +extern int +icmpstate(Conv *c, char *state, int n) +{ + USED(c); + return snprint(state, n, "%s qin %d qout %d", + "Datagram", + c->rq ? qlen(c->rq) : 0, + c->wq ? qlen(c->wq) : 0 + ); +} + +extern char* +icmpannounce(Conv *c, char **argv, int argc) +{ + char *e; + + e = Fsstdannounce(c, argv, argc); + if(e != nil) + return e; + Fsconnected(c, nil); + + return nil; +} + +extern void +icmpclose(Conv *c) +{ + qclose(c->rq); + qclose(c->wq); + ipmove(c->laddr, IPnoaddr); + ipmove(c->raddr, IPnoaddr); + c->lport = 0; +} + +static void +icmpkick(void *x, Block *bp) +{ + Conv *c = x; + Icmp *p; + Icmppriv *ipriv; + + if(bp == nil) + return; + + if(blocklen(bp) < ICMP_IPSIZE + ICMP_HDRSIZE){ + freeblist(bp); + return; + } + p = (Icmp *)(bp->rp); + p->vihl = IP_VER4; + ipriv = c->p->priv; + if(p->type <= Maxtype) + ipriv->out[p->type]++; + + v6tov4(p->dst, c->raddr); + v6tov4(p->src, c->laddr); + p->proto = IP_ICMPPROTO; + hnputs(p->icmpid, c->lport); + memset(p->cksum, 0, sizeof(p->cksum)); + hnputs(p->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE)); + ipriv->stats[OutMsgs]++; + ipoput4(c->p->f, bp, 0, c->ttl, c->tos, nil); +} + +extern void +icmpttlexceeded(Fs *f, uchar *ia, Block *bp) +{ + Block *nbp; + Icmp *p, *np; + + p = (Icmp *)bp->rp; + + netlog(f, Logicmp, "sending icmpttlexceeded -> %V\n", p->src); + nbp = allocb(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8); + nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8; + np = (Icmp *)nbp->rp; + np->vihl = IP_VER4; + memmove(np->dst, p->src, sizeof(np->dst)); + v6tov4(np->src, ia); + memmove(np->data, bp->rp, ICMP_IPSIZE + 8); + np->type = TimeExceed; + np->code = 0; + np->proto = IP_ICMPPROTO; + hnputs(np->icmpid, 0); + hnputs(np->seq, 0); + memset(np->cksum, 0, sizeof(np->cksum)); + hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE)); + ipoput4(f, nbp, 0, MAXTTL, DFLTTOS, nil); + +} + +static void +icmpunreachable(Fs *f, Block *bp, int code, int seq) +{ + Block *nbp; + Icmp *p, *np; + int i; + uchar addr[IPaddrlen]; + + p = (Icmp *)bp->rp; + + /* only do this for unicast sources and destinations */ + v4tov6(addr, p->dst); + i = ipforme(f, addr); + if((i&Runi) == 0) + return; + v4tov6(addr, p->src); + i = ipforme(f, addr); + if(i != 0 && (i&Runi) == 0) + return; + + netlog(f, Logicmp, "sending icmpnoconv -> %V\n", p->src); + nbp = allocb(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8); + nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8; + np = (Icmp *)nbp->rp; + np->vihl = IP_VER4; + memmove(np->dst, p->src, sizeof(np->dst)); + memmove(np->src, p->dst, sizeof(np->src)); + memmove(np->data, bp->rp, ICMP_IPSIZE + 8); + np->type = Unreachable; + np->code = code; + np->proto = IP_ICMPPROTO; + hnputs(np->icmpid, 0); + hnputs(np->seq, seq); + memset(np->cksum, 0, sizeof(np->cksum)); + hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE)); + ipoput4(f, nbp, 0, MAXTTL, DFLTTOS, nil); +} + +extern void +icmpnoconv(Fs *f, Block *bp) +{ + icmpunreachable(f, bp, 3, 0); +} + +extern void +icmpcantfrag(Fs *f, Block *bp, int mtu) +{ + icmpunreachable(f, bp, 4, mtu); +} + +static void +goticmpkt(Proto *icmp, Block *bp) +{ + Conv **c, *s; + Icmp *p; + uchar dst[IPaddrlen]; + ushort recid; + + p = (Icmp *) bp->rp; + v4tov6(dst, p->src); + recid = nhgets(p->icmpid); + + for(c = icmp->conv; *c; c++) { + s = *c; + if(s->lport == recid) + if(ipcmp(s->raddr, dst) == 0){ + bp = concatblock(bp); + if(bp != nil) + qpass(s->rq, bp); + return; + } + } + freeblist(bp); +} + +static Block * +mkechoreply(Block *bp) +{ + Icmp *q; + uchar ip[4]; + + q = (Icmp *)bp->rp; + q->vihl = IP_VER4; + memmove(ip, q->src, sizeof(q->dst)); + memmove(q->src, q->dst, sizeof(q->src)); + memmove(q->dst, ip, sizeof(q->dst)); + q->type = EchoReply; + memset(q->cksum, 0, sizeof(q->cksum)); + hnputs(q->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE)); + + return bp; +} + +static char *unreachcode[] = +{ +[0] "net unreachable", +[1] "host unreachable", +[2] "protocol unreachable", +[3] "port unreachable", +[4] "fragmentation needed and DF set", +[5] "source route failed", +}; + +static void +icmpiput(Proto *icmp, Ipifc*, Block *bp) +{ + int n, iplen; + Icmp *p; + Block *r; + Proto *pr; + char *msg; + char m2[128]; + Icmppriv *ipriv; + + ipriv = icmp->priv; + + ipriv->stats[InMsgs]++; + + p = (Icmp *)bp->rp; + netlog(icmp->f, Logicmp, "icmpiput %d %d\n", p->type, p->code); + n = blocklen(bp); + if(n < ICMP_IPSIZE+ICMP_HDRSIZE){ + ipriv->stats[InErrors]++; + ipriv->stats[HlenErrs]++; + netlog(icmp->f, Logicmp, "icmp hlen %d\n", n); + goto raise; + } + iplen = nhgets(p->length); + if(iplen > n || (iplen % 1)){ + ipriv->stats[LenErrs]++; + ipriv->stats[InErrors]++; + netlog(icmp->f, Logicmp, "icmp length %d\n", iplen); + goto raise; + } + if(ptclcsum(bp, ICMP_IPSIZE, iplen - ICMP_IPSIZE)){ + ipriv->stats[InErrors]++; + ipriv->stats[CsumErrs]++; + netlog(icmp->f, Logicmp, "icmp checksum error\n"); + goto raise; + } + if(p->type <= Maxtype) + ipriv->in[p->type]++; + + switch(p->type) { + case EchoRequest: + if (iplen < n) + bp = trimblock(bp, 0, iplen); + r = mkechoreply(bp); + ipriv->out[EchoReply]++; + ipoput4(icmp->f, r, 0, MAXTTL, DFLTTOS, nil); + break; + case Unreachable: + if(p->code > 5) + msg = unreachcode[1]; + else + msg = unreachcode[p->code]; + + bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE; + if(blocklen(bp) < MinAdvise){ + ipriv->stats[LenErrs]++; + goto raise; + } + p = (Icmp *)bp->rp; + pr = Fsrcvpcolx(icmp->f, p->proto); + if(pr != nil && pr->advise != nil) { + (*pr->advise)(pr, bp, msg); + return; + } + + bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE; + goticmpkt(icmp, bp); + break; + case TimeExceed: + if(p->code == 0){ + sprint(m2, "ttl exceeded at %V", p->src); + + bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE; + if(blocklen(bp) < MinAdvise){ + ipriv->stats[LenErrs]++; + goto raise; + } + p = (Icmp *)bp->rp; + pr = Fsrcvpcolx(icmp->f, p->proto); + if(pr != nil && pr->advise != nil) { + (*pr->advise)(pr, bp, m2); + return; + } + bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE; + } + + goticmpkt(icmp, bp); + break; + default: + goticmpkt(icmp, bp); + break; + } + return; + +raise: + freeblist(bp); +} + +void +icmpadvise(Proto *icmp, Block *bp, char *msg) +{ + Conv **c, *s; + Icmp *p; + uchar dst[IPaddrlen]; + ushort recid; + + p = (Icmp *) bp->rp; + v4tov6(dst, p->dst); + recid = nhgets(p->icmpid); + + for(c = icmp->conv; *c; c++) { + s = *c; + if(s->lport == recid) + if(ipcmp(s->raddr, dst) == 0){ + qhangup(s->rq, msg); + qhangup(s->wq, msg); + break; + } + } + freeblist(bp); +} + +int +icmpstats(Proto *icmp, char *buf, int len) +{ + Icmppriv *priv; + char *p, *e; + int i; + + priv = icmp->priv; + p = buf; + e = p+len; + for(i = 0; i < Nstats; i++) + p = seprint(p, e, "%s: %lud\n", statnames[i], priv->stats[i]); + for(i = 0; i <= Maxtype; i++){ + if(icmpnames[i]) + p = seprint(p, e, "%s: %lud %lud\n", icmpnames[i], priv->in[i], priv->out[i]); + else + p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i], priv->out[i]); + } + return p - buf; +} + +void +icmpinit(Fs *fs) +{ + Proto *icmp; + + icmp = smalloc(sizeof(Proto)); + icmp->priv = smalloc(sizeof(Icmppriv)); + icmp->name = "icmp"; + icmp->connect = icmpconnect; + icmp->announce = icmpannounce; + icmp->state = icmpstate; + icmp->create = icmpcreate; + icmp->close = icmpclose; + icmp->rcv = icmpiput; + icmp->stats = icmpstats; + icmp->ctl = nil; + icmp->advise = icmpadvise; + icmp->gc = nil; + icmp->ipproto = IP_ICMPPROTO; + icmp->nc = 128; + icmp->ptclsize = 0; + + Fsproto(fs, icmp); +} diff --git a/os/ip/icmp6.c b/os/ip/icmp6.c new file mode 100644 index 00000000..bca78a34 --- /dev/null +++ b/os/ip/icmp6.c @@ -0,0 +1,917 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "ip.h" +#include "ipv6.h" + +typedef struct ICMPpkt ICMPpkt; +typedef struct IPICMP IPICMP; +typedef struct Ndpkt Ndpkt; +typedef struct NdiscC NdiscC; + +struct ICMPpkt { + uchar type; + uchar code; + uchar cksum[2]; + uchar icmpid[2]; + uchar seq[2]; +}; + +struct IPICMP { + Ip6hdr; + ICMPpkt; +}; + +struct NdiscC +{ + IPICMP; + uchar target[IPaddrlen]; +}; + +struct Ndpkt +{ + NdiscC; + uchar otype; + uchar olen; // length in units of 8 octets(incl type, code), + // 1 for IEEE 802 addresses + uchar lnaddr[6]; // link-layer address +}; + +enum { + // ICMPv6 types + EchoReply = 0, + UnreachableV6 = 1, + PacketTooBigV6 = 2, + TimeExceedV6 = 3, + SrcQuench = 4, + ParamProblemV6 = 4, + Redirect = 5, + EchoRequest = 8, + TimeExceed = 11, + InParmProblem = 12, + Timestamp = 13, + TimestampReply = 14, + InfoRequest = 15, + InfoReply = 16, + AddrMaskRequest = 17, + AddrMaskReply = 18, + EchoRequestV6 = 128, + EchoReplyV6 = 129, + RouterSolicit = 133, + RouterAdvert = 134, + NbrSolicit = 135, + NbrAdvert = 136, + RedirectV6 = 137, + + Maxtype6 = 137, +}; + +char *icmpnames6[Maxtype6+1] = +{ +[EchoReply] "EchoReply", +[UnreachableV6] "UnreachableV6", +[PacketTooBigV6] "PacketTooBigV6", +[TimeExceedV6] "TimeExceedV6", +[SrcQuench] "SrcQuench", +[Redirect] "Redirect", +[EchoRequest] "EchoRequest", +[TimeExceed] "TimeExceed", +[InParmProblem] "InParmProblem", +[Timestamp] "Timestamp", +[TimestampReply] "TimestampReply", +[InfoRequest] "InfoRequest", +[InfoReply] "InfoReply", +[AddrMaskRequest] "AddrMaskRequest", +[AddrMaskReply] "AddrMaskReply", +[EchoRequestV6] "EchoRequestV6", +[EchoReplyV6] "EchoReplyV6", +[RouterSolicit] "RouterSolicit", +[RouterAdvert] "RouterAdvert", +[NbrSolicit] "NbrSolicit", +[NbrAdvert] "NbrAdvert", +[RedirectV6] "RedirectV6", +}; + +enum +{ + InMsgs6, + InErrors6, + OutMsgs6, + CsumErrs6, + LenErrs6, + HlenErrs6, + HoplimErrs6, + IcmpCodeErrs6, + TargetErrs6, + OptlenErrs6, + AddrmxpErrs6, + RouterAddrErrs6, + + Nstats6, +}; + +static char *statnames6[Nstats6] = +{ +[InMsgs6] "InMsgs", +[InErrors6] "InErrors", +[OutMsgs6] "OutMsgs", +[CsumErrs6] "CsumErrs", +[LenErrs6] "LenErrs", +[HlenErrs6] "HlenErrs", +[HoplimErrs6] "HoplimErrs", +[IcmpCodeErrs6] "IcmpCodeErrs", +[TargetErrs6] "TargetErrs", +[OptlenErrs6] "OptlenErrs", +[AddrmxpErrs6] "AddrmxpErrs", +[RouterAddrErrs6] "RouterAddrErrs", +}; + +typedef struct Icmppriv6 +{ + ulong stats[Nstats6]; + + /* message counts */ + ulong in[Maxtype6+1]; + ulong out[Maxtype6+1]; +} Icmppriv6; + +typedef struct Icmpcb6 +{ + QLock; + uchar headers; +} Icmpcb6; + +static char *unreachcode[] = +{ +[icmp6_no_route] "no route to destination", +[icmp6_ad_prohib] "comm with destination administratively prohibited", +[icmp6_unassigned] "icmp unreachable: unassigned error code (2)", +[icmp6_adr_unreach] "address unreachable", +[icmp6_port_unreach] "port unreachable", +[icmp6_unkn_code] "icmp unreachable: unknown code", +}; + +enum { + ICMP_USEAD6 = 40, +}; + +enum { + Oflag = 1<<5, + Sflag = 1<<6, + Rflag = 1<<7, +}; + +enum { + slladd = 1, + tlladd = 2, + prfinfo = 3, + redhdr = 4, + mtuopt = 5, +}; + +static void icmpkick6(void *x, Block *bp); + +static void +icmpcreate6(Conv *c) +{ + c->rq = qopen(64*1024, Qmsg, 0, c); + c->wq = qbypass(icmpkick6, c); +} + +static void +set_cksum(Block *bp) +{ + IPICMP *p = (IPICMP *)(bp->rp); + + hnputl(p->vcf, 0); // borrow IP header as pseudoheader + hnputs(p->ploadlen, blocklen(bp)-IPV6HDR_LEN); + p->proto = 0; + p->ttl = ICMPv6; // ttl gets set later + hnputs(p->cksum, 0); + hnputs(p->cksum, ptclcsum(bp, 0, blocklen(bp))); + p->proto = ICMPv6; +} + +static Block * +newIPICMP(int packetlen) +{ + Block *nbp; + nbp = allocb(packetlen); + nbp->wp += packetlen; + memset(nbp->rp, 0, packetlen); + return nbp; +} + +void +icmpadvise6(Proto *icmp, Block *bp, char *msg) +{ + Conv **c, *s; + IPICMP *p; + ushort recid; + + p = (IPICMP *) bp->rp; + recid = nhgets(p->icmpid); + + for(c = icmp->conv; *c; c++) { + s = *c; + if(s->lport == recid) + if(ipcmp(s->raddr, p->dst) == 0){ + qhangup(s->rq, msg); + qhangup(s->wq, msg); + break; + } + } + freeblist(bp); +} + +static void +icmpkick6(void *x, Block *bp) +{ + Conv *c = x; + IPICMP *p; + uchar laddr[IPaddrlen], raddr[IPaddrlen]; + Icmppriv6 *ipriv = c->p->priv; + Icmpcb6 *icb = (Icmpcb6*)c->ptcl; + + if(bp == nil) + return; + + if(icb->headers==6) { + /* get user specified addresses */ + bp = pullupblock(bp, ICMP_USEAD6); + if(bp == nil) + return; + bp->rp += 8; + ipmove(laddr, bp->rp); + bp->rp += IPaddrlen; + ipmove(raddr, bp->rp); + bp->rp += IPaddrlen; + bp = padblock(bp, sizeof(Ip6hdr)); + } + + if(blocklen(bp) < sizeof(IPICMP)){ + freeblist(bp); + return; + } + p = (IPICMP *)(bp->rp); + if(icb->headers == 6) { + ipmove(p->dst, raddr); + ipmove(p->src, laddr); + } else { + ipmove(p->dst, c->raddr); + ipmove(p->src, c->laddr); + hnputs(p->icmpid, c->lport); + } + + set_cksum(bp); + p->vcf[0] = 0x06 << 4; + if(p->type <= Maxtype6) + ipriv->out[p->type]++; + ipoput6(c->p->f, bp, 0, c->ttl, c->tos, nil); +} + +char* +icmpctl6(Conv *c, char **argv, int argc) +{ + Icmpcb6 *icb; + + icb = (Icmpcb6*) c->ptcl; + + if(argc==1) { + if(strcmp(argv[0], "headers")==0) { + icb->headers = 6; + return nil; + } + } + return "unknown control request"; +} + +static void +goticmpkt6(Proto *icmp, Block *bp, int muxkey) +{ + Conv **c, *s; + IPICMP *p = (IPICMP *)bp->rp; + ushort recid; + uchar *addr; + + if(muxkey == 0) { + recid = nhgets(p->icmpid); + addr = p->src; + } + else { + recid = muxkey; + addr = p->dst; + } + + for(c = icmp->conv; *c; c++){ + s = *c; + if(s->lport == recid && ipcmp(s->raddr, addr) == 0){ + bp = concatblock(bp); + if(bp != nil) + qpass(s->rq, bp); + return; + } + } + + freeblist(bp); +} + +static Block * +mkechoreply6(Block *bp) +{ + IPICMP *p = (IPICMP *)(bp->rp); + uchar addr[IPaddrlen]; + + ipmove(addr, p->src); + ipmove(p->src, p->dst); + ipmove(p->dst, addr); + p->type = EchoReplyV6; + set_cksum(bp); + return bp; +} + +/* + * sends out an ICMPv6 neighbor solicitation + * suni == SRC_UNSPEC or SRC_UNI, + * tuni == TARG_MULTI => multicast for address resolution, + * and tuni == TARG_UNI => neighbor reachability. + */ + +extern void +icmpns(Fs *f, uchar* src, int suni, uchar* targ, int tuni, uchar* mac) +{ + Block *nbp; + Ndpkt *np; + Proto *icmp = f->t2p[ICMPv6]; + Icmppriv6 *ipriv = icmp->priv; + + + nbp = newIPICMP(sizeof(Ndpkt)); + np = (Ndpkt*) nbp->rp; + + + if(suni == SRC_UNSPEC) + memmove(np->src, v6Unspecified, IPaddrlen); + else + memmove(np->src, src, IPaddrlen); + + if(tuni == TARG_UNI) + memmove(np->dst, targ, IPaddrlen); + else + ipv62smcast(np->dst, targ); + + np->type = NbrSolicit; + np->code = 0; + memmove(np->target, targ, IPaddrlen); + if(suni != SRC_UNSPEC) { + np->otype = SRC_LLADDRESS; + np->olen = 1; /* 1+1+6 = 8 = 1 8-octet */ + memmove(np->lnaddr, mac, sizeof(np->lnaddr)); + } + else { + int r = sizeof(Ndpkt)-sizeof(NdiscC); + nbp->wp -= r; + } + + set_cksum(nbp); + np = (Ndpkt*) nbp->rp; + np->ttl = HOP_LIMIT; + np->vcf[0] = 0x06 << 4; + ipriv->out[NbrSolicit]++; + netlog(f, Logicmp, "sending neighbor solicitation %I\n", targ); + ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil); +} + +/* + * sends out an ICMPv6 neighbor advertisement. pktflags == RSO flags. + */ +extern void +icmpna(Fs *f, uchar* src, uchar* dst, uchar* targ, uchar* mac, uchar flags) +{ + Block *nbp; + Ndpkt *np; + Proto *icmp = f->t2p[ICMPv6]; + Icmppriv6 *ipriv = icmp->priv; + + nbp = newIPICMP(sizeof(Ndpkt)); + np = (Ndpkt*) nbp->rp; + + memmove(np->src, src, IPaddrlen); + memmove(np->dst, dst, IPaddrlen); + + np->type = NbrAdvert; + np->code = 0; + np->icmpid[0] = flags; + memmove(np->target, targ, IPaddrlen); + + np->otype = TARGET_LLADDRESS; + np->olen = 1; + memmove(np->lnaddr, mac, sizeof(np->lnaddr)); + + set_cksum(nbp); + np = (Ndpkt*) nbp->rp; + np->ttl = HOP_LIMIT; + np->vcf[0] = 0x06 << 4; + ipriv->out[NbrAdvert]++; + netlog(f, Logicmp, "sending neighbor advertisement %I\n", src); + ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil); +} + +extern void +icmphostunr(Fs *f, Ipifc *ifc, Block *bp, int code, int free) +{ + Block *nbp; + IPICMP *np; + Ip6hdr *p; + int osz = BLEN(bp); + int sz = MIN(sizeof(IPICMP) + osz, v6MINTU); + Proto *icmp = f->t2p[ICMPv6]; + Icmppriv6 *ipriv = icmp->priv; + + p = (Ip6hdr *) bp->rp; + + if(isv6mcast(p->src)) + goto clean; + + nbp = newIPICMP(sz); + np = (IPICMP *) nbp->rp; + + rlock(ifc); + if(ipv6anylocal(ifc, np->src)) { + netlog(f, Logicmp, "send icmphostunr -> s%I d%I\n", p->src, p->dst); + } + else { + netlog(f, Logicmp, "icmphostunr fail -> s%I d%I\n", p->src, p->dst); + freeblist(nbp); + if(free) + goto clean; + else + return; + } + + memmove(np->dst, p->src, IPaddrlen); + np->type = UnreachableV6; + np->code = code; + memmove(nbp->rp + sizeof(IPICMP), bp->rp, sz - sizeof(IPICMP)); + set_cksum(nbp); + np->ttl = HOP_LIMIT; + np->vcf[0] = 0x06 << 4; + ipriv->out[UnreachableV6]++; + + if(free) + ipiput6(f, ifc, nbp); + else { + ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil); + return; + } + +clean: + runlock(ifc); + freeblist(bp); +} + +extern void +icmpttlexceeded6(Fs *f, Ipifc *ifc, Block *bp) +{ + Block *nbp; + IPICMP *np; + Ip6hdr *p; + int osz = BLEN(bp); + int sz = MIN(sizeof(IPICMP) + osz, v6MINTU); + Proto *icmp = f->t2p[ICMPv6]; + Icmppriv6 *ipriv = icmp->priv; + + p = (Ip6hdr *) bp->rp; + + if(isv6mcast(p->src)) + return; + + nbp = newIPICMP(sz); + np = (IPICMP *) nbp->rp; + + if(ipv6anylocal(ifc, np->src)) { + netlog(f, Logicmp, "send icmpttlexceeded6 -> s%I d%I\n", p->src, p->dst); + } + else { + netlog(f, Logicmp, "icmpttlexceeded6 fail -> s%I d%I\n", p->src, p->dst); + return; + } + + memmove(np->dst, p->src, IPaddrlen); + np->type = TimeExceedV6; + np->code = 0; + memmove(nbp->rp + sizeof(IPICMP), bp->rp, sz - sizeof(IPICMP)); + set_cksum(nbp); + np->ttl = HOP_LIMIT; + np->vcf[0] = 0x06 << 4; + ipriv->out[TimeExceedV6]++; + ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil); +} + +extern void +icmppkttoobig6(Fs *f, Ipifc *ifc, Block *bp) +{ + Block *nbp; + IPICMP *np; + Ip6hdr *p; + int osz = BLEN(bp); + int sz = MIN(sizeof(IPICMP) + osz, v6MINTU); + Proto *icmp = f->t2p[ICMPv6]; + Icmppriv6 *ipriv = icmp->priv; + + p = (Ip6hdr *) bp->rp; + + if(isv6mcast(p->src)) + return; + + nbp = newIPICMP(sz); + np = (IPICMP *) nbp->rp; + + if(ipv6anylocal(ifc, np->src)) { + netlog(f, Logicmp, "send icmppkttoobig6 -> s%I d%I\n", p->src, p->dst); + } + else { + netlog(f, Logicmp, "icmppkttoobig6 fail -> s%I d%I\n", p->src, p->dst); + return; + } + + memmove(np->dst, p->src, IPaddrlen); + np->type = PacketTooBigV6; + np->code = 0; + hnputl(np->icmpid, ifc->maxtu - ifc->m->hsize); + memmove(nbp->rp + sizeof(IPICMP), bp->rp, sz - sizeof(IPICMP)); + set_cksum(nbp); + np->ttl = HOP_LIMIT; + np->vcf[0] = 0x06 << 4; + ipriv->out[PacketTooBigV6]++; + ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil); +} + +/* + * RFC 2461, pages 39-40, pages 57-58. + */ +static int +valid(Proto *icmp, Ipifc *ifc, Block *bp, Icmppriv6 *ipriv) { + int sz, osz, unsp, n, ttl, iplen; + int pktsz = BLEN(bp); + uchar *packet = bp->rp; + IPICMP *p = (IPICMP *) packet; + Ndpkt *np; + + USED(ifc); + n = blocklen(bp); + if(n < sizeof(IPICMP)) { + ipriv->stats[HlenErrs6]++; + netlog(icmp->f, Logicmp, "icmp hlen %d\n", n); + goto err; + } + + iplen = nhgets(p->ploadlen); + if(iplen > n-IPV6HDR_LEN || (iplen % 1)) { + ipriv->stats[LenErrs6]++; + netlog(icmp->f, Logicmp, "icmp length %d\n", iplen); + goto err; + } + + // Rather than construct explicit pseudoheader, overwrite IPv6 header + if(p->proto != ICMPv6) { + // This code assumes no extension headers!!! + netlog(icmp->f, Logicmp, "icmp error: extension header\n"); + goto err; + } + memset(packet, 0, 4); + ttl = p->ttl; + p->ttl = p->proto; + p->proto = 0; + if(ptclcsum(bp, 0, iplen + IPV6HDR_LEN)) { + ipriv->stats[CsumErrs6]++; + netlog(icmp->f, Logicmp, "icmp checksum error\n"); + goto err; + } + p->proto = p->ttl; + p->ttl = ttl; + + /* additional tests for some pkt types */ + if( (p->type == NbrSolicit) || + (p->type == NbrAdvert) || + (p->type == RouterAdvert) || + (p->type == RouterSolicit) || + (p->type == RedirectV6) ) { + + if(p->ttl != HOP_LIMIT) { + ipriv->stats[HoplimErrs6]++; + goto err; + } + if(p->code != 0) { + ipriv->stats[IcmpCodeErrs6]++; + goto err; + } + + switch (p->type) { + case NbrSolicit: + case NbrAdvert: + np = (Ndpkt*) p; + if(isv6mcast(np->target)) { + ipriv->stats[TargetErrs6]++; + goto err; + } + if(optexsts(np) && (np->olen == 0)) { + ipriv->stats[OptlenErrs6]++; + goto err; + } + + if(p->type == NbrSolicit) { + if(ipcmp(np->src, v6Unspecified) == 0) { + if(!issmcast(np->dst) || optexsts(np)) { + ipriv->stats[AddrmxpErrs6]++; + goto err; + } + } + } + + if(p->type == NbrAdvert) { + if((isv6mcast(np->dst))&&(nhgets(np->icmpid) & Sflag)){ + ipriv->stats[AddrmxpErrs6]++; + goto err; + } + } + break; + + case RouterAdvert: + if(pktsz - sizeof(Ip6hdr) < 16) { + ipriv->stats[HlenErrs6]++; + goto err; + } + if(!islinklocal(p->src)) { + ipriv->stats[RouterAddrErrs6]++; + goto err; + } + sz = sizeof(IPICMP) + 8; + while ((sz+1) < pktsz) { + osz = *(packet+sz+1); + if(osz <= 0) { + ipriv->stats[OptlenErrs6]++; + goto err; + } + sz += 8*osz; + } + break; + + case RouterSolicit: + if(pktsz - sizeof(Ip6hdr) < 8) { + ipriv->stats[HlenErrs6]++; + goto err; + } + unsp = (ipcmp(p->src, v6Unspecified) == 0); + sz = sizeof(IPICMP) + 8; + while ((sz+1) < pktsz) { + osz = *(packet+sz+1); + if((osz <= 0) || + (unsp && (*(packet+sz) == slladd)) ) { + ipriv->stats[OptlenErrs6]++; + goto err; + } + sz += 8*osz; + } + break; + + case RedirectV6: + //to be filled in + break; + + default: + goto err; + } + } + + return 1; + +err: + ipriv->stats[InErrors6]++; + return 0; +} + +static int +targettype(Fs *f, Ipifc *ifc, uchar *target) +{ + Iplifc *lifc; + int t; + + rlock(ifc); + if(ipproxyifc(f, ifc, target)) { + runlock(ifc); + return t_uniproxy; + } + + for(lifc = ifc->lifc; lifc; lifc = lifc->next) { + if(ipcmp(lifc->local, target) == 0) { + t = (lifc->tentative) ? t_unitent : t_unirany; + runlock(ifc); + return t; + } + } + + runlock(ifc); + return 0; +} + +static void +icmpiput6(Proto *icmp, Ipifc *ipifc, Block *bp) +{ + uchar *packet = bp->rp; + IPICMP *p = (IPICMP *)packet; + Icmppriv6 *ipriv = icmp->priv; + Block *r; + Proto *pr; + char *msg, m2[128]; + Ndpkt* np; + uchar pktflags; + uchar lsrc[IPaddrlen]; + int refresh = 1; + Iplifc *lifc; + + if(!valid(icmp, ipifc, bp, ipriv)) + goto raise; + + if(p->type <= Maxtype6) + ipriv->in[p->type]++; + else + goto raise; + + switch(p->type) { + case EchoRequestV6: + r = mkechoreply6(bp); + ipriv->out[EchoReply]++; + ipoput6(icmp->f, r, 0, MAXTTL, DFLTTOS, nil); + break; + + case UnreachableV6: + if(p->code > 4) + msg = unreachcode[icmp6_unkn_code]; + else + msg = unreachcode[p->code]; + + bp->rp += sizeof(IPICMP); + if(blocklen(bp) < 8){ + ipriv->stats[LenErrs6]++; + goto raise; + } + p = (IPICMP *)bp->rp; + pr = Fsrcvpcolx(icmp->f, p->proto); + if(pr != nil && pr->advise != nil) { + (*pr->advise)(pr, bp, msg); + return; + } + + bp->rp -= sizeof(IPICMP); + goticmpkt6(icmp, bp, 0); + break; + + case TimeExceedV6: + if(p->code == 0){ + sprint(m2, "ttl exceeded at %I", p->src); + + bp->rp += sizeof(IPICMP); + if(blocklen(bp) < 8){ + ipriv->stats[LenErrs6]++; + goto raise; + } + p = (IPICMP *)bp->rp; + pr = Fsrcvpcolx(icmp->f, p->proto); + if(pr != nil && pr->advise != nil) { + (*pr->advise)(pr, bp, m2); + return; + } + bp->rp -= sizeof(IPICMP); + } + + goticmpkt6(icmp, bp, 0); + break; + + case RouterAdvert: + case RouterSolicit: + /* using lsrc as a temp, munge hdr for goticmp6 + memmove(lsrc, p->src, IPaddrlen); + memmove(p->src, p->dst, IPaddrlen); + memmove(p->dst, lsrc, IPaddrlen); */ + + goticmpkt6(icmp, bp, p->type); + break; + + case NbrSolicit: + np = (Ndpkt*) p; + pktflags = 0; + switch (targettype(icmp->f, ipifc, np->target)) { + case t_unirany: + pktflags |= Oflag; + /* fall through */ + + case t_uniproxy: + if(ipcmp(np->src, v6Unspecified) != 0) { + arpenter(icmp->f, V6, np->src, np->lnaddr, 8*np->olen-2, 0); + pktflags |= Sflag; + } + if(ipv6local(ipifc, lsrc)) { + icmpna(icmp->f, lsrc, + (ipcmp(np->src, v6Unspecified)==0)?v6allnodesL:np->src, + np->target, ipifc->mac, pktflags); + } + else + freeblist(bp); + break; + + case t_unitent: + /* not clear what needs to be done. send up + * an icmp mesg saying don't use this address? */ + + default: + freeblist(bp); + } + + break; + + case NbrAdvert: + np = (Ndpkt*) p; + + /* if the target address matches one of the local interface + * address and the local interface address has tentative bit set, + * then insert into ARP table. this is so the duplication address + * detection part of ipconfig can discover duplication through + * the arp table + */ + lifc = iplocalonifc(ipifc, np->target); + if(lifc && lifc->tentative) + refresh = 0; + arpenter(icmp->f, V6, np->target, np->lnaddr, 8*np->olen-2, refresh); + freeblist(bp); + break; + + case PacketTooBigV6: + + default: + goticmpkt6(icmp, bp, 0); + break; + } + return; + +raise: + freeblist(bp); + +} + +int +icmpstats6(Proto *icmp6, char *buf, int len) +{ + Icmppriv6 *priv; + char *p, *e; + int i; + + priv = icmp6->priv; + p = buf; + e = p+len; + for(i = 0; i < Nstats6; i++) + p = seprint(p, e, "%s: %lud\n", statnames6[i], priv->stats[i]); + for(i = 0; i <= Maxtype6; i++){ + if(icmpnames6[i]) + p = seprint(p, e, "%s: %lud %lud\n", icmpnames6[i], priv->in[i], priv->out[i]); +/* else + p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i], priv->out[i]); +*/ + } + return p - buf; +} + + +// need to import from icmp.c +extern int icmpstate(Conv *c, char *state, int n); +extern char* icmpannounce(Conv *c, char **argv, int argc); +extern char* icmpconnect(Conv *c, char **argv, int argc); +extern void icmpclose(Conv *c); + +void +icmp6init(Fs *fs) +{ + Proto *icmp6 = smalloc(sizeof(Proto)); + + icmp6->priv = smalloc(sizeof(Icmppriv6)); + icmp6->name = "icmpv6"; + icmp6->connect = icmpconnect; + icmp6->announce = icmpannounce; + icmp6->state = icmpstate; + icmp6->create = icmpcreate6; + icmp6->close = icmpclose; + icmp6->rcv = icmpiput6; + icmp6->stats = icmpstats6; + icmp6->ctl = icmpctl6; + icmp6->advise = icmpadvise6; + icmp6->gc = nil; + icmp6->ipproto = ICMPv6; + icmp6->nc = 16; + icmp6->ptclsize = sizeof(Icmpcb6); + + Fsproto(fs, icmp6); +} + diff --git a/os/ip/igmp.c b/os/ip/igmp.c new file mode 100644 index 00000000..109df303 --- /dev/null +++ b/os/ip/igmp.c @@ -0,0 +1,291 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" + +enum +{ + IGMP_IPHDRSIZE = 20, /* size of ip header */ + IGMP_HDRSIZE = 8, /* size of IGMP header */ + IP_IGMPPROTO = 2, + + IGMPquery = 1, + IGMPreport = 2, + + MSPTICK = 100, + MAXTIMEOUT = 10000/MSPTICK, /* at most 10 secs for a response */ +}; + +typedef struct IGMPpkt IGMPpkt; +struct IGMPpkt +{ + /* ip header */ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar len[2]; /* packet length (including headers) */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar Unused; + uchar proto; /* Protocol */ + uchar cksum[2]; /* checksum of ip portion */ + uchar src[IPaddrlen]; /* Ip source */ + uchar dst[IPaddrlen]; /* Ip destination */ + + /* igmp header */ + uchar vertype; /* version and type */ + uchar unused; + uchar igmpcksum[2]; /* checksum of igmp portion */ + uchar group[IPaddrlen]; /* multicast group */ +}; + +/* + * lists for group reports + */ +typedef struct IGMPrep IGMPrep; +struct IGMPrep +{ + IGMPrep *next; + Media *m; + int ticks; + Multicast *multi; +}; + +typedef struct IGMP IGMP; +struct IGMP +{ + Lock; + Rendez r; + IGMPrep *reports; +}; + +IGMP igmpalloc; + + Proto igmp; +extern Fs fs; + +static struct Stats +{ + ulong inqueries; + ulong outqueries; + ulong inreports; + ulong outreports; +} stats; + +void +igmpsendreport(Media *m, uchar *addr) +{ + IGMPpkt *p; + Block *bp; + + bp = allocb(sizeof(IGMPpkt)); + if(bp == nil) + return; + p = (IGMPpkt*)bp->wp; + p->vihl = IP_VER4; + bp->wp += sizeof(IGMPpkt); + memset(bp->rp, 0, sizeof(IGMPpkt)); + hnputl(p->src, Mediagetaddr(m)); + hnputl(p->dst, Ipallsys); + p->vertype = (1<<4) | IGMPreport; + p->proto = IP_IGMPPROTO; + memmove(p->group, addr, IPaddrlen); + hnputs(p->igmpcksum, ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE)); + netlog(Logigmp, "igmpreport %I\n", p->group); + stats.outreports++; + ipoput4(bp, 0, 1, DFLTTOS, nil); /* TTL of 1 */ +} + +static int +isreport(void *a) +{ + USED(a); + return igmpalloc.reports != 0; +} + + +void +igmpproc(void *a) +{ + IGMPrep *rp, **lrp; + Multicast *mp, **lmp; + uchar ip[IPaddrlen]; + + USED(a); + + for(;;){ + sleep(&igmpalloc.r, isreport, 0); + for(;;){ + lock(&igmpalloc); + + if(igmpalloc.reports == nil) + break; + + /* look for a single report */ + lrp = &igmpalloc.reports; + mp = nil; + for(rp = *lrp; rp; rp = *lrp){ + rp->ticks++; + lmp = &rp->multi; + for(mp = *lmp; mp; mp = *lmp){ + if(rp->ticks >= mp->timeout){ + *lmp = mp->next; + break; + } + lmp = &mp->next; + } + if(mp != nil) + break; + + if(rp->multi != nil){ + lrp = &rp->next; + continue; + } else { + *lrp = rp->next; + free(rp); + } + } + unlock(&igmpalloc); + + if(mp){ + /* do a single report and try again */ + hnputl(ip, mp->addr); + igmpsendreport(rp->m, ip); + free(mp); + continue; + } + + tsleep(&up->sleep, return0, 0, MSPTICK); + } + unlock(&igmpalloc); + } + +} + +void +igmpiput(Media *m, Ipifc *, Block *bp) +{ + int n; + IGMPpkt *ghp; + Ipaddr group; + IGMPrep *rp, **lrp; + Multicast *mp, **lmp; + + ghp = (IGMPpkt*)(bp->rp); + netlog(Logigmp, "igmpiput: %d %I\n", ghp->vertype, ghp->group); + + n = blocklen(bp); + if(n < IGMP_IPHDRSIZE+IGMP_HDRSIZE){ + netlog(Logigmp, "igmpiput: bad len\n"); + goto error; + } + if((ghp->vertype>>4) != 1){ + netlog(Logigmp, "igmpiput: bad igmp type\n"); + goto error; + } + if(ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE)){ + netlog(Logigmp, "igmpiput: checksum error %I\n", ghp->src); + goto error; + } + + group = nhgetl(ghp->group); + + lock(&igmpalloc); + switch(ghp->vertype & 0xf){ + case IGMPquery: + /* + * start reporting groups that we're a member of. + */ + stats.inqueries++; + for(rp = igmpalloc.reports; rp; rp = rp->next) + if(rp->m == m) + break; + if(rp != nil) + break; /* already reporting */ + + mp = Mediacopymulti(m); + if(mp == nil) + break; + + rp = malloc(sizeof(*rp)); + if(rp == nil) + break; + + rp->m = m; + rp->multi = mp; + rp->ticks = 0; + for(; mp; mp = mp->next) + mp->timeout = nrand(MAXTIMEOUT); + rp->next = igmpalloc.reports; + igmpalloc.reports = rp; + + wakeup(&igmpalloc.r); + + break; + case IGMPreport: + /* + * find report list for this medium + */ + stats.inreports++; + lrp = &igmpalloc.reports; + for(rp = *lrp; rp; rp = *lrp){ + if(rp->m == m) + break; + lrp = &rp->next; + } + if(rp == nil) + break; + + /* + * if someone else has reported a group, + * we don't have to. + */ + lmp = &rp->multi; + for(mp = *lmp; mp; mp = *lmp){ + if(mp->addr == group){ + *lmp = mp->next; + free(mp); + break; + } + lmp = &mp->next; + } + + break; + } + unlock(&igmpalloc); + +error: + freeb(bp); +} + +int +igmpstats(char *buf, int len) +{ + return snprint(buf, len, "\trcvd %d %d\n\tsent %d %d\n", + stats.inqueries, stats.inreports, + stats.outqueries, stats.outreports); +} + +void +igmpinit(Fs *fs) +{ + igmp.name = "igmp"; + igmp.connect = nil; + igmp.announce = nil; + igmp.ctl = nil; + igmp.state = nil; + igmp.close = nil; + igmp.rcv = igmpiput; + igmp.stats = igmpstats; + igmp.ipproto = IP_IGMPPROTO; + igmp.nc = 0; + igmp.ptclsize = 0; + + igmpreportfn = igmpsendreport; + kproc("igmpproc", igmpproc, 0, 0); + + Fsproto(fs, &igmp); +} diff --git a/os/ip/ihbootp.c b/os/ip/ihbootp.c new file mode 100644 index 00000000..68b14b1d --- /dev/null +++ b/os/ip/ihbootp.c @@ -0,0 +1,323 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "kernel.h" +#include "ip.h" + +static ulong fsip; +static ulong auip; +static ulong gwip; +static ulong ipmask; +static ulong ipaddr; +static ulong dnsip; + +enum +{ + Bootrequest = 1, + Bootreply = 2, +}; + +typedef struct Bootp +{ + /* udp.c oldheader */ + uchar raddr[IPaddrlen]; + uchar laddr[IPaddrlen]; + uchar rport[2]; + uchar lport[2]; + /* bootp itself */ + uchar op; /* opcode */ + uchar htype; /* hardware type */ + uchar hlen; /* hardware address len */ + uchar hops; /* hops */ + uchar xid[4]; /* a random number */ + uchar secs[2]; /* elapsed snce client started booting */ + uchar pad[2]; + uchar ciaddr[4]; /* client IP address (client tells server) */ + uchar yiaddr[4]; /* client IP address (server tells client) */ + uchar siaddr[4]; /* server IP address */ + uchar giaddr[4]; /* gateway IP address */ + uchar chaddr[16]; /* client hardware address */ + uchar sname[64]; /* server host name (optional) */ + uchar file[128]; /* boot file name */ + uchar vend[128]; /* vendor-specific goo */ +} Bootp; + +/* + * bootp returns: + * + * "fsip d.d.d.d + * auip d.d.d.d + * gwip d.d.d.d + * ipmask d.d.d.d + * ipaddr d.d.d.d + * dnsip d.d.d.d" + * + * where d.d.d.d is the IP address in dotted decimal notation, and each + * address is followed by a newline. + */ + +static Bootp req; +static Proc* rcvprocp; +static int recv; +static int done; +static Rendez bootpr; +static char rcvbuf[512]; +static int bootpdebug; + +/* + * Parse the vendor specific fields according to RFC 1084. + * We are overloading the "cookie server" to be the Inferno + * authentication server and the "resource location server" + * to be the Inferno file server. + * + * If the vendor specific field is formatted properly, it + * will begin with the four bytes 99.130.83.99 and end with + * an 0xFF byte. + */ +static void +parsevend(uchar* vend) +{ + /* The field must start with 99.130.83.99 to be compliant */ + if ((vend[0] != 99) || (vend[1] != 130) || + (vend[2] != 83) || (vend[3] != 99)){ + if(bootpdebug) + print("bad bootp vendor field: %.2x%.2x%.2x%.2x", vend[0], vend[1], vend[2], vend[3]); + return; + } + + /* Skip over the magic cookie */ + vend += 4; + + while ((vend[0] != 0) && (vend[0] != 0xFF)) { + if(bootpdebug){ + int i; + print("vend %d [%d]", vend[0], vend[1]); + for(i=0; ichaddr, 6) == 0 && + rp->htype == 1 && rp->hlen == 6) { + ipaddr = (rp->yiaddr[0]<<24)| + (rp->yiaddr[1]<<16)| + (rp->yiaddr[2]<<8)| + rp->yiaddr[3]; + parsevend(rp->vend); + break; + } + } + poperror(); + rcvprocp = nil; + + recv = 1; + wakeup(&bootpr); + pexit("", 0); +} + +static char* +rbootp(Ipifc *ifc) +{ + int cfd, dfd, tries, n; + char ia[5+3*16], im[16], *av[3]; + uchar nipaddr[4], ngwip[4], nipmask[4]; + char dir[Maxpath]; + static uchar vend_rfc1048[] = { 99, 130, 83, 99 }; + + av[1] = "0.0.0.0"; + av[2] = "0.0.0.0"; + ipifcadd(ifc, av, 3, 0, nil); + + cfd = kannounce("udp!*!68", dir); + if(cfd < 0) + return "bootp announce failed"; + strcat(dir, "/data"); + if(kwrite(cfd, "headers", 7) < 0){ + kclose(cfd); + return "bootp ctl headers failed"; + } + kwrite(cfd, "oldheaders", 10); + dfd = kopen(dir, ORDWR); + if(dfd < 0){ + kclose(cfd); + return "bootp open data failed"; + } + kclose(cfd); + + /* create request */ + memset(&req, 0, sizeof(req)); + ipmove(req.raddr, IPv4bcast); + hnputs(req.rport, 67); + req.op = Bootrequest; + req.htype = 1; /* ethernet (all we know) */ + req.hlen = 6; /* ethernet (all we know) */ + + /* Hardware MAC address */ + memmove(req.chaddr, ifc->mac, 6); + /* Fill in the local IP address if we know it */ + ipv4local(ifc, req.ciaddr); + memset(req.file, 0, sizeof(req.file)); + memmove(req.vend, vend_rfc1048, 4); + + done = 0; + recv = 0; + + kproc("rcvbootp", rcvbootp, (void*)dfd, KPDUPFDG); + + /* + * broadcast bootp's till we get a reply, + * or fixed number of tries + */ + tries = 0; + while(recv == 0) { + if(kwrite(dfd, &req, sizeof(req)) < 0) + print("bootp: write: %r"); + + tsleep(&bootpr, return0, 0, 1000); + if(++tries > 10) { + print("bootp: timed out\n"); + break; + } + } + kclose(dfd); + done = 1; + if(rcvprocp != nil){ + postnote(rcvprocp, 1, "timeout", 0); + rcvprocp = nil; + } + + av[1] = "0.0.0.0"; + av[2] = "0.0.0.0"; + ipifcrem(ifc, av, 3); + + hnputl(nipaddr, ipaddr); + sprint(ia, "%V", nipaddr); + hnputl(nipmask, ipmask); + sprint(im, "%V", nipmask); + av[1] = ia; + av[2] = im; + ipifcadd(ifc, av, 3, 0, nil); + + if(gwip != 0) { + hnputl(ngwip, gwip); + n = sprint(ia, "add 0.0.0.0 0.0.0.0 %V", ngwip); + routewrite(ifc->conv->p->f, nil, ia, n); + } + return nil; +} + +static int +rbootpread(char *bp, ulong offset, int len) +{ + int n; + char *buf; + uchar a[4]; + + buf = smalloc(READSTR); + if(waserror()){ + free(buf); + nexterror(); + } + hnputl(a, fsip); + n = snprint(buf, READSTR, "fsip %15V\n", a); + hnputl(a, auip); + n += snprint(buf + n, READSTR-n, "auip %15V\n", a); + hnputl(a, gwip); + n += snprint(buf + n, READSTR-n, "gwip %15V\n", a); + hnputl(a, ipmask); + n += snprint(buf + n, READSTR-n, "ipmask %15V\n", a); + hnputl(a, ipaddr); + n += snprint(buf + n, READSTR-n, "ipaddr %15V\n", a); + hnputl(a, dnsip); + snprint(buf + n, READSTR-n, "dnsip %15V\n", a); + + len = readstr(offset, bp, len, buf); + poperror(); + free(buf); + return len; +} + +char* (*bootp)(Ipifc*) = rbootp; +int (*bootpread)(char*, ulong, int) = rbootpread; diff --git a/os/ip/il.c b/os/ip/il.c new file mode 100644 index 00000000..5423194c --- /dev/null +++ b/os/ip/il.c @@ -0,0 +1,1408 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" + +enum /* Connection state */ +{ + Ilclosed, + Ilsyncer, + Ilsyncee, + Ilestablished, + Illistening, + Ilclosing, + Ilopening, /* only for file server */ +}; + +char *ilstates[] = +{ + "Closed", + "Syncer", + "Syncee", + "Established", + "Listen", + "Closing", + "Opening", /* only for file server */ +}; + +enum /* Packet types */ +{ + Ilsync, + Ildata, + Ildataquery, + Ilack, + Ilquery, + Ilstate, + Ilclose, +}; + +char *iltype[] = +{ + "sync", + "data", + "dataquery", + "ack", + "query", + "state", + "close" +}; + +enum +{ + Seconds = 1000, + Iltickms = 50, /* time base */ + AckDelay = 2*Iltickms, /* max time twixt message rcvd & ack sent */ + MaxTimeout = 30*Seconds, /* max time between rexmit */ + QueryTime = 10*Seconds, /* time between subsequent queries */ + DeathTime = 30*QueryTime, + + MaxRexmit = 16, /* max retransmissions before hangup */ + Defaultwin = 20, + + LogAGain = 3, + AGain = 1< 1){ + p = strstr(argv[1], "!fasttimeout"); + if(p != nil){ + *p = 0; + fast = 1; + } + } + + e = Fsstdconnect(c, argv, argc); + if(e != nil) + return e; + return ilstart(c, IL_CONNECT, fast); +} + +static int +ilstate(Conv *c, char *state, int n) +{ + Ilcb *ic; + + ic = (Ilcb*)(c->ptcl); + return snprint(state, n, "%s qin %d qout %d del %5.5d Br %5.5d md %5.5d una %5.5lud rex %5.5d rxq %5.5d max %5.5d", + ilstates[ic->state], + c->rq ? qlen(c->rq) : 0, + c->wq ? qlen(c->wq) : 0, + ic->delay>>LogAGain, ic->rate>>LogAGain, ic->mdev>>LogDGain, + ic->unackedbytes, ic->rxtot, ic->rxquery, ic->maxrtt); +} + +static int +ilinuse(Conv *c) +{ + Ilcb *ic; + + ic = (Ilcb*)(c->ptcl); + return ic->state != Ilclosed; + +} + +/* called with c locked */ +static char* +ilannounce(Conv *c, char **argv, int argc) +{ + char *e; + + e = Fsstdannounce(c, argv, argc); + if(e != nil) + return e; + e = ilstart(c, IL_LISTEN, 0); + if(e != nil) + return e; + Fsconnected(c, nil); + + return nil; +} + +void +illocalclose(Conv *c) +{ + Ilcb *ic; + Ilpriv *ipriv; + + ipriv = c->p->priv; + ic = (Ilcb*)c->ptcl; + ic->state = Ilclosed; + iphtrem(&ipriv->ht, c); + ipmove(c->laddr, IPnoaddr); + c->lport = 0; +} + +static void +ilclose(Conv *c) +{ + Ilcb *ic; + + ic = (Ilcb*)c->ptcl; + + qclose(c->rq); + qclose(c->wq); + qclose(c->eq); + + switch(ic->state) { + case Ilclosing: + case Ilclosed: + break; + case Ilsyncer: + case Ilsyncee: + case Ilestablished: + ic->state = Ilclosing; + ilsettimeout(ic); + ilsendctl(c, nil, Ilclose, ic->next, ic->recvd, 0); + break; + case Illistening: + illocalclose(c); + break; + } + ilfreeq(ic); +} + +void +ilkick(void *x, Block *bp) +{ + Conv *c = x; + Ilhdr *ih; + Ilcb *ic; + int dlen; + ulong id, ack; + Fs *f; + Ilpriv *priv; + + f = c->p->f; + priv = c->p->priv; + ic = (Ilcb*)c->ptcl; + + if(bp == nil) + return; + + switch(ic->state) { + case Ilclosed: + case Illistening: + case Ilclosing: + freeblist(bp); + qhangup(c->rq, nil); + return; + } + + dlen = blocklen(bp); + + /* Make space to fit il & ip */ + bp = padblock(bp, IL_IPSIZE+IL_HDRSIZE); + ih = (Ilhdr *)(bp->rp); + ih->vihl = IP_VER4; + + /* Ip fields */ + ih->frag[0] = 0; + ih->frag[1] = 0; + v6tov4(ih->dst, c->raddr); + v6tov4(ih->src, c->laddr); + ih->proto = IP_ILPROTO; + + /* Il fields */ + hnputs(ih->illen, dlen+IL_HDRSIZE); + hnputs(ih->ilsrc, c->lport); + hnputs(ih->ildst, c->rport); + + qlock(&ic->ackq); + id = ic->next++; + hnputl(ih->ilid, id); + ack = ic->recvd; + hnputl(ih->ilack, ack); + ic->acksent = ack; + ic->acktime = NOW + AckDelay; + ih->iltype = Ildata; + ih->ilspec = 0; + ih->ilsum[0] = 0; + ih->ilsum[1] = 0; + + /* Checksum of ilheader plus data (not ip & no pseudo header) */ + if(ilcksum) + hnputs(ih->ilsum, ptclcsum(bp, IL_IPSIZE, dlen+IL_HDRSIZE)); + + ilackq(ic, bp); + qunlock(&ic->ackq); + + /* Start the round trip timer for this packet if the timer is free */ + if(ic->rttack == 0) { + ic->rttack = id; + ic->rttstart = fastticks(nil); + ic->rttlen = dlen + IL_IPSIZE + IL_HDRSIZE; + } + + if(later(NOW, ic->timeout, nil)) + ilsettimeout(ic); + ipoput4(f, bp, 0, c->ttl, c->tos, c); + priv->stats[OutMsgs]++; +} + +static void +ilcreate(Conv *c) +{ + c->rq = qopen(Maxrq, 0, 0, c); + c->wq = qbypass(ilkick, c); +} + +int +ilxstats(Proto *il, char *buf, int len) +{ + Ilpriv *priv; + char *p, *e; + int i; + + priv = il->priv; + p = buf; + e = p+len; + for(i = 0; i < Nstats; i++) + p = seprint(p, e, "%s: %lud\n", statnames[i], priv->stats[i]); + return p - buf; +} + +void +ilackq(Ilcb *ic, Block *bp) +{ + Block *np; + int n; + + n = blocklen(bp); + + /* Enqueue a copy on the unacked queue in case this one gets lost */ + np = copyblock(bp, n); + if(ic->unacked) + ic->unackedtail->list = np; + else + ic->unacked = np; + ic->unackedtail = np; + np->list = nil; + ic->unackedbytes += n; +} + +static +void +ilrttcalc(Ilcb *ic, Block *bp) +{ + int rtt, tt, pt, delay, rate; + + rtt = fastticks(nil) - ic->rttstart; + rtt = (rtt*scalemul)/scalediv; + delay = ic->delay; + rate = ic->rate; + + /* Guard against zero wrap */ + if(rtt > 120000 || rtt < 0) + return; + + /* this block had to be transmitted after the one acked so count its size */ + ic->rttlen += blocklen(bp) + IL_IPSIZE + IL_HDRSIZE; + + if(ic->rttlen < 256){ + /* guess fixed delay as rtt of small packets */ + delay += rtt - (delay>>LogAGain); + if(delay < AGain) + delay = AGain; + ic->delay = delay; + } else { + /* if packet took longer than avg rtt delay, recalc rate */ + tt = rtt - (delay>>LogAGain); + if(tt > 0){ + rate += ic->rttlen/tt - (rate>>LogAGain); + if(rate < AGain) + rate = AGain; + ic->rate = rate; + } + } + + /* mdev */ + pt = ic->rttlen/(rate>>LogAGain) + (delay>>LogAGain); + ic->mdev += abs(rtt-pt) - (ic->mdev>>LogDGain); + + if(rtt > ic->maxrtt) + ic->maxrtt = rtt; +} + +void +ilackto(Ilcb *ic, ulong ackto, Block *bp) +{ + Ilhdr *h; + ulong id; + + if(ic->rttack == ackto) + ilrttcalc(ic, bp); + + /* Cancel if we've passed the packet we were interested in */ + if(ic->rttack <= ackto) + ic->rttack = 0; + + qlock(&ic->ackq); + while(ic->unacked) { + h = (Ilhdr *)ic->unacked->rp; + id = nhgetl(h->ilid); + if(ackto < id) + break; + + bp = ic->unacked; + ic->unacked = bp->list; + bp->list = nil; + ic->unackedbytes -= blocklen(bp); + freeblist(bp); + ic->rexmit = 0; + ilsettimeout(ic); + } + qunlock(&ic->ackq); +} + +void +iliput(Proto *il, Ipifc*, Block *bp) +{ + char *st; + Ilcb *ic; + Ilhdr *ih; + uchar raddr[IPaddrlen]; + uchar laddr[IPaddrlen]; + ushort sp, dp, csum; + int plen, illen; + Conv *new, *s; + Ilpriv *ipriv; + + ipriv = il->priv; + + ih = (Ilhdr *)bp->rp; + plen = blocklen(bp); + if(plen < IL_IPSIZE+IL_HDRSIZE){ + netlog(il->f, Logil, "il: hlenerr\n"); + ipriv->stats[HlenErrs]++; + goto raise; + } + + illen = nhgets(ih->illen); + if(illen+IL_IPSIZE > plen){ + netlog(il->f, Logil, "il: lenerr\n"); + ipriv->stats[LenErrs]++; + goto raise; + } + + sp = nhgets(ih->ildst); + dp = nhgets(ih->ilsrc); + v4tov6(raddr, ih->src); + v4tov6(laddr, ih->dst); + + if((csum = ptclcsum(bp, IL_IPSIZE, illen)) != 0) { + if(ih->iltype > Ilclose) + st = "?"; + else + st = iltype[ih->iltype]; + ipriv->stats[CsumErrs]++; + netlog(il->f, Logil, "il: cksum %ux %ux, pkt(%s id %lud ack %lud %I/%d->%d)\n", + csum, st, nhgetl(ih->ilid), nhgetl(ih->ilack), raddr, sp, dp); + goto raise; + } + + qlock(il); + s = iphtlook(&ipriv->ht, raddr, dp, laddr, sp); + if(s == nil){ + if(ih->iltype == Ilsync) + ilreject(il->f, ih); /* no listener */ + qunlock(il); + goto raise; + } + + ic = (Ilcb*)s->ptcl; + if(ic->state == Illistening){ + if(ih->iltype != Ilsync){ + qunlock(il); + if(ih->iltype > Ilclose) + st = "?"; + else + st = iltype[ih->iltype]; + ilreject(il->f, ih); /* no channel and not sync */ + netlog(il->f, Logil, "il: no channel, pkt(%s id %lud ack %lud %I/%ud->%ud)\n", + st, nhgetl(ih->ilid), nhgetl(ih->ilack), raddr, sp, dp); + goto raise; + } + + new = Fsnewcall(s, raddr, dp, laddr, sp, V4); + if(new == nil){ + qunlock(il); + netlog(il->f, Logil, "il: bad newcall %I/%ud->%ud\n", raddr, sp, dp); + ilsendctl(s, ih, Ilclose, 0, nhgetl(ih->ilid), 0); + goto raise; + } + s = new; + + ic = (Ilcb*)s->ptcl; + + ic->conv = s; + ic->state = Ilsyncee; + ilcbinit(ic); + ic->rstart = nhgetl(ih->ilid); + iphtadd(&ipriv->ht, s); + } + + qlock(s); + qunlock(il); + if(waserror()){ + qunlock(s); + nexterror(); + } + ilprocess(s, ih, bp); + qunlock(s); + poperror(); + return; +raise: + freeblist(bp); +} + +void +_ilprocess(Conv *s, Ilhdr *h, Block *bp) +{ + Ilcb *ic; + ulong id, ack; + Ilpriv *priv; + + id = nhgetl(h->ilid); + ack = nhgetl(h->ilack); + + ic = (Ilcb*)s->ptcl; + + ic->lastrecv = NOW; + ic->querytime = NOW + QueryTime; + priv = s->p->priv; + priv->stats[InMsgs]++; + + switch(ic->state) { + default: + netlog(s->p->f, Logil, "il: unknown state %d\n", ic->state); + case Ilclosed: + freeblist(bp); + break; + case Ilsyncer: + switch(h->iltype) { + default: + break; + case Ilsync: + if(ack != ic->start) + ilhangup(s, "connection rejected"); + else { + ic->recvd = id; + ic->rstart = id; + ilsendctl(s, nil, Ilack, ic->next, ic->recvd, 0); + ic->state = Ilestablished; + ic->fasttimeout = 0; + ic->rexmit = 0; + Fsconnected(s, nil); + ilpullup(s); + } + break; + case Ilclose: + if(ack == ic->start) + ilhangup(s, "connection rejected"); + break; + } + freeblist(bp); + break; + case Ilsyncee: + switch(h->iltype) { + default: + break; + case Ilsync: + if(id != ic->rstart || ack != 0){ + illocalclose(s); + } else { + ic->recvd = id; + ilsendctl(s, nil, Ilsync, ic->start, ic->recvd, 0); + } + break; + case Ilack: + if(ack == ic->start) { + ic->state = Ilestablished; + ic->fasttimeout = 0; + ic->rexmit = 0; + ilpullup(s); + } + break; + case Ildata: + if(ack == ic->start) { + ic->state = Ilestablished; + ic->fasttimeout = 0; + ic->rexmit = 0; + goto established; + } + break; + case Ilclose: + if(ack == ic->start) + ilhangup(s, "remote close"); + break; + } + freeblist(bp); + break; + case Ilestablished: + established: + switch(h->iltype) { + case Ilsync: + if(id != ic->rstart) + ilhangup(s, "remote close"); + else + ilsendctl(s, nil, Ilack, ic->next, ic->rstart, 0); + freeblist(bp); + break; + case Ildata: + /* + * avoid consuming all the mount rpc buffers in the + * system. if the input queue is too long, drop this + * packet. + */ + if (s->rq && qlen(s->rq) >= Maxrq) { + priv->stats[DroppedMsgs]++; + freeblist(bp); + break; + } + + ilackto(ic, ack, bp); + iloutoforder(s, h, bp); + ilpullup(s); + break; + case Ildataquery: + ilackto(ic, ack, bp); + iloutoforder(s, h, bp); + ilpullup(s); + ilsendctl(s, nil, Ilstate, ic->next, ic->recvd, h->ilspec); + break; + case Ilack: + ilackto(ic, ack, bp); + freeblist(bp); + break; + case Ilquery: + ilackto(ic, ack, bp); + ilsendctl(s, nil, Ilstate, ic->next, ic->recvd, h->ilspec); + freeblist(bp); + break; + case Ilstate: + if(ack >= ic->rttack) + ic->rttack = 0; + ilackto(ic, ack, bp); + if(h->ilspec > Nqt) + h->ilspec = 0; + if(ic->qt[h->ilspec] > ack){ + ilrexmit(ic); + ilsettimeout(ic); + } + freeblist(bp); + break; + case Ilclose: + freeblist(bp); + if(ack < ic->start || ack > ic->next) + break; + ic->recvd = id; + ilsendctl(s, nil, Ilclose, ic->next, ic->recvd, 0); + ic->state = Ilclosing; + ilsettimeout(ic); + ilfreeq(ic); + break; + } + break; + case Illistening: + freeblist(bp); + break; + case Ilclosing: + switch(h->iltype) { + case Ilclose: + ic->recvd = id; + ilsendctl(s, nil, Ilclose, ic->next, ic->recvd, 0); + if(ack == ic->next) + ilhangup(s, nil); + break; + default: + break; + } + freeblist(bp); + break; + } +} + +void +ilrexmit(Ilcb *ic) +{ + Ilhdr *h; + Block *nb; + Conv *c; + ulong id; + Ilpriv *priv; + + nb = nil; + qlock(&ic->ackq); + if(ic->unacked) + nb = copyblock(ic->unacked, blocklen(ic->unacked)); + qunlock(&ic->ackq); + + if(nb == nil) + return; + + h = (Ilhdr*)nb->rp; + h->vihl = IP_VER4; + + h->iltype = Ildataquery; + hnputl(h->ilack, ic->recvd); + h->ilspec = ilnextqt(ic); + h->ilsum[0] = 0; + h->ilsum[1] = 0; + hnputs(h->ilsum, ptclcsum(nb, IL_IPSIZE, nhgets(h->illen))); + + c = ic->conv; + id = nhgetl(h->ilid); + netlog(c->p->f, Logil, "il: rexmit %d %ud: %d %d: %i %d/%d\n", id, ic->recvd, + ic->rexmit, ic->timeout, + c->raddr, c->lport, c->rport); + + ilbackoff(ic); + + ipoput4(c->p->f, nb, 0, c->ttl, c->tos, c); + + /* statistics */ + ic->rxtot++; + priv = c->p->priv; + priv->rexmit++; +} + +/* DEBUG */ +void +ilprocess(Conv *s, Ilhdr *h, Block *bp) +{ + Ilcb *ic; + + ic = (Ilcb*)s->ptcl; + + USED(ic); + netlog(s->p->f, Logilmsg, "%11s rcv %d/%d snt %d/%d pkt(%s id %d ack %d %d->%d) ", + ilstates[ic->state], ic->rstart, ic->recvd, ic->start, + ic->next, iltype[h->iltype], nhgetl(h->ilid), + nhgetl(h->ilack), nhgets(h->ilsrc), nhgets(h->ildst)); + + _ilprocess(s, h, bp); + + netlog(s->p->f, Logilmsg, "%11s rcv %d snt %d\n", ilstates[ic->state], ic->recvd, ic->next); +} + +void +ilhangup(Conv *s, char *msg) +{ + Ilcb *ic; + int callout; + + netlog(s->p->f, Logil, "il: hangup! %I %d/%d: %s\n", s->raddr, + s->lport, s->rport, msg?msg:"no reason"); + + ic = (Ilcb*)s->ptcl; + callout = ic->state == Ilsyncer; + illocalclose(s); + + qhangup(s->rq, msg); + qhangup(s->wq, msg); + + if(callout) + Fsconnected(s, msg); +} + +void +ilpullup(Conv *s) +{ + Ilcb *ic; + Ilhdr *oh; + Block *bp; + ulong oid, dlen; + Ilpriv *ipriv; + + ic = (Ilcb*)s->ptcl; + if(ic->state != Ilestablished) + return; + + qlock(&ic->outo); + while(ic->outoforder) { + bp = ic->outoforder; + oh = (Ilhdr*)bp->rp; + oid = nhgetl(oh->ilid); + if(oid <= ic->recvd) { + ic->outoforder = bp->list; + freeblist(bp); + continue; + } + if(oid != ic->recvd+1){ + ipriv = s->p->priv; + ipriv->stats[OutOfOrder]++; + break; + } + + ic->recvd = oid; + ic->outoforder = bp->list; + + bp->list = nil; + dlen = nhgets(oh->illen)-IL_HDRSIZE; + bp = trimblock(bp, IL_IPSIZE+IL_HDRSIZE, dlen); + /* + * Upper levels don't know about multiple-block + * messages so copy all into one (yick). + */ + bp = concatblock(bp); + if(bp == 0) + panic("ilpullup"); + bp = packblock(bp); + if(bp == 0) + panic("ilpullup2"); + qpass(s->rq, bp); + } + qunlock(&ic->outo); +} + +void +iloutoforder(Conv *s, Ilhdr *h, Block *bp) +{ + Ilcb *ic; + uchar *lid; + Block *f, **l; + ulong id, newid; + Ilpriv *ipriv; + + ipriv = s->p->priv; + ic = (Ilcb*)s->ptcl; + bp->list = nil; + + id = nhgetl(h->ilid); + /* Window checks */ + if(id <= ic->recvd || id > ic->recvd+ic->window) { + netlog(s->p->f, Logil, "il: message outside window %ud <%ud-%ud>: %i %d/%d\n", + id, ic->recvd, ic->recvd+ic->window, s->raddr, s->lport, s->rport); + freeblist(bp); + return; + } + + /* Packet is acceptable so sort onto receive queue for pullup */ + qlock(&ic->outo); + if(ic->outoforder == nil) + ic->outoforder = bp; + else { + l = &ic->outoforder; + for(f = *l; f; f = f->list) { + lid = ((Ilhdr*)(f->rp))->ilid; + newid = nhgetl(lid); + if(id <= newid) { + if(id == newid) { + ipriv->stats[DupMsg]++; + ipriv->stats[DupBytes] += blocklen(bp); + qunlock(&ic->outo); + freeblist(bp); + return; + } + bp->list = f; + *l = bp; + qunlock(&ic->outo); + return; + } + l = &f->list; + } + *l = bp; + } + qunlock(&ic->outo); +} + +void +ilsendctl(Conv *ipc, Ilhdr *inih, int type, ulong id, ulong ack, int ilspec) +{ + Ilhdr *ih; + Ilcb *ic; + Block *bp; + int ttl, tos; + + bp = allocb(IL_IPSIZE+IL_HDRSIZE); + bp->wp += IL_IPSIZE+IL_HDRSIZE; + + ih = (Ilhdr *)(bp->rp); + ih->vihl = IP_VER4; + + /* Ip fields */ + ih->proto = IP_ILPROTO; + hnputs(ih->illen, IL_HDRSIZE); + ih->frag[0] = 0; + ih->frag[1] = 0; + if(inih) { + hnputl(ih->dst, nhgetl(inih->src)); + hnputl(ih->src, nhgetl(inih->dst)); + hnputs(ih->ilsrc, nhgets(inih->ildst)); + hnputs(ih->ildst, nhgets(inih->ilsrc)); + hnputl(ih->ilid, nhgetl(inih->ilack)); + hnputl(ih->ilack, nhgetl(inih->ilid)); + ttl = MAXTTL; + tos = DFLTTOS; + } + else { + v6tov4(ih->dst, ipc->raddr); + v6tov4(ih->src, ipc->laddr); + hnputs(ih->ilsrc, ipc->lport); + hnputs(ih->ildst, ipc->rport); + hnputl(ih->ilid, id); + hnputl(ih->ilack, ack); + ic = (Ilcb*)ipc->ptcl; + ic->acksent = ack; + ic->acktime = NOW; + ttl = ipc->ttl; + tos = ipc->tos; + } + ih->iltype = type; + ih->ilspec = ilspec; + ih->ilsum[0] = 0; + ih->ilsum[1] = 0; + + if(ilcksum) + hnputs(ih->ilsum, ptclcsum(bp, IL_IPSIZE, IL_HDRSIZE)); + +if(ipc==nil) + panic("ipc is nil caller is %.8lux", getcallerpc(&ipc)); +if(ipc->p==nil) + panic("ipc->p is nil"); + + netlog(ipc->p->f, Logilmsg, "ctl(%s id %d ack %d %d->%d)\n", + iltype[ih->iltype], nhgetl(ih->ilid), nhgetl(ih->ilack), + nhgets(ih->ilsrc), nhgets(ih->ildst)); + + ipoput4(ipc->p->f, bp, 0, ttl, tos, ipc); +} + +void +ilreject(Fs *f, Ilhdr *inih) +{ + Ilhdr *ih; + Block *bp; + + bp = allocb(IL_IPSIZE+IL_HDRSIZE); + bp->wp += IL_IPSIZE+IL_HDRSIZE; + + ih = (Ilhdr *)(bp->rp); + ih->vihl = IP_VER4; + + /* Ip fields */ + ih->proto = IP_ILPROTO; + hnputs(ih->illen, IL_HDRSIZE); + ih->frag[0] = 0; + ih->frag[1] = 0; + hnputl(ih->dst, nhgetl(inih->src)); + hnputl(ih->src, nhgetl(inih->dst)); + hnputs(ih->ilsrc, nhgets(inih->ildst)); + hnputs(ih->ildst, nhgets(inih->ilsrc)); + hnputl(ih->ilid, nhgetl(inih->ilack)); + hnputl(ih->ilack, nhgetl(inih->ilid)); + ih->iltype = Ilclose; + ih->ilspec = 0; + ih->ilsum[0] = 0; + ih->ilsum[1] = 0; + + if(ilcksum) + hnputs(ih->ilsum, ptclcsum(bp, IL_IPSIZE, IL_HDRSIZE)); + + ipoput4(f, bp, 0, MAXTTL, DFLTTOS, nil); +} + +void +ilsettimeout(Ilcb *ic) +{ + ulong pt; + + pt = (ic->delay>>LogAGain) + + ic->unackedbytes/(ic->rate>>LogAGain) + + (ic->mdev>>(LogDGain-1)) + + AckDelay; + if(pt > MaxTimeout) + pt = MaxTimeout; + ic->timeout = NOW + pt; +} + +void +ilbackoff(Ilcb *ic) +{ + ulong pt; + int i; + + pt = (ic->delay>>LogAGain) + + ic->unackedbytes/(ic->rate>>LogAGain) + + (ic->mdev>>(LogDGain-1)) + + AckDelay; + for(i = 0; i < ic->rexmit; i++) + pt = pt + (pt>>1); + if(pt > MaxTimeout) + pt = MaxTimeout; + ic->timeout = NOW + pt; + + if(ic->fasttimeout) + ic->timeout = NOW+Iltickms; + + ic->rexmit++; +} + +// complain if two numbers not within an hour of each other +#define Tfuture (1000*60*60) +int +later(ulong t1, ulong t2, char *x) +{ + int dt; + + dt = t1 - t2; + if(dt > 0) { + if(x != nil && dt > Tfuture) + print("%s: way future %d\n", x, dt); + return 1; + } + if(dt < -Tfuture) { + if(x != nil) + print("%s: way past %d\n", x, -dt); + return 1; + } + return 0; +} + +void +ilackproc(void *x) +{ + Ilcb *ic; + Conv **s, *p; + Proto *il; + + il = x; + +loop: + tsleep(&up->sleep, return0, 0, Iltickms); + for(s = il->conv; s && *s; s++) { + p = *s; + ic = (Ilcb*)p->ptcl; + + switch(ic->state) { + case Ilclosed: + case Illistening: + break; + case Ilclosing: + if(later(NOW, ic->timeout, "timeout0")) { + if(ic->rexmit > MaxRexmit){ + ilhangup(p, nil); + break; + } + ilsendctl(p, nil, Ilclose, ic->next, ic->recvd, 0); + ilbackoff(ic); + } + break; + + case Ilsyncee: + case Ilsyncer: + if(later(NOW, ic->timeout, "timeout1")) { + if(ic->rexmit > MaxRexmit){ + ilhangup(p, etime); + break; + } + ilsendctl(p, nil, Ilsync, ic->start, ic->recvd, 0); + ilbackoff(ic); + } + break; + + case Ilestablished: + if(ic->recvd != ic->acksent) + if(later(NOW, ic->acktime, "acktime")) + ilsendctl(p, nil, Ilack, ic->next, ic->recvd, 0); + + if(later(NOW, ic->querytime, "querytime")){ + if(later(NOW, ic->lastrecv+DeathTime, "deathtime")){ + netlog(il->f, Logil, "il: hangup: deathtime\n"); + ilhangup(p, etime); + break; + } + ilsendctl(p, nil, Ilquery, ic->next, ic->recvd, ilnextqt(ic)); + ic->querytime = NOW + QueryTime; + } + + if(ic->unacked != nil) + if(later(NOW, ic->timeout, "timeout2")) { + if(ic->rexmit > MaxRexmit){ + netlog(il->f, Logil, "il: hangup: too many rexmits\n"); + ilhangup(p, etime); + break; + } + ilsendctl(p, nil, Ilquery, ic->next, ic->recvd, ilnextqt(ic)); + ic->rxquery++; + ilbackoff(ic); + } + break; + } + } + goto loop; +} + +void +ilcbinit(Ilcb *ic) +{ + ic->start = nrand(0x1000000); + ic->next = ic->start+1; + ic->recvd = 0; + ic->window = Defaultwin; + ic->unackedbytes = 0; + ic->unacked = nil; + ic->outoforder = nil; + ic->rexmit = 0; + ic->rxtot = 0; + ic->rxquery = 0; + ic->qtx = 1; + ic->fasttimeout = 0; + + /* timers */ + ic->delay = DefRtt<mdev = DefRtt<rate = DefByteRate<querytime = NOW + QueryTime; + ic->lastrecv = NOW; /* or we'll timeout right away */ + ilsettimeout(ic); +} + +char* +ilstart(Conv *c, int type, int fasttimeout) +{ + Ilcb *ic; + Ilpriv *ipriv; + char kpname[KNAMELEN]; + + ipriv = c->p->priv; + + if(ipriv->ackprocstarted == 0){ + qlock(&ipriv->apl); + if(ipriv->ackprocstarted == 0){ + sprint(kpname, "#I%dilack", c->p->f->dev); + kproc(kpname, ilackproc, c->p, 0); + ipriv->ackprocstarted = 1; + } + qunlock(&ipriv->apl); + } + + ic = (Ilcb*)c->ptcl; + ic->conv = c; + + if(ic->state != Ilclosed) + return nil; + + ilcbinit(ic); + + if(fasttimeout){ + /* timeout if we can't connect quickly */ + ic->fasttimeout = 1; + ic->timeout = NOW+Iltickms; + ic->rexmit = MaxRexmit - 4; + }; + + switch(type) { + default: + netlog(c->p->f, Logil, "il: start: type %d\n", type); + break; + case IL_LISTEN: + ic->state = Illistening; + iphtadd(&ipriv->ht, c); + break; + case IL_CONNECT: + ic->state = Ilsyncer; + iphtadd(&ipriv->ht, c); + ilsendctl(c, nil, Ilsync, ic->start, ic->recvd, 0); + break; + } + + return nil; +} + +void +ilfreeq(Ilcb *ic) +{ + Block *bp, *next; + + qlock(&ic->ackq); + for(bp = ic->unacked; bp; bp = next) { + next = bp->list; + freeblist(bp); + } + ic->unacked = nil; + qunlock(&ic->ackq); + + qlock(&ic->outo); + for(bp = ic->outoforder; bp; bp = next) { + next = bp->list; + freeblist(bp); + } + ic->outoforder = nil; + qunlock(&ic->outo); +} + +void +iladvise(Proto *il, Block *bp, char *msg) +{ + Ilhdr *h; + Ilcb *ic; + uchar source[IPaddrlen], dest[IPaddrlen]; + ushort psource; + Conv *s, **p; + + h = (Ilhdr*)(bp->rp); + + v4tov6(dest, h->dst); + v4tov6(source, h->src); + psource = nhgets(h->ilsrc); + + + /* Look for a connection, unfortunately the destination port is missing */ + qlock(il); + for(p = il->conv; *p; p++) { + s = *p; + if(s->lport == psource) + if(ipcmp(s->laddr, source) == 0) + if(ipcmp(s->raddr, dest) == 0){ + qunlock(il); + ic = (Ilcb*)s->ptcl; + switch(ic->state){ + case Ilsyncer: + ilhangup(s, msg); + break; + } + freeblist(bp); + return; + } + } + qunlock(il); + freeblist(bp); +} + +int +ilnextqt(Ilcb *ic) +{ + int x; + + qlock(&ic->ackq); + x = ic->qtx; + if(++x > Nqt) + x = 1; + ic->qtx = x; + ic->qt[x] = ic->next-1; /* highest xmitted packet */ + ic->qt[0] = ic->qt[x]; /* compatibility with old implementations */ + qunlock(&ic->ackq); + + return x; +} + +/* calculate scale constants that converts fast ticks to ms (more or less) */ +static void +inittimescale(void) +{ + uvlong hz; + + fastticks(&hz); + if(hz > 1000){ + scalediv = hz/1000; + scalemul = 1; + } else { + scalediv = 1; + scalemul = 1000/hz; + } +} + +void +ilinit(Fs *f) +{ + Proto *il; + + inittimescale(); + + il = smalloc(sizeof(Proto)); + il->priv = smalloc(sizeof(Ilpriv)); + il->name = "il"; + il->connect = ilconnect; + il->announce = ilannounce; + il->state = ilstate; + il->create = ilcreate; + il->close = ilclose; + il->rcv = iliput; + il->ctl = nil; + il->advise = iladvise; + il->stats = ilxstats; + il->inuse = ilinuse; + il->gc = nil; + il->ipproto = IP_ILPROTO; + il->nc = scalednconv(); + il->ptclsize = sizeof(Ilcb); + Fsproto(f, il); +} diff --git a/os/ip/ip.c b/os/ip/ip.c new file mode 100644 index 00000000..b0d3f5a6 --- /dev/null +++ b/os/ip/ip.c @@ -0,0 +1,805 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" + +typedef struct Ip4hdr Ip4hdr; +typedef struct IP IP; +typedef struct Fragment4 Fragment4; +typedef struct Fragment6 Fragment6; +typedef struct Ipfrag Ipfrag; + +enum +{ + IP4HDR = 20, /* sizeof(Ip4hdr) */ + IP6HDR = 40, /* sizeof(Ip6hdr) */ + IP_HLEN4 = 0x05, /* Header length in words */ + IP_DF = 0x4000, /* Don't fragment */ + IP_MF = 0x2000, /* More fragments */ + IP6FHDR = 8, /* sizeof(Fraghdr6) */ + IP_MAX = 64*1024, /* Maximum Internet packet size */ +}; + +#define BLKIPVER(xp) (((Ip4hdr*)((xp)->rp))->vihl&0xF0) + +struct Ip4hdr +{ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* ip->identification */ + uchar frag[2]; /* Fragment information */ + uchar ttl; /* Time to live */ + uchar proto; /* Protocol */ + uchar cksum[2]; /* Header checksum */ + uchar src[4]; /* IP source */ + uchar dst[4]; /* IP destination */ +}; + +/* MIB II counters */ +enum +{ + Forwarding, + DefaultTTL, + InReceives, + InHdrErrors, + InAddrErrors, + ForwDatagrams, + InUnknownProtos, + InDiscards, + InDelivers, + OutRequests, + OutDiscards, + OutNoRoutes, + ReasmTimeout, + ReasmReqds, + ReasmOKs, + ReasmFails, + FragOKs, + FragFails, + FragCreates, + + Nstats, +}; + +struct Fragment4 +{ + Block* blist; + Fragment4* next; + ulong src; + ulong dst; + ushort id; + ulong age; +}; + +struct Fragment6 +{ + Block* blist; + Fragment6* next; + uchar src[IPaddrlen]; + uchar dst[IPaddrlen]; + uint id; + ulong age; +}; + +struct Ipfrag +{ + ushort foff; + ushort flen; +}; + +/* an instance of IP */ +struct IP +{ + ulong stats[Nstats]; + + QLock fraglock4; + Fragment4* flisthead4; + Fragment4* fragfree4; + Ref id4; + + QLock fraglock6; + Fragment6* flisthead6; + Fragment6* fragfree6; + Ref id6; + + int iprouting; /* true if we route like a gateway */ +}; + +static char *statnames[] = +{ +[Forwarding] "Forwarding", +[DefaultTTL] "DefaultTTL", +[InReceives] "InReceives", +[InHdrErrors] "InHdrErrors", +[InAddrErrors] "InAddrErrors", +[ForwDatagrams] "ForwDatagrams", +[InUnknownProtos] "InUnknownProtos", +[InDiscards] "InDiscards", +[InDelivers] "InDelivers", +[OutRequests] "OutRequests", +[OutDiscards] "OutDiscards", +[OutNoRoutes] "OutNoRoutes", +[ReasmTimeout] "ReasmTimeout", +[ReasmReqds] "ReasmReqds", +[ReasmOKs] "ReasmOKs", +[ReasmFails] "ReasmFails", +[FragOKs] "FragOKs", +[FragFails] "FragFails", +[FragCreates] "FragCreates", +}; + +#define BLKIP(xp) ((Ip4hdr*)((xp)->rp)) +/* + * This sleazy macro relies on the media header size being + * larger than sizeof(Ipfrag). ipreassemble checks this is true + */ +#define BKFG(xp) ((Ipfrag*)((xp)->base)) + +ushort ipcsum(uchar*); +Block* ip4reassemble(IP*, int, Block*, Ip4hdr*); +void ipfragfree4(IP*, Fragment4*); +Fragment4* ipfragallo4(IP*); + + +void +ip_init_6(Fs *f) +{ + V6params *v6p; + + v6p = smalloc(sizeof(V6params)); + + v6p->rp.mflag = 0; // default not managed + v6p->rp.oflag = 0; + v6p->rp.maxraint = 600000; // millisecs + v6p->rp.minraint = 200000; + v6p->rp.linkmtu = 0; // no mtu sent + v6p->rp.reachtime = 0; + v6p->rp.rxmitra = 0; + v6p->rp.ttl = MAXTTL; + v6p->rp.routerlt = 3*(v6p->rp.maxraint); + + v6p->hp.rxmithost = 1000; // v6 RETRANS_TIMER + + v6p->cdrouter = -1; + + f->v6p = v6p; + +} + +void +initfrag(IP *ip, int size) +{ + Fragment4 *fq4, *eq4; + Fragment6 *fq6, *eq6; + + ip->fragfree4 = (Fragment4*)malloc(sizeof(Fragment4) * size); + if(ip->fragfree4 == nil) + panic("initfrag"); + + eq4 = &ip->fragfree4[size]; + for(fq4 = ip->fragfree4; fq4 < eq4; fq4++) + fq4->next = fq4+1; + + ip->fragfree4[size-1].next = nil; + + ip->fragfree6 = (Fragment6*)malloc(sizeof(Fragment6) * size); + if(ip->fragfree6 == nil) + panic("initfrag"); + + eq6 = &ip->fragfree6[size]; + for(fq6 = ip->fragfree6; fq6 < eq6; fq6++) + fq6->next = fq6+1; + + ip->fragfree6[size-1].next = nil; +} + +void +ip_init(Fs *f) +{ + IP *ip; + + ip = smalloc(sizeof(IP)); + initfrag(ip, 100); + f->ip = ip; + + ip_init_6(f); +} + +void +iprouting(Fs *f, int on) +{ + f->ip->iprouting = on; + if(f->ip->iprouting==0) + f->ip->stats[Forwarding] = 2; + else + f->ip->stats[Forwarding] = 1; +} + +int +ipoput4(Fs *f, Block *bp, int gating, int ttl, int tos, Conv *c) +{ + Ipifc *ifc; + uchar *gate; + ulong fragoff; + Block *xp, *nb; + Ip4hdr *eh, *feh; + int lid, len, seglen, chunk, dlen, blklen, offset, medialen; + Route *r, *sr; + IP *ip; + int rv = 0; + + ip = f->ip; + + /* Fill out the ip header */ + eh = (Ip4hdr*)(bp->rp); + + ip->stats[OutRequests]++; + + /* Number of uchars in data and ip header to write */ + len = blocklen(bp); + + if(gating){ + chunk = nhgets(eh->length); + if(chunk > len){ + ip->stats[OutDiscards]++; + netlog(f, Logip, "short gated packet\n"); + goto free; + } + if(chunk < len) + len = chunk; + } + if(len >= IP_MAX){ + ip->stats[OutDiscards]++; + netlog(f, Logip, "exceeded ip max size %V\n", eh->dst); + goto free; + } + + r = v4lookup(f, eh->dst, c); + if(r == nil){ + ip->stats[OutNoRoutes]++; + netlog(f, Logip, "no interface %V\n", eh->dst); + rv = -1; + goto free; + } + + ifc = r->ifc; + if(r->type & (Rifc|Runi)) + gate = eh->dst; + else + if(r->type & (Rbcast|Rmulti)) { + gate = eh->dst; + sr = v4lookup(f, eh->src, nil); + if(sr != nil && (sr->type & Runi)) + ifc = sr->ifc; + } + else + gate = r->v4.gate; + + if(!gating) + eh->vihl = IP_VER4|IP_HLEN4; + eh->ttl = ttl; + if(!gating) + eh->tos = tos; + + if(!canrlock(ifc)) + goto free; + if(waserror()){ + runlock(ifc); + nexterror(); + } + if(ifc->m == nil) + goto raise; + + /* If we dont need to fragment just send it */ + medialen = ifc->maxtu - ifc->m->hsize; + if(len <= medialen) { + if(!gating) + hnputs(eh->id, incref(&ip->id4)); + hnputs(eh->length, len); + if(!gating){ + eh->frag[0] = 0; + eh->frag[1] = 0; + } + eh->cksum[0] = 0; + eh->cksum[1] = 0; + hnputs(eh->cksum, ipcsum(&eh->vihl)); + ifc->m->bwrite(ifc, bp, V4, gate); + runlock(ifc); + poperror(); + return 0; + } + +if((eh->frag[0] & (IP_DF>>8)) && !gating) print("%V: DF set\n", eh->dst); + + if(eh->frag[0] & (IP_DF>>8)){ + ip->stats[FragFails]++; + ip->stats[OutDiscards]++; + icmpcantfrag(f, bp, medialen); + netlog(f, Logip, "%V: eh->frag[0] & (IP_DF>>8)\n", eh->dst); + goto raise; + } + + seglen = (medialen - IP4HDR) & ~7; + if(seglen < 8){ + ip->stats[FragFails]++; + ip->stats[OutDiscards]++; + netlog(f, Logip, "%V seglen < 8\n", eh->dst); + goto raise; + } + + dlen = len - IP4HDR; + xp = bp; + if(gating) + lid = nhgets(eh->id); + else + lid = incref(&ip->id4); + + offset = IP4HDR; + while(xp != nil && offset && offset >= BLEN(xp)) { + offset -= BLEN(xp); + xp = xp->next; + } + xp->rp += offset; + + if(gating) + fragoff = nhgets(eh->frag)<<3; + else + fragoff = 0; + dlen += fragoff; + for(; fragoff < dlen; fragoff += seglen) { + nb = allocb(IP4HDR+seglen); + feh = (Ip4hdr*)(nb->rp); + + memmove(nb->wp, eh, IP4HDR); + nb->wp += IP4HDR; + + if((fragoff + seglen) >= dlen) { + seglen = dlen - fragoff; + hnputs(feh->frag, fragoff>>3); + } + else + hnputs(feh->frag, (fragoff>>3)|IP_MF); + + hnputs(feh->length, seglen + IP4HDR); + hnputs(feh->id, lid); + + /* Copy up the data area */ + chunk = seglen; + while(chunk) { + if(!xp) { + ip->stats[OutDiscards]++; + ip->stats[FragFails]++; + freeblist(nb); + netlog(f, Logip, "!xp: chunk %d\n", chunk); + goto raise; + } + blklen = chunk; + if(BLEN(xp) < chunk) + blklen = BLEN(xp); + memmove(nb->wp, xp->rp, blklen); + nb->wp += blklen; + xp->rp += blklen; + chunk -= blklen; + if(xp->rp == xp->wp) + xp = xp->next; + } + + feh->cksum[0] = 0; + feh->cksum[1] = 0; + hnputs(feh->cksum, ipcsum(&feh->vihl)); + ifc->m->bwrite(ifc, nb, V4, gate); + ip->stats[FragCreates]++; + } + ip->stats[FragOKs]++; +raise: + runlock(ifc); + poperror(); +free: + freeblist(bp); + return rv; +} + +void +ipiput4(Fs *f, Ipifc *ifc, Block *bp) +{ + int hl; + int hop, tos, proto, olen; + Ip4hdr *h; + Proto *p; + ushort frag; + int notforme; + uchar *dp, v6dst[IPaddrlen]; + IP *ip; + Route *r; + + if(BLKIPVER(bp) != IP_VER4) { + ipiput6(f, ifc, bp); + return; + } + + ip = f->ip; + ip->stats[InReceives]++; + + /* + * Ensure we have all the header info in the first + * block. Make life easier for other protocols by + * collecting up to the first 64 bytes in the first block. + */ + if(BLEN(bp) < 64) { + hl = blocklen(bp); + if(hl < IP4HDR) + hl = IP4HDR; + if(hl > 64) + hl = 64; + bp = pullupblock(bp, hl); + if(bp == nil) + return; + } + + h = (Ip4hdr*)(bp->rp); + + /* dump anything that whose header doesn't checksum */ + if((bp->flag & Bipck) == 0 && ipcsum(&h->vihl)) { + ip->stats[InHdrErrors]++; + netlog(f, Logip, "ip: checksum error %V\n", h->src); + freeblist(bp); + return; + } + v4tov6(v6dst, h->dst); + notforme = ipforme(f, v6dst) == 0; + + /* Check header length and version */ + if((h->vihl&0x0F) != IP_HLEN4) { + hl = (h->vihl&0xF)<<2; + if(hl < (IP_HLEN4<<2)) { + ip->stats[InHdrErrors]++; + netlog(f, Logip, "ip: %V bad hivl %ux\n", h->src, h->vihl); + freeblist(bp); + return; + } + /* If this is not routed strip off the options */ + if(notforme == 0) { + olen = nhgets(h->length); + dp = bp->rp + (hl - (IP_HLEN4<<2)); + memmove(dp, h, IP_HLEN4<<2); + bp->rp = dp; + h = (Ip4hdr*)(bp->rp); + h->vihl = (IP_VER4|IP_HLEN4); + hnputs(h->length, olen-hl+(IP_HLEN4<<2)); + } + } + + /* route */ + if(notforme) { + Conv conv; + + if(!ip->iprouting){ + freeb(bp); + return; + } + + /* don't forward to source's network */ + conv.r = nil; + r = v4lookup(f, h->dst, &conv); + if(r == nil || r->ifc == ifc){ + ip->stats[OutDiscards]++; + freeblist(bp); + return; + } + + /* don't forward if packet has timed out */ + hop = h->ttl; + if(hop < 1) { + ip->stats[InHdrErrors]++; + icmpttlexceeded(f, ifc->lifc->local, bp); + freeblist(bp); + return; + } + + /* reassemble if the interface expects it */ +if(r->ifc == nil) panic("nil route rfc"); + if(r->ifc->reassemble){ + frag = nhgets(h->frag); + if(frag) { + h->tos = 0; + if(frag & IP_MF) + h->tos = 1; + bp = ip4reassemble(ip, frag, bp, h); + if(bp == nil) + return; + h = (Ip4hdr*)(bp->rp); + } + } + + ip->stats[ForwDatagrams]++; + tos = h->tos; + hop = h->ttl; + ipoput4(f, bp, 1, hop - 1, tos, &conv); + return; + } + + frag = nhgets(h->frag); + if(frag) { + h->tos = 0; + if(frag & IP_MF) + h->tos = 1; + bp = ip4reassemble(ip, frag, bp, h); + if(bp == nil) + return; + h = (Ip4hdr*)(bp->rp); + } + + /* don't let any frag info go up the stack */ + h->frag[0] = 0; + h->frag[1] = 0; + + proto = h->proto; + p = Fsrcvpcol(f, proto); + if(p != nil && p->rcv != nil) { + ip->stats[InDelivers]++; + (*p->rcv)(p, ifc, bp); + return; + } + ip->stats[InDiscards]++; + ip->stats[InUnknownProtos]++; + freeblist(bp); +} + +int +ipstats(Fs *f, char *buf, int len) +{ + IP *ip; + char *p, *e; + int i; + + ip = f->ip; + ip->stats[DefaultTTL] = MAXTTL; + + p = buf; + e = p+len; + for(i = 0; i < Nstats; i++) + p = seprint(p, e, "%s: %lud\n", statnames[i], ip->stats[i]); + return p - buf; +} + +Block* +ip4reassemble(IP *ip, int offset, Block *bp, Ip4hdr *ih) +{ + int fend; + ushort id; + Fragment4 *f, *fnext; + ulong src, dst; + Block *bl, **l, *last, *prev; + int ovlap, len, fragsize, pktposn; + + src = nhgetl(ih->src); + dst = nhgetl(ih->dst); + id = nhgets(ih->id); + + /* + * block lists are too hard, pullupblock into a single block + */ + if(bp->next){ + bp = pullupblock(bp, blocklen(bp)); + ih = (Ip4hdr*)(bp->rp); + } + + qlock(&ip->fraglock4); + + /* + * find a reassembly queue for this fragment + */ + for(f = ip->flisthead4; f; f = fnext){ + fnext = f->next; /* because ipfragfree4 changes the list */ + if(f->src == src && f->dst == dst && f->id == id) + break; + if(f->age < NOW){ + ip->stats[ReasmTimeout]++; + ipfragfree4(ip, f); + } + } + + /* + * if this isn't a fragmented packet, accept it + * and get rid of any fragments that might go + * with it. + */ + if(!ih->tos && (offset & ~(IP_MF|IP_DF)) == 0) { + if(f != nil) { + ipfragfree4(ip, f); + ip->stats[ReasmFails]++; + } + qunlock(&ip->fraglock4); + return bp; + } + + if(bp->base+sizeof(Ipfrag) >= bp->rp){ + bp = padblock(bp, sizeof(Ipfrag)); + bp->rp += sizeof(Ipfrag); + } + + BKFG(bp)->foff = offset<<3; + BKFG(bp)->flen = nhgets(ih->length)-IP4HDR; + + /* First fragment allocates a reassembly queue */ + if(f == nil) { + f = ipfragallo4(ip); + f->id = id; + f->src = src; + f->dst = dst; + + f->blist = bp; + + qunlock(&ip->fraglock4); + ip->stats[ReasmReqds]++; + return nil; + } + + /* + * find the new fragment's position in the queue + */ + prev = nil; + l = &f->blist; + bl = f->blist; + while(bl != nil && BKFG(bp)->foff > BKFG(bl)->foff) { + prev = bl; + l = &bl->next; + bl = bl->next; + } + + /* Check overlap of a previous fragment - trim away as necessary */ + if(prev) { + ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff; + if(ovlap > 0) { + if(ovlap >= BKFG(bp)->flen) { + freeblist(bp); + qunlock(&ip->fraglock4); + return nil; + } + BKFG(prev)->flen -= ovlap; + } + } + + /* Link onto assembly queue */ + bp->next = *l; + *l = bp; + + /* Check to see if succeeding segments overlap */ + if(bp->next) { + l = &bp->next; + fend = BKFG(bp)->foff + BKFG(bp)->flen; + /* Take completely covered segments out */ + while(*l) { + ovlap = fend - BKFG(*l)->foff; + if(ovlap <= 0) + break; + if(ovlap < BKFG(*l)->flen) { + BKFG(*l)->flen -= ovlap; + BKFG(*l)->foff += ovlap; + /* move up ih hdrs */ + memmove((*l)->rp + ovlap, (*l)->rp, IP4HDR); + (*l)->rp += ovlap; + break; + } + last = (*l)->next; + (*l)->next = nil; + freeblist(*l); + *l = last; + } + } + + /* + * look for a complete packet. if we get to a fragment + * without IP_MF set, we're done. + */ + pktposn = 0; + for(bl = f->blist; bl; bl = bl->next) { + if(BKFG(bl)->foff != pktposn) + break; + if((BLKIP(bl)->frag[0]&(IP_MF>>8)) == 0) { + bl = f->blist; + len = nhgets(BLKIP(bl)->length); + bl->wp = bl->rp + len; + + /* Pullup all the fragment headers and + * return a complete packet + */ + for(bl = bl->next; bl; bl = bl->next) { + fragsize = BKFG(bl)->flen; + len += fragsize; + bl->rp += IP4HDR; + bl->wp = bl->rp + fragsize; + } + + bl = f->blist; + f->blist = nil; + ipfragfree4(ip, f); + ih = BLKIP(bl); + hnputs(ih->length, len); + qunlock(&ip->fraglock4); + ip->stats[ReasmOKs]++; + return bl; + } + pktposn += BKFG(bl)->flen; + } + qunlock(&ip->fraglock4); + return nil; +} + +/* + * ipfragfree4 - Free a list of fragments - assume hold fraglock4 + */ +void +ipfragfree4(IP *ip, Fragment4 *frag) +{ + Fragment4 *fl, **l; + + if(frag->blist) + freeblist(frag->blist); + + frag->src = 0; + frag->id = 0; + frag->blist = nil; + + l = &ip->flisthead4; + for(fl = *l; fl; fl = fl->next) { + if(fl == frag) { + *l = frag->next; + break; + } + l = &fl->next; + } + + frag->next = ip->fragfree4; + ip->fragfree4 = frag; + +} + +/* + * ipfragallo4 - allocate a reassembly queue - assume hold fraglock4 + */ +Fragment4 * +ipfragallo4(IP *ip) +{ + Fragment4 *f; + + while(ip->fragfree4 == nil) { + /* free last entry on fraglist */ + for(f = ip->flisthead4; f->next; f = f->next) + ; + ipfragfree4(ip, f); + } + f = ip->fragfree4; + ip->fragfree4 = f->next; + f->next = ip->flisthead4; + ip->flisthead4 = f; + f->age = NOW + 30000; + + return f; +} + +ushort +ipcsum(uchar *addr) +{ + int len; + ulong sum; + + sum = 0; + len = (addr[0]&0xf)<<2; + + while(len > 0) { + sum += addr[0]<<8 | addr[1] ; + len -= 2; + addr += 2; + } + + sum = (sum & 0xffff) + (sum >> 16); + sum = (sum & 0xffff) + (sum >> 16); + + return (sum^0xffff); +} diff --git a/os/ip/ip.h b/os/ip/ip.h new file mode 100644 index 00000000..ca49430a --- /dev/null +++ b/os/ip/ip.h @@ -0,0 +1,673 @@ +typedef struct Conv Conv; +typedef struct Fs Fs; +typedef union Hwaddr Hwaddr; +typedef struct IP IP; +typedef struct IPaux IPaux; +typedef struct Ipself Ipself; +typedef struct Ipselftab Ipselftab; +typedef struct Iplink Iplink; +typedef struct Iplifc Iplifc; +typedef struct Ipmulti Ipmulti; +typedef struct IProuter IProuter; +typedef struct Ipifc Ipifc; +typedef struct Iphash Iphash; +typedef struct Ipht Ipht; +typedef struct Netlog Netlog; +typedef struct Ifclog Ifclog; +typedef struct Medium Medium; +typedef struct Proto Proto; +typedef struct Arpent Arpent; +typedef struct Arp Arp; +typedef struct Route Route; + +typedef struct Routerparams Routerparams; +typedef struct Hostparams Hostparams; +typedef struct V6router V6router; +typedef struct V6params V6params; + +#pragma incomplete Arp +#pragma incomplete Ifclog +#pragma incomplete Ipself +#pragma incomplete Ipselftab +#pragma incomplete IP +#pragma incomplete Netlog + +enum +{ + Addrlen= 64, + Maxproto= 20, + Nhash= 64, + Maxincall= 5, + Nchans= 256, + MAClen= 16, /* longest mac address */ + + MAXTTL= 255, + DFLTTOS= 0, + + IPaddrlen= 16, + IPv4addrlen= 4, + IPv4off= 12, + IPllen= 4, + + /* ip versions */ + V4= 4, + V6= 6, + IP_VER4= 0x40, + IP_VER6= 0x60, + + /* 2^Lroot trees in the root table */ + Lroot= 10, + + Maxpath = 64, +}; + +enum +{ + Idle= 0, + Announcing= 1, + Announced= 2, + Connecting= 3, + Connected= 4, +}; + +/* + * one per conversation directory + */ +struct Conv +{ + QLock; + + int x; /* conversation index */ + Proto* p; + + int restricted; /* remote port is restricted */ + uint ttl; /* max time to live */ + uint tos; /* type of service */ + int ignoreadvice; /* don't terminate connection on icmp errors */ + + uchar ipversion; + uchar laddr[IPaddrlen]; /* local IP address */ + uchar raddr[IPaddrlen]; /* remote IP address */ + ushort lport; /* local port number */ + ushort rport; /* remote port number */ + + char *owner; /* protections */ + int perm; + int inuse; /* opens of listen/data/ctl */ + int length; + int state; + + /* udp specific */ + int headers; /* data src/dst headers in udp */ + int reliable; /* true if reliable udp */ + + Conv* incall; /* calls waiting to be listened for */ + Conv* next; + + Queue* rq; /* queued data waiting to be read */ + Queue* wq; /* queued data waiting to be written */ + Queue* eq; /* returned error packets */ + Queue* sq; /* snooping queue */ + Ref snoopers; /* number of processes with snoop open */ + + Rendez cr; + char cerr[ERRMAX]; + + QLock listenq; + Rendez listenr; + + Ipmulti *multi; /* multicast bindings for this interface */ + + void* ptcl; /* protocol specific stuff */ + + Route *r; /* last route used */ + ulong rgen; /* routetable generation for *r */ +}; + +struct Medium +{ + char *name; + int hsize; /* medium header size */ + int mintu; /* default min mtu */ + int maxtu; /* default max mtu */ + int maclen; /* mac address length */ + void (*bind)(Ipifc*, int, char**); + void (*unbind)(Ipifc*); + void (*bwrite)(Ipifc *ifc, Block *b, int version, uchar *ip); + + /* for arming interfaces to receive multicast */ + void (*addmulti)(Ipifc *ifc, uchar *a, uchar *ia); + void (*remmulti)(Ipifc *ifc, uchar *a, uchar *ia); + + /* process packets written to 'data' */ + void (*pktin)(Fs *f, Ipifc *ifc, Block *bp); + + /* routes for router boards */ + void (*addroute)(Ipifc *ifc, int, uchar*, uchar*, uchar*, int); + void (*remroute)(Ipifc *ifc, int, uchar*, uchar*); + void (*flushroutes)(Ipifc *ifc); + + /* for routing multicast groups */ + void (*joinmulti)(Ipifc *ifc, uchar *a, uchar *ia); + void (*leavemulti)(Ipifc *ifc, uchar *a, uchar *ia); + + /* address resolution */ + void (*ares)(Fs*, int, uchar*, uchar*, int, int); /* resolve */ + void (*areg)(Ipifc*, uchar*); /* register */ + + /* v6 address generation */ + void (*pref2addr)(uchar *pref, uchar *ea); + + int unbindonclose; /* if non-zero, unbind on last close */ +}; + +/* logical interface associated with a physical one */ +struct Iplifc +{ + uchar local[IPaddrlen]; + uchar mask[IPaddrlen]; + uchar remote[IPaddrlen]; + uchar net[IPaddrlen]; + uchar tentative; /* =1 => v6 dup disc on, =0 => confirmed unique */ + uchar onlink; /* =1 => onlink, =0 offlink. */ + uchar autoflag; /* v6 autonomous flag */ + long validlt; /* v6 valid lifetime */ + long preflt; /* v6 preferred lifetime */ + long origint; /* time when addr was added */ + Iplink *link; /* addresses linked to this lifc */ + Iplifc *next; +}; + +/* binding twixt Ipself and Iplifc */ +struct Iplink +{ + Ipself *self; + Iplifc *lifc; + Iplink *selflink; /* next link for this local address */ + Iplink *lifclink; /* next link for this ifc */ + ulong expire; + Iplink *next; /* free list */ + int ref; +}; + +/* rfc 2461, pp.40--43. */ + +/* default values, one per stack */ +struct Routerparams { + int mflag; + int oflag; + int maxraint; + int minraint; + int linkmtu; + int reachtime; + int rxmitra; + int ttl; + int routerlt; +}; + +struct Hostparams { + int rxmithost; +}; + +struct Ipifc +{ + RWlock; + + Conv *conv; /* link to its conversation structure */ + char dev[64]; /* device we're attached to */ + Medium *m; /* Media pointer */ + int maxtu; /* Maximum transfer unit */ + int mintu; /* Minumum tranfer unit */ + int mbps; /* megabits per second */ + void *arg; /* medium specific */ + int reassemble; /* reassemble IP packets before forwarding */ + + /* these are used so that we can unbind on the fly */ + Lock idlock; + uchar ifcid; /* incremented each 'bind/unbind/add/remove' */ + int ref; /* number of proc's using this ipifc */ + Rendez wait; /* where unbinder waits for ref == 0 */ + int unbinding; + + uchar mac[MAClen]; /* MAC address */ + + Iplifc *lifc; /* logical interfaces on this physical one */ + + ulong in, out; /* message statistics */ + ulong inerr, outerr; /* ... */ + + uchar sendra6; /* == 1 => send router advs on this ifc */ + uchar recvra6; /* == 1 => recv router advs on this ifc */ + Routerparams rp; /* router parameters as in RFC 2461, pp.40--43. + used only if node is router */ +}; + +/* + * one per multicast-lifc pair used by a Conv + */ +struct Ipmulti +{ + uchar ma[IPaddrlen]; + uchar ia[IPaddrlen]; + Ipmulti *next; +}; + +/* + * hash table for 2 ip addresses + 2 ports + */ +enum +{ + Nipht= 521, /* convenient prime */ + + IPmatchexact= 0, /* match on 4 tuple */ + IPmatchany, /* *!* */ + IPmatchport, /* *!port */ + IPmatchaddr, /* addr!* */ + IPmatchpa, /* addr!port */ +}; +struct Iphash +{ + Iphash *next; + Conv *c; + int match; +}; +struct Ipht +{ + Lock; + Iphash *tab[Nipht]; +}; +void iphtadd(Ipht*, Conv*); +void iphtrem(Ipht*, Conv*); +Conv* iphtlook(Ipht *ht, uchar *sa, ushort sp, uchar *da, ushort dp); + +/* + * one per multiplexed protocol + */ +struct Proto +{ + QLock; + char* name; /* protocol name */ + int x; /* protocol index */ + int ipproto; /* ip protocol type */ + + char* (*connect)(Conv*, char**, int); + char* (*announce)(Conv*, char**, int); + char* (*bind)(Conv*, char**, int); + int (*state)(Conv*, char*, int); + void (*create)(Conv*); + void (*close)(Conv*); + void (*rcv)(Proto*, Ipifc*, Block*); + char* (*ctl)(Conv*, char**, int); + void (*advise)(Proto*, Block*, char*); + int (*stats)(Proto*, char*, int); + int (*local)(Conv*, char*, int); + int (*remote)(Conv*, char*, int); + int (*inuse)(Conv*); + int (*gc)(Proto*); /* returns true if any conversations are freed */ + + Fs *f; /* file system this proto is part of */ + Conv **conv; /* array of conversations */ + int ptclsize; /* size of per protocol ctl block */ + int nc; /* number of conversations */ + int ac; + Qid qid; /* qid for protocol directory */ + ushort nextport; + ushort nextrport; + + void *priv; +}; + +/* + * Stream for sending packets to user level + */ +struct IProuter { + QLock; + int opens; + Queue *q; +}; + +/* + * one per IP protocol stack + */ +struct Fs +{ + RWlock; + int dev; + + int np; + Proto* p[Maxproto+1]; /* list of supported protocols */ + Proto* t2p[256]; /* vector of all protocols */ + Proto* ipifc; /* kludge for ipifcremroute & ipifcaddroute */ + Proto* ipmux; /* kludge for finding an ip multiplexor */ + + IP *ip; + Ipselftab *self; + Arp *arp; + V6params *v6p; + IProuter iprouter; + + Route *v4root[1<= 0. */ +}; + + +int Fsconnected(Conv*, char*); +Conv* Fsnewcall(Conv*, uchar*, ushort, uchar*, ushort, uchar); +int Fspcolstats(char*, int); +int Fsproto(Fs*, Proto*); +int Fsbuiltinproto(Fs*, uchar); +Conv* Fsprotoclone(Proto*, char*); +Proto* Fsrcvpcol(Fs*, uchar); +Proto* Fsrcvpcolx(Fs*, uchar); +char* Fsstdconnect(Conv*, char**, int); +char* Fsstdannounce(Conv*, char**, int); +char* Fsstdbind(Conv*, char**, int); +ulong scalednconv(void); + +/* + * logging + */ +enum +{ + Logip= 1<<1, + Logtcp= 1<<2, + Logfs= 1<<3, + Logil= 1<<4, + Logicmp= 1<<5, + Logudp= 1<<6, + Logcompress= 1<<7, + Logilmsg= 1<<8, + Loggre= 1<<9, + Logppp= 1<<10, + Logtcprxmt= 1<<11, + Logigmp= 1<<12, + Logudpmsg= 1<<13, + Logipmsg= 1<<14, + Logrudp= 1<<15, + Logrudpmsg= 1<<16, + Logesp= 1<<17, + Logtcpwin= 1<<18, +}; + +void netloginit(Fs*); +void netlogopen(Fs*); +void netlogclose(Fs*); +void netlogctl(Fs*, char*, int); +long netlogread(Fs*, void*, ulong, long); +void netlog(Fs*, int, char*, ...); +void ifcloginit(Fs*); +long ifclogread(Fs*, Chan *,void*, ulong, long); +void ifclog(Fs*, uchar *, int); +void ifclogopen(Fs*, Chan*); +void ifclogclose(Fs*, Chan*); + +/* + * iproute.c + */ +typedef struct RouteTree RouteTree; +typedef struct Routewalk Routewalk; +typedef struct V4route V4route; +typedef struct V6route V6route; + +enum +{ + + /* type bits */ + Rv4= (1<<0), /* this is a version 4 route */ + Rifc= (1<<1), /* this route is a directly connected interface */ + Rptpt= (1<<2), /* this route is a pt to pt interface */ + Runi= (1<<3), /* a unicast self address */ + Rbcast= (1<<4), /* a broadcast self address */ + Rmulti= (1<<5), /* a multicast self address */ + Rproxy= (1<<6), /* this route should be proxied */ +}; + +struct Routewalk +{ + int o; + int h; + char* p; + char* e; + void* state; + void (*walk)(Route*, Routewalk*); +}; + +struct RouteTree +{ + Route* right; + Route* left; + Route* mid; + uchar depth; + uchar type; + uchar ifcid; /* must match ifc->id */ + Ipifc *ifc; + char tag[4]; + int ref; +}; + +struct V4route +{ + ulong address; + ulong endaddress; + uchar gate[IPv4addrlen]; +}; + +struct V6route +{ + ulong address[IPllen]; + ulong endaddress[IPllen]; + uchar gate[IPaddrlen]; +}; + +struct Route +{ + RouteTree; + + union { + V6route v6; + V4route v4; + }; +}; +extern void v4addroute(Fs *f, char *tag, uchar *a, uchar *mask, uchar *gate, int type); +extern void v6addroute(Fs *f, char *tag, uchar *a, uchar *mask, uchar *gate, int type); +extern void v4delroute(Fs *f, uchar *a, uchar *mask, int dolock); +extern void v6delroute(Fs *f, uchar *a, uchar *mask, int dolock); +extern Route* v4lookup(Fs *f, uchar *a, Conv *c); +extern Route* v6lookup(Fs *f, uchar *a, Conv *c); +extern long routeread(Fs *f, char*, ulong, int); +extern long routewrite(Fs *f, Chan*, char*, int); +extern void routetype(int, char*); +extern void ipwalkroutes(Fs*, Routewalk*); +extern void convroute(Route*, uchar*, uchar*, uchar*, char*, int*); + +/* + * devip.c + */ + +/* + * Hanging off every ip channel's ->aux is the following structure. + * It maintains the state used by devip and iproute. + */ +struct IPaux +{ + char *owner; /* the user that did the attach */ + char tag[4]; +}; + +extern IPaux* newipaux(char*, char*); + +/* + * arp.c + */ +struct Arpent +{ + uchar ip[IPaddrlen]; + uchar mac[MAClen]; + Medium *type; /* media type */ + Arpent* hash; + Block* hold; + Block* last; + uint ctime; /* time entry was created or refreshed */ + uint utime; /* time entry was last used */ + uchar state; + Arpent *nextrxt; /* re-transmit chain */ + uint rtime; /* time for next retransmission */ + uchar rxtsrem; + Ipifc *ifc; + uchar ifcid; /* must match ifc->id */ +}; + +extern void arpinit(Fs*); +extern int arpread(Arp*, char*, ulong, int); +extern int arpwrite(Fs*, char*, int); +extern Arpent* arpget(Arp*, Block *bp, int version, Ipifc *ifc, uchar *ip, uchar *h); +extern void arprelease(Arp*, Arpent *a); +extern Block* arpresolve(Arp*, Arpent *a, Medium *type, uchar *mac); +extern void arpenter(Fs*, int version, uchar *ip, uchar *mac, int len, int norefresh); + +/* + * ipaux.c + */ + +extern int myetheraddr(uchar*, char*); +extern ulong parseip(uchar*, char*); +extern ulong parseipmask(uchar*, char*); +extern char* v4parseip(uchar*, char*); +extern void maskip(uchar *from, uchar *mask, uchar *to); +extern int parsemac(uchar *to, char *from, int len); +extern uchar* defmask(uchar*); +extern int isv4(uchar*); +extern void v4tov6(uchar *v6, uchar *v4); +extern int v6tov4(uchar *v4, uchar *v6); +extern int eipfmt(Fmt*); + +#define ipmove(x, y) memmove(x, y, IPaddrlen) +#define ipcmp(x, y) ( (x)[IPaddrlen-1] != (y)[IPaddrlen-1] || memcmp(x, y, IPaddrlen) ) + +extern uchar IPv4bcast[IPaddrlen]; +extern uchar IPv4bcastobs[IPaddrlen]; +extern uchar IPv4allsys[IPaddrlen]; +extern uchar IPv4allrouter[IPaddrlen]; +extern uchar IPnoaddr[IPaddrlen]; +extern uchar v4prefix[IPaddrlen]; +extern uchar IPallbits[IPaddrlen]; + +#define NOW TK2MS(MACHP(0)->ticks) + +/* + * media + */ +extern Medium ethermedium; +extern Medium nullmedium; +extern Medium pktmedium; +extern Medium tripmedium; + +/* + * ipifc.c + */ +extern Medium* ipfindmedium(char *name); +extern void addipmedium(Medium *med); +extern int ipforme(Fs*, uchar *addr); +extern int iptentative(Fs*, uchar *addr); +extern int ipisbm(uchar *); +extern int ipismulticast(uchar *); +extern Ipifc* findipifc(Fs*, uchar *remote, int type); +extern void findprimaryip(Fs*, uchar*); +extern void findlocalip(Fs*, uchar *local, uchar *remote); +extern int ipv4local(Ipifc *ifc, uchar *addr); +extern int ipv6local(Ipifc *ifc, uchar *addr); +extern int ipv6anylocal(Ipifc *ifc, uchar *addr); +extern Iplifc* iplocalonifc(Ipifc *ifc, uchar *ip); +extern int ipproxyifc(Fs *f, Ipifc *ifc, uchar *ip); +extern int ipismulticast(uchar *ip); +extern int ipisbooting(void); +extern int ipifccheckin(Ipifc *ifc, Medium *med); +extern void ipifccheckout(Ipifc *ifc); +extern int ipifcgrab(Ipifc *ifc); +extern void ipifcaddroute(Fs*, int, uchar*, uchar*, uchar*, int); +extern void ipifcremroute(Fs*, int, uchar*, uchar*); +extern void ipifcremmulti(Conv *c, uchar *ma, uchar *ia); +extern void ipifcaddmulti(Conv *c, uchar *ma, uchar *ia); +extern char* ipifcrem(Ipifc *ifc, char **argv, int argc); +extern char* ipifcadd(Ipifc *ifc, char **argv, int argc, int tentative, Iplifc *lifcp); +extern long ipselftabread(Fs*, char *a, ulong offset, int n); +extern char* ipifcaddpref6(Ipifc *ifc, char**argv, int argc); +extern void ipsendra6(Fs *f, int on); + +/* + * ip.c + */ +extern void iprouting(Fs*, int); +extern void icmpnoconv(Fs*, Block*); +extern void icmpcantfrag(Fs*, Block*, int); +extern void icmpttlexceeded(Fs*, uchar*, Block*); +extern ushort ipcsum(uchar*); +extern void ipiput4(Fs*, Ipifc*, Block*); +extern void ipiput6(Fs*, Ipifc*, Block*); +extern int ipoput4(Fs*, Block*, int, int, int, Conv*); +extern int ipoput6(Fs*, Block*, int, int, int, Conv*); +extern int ipstats(Fs*, char*, int); +extern ushort ptclbsum(uchar*, int); +extern ushort ptclcsum(Block*, int, int); +extern void ip_init(Fs*); +extern void update_mtucache(uchar*, ulong); +extern ulong restrict_mtu(uchar*, ulong); + +/* + * bootp.c + */ +char* (*bootp)(Ipifc*); +int (*bootpread)(char*, ulong, int); + +/* + * iprouter.c + */ +void useriprouter(Fs*, Ipifc*, Block*); +void iprouteropen(Fs*); +void iprouterclose(Fs*); +long iprouterread(Fs*, void*, int); + +/* + * resolving inferno/plan9 differences + */ +Chan* commonfdtochan(int, int, int, int); +char* commonuser(void); +char* commonerror(void); + +/* + * chandial.c + */ +extern Chan* chandial(char*, char*, char*, Chan**); + +/* + * global to all of the stack + */ +extern int debug; +extern void (*igmpreportfn)(Ipifc*, uchar*); diff --git a/os/ip/ipaux.c b/os/ip/ipaux.c new file mode 100644 index 00000000..2ddae041 --- /dev/null +++ b/os/ip/ipaux.c @@ -0,0 +1,730 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "ip.h" +#include "ipv6.h" + +/* + * well known IP addresses + */ +uchar IPv4bcast[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff +}; +uchar IPv4allsys[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0xff, 0xff, + 0xe0, 0, 0, 0x01 +}; +uchar IPv4allrouter[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0xff, 0xff, + 0xe0, 0, 0, 0x02 +}; +uchar IPallbits[IPaddrlen] = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff +}; + +uchar IPnoaddr[IPaddrlen]; + +/* + * prefix of all v4 addresses + */ +uchar v4prefix[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0xff, 0xff, + 0, 0, 0, 0 +}; + + +char *v6hdrtypes[Maxhdrtype] = +{ + [HBH] "HopbyHop", + [ICMP] "ICMP", + [IGMP] "IGMP", + [GGP] "GGP", + [IPINIP] "IP", + [ST] "ST", + [TCP] "TCP", + [UDP] "UDP", + [ISO_TP4] "ISO_TP4", + [RH] "Routinghdr", + [FH] "Fraghdr", + [IDRP] "IDRP", + [RSVP] "RSVP", + [AH] "Authhdr", + [ESP] "ESP", + [ICMPv6] "ICMPv6", + [NNH] "Nonexthdr", + [ISO_IP] "ISO_IP", + [IGRP] "IGRP", + [OSPF] "OSPF", +}; + +/* + * well known IPv6 addresses + */ +uchar v6Unspecified[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +uchar v6loopback[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x01 +}; +uchar v6linklocal[IPaddrlen] = { + 0xfe, 0x80, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +uchar v6linklocalmask[IPaddrlen] = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +int v6llpreflen = 8; // link-local prefix length +uchar v6sitelocal[IPaddrlen] = { + 0xfe, 0xc0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +uchar v6sitelocalmask[IPaddrlen] = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +int v6slpreflen = 6; // site-local prefix length +uchar v6glunicast[IPaddrlen] = { + 0x08, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +uchar v6multicast[IPaddrlen] = { + 0xff, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +uchar v6multicastmask[IPaddrlen] = { + 0xff, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +int v6mcpreflen = 1; // multicast prefix length +uchar v6allnodesN[IPaddrlen] = { + 0xff, 0x01, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x01 +}; +uchar v6allnodesNmask[IPaddrlen] = { + 0xff, 0xff, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +int v6aNpreflen = 2; // all nodes (N) prefix +uchar v6allnodesL[IPaddrlen] = { + 0xff, 0x02, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x01 +}; +uchar v6allnodesLmask[IPaddrlen] = { + 0xff, 0xff, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +int v6aLpreflen = 2; // all nodes (L) prefix +uchar v6allroutersN[IPaddrlen] = { + 0xff, 0x01, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x02 +}; +uchar v6allroutersL[IPaddrlen] = { + 0xff, 0x02, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x02 +}; +uchar v6allroutersS[IPaddrlen] = { + 0xff, 0x05, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x02 +}; +uchar v6solicitednode[IPaddrlen] = { + 0xff, 0x02, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x01, + 0xff, 0, 0, 0 +}; +uchar v6solicitednodemask[IPaddrlen] = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0x0, 0x0, 0x0 +}; +int v6snpreflen = 13; + + + + +ushort +ptclcsum(Block *bp, int offset, int len) +{ + uchar *addr; + ulong losum, hisum; + ushort csum; + int odd, blocklen, x; + + /* Correct to front of data area */ + while(bp != nil && offset && offset >= BLEN(bp)) { + offset -= BLEN(bp); + bp = bp->next; + } + if(bp == nil) + return 0; + + addr = bp->rp + offset; + blocklen = BLEN(bp) - offset; + + if(bp->next == nil) { + if(blocklen < len) + len = blocklen; + return ~ptclbsum(addr, len) & 0xffff; + } + + losum = 0; + hisum = 0; + + odd = 0; + while(len) { + x = blocklen; + if(len < x) + x = len; + + csum = ptclbsum(addr, x); + if(odd) + hisum += csum; + else + losum += csum; + odd = (odd+x) & 1; + len -= x; + + bp = bp->next; + if(bp == nil) + break; + blocklen = BLEN(bp); + addr = bp->rp; + } + + losum += hisum>>8; + losum += (hisum&0xff)<<8; + while((csum = losum>>16) != 0) + losum = csum + (losum & 0xffff); + + return ~losum & 0xffff; +} + +enum +{ + Isprefix= 16, +}; + +static uchar prefixvals[256] = +{ +[0x00] 0 | Isprefix, +[0x80] 1 | Isprefix, +[0xC0] 2 | Isprefix, +[0xE0] 3 | Isprefix, +[0xF0] 4 | Isprefix, +[0xF8] 5 | Isprefix, +[0xFC] 6 | Isprefix, +[0xFE] 7 | Isprefix, +[0xFF] 8 | Isprefix, +}; + +int +eipfmt(Fmt *f) +{ + char buf[5*8]; + static char *efmt = "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux"; + static char *ifmt = "%d.%d.%d.%d"; + uchar *p, ip[16]; + ulong *lp; + ushort s; + int i, j, n, eln, eli; + + switch(f->r) { + case 'E': /* Ethernet address */ + p = va_arg(f->args, uchar*); + return fmtprint(f, efmt, p[0], p[1], p[2], p[3], p[4], p[5]); + return fmtstrcpy(f, buf); + + case 'I': /* Ip address */ + p = va_arg(f->args, uchar*); +common: + if(memcmp(p, v4prefix, 12) == 0) + return fmtprint(f, ifmt, p[12], p[13], p[14], p[15]); + + /* find longest elision */ + eln = eli = -1; + for(i = 0; i < 16; i += 2){ + for(j = i; j < 16; j += 2) + if(p[j] != 0 || p[j+1] != 0) + break; + if(j > i && j - i > eln){ + eli = i; + eln = j - i; + } + } + + /* print with possible elision */ + n = 0; + for(i = 0; i < 16; i += 2){ + if(i == eli){ + n += sprint(buf+n, "::"); + i += eln; + if(i >= 16) + break; + } else if(i != 0) + n += sprint(buf+n, ":"); + s = (p[i]<<8) + p[i+1]; + n += sprint(buf+n, "%ux", s); + } + return fmtstrcpy(f, buf); + + case 'i': /* v6 address as 4 longs */ + lp = va_arg(f->args, ulong*); + for(i = 0; i < 4; i++) + hnputl(ip+4*i, *lp++); + p = ip; + goto common; + + case 'V': /* v4 ip address */ + p = va_arg(f->args, uchar*); + return fmtprint(f, ifmt, p[0], p[1], p[2], p[3]); + + case 'M': /* ip mask */ + p = va_arg(f->args, uchar*); + + /* look for a prefix mask */ + for(i = 0; i < 16; i++) + if(p[i] != 0xff) + break; + if(i < 16){ + if((prefixvals[p[i]] & Isprefix) == 0) + goto common; + for(j = i+1; j < 16; j++) + if(p[j] != 0) + goto common; + n = 8*i + (prefixvals[p[i]] & ~Isprefix); + } else + n = 8*16; + + /* got one, use /xx format */ + return fmtprint(f, "/%d", n); + } + return fmtstrcpy(f, "(eipfmt)"); +} + +#define CLASS(p) ((*(uchar*)(p))>>6) + +extern char* +v4parseip(uchar *to, char *from) +{ + int i; + char *p; + + p = from; + for(i = 0; i < 4 && *p; i++){ + to[i] = strtoul(p, &p, 0); + if(*p == '.') + p++; + } + switch(CLASS(to)){ + case 0: /* class A - 1 uchar net */ + case 1: + if(i == 3){ + to[3] = to[2]; + to[2] = to[1]; + to[1] = 0; + } else if(i == 2){ + to[3] = to[1]; + to[1] = 0; + } + break; + case 2: /* class B - 2 uchar net */ + if(i == 3){ + to[3] = to[2]; + to[2] = 0; + } + break; + } + return p; +} + +int +isv4(uchar *ip) +{ + return memcmp(ip, v4prefix, IPv4off) == 0; +} + + +/* + * the following routines are unrolled with no memset's to speed + * up the usual case + */ +void +v4tov6(uchar *v6, uchar *v4) +{ + v6[0] = 0; + v6[1] = 0; + v6[2] = 0; + v6[3] = 0; + v6[4] = 0; + v6[5] = 0; + v6[6] = 0; + v6[7] = 0; + v6[8] = 0; + v6[9] = 0; + v6[10] = 0xff; + v6[11] = 0xff; + v6[12] = v4[0]; + v6[13] = v4[1]; + v6[14] = v4[2]; + v6[15] = v4[3]; +} + +int +v6tov4(uchar *v4, uchar *v6) +{ + if(v6[0] == 0 + && v6[1] == 0 + && v6[2] == 0 + && v6[3] == 0 + && v6[4] == 0 + && v6[5] == 0 + && v6[6] == 0 + && v6[7] == 0 + && v6[8] == 0 + && v6[9] == 0 + && v6[10] == 0xff + && v6[11] == 0xff) + { + v4[0] = v6[12]; + v4[1] = v6[13]; + v4[2] = v6[14]; + v4[3] = v6[15]; + return 0; + } else { + memset(v4, 0, 4); + return -1; + } +} + +ulong +parseip(uchar *to, char *from) +{ + int i, elipsis = 0, v4 = 1; + ulong x; + char *p, *op; + + memset(to, 0, IPaddrlen); + p = from; + for(i = 0; i < 16 && *p; i+=2){ + op = p; + x = strtoul(p, &p, 16); + if(*p == '.' || (*p == 0 && i == 0)){ + p = v4parseip(to+i, op); + i += 4; + break; + } else { + to[i] = x>>8; + to[i+1] = x; + } + if(*p == ':'){ + v4 = 0; + if(*++p == ':'){ + elipsis = i+2; + p++; + } + } + } + if(i < 16){ + memmove(&to[elipsis+16-i], &to[elipsis], i-elipsis); + memset(&to[elipsis], 0, 16-i); + } + if(v4){ + to[10] = to[11] = 0xff; + return nhgetl(to+12); + } else + return 6; +} + +/* + * hack to allow ip v4 masks to be entered in the old + * style + */ +ulong +parseipmask(uchar *to, char *from) +{ + ulong x; + int i; + uchar *p; + + if(*from == '/'){ + /* as a number of prefix bits */ + i = atoi(from+1); + if(i < 0) + i = 0; + if(i > 128) + i = 128; + memset(to, 0, IPaddrlen); + for(p = to; i >= 8; i -= 8) + *p++ = 0xff; + if(i > 0) + *p = ~((1<<(8-i))-1); + x = nhgetl(to+IPv4off); + } else { + /* as a straight bit mask */ + x = parseip(to, from); + if(memcmp(to, v4prefix, IPv4off) == 0) + memset(to, 0xff, IPv4off); + } + return x; +} + +void +maskip(uchar *from, uchar *mask, uchar *to) +{ + int i; + + for(i = 0; i < IPaddrlen; i++) + to[i] = from[i] & mask[i]; +} + +uchar classmask[4][16] = { + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0x00,0x00,0x00, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0x00,0x00,0x00, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0x00,0x00, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0x00, +}; + +uchar* +defmask(uchar *ip) +{ + if(isv4(ip)) + return classmask[ip[IPv4off]>>6]; + else { + if(ipcmp(ip, v6loopback) == 0) + return IPallbits; + else if(memcmp(ip, v6linklocal, v6llpreflen) == 0) + return v6linklocalmask; + else if(memcmp(ip, v6sitelocal, v6slpreflen) == 0) + return v6sitelocalmask; + else if(memcmp(ip, v6solicitednode, v6snpreflen) == 0) + return v6solicitednodemask; + else if(memcmp(ip, v6multicast, v6mcpreflen) == 0) + return v6multicastmask; + return IPallbits; + } +} + +void +ipv62smcast(uchar *smcast, uchar *a) +{ + assert(IPaddrlen == 16); + memmove(smcast, v6solicitednode, IPaddrlen); + smcast[13] = a[13]; + smcast[14] = a[14]; + smcast[15] = a[15]; +} + + +/* + * parse a hex mac address + */ +int +parsemac(uchar *to, char *from, int len) +{ + char nip[4]; + char *p; + int i; + + p = from; + memset(to, 0, len); + for(i = 0; i < len; i++){ + if(p[0] == '\0' || p[1] == '\0') + break; + + nip[0] = p[0]; + nip[1] = p[1]; + nip[2] = '\0'; + p += 2; + + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return i; +} + +/* + * hashing tcp, udp, ... connections + */ +ulong +iphash(uchar *sa, ushort sp, uchar *da, ushort dp) +{ + return ((sa[IPaddrlen-1]<<24) ^ (sp << 16) ^ (da[IPaddrlen-1]<<8) ^ dp ) % Nhash; +} + +void +iphtadd(Ipht *ht, Conv *c) +{ + ulong hv; + Iphash *h; + + hv = iphash(c->raddr, c->rport, c->laddr, c->lport); + h = smalloc(sizeof(*h)); + if(ipcmp(c->raddr, IPnoaddr) != 0) + h->match = IPmatchexact; + else { + if(ipcmp(c->laddr, IPnoaddr) != 0){ + if(c->lport == 0) + h->match = IPmatchaddr; + else + h->match = IPmatchpa; + } else { + if(c->lport == 0) + h->match = IPmatchany; + else + h->match = IPmatchport; + } + } + h->c = c; + + lock(ht); + h->next = ht->tab[hv]; + ht->tab[hv] = h; + unlock(ht); +} + +void +iphtrem(Ipht *ht, Conv *c) +{ + ulong hv; + Iphash **l, *h; + + hv = iphash(c->raddr, c->rport, c->laddr, c->lport); + lock(ht); + for(l = &ht->tab[hv]; (*l) != nil; l = &(*l)->next) + if((*l)->c == c){ + h = *l; + (*l) = h->next; + free(h); + break; + } + unlock(ht); +} + +/* look for a matching conversation with the following precedence + * connected && raddr,rport,laddr,lport + * announced && laddr,lport + * announced && *,lport + * announced && laddr,* + * announced && *,* + */ +Conv* +iphtlook(Ipht *ht, uchar *sa, ushort sp, uchar *da, ushort dp) +{ + ulong hv; + Iphash *h; + Conv *c; + + /* exact 4 pair match (connection) */ + hv = iphash(sa, sp, da, dp); + lock(ht); + for(h = ht->tab[hv]; h != nil; h = h->next){ + if(h->match != IPmatchexact) + continue; + c = h->c; + if(sp == c->rport && dp == c->lport + && ipcmp(sa, c->raddr) == 0 && ipcmp(da, c->laddr) == 0){ + unlock(ht); + return c; + } + } + + /* match local address and port */ + hv = iphash(IPnoaddr, 0, da, dp); + for(h = ht->tab[hv]; h != nil; h = h->next){ + if(h->match != IPmatchpa) + continue; + c = h->c; + if(dp == c->lport && ipcmp(da, c->laddr) == 0){ + unlock(ht); + return c; + } + } + + /* match just port */ + hv = iphash(IPnoaddr, 0, IPnoaddr, dp); + for(h = ht->tab[hv]; h != nil; h = h->next){ + if(h->match != IPmatchport) + continue; + c = h->c; + if(dp == c->lport){ + unlock(ht); + return c; + } + } + + /* match local address */ + hv = iphash(IPnoaddr, 0, da, 0); + for(h = ht->tab[hv]; h != nil; h = h->next){ + if(h->match != IPmatchaddr) + continue; + c = h->c; + if(ipcmp(da, c->laddr) == 0){ + unlock(ht); + return c; + } + } + + /* look for something that matches anything */ + hv = iphash(IPnoaddr, 0, IPnoaddr, 0); + for(h = ht->tab[hv]; h != nil; h = h->next){ + if(h->match != IPmatchany) + continue; + c = h->c; + unlock(ht); + return c; + } + unlock(ht); + return nil; +} diff --git a/os/ip/ipifc.c b/os/ip/ipifc.c new file mode 100644 index 00000000..345c7404 --- /dev/null +++ b/os/ip/ipifc.c @@ -0,0 +1,1721 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" +#include "ipv6.h" + +#define DPRINT if(0)print + +enum { + Maxmedia = 32, + Nself = Maxmedia*5, + NHASH = (1<<6), + NCACHE = 256, + QMAX = 64*1024-1, +}; + +Medium *media[Maxmedia] = +{ + 0 +}; + +/* + * cache of local addresses (addresses we answer to) + */ +struct Ipself +{ + uchar a[IPaddrlen]; + Ipself *hnext; /* next address in the hash table */ + Iplink *link; /* binding twixt Ipself and Ipifc */ + ulong expire; + uchar type; /* type of address */ + int ref; + Ipself *next; /* free list */ +}; + +struct Ipselftab +{ + QLock; + int inited; + int acceptall; /* true if an interface has the null address */ + Ipself *hash[NHASH]; /* hash chains */ +}; + +/* + * Multicast addresses are chained onto a Chan so that + * we can remove them when the Chan is closed. + */ +typedef struct Ipmcast Ipmcast; +struct Ipmcast +{ + Ipmcast *next; + uchar ma[IPaddrlen]; /* multicast address */ + uchar ia[IPaddrlen]; /* interface address */ +}; + +/* quick hash for ip addresses */ +#define hashipa(a) ( ( ((a)[IPaddrlen-2]<<8) | (a)[IPaddrlen-1] )%NHASH ) + +static char tifc[] = "ifc "; + +static void addselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a, int type); +static void remselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a); +static char* ipifcjoinmulti(Ipifc *ifc, char **argv, int argc); +static char* ipifcleavemulti(Ipifc *ifc, char **argv, int argc); +static void ipifcregisterproxy(Fs*, Ipifc*, uchar*); +static char* ipifcremlifc(Ipifc*, Iplifc*); + +/* + * link in a new medium + */ +void +addipmedium(Medium *med) +{ + int i; + + for(i = 0; i < nelem(media)-1; i++) + if(media[i] == nil){ + media[i] = med; + break; + } +} + +/* + * find the medium with this name + */ +Medium* +ipfindmedium(char *name) +{ + Medium **mp; + + for(mp = media; *mp != nil; mp++) + if(strcmp((*mp)->name, name) == 0) + break; + return *mp; +} + +/* + * attach a device (or pkt driver) to the interface. + * called with c locked + */ +static char* +ipifcbind(Conv *c, char **argv, int argc) +{ + Ipifc *ifc; + Medium *m; + + if(argc < 2) + return Ebadarg; + + ifc = (Ipifc*)c->ptcl; + + /* bind the device to the interface */ + m = ipfindmedium(argv[1]); + if(m == nil) + return "unknown interface type"; + + wlock(ifc); + if(ifc->m != nil){ + wunlock(ifc); + return "interface already bound"; + } + if(waserror()){ + wunlock(ifc); + nexterror(); + } + + /* do medium specific binding */ + (*m->bind)(ifc, argc, argv); + + /* set the bound device name */ + if(argc > 2) + strncpy(ifc->dev, argv[2], sizeof(ifc->dev)); + else + sprint(ifc->dev, "%s%d", m->name, c->x); + ifc->dev[sizeof(ifc->dev)-1] = 0; + + /* set up parameters */ + ifc->m = m; + ifc->mintu = ifc->m->mintu; + ifc->maxtu = ifc->m->maxtu; + if(ifc->m->unbindonclose == 0) + ifc->conv->inuse++; + ifc->rp.mflag = 0; // default not managed + ifc->rp.oflag = 0; + ifc->rp.maxraint = 600000; // millisecs + ifc->rp.minraint = 200000; + ifc->rp.linkmtu = 0; // no mtu sent + ifc->rp.reachtime = 0; + ifc->rp.rxmitra = 0; + ifc->rp.ttl = MAXTTL; + ifc->rp.routerlt = 3*(ifc->rp.maxraint); + + /* any ancillary structures (like routes) no longer pertain */ + ifc->ifcid++; + + /* reopen all the queues closed by a previous unbind */ + qreopen(c->rq); + qreopen(c->eq); + qreopen(c->sq); + + wunlock(ifc); + poperror(); + + return nil; +} + +/* + * detach a device from an interface, close the interface + * called with ifc->conv closed + */ +static char* +ipifcunbind(Ipifc *ifc) +{ + char *err; + + if(waserror()){ + wunlock(ifc); + nexterror(); + } + wlock(ifc); + + /* dissociate routes */ + if(ifc->m != nil && ifc->m->unbindonclose == 0) + ifc->conv->inuse--; + ifc->ifcid++; + + /* disassociate device */ + if(ifc->m != nil && ifc->m->unbind) + (*ifc->m->unbind)(ifc); + memset(ifc->dev, 0, sizeof(ifc->dev)); + ifc->arg = nil; + ifc->reassemble = 0; + + /* close queues to stop queuing of packets */ + qclose(ifc->conv->rq); + qclose(ifc->conv->wq); + qclose(ifc->conv->sq); + + /* disassociate logical interfaces */ + while(ifc->lifc){ + err = ipifcremlifc(ifc, ifc->lifc); + if(err) + error(err); + } + + ifc->m = nil; + wunlock(ifc); + poperror(); + return nil; +} + + + +char sfixedformat[] = "device %s maxtu %d sendra %d recvra %d mflag %d oflag %d maxraint %d minraint %d linkmtu %d reachtime %d rxmitra %d ttl %d routerlt %d pktin %lud pktout %lud errin %lud errout %lud\n"; + +char slineformat[] = " %-40I %-10M %-40I %-12lud %-12lud\n"; + + +static int +ipifcstate(Conv *c, char *state, int n) +{ + Ipifc *ifc; + Iplifc *lifc; + int m; + + ifc = (Ipifc*)c->ptcl; + + m = snprint(state, n, sfixedformat, + ifc->dev, ifc->maxtu, ifc->sendra6, ifc->recvra6, + ifc->rp.mflag, ifc->rp.oflag, ifc->rp.maxraint, + ifc->rp.minraint, ifc->rp.linkmtu, ifc->rp.reachtime, + ifc->rp.rxmitra, ifc->rp.ttl, ifc->rp.routerlt, + ifc->in, ifc->out, ifc->inerr, ifc->outerr); + + rlock(ifc); + for(lifc = ifc->lifc; lifc && n > m; lifc = lifc->next) + m += snprint(state+m, n - m, slineformat, + lifc->local, lifc->mask, lifc->remote, + lifc->validlt, lifc->preflt); + if(ifc->lifc == nil) + m += snprint(state+m, n - m, "\n"); + runlock(ifc); + return m; +} + +static int +ipifclocal(Conv *c, char *state, int n) +{ + Ipifc *ifc; + Iplifc *lifc; + Iplink *link; + int m; + + ifc = (Ipifc*)c->ptcl; + + m = 0; + + rlock(ifc); + for(lifc = ifc->lifc; lifc; lifc = lifc->next){ + m += snprint(state+m, n - m, "%-40.40I ->", lifc->local); + for(link = lifc->link; link; link = link->lifclink) + m += snprint(state+m, n - m, " %-40.40I", link->self->a); + m += snprint(state+m, n - m, "\n"); + } + runlock(ifc); + return m; +} + +static int +ipifcinuse(Conv *c) +{ + Ipifc *ifc; + + ifc = (Ipifc*)c->ptcl; + return ifc->m != nil; +} + +/* + * called when a process writes to an interface's 'data' + */ +static void +ipifckick(void *x) +{ + Conv *c = x; + Block *bp; + Ipifc *ifc; + + bp = qget(c->wq); + if(bp == nil) + return; + + ifc = (Ipifc*)c->ptcl; + if(!canrlock(ifc)){ + freeb(bp); + return; + } + if(waserror()){ + runlock(ifc); + nexterror(); + } + if(ifc->m == nil || ifc->m->pktin == nil) + freeb(bp); + else + (*ifc->m->pktin)(c->p->f, ifc, bp); + runlock(ifc); + poperror(); +} + +/* + * called when a new ipifc structure is created + */ +static void +ipifccreate(Conv *c) +{ + Ipifc *ifc; + + c->rq = qopen(QMAX, 0, 0, 0); + c->sq = qopen(2*QMAX, 0, 0, 0); + c->wq = qopen(QMAX, Qkick, ipifckick, c); + ifc = (Ipifc*)c->ptcl; + ifc->conv = c; + ifc->unbinding = 0; + ifc->m = nil; + ifc->reassemble = 0; +} + +/* + * called after last close of ipifc data or ctl + * called with c locked, we must unlock + */ +static void +ipifcclose(Conv *c) +{ + Ipifc *ifc; + Medium *m; + + ifc = (Ipifc*)c->ptcl; + m = ifc->m; + if(m != nil && m->unbindonclose) + ipifcunbind(ifc); +} + +/* + * change an interface's mtu + */ +char* +ipifcsetmtu(Ipifc *ifc, char **argv, int argc) +{ + int mtu; + + if(argc < 2) + return Ebadarg; + if(ifc->m == nil) + return Ebadarg; + mtu = strtoul(argv[1], 0, 0); + if(mtu < ifc->m->mintu || mtu > ifc->m->maxtu) + return Ebadarg; + ifc->maxtu = mtu; + return nil; +} + +/* + * add an address to an interface. + */ +char* +ipifcadd(Ipifc *ifc, char **argv, int argc, int tentative, Iplifc *lifcp) +{ + uchar ip[IPaddrlen], mask[IPaddrlen], rem[IPaddrlen]; + uchar bcast[IPaddrlen], net[IPaddrlen]; + Iplifc *lifc, **l; + int i, type, mtu; + Fs *f; + int sendnbrdisc = 0; + + if(ifc->m == nil) + return "ipifc not yet bound to device"; + + f = ifc->conv->p->f; + + type = Rifc; + memset(ip, 0, IPaddrlen); + memset(mask, 0, IPaddrlen); + memset(rem, 0, IPaddrlen); + switch(argc){ + case 6: + if(strcmp(argv[5], "proxy") == 0) + type |= Rproxy; + /* fall through */ + case 5: + mtu = strtoul(argv[4], 0, 0); + if(mtu >= ifc->m->mintu && mtu <= ifc->m->maxtu) + ifc->maxtu = mtu; + /* fall through */ + case 4: + parseip(ip, argv[1]); + parseipmask(mask, argv[2]); + parseip(rem, argv[3]); + maskip(rem, mask, net); + break; + case 3: + parseip(ip, argv[1]); + parseipmask(mask, argv[2]); + maskip(ip, mask, rem); + maskip(rem, mask, net); + break; + case 2: + parseip(ip, argv[1]); + memmove(mask, defmask(ip), IPaddrlen); + maskip(ip, mask, rem); + maskip(rem, mask, net); + break; + default: + return Ebadarg; + break; + } + if(isv4(ip)) + tentative = 0; + wlock(ifc); + + /* ignore if this is already a local address for this ifc */ + for(lifc = ifc->lifc; lifc; lifc = lifc->next) { + if(ipcmp(lifc->local, ip) == 0) { + if(lifc->tentative != tentative) + lifc->tentative = tentative; + if(lifcp != nil) { + lifc->onlink = lifcp->onlink; + lifc->autoflag = lifcp->autoflag; + lifc->validlt = lifcp->validlt; + lifc->preflt = lifcp->preflt; + lifc->origint = lifcp->origint; + } + goto out; + } + } + + /* add the address to the list of logical ifc's for this ifc */ + lifc = smalloc(sizeof(Iplifc)); + ipmove(lifc->local, ip); + ipmove(lifc->mask, mask); + ipmove(lifc->remote, rem); + ipmove(lifc->net, net); + lifc->tentative = tentative; + if(lifcp != nil) { + lifc->onlink = lifcp->onlink; + lifc->autoflag = lifcp->autoflag; + lifc->validlt = lifcp->validlt; + lifc->preflt = lifcp->preflt; + lifc->origint = lifcp->origint; + } + else { // default values + lifc->onlink = 1; + lifc->autoflag = 1; + lifc->validlt = 0xffffffff; + lifc->preflt = 0xffffffff; + lifc->origint = NOW / 10^3; + } + lifc->next = nil; + + for(l = &ifc->lifc; *l; l = &(*l)->next) + ; + *l = lifc; + + /* check for point-to-point interface */ + if(ipcmp(ip, v6loopback)) /* skip v6 loopback, it's a special address */ + if(ipcmp(mask, IPallbits) == 0) + type |= Rptpt; + + /* add local routes */ + if(isv4(ip)) + v4addroute(f, tifc, rem+IPv4off, mask+IPv4off, rem+IPv4off, type); + else + v6addroute(f, tifc, rem, mask, rem, type); + + addselfcache(f, ifc, lifc, ip, Runi); + + if((type & (Rproxy|Rptpt)) == (Rproxy|Rptpt)){ + ipifcregisterproxy(f, ifc, rem); + goto out; + } + + if(isv4(ip) || ipcmp(ip, IPnoaddr) == 0) { + /* add subnet directed broadcast address to the self cache */ + for(i = 0; i < IPaddrlen; i++) + bcast[i] = (ip[i] & mask[i]) | ~mask[i]; + addselfcache(f, ifc, lifc, bcast, Rbcast); + + /* add subnet directed network address to the self cache */ + for(i = 0; i < IPaddrlen; i++) + bcast[i] = (ip[i] & mask[i]) & mask[i]; + addselfcache(f, ifc, lifc, bcast, Rbcast); + + /* add network directed broadcast address to the self cache */ + memmove(mask, defmask(ip), IPaddrlen); + for(i = 0; i < IPaddrlen; i++) + bcast[i] = (ip[i] & mask[i]) | ~mask[i]; + addselfcache(f, ifc, lifc, bcast, Rbcast); + + /* add network directed network address to the self cache */ + memmove(mask, defmask(ip), IPaddrlen); + for(i = 0; i < IPaddrlen; i++) + bcast[i] = (ip[i] & mask[i]) & mask[i]; + addselfcache(f, ifc, lifc, bcast, Rbcast); + + addselfcache(f, ifc, lifc, IPv4bcast, Rbcast); + } + else { + if(ipcmp(ip, v6loopback) == 0) { + /* add node-local mcast address */ + addselfcache(f, ifc, lifc, v6allnodesN, Rmulti); + + /* add route for all node multicast */ + v6addroute(f, tifc, v6allnodesN, v6allnodesNmask, v6allnodesN, Rmulti); + } + + /* add all nodes multicast address */ + addselfcache(f, ifc, lifc, v6allnodesL, Rmulti); + + /* add route for all nodes multicast */ + v6addroute(f, tifc, v6allnodesL, v6allnodesLmask, v6allnodesL, Rmulti); + + /* add solicited-node multicast address */ + ipv62smcast(bcast, ip); + addselfcache(f, ifc, lifc, bcast, Rmulti); + + sendnbrdisc = 1; + } + + /* register the address on this network for address resolution */ + if(isv4(ip) && ifc->m->areg != nil) + (*ifc->m->areg)(ifc, ip); + +out: + wunlock(ifc); + if(tentative && sendnbrdisc) + icmpns(f, 0, SRC_UNSPEC, ip, TARG_MULTI, ifc->mac); + return nil; +} + +/* + * remove a logical interface from an ifc + * always called with ifc wlock'd + */ +static char* +ipifcremlifc(Ipifc *ifc, Iplifc *lifc) +{ + Iplifc **l; + Fs *f; + + f = ifc->conv->p->f; + + /* + * find address on this interface and remove from chain. + * for pt to pt we actually specify the remote address as the + * addresss to remove. + */ + for(l = &ifc->lifc; *l != nil && *l != lifc; l = &(*l)->next) + ; + if(*l == nil) + return "address not on this interface"; + *l = lifc->next; + + /* disassociate any addresses */ + while(lifc->link) + remselfcache(f, ifc, lifc, lifc->link->self->a); + + /* remove the route for this logical interface */ + if(isv4(lifc->local)) + v4delroute(f, lifc->remote+IPv4off, lifc->mask+IPv4off, 1); + else { + v6delroute(f, lifc->remote, lifc->mask, 1); + if(ipcmp(lifc->local, v6loopback) == 0) + /* remove route for all node multicast */ + v6delroute(f, v6allnodesN, v6allnodesNmask, 1); + else if(memcmp(lifc->local, v6linklocal, v6llpreflen) == 0) + /* remove route for all link multicast */ + v6delroute(f, v6allnodesL, v6allnodesLmask, 1); + } + + free(lifc); + return nil; + +} + +/* + * remove an address from an interface. + * called with c locked + */ +char* +ipifcrem(Ipifc *ifc, char **argv, int argc) +{ + uchar ip[IPaddrlen]; + uchar mask[IPaddrlen]; + uchar rem[IPaddrlen]; + Iplifc *lifc; + char *rv; + + if(argc < 3) + return Ebadarg; + + parseip(ip, argv[1]); + parseipmask(mask, argv[2]); + if(argc < 4) + maskip(ip, mask, rem); + else + parseip(rem, argv[3]); + + wlock(ifc); + + /* + * find address on this interface and remove from chain. + * for pt to pt we actually specify the remote address as the + * addresss to remove. + */ + for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next) { + if (memcmp(ip, lifc->local, IPaddrlen) == 0 + && memcmp(mask, lifc->mask, IPaddrlen) == 0 + && memcmp(rem, lifc->remote, IPaddrlen) == 0) + break; + } + + rv = ipifcremlifc(ifc, lifc); + wunlock(ifc); + return rv; +} + +/* + * distribute routes to active interfaces like the + * TRIP linecards + */ +void +ipifcaddroute(Fs *f, int vers, uchar *addr, uchar *mask, uchar *gate, int type) +{ + Medium *m; + Conv **cp, **e; + Ipifc *ifc; + + e = &f->ipifc->conv[f->ipifc->nc]; + for(cp = f->ipifc->conv; cp < e; cp++){ + if(*cp != nil) { + ifc = (Ipifc*)(*cp)->ptcl; + m = ifc->m; + if(m == nil) + continue; + if(m->addroute != nil) + m->addroute(ifc, vers, addr, mask, gate, type); + } + } +} + +void +ipifcremroute(Fs *f, int vers, uchar *addr, uchar *mask) +{ + Medium *m; + Conv **cp, **e; + Ipifc *ifc; + + e = &f->ipifc->conv[f->ipifc->nc]; + for(cp = f->ipifc->conv; cp < e; cp++){ + if(*cp != nil) { + ifc = (Ipifc*)(*cp)->ptcl; + m = ifc->m; + if(m == nil) + continue; + if(m->remroute != nil) + m->remroute(ifc, vers, addr, mask); + } + } +} + +/* + * associate an address with the interface. This wipes out any previous + * addresses. This is a macro that means, remove all the old interfaces + * and add a new one. + */ +static char* +ipifcconnect(Conv* c, char **argv, int argc) +{ + char *err; + Ipifc *ifc; + + ifc = (Ipifc*)c->ptcl; + + if(ifc->m == nil) + return "ipifc not yet bound to device"; + + if(waserror()){ + wunlock(ifc); + nexterror(); + } + wlock(ifc); + while(ifc->lifc){ + err = ipifcremlifc(ifc, ifc->lifc); + if(err) + error(err); + } + wunlock(ifc); + poperror(); + + err = ipifcadd(ifc, argv, argc, 0, nil); + if(err) + return err; + + Fsconnected(c, nil); + + return nil; +} + +char* +ipifcsetpar6(Ipifc *ifc, char **argv, int argc) +{ + int i, argsleft, vmax = ifc->rp.maxraint, vmin = ifc->rp.minraint; + + argsleft = argc - 1; + i = 1; + + if(argsleft % 2 != 0) + return Ebadarg; + + while (argsleft > 1) { + if(strcmp(argv[i],"recvra")==0) + ifc->recvra6 = (atoi(argv[i+1]) != 0); + else if(strcmp(argv[i],"sendra")==0) + ifc->sendra6 = (atoi(argv[i+1]) != 0); + else if(strcmp(argv[i],"mflag")==0) + ifc->rp.mflag = (atoi(argv[i+1]) != 0); + else if(strcmp(argv[i],"oflag")==0) + ifc->rp.oflag = (atoi(argv[i+1]) != 0); + else if(strcmp(argv[i],"maxraint")==0) + ifc->rp.maxraint = atoi(argv[i+1]); + else if(strcmp(argv[i],"minraint")==0) + ifc->rp.minraint = atoi(argv[i+1]); + else if(strcmp(argv[i],"linkmtu")==0) + ifc->rp.linkmtu = atoi(argv[i+1]); + else if(strcmp(argv[i],"reachtime")==0) + ifc->rp.reachtime = atoi(argv[i+1]); + else if(strcmp(argv[i],"rxmitra")==0) + ifc->rp.rxmitra = atoi(argv[i+1]); + else if(strcmp(argv[i],"ttl")==0) + ifc->rp.ttl = atoi(argv[i+1]); + else if(strcmp(argv[i],"routerlt")==0) + ifc->rp.routerlt = atoi(argv[i+1]); + else + return Ebadarg; + + argsleft -= 2; + i += 2; + } + + // consistency check + if(ifc->rp.maxraint < ifc->rp.minraint) { + ifc->rp.maxraint = vmax; + ifc->rp.minraint = vmin; + return Ebadarg; + } + + return nil; +} + +char* +ipifcsendra6(Ipifc *ifc, char **argv, int argc) +{ + int i; + + i = 0; + if(argc > 1) + i = atoi(argv[1]); + ifc->sendra6 = (i!=0); + return nil; +} + +char* +ipifcrecvra6(Ipifc *ifc, char **argv, int argc) +{ + int i; + + i = 0; + if(argc > 1) + i = atoi(argv[1]); + ifc->recvra6 = (i!=0); + return nil; +} + +/* + * non-standard control messages. + * called with c locked. + */ +static char* +ipifcctl(Conv* c, char**argv, int argc) +{ + Ipifc *ifc; + int i; + + ifc = (Ipifc*)c->ptcl; + if(strcmp(argv[0], "add") == 0) + return ipifcadd(ifc, argv, argc, 0, nil); + else if(strcmp(argv[0], "bootp") == 0) + return bootp(ifc); + else if(strcmp(argv[0], "try") == 0) + return ipifcadd(ifc, argv, argc, 1, nil); + else if(strcmp(argv[0], "remove") == 0) + return ipifcrem(ifc, argv, argc); + else if(strcmp(argv[0], "unbind") == 0) + return ipifcunbind(ifc); + else if(strcmp(argv[0], "joinmulti") == 0) + return ipifcjoinmulti(ifc, argv, argc); + else if(strcmp(argv[0], "leavemulti") == 0) + return ipifcleavemulti(ifc, argv, argc); + else if(strcmp(argv[0], "mtu") == 0) + return ipifcsetmtu(ifc, argv, argc); + else if(strcmp(argv[0], "reassemble") == 0){ + ifc->reassemble = 1; + return nil; + } + else if(strcmp(argv[0], "iprouting") == 0){ + i = 1; + if(argc > 1) + i = atoi(argv[1]); + iprouting(c->p->f, i); + return nil; + } + else if(strcmp(argv[0], "addpref6") == 0) + return ipifcaddpref6(ifc, argv, argc); + else if(strcmp(argv[0], "setpar6") == 0) + return ipifcsetpar6(ifc, argv, argc); + else if(strcmp(argv[0], "sendra6") == 0) + return ipifcsendra6(ifc, argv, argc); + else if(strcmp(argv[0], "recvra6") == 0) + return ipifcrecvra6(ifc, argv, argc); + return "unsupported ctl"; +} + +ipifcstats(Proto *ipifc, char *buf, int len) +{ + return ipstats(ipifc->f, buf, len); +} + +void +ipifcinit(Fs *f) +{ + Proto *ipifc; + + ipifc = smalloc(sizeof(Proto)); + ipifc->name = "ipifc"; + ipifc->connect = ipifcconnect; + ipifc->announce = nil; + ipifc->bind = ipifcbind; + ipifc->state = ipifcstate; + ipifc->create = ipifccreate; + ipifc->close = ipifcclose; + ipifc->rcv = nil; + ipifc->ctl = ipifcctl; + ipifc->advise = nil; + ipifc->stats = ipifcstats; + ipifc->inuse = ipifcinuse; + ipifc->local = ipifclocal; + ipifc->ipproto = -1; + ipifc->nc = Maxmedia; + ipifc->ptclsize = sizeof(Ipifc); + + f->ipifc = ipifc; /* hack for ipifcremroute, findipifc, ... */ + f->self = smalloc(sizeof(Ipselftab)); /* hack for ipforme */ + + Fsproto(f, ipifc); +} + +/* + * add to self routing cache + * called with c locked + */ +static void +addselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a, int type) +{ + Ipself *p; + Iplink *lp; + int h; + + qlock(f->self); + + /* see if the address already exists */ + h = hashipa(a); + for(p = f->self->hash[h]; p; p = p->next) + if(memcmp(a, p->a, IPaddrlen) == 0) + break; + + /* allocate a local address and add to hash chain */ + if(p == nil){ + p = smalloc(sizeof(*p)); + ipmove(p->a, a); + p->type = type; + p->next = f->self->hash[h]; + f->self->hash[h] = p; + + /* if the null address, accept all packets */ + if(ipcmp(a, v4prefix) == 0 || ipcmp(a, IPnoaddr) == 0) + f->self->acceptall = 1; + } + + /* look for a link for this lifc */ + for(lp = p->link; lp; lp = lp->selflink) + if(lp->lifc == lifc) + break; + + /* allocate a lifc-to-local link and link to both */ + if(lp == nil){ + lp = smalloc(sizeof(*lp)); + lp->ref = 1; + lp->lifc = lifc; + lp->self = p; + lp->selflink = p->link; + p->link = lp; + lp->lifclink = lifc->link; + lifc->link = lp; + + /* add to routing table */ + if(isv4(a)) + v4addroute(f, tifc, a+IPv4off, IPallbits+IPv4off, a+IPv4off, type); + else + v6addroute(f, tifc, a, IPallbits, a, type); + + if((type & Rmulti) && ifc->m->addmulti != nil) + (*ifc->m->addmulti)(ifc, a, lifc->local); + } else { + lp->ref++; + } + + qunlock(f->self); +} + +/* + * These structures are unlinked from their chains while + * other threads may be using them. To avoid excessive locking, + * just put them aside for a while before freeing them. + * called with f->self locked + */ +static Iplink *freeiplink; +static Ipself *freeipself; + +static void +iplinkfree(Iplink *p) +{ + Iplink **l, *np; + ulong now = NOW; + + l = &freeiplink; + for(np = *l; np; np = *l){ + if(np->expire > now){ + *l = np->next; + free(np); + continue; + } + l = &np->next; + } + p->expire = now + 5000; /* give other threads 5 secs to get out */ + p->next = nil; + *l = p; +} +static void +ipselffree(Ipself *p) +{ + Ipself **l, *np; + ulong now = NOW; + + l = &freeipself; + for(np = *l; np; np = *l){ + if(np->expire > now){ + *l = np->next; + free(np); + continue; + } + l = &np->next; + } + p->expire = now + 5000; /* give other threads 5 secs to get out */ + p->next = nil; + *l = p; +} + +/* + * Decrement reference for this address on this link. + * Unlink from selftab if this is the last ref. + * called with c locked + */ +static void +remselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a) +{ + Ipself *p, **l; + Iplink *link, **l_self, **l_lifc; + + qlock(f->self); + + /* find the unique selftab entry */ + l = &f->self->hash[hashipa(a)]; + for(p = *l; p; p = *l){ + if(ipcmp(p->a, a) == 0) + break; + l = &p->next; + } + + if(p == nil) + goto out; + + /* + * walk down links from an ifc looking for one + * that matches the selftab entry + */ + l_lifc = &lifc->link; + for(link = *l_lifc; link; link = *l_lifc){ + if(link->self == p) + break; + l_lifc = &link->lifclink; + } + + if(link == nil) + goto out; + + /* + * walk down the links from the selftab looking for + * the one we just found + */ + l_self = &p->link; + for(link = *l_self; link; link = *l_self){ + if(link == *(l_lifc)) + break; + l_self = &link->selflink; + } + + if(link == nil) + panic("remselfcache"); + + if(--(link->ref) != 0) + goto out; + + if((p->type & Rmulti) && ifc->m->remmulti != nil) + (*ifc->m->remmulti)(ifc, a, lifc->local); + + /* ref == 0, remove from both chains and free the link */ + *l_lifc = link->lifclink; + *l_self = link->selflink; + iplinkfree(link); + + if(p->link != nil) + goto out; + + /* remove from routing table */ + if(isv4(a)) + v4delroute(f, a+IPv4off, IPallbits+IPv4off, 1); + else + v6delroute(f, a, IPallbits, 1); + + /* no more links, remove from hash and free */ + *l = p->next; + ipselffree(p); + + /* if IPnoaddr, forget */ + if(ipcmp(a, v4prefix) == 0 || ipcmp(a, IPnoaddr) == 0) + f->self->acceptall = 0; + +out: + qunlock(f->self); +} + +static char *stformat = "%-44.44I %2.2d %4.4s\n"; +enum +{ + Nstformat= 41, +}; + +long +ipselftabread(Fs *f, char *cp, ulong offset, int n) +{ + int i, m, nifc, off; + Ipself *p; + Iplink *link; + char state[8]; + + m = 0; + off = offset; + qlock(f->self); + for(i = 0; i < NHASH && m < n; i++){ + for(p = f->self->hash[i]; p != nil && m < n; p = p->next){ + nifc = 0; + for(link = p->link; link; link = link->selflink) + nifc++; + routetype(p->type, state); + m += snprint(cp + m, n - m, stformat, p->a, nifc, state); + if(off > 0){ + off -= m; + m = 0; + } + } + } + qunlock(f->self); + return m; +} + +int +iptentative(Fs *f, uchar *addr) +{ + Ipself *p; + + p = f->self->hash[hashipa(addr)]; + for(; p; p = p->next){ + if(ipcmp(addr, p->a) == 0) { + return p->link->lifc->tentative; + } + } + return 0; +} + +/* + * returns + * 0 - no match + * Runi + * Rbcast + * Rmcast + */ +int +ipforme(Fs *f, uchar *addr) +{ + Ipself *p; + + p = f->self->hash[hashipa(addr)]; + for(; p; p = p->next){ + if(ipcmp(addr, p->a) == 0) + return p->type; + } + + /* hack to say accept anything */ + if(f->self->acceptall) + return Runi; + + return 0; +} + +/* + * find the ifc on same net as the remote system. If none, + * return nil. + */ +Ipifc* +findipifc(Fs *f, uchar *remote, int type) +{ + Ipifc *ifc, *x; + Iplifc *lifc; + Conv **cp, **e; + uchar gnet[IPaddrlen]; + uchar xmask[IPaddrlen]; + + x = nil; memset(xmask, 0, IPaddrlen); + + /* find most specific match */ + e = &f->ipifc->conv[f->ipifc->nc]; + for(cp = f->ipifc->conv; cp < e; cp++){ + if(*cp == 0) + continue; + + ifc = (Ipifc*)(*cp)->ptcl; + + for(lifc = ifc->lifc; lifc; lifc = lifc->next){ + maskip(remote, lifc->mask, gnet); + if(ipcmp(gnet, lifc->net) == 0){ + if(x == nil || ipcmp(lifc->mask, xmask) > 0){ + x = ifc; + ipmove(xmask, lifc->mask); + } + } + } + } + if(x != nil) + return x; + + /* for now for broadcast and multicast, just use first interface */ + if(type & (Rbcast|Rmulti)){ + for(cp = f->ipifc->conv; cp < e; cp++){ + if(*cp == 0) + continue; + ifc = (Ipifc*)(*cp)->ptcl; + if(ifc->lifc != nil) + return ifc; + } + } + + return nil; +} + +enum { + unknownv6, + multicastv6, + unspecifiedv6, + linklocalv6, + sitelocalv6, + globalv6, +}; + +int +v6addrtype(uchar *addr) +{ + if(isv6global(addr)) + return globalv6; + if(islinklocal(addr)) + return linklocalv6; + if(isv6mcast(addr)) + return multicastv6; + if(issitelocal(addr)) + return sitelocalv6; + return unknownv6; +} + +#define v6addrcurr(lifc) (( (lifc)->origint + (lifc)->preflt >= (NOW/10^3) ) || ( (lifc)->preflt == 0xffffffff )) + +void +findprimaryip6(Fs *f, uchar *local) +{ + Conv **cp, **e; + Ipifc *ifc; + Iplifc *lifc; + int atype, atypel; + + ipmove(local, v6Unspecified); + atype = unspecifiedv6; + + /* find "best" (global > sitelocal > link local > unspecified) + * local address; address must be current */ + + e = &f->ipifc->conv[f->ipifc->nc]; + for(cp = f->ipifc->conv; cp < e; cp++){ + if(*cp == 0) + continue; + ifc = (Ipifc*)(*cp)->ptcl; + for(lifc = ifc->lifc; lifc; lifc = lifc->next){ + atypel = v6addrtype(lifc->local); + if(atypel > atype) + if(v6addrcurr(lifc)) { + ipmove(local, lifc->local); + atype = atypel; + if(atype == globalv6) + return; + } + } + } +} + +/* + * returns first ip address configured + */ +void +findprimaryip(Fs *f, uchar *local) +{ + Conv **cp, **e; + Ipifc *ifc; + Iplifc *lifc; + + /* find first ifc local address */ + e = &f->ipifc->conv[f->ipifc->nc]; + for(cp = f->ipifc->conv; cp < e; cp++){ + if(*cp == 0) + continue; + ifc = (Ipifc*)(*cp)->ptcl; + for(lifc = ifc->lifc; lifc; lifc = lifc->next){ + ipmove(local, lifc->local); + return; + } + } +} + +/* + * find the local address 'closest' to the remote system, copy it to + * local and return the ifc for that address + */ +void +findlocalip(Fs *f, uchar *local, uchar *remote) +{ + Ipifc *ifc; + Iplifc *lifc; + Route *r; + uchar gate[IPaddrlen]; + uchar gnet[IPaddrlen]; + int version; + int atype = unspecifiedv6, atypel = unknownv6; + + USED(atype); + USED(atypel); + qlock(f->ipifc); + r = v6lookup(f, remote, nil); + version = (memcmp(remote, v4prefix, IPv4off) == 0) ? V4 : V6; + + if(r != nil){ + ifc = r->ifc; + if(r->type & Rv4) + v4tov6(gate, r->v4.gate); + else { + ipmove(gate, r->v6.gate); + ipmove(local, v6Unspecified); + } + + /* find ifc address closest to the gateway to use */ + switch(version) { + case V4: + for(lifc = ifc->lifc; lifc; lifc = lifc->next){ + maskip(gate, lifc->mask, gnet); + if(ipcmp(gnet, lifc->net) == 0){ + ipmove(local, lifc->local); + goto out; + } + } + break; + case V6: + for(lifc = ifc->lifc; lifc; lifc = lifc->next){ + atypel = v6addrtype(lifc->local); + maskip(gate, lifc->mask, gnet); + if(ipcmp(gnet, lifc->net) == 0) + if(atypel > atype) + if(v6addrcurr(lifc)) { + ipmove(local, lifc->local); + atype = atypel; + if(atype == globalv6) + break; + } + } + if(atype > unspecifiedv6) + goto out; + break; + default: + panic("findlocalip: version %d", version); + } + } + + switch(version){ + case V4: + findprimaryip(f, local); + break; + case V6: + findprimaryip6(f, local); + break; + default: + panic("findlocalip2: version %d", version); + } + +out: + qunlock(f->ipifc); +} + +/* + * return first v4 address associated with an interface + */ +int +ipv4local(Ipifc *ifc, uchar *addr) +{ + Iplifc *lifc; + + for(lifc = ifc->lifc; lifc; lifc = lifc->next){ + if(isv4(lifc->local)){ + memmove(addr, lifc->local+IPv4off, IPv4addrlen); + return 1; + } + } + return 0; +} + +/* + * return first v6 address associated with an interface + */ +int +ipv6local(Ipifc *ifc, uchar *addr) +{ + Iplifc *lifc; + + for(lifc = ifc->lifc; lifc; lifc = lifc->next){ + if(!isv4(lifc->local) && !(lifc->tentative)){ + ipmove(addr, lifc->local); + return 1; + } + } + return 0; +} + +int +ipv6anylocal(Ipifc *ifc, uchar *addr) +{ + Iplifc *lifc; + + for(lifc = ifc->lifc; lifc; lifc = lifc->next){ + if(!isv4(lifc->local)){ + ipmove(addr, lifc->local); + return SRC_UNI; + } + } + return SRC_UNSPEC; +} + +/* + * see if this address is bound to the interface + */ +Iplifc* +iplocalonifc(Ipifc *ifc, uchar *ip) +{ + Iplifc *lifc; + + for(lifc = ifc->lifc; lifc; lifc = lifc->next) + if(ipcmp(ip, lifc->local) == 0) + return lifc; + return nil; +} + + +/* + * See if we're proxying for this address on this interface + */ +int +ipproxyifc(Fs *f, Ipifc *ifc, uchar *ip) +{ + Route *r; + uchar net[IPaddrlen]; + Iplifc *lifc; + + /* see if this is a direct connected pt to pt address */ + r = v6lookup(f, ip, nil); + if(r == nil) + return 0; + if((r->type & (Rifc|Rproxy)) != (Rifc|Rproxy)) + return 0; + + /* see if this is on the right interface */ + for(lifc = ifc->lifc; lifc; lifc = lifc->next){ + maskip(ip, lifc->mask, net); + if(ipcmp(net, lifc->remote) == 0) + return 1; + } + + return 0; +} + +/* + * return multicast version if any + */ +int +ipismulticast(uchar *ip) +{ + if(isv4(ip)){ + if(ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0) + return V4; + } else { + if(ip[0] == 0xff) + return V6; + } + return 0; +} + +int +ipisbm(uchar *ip) +{ + if(isv4(ip)){ + if(ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0) + return V4; + if(ipcmp(ip, IPv4bcast) == 0) + return V4; + } else { + if(ip[0] == 0xff) + return V6; + } + return 0; +} + + +/* + * add a multicast address to an interface, called with c locked + */ +void +ipifcaddmulti(Conv *c, uchar *ma, uchar *ia) +{ + Ipifc *ifc; + Iplifc *lifc; + Conv **p; + Ipmulti *multi, **l; + Fs *f; + + f = c->p->f; + + for(l = &c->multi; *l; l = &(*l)->next) + if(ipcmp(ma, (*l)->ma) == 0) + if(ipcmp(ia, (*l)->ia) == 0) + return; /* it's already there */ + + multi = *l = smalloc(sizeof(*multi)); + ipmove(multi->ma, ma); + ipmove(multi->ia, ia); + multi->next = nil; + + for(p = f->ipifc->conv; *p; p++){ + if((*p)->inuse == 0) + continue; + ifc = (Ipifc*)(*p)->ptcl; + if(waserror()){ + wunlock(ifc); + nexterror(); + } + wlock(ifc); + for(lifc = ifc->lifc; lifc; lifc = lifc->next) + if(ipcmp(ia, lifc->local) == 0) + addselfcache(f, ifc, lifc, ma, Rmulti); + wunlock(ifc); + poperror(); + } +} + + +/* + * remove a multicast address from an interface, called with c locked + */ +void +ipifcremmulti(Conv *c, uchar *ma, uchar *ia) +{ + Ipmulti *multi, **l; + Iplifc *lifc; + Conv **p; + Ipifc *ifc; + Fs *f; + + f = c->p->f; + + for(l = &c->multi; *l; l = &(*l)->next) + if(ipcmp(ma, (*l)->ma) == 0) + if(ipcmp(ia, (*l)->ia) == 0) + break; + + multi = *l; + if(multi == nil) + return; /* we don't have it open */ + + *l = multi->next; + + for(p = f->ipifc->conv; *p; p++){ + if((*p)->inuse == 0) + continue; + + ifc = (Ipifc*)(*p)->ptcl; + if(waserror()){ + wunlock(ifc); + nexterror(); + } + wlock(ifc); + for(lifc = ifc->lifc; lifc; lifc = lifc->next) + if(ipcmp(ia, lifc->local) == 0) + remselfcache(f, ifc, lifc, ma); + wunlock(ifc); + poperror(); + } + + free(multi); +} + +/* + * make lifc's join and leave multicast groups + */ +static char* +ipifcjoinmulti(Ipifc *ifc, char **argv, int argc) +{ + USED(ifc, argv, argc); + return nil; +} + +static char* +ipifcleavemulti(Ipifc *ifc, char **argv, int argc) +{ + USED(ifc, argv, argc); + return nil; +} + +static void +ipifcregisterproxy(Fs *f, Ipifc *ifc, uchar *ip) +{ + Conv **cp, **e; + Ipifc *nifc; + Iplifc *lifc; + Medium *m; + uchar net[IPaddrlen]; + + /* register the address on any network that will proxy for us */ + e = &f->ipifc->conv[f->ipifc->nc]; + + if(!isv4(ip)) { // V6 + for(cp = f->ipifc->conv; cp < e; cp++){ + if(*cp == nil) + continue; + nifc = (Ipifc*)(*cp)->ptcl; + if(nifc == ifc) + continue; + + rlock(nifc); + m = nifc->m; + if(m == nil || m->addmulti == nil) { + runlock(nifc); + continue; + } + for(lifc = nifc->lifc; lifc; lifc = lifc->next){ + maskip(ip, lifc->mask, net); + if(ipcmp(net, lifc->remote) == 0) { /* add solicited-node multicast address */ + ipv62smcast(net, ip); + addselfcache(f, nifc, lifc, net, Rmulti); + arpenter(f, V6, ip, nifc->mac, 6, 0); + //(*m->addmulti)(nifc, net, ip); + break; + } + } + runlock(nifc); + } + return; + } + else { // V4 + for(cp = f->ipifc->conv; cp < e; cp++){ + if(*cp == nil) + continue; + nifc = (Ipifc*)(*cp)->ptcl; + if(nifc == ifc) + continue; + + rlock(nifc); + m = nifc->m; + if(m == nil || m->areg == nil){ + runlock(nifc); + continue; + } + for(lifc = nifc->lifc; lifc; lifc = lifc->next){ + maskip(ip, lifc->mask, net); + if(ipcmp(net, lifc->remote) == 0){ + (*m->areg)(nifc, ip); + break; + } + } + runlock(nifc); + } + } +} + + +// added for new v6 mesg types +static void +adddefroute6(Fs *f, uchar *gate, int force) +{ + Route *r; + + r = v6lookup(f, v6Unspecified, nil); + if(r!=nil) + if(!(force) && (strcmp(r->tag,"ra")!=0)) // route entries generated + return; // by all other means take + // precedence over router annc + + v6delroute(f, v6Unspecified, v6Unspecified, 1); + v6addroute(f, "ra", v6Unspecified, v6Unspecified, gate, 0); +} + +enum +{ + Ngates = 3, +}; + +char* +ipifcaddpref6(Ipifc *ifc, char**argv, int argc) +{ + uchar onlink = 1; + uchar autoflag = 1; + long validlt = 0xffffffff; + long preflt = 0xffffffff; + long origint = NOW / 10^3; + uchar prefix[IPaddrlen]; + int plen = 64; + Iplifc *lifc; + char addr[40], preflen[6]; + char *params[3]; + + switch(argc) { + case 7: + preflt = atoi(argv[6]); + /* fall through */ + case 6: + validlt = atoi(argv[5]); + /* fall through */ + case 5: + autoflag = atoi(argv[4]); + /* fall through */ + case 4: + onlink = atoi(argv[3]); + /* fall through */ + case 3: + plen = atoi(argv[2]); + case 2: + break; + default: + return Ebadarg; + } + + if((parseip(prefix, argv[1])!=6) || + (validlt < preflt) || + (plen < 0) || (plen > 64) || + (islinklocal(prefix)) + ) + return Ebadarg; + + lifc = smalloc(sizeof(Iplifc)); + lifc->onlink = (onlink!=0); + lifc->autoflag = (autoflag!=0); + lifc->validlt = validlt; + lifc->preflt = preflt; + lifc->origint = origint; + + if(ifc->m->pref2addr!=nil) + ifc->m->pref2addr(prefix, ifc->mac); + else + return Ebadarg; + + sprint(addr, "%I", prefix); + sprint(preflen, "/%d", plen); + params[0] = "add"; + params[1] = addr; + params[2] = preflen; + + return ipifcadd(ifc, params, 3, 0, lifc); +} + diff --git a/os/ip/ipmux.c b/os/ip/ipmux.c new file mode 100644 index 00000000..ef67b0fa --- /dev/null +++ b/os/ip/ipmux.c @@ -0,0 +1,857 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" +#define DPRINT if(0)print + +#define offsetof(s, m) (ulong)(&(((s*)0)->m)) + +typedef struct Ipmuxrock Ipmuxrock; +typedef struct Ipmux Ipmux; +typedef struct Ip4hdr Ip4hdr; +typedef struct Ip6hdr Ip6hdr; + +enum +{ + IPHDR = 20, /* sizeof(Ip4hdr) */ +}; + +struct Ip4hdr +{ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* ip->identification */ + uchar frag[2]; /* Fragment information */ + uchar ttl; /* Time to live */ + uchar proto; /* Protocol */ + uchar cksum[2]; /* Header checksum */ + uchar src[4]; /* IP source */ + uchar dst[4]; /* IP destination */ + uchar data[1]; /* start of data */ +}; + +struct Ip6hdr +{ + uchar vcf[4]; /* version, class label, and flow label */ + uchar ploadlen[2]; /* payload length */ + uchar proto; /* next header, i.e. proto */ + uchar ttl; /* hop limit, i.e. ttl */ + uchar src[16]; /* IP source */ + uchar dst[16]; /* IP destination */ +}; + + +enum +{ + Tproto, + Tdata, + Tiph, + Tdst, + Tsrc, + Tifc, + + Cother = 0, + Cbyte, /* single byte */ + Cmbyte, /* single byte with mask */ + Cshort, /* single short */ + Cmshort, /* single short with mask */ + Clong, /* single long */ + Cmlong, /* single long with mask */ + Cifc, + Cmifc, +}; + +char *ftname[] = +{ +[Tproto] "proto", +[Tdata] "data", +[Tiph] "iph", +[Tdst] "dst", +[Tsrc] "src", +[Tifc] "ifc", +}; + +/* + * a node in the decision tree + */ +struct Ipmux +{ + Ipmux *yes; + Ipmux *no; + uchar type; /* type of field(Txxxx) */ + uchar ctype; /* tupe of comparison(Cxxxx) */ + uchar len; /* length in bytes of item to compare */ + uchar n; /* number of items val points to */ + short off; /* offset of comparison */ + short eoff; /* end offset of comparison */ + uchar skiphdr; /* should offset start after ipheader */ + uchar *val; + uchar *mask; + uchar *e; /* val+n*len*/ + + int ref; /* so we can garbage collect */ + Conv *conv; +}; + +/* + * someplace to hold per conversation data + */ +struct Ipmuxrock +{ + Ipmux *chain; +}; + +static int ipmuxsprint(Ipmux*, int, char*, int); +static void ipmuxkick(void *x); + +static char* +skipwhite(char *p) +{ + while(*p == ' ' || *p == '\t') + p++; + return p; +} + +static char* +follows(char *p, char c) +{ + char *f; + + f = strchr(p, c); + if(f == nil) + return nil; + *f++ = 0; + f = skipwhite(f); + if(*f == 0) + return nil; + return f; +} + +static Ipmux* +parseop(char **pp) +{ + char *p = *pp; + int type, off, end, len; + Ipmux *f; + + p = skipwhite(p); + if(strncmp(p, "dst", 3) == 0){ + type = Tdst; + off = offsetof(Ip4hdr, dst[0]); + len = IPv4addrlen; + p += 3; + } + else if(strncmp(p, "src", 3) == 0){ + type = Tsrc; + off = offsetof(Ip4hdr, src[0]); + len = IPv4addrlen; + p += 3; + } + else if(strncmp(p, "ifc", 3) == 0){ + type = Tifc; + off = -IPv4addrlen; + len = IPv4addrlen; + p += 3; + } + else if(strncmp(p, "proto", 5) == 0){ + type = Tproto; + off = offsetof(Ip4hdr, proto); + len = 1; + p += 5; + } + else if(strncmp(p, "data", 4) == 0 || strncmp(p, "iph", 3) == 0){ + if(strncmp(p, "data", 4) == 0) { + type = Tdata; + p += 4; + } + else { + type = Tiph; + p += 3; + } + p = skipwhite(p); + if(*p != '[') + return nil; + p++; + off = strtoul(p, &p, 0); + if(off < 0 || off > (64-IPHDR)) + return nil; + p = skipwhite(p); + if(*p != ':') + end = off; + else { + p++; + p = skipwhite(p); + end = strtoul(p, &p, 0); + if(end < off) + return nil; + p = skipwhite(p); + } + if(*p != ']') + return nil; + p++; + len = end - off + 1; + } + else + return nil; + + f = smalloc(sizeof(*f)); + f->type = type; + f->len = len; + f->off = off; + f->val = nil; + f->mask = nil; + f->n = 1; + f->ref = 1; + if(type == Tdata) + f->skiphdr = 1; + else + f->skiphdr = 0; + + return f; +} + +static int +htoi(char x) +{ + if(x >= '0' && x <= '9') + x -= '0'; + else if(x >= 'a' && x <= 'f') + x -= 'a' - 10; + else if(x >= 'A' && x <= 'F') + x -= 'A' - 10; + else + x = 0; + return x; +} + +static int +hextoi(char *p) +{ + return (htoi(p[0])<<4) | htoi(p[1]); +} + +static void +parseval(uchar *v, char *p, int len) +{ + while(*p && len-- > 0){ + *v++ = hextoi(p); + p += 2; + } +} + +static Ipmux* +parsemux(char *p) +{ + int n, nomask; + Ipmux *f; + char *val; + char *mask; + char *vals[20]; + uchar *v; + + /* parse operand */ + f = parseop(&p); + if(f == nil) + return nil; + + /* find value */ + val = follows(p, '='); + if(val == nil) + goto parseerror; + + /* parse mask */ + mask = follows(val, '&'); + if(mask != nil){ + switch(f->type){ + case Tsrc: + case Tdst: + case Tifc: + f->mask = smalloc(f->len); + v4parseip(f->mask, mask); + break; + case Tdata: + case Tiph: + f->mask = smalloc(f->len); + parseval(f->mask, mask, f->len); + break; + default: + goto parseerror; + } + nomask = 0; + } else { + nomask = 1; + f->mask = smalloc(f->len); + memset(f->mask, 0xff, f->len); + } + + /* parse vals */ + f->n = getfields(val, vals, sizeof(vals)/sizeof(char*), 1, "|"); + if(f->n == 0) + goto parseerror; + f->val = smalloc(f->n*f->len); + v = f->val; + for(n = 0; n < f->n; n++){ + switch(f->type){ + case Tsrc: + case Tdst: + case Tifc: + v4parseip(v, vals[n]); + break; + case Tproto: + case Tdata: + case Tiph: + parseval(v, vals[n], f->len); + break; + } + v += f->len; + } + + f->eoff = f->off + f->len; + f->e = f->val + f->n*f->len; + f->ctype = Cother; + if(f->n == 1){ + switch(f->len){ + case 1: + f->ctype = nomask ? Cbyte : Cmbyte; + break; + case 2: + f->ctype = nomask ? Cshort : Cmshort; + break; + case 4: + if(f->type == Tifc) + f->ctype = nomask ? Cifc : Cmifc; + else + f->ctype = nomask ? Clong : Cmlong; + break; + } + } + return f; + +parseerror: + if(f->mask) + free(f->mask); + if(f->val) + free(f->val); + free(f); + return nil; +} + +/* + * Compare relative ordering of two ipmuxs. This doesn't compare the + * values, just the fields being looked at. + * + * returns: <0 if a is a more specific match + * 0 if a and b are matching on the same fields + * >0 if b is a more specific match + */ +static int +ipmuxcmp(Ipmux *a, Ipmux *b) +{ + int n; + + /* compare types, lesser ones are more important */ + n = a->type - b->type; + if(n != 0) + return n; + + /* compare offsets, call earlier ones more specific */ + n = (a->off+((int)a->skiphdr)*offsetof(Ip4hdr, data[0])) - + (b->off+((int)b->skiphdr)*offsetof(Ip4hdr, data[0])); + if(n != 0) + return n; + + /* compare match lengths, longer ones are more specific */ + n = b->len - a->len; + if(n != 0) + return n; + + /* + * if we get here we have two entries matching + * the same bytes of the record. Now check + * the mask for equality. Longer masks are + * more specific. + */ + if(a->mask != nil && b->mask == nil) + return -1; + if(a->mask == nil && b->mask != nil) + return 1; + if(a->mask != nil && b->mask != nil){ + n = memcmp(b->mask, a->mask, a->len); + if(n != 0) + return n; + } + return 0; +} + +/* + * Compare the values of two ipmuxs. We're assuming that ipmuxcmp + * returned 0 comparing them. + */ +static int +ipmuxvalcmp(Ipmux *a, Ipmux *b) +{ + int n; + + n = b->len*b->n - a->len*a->n; + if(n != 0) + return n; + return memcmp(a->val, b->val, a->len*a->n); +} + +/* + * add onto an existing ipmux chain in the canonical comparison + * order + */ +static void +ipmuxchain(Ipmux **l, Ipmux *f) +{ + for(; *l; l = &(*l)->yes) + if(ipmuxcmp(f, *l) < 0) + break; + f->yes = *l; + *l = f; +} + +/* + * copy a tree + */ +static Ipmux* +ipmuxcopy(Ipmux *f) +{ + Ipmux *nf; + + if(f == nil) + return nil; + nf = smalloc(sizeof *nf); + *nf = *f; + nf->no = ipmuxcopy(f->no); + nf->yes = ipmuxcopy(f->yes); + nf->val = smalloc(f->n*f->len); + nf->e = nf->val + f->len*f->n; + memmove(nf->val, f->val, f->n*f->len); + return nf; +} + +static void +ipmuxfree(Ipmux *f) +{ + if(f->val != nil) + free(f->val); + free(f); +} + +static void +ipmuxtreefree(Ipmux *f) +{ + if(f == nil) + return; + if(f->no != nil) + ipmuxfree(f->no); + if(f->yes != nil) + ipmuxfree(f->yes); + ipmuxfree(f); +} + +/* + * merge two trees + */ +static Ipmux* +ipmuxmerge(Ipmux *a, Ipmux *b) +{ + int n; + Ipmux *f; + + if(a == nil) + return b; + if(b == nil) + return a; + n = ipmuxcmp(a, b); + if(n < 0){ + f = ipmuxcopy(b); + a->yes = ipmuxmerge(a->yes, b); + a->no = ipmuxmerge(a->no, f); + return a; + } + if(n > 0){ + f = ipmuxcopy(a); + b->yes = ipmuxmerge(b->yes, a); + b->no = ipmuxmerge(b->no, f); + return b; + } + if(ipmuxvalcmp(a, b) == 0){ + a->yes = ipmuxmerge(a->yes, b->yes); + a->no = ipmuxmerge(a->no, b->no); + a->ref++; + ipmuxfree(b); + return a; + } + a->no = ipmuxmerge(a->no, b); + return a; +} + +/* + * remove a chain from a demux tree. This is like merging accept that + * we remove instead of insert. + */ +static int +ipmuxremove(Ipmux **l, Ipmux *f) +{ + int n, rv; + Ipmux *ft; + + if(f == nil) + return 0; /* we've removed it all */ + if(*l == nil) + return -1; + + ft = *l; + n = ipmuxcmp(ft, f); + if(n < 0){ + /* *l is maching an earlier field, descend both paths */ + rv = ipmuxremove(&ft->yes, f); + rv += ipmuxremove(&ft->no, f); + return rv; + } + if(n > 0){ + /* f represents an earlier field than *l, this should be impossible */ + return -1; + } + + /* if we get here f and *l are comparing the same fields */ + if(ipmuxvalcmp(ft, f) != 0){ + /* different values mean mutually exclusive */ + return ipmuxremove(&ft->no, f); + } + + /* we found a match */ + if(--(ft->ref) == 0){ + /* + * a dead node implies the whole yes side is also dead. + * since our chain is constrained to be on that side, + * we're done. + */ + ipmuxtreefree(ft->yes); + *l = ft->no; + ipmuxfree(ft); + return 0; + } + + /* + * free the rest of the chain. it is constrained to match the + * yes side. + */ + return ipmuxremove(&ft->yes, f->yes); +} + +/* + * connection request is a semi separated list of filters + * e.g. proto=17;dat[0:4]=11aa22bb;ifc=135.104.9.2&255.255.255.0 + * + * there's no protection against overlapping specs. + */ +static char* +ipmuxconnect(Conv *c, char **argv, int argc) +{ + int i, n; + char *field[10]; + Ipmux *mux, *chain; + Ipmuxrock *r; + Fs *f; + + f = c->p->f; + + if(argc != 2) + return Ebadarg; + + n = getfields(argv[1], field, nelem(field), 1, ";"); + if(n <= 0) + return Ebadarg; + + chain = nil; + mux = nil; + for(i = 0; i < n; i++){ + mux = parsemux(field[i]); + if(mux == nil){ + ipmuxtreefree(chain); + return Ebadarg; + } + ipmuxchain(&chain, mux); + } + if(chain == nil) + return Ebadarg; + mux->conv = c; + + /* save a copy of the chain so we can later remove it */ + mux = ipmuxcopy(chain); + r = (Ipmuxrock*)(c->ptcl); + r->chain = chain; + + /* add the chain to the protocol demultiplexor tree */ + wlock(f); + f->ipmux->priv = ipmuxmerge(f->ipmux->priv, mux); + wunlock(f); + + Fsconnected(c, nil); + return nil; +} + +static int +ipmuxstate(Conv *c, char *state, int n) +{ + Ipmuxrock *r; + + r = (Ipmuxrock*)(c->ptcl); + return ipmuxsprint(r->chain, 0, state, n); +} + +static void +ipmuxcreate(Conv *c) +{ + Ipmuxrock *r; + + c->rq = qopen(64*1024, Qmsg, 0, c); + c->wq = qopen(64*1024, Qkick, ipmuxkick, c); + r = (Ipmuxrock*)(c->ptcl); + r->chain = nil; +} + +static char* +ipmuxannounce(Conv*, char**, int) +{ + return "ipmux does not support announce"; +} + +static void +ipmuxclose(Conv *c) +{ + Ipmuxrock *r; + Fs *f = c->p->f; + + r = (Ipmuxrock*)(c->ptcl); + + qclose(c->rq); + qclose(c->wq); + qclose(c->eq); + ipmove(c->laddr, IPnoaddr); + ipmove(c->raddr, IPnoaddr); + c->lport = 0; + c->rport = 0; + + wlock(f); + ipmuxremove(&(c->p->priv), r->chain); + wunlock(f); + ipmuxtreefree(r->chain); + r->chain = nil; +} + +/* + * takes a fully formed ip packet and just passes it down + * the stack + */ +static void +ipmuxkick(void *x) +{ + Conv *c = x; + Block *bp; + + bp = qget(c->wq); + if(bp == nil) + return; + else { + Ip4hdr *ih4 = (Ip4hdr*)(bp->rp); + if((ih4->vihl)&0xF0 != 0x60) + ipoput4(c->p->f, bp, 0, ih4->ttl, ih4->tos, nil); + else { + Ip6hdr *ih6 = (Ip6hdr*)(bp->rp); + ipoput6(c->p->f, bp, 0, ih6->ttl, 0, nil); + } + } +} + +static void +ipmuxiput(Proto *p, Ipifc *ifc, Block *bp) +{ + int len, hl; + Fs *f = p->f; + uchar *m, *h, *v, *e, *ve, *hp; + Conv *c; + Ipmux *mux; + Ip4hdr *ip; + Ip6hdr *ip6; + + ip = (Ip4hdr*)bp->rp; + hl = (ip->vihl&0x0F)<<2; + + if(p->priv == nil) + goto nomatch; + + h = bp->rp; + len = BLEN(bp); + + /* run the v4 filter */ + rlock(f); + c = nil; + mux = f->ipmux->priv; + while(mux != nil){ + if(mux->eoff > len){ + mux = mux->no; + continue; + } + hp = h + mux->off + ((int)mux->skiphdr)*hl; + switch(mux->ctype){ + case Cbyte: + if(*mux->val == *hp) + goto yes; + break; + case Cmbyte: + if((*hp & *mux->mask) == *mux->val) + goto yes; + break; + case Cshort: + if(*((ushort*)mux->val) == *(ushort*)hp) + goto yes; + break; + case Cmshort: + if((*(ushort*)hp & (*((ushort*)mux->mask))) == *((ushort*)mux->val)) + goto yes; + break; + case Clong: + if(*((ulong*)mux->val) == *(ulong*)hp) + goto yes; + break; + case Cmlong: + if((*(ulong*)hp & (*((ulong*)mux->mask))) == *((ulong*)mux->val)) + goto yes; + break; + case Cifc: + if(*((ulong*)mux->val) == *(ulong*)(ifc->lifc->local + IPv4off)) + goto yes; + break; + case Cmifc: + if((*(ulong*)(ifc->lifc->local + IPv4off) & (*((ulong*)mux->mask))) == *((ulong*)mux->val)) + goto yes; + break; + default: + v = mux->val; + for(e = mux->e; v < e; v = ve){ + m = mux->mask; + hp = h + mux->off; + for(ve = v + mux->len; v < ve; v++){ + if((*hp++ & *m++) != *v) + break; + } + if(v == ve) + goto yes; + } + } + mux = mux->no; + continue; +yes: + if(mux->conv != nil) + c = mux->conv; + mux = mux->yes; + } + runlock(f); + + if(c != nil){ + /* tack on interface address */ + bp = padblock(bp, IPaddrlen); + ipmove(bp->rp, ifc->lifc->local); + bp = concatblock(bp); + if(bp != nil) + if(qpass(c->rq, bp) < 0) + print("Q"); + return; + } + +nomatch: + /* doesn't match any filter, hand it to the specific protocol handler */ + ip = (Ip4hdr*)bp->rp; + if((ip->vihl&0xF0)==0x40) { + p = f->t2p[ip->proto]; + } else { + ip6 = (Ip6hdr*)bp->rp; + p = f->t2p[ip6->proto]; + } + if(p && p->rcv) + (*p->rcv)(p, ifc, bp); + else + freeblist(bp); + return; +} + +static int +ipmuxsprint(Ipmux *mux, int level, char *buf, int len) +{ + int i, j, n; + uchar *v; + + n = 0; + for(i = 0; i < level; i++) + n += snprint(buf+n, len-n, " "); + if(mux == nil){ + n += snprint(buf+n, len-n, "\n"); + return n; + } + n += snprint(buf+n, len-n, "h[%d:%d]&", + mux->off+((int)mux->skiphdr)*((int)offsetof(Ip4hdr, data[0])), + mux->off+(((int)mux->skiphdr)*((int)offsetof(Ip4hdr, data[0])))+mux->len-1); + for(i = 0; i < mux->len; i++) + n += snprint(buf+n, len - n, "%2.2ux", mux->mask[i]); + n += snprint(buf+n, len-n, "="); + v = mux->val; + for(j = 0; j < mux->n; j++){ + for(i = 0; i < mux->len; i++) + n += snprint(buf+n, len - n, "%2.2ux", *v++); + n += snprint(buf+n, len-n, "|"); + } + n += snprint(buf+n, len-n, "\n"); + level++; + n += ipmuxsprint(mux->no, level, buf+n, len-n); + n += ipmuxsprint(mux->yes, level, buf+n, len-n); + return n; +} + +static int +ipmuxstats(Proto *p, char *buf, int len) +{ + int n; + Fs *f = p->f; + + rlock(f); + n = ipmuxsprint(p->priv, 0, buf, len); + runlock(f); + + return n; +} + +void +ipmuxinit(Fs *f) +{ + Proto *ipmux; + + ipmux = smalloc(sizeof(Proto)); + ipmux->priv = nil; + ipmux->name = "ipmux"; + ipmux->connect = ipmuxconnect; + ipmux->announce = ipmuxannounce; + ipmux->state = ipmuxstate; + ipmux->create = ipmuxcreate; + ipmux->close = ipmuxclose; + ipmux->rcv = ipmuxiput; + ipmux->ctl = nil; + ipmux->advise = nil; + ipmux->stats = ipmuxstats; + ipmux->ipproto = -1; + ipmux->nc = 64; + ipmux->ptclsize = sizeof(Ipmuxrock); + + f->ipmux = ipmux; /* hack for Fsrcvpcol */ + + Fsproto(f, ipmux); +} diff --git a/os/ip/iproute.c b/os/ip/iproute.c new file mode 100644 index 00000000..3229435e --- /dev/null +++ b/os/ip/iproute.c @@ -0,0 +1,852 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" + +static void walkadd(Fs*, Route**, Route*); +static void addnode(Fs*, Route**, Route*); +static void calcd(Route*); + +/* these are used for all instances of IP */ +Route* v4freelist; +Route* v6freelist; +RWlock routelock; +ulong v4routegeneration, v6routegeneration; + +static void +freeroute(Route *r) +{ + Route **l; + + r->left = nil; + r->right = nil; + if(r->type & Rv4) + l = &v4freelist; + else + l = &v6freelist; + r->mid = *l; + *l = r; +} + +static Route* +allocroute(int type) +{ + Route *r; + int n; + Route **l; + + if(type & Rv4){ + n = sizeof(RouteTree) + sizeof(V4route); + l = &v4freelist; + } else { + n = sizeof(RouteTree) + sizeof(V6route); + l = &v6freelist; + } + + r = *l; + if(r != nil){ + *l = r->mid; + } else { + r = malloc(n); + if(r == nil) + panic("out of routing nodes"); + } + memset(r, 0, n); + r->type = type; + r->ifc = nil; + r->ref = 1; + + return r; +} + +static void +addqueue(Route **q, Route *r) +{ + Route *l; + + if(r == nil) + return; + + l = allocroute(r->type); + l->mid = *q; + *q = l; + l->left = r; +} + +/* + * compare 2 v6 addresses + */ +static int +lcmp(ulong *a, ulong *b) +{ + int i; + + for(i = 0; i < IPllen; i++){ + if(a[i] > b[i]) + return 1; + if(a[i] < b[i]) + return -1; + } + return 0; +} + +/* + * compare 2 v4 or v6 ranges + */ +enum +{ + Rpreceeds, + Rfollows, + Requals, + Rcontains, + Rcontained, +}; + +static int +rangecompare(Route *a, Route *b) +{ + if(a->type & Rv4){ + if(a->v4.endaddress < b->v4.address) + return Rpreceeds; + + if(a->v4.address > b->v4.endaddress) + return Rfollows; + + if(a->v4.address <= b->v4.address + && a->v4.endaddress >= b->v4.endaddress){ + if(a->v4.address == b->v4.address + && a->v4.endaddress == b->v4.endaddress) + return Requals; + return Rcontains; + } + return Rcontained; + } + + if(lcmp(a->v6.endaddress, b->v6.address) < 0) + return Rpreceeds; + + if(lcmp(a->v6.address, b->v6.endaddress) > 0) + return Rfollows; + + if(lcmp(a->v6.address, b->v6.address) <= 0 + && lcmp(a->v6.endaddress, b->v6.endaddress) >= 0){ + if(lcmp(a->v6.address, b->v6.address) == 0 + && lcmp(a->v6.endaddress, b->v6.endaddress) == 0) + return Requals; + return Rcontains; + } + + return Rcontained; +} + +static void +copygate(Route *old, Route *new) +{ + if(new->type & Rv4) + memmove(old->v4.gate, new->v4.gate, IPv4addrlen); + else + memmove(old->v6.gate, new->v6.gate, IPaddrlen); +} + +/* + * walk down a tree adding nodes back in + */ +static void +walkadd(Fs *f, Route **root, Route *p) +{ + Route *l, *r; + + l = p->left; + r = p->right; + p->left = 0; + p->right = 0; + addnode(f, root, p); + if(l) + walkadd(f, root, l); + if(r) + walkadd(f, root, r); +} + +/* + * calculate depth + */ +static void +calcd(Route *p) +{ + Route *q; + int d; + + if(p) { + d = 0; + q = p->left; + if(q) + d = q->depth; + q = p->right; + if(q && q->depth > d) + d = q->depth; + q = p->mid; + if(q && q->depth > d) + d = q->depth; + p->depth = d+1; + } +} + +/* + * balance the tree at the current node + */ +static void +balancetree(Route **cur) +{ + Route *p, *l, *r; + int dl, dr; + + /* + * if left and right are + * too out of balance, + * rotate tree node + */ + p = *cur; + dl = 0; if(l = p->left) dl = l->depth; + dr = 0; if(r = p->right) dr = r->depth; + + if(dl > dr+1) { + p->left = l->right; + l->right = p; + *cur = l; + calcd(p); + calcd(l); + } else + if(dr > dl+1) { + p->right = r->left; + r->left = p; + *cur = r; + calcd(p); + calcd(r); + } else + calcd(p); +} + +/* + * add a new node to the tree + */ +static void +addnode(Fs *f, Route **cur, Route *new) +{ + Route *p; + + p = *cur; + if(p == 0) { + *cur = new; + new->depth = 1; + return; + } + + switch(rangecompare(new, p)){ + case Rpreceeds: + addnode(f, &p->left, new); + break; + case Rfollows: + addnode(f, &p->right, new); + break; + case Rcontains: + /* + * if new node is superset + * of tree node, + * replace tree node and + * queue tree node to be + * merged into root. + */ + *cur = new; + new->depth = 1; + addqueue(&f->queue, p); + break; + case Requals: + /* + * supercede the old entry if the old one isn't + * a local interface. + */ + if((p->type & Rifc) == 0){ + p->type = new->type; + p->ifcid = -1; + copygate(p, new); + } else if(new->type & Rifc) + p->ref++; + freeroute(new); + break; + case Rcontained: + addnode(f, &p->mid, new); + break; + } + + balancetree(cur); +} + +#define V4H(a) ((a&0x07ffffff)>>(32-Lroot-5)) + +void +v4addroute(Fs *f, char *tag, uchar *a, uchar *mask, uchar *gate, int type) +{ + Route *p; + ulong sa; + ulong m; + ulong ea; + int h, eh; + + m = nhgetl(mask); + sa = nhgetl(a) & m; + ea = sa | ~m; + + eh = V4H(ea); + for(h=V4H(sa); h<=eh; h++) { + p = allocroute(Rv4 | type); + p->v4.address = sa; + p->v4.endaddress = ea; + memmove(p->v4.gate, gate, sizeof(p->v4.gate)); + memmove(p->tag, tag, sizeof(p->tag)); + + wlock(&routelock); + addnode(f, &f->v4root[h], p); + while(p = f->queue) { + f->queue = p->mid; + walkadd(f, &f->v4root[h], p->left); + freeroute(p); + } + wunlock(&routelock); + } + v4routegeneration++; + + ipifcaddroute(f, Rv4, a, mask, gate, type); +} + +#define V6H(a) (((a)[IPllen-1] & 0x07ffffff)>>(32-Lroot-5)) +#define ISDFLT(a, mask, tag) ((ipcmp((a),v6Unspecified)==0) && (ipcmp((mask),v6Unspecified)==0) && (strcmp((tag), "ra")!=0)) + +void +v6addroute(Fs *f, char *tag, uchar *a, uchar *mask, uchar *gate, int type) +{ + Route *p; + ulong sa[IPllen], ea[IPllen]; + ulong x, y; + int h, eh; + + /* + if(ISDFLT(a, mask, tag)) + f->v6p->cdrouter = -1; + */ + + + for(h = 0; h < IPllen; h++){ + x = nhgetl(a+4*h); + y = nhgetl(mask+4*h); + sa[h] = x & y; + ea[h] = x | ~y; + } + + eh = V6H(ea); + for(h = V6H(sa); h <= eh; h++) { + p = allocroute(type); + memmove(p->v6.address, sa, IPaddrlen); + memmove(p->v6.endaddress, ea, IPaddrlen); + memmove(p->v6.gate, gate, IPaddrlen); + memmove(p->tag, tag, sizeof(p->tag)); + + wlock(&routelock); + addnode(f, &f->v6root[h], p); + while(p = f->queue) { + f->queue = p->mid; + walkadd(f, &f->v6root[h], p->left); + freeroute(p); + } + wunlock(&routelock); + } + v6routegeneration++; + + ipifcaddroute(f, 0, a, mask, gate, type); +} + +Route** +looknode(Route **cur, Route *r) +{ + Route *p; + + for(;;){ + p = *cur; + if(p == 0) + return 0; + + switch(rangecompare(r, p)){ + case Rcontains: + return 0; + case Rpreceeds: + cur = &p->left; + break; + case Rfollows: + cur = &p->right; + break; + case Rcontained: + cur = &p->mid; + break; + case Requals: + return cur; + } + } +} + +void +v4delroute(Fs *f, uchar *a, uchar *mask, int dolock) +{ + Route **r, *p; + Route rt; + int h, eh; + ulong m; + + m = nhgetl(mask); + rt.v4.address = nhgetl(a) & m; + rt.v4.endaddress = rt.v4.address | ~m; + rt.type = Rv4; + + eh = V4H(rt.v4.endaddress); + for(h=V4H(rt.v4.address); h<=eh; h++) { + if(dolock) + wlock(&routelock); + r = looknode(&f->v4root[h], &rt); + if(r) { + p = *r; + if(--(p->ref) == 0){ + *r = 0; + addqueue(&f->queue, p->left); + addqueue(&f->queue, p->mid); + addqueue(&f->queue, p->right); + freeroute(p); + while(p = f->queue) { + f->queue = p->mid; + walkadd(f, &f->v4root[h], p->left); + freeroute(p); + } + } + } + if(dolock) + wunlock(&routelock); + } + v4routegeneration++; + + ipifcremroute(f, Rv4, a, mask); +} + +void +v6delroute(Fs *f, uchar *a, uchar *mask, int dolock) +{ + Route **r, *p; + Route rt; + int h, eh; + ulong x, y; + + for(h = 0; h < IPllen; h++){ + x = nhgetl(a+4*h); + y = nhgetl(mask+4*h); + rt.v6.address[h] = x & y; + rt.v6.endaddress[h] = x | ~y; + } + rt.type = 0; + + eh = V6H(rt.v6.endaddress); + for(h=V6H(rt.v6.address); h<=eh; h++) { + if(dolock) + wlock(&routelock); + r = looknode(&f->v6root[h], &rt); + if(r) { + p = *r; + if(--(p->ref) == 0){ + *r = 0; + addqueue(&f->queue, p->left); + addqueue(&f->queue, p->mid); + addqueue(&f->queue, p->right); + freeroute(p); + while(p = f->queue) { + f->queue = p->mid; + walkadd(f, &f->v6root[h], p->left); + freeroute(p); + } + } + } + if(dolock) + wunlock(&routelock); + } + v6routegeneration++; + + ipifcremroute(f, 0, a, mask); +} + +Route* +v4lookup(Fs *f, uchar *a, Conv *c) +{ + Route *p, *q; + ulong la; + uchar gate[IPaddrlen]; + Ipifc *ifc; + + if(c != nil && c->r != nil && c->r->ifc != nil && c->rgen == v4routegeneration) + return c->r; + + la = nhgetl(a); + q = nil; + for(p=f->v4root[V4H(la)]; p;) + if(la >= p->v4.address) { + if(la <= p->v4.endaddress) { + q = p; + p = p->mid; + } else + p = p->right; + } else + p = p->left; + + if(q && (q->ifc == nil || q->ifcid != q->ifc->ifcid)){ + if(q->type & Rifc) { + hnputl(gate+IPv4off, q->v4.address); + memmove(gate, v4prefix, IPv4off); + } else + v4tov6(gate, q->v4.gate); + ifc = findipifc(f, gate, q->type); + if(ifc == nil) + return nil; + q->ifc = ifc; + q->ifcid = ifc->ifcid; + } + + if(c != nil){ + c->r = q; + c->rgen = v4routegeneration; + } + + return q; +} + +Route* +v6lookup(Fs *f, uchar *a, Conv *c) +{ + Route *p, *q; + ulong la[IPllen]; + int h; + ulong x, y; + uchar gate[IPaddrlen]; + Ipifc *ifc; + + if(memcmp(a, v4prefix, IPv4off) == 0){ + q = v4lookup(f, a+IPv4off, c); + if(q != nil) + return q; + } + + if(c != nil && c->r != nil && c->r->ifc != nil && c->rgen == v6routegeneration) + return c->r; + + for(h = 0; h < IPllen; h++) + la[h] = nhgetl(a+4*h); + + q = 0; + for(p=f->v6root[V6H(la)]; p;){ + for(h = 0; h < IPllen; h++){ + x = la[h]; + y = p->v6.address[h]; + if(x == y) + continue; + if(x < y){ + p = p->left; + goto next; + } + break; + } + for(h = 0; h < IPllen; h++){ + x = la[h]; + y = p->v6.endaddress[h]; + if(x == y) + continue; + if(x > y){ + p = p->right; + goto next; + } + break; + } + q = p; + p = p->mid; +next: ; + } + + if(q && (q->ifc == nil || q->ifcid != q->ifc->ifcid)){ + if(q->type & Rifc) { + for(h = 0; h < IPllen; h++) + hnputl(gate+4*h, q->v6.address[h]); + ifc = findipifc(f, gate, q->type); + } else + ifc = findipifc(f, q->v6.gate, q->type); + if(ifc == nil) + return nil; + q->ifc = ifc; + q->ifcid = ifc->ifcid; + } + if(c != nil){ + c->r = q; + c->rgen = v6routegeneration; + } + + return q; +} + +void +routetype(int type, char *p) +{ + memset(p, ' ', 4); + p[4] = 0; + if(type & Rv4) + *p++ = '4'; + else + *p++ = '6'; + if(type & Rifc) + *p++ = 'i'; + if(type & Runi) + *p++ = 'u'; + else if(type & Rbcast) + *p++ = 'b'; + else if(type & Rmulti) + *p++ = 'm'; + if(type & Rptpt) + *p = 'p'; +} + +char *rformat = "%-15I %-4M %-15I %4.4s %4.4s %3s\n"; + +void +convroute(Route *r, uchar *addr, uchar *mask, uchar *gate, char *t, int *nifc) +{ + int i; + + if(r->type & Rv4){ + memmove(addr, v4prefix, IPv4off); + hnputl(addr+IPv4off, r->v4.address); + memset(mask, 0xff, IPv4off); + hnputl(mask+IPv4off, ~(r->v4.endaddress ^ r->v4.address)); + memmove(gate, v4prefix, IPv4off); + memmove(gate+IPv4off, r->v4.gate, IPv4addrlen); + } else { + for(i = 0; i < IPllen; i++){ + hnputl(addr + 4*i, r->v6.address[i]); + hnputl(mask + 4*i, ~(r->v6.endaddress[i] ^ r->v6.address[i])); + } + memmove(gate, r->v6.gate, IPaddrlen); + } + + routetype(r->type, t); + + if(r->ifc) + *nifc = r->ifc->conv->x; + else + *nifc = -1; +} + +/* + * this code is not in rr to reduce stack size + */ +static void +sprintroute(Route *r, Routewalk *rw) +{ + int nifc, n; + char t[5], *iname, ifbuf[5]; + uchar addr[IPaddrlen], mask[IPaddrlen], gate[IPaddrlen]; + char *p; + + convroute(r, addr, mask, gate, t, &nifc); + iname = "-"; + if(nifc != -1) { + iname = ifbuf; + sprint(ifbuf, "%d", nifc); + } + p = seprint(rw->p, rw->e, rformat, addr, mask, gate, t, r->tag, iname); + if(rw->o < 0){ + n = p - rw->p; + if(n > -rw->o){ + memmove(rw->p, rw->p-rw->o, n+rw->o); + rw->p = p + rw->o; + } + rw->o += n; + } else + rw->p = p; +} + +/* + * recurse descending tree, applying the function in Routewalk + */ +static int +rr(Route *r, Routewalk *rw) +{ + int h; + + if(rw->e <= rw->p) + return 0; + if(r == nil) + return 1; + + if(rr(r->left, rw) == 0) + return 0; + + if(r->type & Rv4) + h = V4H(r->v4.address); + else + h = V6H(r->v6.address); + + if(h == rw->h) + rw->walk(r, rw); + + if(rr(r->mid, rw) == 0) + return 0; + + return rr(r->right, rw); +} + +void +ipwalkroutes(Fs *f, Routewalk *rw) +{ + rlock(&routelock); + if(rw->e > rw->p) { + for(rw->h = 0; rw->h < nelem(f->v4root); rw->h++) + if(rr(f->v4root[rw->h], rw) == 0) + break; + } + if(rw->e > rw->p) { + for(rw->h = 0; rw->h < nelem(f->v6root); rw->h++) + if(rr(f->v6root[rw->h], rw) == 0) + break; + } + runlock(&routelock); +} + +long +routeread(Fs *f, char *p, ulong offset, int n) +{ + Routewalk rw; + + rw.p = p; + rw.e = p+n; + rw.o = -offset; + rw.walk = sprintroute; + + ipwalkroutes(f, &rw); + + return rw.p - p; +} + +/* + * this code is not in routeflush to reduce stack size + */ +void +delroute(Fs *f, Route *r, int dolock) +{ + uchar addr[IPaddrlen]; + uchar mask[IPaddrlen]; + uchar gate[IPaddrlen]; + char t[5]; + int nifc; + + convroute(r, addr, mask, gate, t, &nifc); + if(r->type & Rv4) + v4delroute(f, addr+IPv4off, mask+IPv4off, dolock); + else + v6delroute(f, addr, mask, dolock); +} + +/* + * recurse until one route is deleted + * returns 0 if nothing is deleted, 1 otherwise + */ +int +routeflush(Fs *f, Route *r, char *tag) +{ + if(r == nil) + return 0; + if(routeflush(f, r->mid, tag)) + return 1; + if(routeflush(f, r->left, tag)) + return 1; + if(routeflush(f, r->right, tag)) + return 1; + if((r->type & Rifc) == 0){ + if(tag == nil || strncmp(tag, r->tag, sizeof(r->tag)) == 0){ + delroute(f, r, 0); + return 1; + } + } + return 0; +} + +long +routewrite(Fs *f, Chan *c, char *p, int n) +{ + int h, changed; + char *tag; + Cmdbuf *cb; + uchar addr[IPaddrlen]; + uchar mask[IPaddrlen]; + uchar gate[IPaddrlen]; + IPaux *a, *na; + + cb = parsecmd(p, n); + if(waserror()){ + free(cb); + nexterror(); + } + + if(strcmp(cb->f[0], "flush") == 0){ + tag = cb->f[1]; + for(h = 0; h < nelem(f->v4root); h++) + for(changed = 1; changed;){ + wlock(&routelock); + changed = routeflush(f, f->v4root[h], tag); + wunlock(&routelock); + } + for(h = 0; h < nelem(f->v6root); h++) + for(changed = 1; changed;){ + wlock(&routelock); + changed = routeflush(f, f->v6root[h], tag); + wunlock(&routelock); + } + } else if(strcmp(cb->f[0], "remove") == 0){ + if(cb->nf < 3) + error(Ebadarg); + parseip(addr, cb->f[1]); + parseipmask(mask, cb->f[2]); + if(memcmp(addr, v4prefix, IPv4off) == 0) + v4delroute(f, addr+IPv4off, mask+IPv4off, 1); + else + v6delroute(f, addr, mask, 1); + } else if(strcmp(cb->f[0], "add") == 0){ + if(cb->nf < 4) + error(Ebadarg); + parseip(addr, cb->f[1]); + parseipmask(mask, cb->f[2]); + parseip(gate, cb->f[3]); + tag = "none"; + if(c != nil){ + a = c->aux; + tag = a->tag; + } + if(memcmp(addr, v4prefix, IPv4off) == 0) + v4addroute(f, tag, addr+IPv4off, mask+IPv4off, gate+IPv4off, 0); + else + v6addroute(f, tag, addr, mask, gate, 0); + } else if(strcmp(cb->f[0], "tag") == 0) { + if(cb->nf < 2) + error(Ebadarg); + + a = c->aux; + na = newipaux(a->owner, cb->f[1]); + c->aux = na; + free(a); + } + + poperror(); + free(cb); + return n; +} diff --git a/os/ip/iprouter.c b/os/ip/iprouter.c new file mode 100644 index 00000000..631e728b --- /dev/null +++ b/os/ip/iprouter.c @@ -0,0 +1,56 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "../ip/ip.h" + +IProuter iprouter; + +/* + * User level routing. Ip packets we don't know what to do with + * come here. + */ +void +useriprouter(Fs *f, Ipifc *ifc, Block *bp) +{ + qlock(&f->iprouter); + if(f->iprouter.q != nil){ + bp = padblock(bp, IPaddrlen); + if(bp == nil) + return; + ipmove(bp->rp, ifc->lifc->local); + qpass(f->iprouter.q, bp); + }else + freeb(bp); + qunlock(&f->iprouter); +} + +void +iprouteropen(Fs *f) +{ + qlock(&f->iprouter); + f->iprouter.opens++; + if(f->iprouter.q == nil) + f->iprouter.q = qopen(64*1024, 0, 0, 0); + else if(f->iprouter.opens == 1) + qreopen(f->iprouter.q); + qunlock(&f->iprouter); +} + +void +iprouterclose(Fs *f) +{ + qlock(&f->iprouter); + f->iprouter.opens--; + if(f->iprouter.opens == 0) + qclose(f->iprouter.q); + qunlock(&f->iprouter); +} + +long +iprouterread(Fs *f, void *a, int n) +{ + return qread(f->iprouter.q, a, n); +} diff --git a/os/ip/ipv6.c b/os/ip/ipv6.c new file mode 100644 index 00000000..03f5e2df --- /dev/null +++ b/os/ip/ipv6.c @@ -0,0 +1,747 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" +#include "ipv6.h" + +enum +{ + IP4HDR = 20, /* sizeof(Ip4hdr) */ + IP6HDR = 40, /* sizeof(Ip6hdr) */ + IP_HLEN4 = 0x05, /* Header length in words */ + IP_DF = 0x4000, /* Don't fragment */ + IP_MF = 0x2000, /* More fragments */ + IP6FHDR = 8, /* sizeof(Fraghdr6) */ + IP_MAX = (32*1024), /* Maximum Internet packet size */ +}; + +#define IPV6CLASS(hdr) ((hdr->vcf[0]&0x0F)<<2 | (hdr->vcf[1]&0xF0)>>2) +#define BLKIPVER(xp) (((Ip6hdr*)((xp)->rp))->vcf[0]&0xF0) +/* + * This sleazy macro is stolen shamelessly from ip.c, see comment there. + */ +#define BKFG(xp) ((Ipfrag*)((xp)->base)) + +typedef struct IP IP; +typedef struct Fragment4 Fragment4; +typedef struct Fragment6 Fragment6; +typedef struct Ipfrag Ipfrag; + +Block* ip6reassemble(IP*, int, Block*, Ip6hdr*); +void ipfragfree6(IP*, Fragment6*); +Fragment6* ipfragallo6(IP*); +static Block* procxtns(IP *ip, Block *bp, int doreasm); +int unfraglen(Block *bp, uchar *nexthdr, int setfh); +Block* procopts(Block *bp); + +/* MIB II counters */ +enum +{ + Forwarding, + DefaultTTL, + InReceives, + InHdrErrors, + InAddrErrors, + ForwDatagrams, + InUnknownProtos, + InDiscards, + InDelivers, + OutRequests, + OutDiscards, + OutNoRoutes, + ReasmTimeout, + ReasmReqds, + ReasmOKs, + ReasmFails, + FragOKs, + FragFails, + FragCreates, + + Nstats, +}; + +static char *statnames[] = +{ +[Forwarding] "Forwarding", +[DefaultTTL] "DefaultTTL", +[InReceives] "InReceives", +[InHdrErrors] "InHdrErrors", +[InAddrErrors] "InAddrErrors", +[ForwDatagrams] "ForwDatagrams", +[InUnknownProtos] "InUnknownProtos", +[InDiscards] "InDiscards", +[InDelivers] "InDelivers", +[OutRequests] "OutRequests", +[OutDiscards] "OutDiscards", +[OutNoRoutes] "OutNoRoutes", +[ReasmTimeout] "ReasmTimeout", +[ReasmReqds] "ReasmReqds", +[ReasmOKs] "ReasmOKs", +[ReasmFails] "ReasmFails", +[FragOKs] "FragOKs", +[FragFails] "FragFails", +[FragCreates] "FragCreates", +}; + +struct Fragment4 +{ + Block* blist; + Fragment4* next; + ulong src; + ulong dst; + ushort id; + ulong age; +}; + +struct Fragment6 +{ + Block* blist; + Fragment6* next; + uchar src[IPaddrlen]; + uchar dst[IPaddrlen]; + uint id; + ulong age; +}; + +struct Ipfrag +{ + ushort foff; + ushort flen; +}; + +/* an instance of IP */ +struct IP +{ + ulong stats[Nstats]; + + QLock fraglock4; + Fragment4* flisthead4; + Fragment4* fragfree4; + Ref id4; + + QLock fraglock6; + Fragment6* flisthead6; + Fragment6* fragfree6; + Ref id6; + + int iprouting; /* true if we route like a gateway */ +}; + +int +ipoput6(Fs *f, Block *bp, int gating, int ttl, int tos, Conv *c) +{ + int tentative; + Ipifc *ifc; + uchar *gate, nexthdr; + Ip6hdr *eh; + int medialen, len, chunk, uflen, flen, seglen, lid, offset, fragoff, morefrags, blklen; + Route *r, *sr; + Fraghdr6 fraghdr; + Block *xp, *nb; + IP *ip; + int rv = 0; + + ip = f->ip; + + /* Fill out the ip header */ + eh = (Ip6hdr*)(bp->rp); + + ip->stats[OutRequests]++; + + /* Number of uchars in data and ip header to write */ + len = blocklen(bp); + + tentative = iptentative(f, eh->src); + if(tentative){ + netlog(f, Logip, "reject tx of packet with tentative src address\n"); + goto free; + } + + if(gating){ + chunk = nhgets(eh->ploadlen); + if(chunk > len){ + ip->stats[OutDiscards]++; + netlog(f, Logip, "short gated packet\n"); + goto free; + } + if(chunk + IPV6HDR_LEN < len) + len = chunk + IPV6HDR_LEN; + } + + if(len >= IP_MAX){ +// print("len > IP_MAX, free\n"); + ip->stats[OutDiscards]++; + netlog(f, Logip, "exceeded ip max size %I\n", eh->dst); + goto free; + } + + r = v6lookup(f, eh->dst, c); + if(r == nil){ +// print("no route for %I, src %I free\n", eh->dst, eh->src); + ip->stats[OutNoRoutes]++; + netlog(f, Logip, "no interface %I\n", eh->dst); + rv = -1; + goto free; + } + + ifc = r->ifc; + if(r->type & (Rifc|Runi)) + gate = eh->dst; + else + if(r->type & (Rbcast|Rmulti)) { + gate = eh->dst; + sr = v6lookup(f, eh->src, nil); + if(sr != nil && (sr->type & Runi)) + ifc = sr->ifc; + } + else + gate = r->v6.gate; + + if(!gating) + eh->vcf[0] = IP_VER6; + eh->ttl = ttl; + if(!gating) { + eh->vcf[0] |= (tos >> 4); + eh->vcf[1] = (tos << 4); + } + + if(!canrlock(ifc)) { + goto free; + } + + if(waserror()){ + runlock(ifc); + nexterror(); + } + + if(ifc->m == nil) { + goto raise; + } + + /* If we dont need to fragment just send it */ + medialen = ifc->maxtu - ifc->m->hsize; + if(len <= medialen) { + hnputs(eh->ploadlen, len-IPV6HDR_LEN); + ifc->m->bwrite(ifc, bp, V6, gate); + runlock(ifc); + poperror(); + return 0; + } + + if(gating) + if(ifc->reassemble <= 0) { + + /* v6 intermediate nodes are not supposed to fragment pkts; + we fragment if ifc->reassemble is turned on; an exception + needed for nat. + */ + + ip->stats[OutDiscards]++; + icmppkttoobig6(f, ifc, bp); + netlog(f, Logip, "%I: gated pkts not fragmented\n", eh->dst); + goto raise; + } + + /* start v6 fragmentation */ + uflen = unfraglen(bp, &nexthdr, 1); + if(uflen > medialen) { + ip->stats[FragFails]++; + ip->stats[OutDiscards]++; + netlog(f, Logip, "%I: unfragmentable part too big\n", eh->dst); + goto raise; + } + + flen = len - uflen; + seglen = (medialen - (uflen + IP6FHDR)) & ~7; + if(seglen < 8) { + ip->stats[FragFails]++; + ip->stats[OutDiscards]++; + netlog(f, Logip, "%I: seglen < 8\n", eh->dst); + goto raise; + } + + lid = incref(&ip->id6); + fraghdr.nexthdr = nexthdr; + fraghdr.res = 0; + hnputl(fraghdr.id, lid); + + xp = bp; + offset = uflen; + while (xp != nil && offset && offset >= BLEN(xp)) { + offset -= BLEN(xp); + xp = xp->next; + } + xp->rp += offset; + + fragoff = 0; + morefrags = 1; + + for(; fragoff < flen; fragoff += seglen) { + nb = allocb(uflen + IP6FHDR + seglen); + + if(fragoff + seglen >= flen) { + seglen = flen - fragoff; + morefrags = 0; + } + + hnputs(eh->ploadlen, seglen+IP6FHDR); + memmove(nb->wp, eh, uflen); + nb->wp += uflen; + + hnputs(fraghdr.offsetRM, fragoff); // last 3 bits must be 0 + fraghdr.offsetRM[1] |= morefrags; + memmove(nb->wp, &fraghdr, IP6FHDR); + nb->wp += IP6FHDR; + + /* Copy data */ + chunk = seglen; + while (chunk) { + if(!xp) { + ip->stats[OutDiscards]++; + ip->stats[FragFails]++; + freeblist(nb); + netlog(f, Logip, "!xp: chunk in v6%d\n", chunk); + goto raise; + } + blklen = chunk; + if(BLEN(xp) < chunk) + blklen = BLEN(xp); + memmove(nb->wp, xp->rp, blklen); + + nb->wp += blklen; + xp->rp += blklen; + chunk -= blklen; + if(xp->rp == xp->wp) + xp = xp->next; + } + + ifc->m->bwrite(ifc, nb, V6, gate); + ip->stats[FragCreates]++; + } + ip->stats[FragOKs]++; + +raise: + runlock(ifc); + poperror(); +free: + freeblist(bp); + return rv; +} + +void +ipiput6(Fs *f, Ipifc *ifc, Block *bp) +{ + int hl; + int hop, tos; + uchar proto; + Ip6hdr *h; + Proto *p; + int notforme; + int tentative; + uchar v6dst[IPaddrlen]; + IP *ip; + Route *r, *sr; + + ip = f->ip; + ip->stats[InReceives]++; + + /* + * Ensure we have all the header info in the first + * block. Make life easier for other protocols by + * collecting up to the first 64 bytes in the first block. + */ + if(BLEN(bp) < 64) { + hl = blocklen(bp); + if(hl < IP6HDR) + hl = IP6HDR; + if(hl > 64) + hl = 64; + bp = pullupblock(bp, hl); + if(bp == nil) + return; + } + + h = (Ip6hdr *)(bp->rp); + + memmove(&v6dst[0], &(h->dst)[0], IPaddrlen); + notforme = ipforme(f, v6dst) == 0; + tentative = iptentative(f, v6dst); + + if(tentative && (h->proto != ICMPv6)) { + print("tentative addr, drop\n"); + freeblist(bp); + return; + } + + /* Check header version */ + if(BLKIPVER(bp) != IP_VER6) { + ip->stats[InHdrErrors]++; + netlog(f, Logip, "ip: bad version %ux\n", (h->vcf[0]&0xF0)>>2); + freeblist(bp); + return; + } + + /* route */ + if(notforme) { + if(!ip->iprouting){ + freeb(bp); + return; + } + /* don't forward to source's network */ + sr = v6lookup(f, h->src, nil); + r = v6lookup(f, h->dst, nil); + + if(r == nil || sr == r){ + ip->stats[OutDiscards]++; + freeblist(bp); + return; + } + + /* don't forward if packet has timed out */ + hop = h->ttl; + if(hop < 1) { + ip->stats[InHdrErrors]++; + icmpttlexceeded6(f, ifc, bp); + freeblist(bp); + return; + } + + /* process headers & reassemble if the interface expects it */ + bp = procxtns(ip, bp, r->ifc->reassemble); + + if(bp == nil) + return; + + ip->stats[ForwDatagrams]++; + h = (Ip6hdr *) (bp->rp); + tos = IPV6CLASS(h); + hop = h->ttl; + ipoput6(f, bp, 1, hop-1, tos, nil); + return; + } + + /* reassemble & process headers if needed */ + bp = procxtns(ip, bp, 1); + + if(bp == nil) + return; + + h = (Ip6hdr *) (bp->rp); + proto = h->proto; + p = Fsrcvpcol(f, proto); + if(p != nil && p->rcv != nil) { + ip->stats[InDelivers]++; + (*p->rcv)(p, ifc, bp); + return; + } + + ip->stats[InDiscards]++; + ip->stats[InUnknownProtos]++; + freeblist(bp); +} + +/* + * ipfragfree6 - copied from ipfragfree4 - assume hold fraglock6 + */ +void +ipfragfree6(IP *ip, Fragment6 *frag) +{ + Fragment6 *fl, **l; + + if(frag->blist) + freeblist(frag->blist); + + memset(frag->src, 0, IPaddrlen); + frag->id = 0; + frag->blist = nil; + + l = &ip->flisthead6; + for(fl = *l; fl; fl = fl->next) { + if(fl == frag) { + *l = frag->next; + break; + } + l = &fl->next; + } + + frag->next = ip->fragfree6; + ip->fragfree6 = frag; + +} + +/* + * ipfragallo6 - copied from ipfragalloc4 + */ +Fragment6* +ipfragallo6(IP *ip) +{ + Fragment6 *f; + + while(ip->fragfree6 == nil) { + /* free last entry on fraglist */ + for(f = ip->flisthead6; f->next; f = f->next) + ; + ipfragfree6(ip, f); + } + f = ip->fragfree6; + ip->fragfree6 = f->next; + f->next = ip->flisthead6; + ip->flisthead6 = f; + f->age = NOW + 30000; + + return f; +} + +static Block* +procxtns(IP *ip, Block *bp, int doreasm) { + + int offset; + uchar proto; + Ip6hdr *h; + + h = (Ip6hdr *) (bp->rp); + offset = unfraglen(bp, &proto, 0); + + if((proto == FH) && (doreasm != 0)) { + bp = ip6reassemble(ip, offset, bp, h); + if(bp == nil) + return nil; + offset = unfraglen(bp, &proto, 0); + } + + if(proto == DOH || offset > IP6HDR) + bp = procopts(bp); + + return bp; +} + + +/* returns length of "Unfragmentable part", i.e., sum of lengths of ipv6 hdr, + * hop-by-hop & routing headers if present; *nexthdr is set to nexthdr value + * of the last header in the "Unfragmentable part"; if setfh != 0, nexthdr + * field of the last header in the "Unfragmentable part" is set to FH. + */ +int +unfraglen(Block *bp, uchar *nexthdr, int setfh) +{ + uchar *p, *q; + int ufl, hs; + + p = bp->rp; + q = p+6; /* proto, = p+sizeof(Ip6hdr.vcf)+sizeof(Ip6hdr.ploadlen) */ + *nexthdr = *q; + ufl = IP6HDR; + p += ufl; + + for(;;) { + if(*nexthdr == HBH || *nexthdr == RH) { + *nexthdr = *p; + hs = ((int)*(p+1) + 1) * 8; + ufl += hs; + q = p; + p += hs; + } + else + break; + } + + if(*nexthdr == FH) + *q = *p; + + if(setfh) + *q = FH; + + return ufl; +} + +Block* +procopts(Block *bp) +{ + return bp; +} + +Block* +ip6reassemble(IP* ip, int uflen, Block* bp, Ip6hdr* ih) +{ + + int fend, offset; + uint id; + Fragment6 *f, *fnext; + Fraghdr6 *fraghdr; + uchar src[IPaddrlen], dst[IPaddrlen]; + Block *bl, **l, *last, *prev; + int ovlap, len, fragsize, pktposn; + + fraghdr = (Fraghdr6 *) (bp->rp + uflen); + memmove(src, ih->src, IPaddrlen); + memmove(dst, ih->dst, IPaddrlen); + id = nhgetl(fraghdr->id); + offset = nhgets(fraghdr->offsetRM) & ~7; + + /* + * block lists are too hard, pullupblock into a single block + */ + if(bp->next){ + bp = pullupblock(bp, blocklen(bp)); + ih = (Ip6hdr *)(bp->rp); + } + + + qlock(&ip->fraglock6); + + /* + * find a reassembly queue for this fragment + */ + for(f = ip->flisthead6; f; f = fnext){ + fnext = f->next; + if(ipcmp(f->src, src)==0 && ipcmp(f->dst, dst)==0 && f->id == id) + break; + if(f->age < NOW){ + ip->stats[ReasmTimeout]++; + ipfragfree6(ip, f); + } + } + + + /* + * if this isn't a fragmented packet, accept it + * and get rid of any fragments that might go + * with it. + */ + if(nhgets(fraghdr->offsetRM)==0) { // first frag is also the last + if(f != nil) { + ipfragfree6(ip, f); + ip->stats[ReasmFails]++; + } + qunlock(&ip->fraglock6); + return bp; + } + + if(bp->base+sizeof(Ipfrag) >= bp->rp){ + bp = padblock(bp, sizeof(Ipfrag)); + bp->rp += sizeof(Ipfrag); + } + + BKFG(bp)->foff = offset; + BKFG(bp)->flen = nhgets(ih->ploadlen) + IP6HDR - uflen - IP6FHDR; + + /* First fragment allocates a reassembly queue */ + if(f == nil) { + f = ipfragallo6(ip); + f->id = id; + memmove(f->src, src, IPaddrlen); + memmove(f->dst, dst, IPaddrlen); + + f->blist = bp; + + qunlock(&ip->fraglock6); + ip->stats[ReasmReqds]++; + return nil; + } + + /* + * find the new fragment's position in the queue + */ + prev = nil; + l = &f->blist; + bl = f->blist; + while(bl != nil && BKFG(bp)->foff > BKFG(bl)->foff) { + prev = bl; + l = &bl->next; + bl = bl->next; + } + + /* Check overlap of a previous fragment - trim away as necessary */ + if(prev) { + ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff; + if(ovlap > 0) { + if(ovlap >= BKFG(bp)->flen) { + freeblist(bp); + qunlock(&ip->fraglock6); + return nil; + } + BKFG(prev)->flen -= ovlap; + } + } + + /* Link onto assembly queue */ + bp->next = *l; + *l = bp; + + /* Check to see if succeeding segments overlap */ + if(bp->next) { + l = &bp->next; + fend = BKFG(bp)->foff + BKFG(bp)->flen; + + /* Take completely covered segments out */ + + while(*l) { + ovlap = fend - BKFG(*l)->foff; + + if(ovlap <= 0) + break; + if(ovlap < BKFG(*l)->flen) { + BKFG(*l)->flen -= ovlap; + BKFG(*l)->foff += ovlap; + /* move up ih hdrs */ + memmove((*l)->rp + ovlap, (*l)->rp, uflen); + (*l)->rp += ovlap; + break; + } + last = (*l)->next; + (*l)->next = nil; + freeblist(*l); + *l = last; + } + } + + /* + * look for a complete packet. if we get to a fragment + * with the trailing bit of fraghdr->offsetRM[1] set, we're done. + */ + pktposn = 0; + for(bl = f->blist; bl; bl = bl->next) { + if(BKFG(bl)->foff != pktposn) + break; + + fraghdr = (Fraghdr6 *) (bl->rp + uflen); + if((fraghdr->offsetRM[1] & 1) == 0) { + + bl = f->blist; + + /* get rid of frag header in first fragment */ + + memmove(bl->rp + IP6FHDR, bl->rp, uflen); + bl->rp += IP6FHDR; + len = nhgets(((Ip6hdr*)(bl->rp))->ploadlen) - IP6FHDR; + bl->wp = bl->rp + len + IP6HDR; + + /* Pullup all the fragment headers and + * return a complete packet + */ + for(bl = bl->next; bl; bl = bl->next) { + fragsize = BKFG(bl)->flen; + len += fragsize; + bl->rp += uflen + IP6FHDR; + bl->wp = bl->rp + fragsize; + } + + bl = f->blist; + f->blist = nil; + ipfragfree6(ip, f); + ih = (Ip6hdr*)(bl->rp); + hnputs(ih->ploadlen, len); + qunlock(&ip->fraglock6); + ip->stats[ReasmOKs]++; + return bl; + } + pktposn += BKFG(bl)->flen; + } + qunlock(&ip->fraglock6); + return nil; +} + diff --git a/os/ip/ipv6.h b/os/ip/ipv6.h new file mode 100644 index 00000000..8da63cfd --- /dev/null +++ b/os/ip/ipv6.h @@ -0,0 +1,185 @@ +#define MIN(a, b) ((a) <= (b) ? (a) : (b)) + +/* rfc 3513 defines the address prefices */ +#define isv6mcast(addr) ((addr)[0] == 0xff) +#define islinklocal(addr) ((addr)[0] == 0xfe && ((addr)[1] & 0xc0) == 0x80) +#define issitelocal(addr) ((addr)[0] == 0xfe && ((addr)[1] & 0xc0) == 0xc0) +#define isv6global(addr) (((addr)[0] & 0xe0) == 0x20) + +#define optexsts(np) (nhgets((np)->ploadlen) > 24) +#define issmcast(addr) (memcmp((addr), v6solicitednode, 13) == 0) + +/* from RFC 2460 */ + +typedef struct Ip6hdr Ip6hdr; +typedef struct Opthdr Opthdr; +typedef struct Routinghdr Routinghdr; +typedef struct Fraghdr6 Fraghdr6; + +struct Ip6hdr { + uchar vcf[4]; // version:4, traffic class:8, flow label:20 + uchar ploadlen[2]; // payload length: packet length - 40 + uchar proto; // next header type + uchar ttl; // hop limit + uchar src[IPaddrlen]; + uchar dst[IPaddrlen]; +}; + +struct Opthdr { + uchar nexthdr; + uchar len; +}; + +struct Routinghdr { + uchar nexthdr; + uchar len; + uchar rtetype; + uchar segrem; +}; + +struct Fraghdr6 { + uchar nexthdr; + uchar res; + uchar offsetRM[2]; // Offset, Res, M flag + uchar id[4]; +}; + + +enum { /* Header Types */ + HBH = 0, //? + ICMP = 1, + IGMP = 2, + GGP = 3, + IPINIP = 4, + ST = 5, + TCP = 6, + UDP = 17, + ISO_TP4 = 29, + RH = 43, + FH = 44, + IDRP = 45, + RSVP = 46, + AH = 51, + ESP = 52, + ICMPv6 = 58, + NNH = 59, + DOH = 60, + ISO_IP = 80, + IGRP = 88, + OSPF = 89, + + Maxhdrtype = 256, +}; + + +enum { + // multicast flgs and scop + + well_known_flg = 0, + transient_flg = 1, + + node_local_scop = 1, + link_local_scop = 2, + site_local_scop = 5, + org_local_scop = 8, + global_scop = 14, + + // various prefix lengths + + SOLN_PREF_LEN = 13, + + // icmpv6 unreach codes + icmp6_no_route = 0, + icmp6_ad_prohib = 1, + icmp6_unassigned = 2, + icmp6_adr_unreach = 3, + icmp6_port_unreach = 4, + icmp6_unkn_code = 5, + + // various flags & constants + + v6MINTU = 1280, + HOP_LIMIT = 255, + ETHERHDR_LEN = 14, + IPV6HDR_LEN = 40, + IPV4HDR_LEN = 20, + + // option types + + SRC_LLADDRESS = 1, + TARGET_LLADDRESS = 2, + PREFIX_INFO = 3, + REDIR_HEADER = 4, + MTU_OPTION = 5, + + SRC_UNSPEC = 0, + SRC_UNI = 1, + TARG_UNI = 2, + TARG_MULTI = 3, + + t_unitent = 1, + t_uniproxy = 2, + t_unirany = 3, + + // Router constants (all times in milliseconds) + + MAX_INITIAL_RTR_ADVERT_INTERVAL = 16000, + MAX_INITIAL_RTR_ADVERTISEMENTS = 3, + MAX_FINAL_RTR_ADVERTISEMENTS = 3, + MIN_DELAY_BETWEEN_RAS = 3000, + MAX_RA_DELAY_TIME = 500, + + // Host constants + + MAX_RTR_SOLICITATION_DELAY = 1000, + RTR_SOLICITATION_INTERVAL = 4000, + MAX_RTR_SOLICITATIONS = 3, + + // Node constants + + MAX_MULTICAST_SOLICIT = 3, + MAX_UNICAST_SOLICIT = 3, + MAX_ANYCAST_DELAY_TIME = 1000, + MAX_NEIGHBOR_ADVERTISEMENT = 3, + REACHABLE_TIME = 30000, + RETRANS_TIMER = 1000, + DELAY_FIRST_PROBE_TIME = 5000, + +}; + +extern void ipv62smcast(uchar *, uchar *); +extern void icmpns(Fs *f, uchar* src, int suni, uchar* targ, int tuni, uchar* mac); +extern void icmpna(Fs *f, uchar* src, uchar* dst, uchar* targ, uchar* mac, uchar flags); +extern void icmpttlexceeded6(Fs *f, Ipifc *ifc, Block *bp); +extern void icmppkttoobig6(Fs *f, Ipifc *ifc, Block *bp); +extern void icmphostunr(Fs *f, Ipifc *ifc, Block *bp, int code, int free); + +extern uchar v6allnodesN[IPaddrlen]; +extern uchar v6allnodesL[IPaddrlen]; +extern uchar v6allroutersN[IPaddrlen]; +extern uchar v6allroutersL[IPaddrlen]; +extern uchar v6allnodesNmask[IPaddrlen]; +extern uchar v6allnodesLmask[IPaddrlen]; +extern uchar v6allroutersS[IPaddrlen]; +extern uchar v6solicitednode[IPaddrlen]; +extern uchar v6solicitednodemask[IPaddrlen]; +extern uchar v6Unspecified[IPaddrlen]; +extern uchar v6loopback[IPaddrlen]; +extern uchar v6loopbackmask[IPaddrlen]; +extern uchar v6linklocal[IPaddrlen]; +extern uchar v6linklocalmask[IPaddrlen]; +extern uchar v6sitelocal[IPaddrlen]; +extern uchar v6sitelocalmask[IPaddrlen]; +extern uchar v6glunicast[IPaddrlen]; +extern uchar v6multicast[IPaddrlen]; +extern uchar v6multicastmask[IPaddrlen]; + +extern int v6llpreflen; +extern int v6slpreflen; +extern int v6lbpreflen; +extern int v6mcpreflen; +extern int v6snpreflen; +extern int v6aNpreflen; +extern int v6aLpreflen; + +extern int ReTransTimer; diff --git a/os/ip/kernel.h b/os/ip/kernel.h new file mode 100644 index 00000000..55718fa1 --- /dev/null +++ b/os/ip/kernel.h @@ -0,0 +1,10 @@ +extern int kclose(int); +extern int kdial(char*, char*, char*, int*); +extern int kannounce(char*, char*); +extern void kerrstr(char*); +extern void kgerrstr(char*); +extern int kopen(char*, int); +extern long kread(int, void*, long); +extern long kseek(int, vlong, int); +extern long kwrite(int, void*, long); +extern void kwerrstr(char *, ...); diff --git a/os/ip/loopbackmedium.c b/os/ip/loopbackmedium.c new file mode 100644 index 00000000..69d87449 --- /dev/null +++ b/os/ip/loopbackmedium.c @@ -0,0 +1,121 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" + +enum +{ + Maxtu= 16*1024, +}; + +typedef struct LB LB; +struct LB +{ + Proc *readp; + Queue *q; + Fs *f; +}; + +static void loopbackread(void *a); + +static void +loopbackbind(Ipifc *ifc, int, char**) +{ + LB *lb; + + lb = smalloc(sizeof(*lb)); + lb->f = ifc->conv->p->f; + /* TO DO: make queue size a function of kernel memory */ + lb->q = qopen(128*1024, Qmsg, nil, nil); + ifc->arg = lb; + ifc->mbps = 1000; + + kproc("loopbackread", loopbackread, ifc); + +} + +static void +loopbackunbind(Ipifc *ifc) +{ + LB *lb = ifc->arg; + + if(lb->readp) + postnote(lb->readp, 1, "unbind", 0); + + /* wait for reader to die */ + while(lb->readp != 0) + tsleep(&up->sleep, return0, 0, 300); + + /* clean up */ + qfree(lb->q); + free(lb); +} + +static void +loopbackbwrite(Ipifc *ifc, Block *bp, int, uchar*) +{ + LB *lb; + + lb = ifc->arg; + if(qpass(lb->q, bp) < 0) + ifc->outerr++; + ifc->out++; +} + +static void +loopbackread(void *a) +{ + Ipifc *ifc; + Block *bp; + LB *lb; + + ifc = a; + lb = ifc->arg; + lb->readp = up; /* hide identity under a rock for unbind */ + if(waserror()){ + lb->readp = 0; + pexit("hangup", 1); + } + for(;;){ + bp = qbread(lb->q, Maxtu); + if(bp == nil) + continue; + ifc->in++; + if(!canrlock(ifc)){ + freeb(bp); + continue; + } + if(waserror()){ + runlock(ifc); + nexterror(); + } + if(ifc->lifc == nil) + freeb(bp); + else + ipiput4(lb->f, ifc, bp); + runlock(ifc); + poperror(); + } +} + +Medium loopbackmedium = +{ +.hsize= 0, +.mintu= 0, +.maxtu= Maxtu, +.maclen= 0, +.name= "loopback", +.bind= loopbackbind, +.unbind= loopbackunbind, +.bwrite= loopbackbwrite, +}; + +void +loopbackmediumlink(void) +{ + addipmedium(&loopbackmedium); +} diff --git a/os/ip/netdevmedium.c b/os/ip/netdevmedium.c new file mode 100644 index 00000000..9d5f9749 --- /dev/null +++ b/os/ip/netdevmedium.c @@ -0,0 +1,153 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" + +static void netdevbind(Ipifc *ifc, int argc, char **argv); +static void netdevunbind(Ipifc *ifc); +static void netdevbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip); +static void netdevread(void *a); + +typedef struct Netdevrock Netdevrock; +struct Netdevrock +{ + Fs *f; /* file system we belong to */ + Proc *readp; /* reading process */ + Chan *mchan; /* Data channel */ +}; + +Medium netdevmedium = +{ +.name= "netdev", +.hsize= 0, +.mintu= 0, +.maxtu= 64000, +.maclen= 0, +.bind= netdevbind, +.unbind= netdevunbind, +.bwrite= netdevbwrite, +.unbindonclose= 0, +}; + +/* + * called to bind an IP ifc to a generic network device + * called with ifc qlock'd + */ +static void +netdevbind(Ipifc *ifc, int argc, char **argv) +{ + Chan *mchan; + Netdevrock *er; + + if(argc < 2) + error(Ebadarg); + + mchan = namec(argv[2], Aopen, ORDWR, 0); + + er = smalloc(sizeof(*er)); + er->mchan = mchan; + er->f = ifc->conv->p->f; + + ifc->arg = er; + + kproc("netdevread", netdevread, ifc, 0); +} + +/* + * called with ifc wlock'd + */ +static void +netdevunbind(Ipifc *ifc) +{ + Netdevrock *er = ifc->arg; + + if(er->readp != nil) + postnote(er->readp, 1, "unbind", 0); + + /* wait for readers to die */ + while(er->readp != nil) + tsleep(&up->sleep, return0, 0, 300); + + if(er->mchan != nil) + cclose(er->mchan); + + free(er); +} + +/* + * called by ipoput with a single block to write + */ +static void +netdevbwrite(Ipifc *ifc, Block *bp, int, uchar*) +{ + Netdevrock *er = ifc->arg; + + if(bp->next) + bp = concatblock(bp); + if(BLEN(bp) < ifc->mintu) + bp = adjustblock(bp, ifc->mintu); + + devtab[er->mchan->type]->bwrite(er->mchan, bp, 0); + ifc->out++; +} + +/* + * process to read from the device + */ +static void +netdevread(void *a) +{ + Ipifc *ifc; + Block *bp; + Netdevrock *er; + char *argv[1]; + + ifc = a; + er = ifc->arg; + er->readp = up; /* hide identity under a rock for unbind */ + if(waserror()){ + er->readp = nil; + pexit("hangup", 1); + } + for(;;){ + bp = devtab[er->mchan->type]->bread(er->mchan, ifc->maxtu, 0); + if(bp == nil){ + /* + * get here if mchan is a pipe and other side hangs up + * clean up this interface & get out +ZZZ is this a good idea? + */ + poperror(); + er->readp = nil; + argv[0] = "unbind"; + if(!waserror()) + ifc->conv->p->ctl(ifc->conv, argv, 1); + pexit("hangup", 1); + } + if(!canrlock(ifc)){ + freeb(bp); + continue; + } + if(waserror()){ + runlock(ifc); + nexterror(); + } + ifc->in++; + if(ifc->lifc == nil) + freeb(bp); + else + ipiput4(er->f, ifc, bp); + runlock(ifc); + poperror(); + } +} + +void +netdevmediumlink(void) +{ + addipmedium(&netdevmedium); +} diff --git a/os/ip/netlog.c b/os/ip/netlog.c new file mode 100644 index 00000000..3ee200fe --- /dev/null +++ b/os/ip/netlog.c @@ -0,0 +1,263 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "../ip/ip.h" + +enum { + Nlog = 4*1024, +}; + +/* + * action log + */ +struct Netlog { + Lock; + int opens; + char* buf; + char *end; + char *rptr; + int len; + + int logmask; /* mask of things to debug */ + uchar iponly[IPaddrlen]; /* ip address to print debugging for */ + int iponlyset; + + QLock; + Rendez; +}; + +typedef struct Netlogflag { + char* name; + int mask; +} Netlogflag; + +static Netlogflag flags[] = +{ + { "ppp", Logppp, }, + { "ip", Logip, }, + { "fs", Logfs, }, + { "tcp", Logtcp, }, + { "il", Logil, }, + { "icmp", Logicmp, }, + { "udp", Logudp, }, + { "compress", Logcompress, }, + { "ilmsg", Logil|Logilmsg, }, + { "gre", Loggre, }, + { "tcpwin", Logtcp|Logtcpwin, }, + { "tcprxmt", Logtcp|Logtcprxmt, }, + { "udpmsg", Logudp|Logudpmsg, }, + { "ipmsg", Logip|Logipmsg, }, + { "esp", Logesp, }, + { nil, 0, }, +}; + +char Ebadnetctl[] = "too few arguments for netlog control message"; + +enum +{ + CMset, + CMclear, + CMonly, +}; + +static +Cmdtab routecmd[] = { + CMset, "set", 0, + CMclear, "clear", 0, + CMonly, "only", 0, +}; + +void +netloginit(Fs *f) +{ + f->alog = smalloc(sizeof(Netlog)); +} + +void +netlogopen(Fs *f) +{ + lock(f->alog); + if(waserror()){ + unlock(f->alog); + nexterror(); + } + if(f->alog->opens == 0){ + if(f->alog->buf == nil) + f->alog->buf = malloc(Nlog); + f->alog->rptr = f->alog->buf; + f->alog->end = f->alog->buf + Nlog; + } + f->alog->opens++; + unlock(f->alog); + poperror(); +} + +void +netlogclose(Fs *f) +{ + lock(f->alog); + if(waserror()){ + unlock(f->alog); + nexterror(); + } + f->alog->opens--; + if(f->alog->opens == 0){ + free(f->alog->buf); + f->alog->buf = nil; + } + unlock(f->alog); + poperror(); +} + +static int +netlogready(void *a) +{ + Fs *f = a; + + return f->alog->len; +} + +long +netlogread(Fs *f, void *a, ulong, long n) +{ + int i, d; + char *p, *rptr; + + qlock(f->alog); + if(waserror()){ + qunlock(f->alog); + nexterror(); + } + + for(;;){ + lock(f->alog); + if(f->alog->len){ + if(n > f->alog->len) + n = f->alog->len; + d = 0; + rptr = f->alog->rptr; + f->alog->rptr += n; + if(f->alog->rptr >= f->alog->end){ + d = f->alog->rptr - f->alog->end; + f->alog->rptr = f->alog->buf + d; + } + f->alog->len -= n; + unlock(f->alog); + + i = n-d; + p = a; + memmove(p, rptr, i); + memmove(p+i, f->alog->buf, d); + break; + } + else + unlock(f->alog); + + sleep(f->alog, netlogready, f); + } + + qunlock(f->alog); + poperror(); + + return n; +} + +void +netlogctl(Fs *f, char* s, int n) +{ + int i, set; + Netlogflag *fp; + Cmdbuf *cb; + Cmdtab *ct; + + cb = parsecmd(s, n); + if(waserror()){ + free(cb); + nexterror(); + } + + if(cb->nf < 2) + error(Ebadnetctl); + + ct = lookupcmd(cb, routecmd, nelem(routecmd)); + + SET(set); + + switch(ct->index){ + case CMset: + set = 1; + break; + + case CMclear: + set = 0; + break; + + case CMonly: + parseip(f->alog->iponly, cb->f[1]); + if(ipcmp(f->alog->iponly, IPnoaddr) == 0) + f->alog->iponlyset = 0; + else + f->alog->iponlyset = 1; + free(cb); + return; + + default: + cmderror(cb, "unknown ip control message"); + } + + for(i = 1; i < cb->nf; i++){ + for(fp = flags; fp->name; fp++) + if(strcmp(fp->name, cb->f[i]) == 0) + break; + if(fp->name == nil) + continue; + if(set) + f->alog->logmask |= fp->mask; + else + f->alog->logmask &= ~fp->mask; + } + + free(cb); + poperror(); +} + +void +netlog(Fs *f, int mask, char *fmt, ...) +{ + char buf[128], *t, *fp; + int i, n; + va_list arg; + + if(!(f->alog->logmask & mask)) + return; + + if(f->alog->opens == 0) + return; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + + lock(f->alog); + i = f->alog->len + n - Nlog; + if(i > 0){ + f->alog->len -= i; + f->alog->rptr += i; + if(f->alog->rptr >= f->alog->end) + f->alog->rptr = f->alog->buf + (f->alog->rptr - f->alog->end); + } + t = f->alog->rptr + f->alog->len; + fp = buf; + f->alog->len += n; + while(n-- > 0){ + if(t >= f->alog->end) + t = f->alog->buf + (t - f->alog->end); + *t++ = *fp++; + } + unlock(f->alog); + + wakeup(f->alog); +} diff --git a/os/ip/nullmedium.c b/os/ip/nullmedium.c new file mode 100644 index 00000000..bc575c05 --- /dev/null +++ b/os/ip/nullmedium.c @@ -0,0 +1,39 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" + +static void +nullbind(Ipifc*, int, char**) +{ + error("cannot bind null device"); +} + +static void +nullunbind(Ipifc*) +{ +} + +static void +nullbwrite(Ipifc*, Block*, int, uchar*) +{ + error("nullbwrite"); +} + +Medium nullmedium = +{ +.name= "null", +.bind= nullbind, +.unbind= nullunbind, +.bwrite= nullbwrite, +}; + +void +nullmediumlink(void) +{ + addipmedium(&nullmedium); +} diff --git a/os/ip/pktmedium.c b/os/ip/pktmedium.c new file mode 100644 index 00000000..6bba1a4c --- /dev/null +++ b/os/ip/pktmedium.c @@ -0,0 +1,79 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" + + +static void pktbind(Ipifc*, int, char**); +static void pktunbind(Ipifc*); +static void pktbwrite(Ipifc*, Block*, int, uchar*); +static void pktin(Fs*, Ipifc*, Block*); + +Medium pktmedium = +{ +.name= "pkt", +.hsize= 14, +.mintu= 40, +.maxtu= 4*1024, +.maclen= 6, +.bind= pktbind, +.unbind= pktunbind, +.bwrite= pktbwrite, +.pktin= pktin, +.unbindonclose= 1, +}; + +/* + * called to bind an IP ifc to an ethernet device + * called with ifc wlock'd + */ +static void +pktbind(Ipifc*, int, char**) +{ +} + +/* + * called with ifc wlock'd + */ +static void +pktunbind(Ipifc*) +{ +} + +/* + * called by ipoput with a single packet to write + */ +static void +pktbwrite(Ipifc *ifc, Block *bp, int, uchar*) +{ + /* enqueue onto the conversation's rq */ + bp = concatblock(bp); + if(ifc->conv->snoopers.ref > 0) + qpass(ifc->conv->sq, copyblock(bp, BLEN(bp))); + qpass(ifc->conv->rq, bp); +} + +/* + * called with ifc rlocked when someone write's to 'data' + */ +static void +pktin(Fs *f, Ipifc *ifc, Block *bp) +{ + if(ifc->lifc == nil) + freeb(bp); + else { + if(ifc->conv->snoopers.ref > 0) + qpass(ifc->conv->sq, copyblock(bp, BLEN(bp))); + ipiput4(f, ifc, bp); + } +} + +void +pktmediumlink(void) +{ + addipmedium(&pktmedium); +} diff --git a/os/ip/plan9.c b/os/ip/plan9.c new file mode 100644 index 00000000..ce24a8d3 --- /dev/null +++ b/os/ip/plan9.c @@ -0,0 +1,36 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "ip.h" + +/* + * some hacks for commonality twixt inferno and plan9 + */ + +char* +commonuser(void) +{ + return up->env->user; +} + +Chan* +commonfdtochan(int fd, int mode, int a, int b) +{ + return fdtochan(up->env->fgrp, fd, mode, a, b); +} + +char* +commonerror(void) +{ + return up->env->errstr; +} + +int +postnote(Proc *p, int, char *, int) +{ + swiproc(p, 0); + return 0; +} diff --git a/os/ip/ppp.c b/os/ip/ppp.c new file mode 100644 index 00000000..73885adc --- /dev/null +++ b/os/ip/ppp.c @@ -0,0 +1,1656 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include +#include +#include "ip.h" +#include "ppp.h" + +int nocompress; +Ipaddr pppdns[2]; + +/* + * Calculate FCS - rfc 1331 + */ +ushort fcstab[256] = +{ + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +static char *snames[] = +{ + "Sclosed", + "Sclosing", + "Sreqsent", + "Sackrcvd", + "Sacksent", + "Sopened", +}; + +static void init(PPP*); +static void setphase(PPP*, int); +static void pinit(PPP*, Pstate*); +static void ppptimer(void*); +static void ptimer(PPP*, Pstate*); +static int getframe(PPP*, Block**); +static Block* putframe(PPP*, int, Block*); +static uchar* escapebyte(PPP*, ulong, uchar*, ushort*); +static void config(PPP*, Pstate*, int); +static int getopts(PPP*, Pstate*, Block*); +static void rejopts(PPP*, Pstate*, Block*, int); +static void newstate(PPP*, Pstate*, int); +static void rcv(PPP*, Pstate*, Block*); +static void getchap(PPP*, Block*); +static void getpap(PPP*, Block*); +static void sendpap(PPP*); +static void getlqm(PPP*, Block*); +static void putlqm(PPP*); +static void hangup(PPP*); +static void remove(PPP*); + +static int validv4(Ipaddr); +static void invalidate(Ipaddr); +static void ipconnect(PPP *); +static void setdefroute(PPP *, Ipaddr); +static void printopts(PPP *, Pstate*, Block*, int); +static void sendtermreq(PPP*, Pstate*); + +static void +errlog(PPP *ppp, char *err) +{ + int n; + char msg[64]; + + n = snprint(msg, sizeof(msg), "%s\n", err); + qproduce(ppp->ifc->conv->eq, msg, n); +} + +static void +init(PPP* ppp) +{ + if(ppp->inbuf == nil){ + ppp->inbuf = allocb(4096); + ppp->outbuf = allocb(4096); + + ppp->lcp = malloc(sizeof(Pstate)); + ppp->ipcp = malloc(sizeof(Pstate)); + if(ppp->lcp == nil || ppp->ipcp == nil) + error("ppp init: malloc"); + + ppp->lcp->proto = Plcp; + ppp->lcp->state = Sclosed; + ppp->ipcp->proto = Pipcp; + ppp->ipcp->state = Sclosed; + + kproc("ppptimer", ppptimer, ppp, KPDUPPG|KPDUPFDG); + } + + pinit(ppp, ppp->lcp); + setphase(ppp, Plink); +} + +static void +setphase(PPP *ppp, int phase) +{ + int oldphase; + + oldphase = ppp->phase; + + ppp->phase = phase; + switch(phase){ + default: + panic("ppp: unknown phase %d", phase); + case Pdead: + /* restart or exit? */ + pinit(ppp, ppp->lcp); + setphase(ppp, Plink); + break; + case Plink: + /* link down */ + switch(oldphase) { + case Pnet: + newstate(ppp, ppp->ipcp, Sclosed); + } + break; + case Pauth: + if(ppp->usepap) + sendpap(ppp); + else if(!ppp->usechap) + setphase(ppp, Pnet); + break; + case Pnet: + pinit(ppp, ppp->ipcp); + break; + case Pterm: + /* what? */ + break; + } +} + +static void +pinit(PPP *ppp, Pstate *p) +{ + p->timeout = 0; + + switch(p->proto){ + case Plcp: + ppp->magic = TK2MS(MACHP(0)->ticks); + ppp->xctlmap = 0xffffffff; + ppp->period = 0; + p->optmask = 0xffffffff; + ppp->rctlmap = 0; + ppp->ipcp->state = Sclosed; + ppp->ipcp->optmask = 0xffffffff; + + /* quality goo */ + ppp->timeout = 0; + memset(&ppp->in, 0, sizeof(ppp->in)); + memset(&ppp->out, 0, sizeof(ppp->out)); + memset(&ppp->pin, 0, sizeof(ppp->pin)); + memset(&ppp->pout, 0, sizeof(ppp->pout)); + memset(&ppp->sin, 0, sizeof(ppp->sin)); + break; + case Pipcp: + if(ppp->localfrozen == 0) + invalidate(ppp->local); + if(ppp->remotefrozen == 0) + invalidate(ppp->remote); + p->optmask = 0xffffffff; + ppp->ctcp = compress_init(ppp->ctcp); + ppp->usedns = 3; + invalidate(ppp->dns1); + invalidate(ppp->dns2); + break; + } + p->confid = p->rcvdconfid = -1; + config(ppp, p, 1); + newstate(ppp, p, Sreqsent); +} + +/* + * change protocol to a new state. + */ +static void +newstate(PPP *ppp, Pstate *p, int state) +{ + netlog(ppp->f, Logppp, "%ux %ux %s->%s ctlmap %lux/%lux flags %ux mtu %d mru %d\n", ppp, p->proto, + snames[p->state], snames[state], ppp->rctlmap, ppp->xctlmap, p->flags, + ppp->mtu, ppp->mru); + + if(p->proto == Plcp) { + if(state == Sopened) + setphase(ppp, Pauth); + else if(state == Sclosed) + setphase(ppp, Pdead); + else if(p->state == Sopened) + setphase(ppp, Plink); + } + + if(p->proto == Pipcp && state == Sopened && validv4(ppp->local) && validv4(ppp->remote)){ + netlog(ppp->f, Logppp, "pppnewstate: local %I remote %I\n", ppp->local, ppp->remote); + ipmove(pppdns[0], ppp->dns1); + ipmove(pppdns[1], ppp->dns2); + ipconnect(ppp); + /* if this is the only network, set up a default route */ +// if(ppp->ifc->link==nil) /* how??? */ + setdefroute(ppp, ppp->remote); + errlog(ppp, Enoerror); + } + + p->state = state; +} + +static void +remove(PPP *ppp) +{ + free(ppp->ipcp); + ppp->ipcp = 0; + free(ppp->ctcp); + ppp->ctcp = 0; + free(ppp->lcp); + ppp->lcp = 0; + if (ppp->inbuf) { + freeb(ppp->inbuf); + ppp->inbuf = nil; + } + if (ppp->outbuf) { + freeb(ppp->outbuf); + ppp->outbuf = nil; + } + free(ppp); +} + +void +pppclose(PPP *ppp) +{ + hangup(ppp); + remove(ppp); +} + +static void +dumpblock(Block *b) +{ + char x[256]; + int i; + + for(i = 0; i < (sizeof(x)-1)/3 && b->rp+i < b->wp; i++) + sprint(&x[3*i], "%2.2ux ", b->rp[i]); + print("%s\n", x); +} + +/* returns (protocol, information) */ +static int +getframe(PPP *ppp, Block **info) +{ + uchar *p, *from, *to; + int n, len, proto; + ulong c; + ushort fcs; + Block *buf, *b; + + buf = ppp->inbuf; + for(;;){ + /* read till we hit a frame byte or run out of room */ + for(p = buf->rp; buf->wp < buf->lim;){ + for(; p < buf->wp; p++) + if(*p == HDLC_frame) + goto break2; + + len = buf->lim - buf->wp; + n = 0; + if(ppp->dchan != nil) + n = kchanio(ppp->dchan, buf->wp, len, OREAD); + netlog(ppp->f, Logppp, "ppp kchanio %d bytes\n", n); + if(n <= 0){ + buf->wp = buf->rp; +// if(n < 0) +// print("ppp kchanio(%s) returned %d: %r", +// ppp->dchan->path->elem, n); + *info = nil; + return 0; + } + buf->wp += n; + } +break2: + + /* copy into block, undoing escapes, and caculating fcs */ + fcs = PPP_initfcs; + b = allocb(p - buf->rp); + to = b->wp; + for(from = buf->rp; from != p;){ + c = *from++; + if(c == HDLC_esc){ + if(from == p) + break; + c = *from++ ^ 0x20; + } else if((c < 0x20) && (ppp->rctlmap & (1 << c))) + continue; + *to++ = c; + fcs = (fcs >> 8) ^ fcstab[(fcs ^ c) & 0xff]; + } + + /* copy down what's left in buffer */ + p++; + memmove(buf->rp, p, buf->wp - p); + n = p - buf->rp; + buf->wp -= n; + b->wp = to - 2; + + /* return to caller if checksum matches */ + if(fcs == PPP_goodfcs){ + if(b->rp[0] == PPP_addr && b->rp[1] == PPP_ctl) + b->rp += 2; + proto = *b->rp++; + if((proto & 0x1) == 0) + proto = (proto<<8) | *b->rp++; + if(b->rp < b->wp){ + ppp->in.bytes += n; + ppp->in.packets++; + *info = b; + return proto; + } + } else if(BLEN(b) > 0){ + ppp->ifc->inerr++; + ppp->in.discards++; + netlog(ppp->f, Logppp, "len %d/%d cksum %ux (%ux %ux %ux %ux)\n", + BLEN(b), BLEN(buf), fcs, b->rp[0], + b->rp[1], b->rp[2], b->rp[3]); + } + + freeblist(b); + } + *info = nil; + return 0; +} + +/* send a PPP frame */ +static Block * +putframe(PPP *ppp, int proto, Block *b) +{ + Block *buf; + uchar *to, *from; + ushort fcs; + ulong ctlmap; + int c; + Block *bp; + + if(ppp->dchan == nil){ + netlog(ppp->f, Logppp, "putframe: dchan down\n"); + errlog(ppp, Ehungup); + return b; + } + netlog(ppp->f, Logppp, "putframe %ux %d %d (%d bytes)\n", proto, b->rp[0], b->rp[1], BLEN(b)); + + ppp->out.packets++; + + if(proto == Plcp) + ctlmap = 0xffffffff; + else + ctlmap = ppp->xctlmap; + + /* make sure we have head room */ + if(b->rp - b->base < 4){ + b = padblock(b, 4); + b->rp += 4; + } + + /* add in the protocol and address, we'd better have left room */ + from = b->rp; + *--from = proto; + if(!(ppp->lcp->flags&Fpc) || proto > 0x100 || proto == Plcp) + *--from = proto>>8; + if(!(ppp->lcp->flags&Fac) || proto == Plcp){ + *--from = PPP_ctl; + *--from = PPP_addr; + } + + qlock(&ppp->outlock); + buf = ppp->outbuf; + + /* escape and checksum the body */ + fcs = PPP_initfcs; + to = buf->rp; + + *to++ = HDLC_frame; + + for(bp = b; bp; bp = bp->next){ + if(bp != b) + from = bp->rp; + for(; from < bp->wp; from++){ + c = *from; + if(c == HDLC_frame || c == HDLC_esc + || (c < 0x20 && ((1<> 8) ^ fcstab[(fcs ^ c) & 0xff]; + } + } + + /* add on and escape the checksum */ + fcs = ~fcs; + c = fcs; + if(c == HDLC_frame || c == HDLC_esc + || (c < 0x20 && ((1<>8; + if(c == HDLC_frame || c == HDLC_esc + || (c < 0x20 && ((1<wp = to; + if(ppp->dchan == nil){ + netlog(ppp->f, Logppp, "putframe: dchan down\n"); + errlog(ppp, Ehungup); + }else{ + kchanio(ppp->dchan, buf->rp, BLEN(buf), OWRITE); + ppp->out.bytes += BLEN(buf); + } + + qunlock(&ppp->outlock); + return b; +} + +#define IPB2LCP(b) ((Lcpmsg*)((b)->wp-4)) + +static Block* +alloclcp(int code, int id, int len) +{ + Block *b; + Lcpmsg *m; + + /* + * leave room for header + */ + b = allocb(len); + + m = (Lcpmsg*)b->wp; + m->code = code; + m->id = id; + b->wp += 4; + + return b; +} + +static void +putao(Block *b, int type, int aproto, int alg) +{ + *b->wp++ = type; + *b->wp++ = 5; + hnputs(b->wp, aproto); + b->wp += 2; + *b->wp++ = alg; +} + +static void +putlo(Block *b, int type, ulong val) +{ + *b->wp++ = type; + *b->wp++ = 6; + hnputl(b->wp, val); + b->wp += 4; +} + +static void +putv4o(Block *b, int type, Ipaddr val) +{ + *b->wp++ = type; + *b->wp++ = 6; + if(v6tov4(b->wp, val) < 0){ + /*panic("putv4o")*/; + } + b->wp += 4; +} + +static void +putso(Block *b, int type, ulong val) +{ + *b->wp++ = type; + *b->wp++ = 4; + hnputs(b->wp, val); + b->wp += 2; +} + +static void +puto(Block *b, int type) +{ + *b->wp++ = type; + *b->wp++ = 2; +} + +/* + * send configuration request + */ +static void +config(PPP *ppp, Pstate *p, int newid) +{ + Block *b; + Lcpmsg *m; + int id; + + if(newid){ + id = ++(p->id); + p->confid = id; + p->timeout = Timeout; + } else + id = p->confid; + b = alloclcp(Lconfreq, id, 256); + m = IPB2LCP(b); + USED(m); + + switch(p->proto){ + case Plcp: + if(p->optmask & Fmagic) + putlo(b, Omagic, ppp->magic); + if(p->optmask & Fmtu) + putso(b, Omtu, ppp->mru); + if(p->optmask & Fac) + puto(b, Oac); + if(p->optmask & Fpc) + puto(b, Opc); + if(p->optmask & Fctlmap) + putlo(b, Octlmap, 0); /* we don't want anything escaped */ + break; + case Pipcp: + if((p->optmask & Fipaddr) /*&& validv4(ppp->local)*/) + putv4o(b, Oipaddr, ppp->local); + if(!nocompress && (p->optmask & Fipcompress)){ + *b->wp++ = Oipcompress; + *b->wp++ = 6; + hnputs(b->wp, Pvjctcp); + b->wp += 2; + *b->wp++ = MAX_STATES-1; + *b->wp++ = 1; + } + if(ppp->usedns & 1) + putlo(b, Oipdns, 0); + if(ppp->usedns & 2) + putlo(b, Oipdns2, 0); + break; + } + + hnputs(m->len, BLEN(b)); + b = putframe(ppp, p->proto, b); + freeblist(b); +} + +/* + * parse configuration request, sends an ack or reject packet + * + * returns: -1 if request was syntacticly incorrect + * 0 if packet was accepted + * 1 if packet was rejected + */ +static int +getopts(PPP *ppp, Pstate *p, Block *b) +{ + Lcpmsg *m, *repm; + Lcpopt *o; + uchar *cp; + ulong rejecting, nacking, flags, proto; + ulong mtu, ctlmap, period; + ulong x; + Block *repb; + Ipaddr ipaddr; + + rejecting = 0; + nacking = 0; + flags = 0; + + /* defaults */ + invalidate(ipaddr); + mtu = ppp->mtu; + + ctlmap = 0xffffffff; + period = 0; + + m = (Lcpmsg*)b->rp; + repb = alloclcp(Lconfack, m->id, BLEN(b)); + repm = IPB2LCP(repb); + + /* copy options into ack packet */ + memmove(repm->data, m->data, b->wp - m->data); + repb->wp += b->wp - m->data; + + /* look for options we don't recognize or like */ + for(cp = m->data; cp < b->wp; cp += o->len){ + o = (Lcpopt*)cp; + if(cp + o->len > b->wp || o->len == 0){ + freeblist(repb); + netlog(ppp->f, Logppp, "ppp %s: bad option length %ux\n", ppp->ifc->dev, + o->type); + return -1; + } + + switch(p->proto){ + case Plcp: + switch(o->type){ + case Oac: + flags |= Fac; + continue; + case Opc: + flags |= Fpc; + continue; + case Omtu: + mtu = nhgets(o->data); + if(mtu < ppp->ifc->m->mintu){ + netlog(ppp->f, Logppp, "bogus mtu %d\n", mtu); + mtu = ppp->ifc->m->mintu; + } + continue; + case Omagic: + if(ppp->magic == nhgetl(o->data)) + netlog(ppp->f, Logppp, "ppp: possible loop\n"); + continue; + case Octlmap: + ctlmap = nhgetl(o->data); + continue; + case Oquality: + proto = nhgets(o->data); + if(proto != Plqm) + break; + x = nhgetl(o->data+2)*10; + period = (x+Period-1)/Period; + continue; + case Oauth: + proto = nhgets(o->data); + if(proto == Ppap && ppp->chapname[0] && ppp->secret[0]){ + ppp->usepap = 1; + netlog(ppp->f, Logppp, "PPP %s: select PAP\n", ppp->ifc->dev); + continue; + } + if(proto != Pchap || o->data[2] != APmd5){ + if(!nacking){ + nacking = 1; + repb->wp = repm->data; + repm->code = Lconfnak; + } + putao(repb, Oauth, Pchap, APmd5); + } + else + ppp->usechap = 1; + ppp->usepap = 0; + continue; + } + break; + case Pipcp: + switch(o->type){ + case Oipaddr: + v4tov6(ipaddr, o->data); + if(!validv4(ppp->remote)) + continue; + if(!validv4(ipaddr) && !rejecting){ + /* other side requesting an address */ + if(!nacking){ + nacking = 1; + repb->wp = repm->data; + repm->code = Lconfnak; + } + putv4o(repb, Oipaddr, ppp->remote); + } + continue; + case Oipcompress: + proto = nhgets(o->data); + if(nocompress || proto != Pvjctcp || compress_negotiate(ppp->ctcp, o->data+2) < 0) + break; + flags |= Fipcompress; + continue; + } + break; + } + + /* come here if option is not recognized */ + if(!rejecting){ + rejecting = 1; + repb->wp = repm->data; + repm->code = Lconfrej; + } + netlog(ppp->f, Logppp, "ppp %s: bad %ux option %d\n", ppp->ifc->dev, p->proto, o->type); + memmove(repb->wp, o, o->len); + repb->wp += o->len; + } + + /* permanent changes only after we know that we liked the packet */ + if(!rejecting && !nacking){ + switch(p->proto){ + case Plcp: + netlog(ppp->f, Logppp, "Plcp: mtu: %d %d x:%lux/r:%lux %lux\n", mtu, ppp->mtu, ppp->xctlmap, ppp->rctlmap, ctlmap); + ppp->period = period; + ppp->xctlmap = ctlmap; + if(mtu > Maxmtu) + mtu = Maxmtu; + if(mtu < Minmtu) + mtu = Minmtu; + ppp->mtu = mtu; + break; + case Pipcp: + if(validv4(ipaddr) && ppp->remotefrozen == 0) + ipmove(ppp->remote, ipaddr); + break; + } + p->flags = flags; + } + + hnputs(repm->len, BLEN(repb)); + repb = putframe(ppp, p->proto, repb); + freeblist(repb); + + return rejecting || nacking; +} + +/* + * parse configuration rejection, just stop sending anything that they + * don't like (except for ipcp address nak). + */ +static void +rejopts(PPP *ppp, Pstate *p, Block *b, int code) +{ + Lcpmsg *m; + Lcpopt *o; + + /* just give up trying what the other side doesn't like */ + m = (Lcpmsg*)b->rp; + for(b->rp = m->data; b->rp < b->wp; b->rp += o->len){ + o = (Lcpopt*)b->rp; + if(b->rp + o->len > b->wp || o->len == 0){ + netlog(ppp->f, Logppp, "ppp %s: bad roption length %ux\n", ppp->ifc->dev, + o->type); + return; + } + + if(code == Lconfrej){ + if(o->type < 8*sizeof(p->optmask)) + p->optmask &= ~(1<type); + if(o->type == Oipdns) + ppp->usedns &= ~1; + else if(o->type == Oipdns2) + ppp->usedns &= ~2; + netlog(ppp->f, Logppp, "ppp %s: %ux rejecting %d\n", ppp->ifc->dev, p->proto, + o->type); + continue; + } + + switch(p->proto){ + case Plcp: + switch(o->type){ + case Octlmap: + ppp->rctlmap = nhgetl(o->data); + break; + default: + if(o->type < 8*sizeof(p->optmask)) + p->optmask &= ~(1<type); + break; + }; + case Pipcp: + switch(o->type){ + case Oipaddr: + if(!validv4(ppp->local)) + v4tov6(ppp->local, o->data); +// if(o->type < 8*sizeof(p->optmask)) +// p->optmask &= ~(1<type); + break; + case Oipdns: + if(!validv4(ppp->dns1)) + v4tov6(ppp->dns1, o->data); + ppp->usedns &= ~1; + break; + case Oipdns2: + if(!validv4(ppp->dns2)) + v4tov6(ppp->dns2, o->data); + ppp->usedns &= ~2; + break; + default: + if(o->type < 8*sizeof(p->optmask)) + p->optmask &= ~(1<type); + break; + } + break; + } + } +} + + +/* + * put a messages through the lcp or ipcp state machine. They are + * very similar. + */ +static void +rcv(PPP *ppp, Pstate *p, Block *b) +{ + ulong len; + int err; + Lcpmsg *m; + + if(BLEN(b) < 4){ + netlog(ppp->f, Logppp, "ppp %s: short lcp message\n", ppp->ifc->dev); + freeblist(b); + return; + } + m = (Lcpmsg*)b->rp; + len = nhgets(m->len); + if(BLEN(b) < len){ + netlog(ppp->f, Logppp, "ppp %s: short lcp message\n", ppp->ifc->dev); + freeblist(b); + return; + } + + netlog(ppp->f, Logppp, "ppp: %ux rcv %d len %d id %d/%d/%d\n", + p->proto, m->code, len, m->id, p->confid, p->id); + + if(p->proto != Plcp && ppp->lcp->state != Sopened){ + netlog(ppp->f, Logppp, "ppp: non-lcp with lcp not open\n"); + freeb(b); + return; + } + + qlock(ppp); + switch(m->code){ + case Lconfreq: + /* flush the output queue */ + if(p->state == Sopened && p->proto == Plcp) + kchanio(ppp->cchan, "f", 1, OWRITE); + + printopts(ppp, p, b, 0); + err = getopts(ppp, p, b); + if(err < 0) + break; + + if(m->id == p->rcvdconfid) + break; /* don't change state for duplicates */ + p->rcvdconfid = m->id; + + switch(p->state){ + case Sackrcvd: + if(err) + break; + newstate(ppp, p, Sopened); + break; + case Sclosed: + case Sopened: + config(ppp, p, 1); + if(err == 0) + newstate(ppp, p, Sacksent); + else + newstate(ppp, p, Sreqsent); + break; + break; + case Sreqsent: + case Sacksent: + if(err == 0) + newstate(ppp, p, Sacksent); + else + newstate(ppp, p, Sreqsent); + break; + } + break; + case Lconfack: + if(p->confid != m->id){ + /* ignore if it isn't the message we're sending */ + netlog(ppp->f, Logppp, "ppp: dropping confack\n"); + break; + } + p->confid = -1; /* ignore duplicates */ + p->id++; /* avoid sending duplicates */ + + switch(p->state){ + case Sopened: + case Sackrcvd: + config(ppp, p, 1); + newstate(ppp, p, Sreqsent); + break; + case Sreqsent: + newstate(ppp, p, Sackrcvd); + break; + case Sacksent: + newstate(ppp, p, Sopened); + break; + } + break; + case Lconfrej: + case Lconfnak: + if(p->confid != m->id) { + /* ignore if it isn't the message we're sending */ + netlog(ppp->f, Logppp, "ppp: dropping confrej or confnak\n"); + break; + } + p->confid = -1; /* ignore duplicates */ + p->id++; /* avoid sending duplicates */ + + switch(p->state){ + case Sopened: + case Sackrcvd: + config(ppp, p, 1); + newstate(ppp, p, Sreqsent); + break; + case Sreqsent: + case Sacksent: + printopts(ppp, p, b, 0); + rejopts(ppp, p, b, m->code); + config(ppp, p, 1); + break; + } + break; + case Ltermreq: + m->code = Ltermack; + b = putframe(ppp, p->proto, b); + + switch(p->state){ + case Sackrcvd: + case Sacksent: + newstate(ppp, p, Sreqsent); + break; + case Sopened: + newstate(ppp, p, Sclosing); + break; + } + break; + case Ltermack: + if(p->termid != m->id) /* ignore if it isn't the message we're sending */ + break; + + if(p->proto == Plcp) + ppp->ipcp->state = Sclosed; + switch(p->state){ + case Sclosing: + newstate(ppp, p, Sclosed); + break; + case Sackrcvd: + newstate(ppp, p, Sreqsent); + break; + case Sopened: + config(ppp, p, 0); + newstate(ppp, p, Sreqsent); + break; + } + break; + case Lcoderej: + netlog(ppp->f, Logppp, "ppp %s: code reject %d\n", ppp->ifc->dev, m->data[0]); + break; + case Lprotorej: + netlog(ppp->f, Logppp, "ppp %s: proto reject %lux\n", ppp->ifc->dev, nhgets(m->data)); + break; + case Lechoreq: + m->code = Lechoack; + b = putframe(ppp, p->proto, b); + break; + case Lechoack: + case Ldiscard: + /* nothing to do */ + break; + } + + qunlock(ppp); + freeblist(b); +} + +/* + * timer for protocol state machine + */ +static void +ptimer(PPP *ppp, Pstate *p) +{ + if(p->state == Sopened || p->state == Sclosed) + return; + + p->timeout--; + switch(p->state){ + case Sclosing: + sendtermreq(ppp, p); + break; + case Sreqsent: + case Sacksent: + if(p->timeout <= 0){ + if(p->proto && ppp->cchan != nil) + kchanio(ppp->cchan, "f", 1, OWRITE); /* flush output queue */ + newstate(ppp, p, Sclosed); + } else { + config(ppp, p, 0); + } + break; + case Sackrcvd: + if(p->timeout <= 0){ + if(p->proto && ppp->cchan != nil) + kchanio(ppp->cchan, "f", 1, OWRITE); /* flush output queue */ + newstate(ppp, p, Sclosed); + } + else { + config(ppp, p, 0); + newstate(ppp, p, Sreqsent); + } + break; + } +} + +/* + * timer for ppp + */ +static void +ppptimer(void *arg) +{ + PPP *ppp; + + ppp = arg; + ppp->timep = up; + if(waserror()){ + netlog(ppp->f, Logppp, "ppptimer: %I: %s\n", ppp->local, up->env->errstr); + ppp->timep = 0; + pexit("hangup", 1); + } + for(;;){ + tsleep(&up->sleep, return0, nil, Period); + if(ppp->pppup){ + qlock(ppp); + + ptimer(ppp, ppp->lcp); + if(ppp->lcp->state == Sopened) + ptimer(ppp, ppp->ipcp); + + if(ppp->period && --(ppp->timeout) <= 0){ + ppp->timeout = ppp->period; + putlqm(ppp); + } + + qunlock(ppp); + } + } +} + +static void +setdefroute(PPP *ppp, Ipaddr gate) +{ + int fd, n; + char path[128], msg[128]; + + snprint(path, sizeof path, "#I%d/iproute", ppp->f->dev); + fd = kopen(path, ORDWR); + if(fd < 0) + return; + n = snprint(msg, sizeof(msg), "add 0 0 %I", gate); + kwrite(fd, msg, n); + kclose(fd); +} + +static void +ipconnect(PPP *ppp) +{ + int fd, n; + char path[128], msg[128]; + + snprint(path, sizeof path, "#I%d/ipifc/%d/ctl", ppp->f->dev, ppp->ifc->conv->x); + fd = kopen(path, ORDWR); + if(fd < 0) + return; + n = snprint(msg, sizeof(msg), "connect %I 255.255.255.255 %I", ppp->local, ppp->remote); + if (kwrite(fd, msg, n) != n) + print("ppp ipconnect: %s: %r\n", msg); + kclose(fd); +} + +PPP* +pppopen(PPP *ppp, char *dev, + Ipaddr ipaddr, Ipaddr remip, + int mtu, int framing, + char *chapname, char *secret) +{ + int fd, cfd; + char ctl[Maxpath]; + + invalidate(ppp->remote); + invalidate(ppp->local); + invalidate(ppp->dns1); + invalidate(ppp->dns2); + ppp->mtu = Defmtu; + ppp->mru = mtu; + ppp->framing = framing; + + if(remip != nil && validv4(remip)){ + ipmove(ppp->remote, remip); + ppp->remotefrozen = 1; + } + if(ipaddr != nil && validv4(ipaddr)){ + ipmove(ppp->local, ipaddr); + ppp->localfrozen = 1; + } + + /* authentication goo */ + ppp->secret[0] = 0; + if(secret != nil) + strncpy(ppp->secret, secret, sizeof(ppp->secret)); + ppp->chapname[0] = 0; + if(chapname != nil) + strncpy(ppp->chapname, chapname, sizeof(ppp->chapname)); + + if(strchr(dev, '!')) + fd = kdial(dev, nil, nil, nil); + else + fd = kopen(dev, ORDWR); + if(fd < 0){ + netlog(ppp->f, Logppp, "ppp: can't open %s\n", dev); + return nil; + } + ppp->dchan = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1); + kclose(fd); + + /* set up serial line */ +/* XXX this stuff belongs in application, not driver */ + sprint(ctl, "%sctl", dev); + cfd = kopen(ctl, ORDWR); + if(cfd >= 0){ + ppp->cchan = fdtochan(up->env->fgrp, cfd, ORDWR, 0, 1); + kclose(cfd); + kchanio(ppp->cchan, "m1", 2, OWRITE); /* cts/rts flow control/fifo's) on */ + kchanio(ppp->cchan, "q64000", 6, OWRITE);/* increas q size to 64k */ + kchanio(ppp->cchan, "n1", 2, OWRITE); /* nonblocking writes on */ + kchanio(ppp->cchan, "r1", 2, OWRITE); /* rts on */ + kchanio(ppp->cchan, "d1", 2, OWRITE); /* dtr on */ + } + + ppp->pppup = 1; + init(ppp); + return ppp; +} + +static void +hangup(PPP *ppp) +{ + qlock(ppp); + if(waserror()){ + qunlock(ppp); + nexterror(); + } + netlog(ppp->f, Logppp, "PPP Hangup\n"); + errlog(ppp, Ehungup); + if(ppp->pppup && ppp->cchan != nil){ + kchanio(ppp->cchan, "f", 1, OWRITE); /* flush */ + kchanio(ppp->cchan, "h", 1, OWRITE); /* hangup */ + } + cclose(ppp->dchan); + cclose(ppp->cchan); + ppp->dchan = nil; + ppp->cchan = nil; + ppp->pppup = 0; + qunlock(ppp); + poperror(); +} + +/* return next input IP packet */ +Block* +pppread(PPP *ppp) +{ + Block *b; + int proto; + Lcpmsg *m; + + for(;;){ + proto = getframe(ppp, &b); + if(b == nil) + return nil; + netlog(ppp->f, Logppp, "ppp: read proto %d len %d\n", proto, blocklen(b)); + switch(proto){ + case Plcp: + rcv(ppp, ppp->lcp, b); + break; + case Pipcp: + rcv(ppp, ppp->ipcp, b); + break; + case Pip: + if(ppp->ipcp->state == Sopened) + return b; + freeblist(b); + break; + case Plqm: + getlqm(ppp, b); + break; + case Pchap: + getchap(ppp, b); + break; + case Ppap: + getpap(ppp, b); + break; + case Pvjctcp: + case Pvjutcp: + if(ppp->ipcp->state == Sopened){ + b = tcpuncompress(ppp->ctcp, b, proto, ppp->f); + if(b != nil) + return b; + } + freeblist(b); + break; + default: + netlog(ppp->f, Logppp, "unknown proto %ux\n", proto); + if(ppp->lcp->state == Sopened){ + /* reject the protocol */ + b->rp -= 6; + m = (Lcpmsg*)b->rp; + m->code = Lprotorej; + m->id = ++ppp->lcp->id; + hnputs(m->data, proto); + hnputs(m->len, BLEN(b)); + b = putframe(ppp, Plcp, b); + } + freeblist(b); + break; + } + } + return nil; /* compiler confused */ +} + +/* transmit an IP packet */ +int +pppwrite(PPP *ppp, Block *b) +{ + ushort proto; + int r; + + qlock(ppp); + + /* can't send ip packets till we're established */ + if(ppp->ipcp->state != Sopened) + goto ret; + + /* link hung up */ + if(ppp->dchan == nil) + goto ret; + + b = concatblock(b); /* or else compression will barf */ + + proto = Pip; + if(ppp->ipcp->flags & Fipcompress) + proto = compress(ppp->ctcp, b, ppp->f); + b = putframe(ppp, proto, b); + + +ret: + qunlock(ppp); + + r = blocklen(b); + netlog(ppp->f, Logppp, "ppp wrt len %d\n", r); + + freeblist(b); + return r; +} + +/* + * link quality management + */ +static void +getlqm(PPP *ppp, Block *b) +{ + Qualpkt *p; + + p = (Qualpkt*)b->rp; + if(BLEN(b) == sizeof(Qualpkt)){ + ppp->in.reports++; + ppp->pout.reports = nhgetl(p->peeroutreports); + ppp->pout.packets = nhgetl(p->peeroutpackets); + ppp->pout.bytes = nhgetl(p->peeroutbytes); + ppp->pin.reports = nhgetl(p->peerinreports); + ppp->pin.packets = nhgetl(p->peerinpackets); + ppp->pin.discards = nhgetl(p->peerindiscards); + ppp->pin.errors = nhgetl(p->peerinerrors); + ppp->pin.bytes = nhgetl(p->peerinbytes); + + /* save our numbers at time of reception */ + memmove(&ppp->sin, &ppp->in, sizeof(Qualstats)); + + } + freeblist(b); + if(ppp->period == 0) + putlqm(ppp); + +} +static void +putlqm(PPP *ppp) +{ + Qualpkt *p; + Block *b; + + b = allocb(sizeof(Qualpkt)); + b->wp += sizeof(Qualpkt); + p = (Qualpkt*)b->rp; + hnputl(p->magic, 0); + + /* heresay (what he last told us) */ + hnputl(p->lastoutreports, ppp->pout.reports); + hnputl(p->lastoutpackets, ppp->pout.packets); + hnputl(p->lastoutbytes, ppp->pout.bytes); + + /* our numbers at time of last reception */ + hnputl(p->peerinreports, ppp->sin.reports); + hnputl(p->peerinpackets, ppp->sin.packets); + hnputl(p->peerindiscards, ppp->sin.discards); + hnputl(p->peerinerrors, ppp->sin.errors); + hnputl(p->peerinbytes, ppp->sin.bytes); + + /* our numbers now */ + hnputl(p->peeroutreports, ppp->out.reports+1); + hnputl(p->peeroutpackets, ppp->out.packets+1); + hnputl(p->peeroutbytes, ppp->out.bytes+53/*hack*/); + + b = putframe(ppp, Plqm, b); + freeblist(b); + ppp->out.reports++; +} + +/* + * challenge response dialog + */ +static void +getchap(PPP *ppp, Block *b) +{ + Lcpmsg *m; + int len, vlen, n; + char md5buf[512]; + + m = (Lcpmsg*)b->rp; + len = nhgets(m->len); + if(BLEN(b) < len){ + netlog(ppp->f, Logppp, "ppp %s: short chap message\n", ppp->ifc->dev); + freeblist(b); + return; + } + + switch(m->code){ + case Cchallenge: + vlen = m->data[0]; + if(vlen > len - 5){ + netlog(ppp->f, Logppp, "PPP %s: bad challenge len\n", ppp->ifc->dev); + freeblist(b); + break; + } + + netlog(ppp->f, Logppp, "PPP %s: CHAP Challenge\n", ppp->ifc->dev); +netlog(ppp->f, Logppp, "(secret %s chapname %s id %d)\n", ppp->secret, ppp->chapname, m->id); + /* create string to hash */ + md5buf[0] = m->id; + strcpy(md5buf+1, ppp->secret); + n = strlen(ppp->secret) + 1; + memmove(md5buf+n, m->data+1, vlen); + n += vlen; + freeblist(b); + + /* send reply */ + len = 4 + 1 + 16 + strlen(ppp->chapname); + b = alloclcp(2, md5buf[0], len); + m = IPB2LCP(b); + m->data[0] = 16; + md5((uchar*)md5buf, n, m->data+1, 0); + memmove((char*)m->data+17, ppp->chapname, strlen(ppp->chapname)); + hnputs(m->len, len); + b->wp += len-4; + b = putframe(ppp, Pchap, b); + break; + case Cresponse: + netlog(ppp->f, Logppp, "PPP %s: chap response?\n", ppp->ifc->dev); + break; + case Csuccess: + netlog(ppp->f, Logppp, "PPP %s: chap succeeded\n", ppp->ifc->dev); + setphase(ppp, Pnet); + break; + case Cfailure: + netlog(ppp->f, Logppp, "PPP %s: chap failed: %.*s\n", ppp->ifc->dev, len-4, m->data); + errlog(ppp, Eperm); + break; + default: + netlog(ppp->f, Logppp, "PPP %s: chap code %d?\n", ppp->ifc->dev, m->code); + break; + } + freeblist(b); +} + +/* + * password authentication protocol dialog + * -- obsolete but all we know how to use with NT just now + */ +static void +sendpap(PPP *ppp) +{ + Lcpmsg *m; + int clen, slen, len; + Block *b; + uchar *p; + + clen = strlen(ppp->chapname); + slen = strlen(ppp->secret); + len = 4 + 1 + clen + 1 + slen; + ppp->papid = ++ppp->lcp->id; + b = alloclcp(Cpapreq, ppp->papid, len); + m = IPB2LCP(b); + p = m->data; + p[0] = clen; + memmove(p+1, ppp->chapname, clen); + p += clen + 1; + p[0] = slen; + memmove(p+1, ppp->secret, slen); + hnputs(m->len, len); + b->wp += len-4; + b = putframe(ppp, Ppap, b); + netlog(ppp->f, Logppp, "PPP %s: sent pap auth req (%d)\n", ppp->ifc->dev, len); + freeblist(b); +} + +static void +getpap(PPP *ppp, Block *b) +{ + Lcpmsg *m; + int len; + + m = (Lcpmsg*)b->rp; + len = nhgets(m->len); + if(BLEN(b) < len){ + netlog(ppp->f, Logppp, "ppp %s: short pap message\n", ppp->ifc->dev); + freeblist(b); + return; + } + + switch(m->code){ + case Cpapreq: + netlog(ppp->f, Logppp, "PPP %s: pap request?\n", ppp->ifc->dev); + break; + case Cpapack: + netlog(ppp->f, Logppp, "PPP %s: PAP succeeded\n", ppp->ifc->dev); + setphase(ppp, Pnet); + break; + case Cpapnak: + if(m->data[0]) + netlog(ppp->f, Logppp, "PPP %s: PAP failed: %.*s\n", ppp->ifc->dev, len-5, m->data+1); + else + netlog(ppp->f, Logppp, "PPP %s: PAP failed\n", ppp->ifc->dev); + errlog(ppp, Eperm); + break; + default: + netlog(ppp->f, Logppp, "PPP %s: pap code %d?\n", ppp->ifc->dev, m->code); + break; + } + freeblist(b); +} + +static void +printopts(PPP *ppp, Pstate *p, Block *b, int send) +{ + Lcpmsg *m; + Lcpopt *o; + int proto, x, period; + uchar *cp; + char *code, *dir; + + m = (Lcpmsg*)b->rp; + switch(m->code) { + default: code = ""; break; + case Lconfreq: code = "confrequest"; break; + case Lconfack: code = "confack"; break; + case Lconfnak: code = "confnak"; break; + case Lconfrej: code = "confreject"; break; + } + + if(send) + dir = "send"; + else + dir = "recv"; + + netlog(ppp->f, Logppp, "ppp: %s %s: id=%d\n", dir, code, m->id); + + for(cp = m->data; cp < b->wp; cp += o->len){ + o = (Lcpopt*)cp; + if(cp + o->len > b->wp || o->len == 0){ + netlog(ppp->f, Logppp, "\tbad option length %ux\n", o->type); + return; + } + + switch(p->proto){ + case Plcp: + switch(o->type){ + default: + netlog(ppp->f, Logppp, "\tunknown %d len=%d\n", o->type, o->len); + break; + case Omtu: + netlog(ppp->f, Logppp, "\tmtu = %d\n", nhgets(o->data)); + break; + case Octlmap: + netlog(ppp->f, Logppp, "\tctlmap = %ux\n", nhgetl(o->data)); + break; + case Oauth: + netlog(ppp->f, Logppp, "\tauth = ", nhgetl(o->data)); + proto = nhgets(o->data); + switch(proto) { + default: + netlog(ppp->f, Logppp, "unknown auth proto %d\n", proto); + break; + case Ppap: + netlog(ppp->f, Logppp, "password\n"); + break; + case Pchap: + netlog(ppp->f, Logppp, "chap %ux\n", o->data[2]); + break; + } + break; + case Oquality: + proto = nhgets(o->data); + switch(proto) { + default: + netlog(ppp->f, Logppp, "\tunknown quality proto %d\n", proto); + break; + case Plqm: + x = nhgetl(o->data+2)*10; + period = (x+Period-1)/Period; + netlog(ppp->f, Logppp, "\tlqm period = %d\n", period); + break; + } + case Omagic: + netlog(ppp->f, Logppp, "\tmagic = %ux\n", nhgetl(o->data)); + break; + case Opc: + netlog(ppp->f, Logppp, "\tprotocol compress\n"); + break; + case Oac: + netlog(ppp->f, Logppp, "\taddr compress\n"); + break; + } + break; + case Pccp: + switch(o->type){ + default: + netlog(ppp->f, Logppp, "\tunknown %d len=%d\n", o->type, o->len); + break; + case Ocoui: + netlog(ppp->f, Logppp, "\tOUI\n"); + break; + case Ocstac: + netlog(ppp->f, Logppp, "\tstac LZS\n"); + break; + case Ocmppc: + netlog(ppp->f, Logppp, "\tMicrosoft PPC len=%d %ux\n", o->len, nhgetl(o->data)); + break; + } + break; + case Pecp: + switch(o->type){ + default: + netlog(ppp->f, Logppp, "\tunknown %d len=%d\n", o->type, o->len); + break; + case Oeoui: + netlog(ppp->f, Logppp, "\tOUI\n"); + break; + case Oedese: + netlog(ppp->f, Logppp, "\tDES\n"); + break; + } + break; + case Pipcp: + switch(o->type){ + default: + netlog(ppp->f, Logppp, "\tunknown %d len=%d\n", o->type, o->len); + break; + case Oipaddrs: + netlog(ppp->f, Logppp, "\tip addrs - deprecated\n"); + break; + case Oipcompress: + netlog(ppp->f, Logppp, "\tip compress\n"); + break; + case Oipaddr: + netlog(ppp->f, Logppp, "\tip addr %V\n", o->data); + break; + case Oipdns: + netlog(ppp->f, Logppp, "\tdns addr %V\n", o->data); + break; + case Oipwins: + netlog(ppp->f, Logppp, "\twins addr %V\n", o->data); + break; + case Oipdns2: + netlog(ppp->f, Logppp, "\tdns2 addr %V\n", o->data); + break; + case Oipwins2: + netlog(ppp->f, Logppp, "\twins2 addr %V\n", o->data); + break; + } + break; + } + } +} + +static void +sendtermreq(PPP *ppp, Pstate *p) +{ + Block *b; + Lcpmsg *m; + + p->termid = ++(p->id); + b = alloclcp(Ltermreq, p->termid, 4); + m = IPB2LCP(b); + hnputs(m->len, 4); + putframe(ppp, p->proto, b); + freeb(b); + newstate(ppp, p, Sclosing); +} + +static void +sendechoreq(PPP *ppp, Pstate *p) +{ + Block *b; + Lcpmsg *m; + + p->termid = ++(p->id); + b = alloclcp(Lechoreq, p->id, 4); + m = IPB2LCP(b); + hnputs(m->len, 4); + putframe(ppp, p->proto, b); + freeb(b); +} + +/* + * return non-zero if this is a valid v4 address + */ +static int +validv4(Ipaddr addr) +{ + return memcmp(addr, v4prefix, IPv4off) == 0; +} + +static void +invalidate(Ipaddr addr) +{ + ipmove(addr, IPnoaddr); +} diff --git a/os/ip/ppp.h b/os/ip/ppp.h new file mode 100644 index 00000000..4f1e00de --- /dev/null +++ b/os/ip/ppp.h @@ -0,0 +1,258 @@ +typedef struct PPP PPP; +typedef struct Pstate Pstate; +typedef struct Lcpmsg Lcpmsg; +typedef struct Lcpopt Lcpopt; +typedef struct Qualpkt Qualpkt; +typedef struct Qualstats Qualstats; +typedef struct Tcpc Tcpc; + +typedef uchar Ipaddr[IPaddrlen]; + +enum +{ + HDLC_frame= 0x7e, + HDLC_esc= 0x7d, + + /* PPP frame fields */ + PPP_addr= 0xff, + PPP_ctl= 0x3, + PPP_initfcs= 0xffff, + PPP_goodfcs= 0xf0b8, + + /* PPP phases */ + Pdead= 0, + Plink, /* doing LCP */ + Pauth, /* doing chap */ + Pnet, /* doing IPCP, CCP */ + Pterm, /* closing down */ + + /* PPP protocol types */ + Pip= 0x21, /* internet */ + Pvjctcp= 0x2d, /* compressing van jacobson tcp */ + Pvjutcp= 0x2f, /* uncompressing van jacobson tcp */ + Pcdata= 0xfd, /* compressed datagram */ + Pipcp= 0x8021, /* ip control */ + Pecp= 0x8053, /* encryption control */ + Pccp= 0x80fd, /* compressed datagram control */ + Plcp= 0xc021, /* link control */ + Ppap= 0xc023, /* password auth. protocol */ + Plqm= 0xc025, /* link quality monitoring */ + Pchap= 0xc223, /* challenge/response */ + + /* LCP codes */ + Lconfreq= 1, + Lconfack= 2, + Lconfnak= 3, + Lconfrej= 4, + Ltermreq= 5, + Ltermack= 6, + Lcoderej= 7, + Lprotorej= 8, + Lechoreq= 9, + Lechoack= 10, + Ldiscard= 11, + + /* Lcp configure options */ + Omtu= 1, + Octlmap= 2, + Oauth= 3, + Oquality= 4, + Omagic= 5, + Opc= 7, + Oac= 8, + Obad= 12, /* for testing */ + + /* authentication protocols */ + APmd5= 5, + + /* lcp flags */ + Fmtu= 1<ifc = ifc; + ppp->f = ifc->conv->p->f; + ifc->arg = ppp; + if(waserror()){ + pppunbind(ifc); + nexterror(); + } + if(pppopen(ppp, argv[2], ipaddr, remip, mtu, framing, chapname, secret) == nil) + error("ppp open failed"); + poperror(); + kproc("pppreader", pppreader, ifc, KPDUPPG|KPDUPFDG); +} + +static void +pppreader(void *a) +{ + Ipifc *ifc; + Block *bp; + PPP *ppp; + + ifc = a; + ppp = ifc->arg; + ppp->readp = up; /* hide identity under a rock for unbind */ + setpri(PriHi); + + if(waserror()){ + netlog(ppp->f, Logppp, "pppreader: %I: %s\n", ppp->local, up->env->errstr); + ppp->readp = 0; + deadremote(ifc); + pexit("hangup", 1); + } + + for(;;){ + bp = pppread(ppp); + if(bp == nil) + error("hungup"); + if(!canrlock(ifc)){ + freeb(bp); + continue; + } + if(waserror()){ + runlock(ifc); + nexterror(); + } + ifc->in++; + if(ifc->lifc == nil) + freeb(bp); + else + ipiput(ppp->f, ifc, bp); + runlock(ifc); + poperror(); + } +} + +/* + * called with ifc wlock'd + */ +static void +pppunbind(Ipifc *ifc) +{ + PPP *ppp = ifc->arg; + + if(ppp == nil) + return; + if(ppp->readp) + postnote(ppp->readp, 1, "unbind", 0); + if(ppp->timep) + postnote(ppp->timep, 1, "unbind", 0); + + /* wait for kprocs to die */ + while(ppp->readp != 0 || ppp->timep != 0) + tsleep(&up->sleep, return0, 0, 300); + + pppclose(ppp); + qclose(ifc->conv->eq); + ifc->arg = nil; +} + +/* + * called by ipoput with a single packet to write with ifc rlock'd + */ +static void +pppbwrite(Ipifc *ifc, Block *bp, int, uchar*) +{ + PPP *ppp = ifc->arg; + + pppwrite(ppp, bp); + ifc->out++; +} + +/* + * If the other end hangs up, we have to unbind the interface. An extra + * unbind (in the case where we are hanging up) won't do any harm. + */ +static void +deadremote(Ipifc *ifc) +{ + int fd; + char path[128]; + PPP *ppp; + + ppp = ifc->arg; + snprint(path, sizeof path, "#I%d/ipifc/%d/ctl", ppp->f->dev, ifc->conv->x); + fd = kopen(path, ORDWR); + if(fd < 0) + return; + kwrite(fd, "unbind", sizeof("unbind")-1); + kclose(fd); +} + +void +pppmediumlink(void) +{ + addipmedium(&pppmedium); +} diff --git a/os/ip/ptclbsum.c b/os/ip/ptclbsum.c new file mode 100644 index 00000000..4b895ecf --- /dev/null +++ b/os/ip/ptclbsum.c @@ -0,0 +1,72 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "ip.h" + +static short endian = 1; +static uchar* aendian = (uchar*)&endian; +#define LITTLE *aendian + +ushort +ptclbsum(uchar *addr, int len) +{ + ulong losum, hisum, mdsum, x; + ulong t1, t2; + + losum = 0; + hisum = 0; + mdsum = 0; + + x = 0; + if((ulong)addr & 1) { + if(len) { + hisum += addr[0]; + len--; + addr++; + } + x = 1; + } + while(len >= 16) { + t1 = *(ushort*)(addr+0); + t2 = *(ushort*)(addr+2); mdsum += t1; + t1 = *(ushort*)(addr+4); mdsum += t2; + t2 = *(ushort*)(addr+6); mdsum += t1; + t1 = *(ushort*)(addr+8); mdsum += t2; + t2 = *(ushort*)(addr+10); mdsum += t1; + t1 = *(ushort*)(addr+12); mdsum += t2; + t2 = *(ushort*)(addr+14); mdsum += t1; + mdsum += t2; + len -= 16; + addr += 16; + } + while(len >= 2) { + mdsum += *(ushort*)addr; + len -= 2; + addr += 2; + } + if(x) { + if(len) + losum += addr[0]; + if(LITTLE) + losum += mdsum; + else + hisum += mdsum; + } else { + if(len) + hisum += addr[0]; + if(LITTLE) + hisum += mdsum; + else + losum += mdsum; + } + + losum += hisum >> 8; + losum += (hisum & 0xff) << 8; + while(hisum = losum>>16) + losum = hisum + (losum & 0xffff); + + return losum & 0xffff; +} diff --git a/os/ip/rudp.c b/os/ip/rudp.c new file mode 100644 index 00000000..ce431333 --- /dev/null +++ b/os/ip/rudp.c @@ -0,0 +1,1085 @@ +/* + * This protocol is compatible with UDP's packet format. + * It could be done over UDP if need be. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" + +#define DEBUG 0 +#define DPRINT if(DEBUG)print + +#define SEQDIFF(a,b) ( (a)>=(b)?\ + (a)-(b):\ + 0xffffffffUL-((b)-(a)) ) +#define INSEQ(a,start,end) ( (start)<=(end)?\ + ((a)>(start)&&(a)<=(end)):\ + ((a)>(start)||(a)<=(end)) ) +#define UNACKED(r) SEQDIFF(r->sndseq, r->ackrcvd) +#define NEXTSEQ(a) ( (a)+1 == 0 ? 1 : (a)+1 ) + +enum +{ + UDP_HDRSIZE = 20, /* pseudo header + udp header */ + UDP_PHDRSIZE = 12, /* pseudo header */ + UDP_RHDRSIZE = 36, /* pseudo header + udp header + rudp header */ + UDP_IPHDR = 8, /* ip header */ + IP_UDPPROTO = 254, + UDP_USEAD7 = 52, + UDP_USEAD6 = 36, + UDP_USEAD4 = 12, + + Rudprxms = 200, + Rudptickms = 50, + Rudpmaxxmit = 10, + Maxunacked = 100, + +}; + +#define Hangupgen 0xffffffff /* used only in hangup messages */ + +typedef struct Udphdr Udphdr; +struct Udphdr +{ + /* ip header */ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + + /* pseudo header starts here */ + uchar Unused; + uchar udpproto; /* Protocol */ + uchar udpplen[2]; /* Header plus data length */ + uchar udpsrc[4]; /* Ip source */ + uchar udpdst[4]; /* Ip destination */ + + /* udp header */ + uchar udpsport[2]; /* Source port */ + uchar udpdport[2]; /* Destination port */ + uchar udplen[2]; /* data length */ + uchar udpcksum[2]; /* Checksum */ +}; + +typedef struct Rudphdr Rudphdr; +struct Rudphdr +{ + /* ip header */ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + + /* pseudo header starts here */ + uchar Unused; + uchar udpproto; /* Protocol */ + uchar udpplen[2]; /* Header plus data length */ + uchar udpsrc[4]; /* Ip source */ + uchar udpdst[4]; /* Ip destination */ + + /* udp header */ + uchar udpsport[2]; /* Source port */ + uchar udpdport[2]; /* Destination port */ + uchar udplen[2]; /* data length (includes rudp header) */ + uchar udpcksum[2]; /* Checksum */ + + /* rudp header */ + uchar relseq[4]; /* id of this packet (or 0) */ + uchar relsgen[4]; /* generation/time stamp */ + uchar relack[4]; /* packet being acked (or 0) */ + uchar relagen[4]; /* generation/time stamp */ +}; + + +/* + * one state structure per destination + */ +typedef struct Reliable Reliable; +struct Reliable +{ + Ref; + + Reliable *next; + + uchar addr[IPaddrlen]; /* always V6 when put here */ + ushort port; + + Block *unacked; /* unacked msg list */ + Block *unackedtail; /* and its tail */ + + int timeout; /* time since first unacked msg sent */ + int xmits; /* number of times first unacked msg sent */ + + ulong sndseq; /* next packet to be sent */ + ulong sndgen; /* and its generation */ + + ulong rcvseq; /* last packet received */ + ulong rcvgen; /* and its generation */ + + ulong acksent; /* last ack sent */ + ulong ackrcvd; /* last msg for which ack was rcvd */ + + /* flow control */ + QLock lock; + Rendez vous; + int blocked; +}; + + + +/* MIB II counters */ +typedef struct Rudpstats Rudpstats; +struct Rudpstats +{ + ulong rudpInDatagrams; + ulong rudpNoPorts; + ulong rudpInErrors; + ulong rudpOutDatagrams; +}; + +typedef struct Rudppriv Rudppriv; +struct Rudppriv +{ + Ipht ht; + + /* MIB counters */ + Rudpstats ustats; + + /* non-MIB stats */ + ulong csumerr; /* checksum errors */ + ulong lenerr; /* short packet */ + ulong rxmits; /* # of retransmissions */ + ulong orders; /* # of out of order pkts */ + + /* keeping track of the ack kproc */ + int ackprocstarted; + QLock apl; +}; + + +static ulong generation = 0; +static Rendez rend; + +/* + * protocol specific part of Conv + */ +typedef struct Rudpcb Rudpcb; +struct Rudpcb +{ + QLock; + uchar headers; + uchar randdrop; + Reliable *r; +}; + +/* + * local functions + */ +void relsendack(Conv*, Reliable*, int); +int reliput(Conv*, Block*, uchar*, ushort); +Reliable *relstate(Rudpcb*, uchar*, ushort, char*); +void relput(Reliable*); +void relforget(Conv *, uchar*, int, int); +void relackproc(void *); +void relackq(Reliable *, Block*); +void relhangup(Conv *, Reliable*); +void relrexmit(Conv *, Reliable*); +void relput(Reliable*); +void rudpkick(void *x); + +static void +rudpstartackproc(Proto *rudp) +{ + Rudppriv *rpriv; + char kpname[KNAMELEN]; + + rpriv = rudp->priv; + if(rpriv->ackprocstarted == 0){ + qlock(&rpriv->apl); + if(rpriv->ackprocstarted == 0){ + sprint(kpname, "#I%drudpack", rudp->f->dev); + kproc(kpname, relackproc, rudp); + rpriv->ackprocstarted = 1; + } + qunlock(&rpriv->apl); + } +} + +static char* +rudpconnect(Conv *c, char **argv, int argc) +{ + char *e; + Rudppriv *upriv; + + upriv = c->p->priv; + rudpstartackproc(c->p); + e = Fsstdconnect(c, argv, argc); + Fsconnected(c, e); + iphtadd(&upriv->ht, c); + + return e; +} + + +static int +rudpstate(Conv *c, char *state, int n) +{ + Rudpcb *ucb; + Reliable *r; + int m; + + m = snprint(state, n, "%s", c->inuse?"Open":"Closed"); + ucb = (Rudpcb*)c->ptcl; + qlock(ucb); + for(r = ucb->r; r; r = r->next) + m += snprint(state+m, n-m, " %I/%ld", r->addr, UNACKED(r)); + qunlock(ucb); + return m; +} + +static char* +rudpannounce(Conv *c, char** argv, int argc) +{ + char *e; + Rudppriv *upriv; + + upriv = c->p->priv; + rudpstartackproc(c->p); + e = Fsstdannounce(c, argv, argc); + if(e != nil) + return e; + Fsconnected(c, nil); + iphtadd(&upriv->ht, c); + + return nil; +} + +static void +rudpcreate(Conv *c) +{ + c->rq = qopen(64*1024, Qmsg, 0, 0); + c->wq = qopen(64*1024, Qkick, rudpkick, c); +} + +static void +rudpclose(Conv *c) +{ + Rudpcb *ucb; + Reliable *r, *nr; + Rudppriv *upriv; + + upriv = c->p->priv; + iphtrem(&upriv->ht, c); + + /* force out any delayed acks */ + ucb = (Rudpcb*)c->ptcl; + qlock(ucb); + for(r = ucb->r; r; r = r->next){ + if(r->acksent != r->rcvseq) + relsendack(c, r, 0); + } + qunlock(ucb); + + qclose(c->rq); + qclose(c->wq); + qclose(c->eq); + ipmove(c->laddr, IPnoaddr); + ipmove(c->raddr, IPnoaddr); + c->lport = 0; + c->rport = 0; + + ucb->headers = 0; + ucb->randdrop = 0; + qlock(ucb); + for(r = ucb->r; r; r = nr){ + if(r->acksent != r->rcvseq) + relsendack(c, r, 0); + nr = r->next; + relhangup(c, r); + relput(r); + } + ucb->r = 0; + + qunlock(ucb); +} + +/* + * randomly don't send packets + */ +static void +doipoput(Conv *c, Fs *f, Block *bp, int x, int ttl, int tos) +{ + Rudpcb *ucb; + + ucb = (Rudpcb*)c->ptcl; + if(ucb->randdrop && nrand(100) < ucb->randdrop) + freeblist(bp); + else + ipoput4(f, bp, x, ttl, tos, nil); +} + +int +flow(void *v) +{ + Reliable *r = v; + + return UNACKED(r) <= Maxunacked; +} + +void +rudpkick(void *x) +{ + Conv *c = x; + Udphdr *uh; + ushort rport; + uchar laddr[IPaddrlen], raddr[IPaddrlen]; + Block *bp; + Rudpcb *ucb; + Rudphdr *rh; + Reliable *r; + int dlen, ptcllen; + Rudppriv *upriv; + Fs *f; + + upriv = c->p->priv; + f = c->p->f; + + netlog(c->p->f, Logrudp, "rudp: kick\n"); + bp = qget(c->wq); + if(bp == nil) + return; + + ucb = (Rudpcb*)c->ptcl; + switch(ucb->headers) { + case 7: + /* get user specified addresses */ + bp = pullupblock(bp, UDP_USEAD7); + if(bp == nil) + return; + ipmove(raddr, bp->rp); + bp->rp += IPaddrlen; + ipmove(laddr, bp->rp); + bp->rp += IPaddrlen; + /* pick interface closest to dest */ + if(ipforme(f, laddr) != Runi) + findlocalip(f, laddr, raddr); + bp->rp += IPaddrlen; /* Ignore ifc address */ + rport = nhgets(bp->rp); + bp->rp += 2+2; /* Ignore local port */ + break; + case 6: + /* get user specified addresses */ + bp = pullupblock(bp, UDP_USEAD6); + if(bp == nil) + return; + ipmove(raddr, bp->rp); + bp->rp += IPaddrlen; + ipmove(laddr, bp->rp); + bp->rp += IPaddrlen; + /* pick interface closest to dest */ + if(ipforme(f, laddr) != Runi) + findlocalip(f, laddr, raddr); + rport = nhgets(bp->rp); + + bp->rp += 4; /* Igonore local port */ + break; + default: + ipmove(raddr, c->raddr); + ipmove(laddr, c->laddr); + rport = c->rport; + + break; + } + + dlen = blocklen(bp); + + /* Make space to fit rudp & ip header */ + bp = padblock(bp, UDP_IPHDR+UDP_RHDRSIZE); + if(bp == nil) + return; + + uh = (Udphdr *)(bp->rp); + uh->vihl = IP_VER4; + + rh = (Rudphdr*)uh; + + ptcllen = dlen + (UDP_RHDRSIZE-UDP_PHDRSIZE); + uh->Unused = 0; + uh->udpproto = IP_UDPPROTO; + uh->frag[0] = 0; + uh->frag[1] = 0; + hnputs(uh->udpplen, ptcllen); + switch(ucb->headers){ + case 6: + case 7: + v6tov4(uh->udpdst, raddr); + hnputs(uh->udpdport, rport); + v6tov4(uh->udpsrc, laddr); + break; + default: + v6tov4(uh->udpdst, c->raddr); + hnputs(uh->udpdport, c->rport); + if(ipcmp(c->laddr, IPnoaddr) == 0) + findlocalip(f, c->laddr, c->raddr); + v6tov4(uh->udpsrc, c->laddr); + break; + } + hnputs(uh->udpsport, c->lport); + hnputs(uh->udplen, ptcllen); + uh->udpcksum[0] = 0; + uh->udpcksum[1] = 0; + + qlock(ucb); + r = relstate(ucb, raddr, rport, "kick"); + r->sndseq = NEXTSEQ(r->sndseq); + hnputl(rh->relseq, r->sndseq); + hnputl(rh->relsgen, r->sndgen); + + hnputl(rh->relack, r->rcvseq); /* ACK last rcvd packet */ + hnputl(rh->relagen, r->rcvgen); + + if(r->rcvseq != r->acksent) + r->acksent = r->rcvseq; + + hnputs(uh->udpcksum, ptclcsum(bp, UDP_IPHDR, dlen+UDP_RHDRSIZE)); + + relackq(r, bp); + qunlock(ucb); + + upriv->ustats.rudpOutDatagrams++; + + DPRINT("sent: %lud/%lud, %lud/%lud\n", + r->sndseq, r->sndgen, r->rcvseq, r->rcvgen); + + doipoput(c, f, bp, 0, c->ttl, c->tos); + + if(waserror()) { + relput(r); + qunlock(&r->lock); + nexterror(); + } + + /* flow control of sorts */ + qlock(&r->lock); + if(UNACKED(r) > Maxunacked){ + r->blocked = 1; + sleep(&r->vous, flow, r); + r->blocked = 0; + } + + qunlock(&r->lock); + relput(r); + poperror(); +} + +void +rudpiput(Proto *rudp, Ipifc *ifc, Block *bp) +{ + int len, olen, ottl; + Udphdr *uh; + Conv *c; + Rudpcb *ucb; + uchar raddr[IPaddrlen], laddr[IPaddrlen]; + ushort rport, lport; + Rudppriv *upriv; + Fs *f; + uchar *p; + + upriv = rudp->priv; + f = rudp->f; + + upriv->ustats.rudpInDatagrams++; + + uh = (Udphdr*)(bp->rp); + + /* Put back pseudo header for checksum + * (remember old values for icmpnoconv()) + */ + ottl = uh->Unused; + uh->Unused = 0; + len = nhgets(uh->udplen); + olen = nhgets(uh->udpplen); + hnputs(uh->udpplen, len); + + v4tov6(raddr, uh->udpsrc); + v4tov6(laddr, uh->udpdst); + lport = nhgets(uh->udpdport); + rport = nhgets(uh->udpsport); + + if(nhgets(uh->udpcksum)) { + if(ptclcsum(bp, UDP_IPHDR, len+UDP_PHDRSIZE)) { + upriv->ustats.rudpInErrors++; + upriv->csumerr++; + netlog(f, Logrudp, "rudp: checksum error %I\n", raddr); + DPRINT("rudp: checksum error %I\n", raddr); + freeblist(bp); + return; + } + } + + qlock(rudp); + + c = iphtlook(&upriv->ht, raddr, rport, laddr, lport); + if(c == nil){ + /* no converstation found */ + upriv->ustats.rudpNoPorts++; + qunlock(rudp); + netlog(f, Logudp, "udp: no conv %I!%d -> %I!%d\n", raddr, rport, + laddr, lport); + uh->Unused = ottl; + hnputs(uh->udpplen, olen); + icmpnoconv(f, bp); + freeblist(bp); + return; + } + ucb = (Rudpcb*)c->ptcl; + qlock(ucb); + qunlock(rudp); + + if(reliput(c, bp, raddr, rport) < 0){ + qunlock(ucb); + freeb(bp); + return; + } + + /* + * Trim the packet down to data size + */ + + len -= (UDP_RHDRSIZE-UDP_PHDRSIZE); + bp = trimblock(bp, UDP_IPHDR+UDP_RHDRSIZE, len); + if(bp == nil) { + netlog(f, Logrudp, "rudp: len err %I.%d -> %I.%d\n", + raddr, rport, laddr, lport); + DPRINT("rudp: len err %I.%d -> %I.%d\n", + raddr, rport, laddr, lport); + upriv->lenerr++; + return; + } + + netlog(f, Logrudpmsg, "rudp: %I.%d -> %I.%d l %d\n", + raddr, rport, laddr, lport, len); + + switch(ucb->headers){ + case 7: + /* pass the src address */ + bp = padblock(bp, UDP_USEAD7); + p = bp->rp; + ipmove(p, raddr); p += IPaddrlen; + ipmove(p, laddr); p += IPaddrlen; + ipmove(p, ifc->lifc->local); p += IPaddrlen; + hnputs(p, rport); p += 2; + hnputs(p, lport); + break; + case 6: + /* pass the src address */ + bp = padblock(bp, UDP_USEAD6); + p = bp->rp; + ipmove(p, raddr); p += IPaddrlen; + ipmove(p, ipforme(f, laddr)==Runi ? laddr : ifc->lifc->local); p += IPaddrlen; + hnputs(p, rport); p += 2; + hnputs(p, lport); + break; + default: + /* connection oriented rudp */ + if(ipcmp(c->raddr, IPnoaddr) == 0){ + /* save the src address in the conversation */ + ipmove(c->raddr, raddr); + c->rport = rport; + + /* reply with the same ip address (if not broadcast) */ + if(ipforme(f, laddr) == Runi) + ipmove(c->laddr, laddr); + else + v4tov6(c->laddr, ifc->lifc->local); + } + break; + } + if(bp->next) + bp = concatblock(bp); + + if(qfull(c->rq)) { + netlog(f, Logrudp, "rudp: qfull %I.%d -> %I.%d\n", raddr, rport, + laddr, lport); + freeblist(bp); + } + else + qpass(c->rq, bp); + + qunlock(ucb); +} + +static char *rudpunknown = "unknown rudp ctl request"; + +char* +rudpctl(Conv *c, char **f, int n) +{ + Rudpcb *ucb; + uchar ip[IPaddrlen]; + int x; + + ucb = (Rudpcb*)c->ptcl; + if(n < 1) + return rudpunknown; + + if(strcmp(f[0], "headers++4") == 0){ + ucb->headers = 7; + return nil; + } else if(strcmp(f[0], "headers") == 0){ + ucb->headers = 6; + return nil; + } else if(strcmp(f[0], "hangup") == 0){ + if(n < 3) + return "bad syntax"; + parseip(ip, f[1]); + x = atoi(f[2]); + qlock(ucb); + relforget(c, ip, x, 1); + qunlock(ucb); + return nil; + } else if(strcmp(f[0], "randdrop") == 0){ + x = 10; /* default is 10% */ + if(n > 1) + x = atoi(f[1]); + if(x > 100 || x < 0) + return "illegal rudp drop rate"; + ucb->randdrop = x; + return nil; + } + return rudpunknown; +} + +void +rudpadvise(Proto *rudp, Block *bp, char *msg) +{ + Udphdr *h; + uchar source[IPaddrlen], dest[IPaddrlen]; + ushort psource, pdest; + Conv *s, **p; + + h = (Udphdr*)(bp->rp); + + v4tov6(dest, h->udpdst); + v4tov6(source, h->udpsrc); + psource = nhgets(h->udpsport); + pdest = nhgets(h->udpdport); + + /* Look for a connection */ + for(p = rudp->conv; *p; p++) { + s = *p; + if(s->rport == pdest) + if(s->lport == psource) + if(ipcmp(s->raddr, dest) == 0) + if(ipcmp(s->laddr, source) == 0){ + qhangup(s->rq, msg); + qhangup(s->wq, msg); + break; + } + } + freeblist(bp); +} + +int +rudpstats(Proto *rudp, char *buf, int len) +{ + Rudppriv *upriv; + + upriv = rudp->priv; + return snprint(buf, len, "%lud %lud %lud %lud %lud %lud\n", + upriv->ustats.rudpInDatagrams, + upriv->ustats.rudpNoPorts, + upriv->ustats.rudpInErrors, + upriv->ustats.rudpOutDatagrams, + upriv->rxmits, + upriv->orders); +} + +void +rudpinit(Fs *fs) +{ + + Proto *rudp; + + rudp = smalloc(sizeof(Proto)); + rudp->priv = smalloc(sizeof(Rudppriv)); + rudp->name = "rudp"; + rudp->connect = rudpconnect; + rudp->announce = rudpannounce; + rudp->ctl = rudpctl; + rudp->state = rudpstate; + rudp->create = rudpcreate; + rudp->close = rudpclose; + rudp->rcv = rudpiput; + rudp->advise = rudpadvise; + rudp->stats = rudpstats; + rudp->ipproto = IP_UDPPROTO; + rudp->nc = 16; + rudp->ptclsize = sizeof(Rudpcb); + + Fsproto(fs, rudp); +} + +/*********************************************/ +/* Here starts the reliable helper functions */ +/*********************************************/ +/* + * Enqueue a copy of an unacked block for possible retransmissions + */ +void +relackq(Reliable *r, Block *bp) +{ + Block *np; + + np = copyblock(bp, blocklen(bp)); + if(r->unacked) + r->unackedtail->list = np; + else { + /* restart timer */ + r->timeout = 0; + r->xmits = 1; + r->unacked = np; + } + r->unackedtail = np; + np->list = nil; +} + +/* + * retransmit unacked blocks + */ +void +relackproc(void *a) +{ + Rudpcb *ucb; + Proto *rudp; + Reliable *r; + Conv **s, *c; + + rudp = (Proto *)a; + +loop: + tsleep(&up->sleep, return0, 0, Rudptickms); + + for(s = rudp->conv; *s; s++) { + c = *s; + ucb = (Rudpcb*)c->ptcl; + qlock(ucb); + + for(r = ucb->r; r; r = r->next) { + if(r->unacked != nil){ + r->timeout += Rudptickms; + if(r->timeout > Rudprxms*r->xmits) + relrexmit(c, r); + } + if(r->acksent != r->rcvseq) + relsendack(c, r, 0); + } + qunlock(ucb); + } + goto loop; +} + +/* + * get the state record for a conversation + */ +Reliable* +relstate(Rudpcb *ucb, uchar *addr, ushort port, char *from) +{ + Reliable *r, **l; + + l = &ucb->r; + for(r = *l; r; r = *l){ + if(memcmp(addr, r->addr, IPaddrlen) == 0 && + port == r->port) + break; + l = &r->next; + } + + /* no state for this addr/port, create some */ + if(r == nil){ + while(generation == 0) + generation = rand(); + + DPRINT("from %s new state %lud for %I!%ud\n", + from, generation, addr, port); + + r = smalloc(sizeof(Reliable)); + memmove(r->addr, addr, IPaddrlen); + r->port = port; + r->unacked = 0; + if(generation == Hangupgen) + generation++; + r->sndgen = generation++; + r->sndseq = 0; + r->ackrcvd = 0; + r->rcvgen = 0; + r->rcvseq = 0; + r->acksent = 0; + r->xmits = 0; + r->timeout = 0; + r->ref = 0; + incref(r); /* one reference for being in the list */ + + *l = r; + } + + incref(r); + return r; +} + +void +relput(Reliable *r) +{ + if(decref(r) == 0) + free(r); +} + +/* + * forget a Reliable state + */ +void +relforget(Conv *c, uchar *ip, int port, int originator) +{ + Rudpcb *ucb; + Reliable *r, **l; + + ucb = (Rudpcb*)c->ptcl; + + l = &ucb->r; + for(r = *l; r; r = *l){ + if(ipcmp(ip, r->addr) == 0 && port == r->port){ + *l = r->next; + if(originator) + relsendack(c, r, 1); + relhangup(c, r); + relput(r); /* remove from the list */ + break; + } + l = &r->next; + } +} + +/* + * process a rcvd reliable packet. return -1 if not to be passed to user process, + * 0 therwise. + * + * called with ucb locked. + */ +int +reliput(Conv *c, Block *bp, uchar *addr, ushort port) +{ + Block *nbp; + Rudpcb *ucb; + Rudppriv *upriv; + Udphdr *uh; + Reliable *r; + Rudphdr *rh; + ulong seq, ack, sgen, agen, ackreal; + int rv = -1; + + /* get fields */ + uh = (Udphdr*)(bp->rp); + rh = (Rudphdr*)uh; + seq = nhgetl(rh->relseq); + sgen = nhgetl(rh->relsgen); + ack = nhgetl(rh->relack); + agen = nhgetl(rh->relagen); + + upriv = c->p->priv; + ucb = (Rudpcb*)c->ptcl; + r = relstate(ucb, addr, port, "input"); + + DPRINT("rcvd %lud/%lud, %lud/%lud, r->sndgen = %lud\n", + seq, sgen, ack, agen, r->sndgen); + + /* if acking an incorrect generation, ignore */ + if(ack && agen != r->sndgen) + goto out; + + /* Look for a hangup */ + if(sgen == Hangupgen) { + if(agen == r->sndgen) + relforget(c, addr, port, 0); + goto out; + } + + /* make sure we're not talking to a new remote side */ + if(r->rcvgen != sgen){ + if(seq != 0 && seq != 1) + goto out; + + /* new connection */ + if(r->rcvgen != 0){ + DPRINT("new con r->rcvgen = %lud, sgen = %lud\n", r->rcvgen, sgen); + relhangup(c, r); + } + r->rcvgen = sgen; + } + + /* dequeue acked packets */ + if(ack && agen == r->sndgen){ + ackreal = 0; + while(r->unacked != nil && INSEQ(ack, r->ackrcvd, r->sndseq)){ + nbp = r->unacked; + r->unacked = nbp->list; + DPRINT("%lud/%lud acked, r->sndgen = %lud\n", + ack, agen, r->sndgen); + freeb(nbp); + r->ackrcvd = NEXTSEQ(r->ackrcvd); + ackreal = 1; + } + + /* flow control */ + if(UNACKED(r) < Maxunacked/8 && r->blocked) + wakeup(&r->vous); + + /* + * retransmit next packet if the acked packet + * was transmitted more than once + */ + if(ackreal && r->unacked != nil){ + r->timeout = 0; + if(r->xmits > 1){ + r->xmits = 1; + relrexmit(c, r); + } + } + + } + + /* no message or input queue full */ + if(seq == 0 || qfull(c->rq)) + goto out; + + /* refuse out of order delivery */ + if(seq != NEXTSEQ(r->rcvseq)){ + relsendack(c, r, 0); /* tell him we got it already */ + upriv->orders++; + DPRINT("out of sequence %lud not %lud\n", seq, NEXTSEQ(r->rcvseq)); + goto out; + } + r->rcvseq = seq; + + rv = 0; +out: + relput(r); + return rv; +} + +void +relsendack(Conv *c, Reliable *r, int hangup) +{ + Udphdr *uh; + Block *bp; + Rudphdr *rh; + int ptcllen; + Fs *f; + + bp = allocb(UDP_IPHDR + UDP_RHDRSIZE); + if(bp == nil) + return; + bp->wp += UDP_IPHDR + UDP_RHDRSIZE; + f = c->p->f; + uh = (Udphdr *)(bp->rp); + uh->vihl = IP_VER4; + rh = (Rudphdr*)uh; + + ptcllen = (UDP_RHDRSIZE-UDP_PHDRSIZE); + uh->Unused = 0; + uh->udpproto = IP_UDPPROTO; + uh->frag[0] = 0; + uh->frag[1] = 0; + hnputs(uh->udpplen, ptcllen); + + v6tov4(uh->udpdst, r->addr); + hnputs(uh->udpdport, r->port); + hnputs(uh->udpsport, c->lport); + if(ipcmp(c->laddr, IPnoaddr) == 0) + findlocalip(f, c->laddr, c->raddr); + v6tov4(uh->udpsrc, c->laddr); + hnputs(uh->udplen, ptcllen); + + if(hangup) + hnputl(rh->relsgen, Hangupgen); + else + hnputl(rh->relsgen, r->sndgen); + hnputl(rh->relseq, 0); + hnputl(rh->relagen, r->rcvgen); + hnputl(rh->relack, r->rcvseq); + + if(r->acksent < r->rcvseq) + r->acksent = r->rcvseq; + + uh->udpcksum[0] = 0; + uh->udpcksum[1] = 0; + hnputs(uh->udpcksum, ptclcsum(bp, UDP_IPHDR, UDP_RHDRSIZE)); + + DPRINT("sendack: %lud/%lud, %lud/%lud\n", 0L, r->sndgen, r->rcvseq, r->rcvgen); + doipoput(c, f, bp, 0, c->ttl, c->tos); +} + + +/* + * called with ucb locked (and c locked if user initiated close) + */ +void +relhangup(Conv *c, Reliable *r) +{ + int n; + Block *bp; + char hup[ERRMAX]; + + n = snprint(hup, sizeof(hup), "hangup %I!%d", r->addr, r->port); + qproduce(c->eq, hup, n); + + /* + * dump any unacked outgoing messages + */ + for(bp = r->unacked; bp != nil; bp = r->unacked){ + r->unacked = bp->list; + bp->list = nil; + freeb(bp); + } + + r->rcvgen = 0; + r->rcvseq = 0; + r->acksent = 0; + if(generation == Hangupgen) + generation++; + r->sndgen = generation++; + r->sndseq = 0; + r->ackrcvd = 0; + r->xmits = 0; + r->timeout = 0; + wakeup(&r->vous); +} + +/* + * called with ucb locked + */ +void +relrexmit(Conv *c, Reliable *r) +{ + Rudppriv *upriv; + Block *np; + Fs *f; + + upriv = c->p->priv; + f = c->p->f; + r->timeout = 0; + if(r->xmits++ > Rudpmaxxmit){ + relhangup(c, r); + return; + } + + upriv->rxmits++; + np = copyblock(r->unacked, blocklen(r->unacked)); + DPRINT("rxmit r->ackrvcd+1 = %lud\n", r->ackrcvd+1); + doipoput(c, f, np, 0, c->ttl, c->tos); +} diff --git a/os/ip/tcp.c b/os/ip/tcp.c new file mode 100644 index 00000000..c2bf7274 --- /dev/null +++ b/os/ip/tcp.c @@ -0,0 +1,3177 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" + +enum +{ + QMAX = 64*1024-1, + IP_TCPPROTO = 6, + + TCP4_IPLEN = 8, + TCP4_PHDRSIZE = 12, + TCP4_HDRSIZE = 20, + TCP4_TCBPHDRSZ = 40, + TCP4_PKT = TCP4_IPLEN+TCP4_PHDRSIZE, + + TCP6_IPLEN = 0, + TCP6_PHDRSIZE = 40, + TCP6_HDRSIZE = 20, + TCP6_TCBPHDRSZ = 60, + TCP6_PKT = TCP6_IPLEN+TCP6_PHDRSIZE, + + TcptimerOFF = 0, + TcptimerON = 1, + TcptimerDONE = 2, + MAX_TIME = (1<<20), /* Forever */ + TCP_ACK = 50, /* Timed ack sequence in ms */ + MAXBACKMS = 9*60*1000, /* longest backoff time (ms) before hangup */ + + URG = 0x20, /* Data marked urgent */ + ACK = 0x10, /* Acknowledge is valid */ + PSH = 0x08, /* Whole data pipe is pushed */ + RST = 0x04, /* Reset connection */ + SYN = 0x02, /* Pkt. is synchronise */ + FIN = 0x01, /* Start close down */ + + EOLOPT = 0, + NOOPOPT = 1, + MSSOPT = 2, + MSS_LENGTH = 4, /* Mean segment size */ + WSOPT = 3, + WS_LENGTH = 3, /* Bits to scale window size by */ + MSL2 = 10, + MSPTICK = 50, /* Milliseconds per timer tick */ + DEF_MSS = 1460, /* Default mean segment */ + DEF_MSS6 = 1280, /* Default mean segment (min) for v6 */ + DEF_RTT = 500, /* Default round trip */ + DEF_KAT = 120000, /* Default time (ms) between keep alives */ + TCP_LISTEN = 0, /* Listen connection */ + TCP_CONNECT = 1, /* Outgoing connection */ + SYNACK_RXTIMER = 250, /* ms between SYNACK retransmits */ + + TCPREXMTTHRESH = 3, /* dupack threshhold for rxt */ + + FORCE = 1, + CLONE = 2, + RETRAN = 4, + ACTIVE = 8, + SYNACK = 16, + + LOGAGAIN = 3, + LOGDGAIN = 2, + + Closed = 0, /* Connection states */ + Listen, + Syn_sent, + Syn_received, + Established, + Finwait1, + Finwait2, + Close_wait, + Closing, + Last_ack, + Time_wait, + + Maxlimbo = 1000, /* maximum procs waiting for response to SYN ACK */ + NLHT = 256, /* hash table size, must be a power of 2 */ + LHTMASK = NLHT-1, + + HaveWS = 1<<8, +}; + +/* Must correspond to the enumeration above */ +char *tcpstates[] = +{ + "Closed", "Listen", "Syn_sent", "Syn_received", + "Established", "Finwait1", "Finwait2", "Close_wait", + "Closing", "Last_ack", "Time_wait" +}; + +typedef struct Tcptimer Tcptimer; +struct Tcptimer +{ + Tcptimer *next; + Tcptimer *prev; + Tcptimer *readynext; + int state; + int start; + int count; + void (*func)(void*); + void *arg; +}; + +/* + * v4 and v6 pseudo headers used for + * checksuming tcp + */ +typedef struct Tcp4hdr Tcp4hdr; +struct Tcp4hdr +{ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar Unused; + uchar proto; + uchar tcplen[2]; + uchar tcpsrc[4]; + uchar tcpdst[4]; + uchar tcpsport[2]; + uchar tcpdport[2]; + uchar tcpseq[4]; + uchar tcpack[4]; + uchar tcpflag[2]; + uchar tcpwin[2]; + uchar tcpcksum[2]; + uchar tcpurg[2]; + /* Options segment */ + uchar tcpopt[1]; +}; + +typedef struct Tcp6hdr Tcp6hdr; +struct Tcp6hdr +{ + uchar vcf[4]; + uchar ploadlen[2]; + uchar proto; + uchar ttl; + uchar tcpsrc[IPaddrlen]; + uchar tcpdst[IPaddrlen]; + uchar tcpsport[2]; + uchar tcpdport[2]; + uchar tcpseq[4]; + uchar tcpack[4]; + uchar tcpflag[2]; + uchar tcpwin[2]; + uchar tcpcksum[2]; + uchar tcpurg[2]; + /* Options segment */ + uchar tcpopt[1]; +}; + +/* + * this represents the control info + * for a single packet. It is derived from + * a packet in ntohtcp{4,6}() and stuck into + * a packet in htontcp{4,6}(). + */ +typedef struct Tcp Tcp; +struct Tcp +{ + ushort source; + ushort dest; + ulong seq; + ulong ack; + uchar flags; + ushort ws; /* window scale option (if not zero) */ + ulong wnd; + ushort urg; + ushort mss; /* max segment size option (if not zero) */ + ushort len; /* size of data */ +}; + +/* + * this header is malloc'd to thread together fragments + * waiting to be coalesced + */ +typedef struct Reseq Reseq; +struct Reseq +{ + Reseq *next; + Tcp seg; + Block *bp; + ushort length; +}; + +/* + * the qlock in the Conv locks this structure + */ +typedef struct Tcpctl Tcpctl; +struct Tcpctl +{ + uchar state; /* Connection state */ + uchar type; /* Listening or active connection */ + uchar code; /* Icmp code */ + struct { + ulong una; /* Unacked data pointer */ + ulong nxt; /* Next sequence expected */ + ulong ptr; /* Data pointer */ + ulong wnd; /* Tcp send window */ + ulong urg; /* Urgent data pointer */ + ulong wl2; + int scale; /* how much to right shift window in xmitted packets */ + /* to implement tahoe and reno TCP */ + ulong dupacks; /* number of duplicate acks rcvd */ + int recovery; /* loss recovery flag */ + ulong rxt; /* right window marker for recovery */ + } snd; + struct { + ulong nxt; /* Receive pointer to next uchar slot */ + ulong wnd; /* Receive window incoming */ + ulong urg; /* Urgent pointer */ + int blocked; + int una; /* unacked data segs */ + int scale; /* how much to left shift window in rcved packets */ + } rcv; + ulong iss; /* Initial sequence number */ + int sawwsopt; /* true if we saw a wsopt on the incoming SYN */ + ulong cwind; /* Congestion window */ + int scale; /* desired snd.scale */ + ushort ssthresh; /* Slow start threshold */ + int resent; /* Bytes just resent */ + int irs; /* Initial received squence */ + ushort mss; /* Mean segment size */ + int rerecv; /* Overlap of data rerecevived */ + ulong window; /* Recevive window */ + uchar backoff; /* Exponential backoff counter */ + int backedoff; /* ms we've backed off for rexmits */ + uchar flags; /* State flags */ + Reseq *reseq; /* Resequencing queue */ + Tcptimer timer; /* Activity timer */ + Tcptimer acktimer; /* Acknowledge timer */ + Tcptimer rtt_timer; /* Round trip timer */ + Tcptimer katimer; /* keep alive timer */ + ulong rttseq; /* Round trip sequence */ + int srtt; /* Shortened round trip */ + int mdev; /* Mean deviation of round trip */ + int kacounter; /* count down for keep alive */ + uint sndsyntime; /* time syn sent */ + ulong time; /* time Finwait2 or Syn_received was sent */ + int nochecksum; /* non-zero means don't send checksums */ + int flgcnt; /* number of flags in the sequence (FIN,SEQ) */ + + union { + Tcp4hdr tcp4hdr; + Tcp6hdr tcp6hdr; + } protohdr; /* prototype header */ +}; + +/* + * New calls are put in limbo rather than having a conversation structure + * allocated. Thus, a SYN attack results in lots of limbo'd calls but not + * any real Conv structures mucking things up. Calls in limbo rexmit their + * SYN ACK every SYNACK_RXTIMER ms up to 4 times, i.e., they disappear after 1 second. + * + * In particular they aren't on a listener's queue so that they don't figure + * in the input queue limit. + * + * If 1/2 of a T3 was attacking SYN packets, we'ld have a permanent queue + * of 70000 limbo'd calls. Not great for a linear list but doable. Therefore + * there is no hashing of this list. + */ +typedef struct Limbo Limbo; +struct Limbo +{ + Limbo *next; + + uchar laddr[IPaddrlen]; + uchar raddr[IPaddrlen]; + ushort lport; + ushort rport; + ulong irs; /* initial received sequence */ + ulong iss; /* initial sent sequence */ + ushort mss; /* mss from the other end */ + ushort rcvscale; /* how much to scale rcvd windows */ + ushort sndscale; /* how much to scale sent windows */ + ulong lastsend; /* last time we sent a synack */ + uchar version; /* v4 or v6 */ + uchar rexmits; /* number of retransmissions */ +}; + +int tcp_irtt = DEF_RTT; /* Initial guess at round trip time */ +ushort tcp_mss = DEF_MSS; /* Maximum segment size to be sent */ + +enum { + /* MIB stats */ + MaxConn, + ActiveOpens, + PassiveOpens, + EstabResets, + CurrEstab, + InSegs, + OutSegs, + RetransSegs, + RetransTimeouts, + InErrs, + OutRsts, + + /* non-MIB stats */ + CsumErrs, + HlenErrs, + LenErrs, + OutOfOrder, + + Nstats +}; + +static char *statnames[] = +{ +[MaxConn] "MaxConn", +[ActiveOpens] "ActiveOpens", +[PassiveOpens] "PassiveOpens", +[EstabResets] "EstabResets", +[CurrEstab] "CurrEstab", +[InSegs] "InSegs", +[OutSegs] "OutSegs", +[RetransSegs] "RetransSegs", +[RetransTimeouts] "RetransTimeouts", +[InErrs] "InErrs", +[OutRsts] "OutRsts", +[CsumErrs] "CsumErrs", +[HlenErrs] "HlenErrs", +[LenErrs] "LenErrs", +[OutOfOrder] "OutOfOrder", +}; + +typedef struct Tcppriv Tcppriv; +struct Tcppriv +{ + /* List of active timers */ + QLock tl; + Tcptimer *timers; + + /* hash table for matching conversations */ + Ipht ht; + + /* calls in limbo waiting for an ACK to our SYN ACK */ + int nlimbo; + Limbo *lht[NLHT]; + + /* for keeping track of tcpackproc */ + QLock apl; + int ackprocstarted; + + ulong stats[Nstats]; +}; + +/* + * Setting tcpporthogdefense to non-zero enables Dong Lin's + * solution to hijacked systems staking out port's as a form + * of DoS attack. + * + * To avoid stateless Conv hogs, we pick a sequence number at random. If + * it that number gets acked by the other end, we shut down the connection. + * Look for tcpporthogedefense in the code. + */ +int tcpporthogdefense = 0; + +int addreseq(Tcpctl*, Tcppriv*, Tcp*, Block*, ushort); +void getreseq(Tcpctl*, Tcp*, Block**, ushort*); +void localclose(Conv*, char*); +void procsyn(Conv*, Tcp*); +void tcpiput(Proto*, Ipifc*, Block*); +void tcpoutput(Conv*); +int tcptrim(Tcpctl*, Tcp*, Block**, ushort*); +void tcpstart(Conv*, int); +void tcptimeout(void*); +void tcpsndsyn(Conv*, Tcpctl*); +void tcprcvwin(Conv*); +void tcpacktimer(void*); +void tcpkeepalive(void*); +void tcpsetkacounter(Tcpctl*); +void tcprxmit(Conv*); +void tcpsettimer(Tcpctl*); +void tcpsynackrtt(Conv*); +void tcpsetscale(Conv*, Tcpctl*, ushort, ushort); + +static void limborexmit(Proto*); +static void limbo(Conv*, uchar*, uchar*, Tcp*, int); + +void +tcpsetstate(Conv *s, uchar newstate) +{ + Tcpctl *tcb; + uchar oldstate; + Tcppriv *tpriv; + + tpriv = s->p->priv; + + tcb = (Tcpctl*)s->ptcl; + + oldstate = tcb->state; + if(oldstate == newstate) + return; + + if(oldstate == Established) + tpriv->stats[CurrEstab]--; + if(newstate == Established) + tpriv->stats[CurrEstab]++; + + /** + print( "%d/%d %s->%s CurrEstab=%d\n", s->lport, s->rport, + tcpstates[oldstate], tcpstates[newstate], tpriv->tstats.tcpCurrEstab ); + **/ + + switch(newstate) { + case Closed: + qclose(s->rq); + qclose(s->wq); + qclose(s->eq); + break; + + case Close_wait: /* Remote closes */ + qhangup(s->rq, nil); + break; + } + + tcb->state = newstate; + + if(oldstate == Syn_sent && newstate != Closed) + Fsconnected(s, nil); +} + +static char* +tcpconnect(Conv *c, char **argv, int argc) +{ + char *e; + + e = Fsstdconnect(c, argv, argc); + if(e != nil) + return e; + tcpstart(c, TCP_CONNECT); + + return nil; +} + +static int +tcpstate(Conv *c, char *state, int n) +{ + Tcpctl *s; + + s = (Tcpctl*)(c->ptcl); + + return snprint(state, n, + "%s qin %d qout %d srtt %d mdev %d cwin %lud swin %lud>>%d rwin %lud>>%d timer.start %d timer.count %d rerecv %d katimer.start %d katimer.count %d\n", + tcpstates[s->state], + c->rq ? qlen(c->rq) : 0, + c->wq ? qlen(c->wq) : 0, + s->srtt, s->mdev, + s->cwind, s->snd.wnd, s->rcv.scale, s->rcv.wnd, s->snd.scale, + s->timer.start, s->timer.count, s->rerecv, + s->katimer.start, s->katimer.count); +} + +static int +tcpinuse(Conv *c) +{ + Tcpctl *s; + + s = (Tcpctl*)(c->ptcl); + return s->state != Closed; +} + +static char* +tcpannounce(Conv *c, char **argv, int argc) +{ + char *e; + + e = Fsstdannounce(c, argv, argc); + if(e != nil) + return e; + tcpstart(c, TCP_LISTEN); + Fsconnected(c, nil); + + return nil; +} + +/* + * tcpclose is always called with the q locked + */ +static void +tcpclose(Conv *c) +{ + Tcpctl *tcb; + + tcb = (Tcpctl*)c->ptcl; + + qhangup(c->rq, nil); + qhangup(c->wq, nil); + qhangup(c->eq, nil); + qflush(c->rq); + + switch(tcb->state) { + case Listen: + /* + * reset any incoming calls to this listener + */ + Fsconnected(c, "Hangup"); + + localclose(c, nil); + break; + case Closed: + case Syn_sent: + localclose(c, nil); + break; + case Syn_received: + case Established: + tcb->flgcnt++; + tcb->snd.nxt++; + tcpsetstate(c, Finwait1); + tcpoutput(c); + break; + case Close_wait: + tcb->flgcnt++; + tcb->snd.nxt++; + tcpsetstate(c, Last_ack); + tcpoutput(c); + break; + } +} + +void +tcpkick(void *x) +{ + Conv *s = x; + Tcpctl *tcb; + + tcb = (Tcpctl*)s->ptcl; + + if(waserror()){ + qunlock(s); + nexterror(); + } + qlock(s); + + switch(tcb->state) { + case Syn_sent: + case Syn_received: + case Established: + case Close_wait: + /* + * Push data + */ + tcprcvwin(s); + tcpoutput(s); + break; + default: + localclose(s, "Hangup"); + break; + } + + qunlock(s); + poperror(); +} + +void +tcprcvwin(Conv *s) /* Call with tcb locked */ +{ + int w; + Tcpctl *tcb; + + tcb = (Tcpctl*)s->ptcl; + w = tcb->window - qlen(s->rq); + if(w < 0) + w = 0; + tcb->rcv.wnd = w; + if(w == 0) + tcb->rcv.blocked = 1; +} + +void +tcpacktimer(void *v) +{ + Tcpctl *tcb; + Conv *s; + + s = v; + tcb = (Tcpctl*)s->ptcl; + + if(waserror()){ + qunlock(s); + nexterror(); + } + qlock(s); + if(tcb->state != Closed){ + tcb->flags |= FORCE; + tcprcvwin(s); + tcpoutput(s); + } + qunlock(s); + poperror(); +} + +static void +tcpcreate(Conv *c) +{ + c->rq = qopen(QMAX, Qcoalesce, tcpacktimer, c); + c->wq = qopen((3*QMAX)/2, Qkick, tcpkick, c); +} + +static void +timerstate(Tcppriv *priv, Tcptimer *t, int newstate) +{ + if(newstate != TcptimerON){ + if(t->state == TcptimerON){ + // unchain + if(priv->timers == t){ + priv->timers = t->next; + if(t->prev != nil) + panic("timerstate1"); + } + if(t->next) + t->next->prev = t->prev; + if(t->prev) + t->prev->next = t->next; + t->next = t->prev = nil; + } + } else { + if(t->state != TcptimerON){ + // chain + if(t->prev != nil || t->next != nil) + panic("timerstate2"); + t->prev = nil; + t->next = priv->timers; + if(t->next) + t->next->prev = t; + priv->timers = t; + } + } + t->state = newstate; +} + +void +tcpackproc(void *a) +{ + Tcptimer *t, *tp, *timeo; + Proto *tcp; + Tcppriv *priv; + int loop; + + tcp = a; + priv = tcp->priv; + + for(;;) { + tsleep(&up->sleep, return0, 0, MSPTICK); + + qlock(&priv->tl); + timeo = nil; + loop = 0; + for(t = priv->timers; t != nil; t = tp) { + if(loop++ > 10000) + panic("tcpackproc1"); + tp = t->next; + if(t->state == TcptimerON) { + t->count--; + if(t->count == 0) { + timerstate(priv, t, TcptimerDONE); + t->readynext = timeo; + timeo = t; + } + } + } + qunlock(&priv->tl); + + loop = 0; + for(t = timeo; t != nil; t = t->readynext) { + if(loop++ > 10000) + panic("tcpackproc2"); + if(t->state == TcptimerDONE && t->func != nil && !waserror()){ + (*t->func)(t->arg); + poperror(); + } + } + + limborexmit(tcp); + } +} + +void +tcpgo(Tcppriv *priv, Tcptimer *t) +{ + if(t == nil || t->start == 0) + return; + + qlock(&priv->tl); + t->count = t->start; + timerstate(priv, t, TcptimerON); + qunlock(&priv->tl); +} + +void +tcphalt(Tcppriv *priv, Tcptimer *t) +{ + if(t == nil) + return; + + qlock(&priv->tl); + timerstate(priv, t, TcptimerOFF); + qunlock(&priv->tl); +} + +int +backoff(int n) +{ + return 1 << n; +} + +void +localclose(Conv *s, char *reason) /* called with tcb locked */ +{ + Tcpctl *tcb; + Reseq *rp,*rp1; + Tcppriv *tpriv; + + tpriv = s->p->priv; + tcb = (Tcpctl*)s->ptcl; + + iphtrem(&tpriv->ht, s); + + tcphalt(tpriv, &tcb->timer); + tcphalt(tpriv, &tcb->rtt_timer); + tcphalt(tpriv, &tcb->acktimer); + tcphalt(tpriv, &tcb->katimer); + + /* Flush reassembly queue; nothing more can arrive */ + for(rp = tcb->reseq; rp != nil; rp = rp1) { + rp1 = rp->next; + freeblist(rp->bp); + free(rp); + } + tcb->reseq = nil; + + if(tcb->state == Syn_sent) + Fsconnected(s, reason); + if(s->state == Announced) + wakeup(&s->listenr); + + qhangup(s->rq, reason); + qhangup(s->wq, reason); + + tcpsetstate(s, Closed); +} + +/* mtu (- TCP + IP hdr len) of 1st hop */ +int +tcpmtu(Proto *tcp, uchar *addr, int version, int *scale) +{ + Ipifc *ifc; + int mtu; + + ifc = findipifc(tcp->f, addr, 0); + switch(version){ + default: + case V4: + mtu = DEF_MSS; + if(ifc != nil) + mtu = ifc->maxtu - ifc->m->hsize - (TCP4_PKT + TCP4_HDRSIZE); + break; + case V6: + mtu = DEF_MSS6; + if(ifc != nil) + mtu = ifc->maxtu - ifc->m->hsize - (TCP6_PKT + TCP6_HDRSIZE); + break; + } + if(ifc != nil){ + if(ifc->mbps > 100) + *scale = HaveWS | 3; + else if(ifc->mbps > 10) + *scale = HaveWS | 1; + else + *scale = HaveWS | 0; + } else + *scale = HaveWS | 0; + + return mtu; +} + +void +inittcpctl(Conv *s, int mode) +{ + Tcpctl *tcb; + Tcp4hdr* h4; + Tcp6hdr* h6; + int mss; + + tcb = (Tcpctl*)s->ptcl; + + memset(tcb, 0, sizeof(Tcpctl)); + + tcb->ssthresh = 65535; + tcb->srtt = tcp_irtt<mdev = 0; + + /* setup timers */ + tcb->timer.start = tcp_irtt / MSPTICK; + tcb->timer.func = tcptimeout; + tcb->timer.arg = s; + tcb->rtt_timer.start = MAX_TIME; + tcb->acktimer.start = TCP_ACK / MSPTICK; + tcb->acktimer.func = tcpacktimer; + tcb->acktimer.arg = s; + tcb->katimer.start = DEF_KAT / MSPTICK; + tcb->katimer.func = tcpkeepalive; + tcb->katimer.arg = s; + + mss = DEF_MSS; + + /* create a prototype(pseudo) header */ + if(mode != TCP_LISTEN){ + if(ipcmp(s->laddr, IPnoaddr) == 0) + findlocalip(s->p->f, s->laddr, s->raddr); + + switch(s->ipversion){ + case V4: + h4 = &tcb->protohdr.tcp4hdr; + memset(h4, 0, sizeof(*h4)); + h4->proto = IP_TCPPROTO; + hnputs(h4->tcpsport, s->lport); + hnputs(h4->tcpdport, s->rport); + v6tov4(h4->tcpsrc, s->laddr); + v6tov4(h4->tcpdst, s->raddr); + break; + case V6: + h6 = &tcb->protohdr.tcp6hdr; + memset(h6, 0, sizeof(*h6)); + h6->proto = IP_TCPPROTO; + hnputs(h6->tcpsport, s->lport); + hnputs(h6->tcpdport, s->rport); + ipmove(h6->tcpsrc, s->laddr); + ipmove(h6->tcpdst, s->raddr); + mss = DEF_MSS6; + break; + default: + panic("inittcpctl: version %d", s->ipversion); + } + } + + tcb->mss = tcb->cwind = mss; + + /* default is no window scaling */ + tcb->window = QMAX; + tcb->rcv.wnd = QMAX; + tcb->rcv.scale = 0; + tcb->snd.scale = 0; + qsetlimit(s->rq, QMAX); +} + +/* + * called with s qlocked + */ +void +tcpstart(Conv *s, int mode) +{ + Tcpctl *tcb; + Tcppriv *tpriv; + char kpname[KNAMELEN]; + + tpriv = s->p->priv; + + if(tpriv->ackprocstarted == 0){ + qlock(&tpriv->apl); + if(tpriv->ackprocstarted == 0){ + sprint(kpname, "#I%dtcpack", s->p->f->dev); + kproc(kpname, tcpackproc, s->p, 0); + tpriv->ackprocstarted = 1; + } + qunlock(&tpriv->apl); + } + + tcb = (Tcpctl*)s->ptcl; + + inittcpctl(s, mode); + + iphtadd(&tpriv->ht, s); + switch(mode) { + case TCP_LISTEN: + tpriv->stats[PassiveOpens]++; + tcb->flags |= CLONE; + tcpsetstate(s, Listen); + break; + + case TCP_CONNECT: + tpriv->stats[ActiveOpens]++; + tcb->flags |= ACTIVE; + tcpsndsyn(s, tcb); + tcpsetstate(s, Syn_sent); + tcpoutput(s); + break; + } +} + +static char* +tcpflag(ushort flag) +{ + static char buf[128]; + + sprint(buf, "%d", flag>>10); /* Head len */ + if(flag & URG) + strcat(buf, " URG"); + if(flag & ACK) + strcat(buf, " ACK"); + if(flag & PSH) + strcat(buf, " PSH"); + if(flag & RST) + strcat(buf, " RST"); + if(flag & SYN) + strcat(buf, " SYN"); + if(flag & FIN) + strcat(buf, " FIN"); + + return buf; +} + +Block * +htontcp6(Tcp *tcph, Block *data, Tcp6hdr *ph, Tcpctl *tcb) +{ + int dlen; + Tcp6hdr *h; + ushort csum; + ushort hdrlen, optpad = 0; + uchar *opt; + + hdrlen = TCP6_HDRSIZE; + if(tcph->flags & SYN){ + if(tcph->mss) + hdrlen += MSS_LENGTH; + if(tcph->ws) + hdrlen += WS_LENGTH; + optpad = hdrlen & 3; + if(optpad) + optpad = 4 - optpad; + hdrlen += optpad; + } + + if(data) { + dlen = blocklen(data); + data = padblock(data, hdrlen + TCP6_PKT); + if(data == nil) + return nil; + } + else { + dlen = 0; + data = allocb(hdrlen + TCP6_PKT + 64); /* the 64 pad is to meet mintu's */ + if(data == nil) + return nil; + data->wp += hdrlen + TCP6_PKT; + } + + /* copy in pseudo ip header plus port numbers */ + h = (Tcp6hdr *)(data->rp); + memmove(h, ph, TCP6_TCBPHDRSZ); + + /* compose pseudo tcp header, do cksum calculation */ + hnputl(h->vcf, hdrlen + dlen); + h->ploadlen[0] = h->ploadlen[1] = h->proto = 0; + h->ttl = ph->proto; + + /* copy in variable bits */ + hnputl(h->tcpseq, tcph->seq); + hnputl(h->tcpack, tcph->ack); + hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags); + hnputs(h->tcpwin, tcph->wnd>>(tcb != nil ? tcb->snd.scale : 0)); + hnputs(h->tcpurg, tcph->urg); + + if(tcph->flags & SYN){ + opt = h->tcpopt; + if(tcph->mss != 0){ + *opt++ = MSSOPT; + *opt++ = MSS_LENGTH; + hnputs(opt, tcph->mss); + opt += 2; + } + if(tcph->ws != 0){ + *opt++ = WSOPT; + *opt++ = WS_LENGTH; + *opt++ = tcph->ws; + } + while(optpad-- > 0) + *opt++ = NOOPOPT; + } + + if(tcb != nil && tcb->nochecksum){ + h->tcpcksum[0] = h->tcpcksum[1] = 0; + } else { + csum = ptclcsum(data, TCP6_IPLEN, hdrlen+dlen+TCP6_PHDRSIZE); + hnputs(h->tcpcksum, csum); + } + + /* move from pseudo header back to normal ip header */ + memset(h->vcf, 0, 4); + h->vcf[0] = IP_VER6; + hnputs(h->ploadlen, hdrlen+dlen); + h->proto = ph->proto; + + return data; +} + +Block * +htontcp4(Tcp *tcph, Block *data, Tcp4hdr *ph, Tcpctl *tcb) +{ + int dlen; + Tcp4hdr *h; + ushort csum; + ushort hdrlen, optpad = 0; + uchar *opt; + + hdrlen = TCP4_HDRSIZE; + if(tcph->flags & SYN){ + if(tcph->mss) + hdrlen += MSS_LENGTH; + if(tcph->ws) + hdrlen += WS_LENGTH; + optpad = hdrlen & 3; + if(optpad) + optpad = 4 - optpad; + hdrlen += optpad; + } + + if(data) { + dlen = blocklen(data); + data = padblock(data, hdrlen + TCP4_PKT); + if(data == nil) + return nil; + } + else { + dlen = 0; + data = allocb(hdrlen + TCP4_PKT + 64); /* the 64 pad is to meet mintu's */ + if(data == nil) + return nil; + data->wp += hdrlen + TCP4_PKT; + } + + /* copy in pseudo ip header plus port numbers */ + h = (Tcp4hdr *)(data->rp); + memmove(h, ph, TCP4_TCBPHDRSZ); + + /* copy in variable bits */ + hnputs(h->tcplen, hdrlen + dlen); + hnputl(h->tcpseq, tcph->seq); + hnputl(h->tcpack, tcph->ack); + hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags); + hnputs(h->tcpwin, tcph->wnd>>(tcb != nil ? tcb->snd.scale : 0)); + hnputs(h->tcpurg, tcph->urg); + + if(tcph->flags & SYN){ + opt = h->tcpopt; + if(tcph->mss != 0){ + *opt++ = MSSOPT; + *opt++ = MSS_LENGTH; + hnputs(opt, tcph->mss); + opt += 2; + } + if(tcph->ws != 0){ + *opt++ = WSOPT; + *opt++ = WS_LENGTH; + *opt++ = tcph->ws; + } + while(optpad-- > 0) + *opt++ = NOOPOPT; + } + + if(tcb != nil && tcb->nochecksum){ + h->tcpcksum[0] = h->tcpcksum[1] = 0; + } else { + csum = ptclcsum(data, TCP4_IPLEN, hdrlen+dlen+TCP4_PHDRSIZE); + hnputs(h->tcpcksum, csum); + } + + return data; +} + +int +ntohtcp6(Tcp *tcph, Block **bpp) +{ + Tcp6hdr *h; + uchar *optr; + ushort hdrlen; + ushort optlen; + int n; + + *bpp = pullupblock(*bpp, TCP6_PKT+TCP6_HDRSIZE); + if(*bpp == nil) + return -1; + + h = (Tcp6hdr *)((*bpp)->rp); + tcph->source = nhgets(h->tcpsport); + tcph->dest = nhgets(h->tcpdport); + tcph->seq = nhgetl(h->tcpseq); + tcph->ack = nhgetl(h->tcpack); + hdrlen = (h->tcpflag[0]>>2) & ~3; + if(hdrlen < TCP6_HDRSIZE) { + freeblist(*bpp); + return -1; + } + + tcph->flags = h->tcpflag[1]; + tcph->wnd = nhgets(h->tcpwin); + tcph->urg = nhgets(h->tcpurg); + tcph->mss = 0; + tcph->ws = 0; + tcph->len = nhgets(h->ploadlen) - hdrlen; + + *bpp = pullupblock(*bpp, hdrlen+TCP6_PKT); + if(*bpp == nil) + return -1; + + optr = h->tcpopt; + n = hdrlen - TCP6_HDRSIZE; + while(n > 0 && *optr != EOLOPT) { + if(*optr == NOOPOPT) { + n--; + optr++; + continue; + } + optlen = optr[1]; + if(optlen < 2 || optlen > n) + break; + switch(*optr) { + case MSSOPT: + if(optlen == MSS_LENGTH) + tcph->mss = nhgets(optr+2); + break; + case WSOPT: + if(optlen == WS_LENGTH && *(optr+2) <= 14) + tcph->ws = HaveWS | *(optr+2); + break; + } + n -= optlen; + optr += optlen; + } + return hdrlen; +} + +int +ntohtcp4(Tcp *tcph, Block **bpp) +{ + Tcp4hdr *h; + uchar *optr; + ushort hdrlen; + ushort optlen; + int n; + + *bpp = pullupblock(*bpp, TCP4_PKT+TCP4_HDRSIZE); + if(*bpp == nil) + return -1; + + h = (Tcp4hdr *)((*bpp)->rp); + tcph->source = nhgets(h->tcpsport); + tcph->dest = nhgets(h->tcpdport); + tcph->seq = nhgetl(h->tcpseq); + tcph->ack = nhgetl(h->tcpack); + + hdrlen = (h->tcpflag[0]>>2) & ~3; + if(hdrlen < TCP4_HDRSIZE) { + freeblist(*bpp); + return -1; + } + + tcph->flags = h->tcpflag[1]; + tcph->wnd = nhgets(h->tcpwin); + tcph->urg = nhgets(h->tcpurg); + tcph->mss = 0; + tcph->ws = 0; + tcph->len = nhgets(h->length) - (hdrlen + TCP4_PKT); + + *bpp = pullupblock(*bpp, hdrlen+TCP4_PKT); + if(*bpp == nil) + return -1; + + optr = h->tcpopt; + n = hdrlen - TCP4_HDRSIZE; + while(n > 0 && *optr != EOLOPT) { + if(*optr == NOOPOPT) { + n--; + optr++; + continue; + } + optlen = optr[1]; + if(optlen < 2 || optlen > n) + break; + switch(*optr) { + case MSSOPT: + if(optlen == MSS_LENGTH) + tcph->mss = nhgets(optr+2); + break; + case WSOPT: + if(optlen == WS_LENGTH && *(optr+2) <= 14) + tcph->ws = HaveWS | *(optr+2); + break; + } + n -= optlen; + optr += optlen; + } + return hdrlen; +} + +/* + * For outgiing calls, generate an initial sequence + * number and put a SYN on the send queue + */ +void +tcpsndsyn(Conv *s, Tcpctl *tcb) +{ + tcb->iss = (nrand(1<<16)<<16)|nrand(1<<16); + tcb->rttseq = tcb->iss; + tcb->snd.wl2 = tcb->iss; + tcb->snd.una = tcb->iss; + tcb->snd.ptr = tcb->rttseq; + tcb->snd.nxt = tcb->rttseq; + tcb->flgcnt++; + tcb->flags |= FORCE; + tcb->sndsyntime = NOW; + + /* set desired mss and scale */ + tcb->mss = tcpmtu(s->p, s->laddr, s->ipversion, &tcb->scale); +} + +void +sndrst(Proto *tcp, uchar *source, uchar *dest, ushort length, Tcp *seg, uchar version, char *reason) +{ + Block *hbp; + uchar rflags; + Tcppriv *tpriv; + Tcp4hdr ph4; + Tcp6hdr ph6; + + netlog(tcp->f, Logtcp, "sndrst: %s", reason); + + tpriv = tcp->priv; + + if(seg->flags & RST) + return; + + /* make pseudo header */ + switch(version) { + case V4: + memset(&ph4, 0, sizeof(ph4)); + ph4.vihl = IP_VER4; + v6tov4(ph4.tcpsrc, dest); + v6tov4(ph4.tcpdst, source); + ph4.proto = IP_TCPPROTO; + hnputs(ph4.tcplen, TCP4_HDRSIZE); + hnputs(ph4.tcpsport, seg->dest); + hnputs(ph4.tcpdport, seg->source); + break; + case V6: + memset(&ph6, 0, sizeof(ph6)); + ph6.vcf[0] = IP_VER6; + ipmove(ph6.tcpsrc, dest); + ipmove(ph6.tcpdst, source); + ph6.proto = IP_TCPPROTO; + hnputs(ph6.ploadlen, TCP6_HDRSIZE); + hnputs(ph6.tcpsport, seg->dest); + hnputs(ph6.tcpdport, seg->source); + break; + default: + panic("sndrst: version %d", version); + } + + tpriv->stats[OutRsts]++; + rflags = RST; + + /* convince the other end that this reset is in band */ + if(seg->flags & ACK) { + seg->seq = seg->ack; + seg->ack = 0; + } + else { + rflags |= ACK; + seg->ack = seg->seq; + seg->seq = 0; + if(seg->flags & SYN) + seg->ack++; + seg->ack += length; + if(seg->flags & FIN) + seg->ack++; + } + seg->flags = rflags; + seg->wnd = 0; + seg->urg = 0; + seg->mss = 0; + seg->ws = 0; + switch(version) { + case V4: + hbp = htontcp4(seg, nil, &ph4, nil); + if(hbp == nil) + return; + ipoput4(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil); + break; + case V6: + hbp = htontcp6(seg, nil, &ph6, nil); + if(hbp == nil) + return; + ipoput6(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil); + break; + default: + panic("sndrst2: version %d", version); + } +} + +/* + * send a reset to the remote side and close the conversation + * called with s qlocked + */ +char* +tcphangup(Conv *s) +{ + Tcp seg; + Tcpctl *tcb; + Block *hbp; + + tcb = (Tcpctl*)s->ptcl; + if(waserror()) + return commonerror(); + if(s->raddr != 0) { + if(!waserror()){ + seg.flags = RST | ACK; + seg.ack = tcb->rcv.nxt; + tcb->rcv.una = 0; + seg.seq = tcb->snd.ptr; + seg.wnd = 0; + seg.urg = 0; + seg.mss = 0; + seg.ws = 0; + switch(s->ipversion) { + case V4: + tcb->protohdr.tcp4hdr.vihl = IP_VER4; + hbp = htontcp4(&seg, nil, &tcb->protohdr.tcp4hdr, tcb); + ipoput4(s->p->f, hbp, 0, s->ttl, s->tos, s); + break; + case V6: + tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6; + hbp = htontcp6(&seg, nil, &tcb->protohdr.tcp6hdr, tcb); + ipoput6(s->p->f, hbp, 0, s->ttl, s->tos, s); + break; + default: + panic("tcphangup: version %d", s->ipversion); + } + poperror(); + } + } + localclose(s, nil); + poperror(); + return nil; +} + +/* + * (re)send a SYN ACK + */ +int +sndsynack(Proto *tcp, Limbo *lp) +{ + Block *hbp; + Tcp4hdr ph4; + Tcp6hdr ph6; + Tcp seg; + int scale; + + /* make pseudo header */ + switch(lp->version) { + case V4: + memset(&ph4, 0, sizeof(ph4)); + ph4.vihl = IP_VER4; + v6tov4(ph4.tcpsrc, lp->laddr); + v6tov4(ph4.tcpdst, lp->raddr); + ph4.proto = IP_TCPPROTO; + hnputs(ph4.tcplen, TCP4_HDRSIZE); + hnputs(ph4.tcpsport, lp->lport); + hnputs(ph4.tcpdport, lp->rport); + break; + case V6: + memset(&ph6, 0, sizeof(ph6)); + ph6.vcf[0] = IP_VER6; + ipmove(ph6.tcpsrc, lp->laddr); + ipmove(ph6.tcpdst, lp->raddr); + ph6.proto = IP_TCPPROTO; + hnputs(ph6.ploadlen, TCP6_HDRSIZE); + hnputs(ph6.tcpsport, lp->lport); + hnputs(ph6.tcpdport, lp->rport); + break; + default: + panic("sndrst: version %d", lp->version); + } + + seg.seq = lp->iss; + seg.ack = lp->irs+1; + seg.flags = SYN|ACK; + seg.urg = 0; + seg.mss = tcpmtu(tcp, lp->laddr, lp->version, &scale); + seg.wnd = QMAX; + + /* if the other side set scale, we should too */ + if(lp->rcvscale){ + seg.ws = scale; + lp->sndscale = scale; + } else { + seg.ws = 0; + lp->sndscale = 0; + } + + switch(lp->version) { + case V4: + hbp = htontcp4(&seg, nil, &ph4, nil); + if(hbp == nil) + return -1; + ipoput4(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil); + break; + case V6: + hbp = htontcp6(&seg, nil, &ph6, nil); + if(hbp == nil) + return -1; + ipoput6(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil); + break; + default: + panic("sndsnack: version %d", lp->version); + } + lp->lastsend = NOW; + return 0; +} + +#define hashipa(a, p) ( ( (a)[IPaddrlen-2] + (a)[IPaddrlen-1] + p )&LHTMASK ) + +/* + * put a call into limbo and respond with a SYN ACK + * + * called with proto locked + */ +static void +limbo(Conv *s, uchar *source, uchar *dest, Tcp *seg, int version) +{ + Limbo *lp, **l; + Tcppriv *tpriv; + int h; + + tpriv = s->p->priv; + h = hashipa(source, seg->source); + + for(l = &tpriv->lht[h]; *l != nil; l = &lp->next){ + lp = *l; + if(lp->lport != seg->dest || lp->rport != seg->source || lp->version != version) + continue; + if(ipcmp(lp->raddr, source) != 0) + continue; + if(ipcmp(lp->laddr, dest) != 0) + continue; + + /* each new SYN restarts the retransmits */ + lp->irs = seg->seq; + break; + } + lp = *l; + if(lp == nil){ + if(tpriv->nlimbo >= Maxlimbo && tpriv->lht[h]){ + lp = tpriv->lht[h]; + tpriv->lht[h] = lp->next; + lp->next = nil; + } else { + lp = malloc(sizeof(*lp)); + if(lp == nil) + return; + tpriv->nlimbo++; + } + *l = lp; + lp->version = version; + ipmove(lp->laddr, dest); + ipmove(lp->raddr, source); + lp->lport = seg->dest; + lp->rport = seg->source; + lp->mss = seg->mss; + lp->rcvscale = seg->ws; + lp->irs = seg->seq; + lp->iss = (nrand(1<<16)<<16)|nrand(1<<16); + } + + if(sndsynack(s->p, lp) < 0){ + *l = lp->next; + tpriv->nlimbo--; + free(lp); + } +} + +/* + * resend SYN ACK's once every SYNACK_RXTIMER ms. + */ +static void +limborexmit(Proto *tcp) +{ + Tcppriv *tpriv; + Limbo **l, *lp; + int h; + int seen; + ulong now; + + tpriv = tcp->priv; + + if(!canqlock(tcp)) + return; + seen = 0; + now = NOW; + for(h = 0; h < NLHT && seen < tpriv->nlimbo; h++){ + for(l = &tpriv->lht[h]; *l != nil && seen < tpriv->nlimbo; ){ + lp = *l; + seen++; + if(now - lp->lastsend < (lp->rexmits+1)*SYNACK_RXTIMER) + continue; + + /* time it out after 1 second */ + if(++(lp->rexmits) > 5){ + tpriv->nlimbo--; + *l = lp->next; + free(lp); + continue; + } + + /* if we're being attacked, don't bother resending SYN ACK's */ + if(tpriv->nlimbo > 100) + continue; + + if(sndsynack(tcp, lp) < 0){ + tpriv->nlimbo--; + *l = lp->next; + free(lp); + continue; + } + + l = &lp->next; + } + } + qunlock(tcp); +} + +/* + * lookup call in limbo. if found, throw it out. + * + * called with proto locked + */ +static void +limborst(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version) +{ + Limbo *lp, **l; + int h; + Tcppriv *tpriv; + + tpriv = s->p->priv; + + /* find a call in limbo */ + h = hashipa(src, segp->source); + for(l = &tpriv->lht[h]; *l != nil; l = &lp->next){ + lp = *l; + if(lp->lport != segp->dest || lp->rport != segp->source || lp->version != version) + continue; + if(ipcmp(lp->laddr, dst) != 0) + continue; + if(ipcmp(lp->raddr, src) != 0) + continue; + + /* RST can only follow the SYN */ + if(segp->seq == lp->irs+1){ + tpriv->nlimbo--; + *l = lp->next; + free(lp); + } + break; + } +} + +/* + * come here when we finally get an ACK to our SYN-ACK. + * lookup call in limbo. if found, create a new conversation + * + * called with proto locked + */ +static Conv* +tcpincoming(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version) +{ + Conv *new; + Tcpctl *tcb; + Tcppriv *tpriv; + Tcp4hdr *h4; + Tcp6hdr *h6; + Limbo *lp, **l; + int h; + + /* unless it's just an ack, it can't be someone coming out of limbo */ + if((segp->flags & SYN) || (segp->flags & ACK) == 0) + return nil; + + tpriv = s->p->priv; + + /* find a call in limbo */ + h = hashipa(src, segp->source); + for(l = &tpriv->lht[h]; (lp = *l) != nil; l = &lp->next){ + netlog(s->p->f, Logtcp, "tcpincoming s %I,%ux/%I,%ux d %I,%ux/%I,%ux v %d/%d", + src, segp->source, lp->raddr, lp->rport, + dst, segp->dest, lp->laddr, lp->lport, + version, lp->version + ); + + if(lp->lport != segp->dest || lp->rport != segp->source || lp->version != version) + continue; + if(ipcmp(lp->laddr, dst) != 0) + continue; + if(ipcmp(lp->raddr, src) != 0) + continue; + + /* we're assuming no data with the initial SYN */ + if(segp->seq != lp->irs+1 || segp->ack != lp->iss+1){ + netlog(s->p->f, Logtcp, "tcpincoming s %lux/%lux a %lux %lux", + segp->seq, lp->irs+1, segp->ack, lp->iss+1); + lp = nil; + } else { + tpriv->nlimbo--; + *l = lp->next; + } + break; + } + if(lp == nil) + return nil; + + new = Fsnewcall(s, src, segp->source, dst, segp->dest, version); + if(new == nil) + return nil; + + memmove(new->ptcl, s->ptcl, sizeof(Tcpctl)); + tcb = (Tcpctl*)new->ptcl; + tcb->flags &= ~CLONE; + tcb->timer.arg = new; + tcb->timer.state = TcptimerOFF; + tcb->acktimer.arg = new; + tcb->acktimer.state = TcptimerOFF; + tcb->katimer.arg = new; + tcb->katimer.state = TcptimerOFF; + tcb->rtt_timer.arg = new; + tcb->rtt_timer.state = TcptimerOFF; + + tcb->irs = lp->irs; + tcb->rcv.nxt = tcb->irs+1; + tcb->rcv.urg = tcb->rcv.nxt; + + tcb->iss = lp->iss; + tcb->rttseq = tcb->iss; + tcb->snd.wl2 = tcb->iss; + tcb->snd.una = tcb->iss+1; + tcb->snd.ptr = tcb->iss+1; + tcb->snd.nxt = tcb->iss+1; + tcb->flgcnt = 0; + tcb->flags |= SYNACK; + + /* our sending max segment size cannot be bigger than what he asked for */ + if(lp->mss != 0 && lp->mss < tcb->mss) + tcb->mss = lp->mss; + + /* window scaling */ + tcpsetscale(new, tcb, lp->rcvscale, lp->sndscale); + + /* the congestion window always starts out as a single segment */ + tcb->snd.wnd = segp->wnd; + tcb->cwind = tcb->mss; + + /* set initial round trip time */ + tcb->sndsyntime = lp->lastsend+lp->rexmits*SYNACK_RXTIMER; + tcpsynackrtt(new); + + free(lp); + + /* set up proto header */ + switch(version){ + case V4: + h4 = &tcb->protohdr.tcp4hdr; + memset(h4, 0, sizeof(*h4)); + h4->proto = IP_TCPPROTO; + hnputs(h4->tcpsport, new->lport); + hnputs(h4->tcpdport, new->rport); + v6tov4(h4->tcpsrc, dst); + v6tov4(h4->tcpdst, src); + break; + case V6: + h6 = &tcb->protohdr.tcp6hdr; + memset(h6, 0, sizeof(*h6)); + h6->proto = IP_TCPPROTO; + hnputs(h6->tcpsport, new->lport); + hnputs(h6->tcpdport, new->rport); + ipmove(h6->tcpsrc, dst); + ipmove(h6->tcpdst, src); + break; + default: + panic("tcpincoming: version %d", new->ipversion); + } + + tcpsetstate(new, Established); + + iphtadd(&tpriv->ht, new); + + return new; +} + +int +seq_within(ulong x, ulong low, ulong high) +{ + if(low <= high){ + if(low <= x && x <= high) + return 1; + } + else { + if(x >= low || x <= high) + return 1; + } + return 0; +} + +int +seq_lt(ulong x, ulong y) +{ + return (int)(x-y) < 0; +} + +int +seq_le(ulong x, ulong y) +{ + return (int)(x-y) <= 0; +} + +int +seq_gt(ulong x, ulong y) +{ + return (int)(x-y) > 0; +} + +int +seq_ge(ulong x, ulong y) +{ + return (int)(x-y) >= 0; +} + +/* + * use the time between the first SYN and it's ack as the + * initial round trip time + */ +void +tcpsynackrtt(Conv *s) +{ + Tcpctl *tcb; + int delta; + Tcppriv *tpriv; + + tcb = (Tcpctl*)s->ptcl; + tpriv = s->p->priv; + + delta = NOW - tcb->sndsyntime; + tcb->srtt = delta<mdev = delta<rtt_timer); +} + +void +update(Conv *s, Tcp *seg) +{ + int rtt, delta; + Tcpctl *tcb; + ulong acked; + ulong expand; + Tcppriv *tpriv; + + tpriv = s->p->priv; + tcb = (Tcpctl*)s->ptcl; + + /* if everything has been acked, force output(?) */ + if(seq_gt(seg->ack, tcb->snd.nxt)) { + tcb->flags |= FORCE; + return; + } + + /* added by Dong Lin for fast retransmission */ + if(seg->ack == tcb->snd.una + && tcb->snd.una != tcb->snd.nxt + && seg->len == 0 + && seg->wnd == tcb->snd.wnd) { + + /* this is a pure ack w/o window update */ + netlog(s->p->f, Logtcprxmt, "dupack %lud ack %lud sndwnd %d advwin %d\n", + tcb->snd.dupacks, seg->ack, tcb->snd.wnd, seg->wnd); + + if(++tcb->snd.dupacks == TCPREXMTTHRESH) { + /* + * tahoe tcp rxt the packet, half sshthresh, + * and set cwnd to one packet + */ + tcb->snd.recovery = 1; + tcb->snd.rxt = tcb->snd.nxt; + netlog(s->p->f, Logtcprxmt, "fast rxt %lud, nxt %lud\n", tcb->snd.una, tcb->snd.nxt); + tcprxmit(s); + } else { + /* do reno tcp here. */ + } + } + + /* + * update window + */ + if( seq_gt(seg->ack, tcb->snd.wl2) + || (tcb->snd.wl2 == seg->ack && seg->wnd > tcb->snd.wnd)){ + tcb->snd.wnd = seg->wnd; + tcb->snd.wl2 = seg->ack; + } + + if(!seq_gt(seg->ack, tcb->snd.una)){ + /* + * don't let us hangup if sending into a closed window and + * we're still getting acks + */ + if((tcb->flags&RETRAN) && tcb->snd.wnd == 0){ + tcb->backedoff = MAXBACKMS/4; + } + return; + } + + /* + * any positive ack turns off fast rxt, + * (should we do new-reno on partial acks?) + */ + if(!tcb->snd.recovery || seq_ge(seg->ack, tcb->snd.rxt)) { + tcb->snd.dupacks = 0; + tcb->snd.recovery = 0; + } else + netlog(s->p->f, Logtcp, "rxt next %lud, cwin %ud\n", seg->ack, tcb->cwind); + + /* Compute the new send window size */ + acked = seg->ack - tcb->snd.una; + + /* avoid slow start and timers for SYN acks */ + if((tcb->flags & SYNACK) == 0) { + tcb->flags |= SYNACK; + acked--; + tcb->flgcnt--; + goto done; + } + + /* slow start as long as we're not recovering from lost packets */ + if(tcb->cwind < tcb->snd.wnd && !tcb->snd.recovery) { + if(tcb->cwind < tcb->ssthresh) { + expand = tcb->mss; + if(acked < expand) + expand = acked; + } + else + expand = ((int)tcb->mss * tcb->mss) / tcb->cwind; + + if(tcb->cwind + expand < tcb->cwind) + expand = tcb->snd.wnd - tcb->cwind; + if(tcb->cwind + expand > tcb->snd.wnd) + expand = tcb->snd.wnd - tcb->cwind; + tcb->cwind += expand; + } + + /* Adjust the timers according to the round trip time */ + if(tcb->rtt_timer.state == TcptimerON && seq_ge(seg->ack, tcb->rttseq)) { + tcphalt(tpriv, &tcb->rtt_timer); + if((tcb->flags&RETRAN) == 0) { + tcb->backoff = 0; + tcb->backedoff = 0; + rtt = tcb->rtt_timer.start - tcb->rtt_timer.count; + if(rtt == 0) + rtt = 1; /* otherwise all close systems will rexmit in 0 time */ + rtt *= MSPTICK; + if(tcb->srtt == 0) { + tcb->srtt = rtt << LOGAGAIN; + tcb->mdev = rtt << LOGDGAIN; + } else { + delta = rtt - (tcb->srtt>>LOGAGAIN); + tcb->srtt += delta; + if(tcb->srtt <= 0) + tcb->srtt = 1; + + delta = abs(delta) - (tcb->mdev>>LOGDGAIN); + tcb->mdev += delta; + if(tcb->mdev <= 0) + tcb->mdev = 1; + } + tcpsettimer(tcb); + } + } + +done: + if(qdiscard(s->wq, acked) < acked) + tcb->flgcnt--; + + tcb->snd.una = seg->ack; + if(seq_gt(seg->ack, tcb->snd.urg)) + tcb->snd.urg = seg->ack; + + if(tcb->snd.una != tcb->snd.nxt) + tcpgo(tpriv, &tcb->timer); + else + tcphalt(tpriv, &tcb->timer); + + if(seq_lt(tcb->snd.ptr, tcb->snd.una)) + tcb->snd.ptr = tcb->snd.una; + + tcb->flags &= ~RETRAN; + tcb->backoff = 0; + tcb->backedoff = 0; +} + +void +tcpiput(Proto *tcp, Ipifc*, Block *bp) +{ + Tcp seg; + Tcp4hdr *h4; + Tcp6hdr *h6; + int hdrlen; + Tcpctl *tcb; + ushort length; + uchar source[IPaddrlen], dest[IPaddrlen]; + Conv *s; + Fs *f; + Tcppriv *tpriv; + uchar version; + + f = tcp->f; + tpriv = tcp->priv; + + tpriv->stats[InSegs]++; + + h4 = (Tcp4hdr*)(bp->rp); + h6 = (Tcp6hdr*)(bp->rp); + + if((h4->vihl&0xF0)==IP_VER4) { + version = V4; + length = nhgets(h4->length); + v4tov6(dest, h4->tcpdst); + v4tov6(source, h4->tcpsrc); + + h4->Unused = 0; + hnputs(h4->tcplen, length-TCP4_PKT); + if(!(bp->flag & Btcpck) && (h4->tcpcksum[0] || h4->tcpcksum[1]) && + ptclcsum(bp, TCP4_IPLEN, length-TCP4_IPLEN)) { + tpriv->stats[CsumErrs]++; + tpriv->stats[InErrs]++; + netlog(f, Logtcp, "bad tcp proto cksum\n"); + freeblist(bp); + return; + } + + hdrlen = ntohtcp4(&seg, &bp); + if(hdrlen < 0){ + tpriv->stats[HlenErrs]++; + tpriv->stats[InErrs]++; + netlog(f, Logtcp, "bad tcp hdr len\n"); + return; + } + + /* trim the packet to the size claimed by the datagram */ + length -= hdrlen+TCP4_PKT; + bp = trimblock(bp, hdrlen+TCP4_PKT, length); + if(bp == nil){ + tpriv->stats[LenErrs]++; + tpriv->stats[InErrs]++; + netlog(f, Logtcp, "tcp len < 0 after trim\n"); + return; + } + } + else { + int ttl = h6->ttl; + int proto = h6->proto; + + version = V6; + length = nhgets(h6->ploadlen); + ipmove(dest, h6->tcpdst); + ipmove(source, h6->tcpsrc); + + h6->ploadlen[0] = h6->ploadlen[1] = h6->proto = 0; + h6->ttl = proto; + hnputl(h6->vcf, length); + if((h6->tcpcksum[0] || h6->tcpcksum[1]) && + ptclcsum(bp, TCP6_IPLEN, length+TCP6_PHDRSIZE)) { + tpriv->stats[CsumErrs]++; + tpriv->stats[InErrs]++; + netlog(f, Logtcp, "bad tcp proto cksum\n"); + freeblist(bp); + return; + } + h6->ttl = ttl; + h6->proto = proto; + hnputs(h6->ploadlen, length); + + hdrlen = ntohtcp6(&seg, &bp); + if(hdrlen < 0){ + tpriv->stats[HlenErrs]++; + tpriv->stats[InErrs]++; + netlog(f, Logtcp, "bad tcp hdr len\n"); + return; + } + + /* trim the packet to the size claimed by the datagram */ + length -= hdrlen; + bp = trimblock(bp, hdrlen+TCP6_PKT, length); + if(bp == nil){ + tpriv->stats[LenErrs]++; + tpriv->stats[InErrs]++; + netlog(f, Logtcp, "tcp len < 0 after trim\n"); + return; + } + } + + /* lock protocol while searching for a conversation */ + qlock(tcp); + + /* Look for a matching conversation */ + s = iphtlook(&tpriv->ht, source, seg.source, dest, seg.dest); + if(s == nil){ + netlog(f, Logtcp, "iphtlook failed"); +reset: + qunlock(tcp); + sndrst(tcp, source, dest, length, &seg, version, "no conversation"); + freeblist(bp); + return; + } + + /* if it's a listener, look for the right flags and get a new conv */ + tcb = (Tcpctl*)s->ptcl; + if(tcb->state == Listen){ + if(seg.flags & RST){ + limborst(s, &seg, source, dest, version); + qunlock(tcp); + freeblist(bp); + return; + } + + /* if this is a new SYN, put the call into limbo */ + if((seg.flags & SYN) && (seg.flags & ACK) == 0){ + limbo(s, source, dest, &seg, version); + qunlock(tcp); + freeblist(bp); + return; + } + + /* + * if there's a matching call in limbo, tcpincoming will + * return it in state Syn_received + */ + s = tcpincoming(s, &seg, source, dest, version); + if(s == nil) + goto reset; + } + + /* The rest of the input state machine is run with the control block + * locked and implements the state machine directly out of the RFC. + * Out-of-band data is ignored - it was always a bad idea. + */ + tcb = (Tcpctl*)s->ptcl; + if(waserror()){ + qunlock(s); + nexterror(); + } + qlock(s); + qunlock(tcp); + + /* fix up window */ + seg.wnd <<= tcb->rcv.scale; + + /* every input packet in puts off the keep alive time out */ + tcpsetkacounter(tcb); + + switch(tcb->state) { + case Closed: + sndrst(tcp, source, dest, length, &seg, version, "sending to Closed"); + goto raise; + case Syn_sent: + if(seg.flags & ACK) { + if(!seq_within(seg.ack, tcb->iss+1, tcb->snd.nxt)) { + sndrst(tcp, source, dest, length, &seg, version, + "bad seq in Syn_sent"); + goto raise; + } + } + if(seg.flags & RST) { + if(seg.flags & ACK) + localclose(s, Econrefused); + goto raise; + } + + if(seg.flags & SYN) { + procsyn(s, &seg); + if(seg.flags & ACK){ + update(s, &seg); + tcpsynackrtt(s); + tcpsetstate(s, Established); + tcpsetscale(s, tcb, seg.ws, tcb->scale); + } + else { + tcb->time = NOW; + tcpsetstate(s, Syn_received); /* DLP - shouldn't this be a reset? */ + } + + if(length != 0 || (seg.flags & FIN)) + break; + + freeblist(bp); + goto output; + } + else + freeblist(bp); + + qunlock(s); + poperror(); + return; + case Syn_received: + /* doesn't matter if it's the correct ack, we're just trying to set timing */ + if(seg.flags & ACK) + tcpsynackrtt(s); + break; + } + + /* + * One DOS attack is to open connections to us and then forget about them, + * thereby tying up a conv at no long term cost to the attacker. + * This is an attempt to defeat these stateless DOS attacks. See + * corresponding code in tcpsendka(). + */ + if(tcb->state != Syn_received && (seg.flags & RST) == 0){ + if(tcpporthogdefense + && seq_within(seg.ack, tcb->snd.una-(1<<31), tcb->snd.una-(1<<29))){ + print("stateless hog %I.%d->%I.%d f %ux %lux - %lux - %lux\n", + source, seg.source, dest, seg.dest, seg.flags, + tcb->snd.una-(1<<31), seg.ack, tcb->snd.una-(1<<29)); + localclose(s, "stateless hog"); + } + } + + /* Cut the data to fit the receive window */ + if(tcptrim(tcb, &seg, &bp, &length) == -1) { + netlog(f, Logtcp, "tcp len < 0, %lud %d\n", seg.seq, length); + update(s, &seg); + if(qlen(s->wq)+tcb->flgcnt == 0 && tcb->state == Closing) { + tcphalt(tpriv, &tcb->rtt_timer); + tcphalt(tpriv, &tcb->acktimer); + tcphalt(tpriv, &tcb->katimer); + tcpsetstate(s, Time_wait); + tcb->timer.start = MSL2*(1000 / MSPTICK); + tcpgo(tpriv, &tcb->timer); + } + if(!(seg.flags & RST)) { + tcb->flags |= FORCE; + goto output; + } + qunlock(s); + poperror(); + return; + } + + /* Cannot accept so answer with a rst */ + if(length && tcb->state == Closed) { + sndrst(tcp, source, dest, length, &seg, version, "sending to Closed"); + goto raise; + } + + /* The segment is beyond the current receive pointer so + * queue the data in the resequence queue + */ + if(seg.seq != tcb->rcv.nxt) + if(length != 0 || (seg.flags & (SYN|FIN))) { + update(s, &seg); + if(addreseq(tcb, tpriv, &seg, bp, length) < 0) + print("reseq %I.%d -> %I.%d\n", s->raddr, s->rport, s->laddr, s->lport); + tcb->flags |= FORCE; + goto output; + } + + /* + * keep looping till we've processed this packet plus any + * adjacent packets in the resequence queue + */ + for(;;) { + if(seg.flags & RST) { + if(tcb->state == Established) { + tpriv->stats[EstabResets]++; + if(tcb->rcv.nxt != seg.seq) + print("out of order RST rcvd: %I.%d -> %I.%d, rcv.nxt %lux seq %lux\n", s->raddr, s->rport, s->laddr, s->lport, tcb->rcv.nxt, seg.seq); + } + localclose(s, Econrefused); + goto raise; + } + + if((seg.flags&ACK) == 0) + goto raise; + + switch(tcb->state) { + case Syn_received: + if(!seq_within(seg.ack, tcb->snd.una+1, tcb->snd.nxt)){ + sndrst(tcp, source, dest, length, &seg, version, + "bad seq in Syn_received"); + goto raise; + } + update(s, &seg); + tcpsetstate(s, Established); + case Established: + case Close_wait: + update(s, &seg); + break; + case Finwait1: + update(s, &seg); + if(qlen(s->wq)+tcb->flgcnt == 0){ + tcphalt(tpriv, &tcb->rtt_timer); + tcphalt(tpriv, &tcb->acktimer); + tcpsetkacounter(tcb); + tcb->time = NOW; + tcpsetstate(s, Finwait2); + tcb->katimer.start = MSL2 * (1000 / MSPTICK); + tcpgo(tpriv, &tcb->katimer); + } + break; + case Finwait2: + update(s, &seg); + break; + case Closing: + update(s, &seg); + if(qlen(s->wq)+tcb->flgcnt == 0) { + tcphalt(tpriv, &tcb->rtt_timer); + tcphalt(tpriv, &tcb->acktimer); + tcphalt(tpriv, &tcb->katimer); + tcpsetstate(s, Time_wait); + tcb->timer.start = MSL2*(1000 / MSPTICK); + tcpgo(tpriv, &tcb->timer); + } + break; + case Last_ack: + update(s, &seg); + if(qlen(s->wq)+tcb->flgcnt == 0) { + localclose(s, nil); + goto raise; + } + case Time_wait: + tcb->flags |= FORCE; + if(tcb->timer.state != TcptimerON) + tcpgo(tpriv, &tcb->timer); + } + + if((seg.flags&URG) && seg.urg) { + if(seq_gt(seg.urg + seg.seq, tcb->rcv.urg)) { + tcb->rcv.urg = seg.urg + seg.seq; + pullblock(&bp, seg.urg); + } + } + else + if(seq_gt(tcb->rcv.nxt, tcb->rcv.urg)) + tcb->rcv.urg = tcb->rcv.nxt; + + if(length == 0) { + if(bp != nil) + freeblist(bp); + } + else { + switch(tcb->state){ + default: + /* Ignore segment text */ + if(bp != nil) + freeblist(bp); + break; + + case Syn_received: + case Established: + case Finwait1: + /* If we still have some data place on + * receive queue + */ + if(bp) { + bp = packblock(bp); + if(bp == nil) + panic("tcp packblock"); + qpassnolim(s->rq, bp); + bp = nil; + + /* + * Force an ack every 2 data messages. This is + * a hack for rob to make his home system run + * faster. + * + * this also keeps the standard TCP congestion + * control working since it needs an ack every + * 2 max segs worth. This is not quite that, + * but under a real stream is equivalent since + * every packet has a max seg in it. + */ + if(++(tcb->rcv.una) >= 2) + tcb->flags |= FORCE; + } + tcb->rcv.nxt += length; + + /* + * update our rcv window + */ + tcprcvwin(s); + + /* + * turn on the acktimer if there's something + * to ack + */ + if(tcb->acktimer.state != TcptimerON) + tcpgo(tpriv, &tcb->acktimer); + + break; + case Finwait2: + /* no process to read the data, send a reset */ + if(bp != nil) + freeblist(bp); + sndrst(tcp, source, dest, length, &seg, version, + "send to Finwait2"); + qunlock(s); + poperror(); + return; + } + } + + if(seg.flags & FIN) { + tcb->flags |= FORCE; + + switch(tcb->state) { + case Syn_received: + case Established: + tcb->rcv.nxt++; + tcpsetstate(s, Close_wait); + break; + case Finwait1: + tcb->rcv.nxt++; + if(qlen(s->wq)+tcb->flgcnt == 0) { + tcphalt(tpriv, &tcb->rtt_timer); + tcphalt(tpriv, &tcb->acktimer); + tcphalt(tpriv, &tcb->katimer); + tcpsetstate(s, Time_wait); + tcb->timer.start = MSL2*(1000/MSPTICK); + tcpgo(tpriv, &tcb->timer); + } + else + tcpsetstate(s, Closing); + break; + case Finwait2: + tcb->rcv.nxt++; + tcphalt(tpriv, &tcb->rtt_timer); + tcphalt(tpriv, &tcb->acktimer); + tcphalt(tpriv, &tcb->katimer); + tcpsetstate(s, Time_wait); + tcb->timer.start = MSL2 * (1000/MSPTICK); + tcpgo(tpriv, &tcb->timer); + break; + case Close_wait: + case Closing: + case Last_ack: + break; + case Time_wait: + tcpgo(tpriv, &tcb->timer); + break; + } + } + + /* + * get next adjacent segment from the resequence queue. + * dump/trim any overlapping segments + */ + for(;;) { + if(tcb->reseq == nil) + goto output; + + if(seq_ge(tcb->rcv.nxt, tcb->reseq->seg.seq) == 0) + goto output; + + getreseq(tcb, &seg, &bp, &length); + + if(tcptrim(tcb, &seg, &bp, &length) == 0) + break; + } + } +output: + tcpoutput(s); + qunlock(s); + poperror(); + return; +raise: + qunlock(s); + poperror(); + freeblist(bp); + tcpkick(s); +} + +/* + * always enters and exits with the s locked. We drop + * the lock to ipoput the packet so some care has to be + * taken by callers. + */ +void +tcpoutput(Conv *s) +{ + Tcp seg; + int msgs; + Tcpctl *tcb; + Block *hbp, *bp; + int sndcnt, n; + ulong ssize, dsize, usable, sent; + Fs *f; + Tcppriv *tpriv; + uchar version; + + f = s->p->f; + tpriv = s->p->priv; + version = s->ipversion; + + for(msgs = 0; msgs < 100; msgs++) { + tcb = (Tcpctl*)s->ptcl; + + switch(tcb->state) { + case Listen: + case Closed: + case Finwait2: + return; + } + + /* force an ack when a window has opened up */ + if(tcb->rcv.blocked && tcb->rcv.wnd > 0){ + tcb->rcv.blocked = 0; + tcb->flags |= FORCE; + } + + sndcnt = qlen(s->wq)+tcb->flgcnt; + sent = tcb->snd.ptr - tcb->snd.una; + + /* Don't send anything else until our SYN has been acked */ + if(tcb->snd.ptr != tcb->iss && (tcb->flags & SYNACK) == 0) + break; + + /* Compute usable segment based on offered window and limit + * window probes to one + */ + if(tcb->snd.wnd == 0){ + if(sent != 0) { + if((tcb->flags&FORCE) == 0) + break; +// tcb->snd.ptr = tcb->snd.una; + } + usable = 1; + } + else { + usable = tcb->cwind; + if(tcb->snd.wnd < usable) + usable = tcb->snd.wnd; + usable -= sent; + } + ssize = sndcnt-sent; + if(ssize && usable < 2) + netlog(s->p->f, Logtcp, "throttled snd.wnd %lud cwind %lud\n", + tcb->snd.wnd, tcb->cwind); + if(usable < ssize) + ssize = usable; + if(tcb->mss < ssize) + ssize = tcb->mss; + dsize = ssize; + seg.urg = 0; + + if(ssize == 0) + if((tcb->flags&FORCE) == 0) + break; + + tcb->flags &= ~FORCE; + tcprcvwin(s); + + /* By default we will generate an ack */ + tcphalt(tpriv, &tcb->acktimer); + tcb->rcv.una = 0; + seg.source = s->lport; + seg.dest = s->rport; + seg.flags = ACK; + seg.mss = 0; + seg.ws = 0; + switch(tcb->state){ + case Syn_sent: + seg.flags = 0; + if(tcb->snd.ptr == tcb->iss){ + seg.flags |= SYN; + dsize--; + seg.mss = tcb->mss; + seg.ws = tcb->scale; + } + break; + case Syn_received: + /* + * don't send any data with a SYN/ACK packet + * because Linux rejects the packet in its + * attempt to solve the SYN attack problem + */ + if(tcb->snd.ptr == tcb->iss){ + seg.flags |= SYN; + dsize = 0; + ssize = 1; + seg.mss = tcb->mss; + seg.ws = tcb->scale; + } + break; + } + seg.seq = tcb->snd.ptr; + seg.ack = tcb->rcv.nxt; + seg.wnd = tcb->rcv.wnd; + + /* Pull out data to send */ + bp = nil; + if(dsize != 0) { + bp = qcopy(s->wq, dsize, sent); + if(BLEN(bp) != dsize) { + seg.flags |= FIN; + dsize--; + } + } + + if(sent+dsize == sndcnt) + seg.flags |= PSH; + + /* keep track of balance of resent data */ + if(seq_lt(tcb->snd.ptr, tcb->snd.nxt)) { + n = tcb->snd.nxt - tcb->snd.ptr; + if(ssize < n) + n = ssize; + tcb->resent += n; + netlog(f, Logtcp, "rexmit: %I.%d -> %I.%d ptr %lux nxt %lux\n", + s->raddr, s->rport, s->laddr, s->lport, tcb->snd.ptr, tcb->snd.nxt); + tpriv->stats[RetransSegs]++; + } + + tcb->snd.ptr += ssize; + + /* Pull up the send pointer so we can accept acks + * for this window + */ + if(seq_gt(tcb->snd.ptr,tcb->snd.nxt)) + tcb->snd.nxt = tcb->snd.ptr; + + /* Build header, link data and compute cksum */ + switch(version){ + case V4: + tcb->protohdr.tcp4hdr.vihl = IP_VER4; + hbp = htontcp4(&seg, bp, &tcb->protohdr.tcp4hdr, tcb); + if(hbp == nil) { + freeblist(bp); + return; + } + break; + case V6: + tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6; + hbp = htontcp6(&seg, bp, &tcb->protohdr.tcp6hdr, tcb); + if(hbp == nil) { + freeblist(bp); + return; + } + break; + default: + hbp = nil; /* to suppress a warning */ + panic("tcpoutput: version %d", version); + } + + /* Start the transmission timers if there is new data and we + * expect acknowledges + */ + if(ssize != 0){ + if(tcb->timer.state != TcptimerON) + tcpgo(tpriv, &tcb->timer); + + /* If round trip timer isn't running, start it. + * measure the longest packet only in case the + * transmission time dominates RTT + */ + if(tcb->rtt_timer.state != TcptimerON) + if(ssize == tcb->mss) { + tcpgo(tpriv, &tcb->rtt_timer); + tcb->rttseq = tcb->snd.ptr; + } + } + + tpriv->stats[OutSegs]++; + + /* put off the next keep alive */ + tcpgo(tpriv, &tcb->katimer); + + switch(version){ + case V4: + if(ipoput4(f, hbp, 0, s->ttl, s->tos, s) < 0){ + /* a negative return means no route */ + localclose(s, "no route"); + } + break; + case V6: + if(ipoput6(f, hbp, 0, s->ttl, s->tos, s) < 0){ + /* a negative return means no route */ + localclose(s, "no route"); + } + break; + default: + panic("tcpoutput2: version %d", version); + } + if((msgs%4) == 1){ + qunlock(s); + sched(); + qlock(s); + } + } +} + +/* + * the BSD convention (hack?) for keep alives. resend last uchar acked. + */ +void +tcpsendka(Conv *s) +{ + Tcp seg; + Tcpctl *tcb; + Block *hbp,*dbp; + + tcb = (Tcpctl*)s->ptcl; + + dbp = nil; + seg.urg = 0; + seg.source = s->lport; + seg.dest = s->rport; + seg.flags = ACK|PSH; + seg.mss = 0; + seg.ws = 0; + if(tcpporthogdefense) + seg.seq = tcb->snd.una-(1<<30)-nrand(1<<20); + else + seg.seq = tcb->snd.una-1; + seg.ack = tcb->rcv.nxt; + tcb->rcv.una = 0; + seg.wnd = tcb->rcv.wnd; + if(tcb->state == Finwait2){ + seg.flags |= FIN; + } else { + dbp = allocb(1); + dbp->wp++; + } + + if(isv4(s->raddr)) { + /* Build header, link data and compute cksum */ + tcb->protohdr.tcp4hdr.vihl = IP_VER4; + hbp = htontcp4(&seg, dbp, &tcb->protohdr.tcp4hdr, tcb); + if(hbp == nil) { + freeblist(dbp); + return; + } + ipoput4(s->p->f, hbp, 0, s->ttl, s->tos, s); + } + else { + /* Build header, link data and compute cksum */ + tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6; + hbp = htontcp6(&seg, dbp, &tcb->protohdr.tcp6hdr, tcb); + if(hbp == nil) { + freeblist(dbp); + return; + } + ipoput6(s->p->f, hbp, 0, s->ttl, s->tos, s); + } +} + +/* + * set connection to time out after 12 minutes + */ +void +tcpsetkacounter(Tcpctl *tcb) +{ + tcb->kacounter = (12 * 60 * 1000) / (tcb->katimer.start*MSPTICK); + if(tcb->kacounter < 3) + tcb->kacounter = 3; +} + +/* + * if we've timed out, close the connection + * otherwise, send a keepalive and restart the timer + */ +void +tcpkeepalive(void *v) +{ + Tcpctl *tcb; + Conv *s; + + s = v; + tcb = (Tcpctl*)s->ptcl; + if(waserror()){ + qunlock(s); + nexterror(); + } + qlock(s); + if(tcb->state != Closed){ + if(--(tcb->kacounter) <= 0) { + localclose(s, Etimedout); + } else { + tcpsendka(s); + tcpgo(s->p->priv, &tcb->katimer); + } + } + qunlock(s); + poperror(); +} + +/* + * start keepalive timer + */ +char* +tcpstartka(Conv *s, char **f, int n) +{ + Tcpctl *tcb; + int x; + + tcb = (Tcpctl*)s->ptcl; + if(tcb->state != Established) + return "connection must be in Establised state"; + if(n > 1){ + x = atoi(f[1]); + if(x >= MSPTICK) + tcb->katimer.start = x/MSPTICK; + } + tcpsetkacounter(tcb); + tcpgo(s->p->priv, &tcb->katimer); + + return nil; +} + +/* + * turn checksums on/off + */ +char* +tcpsetchecksum(Conv *s, char **f, int) +{ + Tcpctl *tcb; + + tcb = (Tcpctl*)s->ptcl; + tcb->nochecksum = !atoi(f[1]); + + return nil; +} + +void +tcprxmit(Conv *s) +{ + Tcpctl *tcb; + + tcb = (Tcpctl*)s->ptcl; + + tcb->flags |= RETRAN|FORCE; + tcb->snd.ptr = tcb->snd.una; + + /* + * We should be halving the slow start threshhold (down to one + * mss) but leaving it at mss seems to work well enough + */ + tcb->ssthresh = tcb->mss; + + /* + * pull window down to a single packet + */ + tcb->cwind = tcb->mss; + tcpoutput(s); +} + +void +tcptimeout(void *arg) +{ + Conv *s; + Tcpctl *tcb; + int maxback; + Tcppriv *tpriv; + + s = (Conv*)arg; + tpriv = s->p->priv; + tcb = (Tcpctl*)s->ptcl; + + if(waserror()){ + qunlock(s); + nexterror(); + } + qlock(s); + switch(tcb->state){ + default: + tcb->backoff++; + if(tcb->state == Syn_sent) + maxback = MAXBACKMS/2; + else + maxback = MAXBACKMS; + tcb->backedoff += tcb->timer.start * MSPTICK; + if(tcb->backedoff >= maxback) { + localclose(s, Etimedout); + break; + } + netlog(s->p->f, Logtcprxmt, "timeout rexmit 0x%lux %d/%d\n", tcb->snd.una, tcb->timer.start, NOW); + tcpsettimer(tcb); + tcprxmit(s); + tpriv->stats[RetransTimeouts]++; + tcb->snd.dupacks = 0; + break; + case Time_wait: + localclose(s, nil); + break; + case Closed: + break; + } + qunlock(s); + poperror(); +} + +int +inwindow(Tcpctl *tcb, int seq) +{ + return seq_within(seq, tcb->rcv.nxt, tcb->rcv.nxt+tcb->rcv.wnd-1); +} + +/* + * set up state for a received SYN (or SYN ACK) packet + */ +void +procsyn(Conv *s, Tcp *seg) +{ + Tcpctl *tcb; + + tcb = (Tcpctl*)s->ptcl; + tcb->flags |= FORCE; + + tcb->rcv.nxt = seg->seq + 1; + tcb->rcv.urg = tcb->rcv.nxt; + tcb->irs = seg->seq; + + /* our sending max segment size cannot be bigger than what he asked for */ + if(seg->mss != 0 && seg->mss < tcb->mss) + tcb->mss = seg->mss; + + /* the congestion window always starts out as a single segment */ + tcb->snd.wnd = seg->wnd; + tcb->cwind = tcb->mss; +} + +int +addreseq(Tcpctl *tcb, Tcppriv *tpriv, Tcp *seg, Block *bp, ushort length) +{ + Reseq *rp, *rp1; + int i; + static int once; + + rp = malloc(sizeof(Reseq)); + if(rp == nil){ + freeblist(bp); /* bp always consumed by add_reseq */ + return 0; + } + + rp->seg = *seg; + rp->bp = bp; + rp->length = length; + + /* Place on reassembly list sorting by starting seq number */ + rp1 = tcb->reseq; + if(rp1 == nil || seq_lt(seg->seq, rp1->seg.seq)) { + rp->next = rp1; + tcb->reseq = rp; + if(rp->next != nil) + tpriv->stats[OutOfOrder]++; + return 0; + } + + length = 0; + for(i = 0;; i++) { + length += rp1->length; + if(rp1->next == nil || seq_lt(seg->seq, rp1->next->seg.seq)) { + rp->next = rp1->next; + rp1->next = rp; + if(rp->next != nil) + tpriv->stats[OutOfOrder]++; + break; + } + rp1 = rp1->next; + } + if(length > QMAX && once++ == 0){ + print("very long tcp resequence queue: %d\n", length); + for(rp1 = tcb->reseq, i = 0; i < 10 && rp1 != nil; rp1 = rp1->next, i++) + print("0x%lux 0x%lux 0x%ux\n", rp1->seg.seq, rp1->seg.ack, + rp1->seg.flags); + return -1; + } + return 0; +} + +void +getreseq(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length) +{ + Reseq *rp; + + rp = tcb->reseq; + if(rp == nil) + return; + + tcb->reseq = rp->next; + + *seg = rp->seg; + *bp = rp->bp; + *length = rp->length; + + free(rp); +} + +int +tcptrim(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length) +{ + ushort len; + uchar accept; + int dupcnt, excess; + + accept = 0; + len = *length; + if(seg->flags & SYN) + len++; + if(seg->flags & FIN) + len++; + + if(tcb->rcv.wnd == 0) { + if(len == 0 && seg->seq == tcb->rcv.nxt) + return 0; + } + else { + /* Some part of the segment should be in the window */ + if(inwindow(tcb,seg->seq)) + accept++; + else + if(len != 0) { + if(inwindow(tcb, seg->seq+len-1) || + seq_within(tcb->rcv.nxt, seg->seq,seg->seq+len-1)) + accept++; + } + } + if(!accept) { + freeblist(*bp); + return -1; + } + dupcnt = tcb->rcv.nxt - seg->seq; + if(dupcnt > 0){ + tcb->rerecv += dupcnt; + if(seg->flags & SYN){ + seg->flags &= ~SYN; + seg->seq++; + + if(seg->urg > 1) + seg->urg--; + else + seg->flags &= ~URG; + dupcnt--; + } + if(dupcnt > 0){ + pullblock(bp, (ushort)dupcnt); + seg->seq += dupcnt; + *length -= dupcnt; + + if(seg->urg > dupcnt) + seg->urg -= dupcnt; + else { + seg->flags &= ~URG; + seg->urg = 0; + } + } + } + excess = seg->seq + *length - (tcb->rcv.nxt + tcb->rcv.wnd); + if(excess > 0) { + tcb->rerecv += excess; + *length -= excess; + *bp = trimblock(*bp, 0, *length); + if(*bp == nil) + panic("presotto is a boofhead"); + seg->flags &= ~FIN; + } + return 0; +} + +void +tcpadvise(Proto *tcp, Block *bp, char *msg) +{ + Tcp4hdr *h4; + Tcp6hdr *h6; + Tcpctl *tcb; + uchar source[IPaddrlen]; + uchar dest[IPaddrlen]; + ushort psource, pdest; + Conv *s, **p; + + h4 = (Tcp4hdr*)(bp->rp); + h6 = (Tcp6hdr*)(bp->rp); + + if((h4->vihl&0xF0)==IP_VER4) { + v4tov6(dest, h4->tcpdst); + v4tov6(source, h4->tcpsrc); + psource = nhgets(h4->tcpsport); + pdest = nhgets(h4->tcpdport); + } + else { + ipmove(dest, h6->tcpdst); + ipmove(source, h6->tcpsrc); + psource = nhgets(h6->tcpsport); + pdest = nhgets(h6->tcpdport); + } + + /* Look for a connection */ + qlock(tcp); + for(p = tcp->conv; *p; p++) { + s = *p; + tcb = (Tcpctl*)s->ptcl; + if(s->rport == pdest) + if(s->lport == psource) + if(tcb->state != Closed) + if(ipcmp(s->raddr, dest) == 0) + if(ipcmp(s->laddr, source) == 0){ + qlock(s); + qunlock(tcp); + switch(tcb->state){ + case Syn_sent: + localclose(s, msg); + break; + } + qunlock(s); + freeblist(bp); + return; + } + } + qunlock(tcp); + freeblist(bp); +} + +static char* +tcpporthogdefensectl(char *val) +{ + if(strcmp(val, "on") == 0) + tcpporthogdefense = 1; + else if(strcmp(val, "off") == 0) + tcpporthogdefense = 0; + else + return "unknown value for tcpporthogdefense"; + return nil; +} + +/* called with c qlocked */ +char* +tcpctl(Conv* c, char** f, int n) +{ + if(n == 1 && strcmp(f[0], "hangup") == 0) + return tcphangup(c); + if(n >= 1 && strcmp(f[0], "keepalive") == 0) + return tcpstartka(c, f, n); + if(n >= 1 && strcmp(f[0], "checksum") == 0) + return tcpsetchecksum(c, f, n); + if(n >= 1 && strcmp(f[0], "tcpporthogdefense") == 0) + return tcpporthogdefensectl(f[1]); + return "unknown control request"; +} + +int +tcpstats(Proto *tcp, char *buf, int len) +{ + Tcppriv *priv; + char *p, *e; + int i; + + priv = tcp->priv; + p = buf; + e = p+len; + for(i = 0; i < Nstats; i++) + p = seprint(p, e, "%s: %lud\n", statnames[i], priv->stats[i]); + return p - buf; +} + +/* + * garbage collect any stale conversations: + * - SYN received but no SYN-ACK after 5 seconds (could be the SYN attack) + * - Finwait2 after 5 minutes + * + * this is called whenever we run out of channels. Both checks are + * of questionable validity so we try to use them only when we're + * up against the wall. + */ +int +tcpgc(Proto *tcp) +{ + Conv *c, **pp, **ep; + int n; + Tcpctl *tcb; + + + n = 0; + ep = &tcp->conv[tcp->nc]; + for(pp = tcp->conv; pp < ep; pp++) { + c = *pp; + if(c == nil) + break; + if(!canqlock(c)) + continue; + tcb = (Tcpctl*)c->ptcl; + switch(tcb->state){ + case Syn_received: + if(NOW - tcb->time > 5000){ + localclose(c, "timed out"); + n++; + } + break; + case Finwait2: + if(NOW - tcb->time > 5*60*1000){ + localclose(c, "timed out"); + n++; + } + break; + } + qunlock(c); + } + return n; +} + +void +tcpsettimer(Tcpctl *tcb) +{ + int x; + + /* round trip dependency */ + x = backoff(tcb->backoff) * + (tcb->mdev + (tcb->srtt>>LOGAGAIN) + MSPTICK) / MSPTICK; + + /* bounded twixt 1/2 and 64 seconds */ + if(x < 500/MSPTICK) + x = 500/MSPTICK; + else if(x > (64000/MSPTICK)) + x = 64000/MSPTICK; + tcb->timer.start = x; +} + +void +tcpinit(Fs *fs) +{ + Proto *tcp; + Tcppriv *tpriv; + + tcp = smalloc(sizeof(Proto)); + tpriv = tcp->priv = smalloc(sizeof(Tcppriv)); + tcp->name = "tcp"; + tcp->connect = tcpconnect; + tcp->announce = tcpannounce; + tcp->ctl = tcpctl; + tcp->state = tcpstate; + tcp->create = tcpcreate; + tcp->close = tcpclose; + tcp->rcv = tcpiput; + tcp->advise = tcpadvise; + tcp->stats = tcpstats; + tcp->inuse = tcpinuse; + tcp->gc = tcpgc; + tcp->ipproto = IP_TCPPROTO; + tcp->nc = scalednconv(); + tcp->ptclsize = sizeof(Tcpctl); + tpriv->stats[MaxConn] = tcp->nc; + + Fsproto(fs, tcp); +} + +void +tcpsetscale(Conv *s, Tcpctl *tcb, ushort rcvscale, ushort sndscale) +{ + if(rcvscale){ + tcb->rcv.scale = rcvscale & 0xff; + tcb->snd.scale = sndscale & 0xff; + tcb->window = QMAX<snd.scale; + qsetlimit(s->rq, tcb->window); + } else { + tcb->rcv.scale = 0; + tcb->snd.scale = 0; + tcb->window = QMAX; + qsetlimit(s->rq, tcb->window); + } +} diff --git a/os/ip/udp.c b/os/ip/udp.c new file mode 100644 index 00000000..89cfbffb --- /dev/null +++ b/os/ip/udp.c @@ -0,0 +1,649 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" +#include "ipv6.h" + + +#define DPRINT if(0)print + +enum +{ + UDP_UDPHDR_SZ = 8, + + UDP4_PHDR_OFF = 8, + UDP4_PHDR_SZ = 12, + UDP4_IPHDR_SZ = 20, + UDP6_IPHDR_SZ = 40, + UDP6_PHDR_SZ = 40, + UDP6_PHDR_OFF = 0, + + IP_UDPPROTO = 17, + UDP_USEAD7 = 52, + UDP_USEAD6 = 36, + + Udprxms = 200, + Udptickms = 100, + Udpmaxxmit = 10, +}; + +typedef struct Udp4hdr Udp4hdr; +struct Udp4hdr +{ + /* ip header */ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar Unused; + uchar udpproto; /* Protocol */ + uchar udpplen[2]; /* Header plus data length */ + uchar udpsrc[IPv4addrlen]; /* Ip source */ + uchar udpdst[IPv4addrlen]; /* Ip destination */ + + /* udp header */ + uchar udpsport[2]; /* Source port */ + uchar udpdport[2]; /* Destination port */ + uchar udplen[2]; /* data length */ + uchar udpcksum[2]; /* Checksum */ +}; + +typedef struct Udp6hdr Udp6hdr; +struct Udp6hdr { + uchar viclfl[4]; + uchar len[2]; + uchar nextheader; + uchar hoplimit; + uchar udpsrc[IPaddrlen]; + uchar udpdst[IPaddrlen]; + + /* udp header */ + uchar udpsport[2]; /* Source port */ + uchar udpdport[2]; /* Destination port */ + uchar udplen[2]; /* data length */ + uchar udpcksum[2]; /* Checksum */ +}; + +/* MIB II counters */ +typedef struct Udpstats Udpstats; +struct Udpstats +{ + ulong udpInDatagrams; + ulong udpNoPorts; + ulong udpInErrors; + ulong udpOutDatagrams; +}; + +typedef struct Udppriv Udppriv; +struct Udppriv +{ + Ipht ht; + + /* MIB counters */ + Udpstats ustats; + + /* non-MIB stats */ + ulong csumerr; /* checksum errors */ + ulong lenerr; /* short packet */ +}; + +void (*etherprofiler)(char *name, int qlen); +void udpkick(void *x, Block *bp); + +/* + * protocol specific part of Conv + */ +typedef struct Udpcb Udpcb; +struct Udpcb +{ + QLock; + uchar headers; +}; + +static char* +udpconnect(Conv *c, char **argv, int argc) +{ + char *e; + Udppriv *upriv; + + upriv = c->p->priv; + e = Fsstdconnect(c, argv, argc); + Fsconnected(c, e); + if(e != nil) + return e; + + iphtadd(&upriv->ht, c); + return nil; +} + + +static int +udpstate(Conv *c, char *state, int n) +{ + return snprint(state, n, "%s qin %d qout %d", + c->inuse ? "Open" : "Closed", + c->rq ? qlen(c->rq) : 0, + c->wq ? qlen(c->wq) : 0 + ); +} + +static char* +udpannounce(Conv *c, char** argv, int argc) +{ + char *e; + Udppriv *upriv; + + upriv = c->p->priv; + e = Fsstdannounce(c, argv, argc); + if(e != nil) + return e; + Fsconnected(c, nil); + iphtadd(&upriv->ht, c); + + return nil; +} + +static void +udpcreate(Conv *c) +{ + c->rq = qopen(64*1024, Qmsg, 0, 0); + c->wq = qbypass(udpkick, c); +} + +static void +udpclose(Conv *c) +{ + Udpcb *ucb; + Udppriv *upriv; + + upriv = c->p->priv; + iphtrem(&upriv->ht, c); + + c->state = 0; + qclose(c->rq); + qclose(c->wq); + qclose(c->eq); + ipmove(c->laddr, IPnoaddr); + ipmove(c->raddr, IPnoaddr); + c->lport = 0; + c->rport = 0; + + ucb = (Udpcb*)c->ptcl; + ucb->headers = 0; + + qunlock(c); +} + +void +udpkick(void *x, Block *bp) +{ + Conv *c = x; + Udp4hdr *uh4; + Udp6hdr *uh6; + ushort rport; + uchar laddr[IPaddrlen], raddr[IPaddrlen]; + Udpcb *ucb; + int dlen, ptcllen; + Udppriv *upriv; + Fs *f; + int version; + Conv *rc; + + upriv = c->p->priv; + f = c->p->f; + + netlog(c->p->f, Logudp, "udp: kick\n"); + if(bp == nil) + return; + + ucb = (Udpcb*)c->ptcl; + switch(ucb->headers) { + case 7: + /* get user specified addresses */ + bp = pullupblock(bp, UDP_USEAD7); + if(bp == nil) + return; + ipmove(raddr, bp->rp); + bp->rp += IPaddrlen; + ipmove(laddr, bp->rp); + bp->rp += IPaddrlen; + /* pick interface closest to dest */ + if(ipforme(f, laddr) != Runi) + findlocalip(f, laddr, raddr); + bp->rp += IPaddrlen; /* Ignore ifc address */ + rport = nhgets(bp->rp); + bp->rp += 2+2; /* Ignore local port */ + break; + case 6: + /* get user specified addresses */ + bp = pullupblock(bp, UDP_USEAD6); + if(bp == nil) + return; + ipmove(raddr, bp->rp); + bp->rp += IPaddrlen; + ipmove(laddr, bp->rp); + bp->rp += IPaddrlen; + /* pick interface closest to dest */ + if(ipforme(f, laddr) != Runi) + findlocalip(f, laddr, raddr); + rport = nhgets(bp->rp); + bp->rp += 2+2; /* Ignore local port */ + break; + default: + rport = 0; + break; + } + + if(ucb->headers) { + if(memcmp(laddr, v4prefix, IPv4off) == 0 || + ipcmp(laddr, IPnoaddr) == 0) + version = V4; + else + version = V6; + } else { + if( (memcmp(c->raddr, v4prefix, IPv4off) == 0 && + memcmp(c->laddr, v4prefix, IPv4off) == 0) + || ipcmp(c->raddr, IPnoaddr) == 0) + version = V4; + else + version = V6; + } + + dlen = blocklen(bp); + + /* fill in pseudo header and compute checksum */ + switch(version){ + case V4: + bp = padblock(bp, UDP4_IPHDR_SZ+UDP_UDPHDR_SZ); + if(bp == nil) + return; + + uh4 = (Udp4hdr *)(bp->rp); + ptcllen = dlen + UDP_UDPHDR_SZ; + uh4->Unused = 0; + uh4->udpproto = IP_UDPPROTO; + uh4->frag[0] = 0; + uh4->frag[1] = 0; + hnputs(uh4->udpplen, ptcllen); + if(ucb->headers) { + v6tov4(uh4->udpdst, raddr); + hnputs(uh4->udpdport, rport); + v6tov4(uh4->udpsrc, laddr); + rc = nil; + } else { + v6tov4(uh4->udpdst, c->raddr); + hnputs(uh4->udpdport, c->rport); + if(ipcmp(c->laddr, IPnoaddr) == 0) + findlocalip(f, c->laddr, c->raddr); + v6tov4(uh4->udpsrc, c->laddr); + rc = c; + } + hnputs(uh4->udpsport, c->lport); + hnputs(uh4->udplen, ptcllen); + uh4->udpcksum[0] = 0; + uh4->udpcksum[1] = 0; + hnputs(uh4->udpcksum, + ptclcsum(bp, UDP4_PHDR_OFF, dlen+UDP_UDPHDR_SZ+UDP4_PHDR_SZ)); + uh4->vihl = IP_VER4; + ipoput4(f, bp, 0, c->ttl, c->tos, rc); + break; + + case V6: + bp = padblock(bp, UDP6_IPHDR_SZ+UDP_UDPHDR_SZ); + if(bp == nil) + return; + + // using the v6 ip header to create pseudo header + // first then reset it to the normal ip header + uh6 = (Udp6hdr *)(bp->rp); + memset(uh6, 0, 8); + ptcllen = dlen + UDP_UDPHDR_SZ; + hnputl(uh6->viclfl, ptcllen); + uh6->hoplimit = IP_UDPPROTO; + if(ucb->headers) { + ipmove(uh6->udpdst, raddr); + hnputs(uh6->udpdport, rport); + ipmove(uh6->udpsrc, laddr); + rc = nil; + } else { + ipmove(uh6->udpdst, c->raddr); + hnputs(uh6->udpdport, c->rport); + if(ipcmp(c->laddr, IPnoaddr) == 0) + findlocalip(f, c->laddr, c->raddr); + ipmove(uh6->udpsrc, c->laddr); + rc = c; + } + hnputs(uh6->udpsport, c->lport); + hnputs(uh6->udplen, ptcllen); + uh6->udpcksum[0] = 0; + uh6->udpcksum[1] = 0; + hnputs(uh6->udpcksum, + ptclcsum(bp, UDP6_PHDR_OFF, dlen+UDP_UDPHDR_SZ+UDP6_PHDR_SZ)); + memset(uh6, 0, 8); + uh6->viclfl[0] = IP_VER6; + hnputs(uh6->len, ptcllen); + uh6->nextheader = IP_UDPPROTO; + ipoput6(f, bp, 0, c->ttl, c->tos, rc); + break; + + default: + panic("udpkick: version %d", version); + } + upriv->ustats.udpOutDatagrams++; +} + +void +udpiput(Proto *udp, Ipifc *ifc, Block *bp) +{ + int len; + Udp4hdr *uh4; + Udp6hdr *uh6; + Conv *c; + Udpcb *ucb; + uchar raddr[IPaddrlen], laddr[IPaddrlen]; + ushort rport, lport; + Udppriv *upriv; + Fs *f; + int version; + int ottl, oviclfl, olen; + uchar *p; + + upriv = udp->priv; + f = udp->f; + upriv->ustats.udpInDatagrams++; + + uh4 = (Udp4hdr*)(bp->rp); + version = ((uh4->vihl&0xF0)==IP_VER6) ? V6 : V4; + + /* + * Put back pseudo header for checksum + * (remember old values for icmpnoconv()) + */ + switch(version) { + case V4: + ottl = uh4->Unused; + uh4->Unused = 0; + len = nhgets(uh4->udplen); + olen = nhgets(uh4->udpplen); + hnputs(uh4->udpplen, len); + + v4tov6(raddr, uh4->udpsrc); + v4tov6(laddr, uh4->udpdst); + lport = nhgets(uh4->udpdport); + rport = nhgets(uh4->udpsport); + + if(nhgets(uh4->udpcksum)) { + if(ptclcsum(bp, UDP4_PHDR_OFF, len+UDP4_PHDR_SZ)) { + upriv->ustats.udpInErrors++; + netlog(f, Logudp, "udp: checksum error %I\n", raddr); + DPRINT("udp: checksum error %I\n", raddr); + freeblist(bp); + return; + } + } + uh4->Unused = ottl; + hnputs(uh4->udpplen, olen); + break; + case V6: + uh6 = (Udp6hdr*)(bp->rp); + len = nhgets(uh6->udplen); + oviclfl = nhgetl(uh6->viclfl); + olen = nhgets(uh6->len); + ottl = uh6->hoplimit; + ipmove(raddr, uh6->udpsrc); + ipmove(laddr, uh6->udpdst); + lport = nhgets(uh6->udpdport); + rport = nhgets(uh6->udpsport); + memset(uh6, 0, 8); + hnputl(uh6->viclfl, len); + uh6->hoplimit = IP_UDPPROTO; + if(ptclcsum(bp, UDP6_PHDR_OFF, len+UDP6_PHDR_SZ)) { + upriv->ustats.udpInErrors++; + netlog(f, Logudp, "udp: checksum error %I\n", raddr); + DPRINT("udp: checksum error %I\n", raddr); + freeblist(bp); + return; + } + hnputl(uh6->viclfl, oviclfl); + hnputs(uh6->len, olen); + uh6->nextheader = IP_UDPPROTO; + uh6->hoplimit = ottl; + break; + default: + panic("udpiput: version %d", version); + return; /* to avoid a warning */ + } + + qlock(udp); + + c = iphtlook(&upriv->ht, raddr, rport, laddr, lport); + if(c == nil){ + /* no converstation found */ + upriv->ustats.udpNoPorts++; + qunlock(udp); + netlog(f, Logudp, "udp: no conv %I!%d -> %I!%d\n", raddr, rport, + laddr, lport); + + switch(version){ + case V4: + icmpnoconv(f, bp); + break; + case V6: + icmphostunr(f, ifc, bp, icmp6_port_unreach, 0); + break; + default: + panic("udpiput2: version %d", version); + } + + freeblist(bp); + return; + } + ucb = (Udpcb*)c->ptcl; + + if(c->state == Announced){ + if(ucb->headers == 0){ + /* create a new conversation */ + if(ipforme(f, laddr) != Runi) { + switch(version){ + case V4: + v4tov6(laddr, ifc->lifc->local); + break; + case V6: + ipmove(laddr, ifc->lifc->local); + break; + default: + panic("udpiput3: version %d", version); + } + } + c = Fsnewcall(c, raddr, rport, laddr, lport, version); + if(c == nil){ + qunlock(udp); + freeblist(bp); + return; + } + iphtadd(&upriv->ht, c); + ucb = (Udpcb*)c->ptcl; + } + } + + qlock(c); + qunlock(udp); + + /* + * Trim the packet down to data size + */ + len -= UDP_UDPHDR_SZ; + switch(version){ + case V4: + bp = trimblock(bp, UDP4_IPHDR_SZ+UDP_UDPHDR_SZ, len); + break; + case V6: + bp = trimblock(bp, UDP6_IPHDR_SZ+UDP_UDPHDR_SZ, len); + break; + default: + bp = nil; + panic("udpiput4: version %d", version); + } + if(bp == nil){ + qunlock(c); + netlog(f, Logudp, "udp: len err %I.%d -> %I.%d\n", raddr, rport, + laddr, lport); + upriv->lenerr++; + return; + } + + netlog(f, Logudpmsg, "udp: %I.%d -> %I.%d l %d\n", raddr, rport, + laddr, lport, len); + + switch(ucb->headers){ + case 7: + /* pass the src address */ + bp = padblock(bp, UDP_USEAD7); + p = bp->rp; + ipmove(p, raddr); p += IPaddrlen; + ipmove(p, laddr); p += IPaddrlen; + ipmove(p, ifc->lifc->local); p += IPaddrlen; + hnputs(p, rport); p += 2; + hnputs(p, lport); + break; + case 6: + /* pass the src address */ + bp = padblock(bp, UDP_USEAD6); + p = bp->rp; + ipmove(p, raddr); p += IPaddrlen; + ipmove(p, ipforme(f, laddr)==Runi ? laddr : ifc->lifc->local); p += IPaddrlen; + hnputs(p, rport); p += 2; + hnputs(p, lport); + break; + } + + if(bp->next) + bp = concatblock(bp); + + if(qfull(c->rq)){ + qunlock(c); + netlog(f, Logudp, "udp: qfull %I.%d -> %I.%d\n", raddr, rport, + laddr, lport); + freeblist(bp); + return; + } + + qpass(c->rq, bp); + qunlock(c); + +} + +char* +udpctl(Conv *c, char **f, int n) +{ + Udpcb *ucb; + + ucb = (Udpcb*)c->ptcl; + if(n == 1){ + if(strcmp(f[0], "oldheaders") == 0){ + ucb->headers = 6; + return nil; + } else if(strcmp(f[0], "headers") == 0){ + ucb->headers = 7; + return nil; + } + } + return "unknown control request"; +} + +void +udpadvise(Proto *udp, Block *bp, char *msg) +{ + Udp4hdr *h4; + Udp6hdr *h6; + uchar source[IPaddrlen], dest[IPaddrlen]; + ushort psource, pdest; + Conv *s, **p; + int version; + + h4 = (Udp4hdr*)(bp->rp); + version = ((h4->vihl&0xF0)==IP_VER6) ? V6 : V4; + + switch(version) { + case V4: + v4tov6(dest, h4->udpdst); + v4tov6(source, h4->udpsrc); + psource = nhgets(h4->udpsport); + pdest = nhgets(h4->udpdport); + break; + case V6: + h6 = (Udp6hdr*)(bp->rp); + ipmove(dest, h6->udpdst); + ipmove(source, h6->udpsrc); + psource = nhgets(h6->udpsport); + pdest = nhgets(h6->udpdport); + break; + default: + panic("udpadvise: version %d", version); + return; /* to avoid a warning */ + } + + /* Look for a connection */ + qlock(udp); + for(p = udp->conv; *p; p++) { + s = *p; + if(s->rport == pdest) + if(s->lport == psource) + if(ipcmp(s->raddr, dest) == 0) + if(ipcmp(s->laddr, source) == 0){ + if(s->ignoreadvice) + break; + qlock(s); + qunlock(udp); + qhangup(s->rq, msg); + qhangup(s->wq, msg); + qunlock(s); + freeblist(bp); + return; + } + } + qunlock(udp); + freeblist(bp); +} + +int +udpstats(Proto *udp, char *buf, int len) +{ + Udppriv *upriv; + + upriv = udp->priv; + return snprint(buf, len, "InDatagrams: %lud\nNoPorts: %lud\nInErrors: %lud\nOutDatagrams: %lud\n", + upriv->ustats.udpInDatagrams, + upriv->ustats.udpNoPorts, + upriv->ustats.udpInErrors, + upriv->ustats.udpOutDatagrams); +} + +void +udpinit(Fs *fs) +{ + Proto *udp; + + udp = smalloc(sizeof(Proto)); + udp->priv = smalloc(sizeof(Udppriv)); + udp->name = "udp"; + udp->connect = udpconnect; + udp->announce = udpannounce; + udp->ctl = udpctl; + udp->state = udpstate; + udp->create = udpcreate; + udp->close = udpclose; + udp->rcv = udpiput; + udp->advise = udpadvise; + udp->stats = udpstats; + udp->ipproto = IP_UDPPROTO; + udp->nc = Nchans; + udp->ptclsize = sizeof(Udpcb); + + Fsproto(fs, udp); +} diff --git a/os/ipaq1110/Mk b/os/ipaq1110/Mk new file mode 100755 index 00000000..51d69a44 --- /dev/null +++ b/os/ipaq1110/Mk @@ -0,0 +1,8 @@ +#!/bin/rc +rfork ne +ROOT=/usr/inferno +fn cd +NPROC=3 +path=(/usr/inferno/Plan9/$cputype/bin $path) +#bind /usr/inferno/mkconfig.dist /usr/inferno/mkconfig +exec mk $* diff --git a/os/ipaq1110/NOTICE b/os/ipaq1110/NOTICE new file mode 100644 index 00000000..11f5df58 --- /dev/null +++ b/os/ipaq1110/NOTICE @@ -0,0 +1,6 @@ +Inferno® Copyright © 1996-1999 Lucent Technologies Inc. All rights reserved. +iPAQ 36xx Inferno port Copyright © 2000-2003 Vita Nuova Holdings Limited. All rights reserved. + +This software is subject to the Vita Nuova Liberal Source Licence, except for the following: + devaudio.c came from Plan 9 and is covered by the Lucent Public License 1.02 + The etherwavelan.c driver was contributed to Plan 9 by nemo@gsyc.escet.urjc.es diff --git a/os/ipaq1110/README b/os/ipaq1110/README new file mode 100644 index 00000000..7639e96a --- /dev/null +++ b/os/ipaq1110/README @@ -0,0 +1,65 @@ +Installing 4th Edition Inferno on an IPAQ (21 August 2003) + +This Inferno kernel software is only for the iPAQ 36xx, +colour models only. + +Of course, since it's Inferno, the existing +applications, and yours, will run (give or take kernel bugs). +Suspend/resume is implemented except for PCMCIA. +(We are doing the work to make it more general.) +Otherwise the system tries to conserve power in the usual +ways by going into idle to wait for interrupts and powering +devices up and down on open and close. + +The following describes loading the Inferno kernel in to the iPAQ. +This preliminary version has some things hard-wired in to the code +to run on our wavelan network (see the end of archipaq.c). + +os/init/ipaqinit.b will support a file system (currently dossrv for simplicity) +running over a Flash Translation Layer on the iPAQ flash. +Setting up a local file system and loading that onto the iPAQ +is not discussed here. A separate package will deal with that. + +For development, we generally take the software over the net from +an Inferno file service (ie, svc/net) running in emu. +We are providing this iPAQ kernel package to subscribers earlier, +for the benefit of those subscribers that can make use of it now +(eg, modify the networking setup to suit their own network). +A basic local file system package should be available shortly. +Until it is, if you are not confident you can set up the networking +or prepare a local file system, you should wait. + +1. You must first prepare the iPAQ with the handhelds.org bootloader, +version 2.18.54 (later ones probably work but we haven't yet tried them). +Use the iPAQ H3600 Linux installation instructions: + ftp://ftp.handhelds.org/pub/linux/compaq/ipaq/stable/install.html + +Following that procedure will eliminate Windows/CE from the device: if you will need it +in future, be sure to save the flash images as +described in the handhelds.org instructions. +Note that you will also be trusting that they can get you +back to a working WinCE machine, so be sure to read the handhelds.org +documents thoroughly in that regard. + +2. At the end, the instructions say ``At this point you have a working +bootloader and you are ready to install a Linux distribution''. +You can install Inferno instead, or several other systems, and +you can later install them instead of Inferno, since they all use +the same boot loader. + +3. A ready-made Inferno kernel is in the file k.gz in this directory +(os/ipaq1110/k.gz in the Inferno structure). With a 115k serial connection +to the bootloader established, as described for loading Linux, +when you tell the bootloader to `load kernel', send k.gz as the +data file (using the XMODEM protocol as described by Handhelds.org). + +4. Once the system is running, you can update kernels using Inferno, +when your file system is taken as `remote' over a wireless connection. +In an Inferno shell on the device (perhaps using the console serial port +as a keyboard): + bind -a '#F' /dev + echo erase all >/dev/flash/kernelctl + dd /dev/flash/kernel +or just run + /os/ipaq1110/upd +which does that. diff --git a/os/ipaq1110/archipaq.c b/os/ipaq1110/archipaq.c new file mode 100644 index 00000000..0e3c726b --- /dev/null +++ b/os/ipaq1110/archipaq.c @@ -0,0 +1,376 @@ +/* + * ipaq + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" +#include "draw.h" +#include +#include "screen.h" + +#include "../port/netif.h" +#include "etherif.h" +#include "../port/flashif.h" + +#define EGPIOADDR 0x49000000 /* physical and virtual address of write only register in CS5 space */ + +static ulong egpiocopy; + +static void +egpiosc(ulong set, ulong clr) +{ + int s; + + s = splhi(); + egpiocopy = (egpiocopy & ~clr) | set; + *(ulong*)EGPIOADDR = egpiocopy; + splx(s); +} + +void +archreset(void) +{ + GpioReg *g; + PpcReg *p; + + g = GPIOREG; + g->grer = 0; + g->gfer = 0; + g->gedr = g->gedr; + g->gpcr = ~0; + g->gpdr = GPIO_CLK_SET0_o | GPIO_CLK_SET1_o; // | GPIO_LDD8_15_o; + g->gafr |= GPIO_SYS_CLK_i; // | GPIO_LDD8_15_o; + p = PPCREG; + p->ppdr |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; /* not sure about PPC_TXD4 here */ + p->ppsr &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + + archuartpower(3, 1); /* allow console to work sooner rather than later */ + L3init(); +} + +void +archpowerdown(void) +{ + PmgrReg *p = PMGRREG; + + p->pwer = GPIO_PWR_ON_i; /* only power button for now, not RTC */ + p->pcfr = PCFR_opde; /* stop 3.6MHz oscillator, and drive pcmcia and chip select low */ + p->pgsr = 0; /* drive all outputs to zero in sleep */ + PPCREG->psdr = 0; /* all peripheral pins as outputs during sleep */ +} + +void +archpowerup(void) +{ + GpioReg *g; + int i; + + g = GPIOREG; + g->gpdr |= GPIO_COM_RTS_o; /* just in case it's off */ + g->gpsr = GPIO_COM_RTS_o; + *(ulong*)EGPIOADDR = egpiocopy; + for(i=0; i<50*1000; i++) + ; + while((g->gplr & GPIO_PWR_ON_i) == 0) + ; /* wait for it to come up */ +} + +void +archconfinit(void) +{ + int w; + + conf.topofmem = 0xC0000000+32*MB; + w = PMGRREG->ppcr & 0x1f; + m->cpuhz = CLOCKFREQ*(w*4+16); + + conf.useminicache = 1; + conf.portrait = 1; /* should take from param flash or allow dynamic change */ +} + +void +kbdinit(void) +{ + addclock0link(kbdclock, MS2HZ); +} + +static LCDmode lcd320x240x16tft = +{ +// .x = 320, .y = 240, .depth = 16, .hz = 70, +// .pbs = 2, .dual = 0, .mono = 0, .active = 1, +// .hsync_wid = 4-2, .sol_wait = 12-1, .eol_wait = 17-1, +// .vsync_hgt = 3-1, .soft_wait = 10, .eof_wait = 1, +// .lines_per_int = 0, .palette_delay = 0, .acbias_lines = 0, +// .obits = 16, +// .vsynclow = 1, .hsynclow = 1, + 320, 240, 16, 70, + 2, 0, 0, 1, + 4-2, 12-1, 17-1, + 3-1, 10, 1, + 0, 0, 0, + 16, + 1, 1, +}; + +int +archlcdmode(LCDmode *m) +{ + *m = lcd320x240x16tft; + return 0; +} + +void +archlcdenable(int on) +{ + if(on) + egpiosc(EGPIO_LCD_ON|EGPIO_LCD_PCI|EGPIO_LCD_5V_ON|EGPIO_LVDD_ON, 0); + else + egpiosc(0, EGPIO_LCD_ON|EGPIO_LCD_PCI|EGPIO_LCD_5V_ON|EGPIO_LVDD_ON); +} + +void +archconsole(void) +{ + uartspecial(0, 115200, 'n', &kbdq, &printq, kbdcr2nl); +} + +void +archuartpower(int port, int on) +{ + int s; + + if(port != 3) + return; + s = splhi(); + GPIOREG->gpdr |= GPIO_COM_RTS_o; /* should be done elsewhere */ + GPIOREG->gpsr = GPIO_COM_RTS_o; + splx(s); + if(on) + egpiosc(EGPIO_RS232_ON, 0); + else + egpiosc(0, EGPIO_RS232_ON); +} + +void +archreboot(void) +{ + dcflushall(); + GPIOREG->gedr = 1<<0; + mmuputctl(mmugetctl() & ~CpCaltivec); /* restore bootstrap's vectors */ + RESETREG->rsrr = 1; /* software reset */ + for(;;) + spllo(); +} + +void +archflashwp(Flash*, int wp) +{ + if(wp) + egpiosc(0, EGPIO_VPEN); + else + egpiosc(EGPIO_VPEN, 0); +} + +/* + * for ../port/devflash.c:/^flashreset + * retrieve flash type, virtual base and length and return 0; + * return -1 on error (no flash) + */ +int +archflashreset(int bank, Flash *f) +{ + if(bank != 0) + return -1; + f->type = "cfi16"; + f->addr = KADDR(FLASHMEM); + if((MEMCFGREG->msc0 & (1<<2)) == 0){ + f->interleave = 1; + f->width = 4; + }else + f->width = 2; + return 0; +} + +int +archaudiopower(int on) +{ + int s; + + if(on) + egpiosc(EGPIO_CODEC_RESET | EGPIO_AUD_PWR_ON, 0); + else + egpiosc(0, EGPIO_CODEC_RESET | EGPIO_AUD_ON | EGPIO_AUD_PWR_ON | EGPIO_QMUTE); + s = splhi(); + GPIOREG->gafr |= GPIO_SYS_CLK_i; + GPIOREG->gpdr |= GPIO_CLK_SET0_o | GPIO_CLK_SET1_o; + GPIOREG->gpsr = GPIO_CLK_SET0_o; + GPIOREG->gpcr = GPIO_CLK_SET1_o; + splx(s); + return 0; +} + +void +archcodecreset(void) +{ +// egpiosc(0, EGPIO_CODEC_RESET); +// egpiosc(EGPIO_CODEC_RESET, 0); +} + +void +archaudiomute(int on) +{ + if(on) + egpiosc(EGPIO_QMUTE, 0); + else + egpiosc(0, EGPIO_QMUTE); +} + +void +archaudioamp(int on) +{ + if(on) + egpiosc(EGPIO_AUD_ON, 0); + else + egpiosc(0, EGPIO_AUD_ON); +} + +enum { + Fs512 = 0, + Fs384 = 1, + Fs256 = 2, + + MHz4_096 = GPIO_CLK_SET1_o, + MHz5_6245 = GPIO_CLK_SET1_o|GPIO_CLK_SET0_o, + MHz11_2896 = GPIO_CLK_SET0_o, + MHz12_288 = 0 +}; + +typedef struct Csel Csel; +struct Csel{ + int speed; + int cfs; /* codec system clock multiplier */ + int gclk; /* gpio clock generator setting */ + int div; /* ssp clock divisor */ +}; +static Csel csel[] = { + {8000, Fs512, MHz4_096, 16}, + {11025, Fs512, MHz5_6245, 16}, + {16000, Fs256 , MHz4_096, 8}, + {22050, Fs512, MHz11_2896, 16}, + {32000, Fs384, MHz12_288, 12}, + {44100, Fs256, MHz11_2896, 8}, + {48000, Fs256, MHz12_288, 8}, + {0}, +}; + +int +archaudiospeed(int clock, int set) +{ + GpioReg *g; + SspReg *ssp; + Csel *cs; + int s, div, cr0; + + for(cs = csel; cs->speed > 0; cs++) + if(cs->speed == clock){ + if(!set) + return cs->cfs; + div = cs->div; + if(div == 0) + div = 4; + div = div/2 - 1; + s = splhi(); + g = GPIOREG; + g->gpsr = cs->gclk; + g->gpcr = ~cs->gclk & (GPIO_CLK_SET0_o|GPIO_CLK_SET1_o); + ssp = SSPREG; + cr0 = (div<<8) | 0x1f; /* 16 bits, TI frames, not enabled */ + ssp->sscr0 = cr0; + ssp->sscr1 = 0x0020; /* ext clock */ + ssp->sscr0 = cr0 | 0x80; /* enable */ + splx(s); + return cs->cfs; + } + return -1; +} + +/* + * pcmcia + */ +int +pcmpowered(int slotno) +{ + if(slotno) + return 0; + if(egpiocopy & EGPIO_OPT_PWR_ON) + return 3; + return 0; +} + +void +pcmpower(int slotno, int on) +{ + USED(slotno); /* the pack powers both or none */ + if(on){ + if((egpiocopy & EGPIO_OPT_PWR_ON) == 0){ + egpiosc(EGPIO_OPT_PWR_ON | EGPIO_OPT_ON, 0); + delay(200); + } + }else + egpiosc(0, EGPIO_OPT_PWR_ON | EGPIO_OPT_ON); +} + +void +pcmreset(int slot) +{ + USED(slot); + egpiosc(EGPIO_CARD_RESET, 0); + delay(100); // microdelay(10); + egpiosc(0, EGPIO_CARD_RESET); +} + +int +pcmpin(int slot, int type) +{ + switch(type){ + case PCMready: + return slot==0? 21: 11; + case PCMeject: + return slot==0? 17: 10; + case PCMstschng: + return -1; + } +} + +void +pcmsetvpp(int slot, int vpp) +{ + USED(slot, vpp); +} + +/* + * set ether parameters: the contents should be derived from EEPROM or NVRAM + */ +int +archether(int ctlno, Ether *ether) +{ + static char opt[128]; + + if(ctlno == 1){ + sprint(ether->type, "EC2T"); + return 1; + } + if(ctlno > 0) + return -1; + sprint(ether->type, "wavelan"); + if(0) + strcpy(opt, "mode=0 essid=Limbo station=ipaq1 crypt=off"); /* peertopeer */ + else + strcpy(opt, "mode=managed essid=Vitanuova1 station=ipaq1 crypt=off"); + ether->nopt = tokenize(opt, ether->opt, nelem(ether->opt)); + return 1; +} diff --git a/os/ipaq1110/dat.h b/os/ipaq1110/dat.h new file mode 100644 index 00000000..553c0cb0 --- /dev/null +++ b/os/ipaq1110/dat.h @@ -0,0 +1,135 @@ +typedef struct Conf Conf; +typedef struct Dma Dma; +typedef struct FPU FPU; +typedef struct FPenv FPenv; +typedef struct Label Label; +typedef struct Lock Lock; +typedef struct Mach Mach; +typedef struct Ureg Ureg; +typedef struct ISAConf ISAConf; +typedef struct PCMmap PCMmap; +typedef struct PCMslot PCMslot; + +typedef ulong Instr; + +struct Conf +{ + ulong nmach; /* processors */ + ulong nproc; /* processes */ + ulong npage0; /* total physical pages of memory */ + ulong npage1; /* total physical pages of memory */ + ulong topofmem; /* highest physical address + 1 */ + ulong npage; /* total physical pages of memory */ + ulong base0; /* base of bank 0 */ + ulong base1; /* base of bank 1 */ + ulong ialloc; /* max interrupt time allocation in bytes */ + + int useminicache; /* use mini cache: screen.c/lcd.c */ + int textwrite; /* writeable text segment, for debug */ + int portrait; /* display orientation */ +}; + +#define NISAOPT 8 +struct ISAConf { + char type[KNAMELEN]; + ulong port; + ulong irq; + int itype; + ulong dma; + ulong mem; + ulong size; + ulong freq; + + int nopt; + char *opt[NISAOPT]; +}; + +/* + * FPenv.status + */ +enum +{ + FPINIT, + FPACTIVE, + FPINACTIVE, +}; + +struct FPenv +{ + ulong status; + ulong control; + ushort fpistate; /* emulated fp */ + ulong regs[8][3]; /* emulated fp */ +}; + +/* + * This structure must agree with fpsave and fprestore asm routines + */ +struct FPU +{ + FPenv env; +}; + +struct Label +{ + ulong sp; + ulong pc; +}; + +struct Lock +{ + ulong key; + ulong sr; + ulong pc; + int pri; +}; + +#include "../port/portdat.h" + +/* + * machine dependent definitions not used by ../port/portdat.h + */ +struct Mach +{ + /* OFFSETS OF THE FOLLOWING KNOWN BY l.s */ + ulong splpc; /* pc of last caller to splhi */ + + /* ordering from here on irrelevant */ + + int machno; /* physical id of processor */ + ulong ticks; /* of the clock since boot time */ + Proc *proc; /* current process on this processor */ + Label sched; /* scheduler wakeup */ + Lock alarmlock; /* access to alarm list */ + void *alarm; /* alarms bound to this clock */ + ulong cpuhz; + + /* stacks for exceptions */ + ulong fiqstack[4]; + ulong irqstack[4]; + ulong abtstack[4]; + ulong undstack[4]; + + int stack[1]; +}; + +#define MACHP(n) (n == 0 ? (Mach*)(MACHADDR) : (Mach*)0) + +extern Mach *m; +extern Proc *up; + +typedef struct MemBank { + uint pbase; + uint plimit; + uint vbase; + uint vlimit; +} MemBank; + +/* + * Layout at virtual address 0. + */ +typedef struct Vectorpage { + void (*vectors[8])(void); + uint vtable[8]; +} Vectorpage; +extern Vectorpage *page0; diff --git a/os/ipaq1110/defont.c b/os/ipaq1110/defont.c new file mode 100644 index 00000000..285f464c --- /dev/null +++ b/os/ipaq1110/defont.c @@ -0,0 +1,290 @@ +#include "lib9.h" +#include "draw.h" + +/* + * /tmp/latin1.6x13, in uncompressed form + */ +uchar +defontdata[] = +{ + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x30,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x30,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x30,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x31,0x35,0x33,0x36,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x31,0x33,0x20,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2a,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x41,0x0e,0x00,0x60,0xc2,0x0a,0x00, + 0x00,0x00,0x60,0xc2,0x00,0x60,0xc2,0x00,0x00,0xa6,0x0c,0x20,0xa0,0x00,0x01,0x83, + 0x08,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x14,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0x0c,0x00,0x00,0x00,0x00,0x0c,0x00,0x03,0x00,0x00,0x00,0x3f, + 0x30,0x03,0x1c,0x30,0x00,0x00,0x00,0x83,0x00,0x41,0x02,0x00,0x31,0x85,0x14,0x51, + 0xc0,0x00,0x31,0x85,0x14,0x31,0x85,0x14,0x01,0x43,0x18,0x51,0x45,0x00,0x08,0xc6, + 0x14,0x51,0x86,0x1c,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x02,0x00, + 0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x08,0x00,0x0c,0x00,0x73,0xe0,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x85,0x00,0x71,0x24,0x0c,0x11,0x00,0x00,0x00,0x00,0x02, + 0x20,0x87,0x3e,0x13,0xe7,0x3e,0x71,0xc0,0x00,0x08,0x08,0x1c,0x70,0x8f,0x1c,0xf3, + 0xef,0x9c,0x89,0xc3,0xa2,0x82,0x28,0x9c,0xf1,0xcf,0x1c,0xfa,0x28,0xa2,0x8a,0x2f, + 0x9c,0x81,0xc2,0x00,0x30,0x08,0x00,0x08,0x03,0x00,0x80,0x00,0x20,0x60,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x23,0x04,0xaa,0x8f,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0x0c,0x80,0x00,0x02,0x22,0x12,0x00,0xc0,0x80,0x00,0x03,0x00, + 0x48,0x04,0x82,0x60,0x06,0xc0,0x00,0x84,0x80,0x49,0x24,0x08,0x00,0x08,0x80,0x01, + 0x40,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x00,0x00,0x88,0x00,0x00,0x70,0x00, + 0x00,0x00,0x04,0x22,0x60,0xc5,0x0a,0x00,0x00,0x00,0x60,0xc5,0x00,0x60,0xc5,0x00, + 0x09,0x46,0x0c,0x51,0x40,0x00,0x01,0x83,0x14,0x00,0xc8,0x00,0x1b,0xe0,0x00,0x03, + 0x84,0x00,0xc0,0x00,0x00,0xf0,0x0e,0x38,0xc3,0x0c,0x30,0xc2,0x4e,0x38,0x63,0x8e, + 0x38,0xe3,0x8c,0x28,0x00,0x85,0x14,0xa2,0xaa,0x08,0x20,0x82,0x00,0x00,0x00,0x02, + 0x51,0x88,0x82,0x12,0x08,0x82,0x8a,0x20,0x00,0x10,0x04,0x22,0x89,0x44,0xa2,0x4a, + 0x08,0x22,0x88,0x81,0x22,0x82,0x2c,0xa2,0x8a,0x28,0xa2,0x22,0x28,0xa2,0x8a,0x20, + 0x90,0x80,0x45,0x00,0x10,0x08,0x00,0x08,0x04,0x80,0x80,0x81,0x20,0x20,0x00,0x00, + 0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x08,0x20,0x8a,0x94,0x77,0xff,0xff,0x1e, + 0xff,0xcf,0xff,0xff,0xc3,0xfc,0x71,0xcf,0x3c,0xf3,0xcf,0x6c,0x71,0xe7,0x1c,0x71, + 0xc7,0x3c,0x73,0xd7,0x00,0x02,0x00,0x8a,0x22,0x10,0x51,0x23,0x82,0x00,0x04,0x80, + 0x48,0x01,0x0c,0x00,0x0a,0x80,0x00,0x84,0xa0,0x51,0x42,0x80,0x71,0xc7,0x1c,0x71, + 0xc7,0xa2,0xfb,0xef,0xbe,0x71,0xc7,0x1c,0x4a,0x27,0x1c,0x71,0xc7,0x00,0x9a,0x28, + 0xa2,0x8a,0x27,0x22,0x31,0x88,0x94,0x51,0xc0,0x00,0x31,0x88,0x94,0x31,0x88,0x94, + 0x7e,0x83,0x18,0x8a,0x85,0x0c,0x00,0xc6,0x22,0x51,0x88,0x14,0x9f,0xee,0x38,0xe2, + 0x0a,0x08,0xa2,0x88,0x22,0x81,0xc8,0x20,0xa2,0x8a,0x28,0xa3,0x48,0x20,0x92,0x08, + 0x20,0x82,0x0a,0x28,0x00,0x85,0x14,0xa1,0x4a,0x10,0x20,0x8a,0x88,0x00,0x00,0x04, + 0x8a,0x88,0x84,0x32,0x08,0x04,0x8a,0x22,0x08,0x20,0x02,0x22,0x8a,0x24,0xa0,0x4a, + 0x08,0x20,0x88,0x81,0x24,0x83,0x6c,0xa2,0x8a,0x28,0xa0,0x22,0x28,0xa2,0x51,0x41, + 0x10,0x40,0x48,0x80,0x08,0x08,0x00,0x08,0x04,0x00,0x80,0x00,0x20,0x20,0x00,0x00, + 0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x08,0x20,0x89,0x2a,0x74,0x71,0xc7,0x7d, + 0x7d,0xd7,0x5d,0xf7,0x5f,0x8d,0xf7,0xd7,0x5d,0x75,0xd7,0x2d,0xf7,0xdb,0x7d,0xf7, + 0xdf,0x5d,0xf5,0xd7,0x00,0x82,0x0c,0x71,0x42,0x10,0x02,0x14,0x84,0x00,0x08,0x40, + 0x48,0x82,0x02,0x00,0x0a,0x80,0x00,0x83,0x10,0x20,0x8d,0x08,0x8a,0x28,0xa2,0x8a, + 0x2a,0x20,0x82,0x08,0x20,0x20,0x82,0x08,0x4b,0x28,0xa2,0x8a,0x28,0x80,0x9a,0x28, + 0xa2,0x89,0x44,0xa6,0x00,0x00,0x00,0x01,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x0c,0x08,0x00,0x00,0x00,0x08,0x00,0xff,0xe8,0x20,0x83, + 0x8a,0x1c,0xc2,0x88,0x22,0xe2,0x0e,0x38,0xa2,0x8a,0x28,0xa2,0xce,0x30,0x83,0x8e, + 0x38,0xe2,0x8c,0x28,0x00,0x80,0x3e,0x70,0x44,0x00,0x40,0x4f,0x88,0x00,0x00,0x04, + 0x88,0x80,0x88,0x52,0xc8,0x04,0x8a,0x27,0x1c,0x43,0xe1,0x02,0x9a,0x24,0xa0,0x4a, + 0x08,0x20,0x88,0x81,0x28,0x82,0xaa,0xa2,0x8a,0x28,0xa0,0x22,0x28,0xa2,0x51,0x41, + 0x10,0x40,0x40,0x00,0x01,0xcf,0x1c,0x79,0xc4,0x1c,0xb1,0x83,0x24,0x23,0x4b,0x1c, + 0xf1,0xeb,0x1c,0xf2,0x28,0xa2,0x8a,0x2f,0x88,0x20,0x80,0x14,0xf5,0xf7,0xdf,0x1d, + 0x78,0xcf,0x5d,0xf7,0x47,0x7c,0x71,0xd7,0x5d,0x75,0xd7,0x4c,0x73,0xdf,0x1c,0x71, + 0xc7,0x3d,0x73,0xd7,0x00,0x87,0x12,0x51,0x42,0x1c,0x02,0xd4,0x8a,0xf8,0x0b,0x40, + 0x30,0x87,0x9c,0x01,0x2a,0x80,0x00,0x80,0x28,0x49,0x42,0x88,0x8a,0x28,0xa2,0x8a, + 0x2a,0x20,0x82,0x08,0x20,0x20,0x82,0x08,0x4b,0x28,0xa2,0x8a,0x28,0xa1,0xaa,0x28, + 0xa2,0x89,0x44,0xac,0x71,0xc7,0x1c,0x71,0xcf,0x1c,0x71,0xc7,0x1c,0x61,0x86,0x18, + 0x7a,0xc7,0x1c,0x71,0xc7,0x00,0x72,0x28,0xa2,0x8a,0x2f,0x22,0x1b,0xee,0x38,0xc2, + 0x0e,0x1c,0xa3,0x88,0x14,0x82,0x02,0x08,0xa2,0x8a,0x28,0xa2,0x42,0x20,0x92,0x02, + 0x20,0x82,0x8a,0x28,0x00,0x80,0x14,0x28,0x8a,0x00,0x40,0x47,0x3e,0x03,0xe0,0x08, + 0x88,0x81,0x1c,0x53,0x2f,0x08,0x71,0xe2,0x08,0x80,0x00,0x84,0xaa,0x27,0x20,0x4b, + 0xcf,0x20,0xf8,0x81,0x30,0x82,0xaa,0xa2,0xf2,0x2f,0x1c,0x22,0x25,0x2a,0x20,0x82, + 0x10,0x20,0x40,0x00,0x00,0x28,0xa2,0x8a,0x2f,0x22,0xc8,0x81,0x28,0x22,0xac,0xa2, + 0x8a,0x2c,0xa2,0x42,0x28,0xa2,0x52,0x21,0x30,0x20,0x60,0x2a,0xec,0x71,0xcf,0x7c, + 0x78,0xd7,0x1d,0xfa,0xdf,0x7f,0x7d,0xd7,0x5d,0x75,0xd7,0x6f,0x77,0xdb,0x7f,0x77, + 0xdf,0x5d,0x75,0xd7,0x00,0x8a,0x90,0x50,0x80,0x12,0x02,0x93,0x94,0x09,0xea,0x40, + 0x03,0xe0,0x00,0x01,0x26,0x8c,0x00,0x07,0x94,0x9a,0xa5,0x90,0x8a,0x28,0xa2,0x8a, + 0x2b,0xa0,0xf3,0xcf,0x3c,0x20,0x82,0x08,0xea,0xa8,0xa2,0x8a,0x28,0x92,0xaa,0x28, + 0xa2,0x88,0x85,0xa6,0x08,0x20,0x82,0x08,0x22,0xa2,0x8a,0x28,0xa2,0x20,0x82,0x08, + 0x8b,0x28,0xa2,0x8a,0x28,0x9e,0x9a,0x28,0xa2,0x8a,0x28,0xa2,0x3b,0xe2,0x20,0x83, + 0x8a,0x14,0xc2,0x8e,0x08,0x81,0xce,0x38,0xc3,0x0c,0x30,0xc2,0x4e,0x38,0x63,0x8e, + 0x38,0x83,0x8a,0x10,0x00,0x80,0x3e,0x29,0x09,0x80,0x40,0x4f,0x88,0x00,0x00,0x10, + 0x88,0x82,0x02,0x90,0x28,0x88,0x88,0x20,0x00,0x40,0x01,0x08,0xab,0xe4,0xa0,0x4a, + 0x08,0x26,0x88,0x81,0x28,0x82,0x29,0xa2,0x82,0x2a,0x02,0x22,0x25,0x2a,0x50,0x84, + 0x10,0x10,0x40,0x00,0x01,0xe8,0xa0,0x8b,0xe4,0x22,0x88,0x81,0x30,0x22,0xa8,0xa2, + 0x8a,0x28,0x18,0x42,0x28,0xaa,0x22,0x22,0x08,0x20,0x80,0x14,0xdf,0x77,0xdf,0x1d, + 0x7a,0xcf,0x5c,0x7d,0xdf,0x8c,0x71,0xcf,0x3c,0xf3,0xcf,0x6c,0x71,0xe7,0x1c,0x71, + 0xdf,0x5c,0x75,0xef,0x00,0x8a,0x3c,0x53,0xe2,0x0e,0x02,0xd0,0x28,0x09,0xea,0x40, + 0x00,0x80,0x00,0x01,0x22,0x8c,0x00,0x00,0x0a,0x28,0x2a,0xa0,0xfb,0xef,0xbe,0xfb, + 0xee,0x20,0x82,0x08,0x20,0x20,0x82,0x08,0x4a,0xa8,0xa2,0x8a,0x28,0x8c,0xaa,0x28, + 0xa2,0x88,0x86,0x22,0x79,0xe7,0x9e,0x79,0xe7,0xa0,0xfb,0xef,0xbe,0x20,0x82,0x08, + 0x8a,0x28,0xa2,0x8a,0x28,0x9e,0xaa,0x28,0xa2,0x8a,0x28,0xa2,0x33,0xee,0x38,0xf0, + 0xc5,0x3e,0x72,0x87,0x00,0x79,0xc0,0x00,0x20,0x01,0x0e,0x18,0xa4,0x98,0x49,0x16, + 0x1c,0x71,0xc7,0x1c,0x00,0x80,0x14,0x71,0x49,0x00,0x20,0x8a,0x88,0x00,0x00,0x10, + 0x88,0x84,0x02,0xf8,0x28,0x90,0x88,0x20,0x00,0x23,0xe2,0x08,0xb2,0x24,0xa0,0x4a, + 0x08,0x22,0x88,0x81,0x24,0x82,0x29,0xa2,0x82,0x29,0x02,0x22,0x25,0x2a,0x50,0x84, + 0x10,0x10,0x40,0x00,0x02,0x28,0xa0,0x8a,0x04,0x22,0x88,0x81,0x28,0x22,0xa8,0xa2, + 0x8a,0x28,0x04,0x42,0x25,0x2a,0x22,0x64,0x08,0x20,0x80,0x2a,0xdc,0x71,0xc3,0xce, + 0xb0,0x63,0x5e,0x3f,0xe1,0x8f,0xff,0xf7,0xff,0xbc,0x79,0xd6,0xd9,0xed,0xba,0x78, + 0xe3,0x8e,0x38,0xe3,0x00,0x8a,0x08,0x50,0x82,0x02,0x02,0x17,0x94,0x08,0x08,0x40, + 0x00,0x80,0x00,0x01,0x22,0x80,0x00,0x00,0x14,0x48,0x44,0xa2,0x8a,0x28,0xa2,0x8a, + 0x2a,0x20,0x82,0x08,0x20,0x20,0x82,0x08,0x4a,0x68,0xa2,0x8a,0x28,0x8c,0xca,0x28, + 0xa2,0x88,0x84,0x22,0x8a,0x28,0xa2,0x8a,0x2a,0x20,0x82,0x08,0x20,0x20,0x82,0x08, + 0x8a,0x28,0xa2,0x8a,0x28,0x80,0xaa,0x28,0xa2,0x8a,0x68,0xa6,0x33,0xe4,0x92,0x41, + 0x25,0x08,0x41,0xc4,0x3e,0x41,0x23,0x04,0x20,0x42,0x82,0x28,0xa6,0x94,0x69,0xb5, + 0x10,0x41,0x04,0x10,0x00,0x00,0x14,0x22,0xa6,0x80,0x20,0x82,0x00,0x30,0x02,0x20, + 0x50,0x88,0x22,0x12,0x28,0x90,0x8a,0x22,0x0c,0x10,0x04,0x00,0x82,0x24,0xa2,0x4a, + 0x08,0x22,0x88,0x89,0x22,0x82,0x28,0xa2,0x82,0xa8,0xa2,0x22,0x22,0x36,0x88,0x88, + 0x10,0x08,0x40,0x00,0x02,0x28,0xa2,0x8a,0x24,0x1e,0x88,0x81,0x24,0x22,0xa8,0xa2, + 0xf1,0xe8,0x22,0x4a,0x65,0x2a,0x51,0xa8,0x08,0x20,0x80,0x14,0xfe,0xdb,0x6f,0xb6, + 0xbd,0xef,0x8e,0xf0,0x6f,0xb7,0x3e,0xf7,0xef,0x5f,0x75,0xd6,0x5a,0xe5,0x92,0xbb, + 0xef,0xbe,0xfb,0xef,0x00,0x8a,0xbc,0x73,0xe2,0x02,0x01,0x20,0x0a,0x00,0x04,0x80, + 0x03,0xe0,0x00,0x01,0x62,0x80,0x00,0x00,0x28,0x78,0x87,0xa2,0x8a,0x28,0xa2,0x8a, + 0x2a,0x22,0x82,0x08,0x20,0x20,0x82,0x08,0x4a,0x68,0xa2,0x8a,0x28,0x92,0xca,0x28, + 0xa2,0x88,0x84,0x26,0x8a,0x28,0xa2,0x8a,0x2a,0xa2,0x8a,0x28,0xa2,0x20,0x82,0x08, + 0x8a,0x28,0xa2,0x8a,0x28,0x8c,0xca,0x69,0xa6,0x99,0xa8,0x9a,0x03,0xe3,0x0c,0x61, + 0x26,0x00,0x70,0x86,0x08,0x71,0xc4,0x84,0x20,0x41,0x04,0x48,0xc5,0x98,0x59,0x56, + 0x1c,0x71,0xc7,0x1c,0x00,0x80,0x00,0x02,0x40,0x00,0x11,0x00,0x00,0x20,0x07,0x20, + 0x23,0xef,0x9c,0x11,0xc7,0x10,0x71,0xc7,0x08,0x08,0x08,0x08,0x7a,0x2f,0x1c,0xf3, + 0xe8,0x1c,0x89,0xc6,0x22,0xfa,0x28,0x9c,0x81,0xc8,0x9c,0x21,0xc2,0x22,0x88,0x8f, + 0x9c,0x09,0xc0,0x00,0x01,0xef,0x1c,0x79,0xc4,0x02,0x89,0xc9,0x22,0x72,0x28,0x9c, + 0x80,0x28,0x1c,0x31,0xa2,0x14,0x88,0x2f,0x86,0x23,0x00,0x2a,0xdf,0x3c,0xe7,0xb6, + 0x7f,0xe3,0xde,0x7d,0xe3,0x8e,0xde,0xf7,0xef,0xbe,0xed,0xce,0x99,0xe9,0xaa,0x78, + 0xe3,0x8e,0x38,0xe3,0xc0,0x87,0x2a,0x88,0x82,0x12,0x00,0xc0,0x04,0x00,0x03,0x00, + 0x00,0x00,0x00,0x01,0xa2,0x80,0x10,0x00,0x10,0x08,0xe0,0x9c,0x8a,0x28,0xa2,0x8a, + 0x2b,0x9c,0xfb,0xef,0xbe,0x71,0xc7,0x1c,0xf2,0x27,0x1c,0x71,0xc7,0x21,0x71,0xc7, + 0x1c,0x70,0x84,0x2c,0x79,0xe7,0x9e,0x79,0xe7,0x1c,0x71,0xc7,0x1c,0x71,0xc7,0x1c, + 0x7a,0x27,0x1c,0x71,0xc7,0x0c,0x71,0xa6,0x9a,0x68,0x2f,0x02,0x03,0xe3,0x0c,0x40, + 0xc5,0x00,0x10,0x84,0x08,0x41,0x44,0x84,0x20,0x42,0x02,0x7c,0xa4,0x94,0x49,0x15, + 0x04,0x10,0x41,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x02,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x3e,0x00,0x00,0x00,0x00,0x00,0x22,0x00,0x09,0x00,0x00,0x00,0x00, + 0x80,0x20,0x00,0x00,0x00,0x00,0x02,0x20,0x00,0x00,0x00,0x14,0xff,0x3c,0xef,0xce, + 0xbf,0xfb,0xde,0xfd,0xef,0xae,0xde,0xf7,0xef,0x7f,0x60,0xd6,0xda,0xed,0xba,0xbe, + 0xfb,0xef,0xbe,0xfb,0xc0,0x02,0x38,0x00,0x00,0x0c,0x00,0x00,0x02,0x00,0x00,0x00, + 0x00,0x00,0x00,0x02,0x00,0x00,0x10,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00, + 0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x02,0x28,0x22,0x00,0x04,0x92,0x40, + 0x24,0x80,0x70,0x84,0x08,0x41,0x23,0x04,0x38,0x43,0x8e,0x08,0x94,0x98,0x49,0x16, + 0x1c,0x71,0xc7,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1c,0x00,0x06,0x00,0x00,0x00,0x00, + 0x80,0x20,0x00,0x00,0x00,0x00,0x01,0xc0,0x00,0x00,0x00,0x2a,0x02,0xdb,0x6f,0xf6, + 0xdf,0xe3,0xde,0xfd,0xef,0xb7,0x3e,0xf1,0xef,0x1c,0x7d,0xda,0xd9,0xed,0xba,0x78, + 0xe3,0x8e,0x38,0xe3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xc8,0x1c, + 0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x32,0x35,0x36,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x31,0x33,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x31,0x31, + 0x20,0x00,0x00,0x02,0x0a,0x00,0x06,0x06,0x00,0x02,0x0c,0x00,0x06,0x0c,0x00,0x04, + 0x0d,0x00,0x06,0x12,0x00,0x04,0x0d,0x00,0x06,0x18,0x00,0x04,0x0d,0x00,0x06,0x1e, + 0x00,0x03,0x0d,0x00,0x06,0x24,0x00,0x03,0x0d,0x00,0x06,0x2a,0x00,0x04,0x0a,0x00, + 0x06,0x30,0x00,0x03,0x0d,0x00,0x06,0x36,0x00,0x04,0x0d,0x00,0x06,0x3c,0x00,0x04, + 0x0d,0x00,0x06,0x42,0x00,0x04,0x0d,0x00,0x06,0x48,0x00,0x03,0x0d,0x00,0x06,0x4e, + 0x00,0x04,0x0d,0x00,0x06,0x54,0x00,0x03,0x0d,0x00,0x06,0x5a,0x00,0x03,0x0d,0x00, + 0x06,0x60,0x00,0x03,0x0d,0x00,0x06,0x66,0x00,0x03,0x0d,0x00,0x06,0x6c,0x00,0x03, + 0x0d,0x00,0x06,0x72,0x00,0x03,0x0d,0x00,0x06,0x78,0x00,0x03,0x0d,0x00,0x06,0x7e, + 0x00,0x03,0x0d,0x00,0x06,0x84,0x00,0x03,0x0d,0x00,0x06,0x8a,0x00,0x03,0x0d,0x00, + 0x06,0x90,0x00,0x03,0x0d,0x00,0x06,0x96,0x00,0x03,0x0d,0x00,0x06,0x9c,0x00,0x03, + 0x0d,0x00,0x06,0xa2,0x00,0x03,0x0d,0x00,0x06,0xa8,0x00,0x03,0x0d,0x00,0x06,0xae, + 0x00,0x03,0x0d,0x00,0x06,0xb4,0x00,0x03,0x0d,0x00,0x06,0xba,0x00,0x03,0x0d,0x00, + 0x06,0xc0,0x00,0x00,0x00,0x00,0x06,0xc6,0x00,0x02,0x0b,0x00,0x06,0xcc,0x00,0x02, + 0x05,0x00,0x06,0xd2,0x00,0x03,0x0a,0x00,0x06,0xd8,0x00,0x01,0x0a,0x00,0x06,0xde, + 0x00,0x02,0x0b,0x00,0x06,0xe4,0x00,0x02,0x0a,0x00,0x06,0xea,0x00,0x02,0x05,0x00, + 0x06,0xf0,0x00,0x02,0x0b,0x00,0x06,0xf6,0x00,0x02,0x0b,0x00,0x06,0xfc,0x00,0x03, + 0x0a,0x00,0x06,0x02,0x01,0x04,0x09,0x00,0x06,0x08,0x01,0x09,0x0c,0x00,0x06,0x0e, + 0x01,0x06,0x07,0x00,0x06,0x14,0x01,0x09,0x0c,0x00,0x06,0x1a,0x01,0x02,0x0b,0x00, + 0x06,0x20,0x01,0x02,0x0b,0x00,0x06,0x26,0x01,0x02,0x0b,0x00,0x06,0x2c,0x01,0x02, + 0x0b,0x00,0x06,0x32,0x01,0x02,0x0b,0x00,0x06,0x38,0x01,0x02,0x0b,0x00,0x06,0x3e, + 0x01,0x02,0x0b,0x00,0x06,0x44,0x01,0x02,0x0b,0x00,0x06,0x4a,0x01,0x02,0x0b,0x00, + 0x06,0x50,0x01,0x02,0x0b,0x00,0x06,0x56,0x01,0x02,0x0b,0x00,0x06,0x5c,0x01,0x00, + 0x0d,0x00,0x06,0x62,0x01,0x00,0x0d,0x00,0x06,0x68,0x01,0x02,0x0b,0x00,0x06,0x6e, + 0x01,0x00,0x0d,0x00,0x06,0x74,0x01,0x02,0x0b,0x00,0x06,0x7a,0x01,0x02,0x0b,0x00, + 0x06,0x80,0x01,0x02,0x0b,0x00,0x06,0x86,0x01,0x02,0x0b,0x00,0x06,0x8c,0x01,0x02, + 0x0b,0x00,0x06,0x92,0x01,0x02,0x0b,0x00,0x06,0x98,0x01,0x02,0x0b,0x00,0x06,0x9e, + 0x01,0x02,0x0b,0x00,0x06,0xa4,0x01,0x02,0x0b,0x00,0x06,0xaa,0x01,0x02,0x0b,0x00, + 0x06,0xb0,0x01,0x02,0x0b,0x00,0x06,0xb6,0x01,0x02,0x0b,0x00,0x06,0xbc,0x01,0x02, + 0x0b,0x00,0x06,0xc2,0x01,0x02,0x0b,0x00,0x06,0xc8,0x01,0x02,0x0b,0x00,0x06,0xce, + 0x01,0x02,0x0b,0x00,0x06,0xd4,0x01,0x02,0x0b,0x00,0x06,0xda,0x01,0x02,0x0b,0x00, + 0x06,0xe0,0x01,0x02,0x0b,0x00,0x06,0xe6,0x01,0x02,0x0c,0x00,0x06,0xec,0x01,0x02, + 0x0b,0x00,0x06,0xf2,0x01,0x02,0x0b,0x00,0x06,0xf8,0x01,0x02,0x0b,0x00,0x06,0xfe, + 0x01,0x02,0x0b,0x00,0x06,0x04,0x02,0x02,0x0b,0x00,0x06,0x0a,0x02,0x02,0x0b,0x00, + 0x06,0x10,0x02,0x02,0x0b,0x00,0x06,0x16,0x02,0x02,0x0b,0x00,0x06,0x1c,0x02,0x02, + 0x0b,0x00,0x06,0x22,0x02,0x02,0x0b,0x00,0x06,0x28,0x02,0x02,0x0b,0x00,0x06,0x2e, + 0x02,0x02,0x0b,0x00,0x06,0x34,0x02,0x02,0x05,0x00,0x06,0x3a,0x02,0x0b,0x0c,0x00, + 0x06,0x40,0x02,0x02,0x05,0x00,0x06,0x46,0x02,0x05,0x0b,0x00,0x06,0x4c,0x02,0x02, + 0x0b,0x00,0x06,0x52,0x02,0x05,0x0b,0x00,0x06,0x58,0x02,0x02,0x0b,0x00,0x06,0x5e, + 0x02,0x05,0x0b,0x00,0x06,0x64,0x02,0x02,0x0b,0x00,0x06,0x6a,0x02,0x05,0x0d,0x00, + 0x06,0x70,0x02,0x02,0x0b,0x00,0x06,0x76,0x02,0x03,0x0b,0x00,0x06,0x7c,0x02,0x03, + 0x0d,0x00,0x06,0x82,0x02,0x02,0x0b,0x00,0x06,0x88,0x02,0x02,0x0b,0x00,0x06,0x8e, + 0x02,0x05,0x0b,0x00,0x06,0x94,0x02,0x05,0x0b,0x00,0x06,0x9a,0x02,0x05,0x0b,0x00, + 0x06,0xa0,0x02,0x05,0x0d,0x00,0x06,0xa6,0x02,0x05,0x0d,0x00,0x06,0xac,0x02,0x05, + 0x0b,0x00,0x06,0xb2,0x02,0x05,0x0b,0x00,0x06,0xb8,0x02,0x03,0x0b,0x00,0x06,0xbe, + 0x02,0x05,0x0b,0x00,0x06,0xc4,0x02,0x05,0x0b,0x00,0x06,0xca,0x02,0x05,0x0b,0x00, + 0x06,0xd0,0x02,0x05,0x0b,0x00,0x06,0xd6,0x02,0x05,0x0d,0x00,0x06,0xdc,0x02,0x05, + 0x0b,0x00,0x06,0xe2,0x02,0x02,0x0b,0x00,0x06,0xe8,0x02,0x02,0x0b,0x00,0x06,0xee, + 0x02,0x02,0x0b,0x00,0x06,0xf4,0x02,0x02,0x05,0x00,0x06,0xfa,0x02,0x00,0x0d,0x00, + 0x06,0x00,0x03,0x00,0x0c,0x00,0x06,0x06,0x03,0x00,0x0d,0x00,0x06,0x0c,0x03,0x00, + 0x0d,0x00,0x06,0x12,0x03,0x00,0x0d,0x00,0x06,0x18,0x03,0x00,0x0d,0x00,0x06,0x1e, + 0x03,0x00,0x0d,0x00,0x06,0x24,0x03,0x00,0x0d,0x00,0x06,0x2a,0x03,0x00,0x0d,0x00, + 0x06,0x30,0x03,0x00,0x0d,0x00,0x06,0x36,0x03,0x00,0x0d,0x00,0x06,0x3c,0x03,0x00, + 0x0d,0x00,0x06,0x42,0x03,0x00,0x0d,0x00,0x06,0x48,0x03,0x00,0x0d,0x00,0x06,0x4e, + 0x03,0x00,0x0d,0x00,0x06,0x54,0x03,0x00,0x0d,0x00,0x06,0x5a,0x03,0x00,0x0d,0x00, + 0x06,0x60,0x03,0x00,0x0d,0x00,0x06,0x66,0x03,0x00,0x0d,0x00,0x06,0x6c,0x03,0x00, + 0x0d,0x00,0x06,0x72,0x03,0x00,0x0d,0x00,0x06,0x78,0x03,0x00,0x0d,0x00,0x06,0x7e, + 0x03,0x00,0x0d,0x00,0x06,0x84,0x03,0x00,0x0d,0x00,0x06,0x8a,0x03,0x00,0x0d,0x00, + 0x06,0x90,0x03,0x00,0x0d,0x00,0x06,0x96,0x03,0x00,0x0d,0x00,0x06,0x9c,0x03,0x00, + 0x0d,0x00,0x06,0xa2,0x03,0x00,0x0d,0x00,0x06,0xa8,0x03,0x00,0x0d,0x00,0x00,0xae, + 0x03,0x00,0x0d,0x00,0x00,0xb4,0x03,0x00,0x0d,0x00,0x00,0xba,0x03,0x00,0x0d,0x00, + 0x00,0xc0,0x03,0x01,0x0c,0x00,0x06,0xc6,0x03,0x02,0x0b,0x00,0x06,0xcc,0x03,0x03, + 0x0c,0x00,0x06,0xd2,0x03,0x04,0x0c,0x00,0x06,0xd8,0x03,0x03,0x0b,0x00,0x06,0xde, + 0x03,0x02,0x0b,0x00,0x06,0xe4,0x03,0x02,0x0b,0x00,0x06,0xea,0x03,0x01,0x0c,0x00, + 0x06,0xf0,0x03,0x03,0x04,0x00,0x06,0xf6,0x03,0x02,0x0b,0x00,0x06,0xfc,0x03,0x01, + 0x09,0x00,0x06,0x02,0x04,0x03,0x0c,0x00,0x06,0x08,0x04,0x05,0x09,0x00,0x06,0x0e, + 0x04,0x06,0x08,0x00,0x06,0x14,0x04,0x02,0x0b,0x00,0x06,0x1a,0x04,0x01,0x02,0x00, + 0x06,0x20,0x04,0x01,0x06,0x00,0x06,0x26,0x04,0x04,0x0a,0x00,0x06,0x2c,0x04,0x01, + 0x06,0x00,0x06,0x32,0x04,0x01,0x06,0x00,0x06,0x38,0x04,0x00,0x03,0x00,0x06,0x3e, + 0x04,0x05,0x0c,0x00,0x06,0x44,0x04,0x02,0x0b,0x00,0x06,0x4a,0x04,0x06,0x08,0x00, + 0x06,0x50,0x04,0x0a,0x0d,0x00,0x06,0x56,0x04,0x01,0x06,0x00,0x06,0x5c,0x04,0x01, + 0x07,0x00,0x06,0x62,0x04,0x03,0x0c,0x00,0x06,0x68,0x04,0x00,0x0b,0x00,0x06,0x6e, + 0x04,0x00,0x0b,0x00,0x06,0x74,0x04,0x00,0x0b,0x00,0x06,0x7a,0x04,0x02,0x0b,0x00, + 0x06,0x80,0x04,0x00,0x0b,0x00,0x06,0x86,0x04,0x00,0x0b,0x00,0x06,0x8c,0x04,0x00, + 0x0b,0x00,0x06,0x92,0x04,0x00,0x0b,0x00,0x06,0x98,0x04,0x01,0x0b,0x00,0x06,0x9e, + 0x04,0x01,0x0b,0x00,0x06,0xa4,0x04,0x03,0x0b,0x00,0x06,0xaa,0x04,0x02,0x0d,0x00, + 0x06,0xb0,0x04,0x00,0x0b,0x00,0x06,0xb6,0x04,0x00,0x0b,0x00,0x06,0xbc,0x04,0x00, + 0x0b,0x00,0x06,0xc2,0x04,0x01,0x0b,0x00,0x06,0xc8,0x04,0x00,0x0b,0x00,0x06,0xce, + 0x04,0x00,0x0b,0x00,0x06,0xd4,0x04,0x00,0x0b,0x00,0x06,0xda,0x04,0x01,0x0b,0x00, + 0x06,0xe0,0x04,0x02,0x0b,0x00,0x06,0xe6,0x04,0x00,0x0b,0x00,0x06,0xec,0x04,0x00, + 0x0b,0x00,0x06,0xf2,0x04,0x00,0x0b,0x00,0x06,0xf8,0x04,0x00,0x0b,0x00,0x06,0xfe, + 0x04,0x00,0x0b,0x00,0x06,0x04,0x05,0x01,0x0b,0x00,0x06,0x0a,0x05,0x05,0x0b,0x00, + 0x06,0x10,0x05,0x01,0x0c,0x00,0x06,0x16,0x05,0x00,0x0b,0x00,0x06,0x1c,0x05,0x00, + 0x0b,0x00,0x06,0x22,0x05,0x00,0x0b,0x00,0x06,0x28,0x05,0x01,0x0b,0x00,0x06,0x2e, + 0x05,0x00,0x0b,0x00,0x06,0x34,0x05,0x01,0x0b,0x00,0x06,0x3a,0x05,0x01,0x0c,0x00, + 0x06,0x40,0x05,0x02,0x0b,0x00,0x06,0x46,0x05,0x02,0x0b,0x00,0x06,0x4c,0x05,0x01, + 0x0b,0x00,0x06,0x52,0x05,0x02,0x0b,0x00,0x06,0x58,0x05,0x03,0x0b,0x00,0x06,0x5e, + 0x05,0x03,0x0b,0x00,0x06,0x64,0x05,0x05,0x0b,0x00,0x06,0x6a,0x05,0x05,0x0d,0x00, + 0x06,0x70,0x05,0x02,0x0b,0x00,0x06,0x76,0x05,0x02,0x0b,0x00,0x06,0x7c,0x05,0x01, + 0x0b,0x00,0x06,0x82,0x05,0x03,0x0b,0x00,0x06,0x88,0x05,0x02,0x0b,0x00,0x06,0x8e, + 0x05,0x02,0x0b,0x00,0x06,0x94,0x05,0x01,0x0b,0x00,0x06,0x9a,0x05,0x03,0x0b,0x00, + 0x06,0xa0,0x05,0x02,0x0b,0x00,0x06,0xa6,0x05,0x02,0x0b,0x00,0x06,0xac,0x05,0x02, + 0x0b,0x00,0x06,0xb2,0x05,0x02,0x0b,0x00,0x06,0xb8,0x05,0x01,0x0b,0x00,0x06,0xbe, + 0x05,0x02,0x0b,0x00,0x06,0xc4,0x05,0x03,0x0b,0x00,0x06,0xca,0x05,0x03,0x0b,0x00, + 0x06,0xd0,0x05,0x04,0x0c,0x00,0x06,0xd6,0x05,0x02,0x0b,0x00,0x06,0xdc,0x05,0x02, + 0x0b,0x00,0x06,0xe2,0x05,0x01,0x0b,0x00,0x06,0xe8,0x05,0x03,0x0b,0x00,0x06,0xee, + 0x05,0x02,0x0d,0x00,0x06,0xf4,0x05,0x01,0x0d,0x00,0x06,0xfa,0x05,0x03,0x0d,0x00, + 0x06,0x00,0x06,0x00,0x00,0x00,0x00, + + +}; + +int sizeofdefont = sizeof defontdata; + +void +_unpackinfo(Fontchar *fc, uchar *p, int n) +{ + int j; + + for(j=0; j<=n; j++){ + fc->x = p[0]|(p[1]<<8); + fc->top = p[2]; + fc->bottom = p[3]; + fc->left = p[4]; + fc->width = p[5]; + fc++; + p += 6; + } +} diff --git a/os/ipaq1110/devaudio.c b/os/ipaq1110/devaudio.c new file mode 100644 index 00000000..66fb5cdc --- /dev/null +++ b/os/ipaq1110/devaudio.c @@ -0,0 +1,1056 @@ +/* + * SAC/UDA 1341 Audio driver for the Bitsy + * + * This code is covered by the Lucent Public Licence 1.02 (http://plan9.bell-labs.com/plan9dist/license.html); + * see the file NOTICE in the current directory. Modifications for the Inferno environment by Vita Nuova. + * + * The Philips UDA 1341 sound chip is accessed through the Serial Audio + * Controller (SAC) of the StrongARM SA-1110. + * + * The code morphs Nicolas Pitre's Linux controller + * and Ken's Soundblaster controller. + * + * The interface should be identical to that of devaudio.c + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +static int debug = 0; + +/* UDA 1341 Registers */ +enum { + /* Status0 register */ + UdaStatusDC = 0, /* 1 bit */ + UdaStatusIF = 1, /* 3 bits */ + UdaStatusSC = 4, /* 2 bits */ + UdaStatusRST = 6, /* 1 bit */ +}; + +enum { + /* Status1 register */ + UdaStatusPC = 0, /* 2 bits */ + UdaStatusDS = 2, /* 1 bit */ + UdaStatusPDA = 3, /* 1 bit */ + UdaStatusPAD = 4, /* 1 bit */ + UdaStatusIGS = 5, /* 1 bit */ + UdaStatusOGS = 6, /* 1 bit */ +}; + +/* + * UDA1341 L3 address and command types + */ + +enum { + UDA1341_DATA0 = 0, + UDA1341_DATA1, + UDA1341_STATUS, + UDA1341_L3Addr = 0x14, +}; + +typedef struct AQueue AQueue; +typedef struct Buf Buf; +typedef struct IOstate IOstate; + +enum +{ + Qdir = 0, + Qaudio, + Qvolume, + Qstatus, + Qaudioctl, + + Fmono = 1, + Fin = 2, + Fout = 4, + + Aclosed = 0, + Aread, + Awrite, + + Vaudio = 0, + Vmic, + Vtreb, + Vbass, + Vspeed, + Vfilter, + Vinvert, + Nvol, + + Bufsize = 4*1024, /* 46 ms each */ + Nbuf = 32, /* 1.5 seconds total */ + + Speed = 44100, + Ncmd = 50, /* max volume command words */ +}; + +Dirtab +audiodir[] = +{ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "audio", {Qaudio}, 0, 0666, + "volume", {Qvolume}, 0, 0666, + "audioctl", {Qaudioctl}, 0, 0666, + "audiostat",{Qstatus}, 0, 0444, +}; + +struct Buf +{ + uchar* virt; + ulong phys; + uint nbytes; +}; + +struct IOstate +{ + QLock; + Lock ilock; + Rendez vous; + Chan *chan; /* chan of open */ + Dma* dma; /* dma chan, alloc on open, free on close */ + int bufinit; /* boolean, if buffers allocated */ + Buf buf[Nbuf]; /* buffers and queues */ + volatile Buf *current; /* next dma to finish */ + volatile Buf *next; /* next candidate for dma */ + volatile Buf *filling; /* buffer being filled */ +/* just be be cute (and to have defines like linux, a real operating system) */ +#define emptying filling +}; + +static struct +{ + QLock; + int amode; /* Aclosed/Aread/Awrite for /audio */ + int intr; /* boolean an interrupt has happened */ + int rivol[Nvol]; /* right/left input/output volumes */ + int livol[Nvol]; + int rovol[Nvol]; + int lovol[Nvol]; + uvlong totcount; /* how many bytes processed since open */ + vlong tottime; /* time at which totcount bytes were processed */ + int clockout; /* need steady output to provide input clock */ + IOstate i; + IOstate o; +} audio; + +static struct +{ + ulong bytes; + ulong totaldma; + ulong idledma; + ulong faildma; + ulong samedma; +} iostats; + +static struct +{ + char* name; + int flag; + int ilval; /* initial values */ + int irval; +} volumes[] = +{ +[Vaudio] {"audio", Fout|Fmono, 80, 80}, +[Vmic] {"mic", Fin|Fmono, 0, 0}, +[Vtreb] {"treb", Fout|Fmono, 50, 50}, +[Vbass] {"bass", Fout|Fmono, 50, 50}, +[Vspeed] {"speed", Fin|Fout|Fmono, Speed, Speed}, +[Vfilter] {"filter", Fout|Fmono, 0, 0}, +[Vinvert] {"invert", Fin|Fout|Fmono, 0, 0}, +[Nvol] {0} +}; + +static void setreg(char *name, int val, int n); + +static char Emode[] = "illegal open mode"; +static char Evolume[] = "illegal volume specifier"; + +static void +bufinit(IOstate *b) +{ + int i; + + if (debug) print("bufinit\n"); + for (i = 0; i < Nbuf; i++) { + b->buf[i].virt = xspanalloc(Bufsize, CACHELINESZ, 0); + b->buf[i].phys = PADDR(b->buf[i].virt); + } + b->bufinit = 1; +}; + +static void +setempty(IOstate *b) +{ + int i; + + if (debug) print("setempty\n"); + for (i = 0; i < Nbuf; i++) { + b->buf[i].nbytes = 0; + } + b->filling = b->buf; + b->current = b->buf; + b->next = b->buf; +} + +static int +audioqnotempty(void *x) +{ + IOstate *s = x; + + return dmaidle(s->dma) || s->emptying != s->current; +} + +static int +audioqnotfull(void *x) +{ + IOstate *s = x; + + return dmaidle(s->dma) || s->filling != s->current; +} + +static void +audioreset(void) +{ + /* Turn MCP operations off */ + MCPREG->mccr = 0; +} + +uchar status0[1] = {0x22}; +uchar status1[1] = {0x80}; +uchar data00[1] = {0x00}; /* volume control, bits 0 – 5 */ +uchar data01[1] = {0x40}; +uchar data02[1] = {0x80}; +uchar data0e0[2] = {0xc0, 0xe0}; +uchar data0e1[2] = {0xc1, 0xe0}; +uchar data0e2[2] = {0xc2, 0xf2}; +/* there is no data0e3 */ +uchar data0e4[2] = {0xc4, 0xe0}; +uchar data0e5[2] = {0xc5, 0xe0}; +uchar data0e6[2] = {0xc6, 0xe3}; + +static void +enable(void) +{ + uchar data[1]; + int cs; + + L3init(); + + PPCREG->ppar &= ~PPAR_SPR; + + /* external clock and ssp configured for current samples/sec */ + cs = archaudiospeed(audio.livol[Vspeed], 1); + status0[0] = (status0[0] & ~(3<<4)) | (cs<<4); + + /* Enable the audio power */ + archaudiopower(1); +// egpiobits(EGPIO_audio_ic_power | EGPIO_codec_reset, 1); + + /* Wait for the UDA1341 to wake up */ + delay(100); + + /* Reset the chip */ + data[0] = status0[0] | 1<sscr0 = 0x031f; /* disable */ +} + +static void +resetlevel(void) +{ + int i; + + for(i=0; volumes[i].name; i++) { + audio.lovol[i] = volumes[i].ilval; + audio.rovol[i] = volumes[i].irval; + audio.livol[i] = volumes[i].ilval; + audio.rivol[i] = volumes[i].irval; + } +} + +static void +mxvolume(void) { + int *left, *right; + int cs; + + cs = archaudiospeed(audio.livol[Vspeed], 1); + status0[0] = (status0[0] & ~(3<<4)) | (cs<<4); + L3write(UDA1341_L3Addr | UDA1341_STATUS, status0, 1); + if(debug) + print("mxvolume: status0 = %2.2ux\n", status0[0]); + if(audio.amode & Aread){ + left = audio.livol; + right = audio.rivol; + if (left[Vmic]+right[Vmic] == 0) { + /* Turn on automatic gain control (AGC) */ + data0e4[1] |= 0x10; + L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e4, 2 ); + } else { + int v; + /* Turn on manual gain control */ + v = ((left[Vmic]+right[Vmic])*0x7f/200)&0x7f; + data0e4[1] &= ~0x13; + data0e5[1] &= ~0x1f; + data0e4[1] |= v & 0x3; + data0e5[0] |= (v & 0x7c)<<6; + data0e5[1] |= (v & 0x7c)>>2; + L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e4, 2 ); + L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e5, 2 ); + } + if (left[Vinvert]+right[Vinvert] == 0) + status1[0] &= ~0x10; + else + status1[0] |= 0x10; + L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1); + if (debug) { + print("mxvolume: status1 = 0x%2.2ux\n", status1[0]); + print("mxvolume: data0e4 = 0x%4.4ux\n", data0e4[0]|data0e4[0]<<8); + print("mxvolume: data0e5 = 0x%4.4ux\n", data0e5[0]|data0e5[0]<<8); + } + } + if(audio.amode & Awrite){ + left = audio.lovol; + right = audio.rovol; + data00[0] &= ~0x3f; + data00[0] |= ((200-left[Vaudio]-right[Vaudio])*0x3f/200)&0x3f; + if (left[Vtreb]+right[Vtreb] <= 100 + && left[Vbass]+right[Vbass] <= 100) + /* settings neutral */ + data02[0] &= ~0x03; + else { + data02[0] |= 0x03; + data01[0] &= ~0x3f; + data01[0] |= ((left[Vtreb]+right[Vtreb]-100)*0x3/100)&0x03; + data01[0] |= (((left[Vbass]+right[Vbass]-100)*0xf/100)&0xf)<<2; + } + if (left[Vfilter]+right[Vfilter] == 0) + data02[0] &= ~0x10; + else + data02[0]|= 0x10; + if (left[Vinvert]+right[Vinvert] == 0) + status1[0] &= ~0x8; + else + status1[0] |= 0x8; + L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1); + L3write(UDA1341_L3Addr | UDA1341_DATA0, data00, 1); + L3write(UDA1341_L3Addr | UDA1341_DATA0, data01, 1); + L3write(UDA1341_L3Addr | UDA1341_DATA0, data02, 1); + if (debug) { + print("mxvolume: status1 = 0x%2.2ux\n", status1[0]); + print("mxvolume: data00 = 0x%2.2ux\n", data00[0]); + print("mxvolume: data01 = 0x%2.2ux\n", data01[0]); + print("mxvolume: data02 = 0x%2.2ux\n", data02[0]); + } + } +} + +static void +setreg(char *name, int val, int n) +{ + uchar x[2]; + int i; + + if(strcmp(name, "pause") == 0){ + for(i = 0; i < n; i++) + microdelay(val); + return; + } + + x[0] = val; + x[1] = val>>8; + + switch(n){ + case 1: + case 2: + break; + default: + error("setreg"); + } + + if(strcmp(name, "status") == 0){ + L3write(UDA1341_L3Addr | UDA1341_STATUS, x, n); + } else if(strcmp(name, "data0") == 0){ + L3write(UDA1341_L3Addr | UDA1341_DATA0, x, n); + } else if(strcmp(name, "data1") == 0){ + L3write(UDA1341_L3Addr | UDA1341_DATA1, x, n); + } else + error("setreg"); +} + +static void +outenable(void) { + /* turn on DAC, set output gain switch */ + archaudioamp(1); + archaudiomute(0); + status1[0] |= 0x41; + L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1); + /* set volume */ + data00[0] |= 0xf; + L3write(UDA1341_L3Addr | UDA1341_DATA0, data00, 1); + if (debug) { + print("outenable: status1 = 0x%2.2ux\n", status1[0]); + print("outenable: data00 = 0x%2.2ux\n", data00[0]); + } +} + +static void +outdisable(void) { + archaudiomute(1); + dmastop(audio.o.dma); + /* turn off DAC, clear output gain switch */ + archaudioamp(0); + status1[0] &= ~0x41; + L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1); + if (debug) { + print("outdisable: status1 = 0x%2.2ux\n", status1[0]); + } +// egpiobits(EGPIO_audio_power, 0); +} + +static void +inenable(void) { + /* turn on ADC, set input gain switch */ + status1[0] |= 0x22; + L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1); + if (debug) { + print("inenable: status1 = 0x%2.2ux\n", status1[0]); + } +} + +static void +indisable(void) { + dmastop(audio.i.dma); + /* turn off ADC, clear input gain switch */ + status1[0] &= ~0x22; + L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1); + if (debug) { + print("indisable: status1 = 0x%2.2ux\n", status1[0]); + } +} + +static void +sendaudio(IOstate *s) { + /* interrupt routine calls this too */ + int n; + + if (debug > 1) print("#A: sendaudio\n"); + ilock(&s->ilock); + while (s->next != s->filling) { + assert(s->next->nbytes); + if ((n = dmastart(s->dma, (void*)s->next->phys, s->next->nbytes)) == 0) { + iostats.faildma++; + break; + } + iostats.totaldma++; + switch (n) { + case 1: + iostats.idledma++; + break; + case 3: + iostats.faildma++; + break; + } + if (debug) { + if (debug > 1) + print("dmastart @%p\n", s->next); + else + iprint("+"); + } + s->next->nbytes = 0; + s->next++; + if (s->next == &s->buf[Nbuf]) + s->next = &s->buf[0]; + } + iunlock(&s->ilock); +} + +static void +recvaudio(IOstate *s) { + /* interrupt routine calls this too */ + int n; + + if (debug > 1) print("#A: recvaudio\n"); + ilock(&s->ilock); + while (s->next != s->emptying) { + assert(s->next->nbytes == 0); + if ((n = dmastart(s->dma, (void*)s->next->phys, Bufsize)) == 0) { + iostats.faildma++; + break; + } + iostats.totaldma++; + switch (n) { + case 1: + iostats.idledma++; + break; + case 3: + iostats.faildma++; + break; + } + if (debug) { + if (debug > 1) + print("dmastart @%p\n", s->next); + else + iprint("+"); + } + s->next++; + if (s->next == &s->buf[Nbuf]) + s->next = &s->buf[0]; + } + iunlock(&s->ilock); +} + +static void +audiopower(int flag) { + IOstate *s; + + if (debug) { + iprint("audiopower %d\n", flag); + } + if (flag) { + /* power on only when necessary */ + if (audio.amode) { + archaudiopower(1); + enable(); + if (audio.amode & Aread) { + inenable(); + s = &audio.i; + dmastop(s->dma); + recvaudio(s); + } + if (audio.amode & Awrite) { + outenable(); + s = &audio.o; + dmastop(s->dma); + sendaudio(s); + } + mxvolume(); + } + } else { + /* power off */ + if (audio.amode & Aread) + indisable(); + if (audio.amode & Awrite) + outdisable(); + disable(); + archaudiopower(0); + } +} + +static void +audiointr(void *x, ulong ndma) { + IOstate *s = x; + + if (debug) { + if (debug > 1) + iprint("#A: audio interrupt @%p\n", s->current); + else + iprint("-"); + } + /* Only interrupt routine touches s->current */ + s->current++; + if (s->current == &s->buf[Nbuf]) + s->current = &s->buf[0]; + if (ndma > 0) { + if (s == &audio.o) + sendaudio(s); + else if (s == &audio.i) + recvaudio(s); + } + wakeup(&s->vous); +} + +static void +audioinit(void) +{ + audio.amode = Aclosed; + resetlevel(); +// powerenable(audiopower); +} + +static Chan* +audioattach(char *param) +{ + return devattach('A', param); +} + +static Walkqid* +audiowalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen); +} + +static int +audiostat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, audiodir, nelem(audiodir), devgen); +} + +static Chan* +audioopen(Chan *c, int mode) +{ + IOstate *s; + int omode = mode; + + switch((ulong)c->qid.path) { + default: + error(Eperm); + break; + + case Qstatus: + if((omode&7) != OREAD) + error(Eperm); + case Qvolume: + case Qaudioctl: + case Qdir: + break; + + case Qaudio: + omode = (omode & 0x7) + 1; + if (omode & ~(Aread | Awrite)) + error(Ebadarg); + qlock(&audio); + if(audio.amode & omode){ + qunlock(&audio); + error(Einuse); + } + enable(); + memset(&iostats, 0, sizeof(iostats)); + if (omode & Aread) { + inenable(); + s = &audio.i; + if(s->bufinit == 0) + bufinit(s); + setempty(s); + s->emptying = &s->buf[Nbuf-1]; + s->chan = c; + s->dma = dmasetup(DmaSSP, 1, 0, audiointr, (void*)s); + audio.amode |= Aread; + audio.clockout = 1; + } + if (omode & Awrite) { + outenable(); + s = &audio.o; + audio.amode |= Awrite; + if(s->bufinit == 0) + bufinit(s); + setempty(s); + s->chan = c; + s->dma = dmasetup(DmaSSP, 0, 0, audiointr, (void*)s); + audio.amode |= Awrite; + } + mxvolume(); + qunlock(&audio); + if (debug) print("open done\n"); + break; + } + c = devopen(c, mode, audiodir, nelem(audiodir), devgen); + c->mode = openmode(mode); + c->flag |= COPEN; + c->offset = 0; + + return c; +} + +static void +audioclose(Chan *c) +{ + IOstate *s; + + switch((ulong)c->qid.path) { + default: + error(Eperm); + break; + + case Qdir: + case Qvolume: + case Qaudioctl: + case Qstatus: + break; + + case Qaudio: + if (debug > 1) print("#A: close\n"); + if(c->flag & COPEN) { + qlock(&audio); + if(waserror()){ + qunlock(&audio); + nexterror(); + } + if (audio.o.chan == c) { + /* closing the write end */ + audio.amode &= ~Awrite; + s = &audio.o; + qlock(s); + if(waserror()){ + qunlock(s); + nexterror(); + } + if (s->filling->nbytes) { + /* send remaining partial buffer */ + s->filling++; + if (s->filling == &s->buf[Nbuf]) + s->filling = &s->buf[0]; + sendaudio(s); + } + dmawait(s->dma); + outdisable(); + setempty(s); + dmafree(s->dma); + qunlock(s); + poperror(); + } + if (audio.i.chan == c) { + /* closing the read end */ + audio.amode &= ~Aread; + s = &audio.i; + qlock(s); + if(waserror()){ + qunlock(s); + nexterror(); + } + indisable(); + setempty(s); + dmafree(s->dma); + qunlock(s); + poperror(); + } + if (audio.amode == 0) { + /* turn audio off */ + archaudiopower(0); + } + qunlock(&audio); + poperror(); + if (debug) { + print("total dmas: %lud\n", iostats.totaldma); + print("dmas while idle: %lud\n", iostats.idledma); + print("dmas while busy: %lud\n", iostats.faildma); + print("out of order dma: %lud\n", iostats.samedma); + } + } + break; + } +} + +static long +audioread(Chan *c, void *v, long n, vlong off) +{ + int liv, riv, lov, rov; + long m, n0; + char buf[300]; + int j; + ulong offset = off; + char *p; + IOstate *s; + + n0 = n; + p = v; + switch((ulong)c->qid.path) { + default: + error(Eperm); + break; + + case Qdir: + return devdirread(c, p, n, audiodir, nelem(audiodir), devgen); + + case Qaudio: + if (debug > 1) print("#A: read %ld\n", n); + if((audio.amode & Aread) == 0) + error(Emode); + s = &audio.i; + qlock(s); + if(waserror()){ + qunlock(s); + nexterror(); + } + while(n > 0) { + if(s->emptying->nbytes == 0) { + if (debug > 1) print("#A: emptied @%p\n", s->emptying); + recvaudio(s); + s->emptying++; + if (s->emptying == &s->buf[Nbuf]) + s->emptying = s->buf; + } + /* wait if dma in progress */ + while (!dmaidle(s->dma) && s->emptying == s->current) { + if (debug > 1) print("#A: sleep\n"); + sleep(&s->vous, audioqnotempty, s); + } + + m = Bufsize - s->emptying->nbytes; + if(m > n) + m = n; + memmove(p, s->emptying->virt + s->emptying->nbytes, m); + + s->emptying->nbytes -= m; + n -= m; + p += m; + } + poperror(); + qunlock(s); + break; + break; + + case Qstatus: + buf[0] = 0; + snprint(buf, sizeof(buf), "bytes %llud\ntime %lld\n", + audio.totcount, audio.tottime); + return readstr(offset, p, n, buf); + + case Qvolume: + case Qaudioctl: + j = 0; + buf[0] = 0; + for(m=0; volumes[m].name; m++){ + liv = audio.livol[m]; + riv = audio.rivol[m]; + lov = audio.lovol[m]; + rov = audio.rovol[m]; + j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name); + if((volumes[m].flag & Fmono) || liv==riv && lov==rov){ + if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov) + j += snprint(buf+j, sizeof(buf)-j, " %d", liv); + else{ + if(volumes[m].flag & Fin) + j += snprint(buf+j, sizeof(buf)-j, + " in %d", liv); + if(volumes[m].flag & Fout) + j += snprint(buf+j, sizeof(buf)-j, + " out %d", lov); + } + }else{ + if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && + liv==lov && riv==rov) + j += snprint(buf+j, sizeof(buf)-j, + " left %d right %d", + liv, riv); + else{ + if(volumes[m].flag & Fin) + j += snprint(buf+j, sizeof(buf)-j, + " in left %d right %d", + liv, riv); + if(volumes[m].flag & Fout) + j += snprint(buf+j, sizeof(buf)-j, + " out left %d right %d", + lov, rov); + } + } + j += snprint(buf+j, sizeof(buf)-j, "\n"); + } + return readstr(offset, p, n, buf); + } + return n0-n; +} + +static long +audiowrite(Chan *c, void *vp, long n, vlong) +{ + long m, n0; + int i, nf, v, left, right, in, out; + char buf[255], *field[Ncmd]; + char *p; + IOstate *a; + + p = vp; + n0 = n; + switch((ulong)c->qid.path) { + default: + error(Eperm); + break; + + case Qvolume: + case Qaudioctl: + v = Vaudio; + left = 1; + right = 1; + in = 1; + out = 1; + if(n > sizeof(buf)-1) + n = sizeof(buf)-1; + memmove(buf, p, n); + buf[n] = '\0'; + n = 0; + + nf = getfields(buf, field, Ncmd, 1, " \t\n"); + for(i = 0; i < nf; i++){ + /* + * a number is volume + */ + if(field[i][0] >= '0' && field[i][0] <= '9') { + m = strtoul(field[i], 0, 10); + if(v == Vspeed){ + if(archaudiospeed(m, 0) < 0) + error(Evolume); + }else + if(m < 0 || m > 100) + error(Evolume); + if(left && out) + audio.lovol[v] = m; + if(left && in) + audio.livol[v] = m; + if(right && out) + audio.rovol[v] = m; + if(right && in) + audio.rivol[v] = m; + goto cont0; + } + if(strcmp(field[i], "rate") == 0) + field[i] = "speed"; /* honestly ... */ + + for(m=0; volumes[m].name; m++) { + if(strcmp(field[i], volumes[m].name) == 0) { + v = m; + in = 1; + out = 1; + left = 1; + right = 1; + goto cont0; + } + } + if(strcmp(field[i], "enc") == 0) { + if(++i >= nf) + error(Evolume); + if(strcmp(field[i], "pcm") != 0) + error(Evolume); + goto cont0; + } + if(strcmp(field[i], "bits") == 0) { + if(++i >= nf) + error(Evolume); + if(strtol(field[i], 0, 0) != 16) + error(Evolume); + goto cont0; + } + if(strcmp(field[i], "chans") == 0) { + if(++i >= nf) + error(Evolume); + if(strtol(field[i], 0, 0) != 2) + error(Evolume); + goto cont0; + } + if(strcmp(field[i], "reset") == 0) { + resetlevel(); + goto cont0; + } + if(strcmp(field[i], "debug") == 0) { + debug = debug?0:1; + goto cont0; + } + if(strcmp(field[i], "in") == 0) { + in = 1; + out = 0; + goto cont0; + } + if(strcmp(field[i], "out") == 0) { + in = 0; + out = 1; + goto cont0; + } + if(strcmp(field[i], "left") == 0) { + left = 1; + right = 0; + goto cont0; + } + if(strcmp(field[i], "right") == 0) { + left = 0; + right = 1; + goto cont0; + } + if(strcmp(field[i], "reg") == 0) { + if(nf < 3) + error(Evolume); + setreg(field[1], atoi(field[2]), nf == 4 ? atoi(field[3]):1); + return n0; + } + error(Evolume); + break; + cont0:; + } + mxvolume(); + break; + + case Qaudio: + if (debug > 1) print("#A: write %ld\n", n); + if((audio.amode & Awrite) == 0) + error(Emode); + a = &audio.o; + qlock(a); + if(waserror()){ + qunlock(a); + nexterror(); + } + while(n > 0) { + /* wait if dma in progress */ + while (!dmaidle(a->dma) && a->filling == a->current) { + if (debug > 1) print("#A: sleep\n"); + sleep(&a->vous, audioqnotfull, a); + } + + m = Bufsize - a->filling->nbytes; + if(m > n) + m = n; + memmove(a->filling->virt + a->filling->nbytes, p, m); + + a->filling->nbytes += m; + n -= m; + p += m; + if(a->filling->nbytes >= Bufsize) { + if (debug > 1) print("#A: filled @%p\n", a->filling); + a->filling++; + if (a->filling == &a->buf[Nbuf]) + a->filling = a->buf; + sendaudio(a); + } + } + poperror(); + qunlock(a); + break; + } + return n0 - n; +} + +Dev audiodevtab = { + 'A', + "audio", + + audioreset, + audioinit, + devshutdown, + audioattach, + audiowalk, + audiostat, + audioopen, + devcreate, + audioclose, + audioread, + devbread, + audiowrite, + devbwrite, + devremove, + devwstat, + audiopower, +}; diff --git a/os/ipaq1110/devipaq.c b/os/ipaq1110/devipaq.c new file mode 100644 index 00000000..cc432cb5 --- /dev/null +++ b/os/ipaq1110/devipaq.c @@ -0,0 +1,762 @@ +/* + * iPAQ H3650 touch screen and other devices + * + * Inferno driver derived from sketchy documentation and + * information gleaned from linux/char/h3650_ts.c + * by Charles Flynn. + * + * Copyright © 2000,2001 Vita Nuova Holdings Limited. All rights reserved. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "keyboard.h" +#include + +#include +#include +#include "screen.h" + +#define DEBUG 0 + +/* + * packet format + * + * SOF (0x02) + * (id<<4) | len byte length + * data[len] bytes + * chk checksum mod 256 excluding SOF + */ + +enum { + Csof = 0x02, + Ceof = 0x03, + Hdrlen = 3, + + /* opcodes */ + + Oversion = 0, + Okeys = 2, + Otouch = 3, + Ordeeprom = 4, + Owreeprom = 5, + Othermal = 6, + Oled = 8, + Obattery = 9, + Ospiread = 11, + Ospiwrite = 12, + Obacklight = 13, + Oextstatus = 0xA1, + }; + +enum { + Powerbit = 0, /* GPIO bit for power on/off key */ +}; + +enum{ + Qdir, + Qctl, + Qtouchctl, + Qbattery, + Qversion, +}; + +static +Dirtab ipaqtab[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "ipaqctl", {Qctl}, 0, 0600, + "battery", {Qbattery}, 0, 0444, + "version", {Qversion}, 0, 0444, + "touchctl", {Qtouchctl}, 0, 0644, +}; + +static struct { + QLock; + Chan* c; + + Lock rl; /* protect cmd, reply */ + int cmd; + Block* reply; + Rendez r; +} atmel; + +/* to and from fixed point */ +#define FX(a,b) (((a)<<16)/(b)) +#define XF(v) ((v)>>16) + +static struct { + Lock; + int rate; + int m[2][3]; /* transformation matrix */ + Point avg; + Point diff; + Point pts[4]; + int n; /* number of points in pts */ + int p; /* current index in pts */ + int down; + int nout; +} touch = { + {0}, + .m {{-FX(1,3), 0, FX(346,1)},{0, -FX(1,4), FX(256, 1)}}, +}; + +/* + * map rocker positions to same codes as plan 9 + */ +static Rune rockermap[2][4] ={ + {Right, Down, Up, Left}, /* landscape */ + {Up, Right, Left, Down}, /* portrait */ +}; + +static Rendez powerevent; + +static void cmdack(int, void*, int); +static int cmdio(int, void*, int, void*, int); +static void ipaqreadproc(void*); +static void powerwaitproc(void*); +static Block* rdevent(Block**); +static long touchctl(char*, long); +static void touched(Block*, int); +static int wrcmd(int, void*, int, void*, int); +static char* acstatus(int); +static char* batstatus(int); +static void powerintr(Ureg*, void*); + +static void +ipaqreset(void) +{ + intrenable(Powerbit, powerintr, nil, BusGPIOfalling, "power off"); +} + +static void +ipaqinit(void) +{ + kproc("powerwait", powerwaitproc, nil, 0); +} + +static Chan* +ipaqattach(char* spec) +{ + int fd; + + qlock(&atmel); + if(waserror()){ + qunlock(&atmel); + nexterror(); + } + if(atmel.c == nil){ + fd = kopen("#t/eia1ctl", ORDWR); + if(fd < 0) + error(up->env->errstr); + kwrite(fd, "b115200", 7); /* it's already pn, l8 */ + kclose(fd); + fd = kopen("#t/eia1", ORDWR); + if(fd < 0) + error(up->env->errstr); + atmel.c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1); + kclose(fd); + atmel.cmd = -1; + kproc("ipaqread", ipaqreadproc, nil, 0); + } + poperror(); + qunlock(&atmel); + return devattach('T', spec); +} + +static Walkqid* +ipaqwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, ipaqtab, nelem(ipaqtab), devgen); +} + +static int +ipaqstat(Chan* c, uchar *db, int n) +{ + return devstat(c, db, n, ipaqtab, nelem(ipaqtab), devgen); +} + +static Chan* +ipaqopen(Chan* c, int omode) +{ + return devopen(c, omode, ipaqtab, nelem(ipaqtab), devgen); +} + +static void +ipaqclose(Chan*) +{ +} + +static long +ipaqread(Chan* c, void* a, long n, vlong offset) +{ + char *tmp, buf[64]; + uchar reply[12]; + int v, p, l; + + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, ipaqtab, nelem(ipaqtab), devgen); + case Qtouchctl: + tmp = malloc(READSTR); + if(waserror()){ + free(tmp); + nexterror(); + } + snprint(tmp, READSTR, "s%d\nr%d\nR%d\nX %d %d %d\nY %d %d %d\n", + 1000, 0, 1, + touch.m[0][0], touch.m[0][1], touch.m[0][2], + touch.m[1][0], touch.m[1][1], touch.m[1][2]); + n = readstr(offset, a, n, tmp); + poperror(); + free(tmp); + break; + case Qbattery: + cmdio(Obattery, reply, 0, reply, sizeof(reply)); + tmp = malloc(READSTR); + if(waserror()){ + free(tmp); + nexterror(); + } + v = (reply[4]<<8)|reply[3]; + p = 425*v/1000 - 298; + snprint(tmp, READSTR, "voltage: %d %dmV %d%% %d\nac: %s\nstatus: %d %s\nchem: %d\n", + v, 1000*v/228, p, 300*p/100, acstatus(reply[1]), reply[5], batstatus(reply[5]), reply[2]); + n = readstr(offset, a, n, tmp); + poperror(); + free(tmp); + break; + case Qversion: + l = cmdio(Oversion, reply, 0, reply, sizeof(reply)); + if(l > 4){ + l--; + memmove(buf, reply+1, 4); + if(l > 8){ + buf[4] = ' '; + memmove(buf+5, reply+5, 4); /* pack version */ + sprint(buf+9, " %.2x\n", reply[9]); /* ``boot type'' */ + }else{ + buf[4] = '\n'; + buf[5] = 0; + } + return readstr(offset, a, n, buf); + } + n=0; + break; + default: + n=0; + break; + } + return n; +} + +static long +ipaqwrite(Chan* c, void* a, long n, vlong) +{ + char cmd[64], op[32], *fields[6]; + int nf; + + switch((ulong)c->qid.path){ + case Qctl: + if(n >= sizeof(cmd)-1) + n = sizeof(cmd)-1; + memmove(cmd, a, n); + cmd[n] = 0; + nf = getfields(cmd, fields, nelem(fields), 1, " \t\n"); + if(nf <= 0) + error(Ebadarg); + if(nf >= 4 && strcmp(fields[0], "light") == 0){ + op[0] = atoi(fields[1]); /* mode */ + op[1] = atoi(fields[2]); /* power */ + op[2] = atoi(fields[3]); /* brightness */ + cmdack(Obacklight, op, 3); + }else if(nf >= 5 && strcmp(fields[0], "led") == 0){ + op[0] = atoi(fields[1]); + op[1] = atoi(fields[2]); + op[2] = atoi(fields[3]); + op[3] = atoi(fields[4]); + cmdack(Oled, op, 4); + }else if(strcmp(fields[0], "suspend") == 0){ + /* let the kproc do it */ + wakeup(&powerevent); + }else + error(Ebadarg); + break; + case Qtouchctl: + return touchctl(a, n); + default: + error(Ebadusefd); + } + return n; +} + +static void +powerintr(Ureg*, void*) +{ + wakeup(&powerevent); +} + +static void +cmdack(int id, void *a, int n) +{ + uchar reply[16]; + + cmdio(id, a, n, reply, sizeof(reply)); +} + +static int +cmdio(int id, void *a, int n, void *reply, int lim) +{ + qlock(&atmel); + if(waserror()){ + qunlock(&atmel); + nexterror(); + } + n = wrcmd(id, a, n, reply, lim); + poperror(); + qunlock(&atmel); + return n; +} + +static int +havereply(void*) +{ + return atmel.reply != nil; +} + +static int +wrcmd(int id, void *a, int n, void *b, int lim) +{ + uchar buf[32]; + int i, sum; + Block *e; + + if(n >= 16) + error(Eio); + lock(&atmel.rl); + atmel.cmd = id; + unlock(&atmel.rl); + buf[0] = Csof; + buf[1] = (id<<4) | (n&0xF); + if(n) + memmove(buf+2, a, n); + sum = 0; + for(i=1; irp[0] != id){ + print("ipaq: rdreply: mismatched reply %d :: %d\n", id, e->rp[0]); + error(Eio); + } + n = BLEN(e); + if(n < lim) + lim = n; + memmove(b, e->rp, lim); + poperror(); + freeb(e); + return lim; +} + +static void +ipaqreadproc(void*) +{ + Block *e, *b, *partial; + int c, mousemod; + + while(waserror()) + print("ipaqread: %r\n"); + partial = nil; + mousemod = 0; + for(;;){ + e = rdevent(&partial); + if(e == nil){ + print("ipaqread: rdevent: %r\n"); + continue; + } + switch(e->rp[0]){ + case Otouch: + touched(e, mousemod); + freeb(e); + break; + case Okeys: + //print("key %2.2ux\n", e->rp[1]); + c = e->rp[1] & 0xF; + if(c >= 6 && c < 10){ /* rocker */ + if((e->rp[1] & 0x80) == 0){ + kbdrepeat(0); + kbdputc(kbdq, rockermap[conf.portrait&1][c-6]); + }else + kbdrepeat(0); + }else{ + /* TO DO: change tkmouse and mousetrack to allow extra buttons */ + if(--c == 0) + c = 5; + if(e->rp[1] & 0x80) + mousemod &= ~(1<rp[0]){ + b = atmel.reply; + atmel.reply = e; + unlock(&atmel.rl); + wakeup(&atmel.r); + if(b != nil) + freeb(b); + }else{ + unlock(&atmel.rl); + print("ipaqread: discard op %d\n", e->rp[0]); + freeb(e); + } + } + } +} + +static Block * +rdevent(Block **bp) +{ + Block *b, *e; + int s, c, len, csum; + enum {Ssof=16, Sid, Ssum}; + + s = Ssof; + csum = 0; + len = 0; + e = nil; + if(waserror()){ + if(e != nil) + freeb(e); + nexterror(); + } + for(;;){ + b = *bp; + *bp = nil; + if(b == nil){ + b = devtab[atmel.c->type]->bread(atmel.c, 128, 0); + if(b == nil) + error(Eio); + if(DEBUG) + iprint("r: %ld\n", BLEN(b)); + } + while(b->rp < b->wp){ + c = *b->rp++; + switch(s){ + case Ssof: + if(c == Csof) + s = Sid; + else if(1) + iprint("!sof: %2.2ux %d\n", c, s); + break; + case Sid: + csum = c; + len = c & 0xF; + e = allocb(len+1); + if(e == nil) + error(Eio); + *e->wp++ = c>>4; /* id */ + if(len) + s = 0; + else + s = Ssum; + break; + case Ssum: + csum &= 0xFF; + if(c != csum){ + iprint("cksum: %2.2ux != %2.2ux\n", c, csum); + s = Ssof; /* try to resynchronise */ + if(e != nil){ + freeb(e); + e = nil; + } + break; + } + if(b->rp < b->wp) + *bp = b; + else + freeb(b); + if(DEBUG){ + int i; + iprint("event: [%ld]", BLEN(e)); + for(i=0; irp[i]); + iprint("\n"); + } + poperror(); + return e; + default: + csum += c; + *e->wp++ = c; + if(++s >= len) + s = Ssum; + break; + } + } + freeb(b); + } + return 0; /* not reached */ +} + +static char * +acstatus(int x) +{ + switch(x){ + case 0: return "offline"; + case 1: return "online"; + case 2: return "backup"; + } + return "unknown"; +} + +static char * +batstatus(int x) +{ + if(x & 0x40) + return "charging"; /* not in linux but seems to be on mine */ + switch(x){ + case 0: return "ok"; + case 1: return "high"; + case 2: return "low"; + case 4: return "critical"; + case 8: return "charging"; + case 0x80: return "none"; + } + return "unknown"; +} + +static int +ptmap(int *m, int x, int y) +{ + return XF(m[0]*x + m[1]*y + m[2]); +} + +static void +touched(Block *b, int buttons) +{ + int rx, ry, x, y, dx, dy, n; + Point op, *lp, cur; + + if(BLEN(b) == 5){ + /* id Xhi Xlo Yhi Ylo */ + if(touch.down < 0){ + touch.down = 0; + return; + } + rx = (b->rp[1]<<8)|b->rp[2]; + ry = (b->rp[3]<<8)|b->rp[4]; + if(conf.portrait){ + dx = rx; rx = ry; ry = dx; + } + if(touch.down == 0){ + touch.nout = 0; + touch.p = 1; + touch.n = 1; + touch.avg = Pt(rx, ry); + touch.pts[0] = touch.avg; + touch.down = 1; + return; + } + n = touch.p-1; + if(n < 0) + n = nelem(touch.pts)-1; + lp = &touch.pts[n]; /* last point */ + if(touch.n > 0 && (rx-lp->x)*(ry-lp->y) > 50*50){ /* far out */ + if(++touch.nout > 3){ + touch.down = 0; + touch.n = 0; + } + return; + } + op = touch.pts[touch.p]; + touch.pts[touch.p] = Pt(rx, ry); + touch.p = (touch.p+1) % nelem(touch.pts); + touch.avg.x += rx; + touch.avg.y += ry; + if(touch.n < nelem(touch.pts)){ + touch.n++; + return; + } + touch.avg.x -= op.x; + touch.avg.y -= op.y; + cur = mousexy(); + rx = touch.avg.x/touch.n; + ry = touch.avg.y/touch.n; + x = ptmap(touch.m[0], rx, ry); + dx = x-cur.x; + y = ptmap(touch.m[1], rx, ry); + dy = y-cur.y; + if(dx*dx + dy*dy <= 2){ + dx = 0; + dy = 0; + } + if(buttons == 0) + buttons = 1<<0; /* by default, stylus down implies button 1 */ + mousetrack(buttons&0x1f, dx, dy, 1); /* TO DO: allow more than 3 buttons */ + /* TO DO: swcursupdate(oldx, oldy, x, y); */ + touch.down = 1; + }else{ + if(touch.down){ + mousetrack(0, 0, 0, 1); /* stylus up */ + touch.down = 0; + }else + touch.down = -1; + touch.n = 0; + touch.p = 0; + touch.avg.x = 0; + touch.avg.y = 0; + } +} + +/* + * touchctl commands: + * X a b c - set X transformation + * Y d e f - set Y transformation + * s - set sample delay in millisec per sample + * r - set read delay in microsec + * R - set log2 of number of readings to average + */ +static long +touchctl(char* a, long n) +{ + char buf[64]; + char *cp; + int n0 = n; + int bn; + char *field[8]; + int nf, cmd, pn, m[2][3]; + + while(n) { + bn = (cp = memchr(a, '\n', n))!=nil ? cp-a+1 : n; + n -= bn; + cp = a; + a += bn; + bn = bn > sizeof(buf)-1 ? sizeof(buf)-1 : bn; + memmove(buf, cp, bn); + buf[bn] = '\0'; + nf = getfields(buf, field, nelem(field), 1, " \t\n"); + if(nf <= 0) + continue; + if(strcmp(field[0], "calibrate") == 0){ + if(nf == 1){ + lock(&touch); + memset(touch.m, 0, sizeof(touch.m)); + touch.m[0][0] = FX(1,1); + touch.m[1][1] = FX(1,1); + unlock(&touch); + }else if(nf >= 5){ + memset(m, 0, sizeof(m)); + m[0][0] = strtol(field[1], 0, 0); + m[1][1] = strtol(field[2], 0, 0); + m[0][2] = strtol(field[3], 0, 0); + m[1][2] = strtol(field[4], 0, 0); + if(nf > 5) + m[0][1] = strtol(field[5], 0, 0); + if(nf > 6) + m[1][0] = strtol(field[6], 0, 0); + lock(&touch); + memmove(touch.m, m, sizeof(touch.m[0])); + unlock(&touch); + }else + error(Ebadarg); + continue; + } + cmd = *field[0]++; + pn = *field[0] == 0; + switch(cmd) { + case 's': + pn = strtol(field[pn], 0, 0); + if(pn <= 0) + error(Ebadarg); + touch.rate = pn; + break; + case 'r': + /* touch read delay */ + break; + case 'X': + case 'Y': + if(nf < pn+2) + error(Ebadarg); + m[0][0] = strtol(field[pn], 0, 0); + m[0][1] = strtol(field[pn+1], 0, 0); + m[0][2] = strtol(field[pn+2], 0, 0); + lock(&touch); + memmove(touch.m[cmd=='Y'], m[0], sizeof(touch.m[0])); + unlock(&touch); + break; + default: + error(Ebadarg); + } + } + return n0-n; +} + +/* + * this might belong elsewhere + */ +static int +powerwait(void*) +{ + return (GPIOREG->gplr & GPIO_PWR_ON_i) == 0; +} + +static void +powerwaitproc(void*) +{ + for(;;){ + sleep(&powerevent, powerwait, nil); + do{ + tsleep(&up->sleep, return0, nil, 50); + }while((GPIOREG->gplr & GPIO_PWR_ON_i) == 0); + powersuspend(); + } +} + +Dev ipaqdevtab = { + 'T', + "ipaq", + + ipaqreset, + ipaqinit, + devshutdown, + ipaqattach, + ipaqwalk, + ipaqstat, + ipaqopen, + devcreate, + ipaqclose, + ipaqread, + devbread, + ipaqwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/ipaq1110/etherwavelan.c b/os/ipaq1110/etherwavelan.c new file mode 100644 index 00000000..519e40f2 --- /dev/null +++ b/os/ipaq1110/etherwavelan.c @@ -0,0 +1,1307 @@ +/* + Lucent Wavelan IEEE 802.11 pcmcia. + There is almost no documentation for the card. + the driver is done using both the FreeBSD, Linux and + original Plan 9 drivers as `documentation'. + + Has been used with the card plugged in during all up time. + no cards removals/insertions yet. + + For known BUGS see the comments below. Besides, + the driver keeps interrupts disabled for just too + long. When it gets robust, locks should be revisited. + + BUGS: check endian, alignment and mem/io issues; + multicast; + receive watchdog interrupts. + TODO: automatic power management; + improve locking. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" +#include "etherif.h" + +#define print iprint +#define DEBUG if(1)iprint + +#define SEEKEYS 1 + +typedef struct Ctlr Ctlr; +typedef struct Wltv Wltv; +typedef struct WFrame WFrame; +typedef struct Stats Stats; +typedef struct WStats WStats; +typedef struct WKey WKey; + +struct WStats +{ + ulong ntxuframes; // unicast frames + ulong ntxmframes; // multicast frames + ulong ntxfrags; // fragments + ulong ntxubytes; // unicast bytes + ulong ntxmbytes; // multicast bytes + ulong ntxdeferred; // deferred transmits + ulong ntxsretries; // single retries + ulong ntxmultiretries; // multiple retries + ulong ntxretrylimit; + ulong ntxdiscards; + ulong nrxuframes; // unicast frames + ulong nrxmframes; // multicast frames + ulong nrxfrags; // fragments + ulong nrxubytes; // unicast bytes + ulong nrxmbytes; // multicast bytes + ulong nrxfcserr; + ulong nrxdropnobuf; + ulong nrxdropnosa; + ulong nrxcantdecrypt; + ulong nrxmsgfrag; + ulong nrxmsgbadfrag; + ulong end; +}; + +struct WFrame +{ + ushort sts; + ushort rsvd0; + ushort rsvd1; + ushort qinfo; + ushort rsvd2; + ushort rsvd3; + ushort txctl; + ushort framectl; + ushort id; + uchar addr1[Eaddrlen]; + uchar addr2[Eaddrlen]; + uchar addr3[Eaddrlen]; + ushort seqctl; + uchar addr4[Eaddrlen]; + ushort dlen; + uchar dstaddr[Eaddrlen]; + uchar srcaddr[Eaddrlen]; + ushort len; + ushort dat[3]; + ushort type; +}; + +// Lucent's Length-Type-Value records to talk to the wavelan. +// most operational parameters are read/set using this. +enum +{ + WTyp_Stats = 0xf100, + WTyp_Ptype = 0xfc00, + WTyp_Mac = 0xfc01, + WTyp_WantName = 0xfc02, + WTyp_Chan = 0xfc03, + WTyp_NetName = 0xfc04, + WTyp_ApDens = 0xfc06, + WTyp_MaxLen = 0xfc07, + WTyp_PM = 0xfc09, + WTyp_PMWait = 0xfc0c, + WTyp_NodeName = 0xfc0e, + WTyp_Crypt = 0xfc20, + WTyp_XClear = 0xfc22, + WTyp_Tick = 0xfce0, + WTyp_RtsThres = 0xfc83, + WTyp_TxRate = 0xfc84, + WTx1Mbps = 0x0, + WTx2Mbps = 0x1, + WTxAuto = 0x3, + WTyp_Prom = 0xfc85, + WTyp_Keys = 0xfcb0, + WTyp_TxKey = 0xfcb1, + WTyp_StationID = 0xfd20, + WTyp_CurName = 0xfd41, + WTyp_BaseID = 0xfd42, // ID of the currently connected-to base station + WTyp_CurTxRate = 0xfd44, // Current TX rate + WTyp_HasCrypt = 0xfd4f, +}; + +// Controller +enum +{ + WDfltIRQ = 3, // default irq + WDfltIOB = 0x180, // default IO base + + WIOLen = 0x40, // Hermes IO length + + WTmOut = 65536, // Cmd time out + + WPTypePeerToPeer = 0, + WPTypeManaged = 1, + WPTypeWDS = 2, + WPTypeAdHoc = 3, + WDfltPType = WPTypeManaged, + + WDfltApDens = 1, + WDfltRtsThres = 2347, // == disabled + WDfltTxRate = WTxAuto, // 2Mbps + + WMaxLen = 2304, + WNameLen = 32, + + WNKeys = 4, + WKeyLen = 14, + WMinKeyLen = 5, + + // Wavelan hermes registers + WR_Cmd = 0x00, + WCmdIni = 0x0000, + WCmdEna = 0x0001, + WCmdDis = 0x0002, + WCmdTx = 0x000b, + WCmdMalloc = 0x000a, + WCmdAskStats = 0x0011, + WCmdMsk = 0x003f, + WCmdAccRd = 0x0021, + WCmdReclaim = 0x0100, + WCmdAccWr = 0x0121, + WCmdBusy = 0x8000, + WR_Parm0 = 0x02, + WR_Parm1 = 0x04, + WR_Parm2 = 0x06, + WR_Sts = 0x08, + WR_InfoId = 0x10, + WR_Sel0 = 0x18, + WR_Sel1 = 0x1a, + WR_Off0 = 0x1c, + WR_Off1 = 0x1e, + WBusyOff = 0x8000, + WErrOff = 0x4000, + WResSts = 0x7f00, + WR_RXId = 0x20, + WR_Alloc = 0x22, + WR_EvSts = 0x30, + WR_IntEna = 0x32, + WCmdEv = 0x0010, + WRXEv = 0x0001, + WTXEv = 0x0002, + WTxErrEv = 0x0004, + WAllocEv = 0x0008, + WInfoEv = 0x0080, + WIDropEv = 0x2000, + WTickEv = 0x8000, + WEvs = WRXEv|WTXEv|WAllocEv|WInfoEv|WIDropEv, + + WR_EvAck = 0x34, + WR_Data0 = 0x36, + WR_Data1 = 0x38, + + // Frame stuff + + WF_Err = 0x0003, + WF_1042 = 0x2000, + WF_Tunnel = 0x4000, + WF_WMP = 0x6000, + + WF_Data = 0x0008, + + WSnapK1 = 0xaa, + WSnapK2 = 0x00, + WSnapCtlr = 0x03, + WSnap0 = (WSnapK1|(WSnapK1<<8)), + WSnap1 = (WSnapK2|(WSnapCtlr<<8)), + WSnapHdrLen = 6, + + WF_802_11_Off = 0x44, + WF_802_3_Off = 0x2e, + +}; + +#define csr_outs(ctlr,r,arg) outs((ctlr)->iob+(r),(arg)) +#define csr_ins(ctlr,r) ins((ctlr)->iob+(r)) +#define csr_ack(ctlr,ev) outs((ctlr)->iob+WR_EvAck,(ev)) + +struct WKey +{ + ushort len; + char dat[WKeyLen]; +}; + +struct Wltv +{ + ushort len; + ushort type; + union + { + struct { + ushort val; + ushort pad; + }; + struct { + uchar addr[8]; + }; + struct { + ushort slen; + char s[WNameLen]; + }; + struct { + char name[WNameLen]; + }; + struct { + WKey keys[WNKeys]; + }; + }; +}; + +// What the driver thinks. Not what the card thinks. +struct Stats +{ + ulong nints; + ulong nrx; + ulong ntx; + ulong ntxrq; + ulong nrxerr; + ulong ntxerr; + ulong nalloc; // allocation (reclaim) events + ulong ninfo; + ulong nidrop; + ulong nwatchdogs; // transmit time outs, actually + int ticks; + int tickintr; + int signal; + int noise; +}; + +struct Ctlr +{ + Lock; + Rendez timer; + + int attached; + int slot; + int iob; + int ptype; + int apdensity; + int rtsthres; + int txbusy; + int txrate; + int txdid; + int txmid; + int txtmout; + int maxlen; + int chan; + int pmena; + int pmwait; + + char netname[WNameLen]; + char wantname[WNameLen]; + char nodename[WNameLen]; + WFrame txf; + uchar txbuf[1536]; + + int hascrypt; // card has encryption + int crypt; // encryption off/on + int txkey; // transmit key + Wltv keys; // default keys + int xclear; // exclude clear packets off/on + + Stats; + WStats; +}; + +// w_... routines do not ilock the Ctlr and should +// be called locked. + +static void +w_intdis(Ctlr* ctlr) +{ + csr_outs(ctlr, WR_IntEna, 0); + csr_ack(ctlr, 0xffff); +} + +static void +w_intena(Ctlr* ctlr) +{ + csr_outs(ctlr, WR_IntEna, WEvs); +} + +static int +w_cmd(Ctlr *ctlr, ushort cmd, ushort arg) +{ + int i, rc; + + csr_outs(ctlr, WR_Parm0, arg); + csr_outs(ctlr, WR_Cmd, cmd); + for (i = 0; itype)){ + DEBUG("wavelan: access read failed\n"); + return -1; + } + if (w_seek(ctlr,ltv->type,0,1)){ + DEBUG("wavelan: seek failed\n"); + return -1; + } + len = csr_ins(ctlr, WR_Data1); + if (len > ltv->len) + return -1; + ltv->len = len; + if ((code=csr_ins(ctlr, WR_Data1)) != ltv->type){ + DEBUG("wavelan: type %x != code %x\n",ltv->type,code); + return -1; + } + if(ltv->len > 0) + inss((ctlr)->iob+(WR_Data1), <v->val, ltv->len-1); + + return 0; +} + +static void +w_outltv(Ctlr* ctlr, Wltv* ltv) +{ + if(w_seek(ctlr,ltv->type, 0, 1)) + return; + outss((ctlr)->iob+(WR_Data1), ltv, ltv->len+1); + w_cmd(ctlr, WCmdAccWr, ltv->type); +} + +static void +ltv_outs(Ctlr* ctlr, int type, ushort val) +{ + Wltv ltv; + + ltv.len = 2; + ltv.type = type; + ltv.val = val; + w_outltv(ctlr, <v); +} + +static int +ltv_ins(Ctlr* ctlr, int type) +{ + Wltv ltv; + + ltv.len = 2; + ltv.type = type; + ltv.val = 0; + if(w_inltv(ctlr, <v)) + return -1; + return ltv.val; +} + +static void +ltv_outstr(Ctlr* ctlr, int type, char* val) +{ + Wltv ltv; + int len; + + len = strlen(val); + if(len > sizeof(ltv.s)) + len = sizeof(ltv.s); + memset(<v, 0, sizeof(ltv)); + ltv.len = (sizeof(ltv.type)+sizeof(ltv.slen)+sizeof(ltv.s))/2; + ltv.type = type; + +// This should be ltv.slen = len; according to Axel Belinfante + ltv.slen = len; + + strncpy(ltv.s, val, len); + w_outltv(ctlr, <v); +} + +static char Unkname[] = "who knows"; +static char Nilname[] = "card does not tell"; + +static char* +ltv_inname(Ctlr* ctlr, int type) +{ + static Wltv ltv; + int len; + + memset(<v,0,sizeof(ltv)); + ltv.len = WNameLen/2+2; + ltv.type = type; + if (w_inltv(ctlr, <v)) + return Unkname; + len = ltv.slen; + if(len == 0 || ltv.s[0] == 0) + return Nilname; + if(len >= sizeof ltv.s) + len = sizeof ltv.s - 1; + ltv.s[len] = '\0'; + return ltv.s; +} + +static int +w_read(Ctlr* ctlr, int type, int off, void* buf, ulong len) +{ + if (w_seek(ctlr, type, off, 1)){ + DEBUG("wavelan: w_read: seek failed"); + return 0; + } + inss((ctlr)->iob+(WR_Data1), buf, len/2); + + return len; +} + +static int +w_write(Ctlr* ctlr, int type, int off, void* buf, ulong len) +{ + int tries; + + for (tries=0; tries < WTmOut; tries++){ + if (w_seek(ctlr, type, off, 0)){ + DEBUG("wavelan: w_write: seek failed\n"); + return 0; + } + + outss((ctlr)->iob+(WR_Data0), buf, len/2); + + csr_outs(ctlr, WR_Data0, 0xdead); + csr_outs(ctlr, WR_Data0, 0xbeef); + if (w_seek(ctlr, type, off + len, 0)){ + DEBUG("wavelan: write seek failed\n"); + return 0; + } + if (csr_ins(ctlr, WR_Data0) == 0xdead) + if (csr_ins(ctlr, WR_Data0) == 0xbeef) + return len; + DEBUG("wavelan: Hermes bug byte.\n"); + return 0; + } + DEBUG("wavelan: tx timeout\n"); + return 0; +} + +static int +w_alloc(Ctlr* ctlr, int len) +{ + int rc; + int i,j; + + if (w_cmd(ctlr, WCmdMalloc, len)==0) + for (i = 0; ictlr; + + if (!ctlr) + return -1; + + w_intdis(ctlr); + w_cmd(ctlr, WCmdDis, 0); + w_intdis(ctlr); + if(w_cmd(ctlr, WCmdIni, 0)) + return -1; + w_intdis(ctlr); + + ltv_outs(ctlr, WTyp_Tick, 8); + ltv_outs(ctlr, WTyp_MaxLen, ctlr->maxlen); + ltv_outs(ctlr, WTyp_Ptype, ctlr->ptype); + ltv_outs(ctlr, WTyp_RtsThres, ctlr->rtsthres); + ltv_outs(ctlr, WTyp_TxRate, ctlr->txrate); + ltv_outs(ctlr, WTyp_ApDens, ctlr->apdensity); + ltv_outs(ctlr, WTyp_PMWait, ctlr->pmwait); + ltv_outs(ctlr, WTyp_PM, ctlr->pmena); + if (*ctlr->netname) + ltv_outstr(ctlr, WTyp_NetName, ctlr->netname); + if (*ctlr->wantname) + ltv_outstr(ctlr, WTyp_WantName, ctlr->wantname); + ltv_outs(ctlr, WTyp_Chan, ctlr->chan); + if (*ctlr->nodename) + ltv_outstr(ctlr, WTyp_NodeName, ctlr->nodename); + ltv.len = 4; + ltv.type = WTyp_Mac; + memmove(ltv.addr, ether->ea, Eaddrlen); + w_outltv(ctlr, <v); + + ltv_outs(ctlr, WTyp_Prom, (ether->prom?1:0)); + + if (ctlr->hascrypt){ + ltv_outs(ctlr, WTyp_Crypt, ctlr->crypt); + ltv_outs(ctlr, WTyp_TxKey, ctlr->txkey); + w_outltv(ctlr, &ctlr->keys); + ltv_outs(ctlr, WTyp_XClear, ctlr->xclear); + } + + // BUG: set multicast addresses + + if (w_cmd(ctlr, WCmdEna, 0)){ + DEBUG("wavelan: Enable failed"); + return -1; + } + ctlr->txdid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8); + ctlr->txmid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8); + if (ctlr->txdid == -1 || ctlr->txmid == -1) + DEBUG("wavelan: alloc failed"); + ctlr->txbusy = 0; + w_intena(ctlr); + return 0; +} + +static void +w_rxdone(Ether* ether) +{ + Ctlr* ctlr = (Ctlr*) ether->ctlr; + int len, sp; + WFrame f; + Block* bp=0; + Etherpkt* ep; + + sp = csr_ins(ctlr, WR_RXId); + len = w_read(ctlr, sp, 0, &f, sizeof(f)); + if (len == 0){ + DEBUG("wavelan: read frame error\n"); + goto rxerror; + } + if (f.sts&WF_Err){ + goto rxerror; + } + switch(f.sts){ + case WF_1042: + case WF_Tunnel: + case WF_WMP: + len = f.dlen + WSnapHdrLen; + bp = iallocb(ETHERHDRSIZE + len + 2); + if (!bp) + goto rxerror; + ep = (Etherpkt*) bp->wp; + memmove(ep->d, f.addr1, Eaddrlen); + memmove(ep->s, f.addr2, Eaddrlen); + memmove(ep->type,&f.type,2); + bp->wp += ETHERHDRSIZE; + if (w_read(ctlr, sp, WF_802_11_Off, bp->wp, len+2) == 0){ + DEBUG("wavelan: read 802.11 error\n"); + goto rxerror; + } + bp->wp = bp->rp+(ETHERHDRSIZE+f.dlen); + break; + default: + len = ETHERHDRSIZE + f.dlen + 2; + bp = iallocb(len); + if (!bp) + goto rxerror; + if (w_read(ctlr, sp, WF_802_3_Off, bp->wp, len) == 0){ + DEBUG("wavelan: read 800.3 error\n"); + goto rxerror; + } + bp->wp += len; + } + + ctlr->nrx++; + etheriq(ether,bp,1); + ctlr->signal = ((ctlr->signal*15)+((f.qinfo>>8) & 0xFF))/16; + ctlr->noise = ((ctlr->noise*15)+(f.qinfo & 0xFF))/16; + return; + +rxerror: + freeb(bp); + ctlr->nrxerr++; +} + +static void +w_txstart(Ether* ether) +{ + Etherpkt *pkt; + Ctlr *ctlr; + Block *bp; + int len, off; + + if((ctlr = ether->ctlr) == nil || ctlr->attached == 0 || ctlr->txbusy) + return; + + if((bp = qget(ether->oq)) == nil) + return; + pkt = (Etherpkt*)bp->rp; + + // + // If the packet header type field is > 1500 it is an IP or + // ARP datagram, otherwise it is an 802.3 packet. See RFC1042. + // + memset(&ctlr->txf, 0, sizeof(ctlr->txf)); + if(((pkt->type[0]<<8)|pkt->type[1]) > 1500){ + ctlr->txf.framectl = WF_Data; + memmove(ctlr->txf.addr1, pkt->d, Eaddrlen); + memmove(ctlr->txf.addr2, pkt->s, Eaddrlen); + memmove(ctlr->txf.dstaddr, pkt->d, Eaddrlen); + memmove(ctlr->txf.srcaddr, pkt->s, Eaddrlen); + memmove(&ctlr->txf.type, pkt->type, 2); + bp->rp += ETHERHDRSIZE; + len = BLEN(bp); + off = WF_802_11_Off; + ctlr->txf.dlen = len+ETHERHDRSIZE-WSnapHdrLen; + hnputs((uchar*)&ctlr->txf.dat[0], WSnap0); + hnputs((uchar*)&ctlr->txf.dat[1], WSnap1); + hnputs((uchar*)&ctlr->txf.len, len+ETHERHDRSIZE-WSnapHdrLen); + } + else{ + len = BLEN(bp); + off = WF_802_3_Off; + ctlr->txf.dlen = len; + } + w_write(ctlr, ctlr->txdid, 0, &ctlr->txf, sizeof(ctlr->txf)); + w_write(ctlr, ctlr->txdid, off, bp->rp, len+2); + + if(w_cmd(ctlr, WCmdReclaim|WCmdTx, ctlr->txdid)){ + DEBUG("wavelan: transmit failed\n"); + ctlr->ntxerr++; + } + else{ + ctlr->txbusy = 1; + ctlr->txtmout = 2; + } + freeb(bp); +} + +static void +w_txdone(Ctlr* ctlr, int sts) +{ + ctlr->txbusy = 0; + ctlr->txtmout = 0; + if (sts & WTxErrEv) + ctlr->ntxerr++; + else + ctlr->ntx++; +} + +static int +w_stats(Ctlr* ctlr) +{ + int i, rc, sp; + Wltv ltv; + ulong* p = (ulong*)&ctlr->WStats; + ulong* pend = (ulong*)&ctlr->end; + + sp = csr_ins(ctlr, WR_InfoId); + ltv.len = ltv.type = 0; + w_read(ctlr, sp, 0, <v, 4); + if (ltv.type == WTyp_Stats){ + ltv.len--; + for (i = 0; i < ltv.len && p < pend; i++){ + rc = csr_ins(ctlr, WR_Data1); + if (rc > 0xf000) + rc = ~rc & 0xffff; + p[i] += rc; + } + return 0; + } + return -1; +} + +static void +w_intr(Ether *ether) +{ + int rc, txid; + Ctlr* ctlr = (Ctlr*) ether->ctlr; + + if (ctlr->attached == 0){ + csr_ack(ctlr, 0xffff); + csr_outs(ctlr, WR_IntEna, 0); + return; + } + + csr_outs(ctlr, WR_IntEna, 0); + rc = csr_ins(ctlr, WR_EvSts); + csr_ack(ctlr, ~WEvs); // Not interested on them + + if (rc & WRXEv){ + w_rxdone(ether); + csr_ack(ctlr, WRXEv); + } + if (rc & WTXEv){ + w_txdone(ctlr, rc); + csr_ack(ctlr, WTXEv); + } + if (rc & WAllocEv){ + ctlr->nalloc++; + txid = csr_ins(ctlr, WR_Alloc); + csr_ack(ctlr, WAllocEv); + if (txid == ctlr->txdid){ + if ((rc & WTXEv) == 0) + w_txdone(ctlr, rc); + } + } + if (rc & WInfoEv){ + ctlr->ninfo++; + w_stats(ctlr); + csr_ack(ctlr, WInfoEv); + } + if (rc & WTxErrEv){ + w_txdone(ctlr, rc); + csr_ack(ctlr, WTxErrEv); + } + if (rc & WIDropEv){ + ctlr->nidrop++; + csr_ack(ctlr, WIDropEv); + } + + w_intena(ctlr); + w_txstart(ether); +} + +// Watcher to ensure that the card still works properly and +// to request WStats updates once a minute. +// BUG: it runs much more often, see the comment below. + +static void +w_timer(void* arg) +{ + Ether* ether = (Ether*) arg; + Ctlr* ctlr = (Ctlr*)ether->ctlr; + + for(;;){ + tsleep(&ctlr->timer, return0, 0, 50); + ctlr = (Ctlr*)ether->ctlr; + if (ctlr == 0) + break; + if (ctlr->attached == 0) + continue; + ctlr->ticks++; + + ilock(ctlr); + + // Seems that the card gets frames BUT does + // not send the interrupt; this is a problem because + // I suspect it runs out of receive buffers and + // stops receiving until a transmit watchdog + // reenables the card. + // The problem is serious because it leads to + // poor rtts. + // This can be seen clearly by commenting out + // the next if and doing a ping: it will stop + // receiving (although the icmp replies are being + // issued from the remote) after a few seconds. + // Of course this `bug' could be because I'm reading + // the card frames in the wrong way; due to the + // lack of documentation I cannot know. + +// if (csr_ins(ctlr, WR_EvSts)&WEvs){ +// ctlr->tickintr++; +// w_intr(ether); +// } + + if ((ctlr->ticks % 10) == 0) { + if (ctlr->txtmout && --ctlr->txtmout == 0){ + ctlr->nwatchdogs++; + w_txdone(ctlr, WTxErrEv); + if (w_enable(ether)){ + DEBUG("wavelan: wdog enable failed\n"); + } + w_txstart(ether); + } + if ((ctlr->ticks % 120) == 0) + if (ctlr->txbusy == 0) + w_cmd(ctlr, WCmdAskStats, WTyp_Stats); + } + iunlock(ctlr); + } + pexit("terminated",0); +} + +static void +multicast(void*, uchar*, int) +{ + // BUG: to be added. +} + +static void +attach(Ether* ether) +{ + Ctlr* ctlr; + char name[64]; + int rc; + + if (ether->ctlr == 0) + return; + + snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno); + ctlr = (Ctlr*) ether->ctlr; + if (ctlr->attached == 0){ + ilock(ctlr); + rc = w_enable(ether); + iunlock(ctlr); + if(rc == 0){ + ctlr->attached = 1; + kproc(name, w_timer, ether, 0); + } else + print("#l%d: enable failed\n",ether->ctlrno); + } +} + +#define PRINTSTAT(fmt,val) l += snprint(p+l, READSTR-l, (fmt), (val)) +#define PRINTSTR(fmt) l += snprint(p+l, READSTR-l, (fmt)) + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + Ctlr *ctlr = (Ctlr*) ether->ctlr; + char *k, *p; + int i, l, txid; + + ether->oerrs = ctlr->ntxerr; + ether->crcs = ctlr->nrxfcserr; + ether->frames = 0; + ether->buffs = ctlr->nrxdropnobuf; + ether->overflows = 0; + + // + // Offset must be zero or there's a possibility the + // new data won't match the previous read. + // + if(n == 0 || offset != 0) + return 0; + + p = malloc(READSTR); + l = 0; + + PRINTSTAT("Signal: %d\n", ctlr->signal-149); + PRINTSTAT("Noise: %d\n", ctlr->noise-149); + PRINTSTAT("SNR: %ud\n", ctlr->signal-ctlr->noise); + PRINTSTAT("Interrupts: %lud\n", ctlr->nints); + PRINTSTAT("TxPackets: %lud\n", ctlr->ntx); + PRINTSTAT("RxPackets: %lud\n", ctlr->nrx); + PRINTSTAT("TxErrors: %lud\n", ctlr->ntxerr); + PRINTSTAT("RxErrors: %lud\n", ctlr->nrxerr); + PRINTSTAT("TxRequests: %lud\n", ctlr->ntxrq); + PRINTSTAT("AllocEvs: %lud\n", ctlr->nalloc); + PRINTSTAT("InfoEvs: %lud\n", ctlr->ninfo); + PRINTSTAT("InfoDrop: %lud\n", ctlr->nidrop); + PRINTSTAT("WatchDogs: %lud\n", ctlr->nwatchdogs); + PRINTSTAT("Ticks: %ud\n", ctlr->ticks); + PRINTSTAT("TickIntr: %ud\n", ctlr->tickintr); + k = ((ctlr->attached) ? "attached" : "not attached"); + PRINTSTAT("Card %s", k); + k = ((ctlr->txbusy)? ", txbusy" : ""); + PRINTSTAT("%s\n", k); + + if (ctlr->hascrypt){ + PRINTSTR("Keys: "); + for (i = 0; i < WNKeys; i++){ + if (ctlr->keys.keys[i].len == 0) + PRINTSTR("none "); + else if (SEEKEYS == 0) + PRINTSTR("set "); + else + PRINTSTAT("%s ", ctlr->keys.keys[i].dat); + } + PRINTSTR("\n"); + } + + // real card stats + ilock(ctlr); + PRINTSTR("\nCard stats: \n"); + PRINTSTAT("Status: %ux\n", csr_ins(ctlr, WR_Sts)); + PRINTSTAT("Event status: %ux\n", csr_ins(ctlr, WR_EvSts)); + i = ltv_ins(ctlr, WTyp_Ptype); + PRINTSTAT("Port type: %d\n", i); + PRINTSTAT("Transmit rate: %d\n", ltv_ins(ctlr, WTyp_TxRate)); + PRINTSTAT("Current Transmit rate: %d\n", + ltv_ins(ctlr, WTyp_CurTxRate)); + PRINTSTAT("Channel: %d\n", ltv_ins(ctlr, WTyp_Chan)); + PRINTSTAT("AP density: %d\n", ltv_ins(ctlr, WTyp_ApDens)); + PRINTSTAT("Promiscuous mode: %d\n", ltv_ins(ctlr, WTyp_Prom)); + if(i == 3) + PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName)); + else { + Wltv ltv; + PRINTSTAT("Current name: %s\n", ltv_inname(ctlr, WTyp_CurName)); + ltv.type = WTyp_BaseID; + ltv.len = 4; + if (w_inltv(ctlr, <v)) + print("#l%d: unable to read base station mac addr\n", ether->ctlrno); + l += snprint(p+l, READSTR-l, "Base station: %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n", + ltv.addr[0], ltv.addr[1], ltv.addr[2], ltv.addr[3], ltv.addr[4], ltv.addr[5]); + } + PRINTSTAT("Net name: %s\n", ltv_inname(ctlr, WTyp_WantName)); + PRINTSTAT("Node name: %s\n", ltv_inname(ctlr, WTyp_NodeName)); + if (ltv_ins(ctlr, WTyp_HasCrypt) == 0) + PRINTSTR("WEP: not supported\n"); + else { + if (ltv_ins(ctlr, WTyp_Crypt) == 0) + PRINTSTR("WEP: disabled\n"); + else{ + PRINTSTR("WEP: enabled\n"); + k = ((ctlr->xclear)? "excluded": "included"); + PRINTSTAT("Clear packets: %s\n", k); + txid = ltv_ins(ctlr, WTyp_TxKey); + PRINTSTAT("Transmit key id: %d\n", txid); + } + } + iunlock(ctlr); + + PRINTSTAT("ntxuframes: %lud\n", ctlr->ntxuframes); + PRINTSTAT("ntxmframes: %lud\n", ctlr->ntxmframes); + PRINTSTAT("ntxfrags: %lud\n", ctlr->ntxfrags); + PRINTSTAT("ntxubytes: %lud\n", ctlr->ntxubytes); + PRINTSTAT("ntxmbytes: %lud\n", ctlr->ntxmbytes); + PRINTSTAT("ntxdeferred: %lud\n", ctlr->ntxdeferred); + PRINTSTAT("ntxsretries: %lud\n", ctlr->ntxsretries); + PRINTSTAT("ntxmultiretries: %lud\n", ctlr->ntxmultiretries); + PRINTSTAT("ntxretrylimit: %lud\n", ctlr->ntxretrylimit); + PRINTSTAT("ntxdiscards: %lud\n", ctlr->ntxdiscards); + PRINTSTAT("nrxuframes: %lud\n", ctlr->nrxuframes); + PRINTSTAT("nrxmframes: %lud\n", ctlr->nrxmframes); + PRINTSTAT("nrxfrags: %lud\n", ctlr->nrxfrags); + PRINTSTAT("nrxubytes: %lud\n", ctlr->nrxubytes); + PRINTSTAT("nrxmbytes: %lud\n", ctlr->nrxmbytes); + PRINTSTAT("nrxfcserr: %lud\n", ctlr->nrxfcserr); + PRINTSTAT("nrxdropnobuf: %lud\n", ctlr->nrxdropnobuf); + PRINTSTAT("nrxdropnosa: %lud\n", ctlr->nrxdropnosa); + PRINTSTAT("nrxcantdecrypt: %lud\n", ctlr->nrxcantdecrypt); + PRINTSTAT("nrxmsgfrag: %lud\n", ctlr->nrxmsgfrag); + PRINTSTAT("nrxmsgbadfrag: %lud\n", ctlr->nrxmsgbadfrag); + USED(l); + n = readstr(offset, a, n, p); + free(p); + return n; +} +#undef PRINTSTR +#undef PRINTSTAT + +static int +w_option(Ctlr* ctlr, char* buf, long n) +{ + char *p; + int i, r; + WKey *key; + Cmdbuf *cb; + + r = 0; + + cb = parsecmd(buf, n); + if(cb->nf < 2) + r = -1; + else if(cistrcmp(cb->f[0], "essid") == 0){ + if (cistrcmp(cb->f[1],"default") == 0) + p = ""; + else + p = cb->f[1]; + switch(ctlr->ptype){ + case 0: + case 3: + memset(ctlr->netname, 0, sizeof(ctlr->netname)); + strncpy(ctlr->netname, p, WNameLen); + if(ctlr->ptype == 3) + break; + /* fall through to set both for peer-to-peer */ + default: + memset(ctlr->wantname, 0, sizeof(ctlr->wantname)); + strncpy(ctlr->wantname, p, WNameLen); + break; + } + } + else if(cistrcmp(cb->f[0], "station") == 0){ + memset(ctlr->nodename, 0, sizeof(ctlr->nodename)); + strncpy(ctlr->nodename, cb->f[1], WNameLen); + } + else if(cistrcmp(cb->f[0], "channel") == 0){ + if((i = atoi(cb->f[1])) >= 1 && i <= 16) + ctlr->chan = i; + else + r = -1; + } + else if(cistrcmp(cb->f[0], "mode") == 0){ + if(cistrcmp(cb->f[1], "managed") == 0) + ctlr->ptype = WPTypeManaged; + else if(cistrcmp(cb->f[1], "wds") == 0) + ctlr->ptype = WPTypeWDS; + else if(cistrcmp(cb->f[1], "adhoc") == 0) + ctlr->ptype = WPTypeAdHoc; + else if(cistrcmp(cb->f[1], "peertopeer") == 0) + ctlr->ptype = WPTypePeerToPeer; + else if((i = atoi(cb->f[1])) >= 0 && i <= 3) + ctlr->ptype = i; + else + r = -1; + } + else if(cistrcmp(cb->f[0], "crypt") == 0){ + if(cistrcmp(cb->f[1], "off") == 0) + ctlr->crypt = 0; + else if(cistrcmp(cb->f[1], "on") == 0 && ctlr->hascrypt) + ctlr->crypt = 1; + else + r = -1; + } + else if(cistrcmp(cb->f[0], "clear") == 0){ + if(cistrcmp(cb->f[1], "on") == 0) + ctlr->xclear = 0; + else if(cistrcmp(cb->f[1], "off") == 0 && ctlr->hascrypt) + ctlr->xclear = 1; + else + r = -1; + } + else if(strncmp(cb->f[0], "key", 3) == 0){ + if((i = atoi(cb->f[0]+3)) >= 1 && i <= WNKeys){ + ctlr->txkey = i-1; + key = &ctlr->keys.keys[ctlr->txkey]; + key->len = strlen(cb->f[1]); + if (key->len > WKeyLen) + key->len = WKeyLen; + memset(key->dat, 0, sizeof(key->dat)); + memmove(key->dat, cb->f[1], key->len); + } + else + r = -1; + } + else if(cistrcmp(cb->f[0], "txkey") == 0){ + if((i = atoi(cb->f[1])) >= 1 && i <= WNKeys) + ctlr->txkey = i-1; + else + r = -1; + } + else if(cistrcmp(cb->f[0], "pm") == 0){ + if(cistrcmp(cb->f[1], "off") == 0) + ctlr->pmena = 0; + else if(cistrcmp(cb->f[1], "on") == 0){ + ctlr->pmena = 1; + if(cb->nf == 3){ + i = atoi(cb->f[2]); + // check range here? what are the units? + ctlr->pmwait = i; + } + } + else + r = -1; + } + else + r = -2; + free(cb); + + return r; +} + +static long +ctl(Ether* ether, void* buf, long n) +{ + Ctlr *ctlr; + + if((ctlr = ether->ctlr) == nil) + error(Enonexist); + if(ctlr->attached == 0) + error(Eshutdown); + + ilock(ctlr); + if(w_option(ctlr, buf, n)){ + iunlock(ctlr); + error(Ebadctl); + } + if(ctlr->txbusy) + w_txdone(ctlr, WTxErrEv); + w_enable(ether); + w_txstart(ether); + iunlock(ctlr); + + return n; +} + +static void +transmit(Ether* ether) +{ + Ctlr* ctlr = ether->ctlr; + + if (ctlr == 0) + return; + + ilock(ctlr); + ctlr->ntxrq++; + w_txstart(ether); + iunlock(ctlr); +} + +static void +promiscuous(void* arg, int on) +{ + Ether* ether = (Ether*)arg; + Ctlr* ctlr = ether->ctlr; + + if (ctlr == nil) + error("card not found"); + if (ctlr->attached == 0) + error("card not attached"); + ilock(ctlr); + ltv_outs(ctlr, WTyp_Prom, (on?1:0)); + iunlock(ctlr); +} + +static void +interrupt(Ureg* ,void* arg) +{ + Ether* ether = (Ether*) arg; + Ctlr* ctlr = (Ctlr*) ether->ctlr; + + if (ctlr == 0) + return; + ilock(ctlr); + ctlr->nints++; + w_intr(ether); + iunlock(ctlr); +} + +static int +reset(Ether* ether) +{ + int i; + Wltv ltv; + Ctlr* ctlr; + int slot; + char *p; + + if ((slot = pcmspecial("WaveLAN/IEEE", ether))<0){ + DEBUG("no wavelan found\n"); + return -1; + } + + if((ctlr = malloc(sizeof(Ctlr))) == nil) + return -1; + + ilock(ctlr); + + if (ether->port==0) + ether->port = WDfltIOB; + ctlr->iob = ether->port; + ctlr->slot = slot; + + if (ioalloc(ether->port,WIOLen,0,"wavelan")<0){ + print("#l%d: port 0x%lx in use\n", + ether->ctlrno, ether->port); + goto abort; + } + DEBUG("#l%d: port=0x%lx irq=%ld\n", + ether->ctlrno, ether->port, ether->irq); + + w_intdis(ctlr); + if (w_cmd(ctlr,WCmdIni,0)){ + print("#l%d: init failed\n", ether->ctlrno); + goto abort; + } + w_intdis(ctlr); + ltv_outs(ctlr, WTyp_Tick, 8); + + ctlr->chan = 0; + ctlr->ptype = WDfltPType; + ctlr->txkey = 0; + ctlr->keys.len = sizeof(WKey)*WNKeys/2 + 1; + ctlr->keys.type = WTyp_Keys; + if(ctlr->hascrypt = ltv_ins(ctlr, WTyp_HasCrypt)) + ctlr->crypt = 1; + *ctlr->netname = *ctlr->wantname = 0; + strcpy(ctlr->nodename, "Plan 9 STA"); + + for(i = 0; i < ether->nopt; i++){ + // + // The max. length of an 'opt' is ISAOPTLEN in dat.h. + // It should be > 16 to give reasonable name lengths. + // + if(p = strchr(ether->opt[i], '=')) + *p = ' '; + w_option(ctlr, ether->opt[i], strlen(ether->opt[i])); + } + + ctlr->netname[WNameLen-1] = 0; + ctlr->wantname[WNameLen-1] = 0; + ctlr->nodename[WNameLen-1] =0; + + ltv.type = WTyp_Mac; + ltv.len = 4; + if (w_inltv(ctlr, <v)){ + print("#l%d: unable to read mac addr\n", + ether->ctlrno); + goto abort; + } + memmove(ether->ea, ltv.addr, Eaddrlen); + + if (ctlr->chan == 0) + ctlr->chan = ltv_ins(ctlr, WTyp_Chan); + ctlr->apdensity = WDfltApDens; + ctlr->rtsthres = WDfltRtsThres; + ctlr->txrate = WDfltTxRate; + ctlr->maxlen = WMaxLen; + ctlr->pmena = 0; + ctlr->pmwait = 100; + ctlr->signal = 1; + ctlr->noise = 1; + + // link to ether + ether->ctlr = ctlr; + ether->mbps = 10; + ether->attach = attach; + ether->interrupt = interrupt; + ether->transmit = transmit; + ether->ifstat = ifstat; + ether->ctl = ctl; + ether->promiscuous = promiscuous; + ether->multicast = multicast; + ether->arg = ether; + +// DEBUG("#l%d: irq %ld port %lx type %s", +// ether->ctlrno, ether->irq, ether->port, ether->type); +// DEBUG(" %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX\n", +// ether->ea[0], ether->ea[1], ether->ea[2], +// ether->ea[3], ether->ea[4], ether->ea[5]); + + iunlock(ctlr); + return 0; + +abort: + iunlock(ctlr); + free(ctlr); + ether->ctlr = nil; +iprint("wave reset failed\n"); + return -1; +} + +void +etherwavelanlink(void) +{ + addethercard("wavelan", reset); +} diff --git a/os/ipaq1110/fns.h b/os/ipaq1110/fns.h new file mode 100644 index 00000000..2ad379e4 --- /dev/null +++ b/os/ipaq1110/fns.h @@ -0,0 +1,179 @@ +#include "../port/portfns.h" + +ulong aifinit(uchar *aifarr); +void aamloop(int); +int archaudiopower(int); +void archaudiomute(int); +void archaudioamp(int); +int archaudiospeed(int, int); +void archcodecreset(void); +void archconfinit(void); +void archconsole(void); +int archflash12v(int); +int archhooksw(int); +long archkprofmicrosecondspertick(void); +void archkprofenable(int); +void archlcdenable(int); +void archpowerdown(void); +void archpowerup(void); +void archreboot(void); +void archreset(void); +vlong archrdtsc(void); +ulong archrdtsc32(void); +void archuartpower(int, int); +void blankscreen(int); +ulong call_apcs(ulong addr, int nargs, ...); +ulong call_apcs0(ulong addr); +ulong call_apcs1(ulong addr, ulong a1); +ulong call_apcs2(ulong addr, ulong a1, ulong a2); +ulong call_apcs3(ulong addr, ulong a1, ulong a2, ulong a3); +void cisread(int slotno, void (*f)(int, uchar *)); +void clockcheck(void); +void clockinit(void); +void clockpoll(void); +#define coherence() /* nothing to do for cache coherence for uniprocessor */ +void cursorhide(void); +void cursorunhide(void); +void dcflush(void*, ulong); +void dcflushall(void); +void dcinval(void); +int dmaidle(Dma*); +Dma* dmasetup(int device, int direction, int bigend, void(*)(void*,ulong), void*); +int dmastart(Dma*, void*, int); +int dmacontinue(Dma*, void*, int); +void dmastop(Dma*); +int dmaerror(Dma*); +void dmafree(Dma*); +void dmareset(void); +void dmawait(Dma*); +void dumplongs(char *, ulong *, int); +void dumpregs(Ureg* ureg); +void dumpstack(void); +int fpiarm(Ureg*); +void fpinit(void); +ulong getcallerpc(void*); +ulong getcpsr(void); +ulong getcpuid(void); +ulong getspsr(void); +void gotopc(ulong); + +void icflushall(void); +void _idlemode(void); +void (*idle)(void); +void idlehands(void); +int inb(ulong); +int ins(ulong); +ulong inl(ulong); +void outb(ulong, int); +void outs(ulong, int); +void outl(ulong, ulong); +void inss(ulong, void*, int); +void outss(ulong, void*, int); +void insb(ulong, void*, int); +void outsb(ulong, void*, int); +void intrdisable(int, void (*)(Ureg*, void*), void*, int, char*); +void intrenable(int, void (*)(Ureg*, void*), void*, int, char*); +void iofree(int); +#define iofree(x) +void ioinit(void); +int iounused(int, int); +int ioalloc(int, int, int, char*); +#define ioalloc(a,b,c,d) 0 +int iprint(char*, ...); +void installprof(void (*)(Ureg *, int)); +int isvalid_va(void*); +void kbdinit(void); +void L3init(void); +int L3read(int, void*, int); +int L3write(int, void*, int); +void lcd_setbacklight(int); +void lcd_sethz(int); +void lights(ulong); +void links(void); +ulong mcpgettfreq(void); +void mcpinit(void); +void mcpsettfreq(ulong tfreq); +void mcpspeaker(int, int); +void mcptelecomsetup(ulong hz, int adm, int xint, int rint); +ushort mcpadcread(int ts); +void mcptouchsetup(int ts); +void mcptouchintrenable(void); +void mcptouchintrdisable(void); +void mcpgpiowrite(ushort mask, ushort data); +void mcpgpiosetdir(ushort mask, ushort dir); +ushort mcpgpioread(void); +void* minicached(void*); +void minidcflush(void); +void mmuenable(ulong); +ulong mmugetctl(void); +ulong mmugetdac(void); +ulong mmugetfar(void); +ulong mmugetfsr(void); +void mmuinit(void); +void* mmuphysmap(ulong, ulong); +void mmuputctl(ulong); +void mmuputdac(ulong); +void mmuputfsr(ulong); +void mmuputttb(ulong); +void mmureset(void); +void mouseinit(void); +void nowriteSeg(void *, void *); +void* pa2va(ulong); +void pcmcisread(PCMslot*); +int pcmcistuple(int, int, int, void*, int); +PCMmap* pcmmap(int, ulong, int, int); +void pcmunmap(int, PCMmap*); +int pcmpin(int slot, int type); +void pcmpower(int slotno, int on); +int pcmpowered(int); +void pcmreset(int); +void pcmsetvcc(int, int); +void pcmsetvpp(int, int); +int pcmspecial(char *idstr, ISAConf *isa); +void pcmspecialclose(int slotno); +void pcmintrenable(int, void (*)(Ureg*, void*), void*); +void powerenable(void (*)(int)); +void powerdisable(void (*)(int)); +void powerdown(void); +void powerinit(void); +void powersuspend(void); +#define procsave(p) +#define procrestore(p) +long rtctime(void); +void screeninit(void); +void (*screenputs)(char*, int); +int segflush(void*, ulong); +void setpanic(void); +void setr13(int, void*); +int splfhi(void); +int splflo(void); +void _suspendcode(void); +void tlbinvalidate(void); +void tlbinvalidateaddr(void*); +void trapinit(void); +void trapstacks(void); +void trapspecial(int (*)(Ureg *, uint)); +int uartprint(char*, ...); +void uartspecial(int, int, char, Queue**, Queue**, int (*)(Queue*, int)); +ulong va2pa(void*); +void vectors(void); +void vtable(void); +#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) +int wasbusy(int); + +#define KADDR(p) ((void *) p) +#define PADDR(v) va2pa((void*)(v)) + +// #define timer_start() (*OSCR) +// #define timer_ticks(t) (*OSCR - (ulong)(t)) +ulong timer_start(void); +ulong timer_ticks(ulong); +int timer_devwait(ulong *adr, ulong mask, ulong val, int ost); +void timer_setwatchdog(int ost); +void timer_delay(int ost); +ulong ms2tmr(int ms); +int tmr2ms(ulong t); +void delay(int ms); +ulong us2tmr(int us); +int tmr2us(ulong t); +void microdelay(int us); diff --git a/os/ipaq1110/inflate b/os/ipaq1110/inflate new file mode 100644 index 00000000..b5761204 Binary files /dev/null and b/os/ipaq1110/inflate differ diff --git a/os/ipaq1110/io.h b/os/ipaq1110/io.h new file mode 100644 index 00000000..29cba8cb --- /dev/null +++ b/os/ipaq1110/io.h @@ -0,0 +1,60 @@ +/* + * iPAQ 36xx-specific definitions + */ + +/* + * GPIO assignment on iPAQ (see H3600 hardware spec). + * Following Plan 9, _i is input signal, _o is output, _io is both. + */ +enum { + GPIO_PWR_ON_i = 1<<0, /* power on/off (active low)*/ + GPIO_UP_IRQ_i = 1<<1, /* microcontroller interrupt (active low) */ + /* 2-9 are LCD 8-15 */ + GPIO_CARD_IND1_i = 1<<10, /* PCMCIA/CF socket 1 card inserted (active low) */ + GPIO_CARD_IRQ1_i = 1<<11, /* socket 1 IRQ (active low) */ + GPIO_CLK_SET0_o = 1<<12, /* codec clock select 0 */ + GPIO_CLK_SET1_o = 1<<13, /* codec clock select 1 */ + GPIO_L3_SDA_io = 1<<14, /* L3 data to/from UDA1341 */ + GPIO_L3_MODE_o = 1<<15, /* L3 mode to UDA1341 */ + GPIO_L3_SCLK_o = 1<<16, /* L3 SCLK to UDA1341 */ + GPIO_CARD_IND0_i = 1<<17, /* PCMCIA/CF socket 0 card inserted (active low) */ + GPIO_KEY_ACT_i = 1<<18, /* joypad centre button (active low) */ + GPIO_SYS_CLK_i = 1<<19, /* codec external clock */ + GPIO_BAT_FAULT_i = 1<<20, /* battery fault (active high) */ + GPIO_CARD_IRQ0_i = 1<<21, /* socket 0 IRQ (active low) */ + GPIO_LOCK_i = 1<<22, /* expansion pack lock/unlock signal (active low) */ + GPIO_COM_DCD_i = 1<<23, /* UART3 DCD from cradle (active high) */ + GPIO_OPT_IRQ_i = 1<<24, /* expansion pack shared IRQ (all but PCMCIA/CF, active high) */ + GPIO_COM_CTS_i = 1<<25, /* UART3 CTS (active high) */ + GPIO_COM_RTS_o = 1<<26, /* UART3 RTS (active high) */ + GPIO_OPT_IND_i = 1<<27, /* expansion pack inserted (active low) */ +}; + +/* special EGPIO register, write only*/ +enum { + EGPIO_VPEN = 1<<0, /* flash write enable */ + EGPIO_CARD_RESET = 1<<1, /* CF/PCMCIA reset signal */ + EGPIO_OPT_RESET = 1<<2, /* expansion pack reset for other than CF/PCMCIA */ + EGPIO_CODEC_RESET = 1<<3, /* codec reset signal (active low) */ + EGPIO_OPT_PWR_ON = 1<<4, /* enable power to NVRAM in expansion pack */ + EGPIO_OPT_ON = 1<<5, /* enable full power to expansion pack */ + EGPIO_LCD_ON = 1<<6, /* enable LCD 3.3v supply */ + EGPIO_RS232_ON = 1<<7, /* enable RS232 transceiver */ + EGPIO_LCD_PCI = 1<<8, /* enable power to LCD control IC */ + EGPIO_IR_ON = 1<<9, /* enable power to IR module */ + EGPIO_AUD_ON = 1<<10, /* enable power to audio amp */ + EGPIO_AUD_PWR_ON = 1<<11, /* enable power to all other audio circuitry */ + EGPIO_QMUTE = 1<<12, /* mute audio codec (nb: wastes power if set when audio not powered) */ + EGPIO_IR_FSEL = 1<<13, /* FIR mode selection: 1=FIR, 0=SIR */ + EGPIO_LCD_5V_ON = 1<<14, /* enable 5V to LCD module */ + EGPIO_LVDD_ON = 1<<15, /* enable 9V and -6.5V to LCD module */ +}; + +/* board-dependent GPIO pin assignment for l3gpio.c */ +enum { + L3Data = GPIO_L3_SDA_io, + L3Mode = GPIO_L3_MODE_o, + L3Clock = GPIO_L3_SCLK_o, +}; + +#include "../sa1110/sa1110io.h" diff --git a/os/ipaq1110/ipaq b/os/ipaq1110/ipaq new file mode 100644 index 00000000..19559326 --- /dev/null +++ b/os/ipaq1110/ipaq @@ -0,0 +1,172 @@ +dev + root + cons archipaq lcd l3gpio + env + gpio + mnt + pipe + prog + rtc + srv + dup + ssl + cap + sign + draw screen + pointer + uart + ip ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium netaux + flash + ftl + pcmcia cis + ether netif netaux + + audio + ipaq suspend + kprof + +ip + il + tcp + udp +# rudp +# igmp + ipifc + icmp + icmp6 +# ipmux + +link + flashcfi16 + etherwavelan + ethermedium + +lib + interp + tk + draw + memlayer + memdraw + keyring + sec + mp + math + kern + +mod + math + sys + draw + tk + keyring + +port + alarm + alloc + allocb + chan + dev + dial + dis + discall + exception + exportfs + inferno + latin1 + nocache + nodynld + parse + pgrp + print + proc + qio + qlock + random + sysfile + taslock + xalloc + +code + int kernel_pool_pcnt = 10; + int main_pool_pcnt = 33; + int heap_pool_pcnt = 34; + int image_pool_pcnt = 33; + int cflag = 0; /* for JIT */ + + int consoleprint = 1; + int redirectconsole = 1; + char debug_keys = 1; + int panicreset = 0; + char *tkfont = "/fonts/lucidasans/unicode.7.font"; + int tkstylus = 1; + +init + ipaqinit + +root + /chan / + /dev / + /dis + /env / + /fd / + /net / + /net.alt / + /nvfs / + /prog / + /dis/lib + /dis/disk + /osinit.dis + +# initialisation + /dis/touchcal.dis + +# dos file system + /dis/dossrv.dis + /dis/disk/format.dis + +# kfs file system + /dis/disk/kfs.dis + /dis/disk/kfscmd.dis + +# used by file systems and commands + /dis/lib/arg.dis + /dis/lib/styx.dis + /dis/lib/string.dis + /dis/lib/daytime.dis + +# For development work: + /dis/sh.dis /dis/tiny/sh.dis + /dis/ls.dis + /dis/cat.dis + /dis/bind.dis + /dis/mount.dis + /dis/pwd.dis + /dis/echo.dis + /dis/cd.dis + /dis/xd.dis + /dis/cp.dis + /dis/mkdir.dis + /dis/rm.dis + /dis/p.dis + /dis/ps.dis + /dis/lib/readdir.dis + /dis/lib/workdir.dis + /dis/lib/daytime.dis + /dis/lib/auth.dis + /dis/lib/ssl.dis + /dis/lib/bufio.dis + /dis/lib/string.dis +# /dis/pcmcia.dis /dis/auxi/pcmcia.dis +# dhcp + /dis/lib/dhcpclient.dis /dis/lib/dhcpclient.dis + /dis/lib/ip.dis /dis/lib/ip.dis + + /n/remote + /n/local + /n/client + /n/rdbg + /n/dump + /n/disk + /n/kfs +# Authentication + /nvfs/default /usr/inferno/keyring/default diff --git a/os/ipaq1110/lcd.c b/os/ipaq1110/lcd.c new file mode 100644 index 00000000..11b9ef7d --- /dev/null +++ b/os/ipaq1110/lcd.c @@ -0,0 +1,185 @@ +#include "u.h" +#include "mem.h" +#include "../port/lib.h" +#include "dat.h" +#include "draw.h" +#include "fns.h" +#include "io.h" +#include +#include "screen.h" + +#define DPRINT if(1)iprint + +enum { + /* lccr0 */ + EnableCtlr = 1<<0, /* controller enable */ + IsColour = 0<<1, + IsMono = 1<<1, + SinglePanel = 0<<2, + DualPanel = 1<<2, + DisableDone = 1<<3, + DisableBAU = 1<<4, + DisableErr = 1<<5, + PassivePanel = 0<<7, + ActivePanel = 1<<7, + BigEndian = 1<<8, + DoublePixel = 1<<9, + /* 19:12 is palette dma delay */ + + /* lcsr */ + CtlrReady = 1<<0, + + /* lccr3 */ + VsyncLow = 1<<20, + HsyncLow = 1<<21, + PixelClockLow = 1<<22, + OELow = 1<<23, +}; + +typedef struct { + Vdisplay; + LCDparam; + ushort* palette; + uchar* upper; + uchar* lower; +} LCDdisplay; + +static LCDdisplay *vd; // current active display + +void +lcd_setcolor(ulong p, ulong r, ulong g, ulong b) +{ + if(vd->pbs == 0 && p > 15 || + vd->pbs == 1 && p > 255 || + vd->pbs == 2) + return; + vd->palette[p] = (vd->pbs<<12) | + ((r>>(32-4))<<8) | + ((g>>(32-4))<<4) | + (b>>(32-4)); +} + +static void +disablelcd(void) +{ + LcdReg *lcd = LCDREG; + int i; + + /* if LCD enabled, turn off and wait for current frame to end */ + if(lcd->lccr0 & EnableCtlr) { + lcd->lccr0 &= ~EnableCtlr; + for(i=0; i < 50 && !(lcd->lcsr & CtlrReady); i++) + delay(5); + } +} + +static void +setlcdmode(LCDdisplay *vd) +{ + LCDmode *p; + int ppf, pclk, clockdiv; + ulong v, c; + LcdReg *lcd = LCDREG; + GpioReg *gpio = GPIOREG; + + p = (LCDmode*)&vd->Vmode; + ppf = ((((p->x+p->sol_wait+p->eol_wait) * + (p->mono ? 1 : 3)) >> (3-p->mono)) + + p->hsync_wid) * + (p->y/(p->dual+1)+p->vsync_hgt+ + p->sof_wait+p->eof_wait); + pclk = ppf*p->hz; + clockdiv = ((m->cpuhz/pclk) >> 1)-2; + DPRINT(" oclockdiv=%d\n", clockdiv); +clockdiv=0x10; + disablelcd(); + lcd->lccr0 = 0; /* reset it */ + + DPRINT(" pclk=%d clockdiv=%d\n", pclk, clockdiv); + lcd->lccr3 = (clockdiv << 0) | + (p->acbias_lines << 8) | + (p->lines_per_int << 16) | + VsyncLow | HsyncLow; /* vsync active low, hsync active low */ + lcd->lccr2 = (((p->y/(p->dual+1))-1) << 0) | + (p->vsync_hgt << 10) | + (p->eof_wait << 16) | + (p->sof_wait << 24); + lcd->lccr1 = ((p->x-16) << 0) | + (p->hsync_wid << 10) | + (p->eol_wait << 16) | + (p->sol_wait << 24); + + // enable LCD controller, CODEC, and lower 4/8 data bits (for tft/dual) + v = p->obits < 12? 0: p->obits < 16? 0x3c: 0x3fc; + c = p->obits == 12? 0x3c0: 0; + gpio->gafr |= v; + gpio->gpdr |= v | c; + gpio->gpcr = c; + + lcd->dbar1 = PADDR(vd->palette); + if(vd->dual) + lcd->dbar2 = PADDR(vd->lower); + + // Enable LCD + lcd->lccr0 = EnableCtlr | (p->mono?IsMono:IsColour) + | (p->palette_delay << 12) + | (p->dual ? DualPanel : SinglePanel) + | (p->active? ActivePanel: PassivePanel) + | DisableDone | DisableBAU | DisableErr; + + // recalculate actual HZ + pclk = (m->cpuhz/(clockdiv+2)) >> 1; + p->hz = pclk/ppf; + + archlcdenable(1); +iprint("lccr0=%8.8lux lccr1=%8.8lux lccr2=%8.8lux lccr3=%8.8lux\n", lcd->lccr0, lcd->lccr1, lcd->lccr2, lcd->lccr3); +} +static LCDdisplay main_display; /* TO DO: limits us to a single display */ + +Vdisplay* +lcd_init(LCDmode *p) +{ + int palsize; + int fbsize; + + vd = &main_display; + vd->Vmode = *p; + vd->LCDparam = *p; + DPRINT("%dx%dx%d: hz=%d\n", vd->x, vd->y, vd->depth, vd->hz); /* */ + + palsize = vd->pbs==1? 256 : 16; + fbsize = palsize*2+(((vd->x*vd->y) * vd->depth) >> 3); + if((vd->palette = xspanalloc(fbsize+CACHELINESZ+512, CACHELINESZ, 0)) == nil) /* at least 16-byte alignment */ + panic("no vidmem, no party..."); + vd->palette[0] = (vd->pbs<<12); + vd->palette = minicached(vd->palette); + vd->upper = (uchar*)(vd->palette + palsize); + vd->bwid = (vd->x << vd->pbs) >> 1; + vd->lower = vd->upper+((vd->bwid*vd->y) >> 1); + vd->fb = vd->upper; + DPRINT(" fbsize=%d p=%p u=%p l=%p\n", fbsize, vd->palette, vd->upper, vd->lower); /* */ + + setlcdmode(vd); + return vd; +} + +void +lcd_flush(void) +{ + if(conf.useminicache) + minidcflush(); + else + dcflushall(); /* need more precise addresses */ +} + +void +blankscreen(int blank) +{ + if (blank) { + disablelcd(); + archlcdenable(0); + } else { + archlcdenable(1); + setlcdmode(&main_display); + } +} diff --git a/os/ipaq1110/main.c b/os/ipaq1110/main.c new file mode 100644 index 00000000..58bd792f --- /dev/null +++ b/os/ipaq1110/main.c @@ -0,0 +1,255 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" +#include "version.h" + +Mach *m = (Mach*)MACHADDR; +Proc *up = 0; +Vectorpage *page0 = (Vectorpage*)KZERO; /* doubly-mapped to AIVECADDR */ +Conf conf; + +extern ulong kerndate; +extern int cflag; +extern int main_pool_pcnt; +extern int heap_pool_pcnt; +extern int image_pool_pcnt; +ulong cpuidlecount; + +static void +poolsizeinit(void) +{ + ulong nb; + + nb = conf.npage*BY2PG; + poolsize(mainmem, (nb*main_pool_pcnt)/100, 0); + poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0); + poolsize(imagmem, (nb*image_pool_pcnt)/100, 1); +} + +void +main(void) +{ + memset(edata, 0, end-edata); /* clear the BSS */ + memset(m, 0, sizeof(Mach)); /* clear the mach struct */ + conf.nmach = 1; + archreset(); + dmareset(); + quotefmtinstall(); + confinit(); + xinit(); + mmuinit(); + poolinit(); + poolsizeinit(); + trapinit(); + clockinit(); + printinit(); + screeninit(); + procinit(); + links(); + chandevreset(); + + eve = strdup("inferno"); + + archconsole(); + kbdinit(); + + print("%ld MHz id %8.8lux\n", (m->cpuhz+500000)/1000000, getcpuid()); + print("\nInferno %s\n", VERSION); + print("Vita Nuova\n"); + print("conf %s (%lud) jit %d\n\n",conffile, kerndate, cflag); + + userinit(); + schedinit(); +} + +void +reboot(void) +{ + exit(0); +} + +void +halt(void) +{ + spllo(); + print("cpu halted\n"); + for(;;){ + /* nothing to do */ + } +} + +void +confinit(void) +{ + ulong base; + + archconfinit(); + + base = PGROUND((ulong)end); + conf.base0 = base; + + conf.base1 = 0; + conf.npage1 = 0; + + conf.npage0 = (conf.topofmem - base)/BY2PG; + + conf.npage = conf.npage0 + conf.npage1; + conf.ialloc = (((conf.npage*(main_pool_pcnt))/100)/2)*BY2PG; + + conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5; + conf.nmach = 1; + +} + +void +init0(void) +{ + Osenv *o; + char buf[2*KNAMELEN]; + + up->nerrlab = 0; + + spllo(); + + if(waserror()) + panic("init0 %r"); + /* + * These are o.k. because rootinit is null. + * Then early kproc's will have a root and dot. + */ + o = up->env; + o->pgrp->slash = namec("#/", Atodir, 0, 0); + cnameclose(o->pgrp->slash->name); + o->pgrp->slash->name = newcname("/"); + o->pgrp->dot = cclone(o->pgrp->slash); + + chandevinit(); + + if(!waserror()){ + ksetenv("cputype", "arm", 0); + snprint(buf, sizeof(buf), "arm %s", conffile); + ksetenv("terminal", buf, 0); + poperror(); + } + + poperror(); + + disinit("/osinit.dis"); +} + +void +userinit(void) +{ + Proc *p; + Osenv *o; + + p = newproc(); + o = p->env; + + o->fgrp = newfgrp(nil); + o->pgrp = newpgrp(); + o->egrp = newegrp(); + kstrdup(&o->user, eve); + + strcpy(p->text, "interp"); + + p->fpstate = FPINIT; + + /* + * Kernel Stack + * + * N.B. The -12 for the stack pointer is important. + * 4 bytes for gotolabel's return PC + */ + p->sched.pc = (ulong)init0; + p->sched.sp = (ulong)p->kstack+KSTACK-8; + + ready(p); +} + +void +exit(int inpanic) +{ + up = 0; + + /* Shutdown running devices */ + chandevshutdown(); + + if(inpanic && 0){ + print("Hit the reset button\n"); + for(;;) + clockpoll(); + } + archreboot(); +} + +static void +linkproc(void) +{ + spllo(); + if (waserror()) + print("error() underflow: %r\n"); + else + (*up->kpfun)(up->arg); + pexit("end proc", 1); +} + +void +kprocchild(Proc *p, void (*func)(void*), void *arg) +{ + p->sched.pc = (ulong)linkproc; + p->sched.sp = (ulong)p->kstack+KSTACK-8; + + p->kpfun = func; + p->arg = arg; +} + +void +idlehands(void) +{ + cpuidlecount++; + INTRREG->iccr = 1; /* only unmasked interrupts will stop idle mode */ + idle(); +} + +/* stubs */ +void +setfsr(ulong) +{ +} + +ulong +getfsr() +{ + return 0; +} + +void +setfcr(ulong) +{ +} + +ulong +getfcr() +{ + return 0; +} + +void +fpinit(void) +{ +} + +void +FPsave(void*) +{ +} + +void +FPrestore(void*) +{ +} diff --git a/os/ipaq1110/mem.h b/os/ipaq1110/mem.h new file mode 100644 index 00000000..19d55b07 --- /dev/null +++ b/os/ipaq1110/mem.h @@ -0,0 +1,200 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ + +/* + * Sizes + */ +#define BI2BY 8 /* bits per byte */ +#define BI2WD 32 /* bits per word */ +#define BY2WD 4 /* bytes per word */ +#define BY2V 8 /* bytes per double word */ +#define BY2PG 4096 /* bytes per page */ +#define WD2PG (BY2PG/BY2WD) /* words per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1)) +#define PGROUND(s) ROUND(s, BY2PG) +#define BIT(n) (1< 4096 descriptors -> 16Kb + * L2: 8-bit index -> 256 descriptors -> 1Kb + * Each L2 descriptor has access permissions for 4 1Kb sub-pages. + * + * TTB + L1Tx gives address of L1 descriptor + * L1 descriptor gives PTBA + * PTBA + L2Tx gives address of L2 descriptor + * L2 descriptor gives PBA + */ +#define MmuSection (1<<20) +#define MmuLargePage (1<<16) +#define MmuSmallPage (1<<12) +#define MmuTTB(pa) ((pa) & ~0x3FFF) /* translation table base */ +#define MmuL1x(pa) (((pa)>>20) & 0xFFF) /* L1 table index */ +#define MmuPTBA(pa) ((pa) & ~0x3FF) /* page table base address */ +#define MmuL2x(pa) (((pa)>>12) & 0xFF) /* L2 table index */ +#define MmuPBA(pa) ((pa) & ~0xFFF) /* page base address */ +#define MmuSBA(pa) ((pa) & ~0xFFFFF) /* section base address */ + +#define MmuL1type 0x03 +#define MmuL1page 0x01 /* descriptor is for L2 pages */ +#define MmuL1section 0x02 /* descriptor is for section */ + +#define MmuL2invalid 0x000 +#define MmuL2large 0x001 /* large */ +#define MmuL2small 0x002 /* small */ +#define MmuWB 0x004 /* data goes through write buffer */ +#define MmuIDC 0x008 /* data placed in cache */ + +#define MmuDAC(d) (((d) & 0xF)<<5) /* L1 domain */ +#define MmuAP(i, v) ((v)<<(((i)*2)+4)) /* access permissions */ +#define MmuL1AP(v) MmuAP(3, (v)) +#define MmuL2AP(v) MmuAP(3, (v))|MmuAP(2, (v))|MmuAP(1, (v))|MmuAP(0, (v)) +#define MmuAPsro 0 /* supervisor ro if S|R */ +#define MmuAPsrw 1 /* supervisor rw */ +#define MmuAPuro 2 /* supervisor rw + user ro */ +#define MmuAPurw 3 /* supervisor rw + user rw */ diff --git a/os/ipaq1110/mkfile b/os/ipaq1110/mkfile new file mode 100644 index 00000000..d76a8110 --- /dev/null +++ b/os/ipaq1110/mkfile @@ -0,0 +1,103 @@ +<../../mkconfig +TKSTYLE=std + +#Configurable parameters + +CONF=ipaq #default configuration +CONFLIST=ipaq + +SYSTARG=$OSTARG +OBJTYPE=arm +INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin #path of directory where kernel is installed +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS + +KTZERO=0xC0008010 + +OBJ=\ + l.$O\ + clock.$O\ + dma.$O\ + fpi.$O\ + fpiarm.$O\ + fpimem.$O\ + defont.$O\ + main.$O\ + mmu.$O\ + trap.$O\ + $CONF.root.$O\ + $IP\ + $DEVS\ + $ETHERS\ + $LINKS\ + $PORT\ + $MISC\ + $OTHERS\ + +LIBNAMES=${LIBS:%=lib%.a} +LIBDIRS=$LIBS + +HFILES=\ + mem.h\ + dat.h\ + fns.h\ + io.h\ + ../sa1110/sa1110io.h\ + ../sa1110/fpi.h\ + +CFLAGS=-wFV -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp -I../sa1110 +KERNDATE=`{$NDATE} + +default:V: i$CONF.gz i$CONF.p9 k.gz + +install:V: $INSTALLDIR/i$CONF $INSTALLDIR/i$CONF.gz $INSTALLDIR/i$CONF.p9.gz $INSTALLDIR/i$CONF.raw + +i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES i$CONF.p9 + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD -s -o $target -H5 -T0xC0008010 -R4 -l $OBJ $CONF.$O $LIBFILES + +i$CONF.p9: $OBJ $CONF.c $CONF.root.h $LIBNAMES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD -o $target -T0xC0008010 -R4 -l $OBJ $CONF.$O $LIBFILES + +i$CONF.gz: i$CONF + rm -f i$CONF.gz + gzip -9 i$CONF.gz + +<../port/portmkfile +CLEANEXTRA=k.gz + +../init/$INIT.dis: ../init/$INIT.b + cd ../init; mk $INIT.dis + +clock.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +devether.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +main.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +trap.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h + +devether.$O $ETHERS: ../sa1110/etherif.h ../port/netif.h +$IP devip.$O: ../ip/ip.h +io.h:N: ../sa1110/sa1110io.h + +%.$O: ../sa1110/%.c + $CC $CFLAGS -I. ../sa1110/$stem.c + +%.$O: ../sa1110/%.s + $AS -I. -I../sa1110 ../sa1110/$stem.s + +dummy:V: + +k.gz: i$CONF.gz + cat inflate i$CONF.gz >k.gz + echo burble burble >>k.gz + +devaudio.$O: devaudio.c + $CC $CFLAGS devaudio.c + +arch$CONF.$O: ../sa1110/etherif.h + +devuart.$O: ../sa1110/devuart.c + $CC $CFLAGS ../sa1110/devuart.c diff --git a/os/ipaq1110/screen.c b/os/ipaq1110/screen.c new file mode 100644 index 00000000..2ed7bd32 --- /dev/null +++ b/os/ipaq1110/screen.c @@ -0,0 +1,917 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +#include +#include +#include + +#include "screen.h" + +enum { + Backgnd = 0xFF, /* white */ + Foregnd = 0x00, /* black */ +}; + +#define DPRINT if(1)iprint + +static Memdata xgdata; +static Memimage xgscreen = +{ + {0, 0, 0, 0}, /* r */ + {0, 0, 0, 0}, /* clipr */ + 8, /* depth */ + 1, /* nchan */ + CMAP8, /* chan */ + nil, /* cmap */ + &xgdata, /* data */ + 0, /* zero */ + 0, /* width */ + nil, /* layer */ + 0, /* flags */ +}; + +Memimage *gscreen; +Memimage *conscol; +Memimage *back; + +Memsubfont *memdefont; + +static Point curpos; +static Rectangle window; + +typedef struct SWcursor SWcursor; + +static Vdisplay *vd; +static SWcursor *swc = nil; + +SWcursor* swcurs_create(ulong *, int, int, Rectangle, int); +void swcurs_destroy(SWcursor*); +void swcurs_enable(SWcursor*); +void swcurs_disable(SWcursor*); +void swcurs_hide(SWcursor*); +void swcurs_unhide(SWcursor*); +void swcurs_load(SWcursor*, Cursor*); +void swcursupdate(int, int, int, int); + +static char printbuf[1024]; +static int printbufpos = 0; +static void lcdscreenputs(char*, int); +static void screenpbuf(char*, int); +void (*screenputs)(char*, int) = screenpbuf; + +static Cursor arrow = { + { -1, -1 }, + { 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04, + 0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04, + 0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40, + }, + { 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0, + 0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8, + 0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8, + 0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00, + }, +}; + +static ushort palette16[256]; +static void (*flushpixels)(Rectangle, ulong*, int, ulong*, int); +static void flush8to4(Rectangle, ulong*, int, ulong*, int); +static void flush8to4r(Rectangle, ulong*, int, ulong*, int); +static void flush8to16(Rectangle, ulong*, int, ulong*, int); +static void flush8to16r(Rectangle, ulong*, int, ulong*, int); + +/* +lccr0=000000b9 lccr1=0b100930 lccr2=0a0108ef lccr3=00300010 + --- +vd->wid=320 bwid=640 gscreen->width=60 fb=d0b7cb80 data=d0ba25c0 + */ + +int +setcolor(ulong p, ulong r, ulong g, ulong b) +{ + if(vd->depth >= 8) + p &= 0xff; + else + p &= 0xf; + vd->colormap[p][0] = r; + vd->colormap[p][1] = g; + vd->colormap[p][2] = b; + palette16[p] = ((r>>(32-4))<<12)|((g>>(32-4))<<7)|((b>>(32-4))<<1); + lcd_setcolor(p, r, g, b); + return ~0; +} + +void +getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb) +{ + if(vd->depth >= 8) + p = (p&0xff)^0xff; + else + p = (p&0xf)^0xf; + *pr = vd->colormap[p][0]; + *pg = vd->colormap[p][1]; + *pb = vd->colormap[p][2]; +} + +void +graphicscmap(int invert) +{ + int num, den, i, j; + int r, g, b, cr, cg, cb, v, p; + + for(r=0,i=0;r!=4;r++) for(v=0;v!=4;v++,i+=16){ + for(g=0,j=v-r;g!=4;g++) for(b=0;b!=4;b++,j++){ + den=r; + if(g>den) den=g; + if(b>den) den=b; + if(den==0) /* divide check -- pick grey shades */ + cr=cg=cb=v*17; + else{ + num=17*(4*den+v); + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + p = (i+(j&15)); + if(invert) + p ^= 0xFF; + if(vd->depth == 4) { + if((p&0xf) != (p>>4)) + continue; + p &= 0xf; + } + setcolor(p, + cr*0x01010101, + cg*0x01010101, + cb*0x01010101); + } + } + lcd_flush(); +} + +static uchar lum[256]={ + 0, 7, 15, 23, 39, 47, 55, 63, 79, 87, 95, 103, 119, 127, 135, 143, +154, 17, 9, 17, 25, 49, 59, 62, 68, 89, 98, 107, 111, 129, 138, 146, +157, 166, 34, 11, 19, 27, 59, 71, 69, 73, 99, 109, 119, 119, 139, 148, +159, 169, 178, 51, 13, 21, 29, 69, 83, 75, 78, 109, 120, 131, 128, 149, + 28, 35, 43, 60, 68, 75, 83, 100, 107, 115, 123, 140, 147, 155, 163, 20, + 25, 35, 40, 47, 75, 85, 84, 89, 112, 121, 129, 133, 151, 159, 168, 176, +190, 30, 42, 44, 50, 90, 102, 94, 97, 125, 134, 144, 143, 163, 172, 181, +194, 204, 35, 49, 49, 54, 105, 119, 103, 104, 137, 148, 158, 154, 175, 184, + 56, 63, 80, 88, 96, 103, 120, 128, 136, 143, 160, 168, 175, 183, 40, 48, + 54, 63, 69, 90, 99, 107, 111, 135, 144, 153, 155, 173, 182, 190, 198, 45, + 50, 60, 70, 74, 100, 110, 120, 120, 150, 160, 170, 167, 186, 195, 204, 214, +229, 55, 66, 77, 79, 110, 121, 131, 129, 165, 176, 187, 179, 200, 210, 219, + 84, 100, 108, 116, 124, 140, 148, 156, 164, 180, 188, 196, 204, 60, 68, 76, + 82, 91, 108, 117, 125, 134, 152, 160, 169, 177, 195, 204, 212, 221, 66, 74, + 80, 89, 98, 117, 126, 135, 144, 163, 172, 181, 191, 210, 219, 228, 238, 71, + 76, 85, 95, 105, 126, 135, 145, 155, 176, 185, 195, 205, 225, 235, 245, 255, +}; + +void flushmemscreen(Rectangle r); + +void +screenclear(void) +{ + memimagedraw(gscreen, gscreen->r, memwhite, ZP, memopaque, ZP, SoverD); + curpos = window.min; + flushmemscreen(gscreen->r); +} + +static void +setscreen(LCDmode *mode) +{ + int h; + + if(swc != nil) + swcurs_destroy(swc); + + vd = lcd_init(mode); + if(vd == nil) + panic("can't initialise LCD"); + + if(lum[255] == 255) { + int i; + for(i=0; i<256; i++) + lum[i] >>= 4; /* could support depths other than 4 */ + } + + gscreen = &xgscreen; + xgdata.ref = 1; + + if(conf.portrait == 0) + gscreen->r = Rect(0, 0, vd->x, vd->y); + else + gscreen->r = Rect(0, 0, vd->y, vd->x); + gscreen->clipr = gscreen->r; + gscreen->depth = 8; + gscreen->width = wordsperline(gscreen->r, gscreen->depth); + if(vd->depth == 4 || vd->depth == 16 || conf.portrait) { /* use 8 to 4 bit fakeout for speed */ + if((xgdata.bdata = xspanalloc(gscreen->width*gscreen->r.max.y*BY2WD+CACHELINESZ, CACHELINESZ, 0)) == nil) + panic("can't alloc vidmem"); + xgdata.bdata = minicached(xgdata.bdata); + if(conf.portrait == 0) + flushpixels = vd->depth==4? flush8to4: flush8to16; + else + flushpixels = vd->depth==4? flush8to4r: flush8to16r; + } else{ + xgdata.bdata = (uchar*)vd->fb; + flushpixels = nil; + } + memimageinit(); + memdefont = getmemdefont(); + + memsetchan(gscreen, CMAP8); /* TO DO: could now use RGB16 */ + back = memwhite; + conscol = memblack; + memimagedraw(gscreen, gscreen->r, memwhite, ZP, memopaque, ZP, SoverD); + + DPRINT("vd->wid=%d bwid=%d gscreen->width=%ld fb=%p data=%p\n", + vd->x, vd->bwid, gscreen->width, vd->fb, xgdata.bdata); + graphicscmap(0); + h = memdefont->height; + window = insetrect(gscreen->r, 4); + window.max.y = window.min.y+(Dy(window)/h)*h; + screenclear(); + +// swc = swcurs_create(gscreendata.data, gscreen.width, gscreen.ldepth, gscreen.r, 1); + + drawcursor(nil); +} + +void +screeninit(void) +{ + LCDmode lcd; + + memset(&lcd, 0, sizeof(lcd)); + if(archlcdmode(&lcd) < 0) + return; + setscreen(&lcd); + screenputs = lcdscreenputs; + if(printbufpos) + screenputs("", 0); + blanktime = 3; /* minutes */ +} + +uchar* +attachscreen(Rectangle *r, ulong *chan, int* d, int *width, int *softscreen) +{ + *r = gscreen->r; + *d = gscreen->depth; + *chan = gscreen->chan; + *width = gscreen->width; + *softscreen = (gscreen->data->bdata != (uchar*)vd->fb); + + return (uchar*)gscreen->data->bdata; +} + +void +detachscreen(void) +{ +} + +static void +flush8to4(Rectangle r, ulong *s, int sw, ulong *d, int dw) +{ + int i, h, w; + +/* + print("1) s=%ux sw=%d d=%ux dw=%d r=(%d,%d)(%d,%d)\n", + s, sw, d, dw, r.min.x, r.min.y, r.max.x, r.max.y); +*/ + + r.min.x &= ~7; + r.max.x = (r.max.x + 7) & ~7; + s += (r.min.y*sw)+(r.min.x>>2); + d += (r.min.y*dw)+(r.min.x>>3); + h = Dy(r); + w = Dx(r) >> 3; + sw -= w*2; + dw -= w; + + while(h--) { + for(i=w; i; i--) { + ulong v1 = *s++; + ulong v2 = *s++; + *d++ = (lum[v2>>24]<<28) + |(lum[(v2>>16)&0xff]<<24) + |(lum[(v2>>8)&0xff]<<20) + |(lum[v2&0xff]<<16) + |(lum[v1>>24]<<12) + |(lum[(v1>>16)&0xff]<<8) + |(lum[(v1>>8)&0xff]<<4) + |(lum[v1&0xff]) + ; + } + s += sw; + d += dw; + } +} + +static void +flush8to16(Rectangle r, ulong *s, int sw, ulong *d, int dw) +{ + int i, h, w; + ushort *p; + + if(0) + iprint("1) s=%p sw=%d d=%p dw=%d r=[%d,%d, %d,%d]\n", + s, sw, d, dw, r.min.x, r.min.y, r.max.x, r.max.y); + + r.min.x &= ~3; + r.max.x = (r.max.x + 3) & ~3; /* nearest ulong */ + s += (r.min.y*sw)+(r.min.x>>2); + d += (r.min.y*dw)+(r.min.x>>1); + h = Dy(r); + w = Dx(r) >> 2; /* also ulong */ + sw -= w; + dw -= w*2; + if(0) + iprint("h=%d w=%d sw=%d dw=%d\n", h, w, sw, dw); + + p = palette16; + while(--h >= 0){ + for(i=w; --i>=0;){ + ulong v = *s++; + *d++ = (p[(v>>8)&0xFF]<<16) | p[v & 0xFF]; + *d++ = (p[v>>24]<<16) | p[(v>>16)&0xFF]; + } + s += sw; + d += dw; + } +} + +static void +flush8to4r(Rectangle r, ulong *s, int sw, ulong *d, int dw) +{ + flush8to4(r, s, sw, d, dw); /* rotation not implemented */ +} + +static void +flush8to16r(Rectangle r, ulong *s, int sw, ulong *d, int dw) +{ + int x, y, w, dws; + ushort *p; + ushort *ds; + + if(0) + iprint("1) s=%p sw=%d d=%p dw=%d r=[%d,%d, %d,%d]\n", + s, sw, d, dw, r.min.x, r.min.y, r.max.x, r.max.y); + + r.min.y &= ~3; + r.max.y = (r.max.y+3) & ~3; + r.min.x &= ~7; + r.max.x = (r.max.x + 7) & ~7; + s += (r.min.y*sw)+(r.min.x>>2); +// d += (r.min.y*dw)+(r.min.x>>1); + w = Dx(r) >> 2; /* also ulong */ + sw -= w; + dws = dw*2; + if(0) + iprint("h=%d w=%d sw=%d dw=%d x,y=%d,%d %d\n", Dy(r), w, sw, dw, r.min.x,r.min.y, dws); + + p = palette16; + for(y=r.min.y; yr.max.y-(y+1)); + ds[0] = p[v & 0xFF]; + ds[dws] = p[(v>>8)&0xFF]; + ds[dws*2] = p[(v>>16)&0xFF]; + ds[dws*3] = p[(v>>24)&0xFF]; + } + s += sw; + } +} + +void +flushmemscreen(Rectangle r) +{ + if(rectclip(&r, gscreen->r) == 0) + return; + if(r.min.x >= r.max.x || r.min.y >= r.max.y) + return; + if(flushpixels != nil) + flushpixels(r, (ulong*)gscreen->data->bdata, gscreen->width, (ulong*)vd->fb, vd->bwid >> 2); + lcd_flush(); +} + +static void +scroll(void) +{ + int o; + Point p; + Rectangle r; + + o = 4*memdefont->height; + r = Rpt(window.min, Pt(window.max.x, window.max.y-o)); + p = Pt(window.min.x, window.min.y+o); + memimagedraw(gscreen, r, gscreen, p, nil, p, SoverD); + flushmemscreen(r); + r = Rpt(Pt(window.min.x, window.max.y-o), window.max); + memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD); + flushmemscreen(r); + + curpos.y -= o; +} + +static void +clearline(void) +{ + Rectangle r; + int yloc = curpos.y; + + r = Rpt(Pt(window.min.x, window.min.y + yloc), + Pt(window.max.x, window.min.y+yloc+memdefont->height)); + memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD); +} + +static void +screenputc(char *buf) +{ + Point p; + int h, w, pos; + Rectangle r; + static int *xp; + static int xbuf[256]; + + h = memdefont->height; + if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)]) + xp = xbuf; + + switch(buf[0]) { + case '\n': + if(curpos.y+h >= window.max.y) + scroll(); + curpos.y += h; + /* fall through */ + case '\r': + xp = xbuf; + curpos.x = window.min.x; + break; + case '\t': + if(curpos.x == window.min.x) + clearline(); + p = memsubfontwidth(memdefont, " "); + w = p.x; + *xp++ = curpos.x; + pos = (curpos.x-window.min.x)/w; + pos = 8-(pos%8); + r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y+h); + memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD); + flushmemscreen(r); + curpos.x += pos*w; + break; + case '\b': + if(xp <= xbuf) + break; + xp--; + r = Rpt(Pt(*xp, curpos.y), Pt(curpos.x, curpos.y + h)); + memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD); + flushmemscreen(r); + curpos.x = *xp; + break; + case '\0': + break; + default: + p = memsubfontwidth(memdefont, buf); + w = p.x; + + if(curpos.x >= window.max.x-w) + screenputc("\n"); + + if(curpos.x == window.min.x) + clearline(); + if(xp < xbuf+nelem(xbuf)) + *xp++ = curpos.x; + r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y+h); + memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD); + memimagestring(gscreen, curpos, conscol, ZP, memdefont, buf); + flushmemscreen(r); + curpos.x += w; + } +} + +static void +screenpbuf(char *s, int n) +{ + if(printbufpos+n > sizeof(printbuf)) + n = sizeof(printbuf)-printbufpos; + if(n > 0) { + memmove(&printbuf[printbufpos], s, n); + printbufpos += n; + } +} + +static void +screendoputs(char *s, int n) +{ + int i; + Rune r; + char buf[4]; + + while(n > 0) { + i = chartorune(&r, s); + if(i == 0){ + s++; + --n; + continue; + } + memmove(buf, s, i); + buf[i] = 0; + n -= i; + s += i; + screenputc(buf); + } +} + +void +screenflush(void) +{ + int j = 0; + int k; + + for (k = printbufpos; j < k; k = printbufpos) { + screendoputs(printbuf + j, k - j); + j = k; + } + printbufpos = 0; +} + +static void +lcdscreenputs(char *s, int n) +{ + static Proc *me; + + if(!canlock(vd)) { + /* don't deadlock trying to print in interrupt */ + /* don't deadlock trying to print while in print */ + if(islo() == 0 || up != nil && up == me){ + /* save it for later... */ + /* In some cases this allows seeing a panic message + that would be locked out forever */ + screenpbuf(s, n); + return; + } + lock(vd); + } + + me = up; + if (printbufpos) + screenflush(); + screendoputs(s, n); + if (printbufpos) + screenflush(); + me = nil; + + unlock(vd); +} + +/* + * Software cursor code: done by hand, might be better to use memdraw + */ + +typedef struct SWcursor { + ulong *fb; /* screen frame buffer */ + Rectangle r; + int d; /* ldepth of screen */ + int width; /* width of screen in ulongs */ + int x; + int y; + int hotx; + int hoty; + uchar cbwid; /* cursor byte width */ + uchar f; /* flags */ + uchar cwid; + uchar chgt; + int hidecount; + uchar data[CURSWID*CURSHGT]; + uchar mask[CURSWID*CURSHGT]; + uchar save[CURSWID*CURSHGT]; +} SWcursor; + +enum { + CUR_ENA = 0x01, /* cursor is enabled */ + CUR_DRW = 0x02, /* cursor is currently drawn */ + CUR_SWP = 0x10, /* bit swap */ +}; + +static Rectangle cursoroffrect; +static int cursorisoff; + +static void swcursorflush(int, int); +static void swcurs_draw_or_undraw(SWcursor *); + +static void +cursorupdate0(void) +{ + int inrect, x, y; + Point m; + + m = mousexy(); + x = m.x - swc->hotx; + y = m.y - swc->hoty; + inrect = (x >= cursoroffrect.min.x && x < cursoroffrect.max.x + && y >= cursoroffrect.min.y && y < cursoroffrect.max.y); + if (cursorisoff == inrect) + return; + cursorisoff = inrect; + if (inrect) + swcurs_hide(swc); + else { + swc->hidecount = 0; + swcurs_draw_or_undraw(swc); + } + swcursorflush(m.x, m.y); +} + +void +cursorupdate(Rectangle r) +{ + lock(vd); + r.min.x -= 16; + r.min.y -= 16; + cursoroffrect = r; + if (swc != nil) + cursorupdate0(); + unlock(vd); +} + +void +cursorenable(void) +{ + Point m; + + lock(vd); + if(swc != nil) { + swcurs_enable(swc); + m = mousexy(); + swcursorflush(m.x, m.y); + } + unlock(vd); +} + +void +cursordisable(void) +{ + Point m; + + lock(vd); + if(swc != nil) { + swcurs_disable(swc); + m = mousexy(); + swcursorflush(m.x, m.y); + } + unlock(vd); +} + +void +swcursupdate(int oldx, int oldy, int x, int y) +{ + + if(!canlock(vd)) + return; /* if can't lock, don't wake up stuff */ + + if(x < gscreen->r.min.x) + x = gscreen->r.min.x; + if(x >= gscreen->r.max.x) + x = gscreen->r.max.x; + if(y < gscreen->r.min.y) + y = gscreen->r.min.y; + if(y >= gscreen->r.max.y) + y = gscreen->r.max.y; + if(swc != nil) { + swcurs_hide(swc); + swc->x = x; + swc->y = y; + cursorupdate0(); + swcurs_unhide(swc); + swcursorflush(oldx, oldy); + swcursorflush(x, y); + } + + unlock(vd); +} + +void +drawcursor(Drawcursor* c) +{ + Point p, m; + Cursor curs, *cp; + int j, i, h, bpl; + uchar *bc, *bs, *cclr, *cset; + + if(swc == nil) + return; + + /* Set the default system cursor */ + if(c == nil || c->data == nil){ + swcurs_disable(swc); + return; + } + else { + cp = &curs; + p.x = c->hotx; + p.y = c->hoty; + cp->offset = p; + bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1); + + h = (c->maxy-c->miny)/2; + if(h > 16) + h = 16; + + bc = c->data; + bs = c->data + h*bpl; + + cclr = cp->clr; + cset = cp->set; + for(i = 0; i < h; i++) { + for(j = 0; j < 2; j++) { + cclr[j] = bc[j]; + cset[j] = bs[j]; + } + bc += bpl; + bs += bpl; + cclr += 2; + cset += 2; + } + } + swcurs_load(swc, cp); + m = mousexy(); + swcursorflush(m.x, m.y); + swcurs_enable(swc); +} + +SWcursor* +swcurs_create(ulong *fb, int width, int ldepth, Rectangle r, int bitswap) +{ + SWcursor *swc; + + swc = (SWcursor*)malloc(sizeof(SWcursor)); + swc->fb = fb; + swc->r = r; + swc->d = ldepth; + swc->width = width; + swc->f = bitswap ? CUR_SWP : 0; + swc->x = swc->y = 0; + swc->hotx = swc->hoty = 0; + swc->hidecount = 0; + return swc; +} + +void +swcurs_destroy(SWcursor *swc) +{ + swcurs_disable(swc); + free(swc); +} + +static void +swcursorflush(int x, int y) +{ + Rectangle r; + + /* XXX a little too paranoid here */ + r.min.x = x-16; + r.min.y = y-16; + r.max.x = x+17; + r.max.y = y+17; + flushmemscreen(r); +} + +static void +swcurs_draw_or_undraw(SWcursor *swc) +{ + uchar *p; + uchar *cs; + int w, vw; + int x1 = swc->r.min.x; + int y1 = swc->r.min.y; + int x2 = swc->r.max.x; + int y2 = swc->r.max.y; + int xp = swc->x - swc->hotx; + int yp = swc->y - swc->hoty; + int ofs; + + if(((swc->f & CUR_ENA) && (swc->hidecount <= 0)) + == ((swc->f & CUR_DRW) != 0)) + return; + w = swc->cbwid*BI2BY/(1 << swc->d); + x1 = xp < x1 ? x1 : xp; + y1 = yp < y1 ? y1 : yp; + x2 = xp+w >= x2 ? x2 : xp+w; + y2 = yp+swc->chgt >= y2 ? y2 : yp+swc->chgt; + if(x2 <= x1 || y2 <= y1) + return; + p = (uchar*)(swc->fb + swc->width*y1) + + x1*(1 << swc->d)/BI2BY; + y2 -= y1; + x2 = (x2-x1)*(1 << swc->d)/BI2BY; + vw = swc->width*BY2WD - x2; + w = swc->cbwid - x2; + ofs = swc->cbwid*(y1-yp)+(x1-xp); + cs = swc->save + ofs; + if((swc->f ^= CUR_DRW) & CUR_DRW) { + uchar *cm = swc->mask + ofs; + uchar *cd = swc->data + ofs; + while(y2--) { + x1 = x2; + while(x1--) { + *p = ((*cs++ = *p) & *cm++) ^ *cd++; + p++; + } + cs += w; + cm += w; + cd += w; + p += vw; + } + } else { + while(y2--) { + x1 = x2; + while(x1--) + *p++ = *cs++; + cs += w; + p += vw; + } + } +} + +void +swcurs_hide(SWcursor *swc) +{ + ++swc->hidecount; + swcurs_draw_or_undraw(swc); +} + +void +swcurs_unhide(SWcursor *swc) +{ + if (--swc->hidecount < 0) + swc->hidecount = 0; + swcurs_draw_or_undraw(swc); +} + +void +swcurs_enable(SWcursor *swc) +{ + swc->f |= CUR_ENA; + swcurs_draw_or_undraw(swc); +} + +void +swcurs_disable(SWcursor *swc) +{ + swc->f &= ~CUR_ENA; + swcurs_draw_or_undraw(swc); +} + +void +swcurs_load(SWcursor *swc, Cursor *c) +{ + int i, k; + uchar *bc, *bs, *cd, *cm; + static uchar bdv[4] = {0,Backgnd,Foregnd,0xff}; + static uchar bmv[4] = {0xff,0,0,0xff}; + int bits = 1<d; + uchar mask = (1<f&CUR_SWP) ? 8-bits : 0; + + bc = c->clr; + bs = c->set; + + swcurs_hide(swc); + cd = swc->data; + cm = swc->mask; + swc->hotx = c->offset.x; + swc->hoty = c->offset.y; + swc->chgt = CURSHGT; + swc->cwid = CURSWID; + swc->cbwid = CURSWID*(1<d)/BI2BY; + for(i = 0; i < CURSWID/BI2BY*CURSHGT; i++) { + uchar bcb = *bc++; + uchar bsb = *bs++; + for(k=0; k>7; + int s = z^bswp; + cdv |= (bdv[n]&mask) << s; + cmv |= (bmv[n]&mask) << s; + bcb <<= 1; + bsb <<= 1; + k++; + } + *cd++ = cdv; + *cm++ = cmv; + } + } + swcurs_unhide(swc); +} diff --git a/os/ipaq1110/screen.h b/os/ipaq1110/screen.h new file mode 100644 index 00000000..2d42e9e0 --- /dev/null +++ b/os/ipaq1110/screen.h @@ -0,0 +1,64 @@ +typedef struct Cursor Cursor; +typedef struct LCDmode LCDmode; +typedef struct LCDparam LCDparam; +typedef struct Vdisplay Vdisplay; +typedef struct Vmode Vmode; + +#define CURSWID 16 +#define CURSHGT 16 + +struct Cursor { + Point offset; + uchar clr[CURSWID/BI2BY*CURSHGT]; + uchar set[CURSWID/BI2BY*CURSHGT]; +}; + +struct Vmode { + int x; /* 0 -> default or any match for all fields */ + int y; + uchar depth; + uchar hz; +}; + +struct Vdisplay { + uchar* fb; /* frame buffer */ + ulong colormap[256][3]; + int bwid; + Lock; + Vmode; +}; + +struct LCDparam { + uchar pbs; + uchar dual; + uchar mono; + uchar active; + uchar hsync_wid; + uchar sol_wait; + uchar eol_wait; + uchar vsync_hgt; + uchar sof_wait; + uchar eof_wait; + uchar lines_per_int; + uchar palette_delay; + uchar acbias_lines; + uchar obits; + uchar vsynclow; + uchar hsynclow; +}; + +struct LCDmode { + Vmode; + LCDparam; +}; + +int archlcdmode(LCDmode*); + +Vdisplay *lcd_init(LCDmode*); +void lcd_setcolor(ulong, ulong, ulong, ulong); +void lcd_flush(void); + +extern void blankscreen(int); +extern void drawblankscreen(int); +extern ulong blanktime; +extern Point mousexy(void); diff --git a/os/ipaq1110/tstdraw.b b/os/ipaq1110/tstdraw.b new file mode 100644 index 00000000..dff42a2f --- /dev/null +++ b/os/ipaq1110/tstdraw.b @@ -0,0 +1,107 @@ +implement Test; + +include "sys.m"; + +include "draw.m"; + +Test: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +init(ctxt: ref Draw->Context, nil: list of string) +{ + sys := load Sys Sys->PATH; + draw := load Draw Draw->PATH; + Display, Font, Rect, Point, Image, Screen: import draw; + + # + # Set up connection to display, or use the existing one + # if provided. + # + display: ref Display; + disp: ref Image; + if (ctxt == nil) { + display = draw->Display.allocate(nil); + disp = display.image; + } else { + display = ctxt.display; + disp = ctxt.screen.newwindow(display.image.r, Draw->White); + } + + # + # Initialize colours. + # + red := display.color(Draw->Red); + blue := display.color(Draw->Blue); + white := display.color(Draw->White); + yellow := display.color(Draw->Yellow); + ones := display.ones; + + # + # Paint the screen red. + # + disp.draw(disp.r, red, ones, disp.r.min); + sys->sleep(5000); + + # + # Texture a region with rectangular tiles. + # + texture := display.newimage(((0,0),(2,3)), disp.ldepth, 1, 0); + texture.clipr = ((-10000,-10000),(10000,10000)); + # put something in the texture + texture.draw(((0,0),(1,3)), blue, ones, (0,0)); + texture.draw(((0,0),(2, 1)), blue, ones, (0,0)); + # use texture as both source and mask to let + # destination colour show through + disp.draw(((100,100),(200,200)), texture, texture, (0,0)); + sys->sleep(5000); + + # + # White-out a quarter of the pixels in a region, + # to make the region appear shaded. + # + stipple := display.newimage(((0,0),(2,2)), disp.ldepth, 1, 0); + stipple.draw(((0,0),(1,1)), ones, ones, (0,0)); + disp.draw(((100,100),(300,200)), white, stipple, (0,0)); + sys->sleep(5000); + + # + # Draw textured characters. + # + font := Font.open(display, "*default*"); + disp.text((100,210), texture, (0,0), font, "Hello world"); + sys->sleep(5000); + + # + # Draw picture in elliptical frame. + # + delight := display.open("/icons/delight.bit"); + piccenter := delight.r.min.add(delight.r.max).div(2); + disp.fillellipse((200,100), 150, 50, delight, piccenter); + disp.ellipse((200,100), 150, 50, 3, yellow, (0,0)); + sys->sleep(5000); + + # + # Draw a parabolic brush stroke using an elliptical brush + # to reveal more of the picture, consistent with what's + # already visible. + # + dx : con 15; + dy : con 3; + brush := display.newimage(((0,0),(2*dx+1,2*dy+1)), disp.ldepth, + 0, 0); + brush.fillellipse((dx,dy), dx, dy, ones, (0,0)); + for(x:=delight.r.min.x; xsleep(5); + } +} diff --git a/os/ipaq1110/upd b/os/ipaq1110/upd new file mode 100755 index 00000000..c4d69032 --- /dev/null +++ b/os/ipaq1110/upd @@ -0,0 +1,4 @@ +#!/dis/sh +bind -a '#F' /dev +echo erase all >/dev/flash/kernelctl +dd -conv sync /dev/flash/kernel diff --git a/os/ipengine/NOTICE b/os/ipengine/NOTICE new file mode 100644 index 00000000..f7d66a0d --- /dev/null +++ b/os/ipengine/NOTICE @@ -0,0 +1,4 @@ +Inferno® Copyright © 1996-1999 Lucent Technologies Inc. All rights reserved. +PowerPC support Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net). All rights reserved. +MPC8xx Inferno PowerPC port Copyright © 1998-2003 Vita Nuova Holdings Limited. All rights reserved. +ip-Engine1 PowerPC port Copyright © 2000-2003 Vita Nuova Holdings Limited. All rights reserved. diff --git a/os/ipengine/README b/os/ipengine/README new file mode 100644 index 00000000..3572de78 --- /dev/null +++ b/os/ipengine/README @@ -0,0 +1,40 @@ +The ipEngine-1 is a Motorola MPC8xx-based small computer +made by Bright Star Engineering (www.brightstareng.com) +which also provides Linux and their own operating system for it. +It is an interesting hardware platform to do network-oriented Inferno +applications. + +The ipEngine is unusual in including an FPGA (Altera Flex 6016). +See fpga(3) and fpgaload(8). +We are also working on software to help program the thing. + + +Booting the ipEngine + +0. serial cable (port 1, 9600 baud), ether, power etc. + +1. make appropriate entries for your site in flash + using the BSE monitor: + + set netmask 255.255.255.0 + set nameserver 200.1.1.11 + set server 200.1.1.11 + set serverip 200.1.1.11 + set gateway 200.1.1.50 + set hostname stella + set domain vitanuova.com + set myip 200.1.1.96 + +2. add an entry to do the boot by tftp: + + set bootcmd "load /usr/inferno/os/ipengine/iipe 3000; go 3020" + + contrary to the BSE documentation this loads any + binary file at the given address. the -b option + doesn't seem to be needed. + +3. reset + it should load and start the kernel + +Vita Nuova +16 July 2003 diff --git a/os/ipengine/archipe.c b/os/ipengine/archipe.c new file mode 100644 index 00000000..151f72f7 --- /dev/null +++ b/os/ipengine/archipe.c @@ -0,0 +1,488 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include +#include +#include "screen.h" + +#include "../port/netif.h" +#include "etherif.h" +#include "../port/flashif.h" + +#include "archipe.h" + +/* + * board-specific support for the Bright Star Engineering ipEngine-1 + */ + +enum { + /* sccr */ + COM3= IBIT(1)|IBIT(2), /* clock output disabled */ + TBS = IBIT(6), /* =0, time base is OSCCLK/{4,16}; =1, time base is GCLK2/16 */ + RTSEL = IBIT(8), /* =0, select main oscillator (OSCM); =1, select external crystal (EXTCLK) */ + RTDIV = IBIT(7), /* =0, divide by 4; =1, divide by 512 */ + CRQEN = IBIT(9), /* =1, switch to high frequency when CPM active */ + PRQEN = IBIT(10), /* =1, switch to high frequency when interrupt pending */ + + /* plprcr */ + CSRC = IBIT(21), /* =0, clock is DFNH; =1, clock is DFNL */ + + Showports = 0, /* used to find lightly-documented bits set by bootstrap (eg, PDN) */ +}; + +static ulong ports[4*4]; + +/* + * called early in main.c, after machinit: + * using board and architecture specific registers, initialise + * 8xx registers that need it and complete initialisation of the Mach structure. + */ +void +archinit(void) +{ + IMM *io; + int mf; + + io = m->iomem; /* run by reset code: no need to lock */ + m->clockgen = 4000000; /* crystal frequency */ + m->oscclk = m->clockgen/MHz; + io->plprcrk = KEEP_ALIVE_KEY; + io->plprcr &= ~CSRC; /* general system clock is DFNH */ + mf = (io->plprcr >> 20)+1; /* use timing set by bootstrap */ + io->plprcrk = ~KEEP_ALIVE_KEY; + io->sccrk = KEEP_ALIVE_KEY; + io->sccr |= CRQEN | PRQEN | RTDIV | COM3; /* devfpga.c resets COM3 if needed */ + io->sccrk = ~KEEP_ALIVE_KEY; + m->cpuhz = m->clockgen*mf; + m->speed = m->cpuhz/MHz; + if((io->memc[CLOCKCS].base & 1) == 0){ /* prom hasn't mapped it */ + io->memc[CLOCKCS].option = 0xFFFF0F24; + io->memc[CLOCKCS].base = 0xFF020001; + } + if(Showports){ + ports[0] = io->padat; + ports[1] = io->padir; + ports[2] = io->papar; + ports[3] = io->paodr; + ports[4] = io->pbdat; + ports[5] = io->pbdir; + ports[6] = io->pbpar; + ports[7] = io->pbodr; + ports[8] = io->pcdat; + ports[9] = io->pcdir; + ports[10] = io->pcpar; + ports[11] = io->pcso; + ports[12] = io->pddat; + ports[13] = io->pddir; + ports[14] = io->pdpar; + ports[15] = 0; + } +} + +static ulong +banksize(int x, ulong *pa) +{ + IMM *io; + + io = m->iomem; + if((io->memc[x].base & 1) == 0) + return 0; /* bank not valid */ + *pa = io->memc[x].base & ~0x7FFF; + return -(io->memc[x].option&~0x7FFF); +} + +/* + * initialise the kernel's memory configuration: + * there are two banks (base0, npage0) and (base1, npage1). + * initialise any other values in conf that are board-specific. + */ +void +archconfinit(void) +{ + ulong pa, nbytes, ktop; + + conf.nscc = 2; + conf.sccuarts = 0; /* no SCC uarts */ + conf.smcuarts = (1<<0)|(1<<1); /* SMC1 (console) and SMC2 */ + + nbytes = banksize(DRAMCS, &pa); + if(nbytes == 0){ /* force default */ + nbytes = 16*1024*1024; + pa = 0; + } + conf.npage0 = nbytes/BY2PG; + conf.base0 = pa; + + conf.npage1 = 0; + + /* the following assumes the kernel text and/or data is in bank 0 */ + ktop = PGROUND((ulong)end); + ktop = PADDR(ktop) - conf.base0; + conf.npage0 -= ktop/BY2PG; + conf.base0 += ktop; +} + +void +cpuidprint(void) +{ + ulong v; + + print("PVR: "); + switch(m->cputype){ + case 0x01: print("MPC601"); break; + case 0x03: print("MPC603"); break; + case 0x04: print("MPC604"); break; + case 0x06: print("MPC603e"); break; + case 0x07: print("MPC603e-v7"); break; + case 0x50: print("MPC8xx"); break; + default: print("PowerPC version #%x", m->cputype); break; + } + print(", revision #%lux\n", getpvr()&0xffff); + print("IMMR: "); + v = getimmr() & 0xFFFF; + switch(v>>8){ + case 0x00: print("MPC860/821"); break; + case 0x20: print("MPC823"); break; + case 0x21: print("MPC823A"); break; + default: print("Type #%lux", v>>8); break; + } + print(", mask #%lux\n", v&0xFF); + print("%lud MHz system\n", m->cpuhz/MHz); + print("\n"); + + if(Showports){ + + print("plprcr=%8.8lux sccr=%8.8lux\n", m->iomem->plprcr, m->iomem->sccr); + print("pipr=%8.8lux\n", m->iomem->pipr); + + print("ports:\n"); + for(v=0;viomem->memc); v++) + if(m->iomem->memc[v].base & 1) + print("%ld %8.8lux %8.8lux\n", v, m->iomem->memc[v].base, m->iomem->memc[v].option); + } +} + +/* + * fetch parameters from flash, as stored by BSE bootstrap, + * compensating for a bug in its fset that produces silly entries. + */ + +static int +envnameok(char *s) +{ + if(*s == '*') + s++; + if(*s >= '0' && *s <= '9' || *s == 0) + return 0; + for(; *s; s++) + if(*s >= '0' && *s <= '9' || + *s >= 'a' && *s <= 'z' || + *s >= 'A' && *s <= 'Z' || + *s == '.' || *s == '_' || *s == '#'){ + /* ok */ + }else + return 0; + return 1; +} + +int +archconfval(char **names, char **vals, int limit) +{ + uchar *b, *e; + char *s; + int n, v, l, o; + static char bootargs[512]; + + /* we assume we can access this space before mmuinit() */ + b = KADDR(PHYSFLASH+0x4000); + if(*b & 1){ + b += 0x2000; /* try alternative location */ + if(*b & 1) + return 0; + } + v = (b[2]<<8)|b[3]; + b += 4; + if(v >= 0x2000-4) + return 0; + n = 0; + o = 0; + e = b+v; + for(; b < e; b += v){ + v = *b; + if(v == 0xFF || n >= limit) + break; + s = (char*)b+1; + if(v >= 0x80){ + v = ((v&0x7F)<<8) | b[1]; + s++; + } + if(envnameok(s)){ + names[n] = s; + s += strlen(s)+1; + l = strlen(s)+1; + if(o+l > sizeof(bootargs)) + break; + vals[n] = bootargs+o; + memmove(vals[n], s, l); + o += l; + n++; + } + } + return n; +} + +void +toggleled(int b) +{ + int s; + s = splhi(); + m->iomem->pdpar &= ~(0x20<iomem->pddir |= 0x20<iomem->pddat ^= 0x020<ticks%MS2TK(1000) == 0 && m->iomem) + toggleled(0); +} + +void (*archclocktick)(void) = twinkle; + +/* + * invoked by ../port/taslock.c:/^ilock: + * reset watchdog timer here, if there is one and it is enabled + */ +void +clockcheck(void) +{ +} + +/* + * for ../port/devflash.c:/^flashreset + * retrieve flash type, virtual base and length and return 0; + * return -1 on error (no flash) + */ +int +archflashreset(int bank, Flash *f) +{ + if(bank != 0) + return -1; + f->type = "Intel28F320B3B"; + f->addr = KADDR(PHYSFLASH); + f->size = 4*1024*1024; + f->width = 2; + return 0; +} + +void +archflashwp(Flash*, int) +{ +} + +/* + * set ether parameters: the contents should be derived from EEPROM or NVRAM + */ +int +archether(int ctlno, Ether *ether) +{ + if(isaconfig("ether", ctlno, ether) == 0 && ctlno > 0) + return -1; + if(ctlno == 0){ + ether->type = "SCC"; + ether->port = 2; + } + memmove(ether->ea, KADDR(PHYSFLASH+0x3FFA), Eaddrlen); + return 1; +} + +/* + * enable the clocks for the given SCC ether and reveal them to the caller. + * do anything else required to prepare the transceiver (eg, set full-duplex, reset loopback). + */ +int +archetherenable(int cpmid, int *rcs, int *tcs, int mbps, int fd) +{ + IMM *io; + + if(cpmid != CPscc2) + return -1; + USED(mbps); + io = ioplock(); + io->pcpar &= ~EnetLoopback; + io->pcso &= ~EnetLoopback; + io->pcdir |= EnetLoopback; + io->pcdat &= ~EnetLoopback; + if(0){ /* TO CHECK: causes ether errors if used */ + io->pbpar &= ~EnetFullDuplex; + io->pbdir |= EnetFullDuplex; + if(fd) + io->pbdat |= EnetFullDuplex; + else + io->pbdat &= ~EnetFullDuplex; + } + io->pbpar &= ~EnableEnet; + io->pbdir |= EnableEnet; + io->pbdat |= EnableEnet; + io->papar |= SIBIT(7)|SIBIT(6); /* enable CLK1 and CLK2 */ + io->padir &= ~(SIBIT(7)|SIBIT(6)); + iopunlock(); + *rcs = CLK2; + *tcs = CLK1; + return 0; +} + +/* + * do anything extra required to enable the UART on the given CPM port + */ +static ulong uartsactive; + +void +archenableuart(int id, int irda) +{ + IMM *io; + + USED(id); /* both uarts seem to be controlled by the same bit */ + USED(irda); /* no IrDA on ipEngine */ + io = ioplock(); + if(uartsactive == 0){ + io->pbpar &= ~EnableRS232; + io->pbdir |= EnableRS232; + io->pbdat |= EnableRS232; + } + uartsactive |= 1<pbdat &= ~EnableRS232; + iopunlock(); +} + +/* + * enable the external USB transceiver + * speed is 12MHz if highspeed is non-zero; 1.5MHz if zero + * master is non-zero if the node is acting as USB Host and should provide power + */ +void +archenableusb(int highspeed, int master) +{ + IMM *io; + + USED(master); + io = ioplock(); + io->pcpar &= ~USBFullSpeed; + io->pcso &= ~USBFullSpeed; + if(highspeed) + io->pcdat |= USBFullSpeed; + else + io->pcdat &= ~USBFullSpeed; + io->pcdir |= USBFullSpeed; + iopunlock(); +} + +/* + * shut down the USB transceiver + */ +void +archdisableusb(void) +{ + /* nothing to be done on ipEngine, apparently */ +} + +/* + * set the external infrared transceiver to the given speed + */ +void +archsetirxcvr(int highspeed) +{ + USED(highspeed); +} + +/* + * force hardware reset/reboot + */ +void +archreboot(void) +{ + IMM *io; + + io = m->iomem; + io->plprcrk = KEEP_ALIVE_KEY; + io->plprcr |= 1<<7; /* checkstop reset enable */ + io->plprcrk = ~KEEP_ALIVE_KEY; + eieio(); + io->sdcr = 1; + eieio(); + io->lccr = 0; /* switch LCD off */ + eieio(); + firmware(0); +} + +/* + * enable/disable the LCD panel's backlight + */ +void +archbacklight(int on) +{ + USED(on); +} + +/* + * set parameters to describe the screen + */ +int +archlcdmode(Mode *m) +{ + /* sample parameters in case a panel is attached to the external pins */ + m->x = 640; + m->y = 480; + m->d = 3; + m->lcd.freq = 25000000; + m->lcd.ac = 0; + m->lcd.vpw = 1; + m->lcd.wbf = 33; + m->lcd.wbl = 228; + m->lcd.flags = IsColour | IsTFT | OELow | VsyncLow | ClockLow; + return -1; /* there isn't a screen */ +} + +/* + * there isn't a keyboard port + */ +void +archkbdinit(void) +{ +} diff --git a/os/ipengine/archipe.h b/os/ipengine/archipe.h new file mode 100644 index 00000000..575f7d8d --- /dev/null +++ b/os/ipengine/archipe.h @@ -0,0 +1,32 @@ +/* + * values for Brightstar Engineering ipEngine + */ +enum { + /* CS assignment */ + BOOTCS = 0, /* flash */ + FPGACS = 1, /* 8Mb FPGA space */ + DRAMCS = 2, + FPGACONFCS = 3, /* FPGA config */ + CLOCKCS = 4, /* clock synth reg */ +}; + +enum { + /* port A pins */ + VCLK= SIBIT(5), + BCLK= SIBIT(4), + + /* port B */ + EnableVCLK= IBIT(30), + EnableEnet= IBIT(29), + EnableRS232= IBIT(28), + EnetFullDuplex= IBIT(16), + + /* port C */ + nCONFIG = SIBIT(13), /* FPGA configuration */ + USBFullSpeed= SIBIT(12), + PDN= SIBIT(5), /* ? seems to control power to FPGA subsystem? */ + EnetLoopback= SIBIT(4), + + /* nSTATUS is ip_b1, conf_done is ip_b0 in PIPR (hardware doc wrongly says ip_b2 and ip_b1) */ + +}; diff --git a/os/ipengine/dat.h b/os/ipengine/dat.h new file mode 100644 index 00000000..19d7c560 --- /dev/null +++ b/os/ipengine/dat.h @@ -0,0 +1,162 @@ +typedef struct Conf Conf; +typedef struct FPU FPU; +typedef struct FPenv FPenv; +typedef struct IMM IMM; +typedef struct Irqctl Irqctl; +typedef struct ISAConf ISAConf; +typedef struct Label Label; +typedef struct Lock Lock; +typedef struct Mach Mach; +typedef struct Map Map; +typedef struct Power Power; +typedef struct RMap RMap; +typedef struct Ureg Ureg; + +typedef ulong Instr; + +#define MACHP(n) (n==0? &mach0 : *(Mach**)0) + +struct Lock +{ + ulong key; + ulong pc; + ulong sr; + int pri; +}; + +struct Label +{ + ulong sp; + ulong pc; +}; + +/* + * Proc.fpstate + */ +enum +{ + FPINIT, + FPACTIVE, + FPINACTIVE, +}; + +/* + * This structure must agree with FPsave and FPrestore asm routines + */ +struct FPenv +{ + union { + double fpscrd; + struct { + ulong pad; + ulong fpscr; + }; + }; + int fpistate; /* emulated fp */ + ulong emreg[32][3]; /* emulated fp */ +}; +/* + * This structure must agree with fpsave and fprestore asm routines + */ +struct FPU +{ + double fpreg[32]; + FPenv env; +}; + +struct Conf +{ + ulong nmach; /* processors */ + ulong nproc; /* processes */ + ulong npage0; /* total physical pages of memory */ + ulong npage1; /* total physical pages of memory */ + ulong npage; /* total physical pages of memory */ + ulong base0; /* base of bank 0 */ + ulong base1; /* base of bank 1 */ + ulong ialloc; /* max interrupt time allocation in bytes */ + + int nscc; /* number of SCCs implemented */ + ulong smcuarts; /* bits for SMCs to define as eiaN */ + ulong sccuarts; /* bits for SCCs to define as eiaN */ + int nocts2; /* CTS2 and CD2 aren't connected */ + uchar* nvrambase; /* virtual address of nvram */ + ulong nvramsize; /* size in bytes */ +}; + +#include "../port/portdat.h" + +/* + * machine dependent definitions not used by ../port/dat.h + */ + +struct Mach +{ + /* OFFSETS OF THE FOLLOWING KNOWN BY l.s */ + int machno; /* physical id of processor (unused) */ + ulong splpc; /* pc of last caller to splhi (unused) */ + int mmask; /* 1<machno (unused) */ + + /* ordering from here on irrelevant */ + ulong ticks; /* of the clock since boot time */ + Proc *proc; /* current process on this processor */ + Label sched; /* scheduler wakeup */ + Lock alarmlock; /* access to alarm list */ + void *alarm; /* alarms bound to this clock */ + int nrdy; + int speed; /* general system clock in MHz */ + long oscclk; /* oscillator frequency (MHz) */ + long cpuhz; /* general system clock (cycles) */ + long clockgen; /* clock generator frequency (cycles) */ + int cputype; + ulong delayloop; + ulong* bcsr; + IMM* iomem; /* MPC8xx internal i/o control memory */ + + /* MUST BE LAST */ + int stack[1]; +}; +extern Mach mach0; + + +/* + * a parsed .ini line + */ +#define NISAOPT 8 + +struct ISAConf { + char* type; + ulong port; + ulong irq; + ulong mem; + int dma; + ulong size; + ulong freq; + uchar bus; + + int nopt; + char* opt[NISAOPT]; +}; + +struct Map { + int size; + ulong addr; +}; + +struct RMap { + char* name; + Map* map; + Map* mapend; + + Lock; +}; + +struct Power { + Dev* dev; + int (*powerdown)(Power*); + int (*powerup)(Power*); + int state; + void* arg; +}; + +extern register Mach *m; +extern register Proc *up; diff --git a/os/ipengine/devfpga.c b/os/ipengine/devfpga.c new file mode 100644 index 00000000..41d1a445 --- /dev/null +++ b/os/ipengine/devfpga.c @@ -0,0 +1,389 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "io.h" +#include "archipe.h" + +enum { + FPGASIZE = 8*1024*1024, + FPGATMR = 2-1, /* BCLK timer number (mapped to origin 0) */ + TIMERSH = FPGATMR*4, /* timer field shift */ + + COM3= IBIT(1)|IBIT(2), /* sccr: clock output disabled */ + + ConfDone = 1<<1, + nStatus = 1<<0, +}; + +/* + * provisional FPGA interface for simple development work; + * for more complex things, use this to load the device then have a + * purpose-built device driver or module + */ + +enum{ + Qdir, + Qmemb, + Qmemw, + Qprog, + Qctl, + Qclk, + Qstatus, +}; + +static struct { + QLock; + int clkspeed; +} fpga; + +static void resetfpga(void); +static void startfpga(int); +static int endfpga(void); +static int fpgastatus(void); +static void powerfpga(int); +static void vclkenable(int); +static void vclkset(char*, char*, char*, char*); +static void memmovew(ushort*, ushort*, long); + +static Dirtab fpgadir[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "fpgamemb", {Qmemb, 0}, FPGASIZE, 0666, + "fpgamemw", {Qmemw, 0}, FPGASIZE, 0666, + "fpgaprog", {Qprog, 0}, 0, 0222, + "fpgastatus", {Qstatus, 0}, 0, 0444, + "fpgactl", {Qctl, 0}, 0, 0666, + "fpgaclk", {Qclk, 0}, 0, 0666, +}; + +static char Eodd[] = "odd count or offset"; + +static void +fpgareset(void) +{ + powerfpga(0); +} + +static Chan* +fpgaattach(char *spec) +{ + return devattach('G', spec); +} + +static Walkqid* +fpgawalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, fpgadir, nelem(fpgadir), devgen); +} + +static int +fpgastat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, fpgadir, nelem(fpgadir), devgen); +} + +static Chan* +fpgaopen(Chan *c, int omode) +{ + return devopen(c, omode, fpgadir, nelem(fpgadir), devgen); +} + +static void +fpgaclose(Chan*) +{ +} + +static long +fpgaread(Chan *c, void *buf, long n, vlong offset) +{ + int v; + char stat[32], *p; + + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, fpgadir, nelem(fpgadir), devgen); + + switch((ulong)c->qid.path){ + case Qmemb: + if(offset >= FPGASIZE) + return 0; + if(offset+n >= FPGASIZE) + n = FPGASIZE-offset; + memmove(buf, KADDR(FPGAMEM+offset), n); + return n; + case Qmemw: + if((n | offset) & 1) + error(Eodd); + if(offset >= FPGASIZE) + return 0; + if(offset+n >= FPGASIZE) + n = FPGASIZE-offset; + memmovew((ushort*)buf, (ushort*)KADDR(FPGAMEM+offset), n); + return n; + case Qstatus: + v = fpgastatus(); + p = seprint(stat, stat+sizeof(stat), "%sconfig", v&ConfDone?"":"!"); + seprint(p, stat+sizeof(stat), " %sstatus\n", v&nStatus?"":"!"); + return readstr(offset, buf, n, stat); + case Qclk: + return readnum(offset, buf, n, fpga.clkspeed, NUMSIZE); + case Qctl: + case Qprog: + return 0; + } + error(Egreg); + return 0; /* not reached */ +} + +static long +fpgawrite(Chan *c, void *buf, long n, vlong offset) +{ + int i, j, v; + ulong w; + Cmdbuf *cb; + ulong *cfg; + uchar *cp; + + switch((ulong)c->qid.path){ + case Qmemb: + if(offset >= FPGASIZE) + return 0; + if(offset+n >= FPGASIZE) + n = FPGASIZE-offset; + memmove(KADDR(FPGAMEM+offset), buf, n); + return n; + case Qmemw: + if((n | offset) & 1) + error(Eodd); + if(offset >= FPGASIZE) + return 0; + if(offset+n >= FPGASIZE) + n = FPGASIZE-offset; + memmovew((ushort*)KADDR(FPGAMEM+offset), (ushort*)buf, n); + return n; + case Qctl: + cb = parsecmd(buf, n); + if(waserror()){ + free(cb); + nexterror(); + } + if(cb->nf < 1) + error(Ebadarg); + if(strcmp(cb->f[0], "reset") == 0) + resetfpga(); + else if(strcmp(cb->f[0], "bclk") == 0){ + v = 48; + if(cb->nf > 1) + v = strtoul(cb->f[1], nil, 0); + if(v <= 0 || 48%v != 0) + error(Ebadarg); + startfpga(48/v-1); + }else if(strcmp(cb->f[0], "vclk") == 0){ + if(cb->nf == 5){ /* vclk n m v r */ + vclkenable(1); + vclkset(cb->f[1], cb->f[2], cb->f[3], cb->f[4]); + }else + vclkenable(cb->nf < 2 || strcmp(cb->f[1], "on") == 0); + }else if(strcmp(cb->f[0], "power") == 0) + powerfpga(cb->nf < 2 || strcmp(cb->f[1], "off") != 0); + else + error(Ebadarg); + poperror(); + free(cb); + return n; + case Qprog: + qlock(&fpga); + if(waserror()){ + qunlock(&fpga); + nexterror(); + } + powerfpga(1); + resetfpga(); + cfg = KADDR(FPGACR); + cp = buf; + for(i=0; i>= 1; + } + } + for(j=0; j<50; j++) /* Altera note says at least 10 clock cycles, but microblaster uses 50 */ + *cfg = 0; + v = fpgastatus(); + if(v != (nStatus|ConfDone)){ + snprint(up->genbuf, sizeof(up->genbuf), "error loading fpga: status %d", v); + error(up->genbuf); + } + poperror(); + qunlock(&fpga); + return n; + } + error(Egreg); + return 0; /* not reached */ +} + +/* + * PDN seems to control power to the FPGA subsystem + * but it is not documented nor is its scope clear (PLL as well?). + * It will not run without it. + */ +static void +powerfpga(int on) +{ + IMM *io; + + io = ioplock(); + if(io->sccr & COM3){ + io->sccrk = KEEP_ALIVE_KEY; + io->sccr &= ~ COM3; /* FPGA designs can use the clock */ + io->sccrk = ~KEEP_ALIVE_KEY; + } + io->pcpar &= ~PDN; + io->pcdir |= PDN; + if(on) + io->pcdat &= ~PDN; + else + io->pcdat |= PDN; + iopunlock(); +} + +static void +resetfpga(void) +{ + IMM *io; + + io = ioplock(); + io->pcpar &= ~nCONFIG; + io->pcdir |= nCONFIG; + io->pcdat &= ~nCONFIG; + microdelay(200); + io->pcdat |= nCONFIG; + iopunlock(); +} + +static int +fpgastatus(void) +{ + /* isolate status bits IP_B0 and IP_B1 */ + return (m->iomem->pipr>>14) & (ConfDone|nStatus); +} + +static void +startfpga(int scale) +{ + IMM *io; + + io = ioplock(); + io->tgcr &= ~(0xF<tmr2 = ((scale&0xFF)<<8) | 0x2A; + io->tcn2 = 0; + io->trr2 = 0; + io->ter2 = 0xFFFF; + io->tgcr |= 0x1<padir |= BCLK; + io->papar |= BCLK; + iopunlock(); +} + +static void +vclkenable(int i) +{ + IMM *io; + + io = ioplock(); + io->padir &= ~VCLK; + io->papar &= ~VCLK; + io->pbdir |= EnableVCLK; + io->pbpar &= ~EnableVCLK; + if(i) + io->pbdat |= EnableVCLK; + else + io->pbdat &= ~EnableVCLK; + iopunlock(); +} + +static void +vclkin(ulong *clk, int v) +{ + int i; + + for(i=0; i<7; i++) + *clk = (v>>i) & 1; +} + +static void +vclkset(char *ns, char *ms, char *vs, char *rs) +{ + int n, m, v, r; + ulong *clk; + + clk = KADDR(CLOCKCR); + n = strtol(ns, nil, 0); + m = strtol(ms, nil, 0); + v = strtol(vs, nil, 0); + r = strtol(rs, nil, 0); + if(n < 3 || n > 127 || m < 3 || m > 127 || v != 1 && v != 8 || + r != 1 && r != 2 && r != 4 && r != 8) + error(Ebadarg); + vclkenable(0); + vclkin(clk, n); + vclkin(clk, m); + *clk = (v==0) & 1; + *clk = 1; *clk = 1; + *clk = r == 2 || r == 8; + *clk = r == 4 || r == 8; + *clk = 1; /* clock out */ + *clk = 0; /* disable clk/x */ + *clk = 1; *clk = 0; *clk = 1; + *clk = 0; *clk = 0; *clk = 0; + vclkenable(1); +} + +/* + * copy data aligned on 16-bit word boundaries. + */ +static void +memmovew(ushort *to, ushort *from, long count) +{ + int n; + + if(count <= 0) + return; + count >>= 1; + n = (count+7) >> 3; + switch(count&7) { /* Duff's device */ + case 0: do { *to++ = *from++; + case 7: *to++ = *from++; + case 6: *to++ = *from++; + case 5: *to++ = *from++; + case 4: *to++ = *from++; + case 3: *to++ = *from++; + case 2: *to++ = *from++; + case 1: *to++ = *from++; + } while(--n > 0); + } +} + +Dev fpgadevtab = { + 'G', + "fpga", + + fpgareset, + devinit, + devshutdown, + fpgaattach, + fpgawalk, + fpgastat, + fpgaopen, + devcreate, + fpgaclose, + fpgaread, + devbread, + fpgawrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/ipengine/flash28f320b3b.c b/os/ipengine/flash28f320b3b.c new file mode 100644 index 00000000..27bb90d6 --- /dev/null +++ b/os/ipengine/flash28f320b3b.c @@ -0,0 +1,43 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "../port/flashif.h" + +/* + * Intel 28F320B3B in word mode + */ + +#define I(x) (x) +enum { + ReadArray = 0x00FF, +}; + +#include "../port/flashintel" + +static int +reset(Flash *f) +{ + f->id = 0x0089; /* can't use autoselect: might be running in flash */ + f->devid = 0x8897; + f->write = intelwrite2; + f->erasezone = intelerase; + f->width = 2; + f->cmask = 0x00FF; + *(ushort*)f->addr = ClearStatus; + *(ushort*)f->addr = ReadArray; + f->nr = 2; + f->regions[0] = (Flashregion){8, 0, 8*(8*1024), 8*1024, 0}; + f->regions[1] = (Flashregion){63, 64*1024, 4*1024*1024, 64*1024, 0}; + return 0; +} + +void +flash28f320b3blink(void) +{ + addflashcard("Intel28F320B3B", reset); +} diff --git a/os/ipengine/fns.h b/os/ipengine/fns.h new file mode 100644 index 00000000..6a92e5cd --- /dev/null +++ b/os/ipengine/fns.h @@ -0,0 +1,120 @@ +#include "../port/portfns.h" + +void addpower(Power*); +void archbacklight(int); +void archconfinit(void); +int archconfval(char**, char**, int); +void archdisableuart(int); +void archdisableusb(void); +void archdisablevideo(void); +void archenableuart(int, int); +void archenableusb(int, int); +void archenablevideo(void); +void archkbdinit(void); +void archresetvideo(void); +int archetherenable(int, int*, int*, int, int); +void archinit(void); +int archoptionsw(void); +void archreboot(void); +void archsetirxcvr(int); +uchar* archvideobuffer(long); +ulong baudgen(int, int); +int brgalloc(void); +void brgfree(int); +int cistrcmp(char*, char*); +int cistrncmp(char*, char*, int); +void clockcheck(void); +void clockinit(void); +void clockintr(Ureg*); +void clrfptrap(void); +#define coherence() /* nothing needed for uniprocessor */ +void cpminit(void); +void cpuidprint(void); +void dcflush(void*, ulong); +void dcinval(void*, ulong); +void delay(int); +void dtlbmiss(void); +void dumplongs(char*, ulong*, int); +void dumpregs(Ureg*); +void eieio(void); +void faultpower(Ureg*); +void firmware(int); +void fpinit(void); +int fpipower(Ureg*); +void fpoff(void); +void fprestore(FPU*); +void fpsave(FPU*); +ulong fpstatus(void); +char* getconf(char*); +ulong getdar(void); +ulong getdec(void); +ulong getdepn(void); +ulong getdsisr(void); +ulong getimmr(void); +ulong getmsr(void); +ulong getpvr(void); +ulong gettbl(void); +ulong gettbu(void); +void gotopc(ulong); +void icflush(void*, ulong); +void idle(void); +#define idlehands() /* nothing to do in the runproc */ +void intr(Ureg*); +void intrenable(int, void (*)(Ureg*, void*), void*, int, char*); +void intrdisable(int, void (*)(Ureg*, void*), void*, int, char*); +int intrstats(char*, int); +void intrvec(void); +int isaconfig(char*, int, ISAConf*); +int isvalid_va(void*); +void itlbmiss(void); +void kbdinit(void); +void kbdreset(void); +void lcdpanel(int); +void links(void); +void mapfree(RMap*, ulong, int); +void mapinit(RMap*, Map*, int); +void mathinit(void); +void mmuinit(void); +ulong* mmuwalk(ulong*, ulong, int); +void pcmenable(void); +void pcmintrenable(int, void (*)(Ureg*, void*), void*); +int pcmpin(int slot, int type); +void pcmpower(int, int); +int pcmpowered(int); +void pcmsetvcc(int, int); +void pcmsetvpp(int, int); +void procsave(Proc*); +void procsetup(Proc*); +void putdec(ulong); +void putmsr(ulong); +void puttwb(ulong); +ulong rmapalloc(RMap*, ulong, int, int); +void screeninit(void); +int screenprint(char*, ...); /* debugging */ +void (*screenputs)(char*, int); +int segflush(void*, ulong); +void toggleled(int); +void setpanic(void); +long spioutin(void*, long, void*); +void spireset(void); +ulong _tas(ulong*); +void trapinit(void); +void trapvec(void); +void uartinstall(void); +void uartspecial(int, int, Queue**, Queue**, int (*)(Queue*, int)); +void uartwait(void); /* debugging */ +void videoreset(void); +void videotest(void); +void wbflush(void); + +#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) +ulong getcallerpc(void*); + +#define KADDR(a) ((void*)((ulong)(a)|KZERO)) +#define PADDR(a) ((((ulong)(a)&KSEGM)!=KSEG0)?(ulong)(a):((ulong)(a)&~KZERO)) + +/* IBM bit field order */ +#define IBIT(b) ((ulong)1<<(31-(b))) +#define SIBIT(n) ((ushort)1<<(15-(n))) + +/* compatibility with inf2.1 */ diff --git a/os/ipengine/io.h b/os/ipengine/io.h new file mode 100644 index 00000000..30312c68 --- /dev/null +++ b/os/ipengine/io.h @@ -0,0 +1 @@ +#include "../mpc/800io.h" diff --git a/os/ipengine/ipe b/os/ipengine/ipe new file mode 100644 index 00000000..8e00c855 --- /dev/null +++ b/os/ipengine/ipe @@ -0,0 +1,109 @@ +# Bright Star Engineering ipEngine-1 +dev + root + cons archipe + env + mnt + pipe + prog + rtc + srv + dup + ssl + cap + sign + + ip bootp ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium netaux + ether netif netaux ethermedium + uart + flash +# i2c i2c + + ftl + + fpga + +ip + il + tcp + udp +# rudp +# igmp + ipifc + icmp + icmp6 + ipmux + +lib + interp + keyring + sec + mp + math + kern + +link + etherscc + ethermedium + flash28f320b3b + +mod + math + sys + keyring + +port + alarm + alloc + allocb + chan + dev + dial + dis + discall + exception + exportfs + inferno + latin1 + nocache + nodynld + parse + pgrp + print + proc + qio + qlock + random + sysfile + taslock + xalloc + +code + int cflag = 0; + int consoleprint = 1; + int panicreset = 0; + int main_pool_pcnt = 50; + int heap_pool_pcnt = 50; + int image_pool_pcnt = 0; + void screeninit(void){} + +init + ipeinit + +root + /chan / + /dev / + /dis / + /env / + /fd / + /n / + /net / + /nvfs / + /prog / + /osinit.dis + /dis/lib/auth.dis + /dis/lib/ssl.dis + /n/local / + /n/remote / +# authentication + /nvfs/default /usr/inferno/keyring/default diff --git a/os/ipengine/main.c b/os/ipengine/main.c new file mode 100644 index 00000000..cdd7ed58 --- /dev/null +++ b/os/ipengine/main.c @@ -0,0 +1,348 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "version.h" + +#define MAXCONF 32 + +extern ulong kerndate; +extern int cflag; +int remotedebug; + +extern int main_pool_pcnt; +extern int heap_pool_pcnt; +extern int image_pool_pcnt; + +char *confname[MAXCONF]; +char *confval[MAXCONF]; +int nconf; + +void addconf(char *, char *); + +static void +options(void) +{ + nconf = archconfval(confname, confval, sizeof(confname)); +} + +void +doc(char *m) +{ + USED(m); + //iprint("%s...\n", m); +} + +static void +poolsizeinit(void) +{ + ulong nb; + + nb = conf.npage*BY2PG; + poolsize(mainmem, (nb*main_pool_pcnt)/100, 0); + poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0); + poolsize(imagmem, (nb*image_pool_pcnt)/100, 1); +} + +static void +serialconsole(void) +{ + char *p; + int port, baud; + + p = getconf("console"); + if(p == nil) + p = "0"; + if(p != nil && !remotedebug){ + port = strtol(p, nil, 0); + baud = 9600; + p = getconf("baud"); + if(p != nil){ + baud = strtol(p, nil, 0); + if(baud < 9600) + baud = 9600; + } + uartspecial(port, baud, &kbdq, &printq, kbdcr2nl); + } +} + +void +main(void) +{ + machinit(); + options(); + archinit(); + quotefmtinstall(); + confinit(); + cpminit(); + xinit(); + poolsizeinit(); + trapinit(); + mmuinit(); + printinit(); + uartinstall(); + serialconsole(); + doc("screeninit"); + screeninit(); + doc("kbdinit"); + kbdinit(); + doc("clockinit"); + clockinit(); + doc("procinit"); + procinit(); + cpuidprint(); + doc("links"); + links(); + doc("chandevreset"); + chandevreset(); + + eve = strdup("inferno"); + + print("\nInferno %s\n", VERSION); + print("Vita Nuova\n"); + print("conf %s (%lud) jit %d\n\n",conffile, kerndate, cflag); + + doc("userinit"); + userinit(); + doc("schedinit"); + schedinit(); +} + +void +machinit(void) +{ + int n; + + n = m->machno; + memset(m, 0, sizeof(Mach)); + m->machno = n; + m->mmask = 1<machno; + m->iomem = KADDR(getimmr() & ~0xFFFF); + m->cputype = getpvr()>>16; + m->delayloop = 20000; /* initial estimate only; set by clockinit */ + m->speed = 50; /* initial estimate only; set by archinit */ +} + +void +init0(void) +{ + Osenv *o; + int i; + char buf[2*KNAMELEN]; + + up->nerrlab = 0; + + spllo(); + + if(waserror()) + panic("init0"); + /* + * These are o.k. because rootinit is null. + * Then early kproc's will have a root and dot. + */ + o = up->env; + o->pgrp->slash = namec("#/", Atodir, 0, 0); + cnameclose(o->pgrp->slash->name); + o->pgrp->slash->name = newcname("/"); + o->pgrp->dot = cclone(o->pgrp->slash); + + chandevinit(); + + if(!waserror()){ + ksetenv("cputype", "power", 0); + snprint(buf, sizeof(buf), "power %s", conffile); + ksetenv("terminal", buf, 0); + poperror(); + } + for(i = 0; i < nconf; i++) + if(confname[i][0] != '*'){ + if(!waserror()){ + ksetenv(confname[i], confval[i], 0); + poperror(); + } + } + + poperror(); + disinit("/osinit.dis"); +} + +void +userinit(void) +{ + Proc *p; + Osenv *o; + + p = newproc(); + o = p->env; + + o->fgrp = newfgrp(nil); + o->pgrp = newpgrp(); + o->egrp = newegrp(); + kstrdup(&o->user, eve); + + strcpy(p->text, "interp"); + + /* + * Kernel Stack + */ + p->sched.pc = (ulong)init0; + p->sched.sp = (ulong)p->kstack+KSTACK; + + ready(p); +} + +Conf conf; + +void +addconf(char *name, char *val) +{ + if(nconf >= MAXCONF) + return; + confname[nconf] = name; + confval[nconf] = val; + nconf++; +} + +char* +getconf(char *name) +{ + int i; + + for(i = 0; i < nconf; i++) + if(cistrcmp(confname[i], name) == 0) + return confval[i]; + return 0; +} + +void +confinit(void) +{ + char *p; + int pcnt; + + if(p = getconf("*kernelpercent")) + pcnt = 100 - strtol(p, 0, 0); + else + pcnt = 0; + + conf.nscc = 4; + conf.smcuarts = (1<<1)|(1<<0); /* SMC2, SMC1 (usual console) */ + conf.sccuarts = 0; /* SCC2 not available by default (it's Ether) */ + + archconfinit(); + + conf.npage = conf.npage0 + conf.npage1; + if(pcnt < 10) + pcnt = 70; + conf.ialloc = (((conf.npage*(100-pcnt))/100)/2)*BY2PG; + + conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5; + conf.nmach = MAXMACH; +} + +void +exit(int ispanic) +{ + up = 0; + toggleled(3); + spllo(); + print("cpu %d exiting\n", m->machno); + + /* Shutdown running devices */ + chandevshutdown(); + + delay(1000); + splhi(); + if(ispanic) + for(;;); + archreboot(); +} + +void +reboot(void) +{ + exit(0); +} + +void +halt(void) +{ + print("cpu halted\n"); + microdelay(1000); + for(;;) + ; +} + +int +isaconfig(char *class, int ctlrno, ISAConf *isa) +{ + char cc[KNAMELEN], *p; + int i; + + snprint(cc, sizeof cc, "%s%d", class, ctlrno); + p = getconf(cc); + if(p == nil) + return 0; + + isa->nopt = tokenize(p, isa->opt, NISAOPT); + for(i = 0; i < isa->nopt; i++){ + p = isa->opt[i]; + if(cistrncmp(p, "type=", 5) == 0) + isa->type = p + 5; + else if(cistrncmp(p, "port=", 5) == 0) + isa->port = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "irq=", 4) == 0) + isa->irq = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "mem=", 4) == 0) + isa->mem = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "size=", 5) == 0) + isa->size = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "freq=", 5) == 0) + isa->freq = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "dma=", 4) == 0) + isa->dma = strtoul(p+4, &p, 0); + } + return 1; +} + +/* + * Save the mach dependent part of the process state. + */ +void +procsave(Proc*) +{ +} + +void +uartputs(char *s, int n) +{ +// screenputs(buf, n); + putstrn(s, n); + uartwait(); +} + +/* stubs */ +void +setfsr(ulong) +{ +} + +ulong +getfsr() +{ + return 0; +} + +void +setfcr(ulong) +{ +} + +ulong +getfcr() +{ + return 0; +} diff --git a/os/ipengine/mem.h b/os/ipengine/mem.h new file mode 100644 index 00000000..4934fc50 --- /dev/null +++ b/os/ipengine/mem.h @@ -0,0 +1,156 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ + +/* + * Sizes + */ + +#define BI2BY 8 /* bits per byte */ +#define BI2WD 32 /* bits per word */ +#define BY2WD 4 /* bytes per word */ +#define BY2V 8 /* bytes per double word */ +#define BY2PG 4096 /* bytes per page */ +#define WD2PG (BY2PG/BY2WD) /* words per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1)) +#define PGROUND(s) ROUND(s, BY2PG) +#define CACHELINELOG 4 +#define CACHELINESZ (1< */ +#define USER 29 /* R29 is up-> */ +#define IOMEMR 28 /* R28 will be iomem-> */ + +/* + * Fundamental addresses + */ + +#define UREGSIZE ((8+32)*4) + +/* + * MMU + */ + +/* L1 table entry and Mx_TWC flags */ +#define PTEVALID (1<<0) +#define PTEWT (1<<1) /* write through */ +#define PTE4K (0<<2) +#define PTE512K (1<<2) +#define PTE8MB (3<<2) +#define PTEG (1<<4) /* guarded */ + +/* L2 table entry and Mx_RPN flags (also PTEVALID) */ +#define PTECI (1<<1) /* cache inhibit */ +#define PTESH (1<<2) /* page is shared; ASID ignored */ +#define PTELPS (1<<3) /* large page size */ +#define PTEWRITE 0x9F0 + +/* TLB and MxEPN flag */ +#define TLBVALID (1<<9) + +/* + * Address spaces + */ + +#define KUSEG 0x00000000 +#define KSEG0 0x20000000 +#define KSEGM 0xE0000000 /* mask to check which seg */ + +#define KZERO KSEG0 /* base of kernel address space */ +#define KTZERO (KZERO+0x3000) /* first address in kernel text */ +#define KSTACK 8192 /* Size of kernel stack */ + +/* + * Exception codes (trap vectors) + */ +#define CRESET 0x01 +#define CMCHECK 0x02 +#define CDSI 0x03 +#define CISI 0x04 +#define CEI 0x05 +#define CALIGN 0x06 +#define CPROG 0x07 +#define CFPU 0x08 +#define CDEC 0x09 +#define CSYSCALL 0x0C +#define CTRACE 0x0D +#define CFPA 0x0E +/* rest are power-implementation dependent (8xx) */ +#define CEMU 0x10 +#define CIMISS 0x11 +#define CDMISS 0x12 +#define CITLBE 0x13 +#define CDTLBE 0x14 +#define CDBREAK 0x1C +#define CIBREAK 0x1D +#define CPBREAK 0x1E +#define CDPORT 0x1F + +/* + * MPC8xx physical addresses (ipEngine) + */ +#define PHYSDRAM 0x00000000 +#define PHYSIMM 0xFF000000 +#define PHYSFLASH 0xFE000000 +#define FPGAMEM 0xFC000000 + +/* extra addresses on ipEngine-1 */ +#define FPGACR 0xFF010000 /* FPGA control */ +#define CLOCKCR 0xFF020000 /* Clock synthesiser register */ + +/* remaining ones are our choice */ +#define PHYSPCMCIA 0x40000000 +#define PCMCIALEN (8*MB) /* chosen to allow mapping by single TLB entry */ +#define ISAIO PHYSPCMCIA /* for inb.s */ + +/* + * MPC8xx dual-ported CPM memory physical addresses + */ +#define PHYSDPRAM (PHYSIMM+0x2000) +#define DPLEN1 0x200 +#define DPLEN2 0x400 +#define DPLEN3 0x800 +#define DPBASE (PHYSDPRAM+DPLEN1) + +#define KEEP_ALIVE_KEY 0x55ccaa33 /* clock and rtc register key */ diff --git a/os/ipengine/mkfile b/os/ipengine/mkfile new file mode 100644 index 00000000..ca6d6373 --- /dev/null +++ b/os/ipengine/mkfile @@ -0,0 +1,105 @@ +SYSTARG=Inferno +OBJTYPE=power +<../../mkconfig + +#Configurable parameters + +CONF=ipe #default configuration +CONFLIST=ipe +KZERO=0x20003020 + +SYSTARG=$OSTARG +OBJTYPE=power +INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin #path of directory where kernel is installed +#INSTALLDIR=/$OBJTYPE + +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS + +OBJ=\ + l.$O\ + tlb.$O\ + nofp.$O\ + clock.$O\ + cpm.$O\ + faultpower.$O\ + fpi.$O\ + fpimem.$O\ + fpipower.$O\ + kbd.$O\ + main.$O\ + mmu.$O\ + rmap.$O\ + trap.$O\ + $CONF.root.$O\ + $IP\ + $DEVS\ + $ETHERS\ + $LINKS\ + $VGAS\ + $PORT\ + $MISC\ + $OTHERS\ + +LIBNAMES=${LIBS:%=lib%.a} + +HFILES=\ + mem.h\ + dat.h\ + fns.h\ + io.h\ + ../mpc/800io.h\ + +CFLAGS=-wFV -I. -I../mpc -I../port -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp +KERNDATE=`{$NDATE} + +#default:V: i$CONF.sq +default:V: i$CONF + +i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD -o $target -T$KZERO -l -R4 $OBJ $CONF.$O $LIBFILES + $KSIZE $target + +i$CONF.sq: i$CONF + sqz -w i$CONF >$target + +install:V: i$CONF # i$CONF.sq + cp i$CONF $INSTALLDIR/i$CONF + #cp i$CONF.sq $INSTALLDIR/i$CONF.sq + +uninstall:V: + rm -f $ROOT/$OBJDIR/bin/i$CONF + rm -f $ROOT/$OBJDIR/bin/i$CONF.sq + +<../port/portmkfile + +%.$O: ../mpc/%.c + $CC $CFLAGS -I. ../mpc/$stem.c + +%.$O: ../mpc/%.s + $AS -I. -I../mpc ../mpc/$stem.s + +../init/$INIT.dis: ../init/$INIT.b + cd ../init; mk $INIT.dis + +clock.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +devether.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +faultpower.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +main.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +trap.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h + +devether.$O $ETHERS: ../mpc/etherif.h ../port/netif.h +archipe.$O: ../mpc/screen.h archipe.h +screen.$O: ../mpc/screen.h + +$IP devip.$O: ../ip/ip.h + +devboot.$O: devboot.c + $CC $CFLAGS devboot.c + +devuart.$O: ../mpc/devuart.c + $CC $CFLAGS ../mpc/devuart.c diff --git a/os/ipengine/mmu.c b/os/ipengine/mmu.c new file mode 100644 index 00000000..c9cd758b --- /dev/null +++ b/os/ipengine/mmu.c @@ -0,0 +1,20 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +void +mmuinit(void) +{ + /* the l.s initial TLB settings do all that's required */ +} + +int +segflush(void *a, ulong n) +{ + /* flush dcache then invalidate icache */ + dcflush(a, n); + icflush(a, n); + return 0; +} diff --git a/os/ipengine/tlb.s b/os/ipengine/tlb.s new file mode 100644 index 00000000..c7451823 --- /dev/null +++ b/os/ipengine/tlb.s @@ -0,0 +1,21 @@ +#include "mem.h" +#define MB (1024*1024) + +/* + * TLB prototype entries, loaded once-for-all at startup, + * remaining unchanged thereafter. + * Limit the table to at most 8 entries to ensure + * it works on the 823 (other 8xx processors allow up to 32 TLB entries). + */ +#define TLBE(epn,rpn,twc) WORD $(epn); WORD $(twc); WORD $(rpn) + +TEXT tlbtab(SB), $-4 + + /* epn, rpn, twc */ + TLBE(KZERO|PHYSDRAM|TLBVALID, PHYSDRAM|PTEWRITE|PTELPS|PTESH|PTEVALID, PTE8MB|PTEVALID) /* DRAM, 8M */ + TLBE(KZERO|(PHYSDRAM+8*MB)|TLBVALID, (PHYSDRAM+8*MB)|PTEWRITE|PTELPS|PTESH|PTEVALID, PTE8MB|PTEVALID) /* DRAM, 8M */ + TLBE(KZERO|PHYSIMM|TLBVALID, PHYSIMM|PTEWRITE|PTELPS|PTESH|PTECI|PTEVALID, PTE512K|PTEVALID) /* IMMR, 512K (includes FPGA control and clock synth) */ + TLBE(KZERO|PHYSFLASH|TLBVALID, PHYSFLASH|PTEWRITE|PTELPS|PTESH|PTECI|PTEVALID, PTE8MB|PTEWT|PTEVALID) /* Flash, 8M */ + TLBE(KZERO|FPGAMEM|TLBVALID, FPGAMEM|PTEWRITE|PTELPS|PTESH|PTECI|PTEVALID, PTE8MB|PTEG|PTEVALID) /* FPGA mem, 8M */ +TEXT tlbtabe(SB), $-4 + RETURN diff --git a/os/js/README b/os/js/README new file mode 100644 index 00000000..1bef576f --- /dev/null +++ b/os/js/README @@ -0,0 +1,10 @@ +This port was done in a matter of days by Tad Hunt, Eric van Hensbergen +and others when they were in the Lucent Inferno Business Unit, with a little +bit of advice from Vita Nuova about various Sparc details (since one +of us had done ports to micro-Sparcs). It is included mainly to +allow another micro-Sparc port to be developed. Javastation-1s +can still be found (but we haven't got one). A good cheap reference +board would be good to have. + +The Javastation running Java and JavaOS was sluggish; running Limbo +under Inferno it was actually quite snappy! diff --git a/os/js/audio.h b/os/js/audio.h new file mode 100644 index 00000000..1d88aa1b --- /dev/null +++ b/os/js/audio.h @@ -0,0 +1,9 @@ +enum +{ + Bufsize = 16*1024, /* 92 ms each */ + Nbuf = 16, /* 1.5 seconds total */ + Rdma = 666, /* XXX - Tad: fixme */ + Wdma = 666, /* XXX - Tad: fixme */ +}; + +#define UNCACHED(type, v) (type*)((ulong)(v)) diff --git a/os/js/clock.c b/os/js/clock.c new file mode 100644 index 00000000..581ee441 --- /dev/null +++ b/os/js/clock.c @@ -0,0 +1,104 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "ureg.h" + +typedef struct Clock0link Clock0link; +typedef struct Clock0link { + void (*clock)(void); + Clock0link* link; +} Clock0link; + +static Clock0link *clock0link; +static Lock clock0lock; + +void +microdelay(int ms) +{ + int i; + + ms *= 13334; /* experimentally indetermined */ + for(i=0; ilim = (CLOCKFREQ/HZ)<<10; +} + +void +clock(Ureg *ur) +{ + Clock0link *lp; + ulong i; + + USED(ur); + + i = ctr->lim; /* clear interrupt */ + USED(i); + /* is this needed? page 6-43 801-3137-10 suggests so */ + ctr->lim = (CLOCKFREQ/HZ)<<10; + + m->ticks++; + + if(up) + up->pc = ur->pc; + + checkalarms(); + + lock(&clock0lock); + for(lp = clock0link; lp; lp = lp->link) + lp->clock(); + unlock(&clock0lock); + + if(up && up->state == Running) { + if(anyready()) + sched(); + } +} + +Timer* +addclock0link(void (*clockfunc)(void), int) +{ + Clock0link *lp; + + if((lp = malloc(sizeof(Clock0link))) == 0){ + print("addclock0link: too many links\n"); + return nil; + } + ilock(&clock0lock); + lp->clock = clockfunc; + lp->link = clock0link; + clock0link = lp; + iunlock(&clock0lock); + return nil; +} + +uvlong +fastticks(uvlong *hz) +{ + if(hz) + *hz = HZ; + return m->ticks; +} diff --git a/os/js/cs4231.h b/os/js/cs4231.h new file mode 100644 index 00000000..daeda1b8 --- /dev/null +++ b/os/js/cs4231.h @@ -0,0 +1,20 @@ +#define IN(x) inb(csdev.port+(x)) +#define OUT(x,v) outb(csdev.port+(x),(v)) + +void +cs4231install(void) +{ + KMap *k; + static int installed=0; + + if(installed) + return; + + k = kmappa(AUDIO_PHYS_PAGE, PTEIO|PTENOCACHE); + + csdev.port = VA(k)+AUDIO_INDEX_OFFSET; + dmasize(Wdma, 8); + dmasize(Rdma, 8); + + installed=1; +} diff --git a/os/js/dat.h b/os/js/dat.h new file mode 100644 index 00000000..e1691f4e --- /dev/null +++ b/os/js/dat.h @@ -0,0 +1,163 @@ +typedef struct Conf Conf; +typedef struct FPenv FPenv; +typedef struct FPU FPU; +typedef struct Label Label; +typedef struct Lock Lock; +typedef struct Mach Mach; +typedef struct Ureg Ureg; +typedef struct Lance Lance; +typedef struct Lancemem Lancemem; +typedef struct Etherpkt Etherpkt; +typedef struct Lancepkt Lancepkt; + +typedef ulong Instr; + +struct Conf +{ + int nmach; /* processors */ + int nproc; /* processes */ + ulong monitor; /* graphics monitor id; 0 for none */ + char ss2; /* is a sparcstation 2 */ + char ss2cachebug; /* has sparcstation2 cache bug */ + int ncontext; /* in mmu */ + int vacsize; /* size of virtual address cache, in bytes */ + int vaclinesize; /* size of cache line */ + ulong npage0; /* total physical pages of memory, bank 0 */ + ulong npage1; /* total physical pages of memory, bank 1 */ + ulong base0; /* base of bank 0 */ + ulong base1; /* base of bank 1 */ + ulong ialloc; /* max interrupt time allocation in bytes */ + ulong npage; /* total physical pages of memory */ + int copymode; /* 0 is copy on write, 1 is copy on reference */ + ulong ipif; /* Ip protocol interfaces */ + ulong ip; /* Ip conversations per interface */ + ulong arp; /* Arp table size */ + ulong frag; /* Ip fragment assemble queue size */ +}; + + +/* + * FPenv.status + */ +enum +{ + FPINIT, + FPACTIVE, + FPINACTIVE, +}; + +struct FPenv +{ + ulong status; + ulong pad; +}; + +/* + * This structure must agree with fpsave and fprestore asm routines + */ +struct FPU +{ + + double regs[17]; /* floating point registers */ + FPenv env; +}; + +/* + * machine dependent definitions used by ../port/dat.h + */ + +struct Label +{ + ulong sp; + ulong pc; +}; + +struct Lock +{ + ulong key; + ulong pc; + ulong sr; + int pri; +}; + +#include "../port/portdat.h" + +/* + * machine dependent definitions not used by ../port/dat.h + */ + +struct Mach +{ + ulong ticks; /* of the clock since boot time */ + int machno; /* physical id of this processor */ + Proc *proc; /* current process on this processor */ + Label sched; /* scheduler wakeup */ + Lock alarmlock; /* access to alarm list */ + void *alarm; /* alarms bound to this clock */ + ulong *contexts; /* hardware context table */ + ulong *ctx; /* the context */ + int fptrap; /* FP trap occurred while unsave */ + + int nrdy; + + int stack[1]; +}; + +/* + * XXX - Eric: It just works.... + */ + +/* + * LANCE CSR3 (bus control bits) + */ +#define BSWP 0x4 +#define ACON 0x2 +#define BCON 0x1 + +struct Lancepkt +{ + uchar d[6]; + uchar s[6]; + uchar type[2]; + uchar data[1500]; + uchar crc[4]; +}; + +/* + * system dependent lance stuff + * filled by lancesetup() + */ +struct Lance +{ + ushort lognrrb; /* log2 number of receive ring buffers */ + ushort logntrb; /* log2 number of xmit ring buffers */ + ushort nrrb; /* number of receive ring buffers */ + ushort ntrb; /* number of xmit ring buffers */ + ushort *rap; /* lance address register */ + ushort *rdp; /* lance data register */ + ushort busctl; /* bus control bits */ + uchar ea[6]; /* our ether addr */ + int sep; /* separation between shorts in lance ram + as seen by host */ + ushort *lanceram; /* start of lance ram as seen by host */ + Lancemem *lm; /* start of lance ram as seen by lance */ + Lancepkt *rp; /* receive buffers (host address) */ + Lancepkt *tp; /* transmit buffers (host address) */ + Lancepkt *lrp; /* receive buffers (lance address) */ + Lancepkt *ltp; /* transmit buffers (lance address) */ +}; + +/* + * Fake kmap + */ +typedef void KMap; +#define VA(k) ((ulong)(k)) +#define kmap(p) (KMap*)((p)->pa|KZERO) +#define kunmap(k) +#define MACHP(n) (n==0? &mach0 : *(Mach**)0) + +extern Mach *m; +extern Proc *up; +extern Mach mach0; + +#define swcursor 1 diff --git a/os/js/devcs4231.c b/os/js/devcs4231.c new file mode 100644 index 00000000..2e02c462 --- /dev/null +++ b/os/js/devcs4231.c @@ -0,0 +1,1186 @@ +/* + * preliminary Crystal CS4231 audio driver, + * initially based on SB16 driver, and therefore needs work. + * for instance, i suspect the linked-list buffering is excessive: + * a rolling buffering scheme or double buffering should be fine, + * and possibly simpler. + * + * To do: + * stop/start? + * is the linux mix_cvt ideal? + * ad1845 differences + * adpcm freezing + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "devtab.h" +#include "io.h" +#include "audio.h" + +#define DPRINT if(chatty)print + +typedef struct AChan AChan; +typedef struct AQueue AQueue; +typedef struct Buf Buf; +typedef struct Vol Vol; + +enum +{ + Qdir = 0, + Qaudio, + Qaudioctl, + + Fmono = 1, + Fin = 2, + Fout = 4, + + Vaudio = 0, + Vaux1, + Vaux2, + Vline, + Vmic, + Vmono, + Vspeed, + Vchans, + Vbits, + Nvol, + + Speed = 22050, + Ncmd = 50, /* max volume command words */ +}; + +enum { + Paddr= 0, + TRD= 1<<5, + MCE= 1<<6, + Pdata= 1, + Pstatus= 2, + Pio= 3, + + LeftADC= 0, + MGE= 1<<5, + ISline= 0<<6, + ISaux1= 1<<6, + ISmic= 2<<6, + ISloop= 3<<6, + ISmask= 3<<6, + RightADC= 1, + LeftAux1= 2, + Mute= 1<<7, + RightAux1= 3, + LeftAux2= 4, + RightAux2= 5, + LeftDAC= 6, + RightDAC= 7, + OutFormat= 8, + Stereo= 1<<4, + Linear8= 0<<5, + uLaw= 1<<5, + Linear16= 2<<5, + aLaw= 3<<5, + ADPCM= 5<<5, + Fmask= 7<<5, + Config= 9, + PEN= 1<<0, + CEN= 1<<1, + Nocal= 0<<3, + Convcal= 1<<3, + DACcal= 2<<3, + Fullcal= 3<<3, + PinControl= 10, + IEN= 1<<1, + DEN= 1<<3, + Xctl0= 1<<6, + Xctl1= 1<<7, + Status= 11, + ACI= 1<<5, + Mode= 12, + Mode2= 1<<6, + Loopback= 13, + LBE= 1<<0, + PlayCount1= 14, + PlayCount0= 15, + Feature1= 16, + PMCE= 1<<4, + CMCE= 1<<5, + Feature2= 17, + LeftLine= 18, + RightLine= 19, + Timer0= 20, + Timer1= 21, + Feature3= 23, + FeatureStatus= 24, + PI= 1<<4, /* playback interrupt */ + CI= 1<<5, /* capture interrupt */ + TI= 1<<6, /* timer interrupt */ + ChipID= 25, + MonoCtl= 26, + MBY= 1<<5, /* mono bypass */ + MOM= 1<<6, + InFormat= 28, + RecCount1= 30, + RecCount0= 31, +}; + +#define csdelay() microdelay(1) + +static Dirtab audiodir[] = +{ + "audio", {Qaudio}, 0, 0666, + "audioctl", {Qaudioctl}, 0, 0666, +}; +#define NPORT (sizeof audiodir/sizeof(Dirtab)) + +struct Buf +{ + uchar* virt; + int count; + Buf* next; +}; +struct AQueue +{ + Lock; + Buf* first; + Buf* last; +}; +struct AChan +{ + QLock; + Rendez r; + Buf buf[Nbuf]; /* buffers and queues */ + AQueue empty; + AQueue full; + Buf* current; + Buf* filling; + int flushing; +}; +static struct +{ + QLock; + int opened; + int bufinit; /* boolean if buffers allocated */ + int rivol[Nvol]; /* right/left input/output volumes */ + int livol[Nvol]; + int rovol[Nvol]; + int lovol[Nvol]; + int loopback; + + AChan in; + AChan out; +} audio; + +static char* encname(int); + +static int dacload(int, int); +static int auxload(int, int); +static int adcload(int, int); +static int monoload(int, int); + +struct Vol +{ + char* name; + int flag; + int ilval; /* initial values */ + int irval; + int reg; + int (*load)(int, int); +}; + +static Vol volumes[] = { +[Vaudio] {"audio", Fout, 50, 50, LeftDAC, dacload}, +[Vaux1] {"aux1", Fin, 0, 0, LeftAux1, auxload}, +[Vaux2] {"aux2", Fin, 0, 0, LeftAux2, auxload}, +[Vline] {"line", Fin, 0, 0, LeftLine, auxload}, +[Vmono] {"mono", Fin|Fout|Fmono, 0, 0, MonoCtl, monoload}, +[Vmic] {"mic", Fin, 0, 0, LeftADC, adcload}, + +[Vspeed] {"rate", Fin|Fout|Fmono, Speed, Speed,}, +[Vchans] {"chans", Fin|Fout|Fmono, 2, 2,}, +[Vbits] {"bits", Fin|Fout|Fmono, 8, 8,}, + {0}, +}; + +static struct +{ + Lock; + int port; + int irq; + uchar sticky; + uchar regs[32]; +} csdev; + +static void contininput(void); +static void continoutput(void); + +static char Evolume[] = "illegal audioctl specifier"; + +static int chatty; + +#include "cs4231.h" + +static int +xin(int r) +{ + int i; + + for(i=100; --i >= 0 && IN(Paddr) & 0x80;) + csdelay(); + OUT(Paddr, r|csdev.sticky); + csdelay(); + return IN(Pdata); +} + +static void +xout(int r, int v) +{ + int i; + + for(i=100; --i >= 0 && IN(Paddr) & 0x80;) + csdelay(); + OUT(Paddr, r|csdev.sticky); + csdelay(); + OUT(Pdata, v); + //csdelay(); +} + +static void +speaker(int on) +{ + int s; + + s = xin(PinControl); + if(on) + s |= Xctl0; + else + s &= ~Xctl0; + xout(PinControl, s); +} + +static Buf* +getbuf(AQueue *q) +{ + Buf *b; + + ilock(q); + b = q->first; + if(b) + q->first = b->next; + iunlock(q); + + return b; +} + +static void +putbuf(AQueue *q, Buf *b) +{ + ilock(q); + b->next = 0; + if(q->first) + q->last->next = b; + else + q->first = b; + q->last = b; + iunlock(q); +} + +static void +achanreset(AChan *ac) +{ + int i; + + ac->filling = 0; + ac->flushing = 0; + ac->current = 0; + ac->empty.first = 0; + ac->empty.last = 0; + ac->full.first = 0; + ac->full.last = 0; + for(i=0; ibuf[i].count = 0; + putbuf(&ac->empty, &ac->buf[i]); + } +} + +static void +startoutput(void) +{ + ilock(&csdev); + if(audio.out.current == 0) + continoutput(); + iunlock(&csdev); +} + +static void +continoutput(void) +{ + Buf *b; + int f; + ulong n; + + b = getbuf(&audio.out.full); + audio.out.current = b; + //xout(Config, xin(Config)&~PEN); + if(b){ + n = b->count; + dmasetup(Wdma, b->virt, n, 0); + f = xin(OutFormat); + if((f & Fmask) == ADPCM) + n >>= 2; + else{ + if((f & Fmask) == Linear16) + n >>= 1; + if(f & Stereo) + n >>= 1; + } + n--; + xout(PlayCount0, n); + xout(PlayCount1, n>>8); + xout(Config, xin(Config)|PEN); + DPRINT("cs: out %d\n", n); + } else + xout(Config, xin(Config)&~PEN); +} + +static void +startinput(void) +{ + ilock(&csdev); + if(audio.in.current == 0) + contininput(); + iunlock(&csdev); +} + +static void +contininput(void) +{ + Buf *b; + int f; + ulong n; + + xout(Config, xin(Config)&~CEN); + if(!audio.opened || audio.in.flushing){ + return; + } + b = getbuf(&audio.in.empty); + audio.in.current = b; + if(b){ + n = Bufsize; + dmasetup(Rdma, b->virt, Bufsize, 1); + f = xin(InFormat); + if((f & Fmask) == ADPCM) + n >>= 2; + else{ + if((f & Fmask) == Linear16) + n >>= 1; + if(f & Stereo) + n >>= 1; + } + n--; + xout(RecCount0, n); + xout(RecCount1, n>>8); + xout(Config, xin(Config)|CEN); + DPRINT("cs: in %d\n", n); + } +} + +static void +cswait(void) +{ + int i; + + for(i=50; --i >= 0 && IN(Paddr) & 0x80;) + microdelay(2000); + if(i < 0) + print("cswait1\n"); + for(i=1000; --i >= 0 && (xin(Status) & ACI) == 0;) + csdelay(); + for(i=1000; --i >= 0 && xin(Status) & ACI;) + microdelay(2000); + /* could give error(Eio) if i < 0 */ + if(i < 0) + print("cswait2\n"); +} + +static int +csspeed(int freq) +{ + int i; + static int freqtab[] = { /* p. 33 CFS2-CFS0 */ + /* xtal1 xtal2 */ + 8000, 5510, + 16000, 11025, + 27420, 18900, + 32000, 22050, + 0, 37800, + 0, 44100, + 48000, 33075, + 9600, 6620, + }; + for(i=0; i<16; i++) + if(freqtab[i] == freq){ + xout(OutFormat, (xin(OutFormat)&~0xF) | i); + return 1; + } + return 0; +} + +static void +csformat(int r, int flag, int form, int *vec) +{ + int v; + + if(form == Linear8){ + if(vec[Vbits] == 16) + form = Linear16; + else if(vec[Vbits] == 4) + form = ADPCM; + } + if(vec[Vchans] == 2) + form |= Stereo; + DPRINT("csformat(%x,%x,%x)\n", r, flag, form); + if((xin(r)&0xF0) != form){ + v = xin(Feature1); + xout(Feature1, v|flag); + xout(r, (xin(r)&~0xF0)|form); + xout(Feature1, v); + } + csdev.regs[r] = form; +} + +static void +cs4231intr(Ureg*, void*) +{ + int ir, s; + Buf *b; + + lock(&csdev); + csdev.sticky |= TRD; + ir = IN(Pstatus); + s = xin(FeatureStatus); + if(s & PI){ + b = audio.out.current; + audio.out.current = 0; + dmaend(Wdma); + continoutput(); + if(b) + putbuf(&audio.out.empty, b); + wakeup(&audio.out.r); + } + if(s & CI){ + b = audio.in.current; + audio.in.current = 0; + dmaend(Rdma); + contininput(); + if(b){ + b->count = Bufsize; + putbuf(&audio.in.full, b); + } + wakeup(&audio.in.r); + } + OUT(Pstatus, 0); + csdev.sticky &= ~TRD; + unlock(&csdev); + if(s & 0xF) + DPRINT("audiointr: #%x\n", s); +} + +static int +anybuf(void *p) +{ + return ((AChan*)p)->empty.first != 0; +} + +static int +anyinput(void *p) +{ + return ((AChan*)p)->full.first != 0; +} + +static int +outcomplete(void *p) +{ + return ((AChan*)p)->full.first == 0 && ((AChan*)p)->current==0; +} + +static int +incomplete(void *p) +{ + return ((AChan*)p)->current == 0; +} + +static void +acbufinit(AChan *ac) +{ + int i; + void *p; + + for(i=0; ibuf[i].virt = UNCACHED(uchar, p); + } +} + +static void +setempty(void) +{ + ilock(&csdev); + achanreset(&audio.in); + achanreset(&audio.out); + iunlock(&csdev); +} + +void +cs4231reset(void) +{ +} + +static char mix_cvt[101] = { + 0, 0,3,7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42, + 43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65, + 65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79, + 80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90, + 91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99, + 100 +}; + +static int +dacload(int r, int v) +{ + USED(r); + DPRINT("dacload(%x,%d)\n", r, v); + if(v == 0) + return Mute; + return 63-((v*63)/100); +} + +static int +monoload(int r, int v) +{ + DPRINT("monoload(%x,%d)\n", r, v); + if(v == 0) + return r|Mute; + return (r&~(Mute|MBY))|(15-((v*15)/100)); +} + +static int +auxload(int r, int v) +{ + DPRINT("auxload(%x,%d)\n", r, v); + USED(r); + if(v == 0) + return Mute; + return 31-(v*31)/100; +} + +static int +adcload(int r, int v) +{ + DPRINT("adcload(%x,%d)\n", r, v); + return (r&~0xF)|((v*15)/100)|MGE; +} + +static void +mxvolume(void) +{ + Vol *v; + int i, l, r; + + ilock(&csdev); + speaker(0); + for(i =0; volumes[i].name; i++){ + v = &volumes[i]; + if(v->load == 0) + continue; + if(v->flag & Fin){ + l = audio.livol[i]; + r = audio.rivol[i]; + } else { + l = audio.lovol[i]; + r = audio.rovol[i]; + } + if(l < 0) + l = 0; + if(r < 0) + r = 0; + if(l > 100) + l = 100; + if(r > 100) + r = 100; + l = mix_cvt[l]; + r = mix_cvt[r]; + if((v->flag & Fmono) == 0){ + xout(v->reg, (*v->load)(xin(v->reg), l)); + xout(v->reg+1, (*v->load)(xin(v->reg+1), r)); + } else + xout(v->reg, (*v->load)(xin(v->reg), l)); + } + xout(LeftADC, (xin(LeftADC)&~ISmask)|csdev.regs[LeftADC]); + xout(RightADC, (xin(RightADC)&~ISmask)|csdev.regs[RightADC]); + if(audio.loopback) + xout(Loopback, xin(Loopback)|LBE); + else + xout(Loopback, xin(Loopback)&~LBE); + csformat(InFormat, CMCE, csdev.regs[InFormat], audio.livol); + csformat(OutFormat, PMCE, csdev.regs[OutFormat], audio.lovol); + if(audio.lovol[Vaudio] || audio.rovol[Vaudio]) + speaker(1); + iunlock(&csdev); +} + +static void +flushinput(void) +{ + Buf *b; + + ilock(&csdev); + audio.in.flushing = 1; + iunlock(&csdev); + qlock(&audio.in); + if(waserror()){ + qunlock(&audio.in); + nexterror(); + } + sleep(&audio.in.r, incomplete, &audio.in); + qunlock(&audio.in); + poperror(); + ilock(&csdev); + audio.in.flushing = 0; + iunlock(&csdev); + if((b = audio.in.filling) != 0){ + audio.in.filling = 0; + putbuf(&audio.in.empty, b); + } + while((b = getbuf(&audio.in.full)) != 0) + putbuf(&audio.in.empty, b); +} + +static void +waitoutput(void) +{ + qlock(&audio.out); + if(waserror()){ + qunlock(&audio.out); + nexterror(); + } + startoutput(); + while(!outcomplete(&audio.out)) + sleep(&audio.out.r, outcomplete, &audio.out); + qunlock(&audio.out); + poperror(); +} + +static void +resetlevel(void) +{ + int i; + + for(i=0; volumes[i].name; i++) { + audio.lovol[i] = volumes[i].ilval; + audio.rovol[i] = volumes[i].irval; + audio.livol[i] = volumes[i].ilval; + audio.rivol[i] = volumes[i].irval; + } +} + +void +cs4231init(void) +{ + cs4231install(); + + csdev.regs[LeftADC] = ISmic; + csdev.regs[RightADC] = ISmic; + dmasize(Wdma, 8); + dmasize(Rdma, 8); + csdev.sticky = 0; + OUT(Paddr, Mode); + csdelay(); + if((IN(Pdata) & 0x8F) != 0x8a){ + DPRINT("port %x not cs4231a: %x\n", IN(Pdata)); + return; + } + print("audio0: cs4231a: port %x irq %d wdma %d rdma %d\n", csdev.port, csdev.irq, Wdma, Rdma); + + resetlevel(); + + cswait(); + OUT(Paddr, Mode); + csdelay(); + OUT(Pdata, Mode2|IN(Pdata)); /* mode2 for all the trimmings */ + csdelay(); + cswait(); + + csdev.sticky = MCE; + xout(Config, Fullcal); + csspeed(volumes[Vspeed].ilval); + csformat(InFormat, CMCE, Linear8, audio.livol); + csformat(OutFormat, PMCE, Linear8, audio.lovol); + csdev.sticky &= ~MCE; + OUT(Paddr, csdev.sticky); + microdelay(10000); + cswait(); /* recalibration takes ages */ + + xout(FeatureStatus, 0); + OUT(Pstatus, 0); + setvec(csdev.irq, cs4231intr, 0); + xout(PinControl, xin(PinControl)|IEN); +} + +Chan* +cs4231attach(char *param) +{ + return devattach('A', param); +} + +Chan* +cs4231clone(Chan *c, Chan *nc) +{ + return devclone(c, nc); +} + +int +cs4231walk(Chan *c, char *name) +{ + return devwalk(c, name, audiodir, NPORT, devgen); +} + +void +cs4231stat(Chan *c, char *db) +{ + devstat(c, db, audiodir, NPORT, devgen); +} + +Chan* +cs4231open(Chan *c, int omode) +{ + switch(c->qid.path & ~CHDIR) { + default: + error(Eperm); + break; + + case Qaudioctl: + case Qdir: + break; + + case Qaudio: + qlock(&audio); + if(audio.opened){ + qunlock(&audio); + error(Einuse); + } + if(audio.bufinit == 0) { + audio.bufinit = 1; + acbufinit(&audio.in); + acbufinit(&audio.out); + } + audio.opened = 1; + setempty(); + qunlock(&audio); + mxvolume(); + break; + } + c = devopen(c, omode, audiodir, NPORT, devgen); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + + return c; +} + +void +cs4231create(Chan *c, char *name, int omode, ulong perm) +{ + USED(c, name, omode, perm); + error(Eperm); +} + +void +cs4231close(Chan *c) +{ + Buf *b; + + switch(c->qid.path & ~CHDIR) { + default: + error(Eperm); + break; + + case Qdir: + case Qaudioctl: + break; + + case Qaudio: + if(c->flag & COPEN) { + qlock(&audio); + audio.opened = 0; + if(waserror()){ + qunlock(&audio); + nexterror(); + } + b = audio.out.filling; + if(b){ + audio.out.filling = 0; + putbuf(&audio.out.full, b); + } + waitoutput(); + flushinput(); + //tsleep(&up->sleep, return0, 0, 500); + //speaker(0); + qunlock(&audio); + poperror(); + } + break; + } +} + +long +cs4231read(Chan *c, char *a, long n, vlong offset) +{ + int liv, riv, lov, rov, ifmt, ofmt; + long m, n0; + char buf[350]; + Buf *b; + int j; + + n0 = n; + switch(c->qid.path & ~CHDIR) { + default: + error(Eperm); + break; + + case Qdir: + return devdirread(c, a, n, audiodir, NPORT, devgen); + + case Qaudio: + qlock(&audio.in); + if(waserror()){ + qunlock(&audio.in); + nexterror(); + } + while(n > 0) { + b = audio.in.filling; + if(b == 0) { + b = getbuf(&audio.in.full); + if(b == 0) { + startinput(); + sleep(&audio.in.r, anyinput, &audio.in); + continue; + } + audio.in.filling = b; + b->count = 0; + } + m = Bufsize-b->count; + if(m > n) + m = n; + memmove(a, b->virt+b->count, m); + + b->count += m; + n -= m; + a += m; + if(b->count >= Bufsize) { + audio.in.filling = 0; + putbuf(&audio.in.empty, b); + } + } + qunlock(&audio.in); + poperror(); + break; + + case Qaudioctl: + j = 0; + buf[0] = 0; + for(m=0; volumes[m].name; m++){ + liv = audio.livol[m]; + riv = audio.rivol[m]; + lov = audio.lovol[m]; + rov = audio.rovol[m]; + j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name); + if((volumes[m].flag & Fmono) || liv==riv && lov==rov){ + if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov) + j += snprint(buf+j, sizeof(buf)-j, " %d", liv); + else{ + if(volumes[m].flag & Fin) + j += snprint(buf+j, sizeof(buf)-j, " in %d", liv); + if(volumes[m].flag & Fout) + j += snprint(buf+j, sizeof(buf)-j, " out %d", lov); + } + }else{ + if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov && riv==rov) + j += snprint(buf+j, sizeof(buf)-j, " left %d right %d", + liv, riv); + else{ + if(volumes[m].flag & Fin) + j += snprint(buf+j, sizeof(buf)-j, " in left %d right %d", + liv, riv); + if(volumes[m].flag & Fout) + j += snprint(buf+j, sizeof(buf)-j, " out left %d right %d", + lov, rov); + } + } + j += snprint(buf+j, sizeof(buf)-j, "\n"); + } + ifmt = xin(InFormat); + ofmt = xin(OutFormat); + if(ifmt != ofmt){ + j += snprint(buf+j, sizeof(buf)-j, "in enc %s\n", encname(ifmt)); + j += snprint(buf+j, sizeof(buf)-j, "out enc %s\n", encname(ofmt)); + } else + j += snprint(buf+j, sizeof(buf)-j, "enc %s\n", encname(ifmt)); + j += snprint(buf+j, sizeof(buf)-j, "loop %d\n", audio.loopback); + {int i; for(i=0; i<32; i++){j += snprint(buf+j, sizeof(buf)-j, " %d:%x", i, xin(i)); }j += snprint(buf+j,sizeof(buf)-j,"\n");} + USED(j); + + return readstr(offset, a, n, buf); + } + return n0-n; +} + +Block* +cs4231bread(Chan *c, long n, ulong offset) +{ + return devbread(c, n, offset); +} + +long +cs4231write(Chan *c, char *a, long n, vlong offset) +{ + long m, n0; + int i, nf, v, left, right, in, out, fmt, doload; + char buf[255], *field[Ncmd]; + Buf *b; + + USED(offset); + + n0 = n; + switch(c->qid.path & ~CHDIR) { + default: + error(Eperm); + break; + + case Qaudioctl: + waitoutput(); + flushinput(); + qlock(&audio); + if(waserror()){ + qunlock(&audio); + nexterror(); + } + v = Vaudio; + doload = 0; + left = 1; + right = 1; + in = 1; + out = 1; + if(n > sizeof(buf)-1) + n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = '\0'; + + nf = getfields(buf, field, Ncmd, 1, " \t\n,"); + for(i = 0; i < nf; i++){ + /* + * a number is volume + */ + if(field[i][0] >= '0' && field[i][0] <= '9') { + m = strtoul(field[i], 0, 10); + if(left && out) + audio.lovol[v] = m; + if(left && in) + audio.livol[v] = m; + if(right && out) + audio.rovol[v] = m; + if(right && in) + audio.rivol[v] = m; + if(v == Vspeed){ + ilock(&csdev); + csdev.sticky = MCE; + csspeed(m); + csdev.sticky &= ~MCE; + OUT(Paddr, csdev.sticky); + microdelay(10000); + cswait(); + iunlock(&csdev); + } else + doload = 1; + continue; + } + + for(m=0; volumes[m].name; m++) { + if(strcmp(field[i], volumes[m].name) == 0) { + v = m; + in = 1; + out = 1; + left = 1; + right = 1; + break; + } + } + if(volumes[m].name) + continue; + + if(strcmp(field[i], "chat") == 0){ + chatty = !chatty; + continue; + } + + if(strcmp(field[i], "reset") == 0) { + resetlevel(); + doload = 1; + continue; + } + if(strcmp(field[i], "loop") == 0) { + if(++i >= nf) + error(Evolume); + audio.loopback = strtoul(field[i], 0, 10); + doload = 1; + continue; + } + if(strcmp(field[i], "enc") == 0) { + if(++i >= nf) + error(Evolume); + fmt = -1; + if(strcmp(field[i], "ulaw") == 0) + fmt = uLaw; + else if(strcmp(field[i], "alaw") == 0) + fmt = aLaw; + else if(strcmp(field[i], "pcm") == 0) + fmt = Linear8; + else if(strcmp(field[i], "adpcm") == 0) + fmt = ADPCM; + else + error(Evolume); + if(in) + csdev.regs[InFormat] = fmt; + if(out) + csdev.regs[OutFormat] = fmt; + doload = 1; + continue; + } + if(strcmp(field[i], "dev") == 0) { + if(++i >= nf) + error(Evolume); + if(in){ + fmt = -1; + if(strcmp(field[i], "mic") == 0) + fmt = ISmic; + else if(strcmp(field[i], "line") == 0) + fmt = ISline; + else if(strcmp(field[i], "aux1") == 0) + fmt = ISaux1; + else if(strcmp(field[i], "loop") == 0) + fmt = ISloop; + else + error(Evolume); + if(left) + csdev.regs[LeftADC] = fmt; + if(right) + csdev.regs[RightADC] = fmt; + doload = 1; + } + continue; + } + if(strcmp(field[i], "in") == 0) { + in = 1; + out = 0; + continue; + } + if(strcmp(field[i], "out") == 0) { + in = 0; + out = 1; + continue; + } + if(strcmp(field[i], "left") == 0) { + left = 1; + right = 0; + continue; + } + if(strcmp(field[i], "right") == 0) { + left = 0; + right = 1; + continue; + } + error(Evolume); + } + if(doload) + mxvolume(); + qunlock(&audio); + poperror(); + n=0; + break; + + case Qaudio: + qlock(&audio.out); + if(waserror()){ + qunlock(&audio.out); + nexterror(); + } + while(n > 0) { + b = audio.out.filling; + if(b == 0) { + b = getbuf(&audio.out.empty); + if(b == 0) { + startoutput(); + sleep(&audio.out.r, anybuf, &audio.out); + continue; + } + b->count = 0; + audio.out.filling = b; + } + + m = Bufsize-b->count; + if(m > n) + m = n; + memmove(b->virt+b->count, a, m); + + b->count += m; + n -= m; + a += m; + if(b->count >= Bufsize) { + audio.out.filling = 0; + putbuf(&audio.out.full, b); + } + } + qunlock(&audio.out); + poperror(); + break; + } + return n0 - n; +} + +long +cs4231bwrite(Chan *c, Block *bp, ulong offset) +{ + return devbwrite(c, bp, offset); +} + +void +cs4231remove(Chan *c) +{ + USED(c); + error(Eperm); +} + +void +cs4231wstat(Chan *c, char *dp) +{ + USED(c, dp); + error(Eperm); +} + +static char * +encname(int v) +{ + switch(v & ~(0xF|Stereo)){ + case uLaw: return "ulaw"; + case aLaw: return "alaw"; + case Linear8: return "pcm"; + case Linear16: return "pcm16"; + case ADPCM: return "adpcm"; + default: return "?"; + } +} diff --git a/os/js/devrtc.c b/os/js/devrtc.c new file mode 100644 index 00000000..25fcfa1e --- /dev/null +++ b/os/js/devrtc.c @@ -0,0 +1,413 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "io.h" + +/* + * Mostek MK48T12-15 Zeropower/Timekeeper + * This driver is actually portable. + */ +typedef struct Rtc Rtc; +struct Rtc +{ + int sec; + int min; + int hour; + int wday; + int mday; + int mon; + int year; +}; + +static uchar rtcgencksum(void); +static void setrtc(Rtc *rtc); +static long rtctime(void); +static int *yrsize(int yr); +static int *yrsize(int yr); +static ulong rtc2sec(Rtc *rtc); +static void sec2rtc(ulong secs, Rtc *rtc); + +static struct +{ + uchar *cksum; + uchar *ram; + RTCdev *rtc; +}nvr; + +enum{ + Qdir, + Qrtc, + Qnvram, +}; + +QLock rtclock; /* mutex on clock operations */ + +static Dirtab rtcdir[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "rtc", {Qrtc, 0}, 0, 0666, + "nvram", {Qnvram, 0}, NVWRITE, 0666, +}; +#define NRTC (sizeof(rtcdir)/sizeof(rtcdir[0])) + +static void +rtcinit(void) +{ + KMap *k; + + k = kmappa(NVR_CKSUM_PHYS, PTENOCACHE|PTEIO); + nvr.cksum = (uchar*)VA(k); + + k = kmappa(NVR_PHYS, PTENOCACHE|PTEIO); + nvr.ram = (uchar*)VA(k); + nvr.rtc = (RTCdev*)(VA(k)+RTCOFF); + + rtcgencksum(); +} + +static Chan* +rtcattach(char *spec) +{ + return devattach('r',spec); +} + +static Walkqid* +rtcwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, rtcdir, NRTC, devgen); +} + +static int +rtcstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, rtcdir, NRTC, devgen); +} + +static Chan* +rtcopen(Chan *c, int omode) +{ + omode = openmode(omode); + switch((ulong)c->qid.path){ + case Qrtc: + if(strcmp(up->env->user, eve)!=0 && omode!=OREAD) + error(Eperm); + break; + case Qnvram: + if(strcmp(up->env->user, eve)!=0) + error(Eperm); + } + return devopen(c, omode, rtcdir, NRTC, devgen); +} + +static void +rtccreate(Chan *c, char *name, int omode, ulong perm) +{ + USED(c, name, omode, perm); + error(Eperm); +} + +static void +rtcclose(Chan *c) +{ + USED(c); +} + +static long +rtcread(Chan *c, void *buf, long n, vlong offset) +{ + ulong t, ot; + + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, rtcdir, NRTC, devgen); + + switch((ulong)c->qid.path){ + case Qrtc: + qlock(&rtclock); + t = rtctime(); + do{ + ot = t; + t = rtctime(); /* make sure there's no skew */ + }while(t != ot); + qunlock(&rtclock); + n = readnum(offset, buf, n, t, 12); + return n; + case Qnvram: + if(offset > NVREAD) + return 0; + if(n > NVREAD - offset) + n = NVREAD - offset; + qlock(&rtclock); + memmove(buf, nvr.ram+offset, n); + qunlock(&rtclock); + return n; + } + error(Egreg); + return 0; /* not reached */ +} + +/* + * XXX - Tad: fixme to generate the correct checksum + */ +static uchar +rtcgencksum(void) +{ + uchar cksum; + int i; + static uchar p1cksum = 0; + static uchar p1cksumvalid=0; + + if(!p1cksumvalid) { + for(i=1; i < 0x1000 ; i++) + p1cksum ^= nvr.cksum[i]; + p1cksumvalid = 1; + } + + cksum = p1cksum; + + for(i=0; i < 0xfdf ; i++) { + cksum ^= nvr.ram[i]; + } + + return cksum; +} + +static long +rtcwrite(Chan *c, void *buf, long n, vlong offset) +{ + Rtc rtc; + ulong secs; + char *cp, sbuf[32]; + + switch((ulong)c->qid.path){ + case Qrtc: + /* + * read the time + */ + if(offset != 0 || n >= sizeof(sbuf)-1) + error(Ebadarg); + memmove(sbuf, buf, n); + sbuf[n] = '\0'; + cp = sbuf; + while(*cp){ + if(*cp>='0' && *cp<='9') + break; + cp++; + } + secs = strtoul(cp, 0, 0); + /* + * convert to bcd + */ + sec2rtc(secs, &rtc); + /* + * write it + */ + qlock(&rtclock); + setrtc(&rtc); + qunlock(&rtclock); + return n; + case Qnvram: + if(offset > NVWRITE) + return 0; + if(n > NVWRITE - offset) + n = NVWRITE - offset; + qlock(&rtclock); + memmove(nvr.ram+offset, buf, n); + *nvr.cksum = rtcgencksum(); + qunlock(&rtclock); + return n; + } + error(Egreg); + return 0; /* not reached */ +} + +#define bcd2dec(bcd) (((((bcd)>>4) & 0x0F) * 10) + ((bcd) & 0x0F)) +#define dec2bcd(dec) ((((dec)/10)<<4)|((dec)%10)) + +static void +setrtc(Rtc *rtc) +{ + struct RTCdev *dev; + + dev = nvr.rtc; + dev->control |= RTCWRITE; + wbflush(); + dev->year = dec2bcd(rtc->year % 100); + dev->mon = dec2bcd(rtc->mon); + dev->mday = dec2bcd(rtc->mday); + dev->hour = dec2bcd(rtc->hour); + dev->min = dec2bcd(rtc->min); + dev->sec = dec2bcd(rtc->sec); + wbflush(); + dev->control &= ~RTCWRITE; + wbflush(); +} + +static long +rtctime(void) +{ + struct RTCdev *dev; + Rtc rtc; + + dev = nvr.rtc; + dev->control |= RTCREAD; + wbflush(); + rtc.sec = bcd2dec(dev->sec) & 0x7F; + rtc.min = bcd2dec(dev->min & 0x7F); + rtc.hour = bcd2dec(dev->hour & 0x3F); + rtc.mday = bcd2dec(dev->mday & 0x3F); + rtc.mon = bcd2dec(dev->mon & 0x3F); + rtc.year = bcd2dec(dev->year); + dev->control &= ~RTCREAD; + wbflush(); + + if (rtc.mon < 1 || rtc.mon > 12) + return 0; + /* + * the world starts Jan 1 1970 + */ + if(rtc.year < 70) + rtc.year += 2000; + else + rtc.year += 1900; + + return rtc2sec(&rtc); +} + +#define SEC2MIN 60L +#define SEC2HOUR (60L*SEC2MIN) +#define SEC2DAY (24L*SEC2HOUR) + +/* + * days per month plus days/year + */ +static int dmsize[] = +{ + 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; +static int ldmsize[] = +{ + 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +/* + * return the days/month for the given year + */ +static int * +yrsize(int yr) +{ + if((yr % 4) == 0) + return ldmsize; + else + return dmsize; +} + +/* + * compute seconds since Jan 1 1970 + */ +static ulong +rtc2sec(Rtc *rtc) +{ + ulong secs; + int i; + int *d2m; + + secs = 0; + + /* + * seconds per year + */ + for(i = 1970; i < rtc->year; i++){ + d2m = yrsize(i); + secs += d2m[0] * SEC2DAY; + } + + /* + * seconds per month + */ + d2m = yrsize(rtc->year); + for(i = 1; i < rtc->mon; i++) + secs += d2m[i] * SEC2DAY; + + secs += (rtc->mday-1) * SEC2DAY; + secs += rtc->hour * SEC2HOUR; + secs += rtc->min * SEC2MIN; + secs += rtc->sec; + + return secs; +} + +/* + * compute rtc from seconds since Jan 1 1970 + */ +static void +sec2rtc(ulong secs, Rtc *rtc) +{ + int d; + long hms, day; + int *d2m; + + /* + * break initial number into days + */ + hms = secs % SEC2DAY; + day = secs / SEC2DAY; + if(hms < 0) { + hms += SEC2DAY; + day -= 1; + } + + /* + * generate hours:minutes:seconds + */ + rtc->sec = hms % 60; + d = hms / 60; + rtc->min = d % 60; + d /= 60; + rtc->hour = d; + + /* + * year number + */ + if(day >= 0) + for(d = 1970; day >= *yrsize(d); d++) + day -= *yrsize(d); + else + for (d = 1970; day < 0; d--) + day += *yrsize(d-1); + rtc->year = d; + + /* + * generate month + */ + d2m = yrsize(rtc->year); + for(d = 1; day >= d2m[d]; d++) + day -= d2m[d]; + rtc->mday = day + 1; + rtc->mon = d; + + return; +} + +Dev rtcdevtab = { + 'r', + "rtc", + + devreset, + rtcinit, + devshutdown, + rtcattach, + rtcwalk, + rtcstat, + rtcopen, + rtccreate, + rtcclose, + rtcread, + devbread, + rtcwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/js/fns.h b/os/js/fns.h new file mode 100644 index 00000000..2ad8ac14 --- /dev/null +++ b/os/js/fns.h @@ -0,0 +1,110 @@ +#include "../port/portfns.h" +#define dumplongs(x, y, z) +#define clockcheck() +#define setpanic() + +void links(void); +void prom_printf(char *format, ...); /* can't use after mmuinit() */ +void savefpregs(FPU *); +void restfpregs(FPU*); +void savefsr(FPenv *); +void restfsr(FPenv *); +void disabfp(void); +void fpinit(void); +void fpsave(FPU*); +void bootargs(ulong); +void cacheinit(void); +ulong call_openboot(void*, ...); +void clearftt(ulong); +#define clearmmucache() +void clockinit(void); +void clock(Ureg*); +#define coherence() /* nothing to do on uniprocessor */ +void dcflush(void); +void disabfp(void); +void enabfp(void); +char* excname(ulong); + +#define flushpage(pa) icflush() +void flushtlb(void); +void flushtlbctx(void); +void flushtlbpage(ulong); +int fpcr(int); +int fpquiet(void); +void fpregrestore(char*); +void fpregsave(char*); +int fptrap(void); +int getfpq(ulong*); +ulong getfsr(void); +ulong getpcr(void); +ulong getphys(ulong); +ulong getrmmu(ulong); +ulong getpsr(void); +void icflush(void); +int isvalid_va(void*); +void flushicache(void); +void flushdcache(void); +void flushiline(ulong); +void flushdline(ulong); +#define idlehands() /* nothing to do in the runproc */ +void intrinit(void); +void ioinit(void); +void kbdclock(void); +void kbdrepeat(int); +void kbdinit(void); +void kbdintr(void); +#define KADDR(a) ((void*)((ulong)(a)|KZERO)) +#define PADDR(a) ((ulong)(a)&~KZERO) +void kmapinit(void); +void* kmappa(ulong, ulong); +ulong kmapsbus(int); +ulong kmapdma(ulong, ulong); +int kprint(char*, ...); +void kproftimer(ulong); +void kunmap(KMap*); +void lanceintr(void); +void lancesetup(Lance*); +void lancetoggle(void); +void mmuinit(void); +void mousebuttons(int); +void printinit(void); +#define procrestore(p) +#define procsave(p) +#define procsetup(x) ((p)->fpstate = FPinit) +void putphys(ulong, ulong); +void putrmmu(ulong, ulong); +void putstr(char*); +void puttbr(ulong); +void systemreset(void); +void screeninit(void); +void screenputc(char *); +void screenputs(char*, int); +void scsiintr(void); +void setpcr(ulong); +void setpsr(ulong); +void spldone(void); +void trap(Ureg*); +void trapinit(void); +#define wbflush() /* mips compatibility */ +#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) +ulong getcallerpc(void*); +void dumpregs(Ureg*); + +void ns16552special(int,int,Queue**,Queue**,int (*)(Queue*,int)); +char ns16552dmarcv(int); +void ns16552install(void); +void ns16552intr(int); + +long dmasetup(int,void*,long,int); +void dmaend(int); +int dmacount(int); + +void superioinit(ulong va, uchar*, uchar*, uchar*, uchar*); +ulong superiova(void); + +uchar superio_readctl(void); +uchar superio_readdata(void); +void superio_writectl(uchar val); +void superio_writedata(uchar val); +void outb(ulong, uchar); +uchar inb(ulong); diff --git a/os/js/fsv.c b/os/js/fsv.c new file mode 100644 index 00000000..9832ce3b --- /dev/null +++ b/os/js/fsv.c @@ -0,0 +1,198 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "io.h" +#include "dat.h" +#include "fns.h" + +#include +#include +#include "screen.h" + +extern Video *vid; +extern Memimage gscreen; + +static Vctlr* init(Vctlr* vctlr, int x, int y, int d); +static int setcolour(ulong p, ulong r, ulong g, ulong b); +static void enable(void); +static void disable(void); +static void move(int cx, int cy); +static void load(Cursor *c); +static int isloaded(void); + +extern void* memcpy(void*, void*, int); +extern void cursorupdate0(void); + +Vctlr FSV = { + "FSV", /* name */ + init, /* init */ + 0, /* page */ + setcolour, /* setcolor */ + + enable, /* enable cursor fn */ + disable, /* disable cursor fn */ + move, /* move cursor fn */ + load, /* load cursor fn */ + isloaded, /* is cursor loaded? */ + 0, /* deprecated */ + + 1024, /* screen width (x) */ + 768, /* screen height (y) */ + 3, /* depth */ + + 0, /* hidecount */ + 0, /* loaded */ +}; + +static int lastx=-1; +static int lasty=-1; + +static Vctlr* +init(Vctlr* vctlr, int x, int y, int d) +{ + USED(vctlr,x,y,d); + + return &FSV; +} + +static int +setcolour(ulong p, ulong r, ulong g, ulong b) +{ + if(gscreen.ldepth == 0) + return 0; /* can't change mono screen colormap */ + else{ + vid->addr = p << 24; + vid->color = r << 24; + vid->color = g << 24; + vid->color = b << 24; + return 1; + } +} + +static ulong backingstore[64]; +static Memdata backingstoredata = { + nil, + backingstore +}; + +static ulong backingnocursor[64]; +static Memdata backingnocursordata = { + nil, + backingnocursor +}; + +static ulong backwithcursor[64]; +static Memdata backwithcursordata = { + nil, + backwithcursor +}; + +static Memimage backingnocursormem = { + {0,0,16,16}, + {0,0,16,16}, + 3, + 0, + &backingnocursordata, + 0, + 16/4, + 0, + 0, +}; +static Memimage backingmem = { + {0,0,16,16}, + {0,0,16,16}, + 3, + 0, + &backingstoredata, + 0, + 16/4, + 0, + 0, +}; + +static void +disable(void) +{ + if(FSV.hidecount++) + return; + if(lastx < 0 || lasty < 0) + return; + + memimagedraw(&gscreen, Rect(lastx,lasty,lastx+16,lasty+16), + &backingnocursormem, Pt(0,0), memones, Pt(0,0)); +} + +static void +enable(void) +{ + uchar *p; + uchar mask; + uchar *cset; + int i; + + if(--FSV.hidecount > 0) + return; + FSV.hidecount = 0; + + if(lastx < 0 || lasty < 0) + return; + + memimagedraw(&backingmem,Rect(0,0,16,16),&gscreen,Pt(lastx,lasty),memones, + Pt(0,0)); + + memcpy(backingnocursor,backingstore,256); + p = (uchar*)backingmem.data->data; + + cset = FSV.cursor.set; + + for(i=0;i<32;i++) { + mask = ~cset[i]; + + if(!(mask&(1<<7))) *p = 0xff; + ++p; + if(!(mask&(1<<6))) *p = 0xff; + ++p; + if(!(mask&(1<<5))) *p = 0xff; + ++p; + if(!(mask&(1<<4))) *p = 0xff; + ++p; + if(!(mask&(1<<3))) *p = 0xff; + ++p; + if(!(mask&(1<<2))) *p = 0xff; + ++p; + if(!(mask&(1<<1))) *p = 0xff; + ++p; + if(!(mask&(1<<0))) *p = 0xff; + ++p; + } + + memimagedraw(&gscreen,Rect(lastx,lasty,lastx+16,lasty+16),&backingmem,Pt(0,0), + memones,Pt(0,0)); +} + +static void +move(int cx, int cy) +{ + if(!FSV.loaded) + return; + + disable(); + cursorupdate0(); + lastx = cx; + lasty = cy; + enable(); +} + + +static void +load(Cursor *curs) +{ + FSV.cursor = *curs; + FSV.loaded=1; +} + +static int +isloaded(void) +{ + return FSV.loaded; +} diff --git a/os/js/io.h b/os/js/io.h new file mode 100644 index 00000000..c3868eb2 --- /dev/null +++ b/os/js/io.h @@ -0,0 +1,224 @@ +/* + * most device registers are memory mapped, but + * a few things are accessed using putphys/getphys + */ +#define SBUS(n) (0x30000000+(n)*0x10000000) +#define FRAMEBUF(n) SBUS(n) +#define FRAMEBUFID(n) (SBUS(n)+0x000000) +#define DISPLAYRAM(n) (SBUS(n)+0x800000) +#define CLOCK 0x71D00000 +#define CLOCKFREQ 1000000 /* one microsecond increments */ + +#define SUPERIO_PHYS_PAGE 0x71300000 +#define SUPERIO_INDEX_OFFSET 0x398 +#define SUPERIO_DATA_OFFSET 0x399 +#define SUPERIO_MOUSE_KBD_DATA_PORT 0x60 +#define SUPERIO_MOUSE_KBD_CTL_PORT 0x64 + +#define AUDIO_PHYS_PAGE 0x66666666 +#define AUDIO_INDEX_OFFSET 0x830 + +enum +{ + Mousevec = 13, + Kbdvec = 13 +}; + +#define NVR_CKSUM_PHYS 0x71200000 /* non-volatile RAM cksum page */ +#define NVR_PHYS 0x71201000 /* non-volatile RAM */ +#define DMA 0x78400000 /* SCSI and Ether DMA registers */ +#define SCSI 0x78800000 /* NCR53C90 registers */ +#define ETHER 0x78C00000 /* RDP, RAP */ +#define FLOPPY 0x71400000 +#define SYSINTR 0x71E10000 /* system interrupt control registers */ + +#define TIMECONFIG 0x71D10010 /* timer configuration register (phys) */ +#define AUXIO1 0x71900000 +#define AUXIO2 0x71910000 + +typedef struct Sysint Sysint; +struct Sysint +{ + ulong pending; + ulong mask; + ulong maskclr; + ulong maskset; + ulong target; +}; + +enum { + MaskAllIntr = 1<<31, + MEIntr = 1<<30, + MSIIntr = 1<<29, + EMCIntr = 1<<28, + VideoIntr = 1<<20, /* supersparc only */ + Timer10 = 1<<19, + EtherIntr = 1<<16, + SCCIntr = 1<<15, + KbdIntr = 1<<13, + /* bits 7 to 13 are SBUS levels 1 to 7 */ +}; +#define SBUSINTR(x) (1<<((x)+6)) + +typedef struct SCCdev SCCdev; +struct SCCdev +{ + uchar ptrb; + uchar dummy1; + uchar datab; + uchar dummy2; + uchar ptra; + uchar dummy3; + uchar dataa; + uchar dummy4; +}; + +/* + * non-volatile ram + */ +#define NVREAD (4096-32) /* minus RTC */ +#define NVWRITE (0x800) /* */ +#define IDOFF (4096-8-32) + +/* + * real-time clock + */ +typedef struct RTCdev RTCdev; +struct RTCdev +{ + uchar control; /* read or write the device */ + uchar sec; + uchar min; + uchar hour; + uchar wday; + uchar mday; + uchar mon; + uchar year; +}; +#define RTCOFF 0xFF8 +#define RTCREAD (0x40) +#define RTCWRITE (0x80) + +/* + * dma + */ +typedef struct DMAdev DMAdev; +struct DMAdev { + /* ESP/SCSI DMA */ + ulong csr; /* Control/Status */ + ulong addr; /* address in 16Mb segment */ + ulong count; /* transfer byte count */ + ulong diag; + + /* Ether DMA */ + ulong ecsr; /* Control/Status */ + ulong ediag; + ulong cache; /* cache valid bits */ + uchar base; /* base address (16Mb segment) */ +}; + +enum { + Int_pend = 0x00000001, /* interrupt pending */ + Err_pend = 0x00000002, /* error pending */ + Pack_cnt = 0x0000000C, /* pack count (mask) */ + Int_en = 0x00000010, /* interrupt enable */ + Dma_Flush = 0x00000020, /* flush pack end error */ + Drain = 0x00000040, /* drain pack to memory */ + Dma_Reset = 0x00000080, /* hardware reset (sticky) */ + Write = 0x00000100, /* set for device to memory (!) */ + En_dma = 0x00000200, /* enable DMA */ + Req_pend = 0x00000400, /* request pending */ + Byte_addr = 0x00001800, /* next byte addr (mask) */ + En_cnt = 0x00002000, /* enable count */ + Tc = 0x00004000, /* terminal count */ + Ilacc = 0x00008000, /* which ether chip */ + Dev_id = 0xF0000000, /* device ID */ +}; + +/* + * NCR53C90 SCSI controller (every 4th location) + */ +typedef struct SCSIdev SCSIdev; +struct SCSIdev { + uchar countlo; /* byte count, low bits */ + uchar pad1[3]; + uchar countmi; /* byte count, middle bits */ + uchar pad2[3]; + uchar fifo; /* data fifo */ + uchar pad3[3]; + uchar cmd; /* command byte */ + uchar pad4[3]; + union { + struct { /* read only... */ + uchar status; /* status */ + uchar pad05[3]; + uchar intr; /* interrupt status */ + uchar pad06[3]; + uchar step; /* sequence step */ + uchar pad07[3]; + uchar fflags; /* fifo flags */ + uchar pad08[3]; + uchar config; /* RW: configuration */ + uchar pad09[3]; + uchar Reserved1; + uchar pad0A[3]; + uchar Reserved2; + uchar pad0B[3]; + uchar conf2; /* RW: configuration */ + uchar pad0C[3]; + uchar conf3; /* RW: configuration */ + uchar pad0D[3]; + uchar partid; /* unique part id */ + uchar pad0E[3]; + uchar fbottom; /* RW: fifo bottom */ + uchar pad0F[3]; + }; + struct { /* write only... */ + uchar destid; /* destination id */ + uchar pad15[3]; + uchar timeout; /* during selection */ + uchar pad16[3]; + uchar syncperiod; /* synchronous xfr period */ + uchar pad17[3]; + uchar syncoffset; /* synchronous xfr offset */ + uchar pad18[3]; + uchar RW0; + uchar pad19[3]; + uchar clkconf; + uchar pad1A[3]; + uchar test; + uchar pad1B[3]; + uchar RW1; + uchar pad1C[3]; + uchar RW2; + uchar pad1D[3]; + uchar counthi; /* byte count, hi bits */ + uchar pad1E[3]; + uchar RW3; + uchar pad1F[3]; + }; + }; +}; + +/* + * DMA2 ENET + */ +enum { + E_Int_pend = 0x00000001, /* interrupt pending */ + E_Err_pend = 0x00000002, /* error pending */ + E_draining = 0x0000000C, /* E-cache draining */ + E_Int_en = 0x00000010, /* interrupt enable */ + E_Invalidate = 0x00000020, /* mark E-cache invalid */ + E_Slave_err = 0x00000040, /* slave access size error (sticky) */ + E_Reset = 0x00000080, /* invalidate cache & reset interface (sticky) */ + E_Drain = 0x00000400, /* force draining of E-cache to memory */ + E_Dsbl_wr_drn = 0x00000800, /* disable E-cache drain on descriptor writes from ENET */ + E_Dsbl_rd_drn = 0x00001000, /* disable E-cache drain on slave reads to ENET */ + E_Ilacc = 0x00008000, /* `modifies ENET DMA cycle' */ + E_Dsbl_buf_wr = 0x00010000, /* disable buffering of slave writes to ENET */ + E_Dsbl_wr_inval = 0x00020000, /* do not invalidate E-cache on slave writes */ + E_Burst_size = 0x000C0000, /* DMA burst size */ + E_Loop_test = 0x00200000, /* loop back mode */ + E_TP_select = 0x00400000, /* zero for AUI mode */ + E_Dev_id = 0xF0000000, /* device ID */ +}; diff --git a/os/js/iob.c b/os/js/iob.c new file mode 100644 index 00000000..4873847e --- /dev/null +++ b/os/js/iob.c @@ -0,0 +1,13 @@ +#include "u.h" + +void +outb(ulong addr, uchar val) +{ + *(uchar*)addr = val; +} + +uchar +inb(ulong addr) +{ + return *(uchar*)addr; +} diff --git a/os/js/js b/os/js/js new file mode 100644 index 00000000..16e2ff64 --- /dev/null +++ b/os/js/js @@ -0,0 +1,104 @@ +dev + root + cons + env + mnt + pipe + prog + rtc + srv + dup + ssl + draw + pointer + + ip bootp ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium + lance netif netaux ethermedium + + ns16552 + tinyfs + cap +# cs4231 A + +ip +# il + tcp + udp +# rudp +# igmp + ipifc + icmp + icmp6 + ipmux + +lib + interp + tk + prefab + draw + memlayer + memdraw + keyring + sec + mp + kern +# math + +mod + sys + draw + prefab + tk + keyring + +link + pppmedium ppp compress + +init + jsinit + + +port + alarm + alloc + allocb + chan + dev + dial + dis + discall + exception + exportfs + inferno + latin1 + nocache + parse + pgrp + print + proc + qio + qlock + sysfile + taslock + xalloc + +code + int kernel_pool_pcnt = 10; + int main_pool_pcnt = 40; + int heap_pool_pcnt = 20; + int image_pool_pcnt = 40; + int consoleprint = 1; + int cflag = 1; + + +root + /chan / + /dev / + /dis + /env / + /fd / + /n + /net / + /nvfs / + /prog / + /osinit.dis diff --git a/os/js/kbd.c b/os/js/kbd.c new file mode 100644 index 00000000..38bae986 --- /dev/null +++ b/os/js/kbd.c @@ -0,0 +1,482 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +enum +{ + Data= 0x60, /* data port */ + + Status= 0x64, /* status port */ + Inready= 0x01, /* input character ready */ + Outbusy= 0x02, /* output busy */ + Sysflag= 0x04, /* system flag */ + Cmddata= 0x08, /* cmd==0, data==1 */ + Inhibit= 0x10, /* keyboard/mouse inhibited */ + Minready= 0x20, /* mouse character ready */ + Rtimeout= 0x40, /* general timeout */ + Parity= 0x80, + + Cmd= 0x64, /* command port (write only) */ + + CTdata= 0x0, /* chips & Technologies ps2 data port */ + CTstatus= 0x1, /* chips & Technologies ps2 status port */ + Enable= 1<<7, + Clear= 1<<6, + Error= 1<<5, + Intenable= 1<<4, + Reset= 1<<3, + Tready= 1<<2, + Rready= 1<<1, + Idle= 1<<0, + + Spec= 0x80, + + PF= Spec|0x20, /* num pad function key */ + View= Spec|0x00, /* view (shift window up) */ + KF= Spec|0x40, /* function key */ + Shift= Spec|0x60, + Break= Spec|0x61, + Ctrl= Spec|0x62, + Latin= Spec|0x63, + Caps= Spec|0x64, + Num= Spec|0x65, + Middle= Spec|0x66, + No= 0x00, /* peter */ + + Home= KF|13, + Up= KF|14, + Pgup= KF|15, + Print= KF|16, + Left= View, + Right= View, + End= '\r', + Down= View, + Pgdown= View, + Ins= KF|20, + Del= 0x7F, + + Rbutton=4, + Mbutton=2, + Lbutton=1, +}; + +uchar +kbtab[] = { +[0x00] No, 0x1b, '1', '2', '3', '4', '5', '6', +[0x08] '7', '8', '9', '0', '-', '=', '\b', '\t', +[0x10] 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', +[0x18] 'o', 'p', '[', ']', '\n', Ctrl, 'a', 's', +[0x20] 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', +[0x28] '\'', '`', Shift, '\\', 'z', 'x', 'c', 'v', +[0x30] 'b', 'n', 'm', ',', '.', '/', Shift, '*', +[0x38] Latin, ' ', Ctrl, KF|1, KF|2, KF|3, KF|4, KF|5, +[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, KF|12, '7', +[0x48] '8', '9', '-', '4', '5', '6', '+', '1', +[0x50] '2', '3', '0', '.', No, No, No, KF|11, +[0x58] KF|12, No, No, No, No, No, No, No, +}; + +uchar +kbtabshift[] = { +[0x00] No, 0x1b, '!', '@', '#', '$', '%', '^', +[0x08] '&', '*', '(', ')', '_', '+', '\b', '\t', +[0x10] 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', +[0x18] 'O', 'P', '{', '}', '\n', Ctrl, 'A', 'S', +[0x20] 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', +[0x28] '"', '~', Shift, '|', 'Z', 'X', 'C', 'V', +[0x30] 'B', 'N', 'M', '<', '>', '?', Shift, '*', +[0x38] Latin, ' ', Ctrl, KF|1, KF|2, KF|3, KF|4, KF|5, +[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, KF|12, '7', +[0x48] '8', '9', '-', '4', '5', '6', '+', '1', +[0x50] '2', '3', '0', '.', No, No, No, KF|11, +[0x58] KF|12, No, No, No, No, No, No, No, +}; + +uchar +kbtabesc1[] = { +[0x00] No, No, No, No, No, No, No, No, +[0x08] No, No, No, No, No, No, No, No, +[0x10] No, No, No, No, No, No, No, No, +[0x18] No, No, No, No, '\n', Ctrl, No, No, +[0x20] No, No, No, No, No, No, No, No, +[0x28] No, No, Shift, No, No, No, No, No, +[0x30] No, No, No, No, No, '/', No, Print, +[0x38] Latin, No, No, No, No, No, No, No, +[0x40] No, No, No, No, No, No, Break, Home, +[0x48] Up, Pgup, No, Left, No, Right, No, End, +[0x50] Down, Pgdown, Ins, Del, No, No, No, No, +[0x58] No, No, No, No, No, No, No, No, +}; + +static int keybuttons; +static uchar ccc; +static int shift; + +enum +{ + /* controller command byte */ + Cscs1= (1<<6), /* scan code set 1 */ + Cmousedis= (1<<5), /* mouse disable */ + Ckbddis= (1<<4), /* kbd disable */ + Csf= (1<<2), /* system flag */ + Cmouseint= (1<<1), /* mouse interrupt enable */ + Ckbdint= (1<<0), /* kbd interrupt enable */ +}; + +/* + * wait for output no longer busy + */ +static int +outready(void) +{ + int tries; + + for(tries = 0; (superio_readctl() & Outbusy); tries++){ + if(tries > 500) + return -1; + microdelay(2); + } + return 0; +} + +/* + * wait for input + */ +static int +inready(void) +{ + int tries; + + for(tries = 0; !(superio_readctl() & Inready); tries++){ + if(tries > 500) + return -1; + microdelay(2); + } + return 0; +} + +/* + * send a command to the mouse + */ +static int +mousecmd(int cmd) +{ + unsigned int c; + int tries; + + c = 0; + tries = 0; + do{ + if(tries++ > 2) + break; + if(outready() < 0) + break; + superio_writectl(0xD4); + if(outready() < 0) + break; + superio_writedata(cmd); + if(outready() < 0) + break; + if(inready() < 0) + break; + c = superio_readdata(); + } while(c == 0xFE || c == 0); + + if(c != 0xFA){ + print("mouse returns %2.2ux to the %2.2ux command\n", c, cmd); + return -1; + } + return 0; +} + +/* + * ask 8042 to enable the use of address bit 20 + */ +void +i8042a20(void) +{ + outready(); + superio_writectl(0xD1); + outready(); + superio_writedata(0xDF); + outready(); +} + +/* + * ask 8042 to reset the machine + */ +void +i8042reset(void) +{ + ushort *s = (ushort*)(KZERO|0x472); + int i, x; + + *s = 0x1234; /* BIOS warm-boot flag */ + + /* + * newer reset the machine command + */ + outready(); + superio_writectl(0xFE); + outready(); + + /* + * Pulse it by hand (old somewhat reliable) + */ + x = 0xDF; + for(i = 0; i < 5; i++){ + x ^= 1; + outready(); + superio_writectl(0xD1); + outready(); + superio_writedata(x); /* toggle reset */ + microdelay(100); + } +} + +/* + * ps/2 mouse message is three bytes + * + * byte 0 - 0 0 SDY SDX 1 M R L + * byte 1 - DX + * byte 2 - DY + * + * shift & left button is the same as middle button + */ +static int +ps2mouseputc(int c) +{ + static short msg[3]; + static int nb; + static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 5, 2, 3, 6, 7 }; + int buttons, dx, dy; + + /* + * check byte 0 for consistency + */ + if(nb==0 && (c&0xc8)!=0x08) + return 0; + + msg[nb] = c; + if(++nb == 3) { + nb = 0; + if(msg[0] & 0x10) + msg[1] |= 0xFF00; + if(msg[0] & 0x20) + msg[2] |= 0xFF00; + + buttons = b[(msg[0]&7) | (shift ? 8 : 0)] | keybuttons; + dx = msg[1]; + dy = -msg[2]; + mousetrack(buttons, dx, dy, 1); + } + return 0; +} + +/* + * keyboard interrupt + */ +void +kbdintr(void) +{ + int s, c, i; + static int esc1, esc2; + static int caps; + static int ctl; + static int num; + static int collecting, nk; + static int alt; + static Rune kc[5]; + int keyup; + + /* + * get status + */ + s = superio_readctl(); + if(!(s&Inready)) + return; + + /* + * get the character + */ + c = superio_readdata(); + + /* + * if it's the mouse... + */ + if(s & Minready) { + ps2mouseputc(c); + return; + } + + /* + * e0's is the first of a 2 character sequence + */ + if(c == 0xe0){ + esc1 = 1; + return; + } else if(c == 0xe1){ + esc2 = 2; + return; + } + + keyup = c&0x80; + c &= 0x7f; + if(c > sizeof kbtab){ +/* print("unknown key %ux\n", c|keyup); */ + return; + } + + if(esc1){ + c = kbtabesc1[c]; + esc1 = 0; + } else if(esc2){ + esc2--; + return; + } else if(shift) + c = kbtabshift[c]; + else + c = kbtab[c]; + + if(caps && c<='z' && c>='a') + c += 'A' - 'a'; + + /* + * keyup only important for shifts + */ + if(keyup){ + switch(c){ + case Latin: + alt = 0; + break; + case Shift: + shift = 0; + break; + case Ctrl: + ctl = 0; + break; + } + return; + } + + /* + * normal character + */ + if(!(c & Spec)){ + if(ctl){ + if(alt && c == Del) + exit(0); + c &= 0x1f; + } + if(!collecting){ + kbdputc(kbdq, c); + return; + } + kc[nk++] = c; + c = latin1(kc, nk); + if(c < -1) /* need more keystrokes */ + return; + if(c != -1) /* valid sequence */ + kbdputc(kbdq, c); + else /* dump characters */ + for(i=0; i>24)&0xFF)*4), R4 /* KZERO base in level 1 ptab */ + MOVW (R5, PHYSASI), R6 /* L1 region 0 set by boot */ + ADD R4,R5,R10 + MOVW R6, (R10, PHYSASI) /* map KZERO */ + + ADD $4, R5 + MOVW (R5, PHYSASI), R6 + ADD R4, R5, R10 + MOVW R6, (R10, PHYSASI) /* map KZERO+16Mbytes */ + + /* now mapped correctly. jmpl to where we want to be */ + + MOVW $startvirt(SB), R7 + JMPL (R7) + RETURN /* can't get here */ + +TEXT startvirt(SB), $-4 + MOVW $edata(SB),R9 + MOVW $end(SB),R10 +clrbss: + MOVW R0,(R9) + ADD $4, R9 + CMP R9, R10 + BNE clrbss + NOOP + + MOVW $rom(SB), R7 + MOVW R8, (R7) /* romvec passed in %i0==R8 */ + + /* turn off the cache but ensure ITBR enabled */ + MOVW (R0, MMUASI), R7 + ANDN $(ENABCACHE|ITBRDISABLE), R7 + MOVW R7, (R0, MMUASI) + + FLUSH + NOOP + + + MOVW $MID, R7 /* disable Sbus DMA */ + MOVW (R7, PHYSASI), R8 + ANDN $(0x1E<<15), R8 + MOVW R8, (R7, PHYSASI) + + MOVW $BOOTSTACK, R1 + + MOVW $(SPL(0xF)|PSREF|PSRSUPER), R7 + OR $PSRET, R7 /* allow rom use while debugging ... */ + MOVW R7, PSR + + MOVW $(0x35<<22), R7 /* NVM OFM DZM NS */ + MOVW R7, fsr+0(SB) + MOVW $fsr+0(SB), R8 + MOVW (R8), FSR + FMOVD $0.5, F26 /* 0.5 -> F26 */ + FSUBD F26, F26, F24 /* 0.0 -> F24 */ + FADDD F26, F26, F28 /* 1.0 -> F28 */ + FADDD F28, F28, F30 /* 2.0 -> F30 */ + + FMOVD F24, F0 + FMOVD F24, F2 + FMOVD F24, F4 + FMOVD F24, F6 + FMOVD F24, F8 + FMOVD F24, F10 + FMOVD F24, F12 + FMOVD F24, F14 + FMOVD F24, F16 + FMOVD F24, F18 + FMOVD F24, F20 + FMOVD F24, F22 + + MOVW $mach0(SB), R(MACH) +/* MOVW $0x8, R7 /**/ + MOVW R0, WIM + + JMPL main(SB) + MOVW (R0), R0 + RETURN + +TEXT call0(SB), $-4 + JMPL (R0) + JMPL (R0) + JMPL (R0) + JMPL (R0) + RETURN + +TEXT getcinfo(SB), $0 + MOVW (R7, 0x0C), R7 + RETURN + +TEXT flushiline(SB), $0 + MOVW R0, (R7, 0x0C) + NOOP + RETURN + +TEXT flushdline(SB), $0 + MOVW R0, (R7, 0x0E) + NOOP + RETURN + +TEXT getphys(SB), $0 + + MOVW (R7, PHYSASI), R7 + RETURN + +TEXT putphys(SB), $0 + + MOVW 4(FP), R8 + MOVW R8, (R7, PHYSASI) + RETURN + +TEXT getrmmu(SB), $0 + + MOVW (R7, MMUASI), R7 + RETURN + +TEXT putrmmu(SB), $0 + + MOVW 4(FP), R8 + MOVW R8, (R7, MMUASI) + RETURN + +TEXT getpcr(SB), $0 + + MOVW (R0, MMUASI), R7 + RETURN + +TEXT setpcr(SB), $0 + + MOVW R7, (R0, MMUASI) + FLUSH /* `strongly recommended' after STA to PCR */ + NOOP + RETURN + +TEXT _tas(SB), $0 + + TAS (R7), R7 /* LDSTUB, thank you ken */ + RETURN + +#ifdef notdef +TEXT _tas(SB), $0 /* it seems we must be splhi */ + + MOVW PSR, R8 + MOVW $SYSPSR, R9 + MOVW R9, PSR + NOOP + TAS (R7), R7 /* LDSTUB, thank you ken */ + MOVW R8, PSR + NOOP + RETURN +#endif + +TEXT softtas(SB), $0 /* all software; avoid LDSTUB */ + + MOVW PSR, R8 + MOVW $SYSPSR, R9 + MOVW R9, PSR + NOOP + MOVB (R7), R10 + CMP R10, R0 + BE gotit + /* cache is write through, no need to flush */ + MOVW $0xFF, R7 + MOVW R8, PSR + NOOP + RETURN + +gotit: + MOVW $0xFF, R10 + MOVB R10, (R7) + /* cache is write through, no need to flush */ + MOVW $0, R7 + MOVW R8, PSR + NOOP + RETURN + +TEXT spllo(SB), $0 + + MOVW PSR, R7 + MOVW R7, R10 + ANDN $SPL(15), R10 + MOVW R10, PSR + NOOP + RETURN + +TEXT splhi(SB), $0 + + MOVW R15, 4(R(MACH)) /* save PC in m->splpc */ + MOVW PSR, R7 + MOVW R7, R10 + OR $SPL(15), R10 + MOVW R10, PSR + NOOP + RETURN + +TEXT splxpc(SB), $0 + + MOVW R7, PSR /* BUG: book says this is buggy */ + NOOP + RETURN + + +TEXT splx(SB), $0 + + MOVW R15, 4(R(MACH)) /* save PC in m->splpc */ + MOVW R7, PSR /* BUG: book says this is buggy */ + NOOP + RETURN +TEXT spldone(SB), $0 + + RETURN + +TEXT rfnote(SB), $0 + + MOVW R7, R1 /* 1st arg is &uregpointer */ + ADD $4, R1 /* point at ureg */ + JMP restore + +TEXT traplink(SB), $-4 + + /* R8 to R23 are free to play with */ + /* R17 contains PC, R18 contains nPC */ + /* R19 has PSR loaded from vector code */ + + ANDCC $PSRPSUPER, R19, R0 + BE usertrap + +kerneltrap: + /* + * Interrupt or fault from kernel + */ + ANDN $7, R1, R20 /* dbl aligned */ + MOVW R1, (0-(4*(32+6))+(4*1))(R20) /* save R1=SP */ + /* really clumsy: store these in Ureg so can be restored below */ + MOVW R2, (0-(4*(32+6))+(4*2))(R20) /* SB */ + MOVW R5, (0-(4*(32+6))+(4*5))(R20) /* USER */ + MOVW R6, (0-(4*(32+6))+(4*6))(R20) /* MACH */ + SUB $(4*(32+6)), R20, R1 + +trap1: + MOVW Y, R20 + MOVW R20, (4*(32+0))(R1) /* Y */ + MOVW TBR, R20 + MOVW R20, (4*(32+1))(R1) /* TBR */ + AND $~0x1F, R19 /* force CWP=0 */ + MOVW R19, (4*(32+2))(R1) /* PSR */ + MOVW R18, (4*(32+3))(R1) /* nPC */ + MOVW R17, (4*(32+4))(R1) /* PC */ + MOVW R0, (4*0)(R1) + MOVW R3, (4*3)(R1) + MOVW R4, (4*4)(R1) + MOVW R7, (4*7)(R1) + RESTORE R0, R0 + /* now our registers R8-R31 are same as before trap */ + /* save registers two at a time */ + MOVD R8, (4*8)(R1) + MOVD R10, (4*10)(R1) + MOVD R12, (4*12)(R1) + MOVD R14, (4*14)(R1) + MOVD R16, (4*16)(R1) + MOVD R18, (4*18)(R1) + MOVD R20, (4*20)(R1) + MOVD R22, (4*22)(R1) + MOVD R24, (4*24)(R1) + MOVD R26, (4*26)(R1) + MOVD R28, (4*28)(R1) + MOVD R30, (4*30)(R1) + /* SP and SB and u and m are already set; away we go */ + MOVW R1, R7 /* pointer to Ureg */ + SUB $8, R1 + MOVW $SYSPSR, R8 + MOVW R8, PSR + NOOP + JMPL trap(SB) + + ADD $8, R1 +restore: + MOVW (4*(32+2))(R1), R8 /* PSR */ + MOVW R8, PSR + NOOP + + MOVD (4*30)(R1), R30 + MOVD (4*28)(R1), R28 + MOVD (4*26)(R1), R26 + MOVD (4*24)(R1), R24 + MOVD (4*22)(R1), R22 + MOVD (4*20)(R1), R20 + MOVD (4*18)(R1), R18 + MOVD (4*16)(R1), R16 + MOVD (4*14)(R1), R14 + MOVD (4*12)(R1), R12 + MOVD (4*10)(R1), R10 + MOVD (4*8)(R1), R8 + SAVE R0, R0 + MOVD (4*6)(R1), R6 + MOVD (4*4)(R1), R4 + MOVD (4*2)(R1), R2 + MOVW (4*(32+0))(R1), R20 /* Y */ + MOVW R20, Y + MOVW (4*(32+4))(R1), R17 /* PC */ + MOVW (4*(32+3))(R1), R18 /* nPC */ + MOVW (4*1)(R1), R1 /* restore R1=SP */ + RETT R17, R18 + +usertrap: + /* + * Interrupt or fault from user + */ + MOVW R1, R8 + MOVW R2, R9 + MOVW $setSB(SB), R2 + MOVW $(USERADDR+BY2PG), R1 + MOVW R8, (0-(4*(32+6))+(4*1))(R1) /* save R1=SP */ + MOVW R9, (0-(4*(32+6))+(4*2))(R1) /* save R2=SB */ + MOVW R5, (0-(4*(32+6))+(4*5))(R1) /* save R5=USER */ + MOVW R6, (0-(4*(32+6))+(4*6))(R1) /* save R6=MACH */ + MOVW $USERADDR, R(USER) + MOVW $mach0(SB), R(MACH) + SUB $(4*(32+6)), R1 + JMP trap1 + +TEXT puttbr(SB), $0 + MOVW R7, TBR + NOOP + RETURN + +TEXT gettbr(SB), $0 + + MOVW TBR, R7 + RETURN + +TEXT r1(SB), $0 + + MOVW R1, R7 + RETURN + +TEXT getwim(SB), $0 + + MOVW WIM, R7 + RETURN + +TEXT setlabel(SB), $0 + + MOVW R1, (R7) + MOVW R15, 4(R7) + MOVW $0, R7 + RETURN + +TEXT gotolabel(SB), $0 + + MOVW (R7), R1 + MOVW 4(R7), R15 + MOVW $1, R7 + RETURN + +TEXT getpsr(SB), $0 + + MOVW PSR, R7 + RETURN + +TEXT setpsr(SB), $0 + + MOVW R7, PSR + NOOP + RETURN + +TEXT savefpregs(SB), $0 + + MOVD F0, (0*4)(R7) + MOVD F2, (2*4)(R7) + MOVD F4, (4*4)(R7) + MOVD F6, (6*4)(R7) + MOVD F8, (8*4)(R7) + MOVD F10, (10*4)(R7) + MOVD F12, (12*4)(R7) + MOVD F14, (14*4)(R7) + MOVD F16, (16*4)(R7) + MOVD F18, (18*4)(R7) + MOVD F20, (20*4)(R7) + MOVD F22, (22*4)(R7) + MOVD F24, (24*4)(R7) + MOVD F26, (26*4)(R7) + MOVD F28, (28*4)(R7) + MOVD F30, (30*4)(R7) + MOVW FSR, (34*4)(R7) + + MOVW PSR, R8 + ANDN $PSREF, R8 + MOVW R8, PSR + RETURN + +TEXT savefsr(SB), $0 + MOVW FSR, 0(R7) + RETURN + +TEXT restfsr(SB), $0 + MOVW 0(R7), FSR + RETURN + + +TEXT fpinit(SB), $0 + MOVW PSR, R8 + OR $PSREF, R8 + MOVW R8, PSR + RETURN + +TEXT disabfp(SB), $0 + + MOVW PSR, R8 + ANDN $PSREF, R8 + MOVW R8, PSR + RETURN + +TEXT restfpregs(SB), $0 + + MOVW PSR, R8 + OR $PSREF, R8 + MOVW R8, PSR + + NOOP /* wait for PSR to quiesce */ + + + MOVD (0*4)(R7), F0 + MOVD (2*4)(R7), F2 + MOVD (4*4)(R7), F4 + MOVD (6*4)(R7), F6 + MOVD (8*4)(R7), F8 + MOVD (10*4)(R7), F10 + MOVD (12*4)(R7), F12 + MOVD (14*4)(R7), F14 + MOVD (16*4)(R7), F16 + MOVD (18*4)(R7), F18 + MOVD (20*4)(R7), F20 + MOVD (22*4)(R7), F22 + MOVD (24*4)(R7), F24 + MOVD (26*4)(R7), F26 + MOVD (28*4)(R7), F28 + MOVD (30*4)(R7), F30 + MOVW (34*4)(R7), FSR + + ANDN $PSREF, R8 + MOVW R8, PSR + RETURN + +TEXT getfpq(SB), $0 + + MOVW R7, R8 /* must be D aligned */ + MOVW $fsr+0(SB), R9 + MOVW $0, R7 +getfpq1: + MOVW FSR, (R9) + MOVW (R9), R10 + ANDCC $(1<<13), R10 /* queue not empty? */ + BE getfpq2 + MOVW (R8), R0 /* SS2 bug fix */ + MOVD FQ, (R8) + ADD $1, R7 + ADD $8, R8 + BA getfpq1 +getfpq2: + RETURN + +TEXT getfsr(SB), $0 + MOVW $fsr+0(SB), R7 + MOVW FSR, (R7) + MOVW (R7), R7 + RETURN + +TEXT clearftt(SB), $0 + MOVW R7, fsr+0(SB) + MOVW $fsr+0(SB), R7 + MOVW (R7), FSR + FMOVF F0, F0 + RETURN + +TEXT getcallerpc(SB), $-4 + MOVW 0(R1), R7 + RETURN + +TEXT icflush(SB), $-4 +JMPL (R0) + MOVW R0, (R0, IFLUSHASI) + FLUSH /* flush prefetch */ + NOOP + RETURN + +TEXT dcflush(SB), $0 + + MOVW R0, (R0, DFLUSHASI) /* can only flush the lot */ + RETURN + +TEXT flushtlbpage(SB), $0 + + AND $(~(BY2PG-1)), R7 /* type 0 */ + MOVW R0, (R7, TLBASI) + RETURN + +TEXT flushtlbctx(SB), $0 + + MOVW $0x300, R7 + MOVW R0, (R7, TLBASI) + RETURN + +TEXT flushtlb(SB), $0 + + MOVW $0x400, R7 + MOVW R0, (R7, TLBASI) + RETURN + +GLOBL mach0+0(SB), $MACHSIZE +GLOBL fsr+0(SB), $BY2WD + +/* + * Interface to OPEN BOOT ROM. Must save and restore state because + * of different calling conventions. We don't use it, but it's here + * for reference.. + */ + +TEXT call_openboot(SB), $16 + MOVW R1, R14 /* save my SP in their SP */ + MOVW R2, sb-4(SP) + MOVW R(MACH), mach-8(SP) + MOVW R(USER), user-12(SP) + MOVW param1+4(FP), R8 + MOVW param2+8(FP), R9 + MOVW param3+12(FP), R10 + MOVW param4+16(FP), R11 + JMPL (R7) + MOVW R14, R1 /* restore my SP */ + MOVW user-12(SP), R(USER) + MOVW mach-8(SP), R(MACH) + MOVW sb-4(SP), R2 + MOVW R8, R7 /* move their return value into mine */ + RETURN diff --git a/os/js/main.c b/os/js/main.c new file mode 100644 index 00000000..d539ca11 --- /dev/null +++ b/os/js/main.c @@ -0,0 +1,564 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "version.h" + +Mach *m = &mach0; +Proc *up; +int cflag; + +ulong cachetable[1024]; + +Sysint *sysintr; +struct { + uchar format; + uchar type; + uchar ea[6]; + uchar pad[32-8]; +} idprom; + +int cpuserver; +ulong bank[8]; +uchar mempres[64]; +char fbstr[32]; +ulong fbslot; +int usecg6; +Label catch; +uchar *sp; + +int cold=1; + +typedef struct Sysparam Sysparam; +struct Sysparam +{ + int id; /* Model type from id prom */ + char *name; /* System name */ + char ss2; /* Is Sparcstation 2? */ + int vacsize; /* Cache size */ + int vacline; /* Cache line size */ + int ncontext; /* Number of MMU contexts */ + char cachebug; /* Machine needs cache bug work around */ + int nbank; /* Number of banks of memory */ + int banksize; /* Maximum Mbytes per bank */ + int pcnt; /* percent of mem for kernel? */ +} +sysparam[] = +{ + { 0xFF, "unknown Sun4M",0, 0, 0, 64, 0, 4, 32 ,0}, + { 0x80, "JavaStation uSparcII",0, 0, 0, 256, 0, 4, 32 ,2}, + { 0 } +}; +Sysparam *sparam; + +void +doc(char *m) +{ + print("%s\n", m); +} + +static void poolsizeinit(void); + +void +main(void) +{ + + + machinit(); + trapinit(); + quotefmtinstall(); + confinit(); + xinit(); + mmuinit(); + intrinit(); + clockinit(); + printinit(); + screeninit(); + ioinit(); + doc("ioinit..."); + ns16552install(); + poolsizeinit(); + doc("ns16552install..."); + kbdinit(); + doc("kbdinit..."); + cacheinit(); + doc("cacheinit..."); + procinit(); + doc("procinit..."); + putphys(MID, 0x1F<<16); /* enable arbitration */ + links(); + doc("links"); + chandevreset(); + doc("chandevreset..."); + + print("\nInferno Operating System\n"); + print("%s-%s \n\n",VERSION, conffile); + print("JIT Compilation Mode = %d\n",cflag); + + userinit(); + doc("userinit..."); + + /* clear pending processor interrupts */ + putphys(PROCINTCLR, (~0<<17)|(1<<15)); + print("berore schedinit\n"); + schedinit(); +} + +extern int main_pool_pcnt; +extern int heap_pool_pcnt; +extern int image_pool_pcnt; + +static void +poolsizeinit(void) +{ + ulong nb = conf.npage*BY2PG; + + print("Total memory available: %ld K\n",nb/1024); + poolsize(mainmem, (nb*main_pool_pcnt)/100, 0); + poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0); + poolsize(imagmem, (nb*image_pool_pcnt)/100, 1); +} + +void +intrinit(void) +{ + KMap *k; + + /* clear fault status */ + getphys(AFSR); + + k = kmappa(SYSINTR, PTEIO|PTENOCACHE); + sysintr = (Sysint*)VA(k); + + /* mask all interrupts */ + sysintr->maskset = ~0; + + /* allow these */ + sysintr->maskclr=MaskAllIntr|MEIntr|MSIIntr|EMCIntr|EtherIntr|KbdIntr; + + /* clear pending processor interrupts */ + putphys(PROCINTCLR, (~0<<17)|(1<<15)); + +} + +void +systemreset(void) +{ + microdelay(200); + putphys(SYSCTL, getphys(SYSCTL)|1); /* power on reset */ +} + +void +machinit(void) +{ + memset(m, 0, sizeof(Mach)); +} + +void +ioinit(void) +{ + KMap *k; + uchar *sindex; /* superio index */ + uchar *sdata; /* superio data */ + uchar *mkctl; /* superio mouse/kbd ctl register */ + uchar *mkdata; /* superio mouse/kbd data register */ + + + /* enable the uart's on the superio chip */ + k = kmappa(SUPERIO_PHYS_PAGE, PTEIO|PTENOCACHE); + sindex = (uchar*)(VA(k)+SUPERIO_INDEX_OFFSET); + sdata = (uchar*)(VA(k)+SUPERIO_DATA_OFFSET); + mkdata = (uchar*)(VA(k)+SUPERIO_MOUSE_KBD_DATA_PORT); + mkctl = (uchar*)(VA(k)+SUPERIO_MOUSE_KBD_CTL_PORT); + + superioinit(VA(k),sindex,sdata,mkctl,mkdata); + doc("superioinit..."); +} + +void +init0(void) +{ + Osenv *o; + + up->nerrlab = 0; + + print("before spllo"); + + spllo(); + + print("Sun Sparc %s\n", sparam->name); + print("bank 0: %ldM 1: %ldM\n", bank[0], bank[1]); + print("frame buffer id %lux slot %ld %s\n",conf.monitor,fbslot,fbstr); + + + if(waserror()) + panic("init0"); + + /* + * These are o.k. because rootinit is null. + * Then early kproc's will have a root and dot. + */ + o = up->env; + o->pgrp->slash = namec("#/", Atodir, 0, 0); + cnameclose(o->pgrp->slash->name); + o->pgrp->slash->name = newcname("/"); + o->pgrp->dot = cclone(o->pgrp->slash); + + chandevinit(); + poperror(); + disinit("/osinit.dis"); +} + + +void +userinit(void) +{ + Proc *p; + Osenv *o; + + p = newproc(); + o = p->env; + + o->fgrp = newfgrp(nil); + o->pgrp = newpgrp(); + kstrdup(&o->user, eve); + strcpy(p->text,"interp"); + + p->fpstate = FPINIT; + fpinit(); + + /* + * Kernel Stack + */ + p->sched.pc = (ulong)init0; + p->sched.sp = (ulong)p->kstack+KSTACK-8; + p->sched.sp &= ~7; /* SP must be 8-byte aligned */ + + ready(p); +} + +uchar * +pusharg(char *p) +{ + int n; + + n = strlen(p)+1; + sp -= n; + memmove(sp, p, n); + return sp; +} + +void +exit(int ispanic) +{ + USED(ispanic); + + spllo(); + print("cpu exiting\n"); + + /* Shutdown running devices */ + chandevshutdown(); + + microdelay(500); + systemreset(); +} + +void +reboot(void) +{ + exit(0); +} + +void +halt(void) +{ + spllo(); + print("cpu halted\n"); + microdelay(500); + for(;;); +} + +int +probemem(ulong addr) +{ + ulong pcr, save0; + int works; + + save0 = getphys(0); + pcr = getpcr()|NOFAULT; + works = 0; + setpcr(pcr & ~MEMPCHECK); + putphys(addr, ~addr); + if(addr) + putphys(0, 0x89ABCDEF); + if(getphys(addr) == ~addr){ + setpcr(pcr); + putphys(addr, addr); + if(addr) + putphys(0, 0x89ABCDEF); + if(getphys(addr) == addr) + works = 1; + } + setpcr(pcr & ~NOFAULT); + putphys(0, save0); + getphys(AFSR); /* clear fault status */ + getrmmu(SFSR); /* clear fault status */ + return works; +} + +/* + * this assumes that if a bank is not empty, + * its first slot is filled. + * + * ../port/alloc.c and ../port/page.c + * need to be changed to support more than two banks. + */ +void +scanbank(ulong base, uchar *mempres, int n) +{ + int i; + ulong addr, npg; + + npg = 0; + for(i=0; i sizeof(ulong)) + j = sizeof(ulong); + memmove((uchar*)d+i, &w, j); + } +} + +Conf conf; + +void +confinit(void) +{ + ulong i; + ulong ktop; + + conf.monitor = 0; + + conf.nmach = 1; + if(conf.nmach > MAXMACH) + panic("confinit"); + + /* fetch ID prom */ + physcopyin(&idprom, NVR_PHYS+IDOFF, sizeof(idprom)); + if(idprom.format!=1 || (idprom.type&0xF0)!=0x80) + *(ulong*)~0 = 0; /* not a new generation sparc; die! */ + + for(sparam = sysparam; sparam->id; sparam++) + if(sparam->id == idprom.type) + break; + + /* First entry in the table is the default */ + if(sparam->id == 0) + sparam = sysparam; + + conf.ss2 = sparam->ss2; + conf.vacsize = sparam->vacsize; + conf.vaclinesize = sparam->vacline; + conf.ncontext = sparam->ncontext; + conf.ss2cachebug = sparam->cachebug; + + for(i=0; inbank; i++) + if(probemem(i*sparam->banksize*MB)) + scanbank(i*sparam->banksize*MB, mempres, + sparam->banksize); + + bank[0] = conf.npage0*BY2PG/MB; + bank[1] = conf.npage1*BY2PG/MB; + + if(bank[1] == 0){ + /* + * This split of memory into 2 banks fools the allocator into + * allocating low memory pages from bank 0 for the ethernet + * since it has only a 24bit address *counter. + * NB. Suns must have at LEAST 8Mbytes. + */ + conf.npage1 = conf.npage0 - (8*MB)/BY2PG; + conf.base1 = conf.base0 + 8*MB; + conf.npage0 = (8*MB)/BY2PG; + bank[1] = bank[0]-8; + bank[0] = 8; + } + + conf.npage = conf.npage0+conf.npage1; + + ktop = PGROUND((ulong)end); + ktop = PADDR(ktop); + conf.npage0 -= ktop/BY2PG; + conf.base0 += ktop; + + conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5; + conf.copymode = 0; /* copy on write */ + conf.arp = 32; + conf.ialloc = (((conf.npage*(100-sparam->pcnt))/100)/2)*BY2PG; + + eve = strdup("inferno"); + +#ifdef notdef + /* XXX - Eric - Autoconfigure memory */ + /* XXX - Tad: 8 eigths, total... */ + mainmem->maxsize = (conf.npage*BY2PG)/8; + heapmem->maxsize = ((conf.npage*BY2PG)*5)/8; + imagmem->maxsize = ((conf.npage*BY2PG)*2)/8; +#endif +} + +/* + * set up the lance + */ +void +lancesetup(Lance *lp) +{ + KMap *k; + DMAdev *dma; + ulong pa, va; + int i; + + k = kmappa(ETHER, PTEIO|PTENOCACHE); + lp->rdp = (void*)(VA(k)+0); + lp->rap = (void*)(VA(k)+2); + for(i=0; i<6; i++) + lp->ea[i] = idprom.ea[i]; + + lp->lognrrb = 7; + lp->logntrb = 7; + lp->nrrb = 1<lognrrb; + lp->ntrb = 1<logntrb; + lp->sep = 1; + lp->busctl = BSWP | ACON | BCON; + + /* + * Allocate area for lance init block and descriptor rings + */ + pa = PADDR(xspanalloc(BY2PG, BY2PG, 0)); + + /* map at LANCESEGM */ + va = kmapdma(pa, BY2PG); + lp->lanceram = (ushort*)va; + lp->lm = (Lancemem*)va; + + /* + * Allocate space in host memory for the io buffers. + */ + i = (lp->nrrb+lp->ntrb)*sizeof(Lancepkt); + i = (i+(BY2PG-1))/BY2PG; + pa = PADDR(xspanalloc(i*BY2PG, BY2PG, 0)); + va = kmapdma(pa, i*BY2PG); + + lp->lrp = (Lancepkt*)va; + lp->rp = (Lancepkt*)va; + lp->ltp = lp->lrp+lp->nrrb; + lp->tp = lp->rp+lp->nrrb; + + k = kmappa(DMA, PTEIO|PTENOCACHE); + dma = (DMAdev*)VA(k); + dma->base = 0xff; + + /* + * for now, let's assume the ROM has left the results of its + * auto-sensing + */ +#ifdef notdef + if(dma->ecsr & E_TP_select) + print("Twisted pair ethernet\n"); + else + print("AUI ethernet\n"); +#endif + microdelay(1); + dma->ecsr |= E_Int_en|E_Invalidate|E_Dsbl_wr_inval|E_Dsbl_rd_drn; + microdelay(1); +} + +static void +linkproc(void) +{ + spllo(); + (*up->kpfun)(up->arg); +} + +void +kprocchild(Proc *p, void (*func)(void*), void *arg) +{ + p->sched.pc = (ulong)linkproc; + p->sched.sp = (ulong)p->kstack+KSTACK-8; + + p->kpfun = func; + p->arg = arg; +} + + +void +FPsave(void *f) /* f should be a FPenv */ +{ + savefsr(f); +} + +void +FPrestore(void *f) /* f should be a FPenv */ +{ + restfsr(f); +} + +void +fpsave(FPU *f) +{ + savefpregs( f ); +} + +void +fprestore(FPU *f) +{ + restfpregs(f); +} + +int +islo(void) +{ + int val; + val = (getpsr()&SPL(15)) == 0; + + return val; +} + +void +setvec(void) +{ + /* XXX - Tad: eventually implement this */ +} diff --git a/os/js/mem.h b/os/js/mem.h new file mode 100644 index 00000000..f17c1782 --- /dev/null +++ b/os/js/mem.h @@ -0,0 +1,149 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ + +/* + * Sizes + */ + +#define BI2BY 8 /* bits per byte */ +#define BI2WD 32 /* bits per word */ +#define BY2WD 4 /* bytes per word */ +#define BY2V 8 /* bytes per double word */ +#define BY2PG 4096 /* bytes per page */ +#define WD2PG (BY2PG/BY2WD) /* words per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1)) +#define PGROUND(s) ROUND(s,BY2PG) + +#define MAXMACH 1 /* max # cpus system can run */ + +/* + * Time + */ +#define HZ 50 /* clock frequency */ +#define MS2HZ (1000/HZ) /* millisec per clock tick */ +#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ +#define MS2TK(t) ((((ulong)(t))*HZ)/1000) /* milliseconds to ticks */ + +/* + * PSR bits + */ +#define PSREC 0x00002000 +#define PSREF 0x00001000 +#define PSRSUPER 0x00000080 +#define PSRPSUPER 0x00000040 +#define PSRET 0x00000020 +#define SPL(n) (n<<8) + +/* + * Magic registers + */ + +#define MACH 6 /* R6 is m-> */ +#define USER 5 /* R5 is u-> */ + +/* + * Fundamental addresses + */ + +#define USERADDR 0xE0000000 +#define UREGADDR (USERADDR+BY2PG-((32+6)*BY2WD)) +#define BOOTSTACK (KTZERO-0*BY2PG) +#define TRAPS (KTZERO-2*BY2PG) + +/* + * Reference MMU registers (ASI 4) + */ +#define PCR 0x000 +#define CTPR 0x100 +#define CXR 0x200 +#define SFSR 0x300 +#define SFAR 0x400 + +/* + * Processor Control Register + */ +#define ITBRDISABLE (1<<16) +#define BOOTMODE (1<<14) /* `must be cleared for normal operation' */ +#define MEMPCHECK (1<<12) /* check parity */ +#define ENABCACHE (3<<8) /* I & D caches */ +#define NOFAULT (1<<1) /* no fault */ + +/* + * special MMU regions + * DMA segment for SBus DMA mapping via I/O MMU (hardware fixes location) + * the frame buffer is mapped as one MMU region (16 Mbytes) + * IO segments for device register pages etc. + */ +#define DMARANGE 0 +#define DMASEGSIZE ((16*MB)<>4)&0x7FFFFF0) +#define PPT(pn) ((ulong*)KADDR((((ulong)(pn)&~0xF)<<4))) + +/* + * Virtual addresses + */ +#define VTAG(va) ((va>>22)&0x03F) +#define VPN(va) ((va>>13)&0x1FF) + +/* + * Address spaces + */ +#define KZERO 0xE0000000 /* base of kernel address space */ +#define KTZERO (KZERO+4*BY2PG) /* first address in kernel text */ +#define KSTACK 8192 /* size of kernel stack */ + +#define MACHSIZE 4096 + +/* + * control registers in physical address space (ASI 20) + */ +#define IOCR 0x10000000 /* IO MMU control register */ +#define IBAR 0x10000004 /* IO MMU page table base address */ +#define AFR 0x10000018 /* address flush register */ +#define AFSR 0x10001000 /* asynch fault status */ +#define AFAR 0x10001004 /* asynch fault address */ +#define SSCR(i) (0x10001010+(i)*4) /* Sbus slot i config register */ +#define MFSR 0x10001020 /* memory fault status register */ +#define MFAR 0x10001024 /* memory fault address register */ +#define MID 0x10002000 /* sbus arbitration enable */ + +#define SYSCTL 0x71F00000 /* system control & reset register */ +#define PROCINTCLR 0x71E00004 /* clear pending processor interrupts */ + +/* + * IO MMU page table entry + */ +#define IOPTEVALID (1<<1) +#define IOPTEWRITE (1<<2) diff --git a/os/js/mkfile b/os/js/mkfile new file mode 100644 index 00000000..1640eb9e --- /dev/null +++ b/os/js/mkfile @@ -0,0 +1,77 @@ +SYSTARG=Inferno +OBJTYPE=sparc +<../../mkconfig + +#Configurable parameters + +CONF=js #default configuration +CONFLIST=js + +SYSTARG=$OSTARG +OBJTYPE=sparc +INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin #path of directory where kernel is installed + +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS + +OBJ=\ + l.$O\ + clock.$O\ + main.$O\ + mmu.$O\ + fsv.$O\ + screen.$O\ + trap.$O\ + rom.$O\ + iob.$O\ + superio.$O\ + kbd.$O\ + $CONF.root.$O\ + $IP\ + $DEVS\ + $ETHERS\ + $LINKS\ + $VGAS\ + $PORT\ + $MISC\ + $OTHERS\ + +LIBNAMES=${LIBS:%=lib%.a} +#LIBDIRS=$LIBS + +HFILES=\ + mem.h\ + dat.h\ + fns.h\ + io.h\ + audio.h\ + cs4231.h\ + ns16552.h\ + rom.h\ + screen.h\ + softcursor.h\ + ureg.h\ + +CFLAGS=-wFV -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp +KERNDATE=`{$NDATE} + +default:V: i$CONF + +i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD -M -o $target -H3 -T0xE0004000 -R0x4 -l $OBJ $CONF.$O $LIBFILES + +# "raw" version of kernel for binary comparison testing +i$CONF.raw: $OBJ $CONF.c $CONF.root.h $LIBNAMES + $CC $CFLAGS '-DKERNDATE='0 $CONF.c + $LD -s -M -o $target -H3 -T0xE0004000 -R0x4 -l $OBJ $CONF.$O $LIBFILES + +install:V: $INSTALLDIR/i$CONF $INSTALLDIR/i$CONF.raw + +<../port/portmkfile + +%.$O: io.h +clock.$O main.$O trap.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h diff --git a/os/js/mmu.c b/os/js/mmu.c new file mode 100644 index 00000000..4283fa79 --- /dev/null +++ b/os/js/mmu.c @@ -0,0 +1,252 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +typedef struct Ctx Ctx; +/* + * software description of an MMU context + */ +struct Ctx +{ + Ctx *prev; /* less recently used */ + Ctx *next; /* more recently used */ + Proc *proc; /* process that owns this context */ + ushort index; /* which context this is */ +}; + +ulong *ioptes; /* IO MMU's table (shared by all processors) */ + +/* offset of x into the three page table levels in a context */ +#define AOFF(x) (((ulong)x)>>24) +#define BOFF(x) ((((ulong)x)>>18)&(64-1)) +#define COFF(x) ((((ulong)x)>>12)&(64-1)) +#define ISPTAB(x) ((((ulong)x)&3) == PTPVALID) +#define KPN(va) PPN(PADDR(va)) + +#define NIOPTE (DMASEGSIZE/BY2PG) + +/* + * allocate kernel page map and enter one mapping. Return + * address of the mapping. + */ +static ulong* +putkmmu(ulong virt, ulong phys, int level) +{ + ulong *a, *b, *c; + + + a = &PPT(m->contexts[0])[AOFF(virt)]; + if(level > 1) { + if(*a == 0){ + b = (ulong*)xspanalloc(64*sizeof(ulong), + 64*sizeof(ulong), 0); + *a = KPN(b) | PTPVALID; + } else { + if(!ISPTAB(*a)) + panic("putkmmu virt=%lux *a=%lux", virt, *a); + b = PPT(*a); + } + b = &b[BOFF(virt)]; + if(level > 2) { + if(*b == 0){ + c = (ulong*)xspanalloc(64*sizeof(ulong), + 64*sizeof(ulong), 0); + *b = KPN(c) | PTPVALID; + } else { + if(!ISPTAB(*b)) + panic("putkmmu virt=%lux *b=%lux", + virt, *b); + c = PPT(*b); + } + c = &c[COFF(virt)]; + *c = phys; + return c; + } else { + *b = phys; + return b; + } + } else { + *a = phys; + return a; + } +} + +void +mmuinit(void) +{ + int i, n; + ulong *a; + + m->contexts = (ulong*)xspanalloc(conf.ncontext*sizeof(ulong), + conf.ncontext*sizeof(ulong), + 0); + + /* + * context 0 will have the prototype level 1 entries + */ + a = (ulong*)xspanalloc(256*sizeof(ulong), 256*sizeof(ulong), 0); + + m->contexts[0] = KPN(a) | PTPVALID; + + /* + * map all memory to KZERO + */ + n = 128*MB/BY2PG; + + /* pages to first segment boundary */ + for(i=0; i<(256*1024/BY2PG); i++) + putkmmu(KZERO|(i*BY2PG), + PPN(i*BY2PG)|PTEKERNEL|PTEWRITE|PTEVALID|PTECACHE, 3); + + /* segments to first 16Mb boundary */ + for(; i<(16*MB)/BY2PG; i += 64) + putkmmu(KZERO|(i*BY2PG), + PPN(i*BY2PG)|PTEKERNEL|PTEWRITE|PTEVALID|PTECACHE, 2); + + /* 16 Mbyte regions to end */ + for(; icontexts)>>4); + putrmmu(CXR, 0); + flushtlb(); + + ioptes = (ulong*)xspanalloc(NIOPTE*sizeof(ulong), DMASEGSIZE/1024, 0); + putphys(IBAR, PADDR(ioptes)>>4); + putphys(IOCR, (DMARANGE<<2)|1); /* IO MMU enable */ +} + + +void +flushicache(void) +{ + int i; + ulong addr = 0; + + for(i=0;i<512;i++) { + flushiline(addr); + addr += 1<<5; + } +} + +void +flushdcache(void) +{ + int i; + ulong addr = 0; + + for(i=0;i<512;i++) { + flushdline(addr); + addr += 1<<5; + } +} + +int +segflush(void *p, ulong l) +{ + USED(p,l); + flushicache(); + return 0; +} + +void +cacheinit(void) +{ + flushdcache(); + flushicache(); + setpcr(getpcr()|ENABCACHE); +} + +typedef struct Mregion Mregion; +struct Mregion +{ + ulong addr; + long size; +}; + +struct +{ + Mregion io; + Mregion dma; + Lock; +}kmapalloc = { + {IOSEGBASE, IOSEGSIZE}, + {DMASEGBASE, DMASEGSIZE}, +}; + +void +kmapinit(void) +{ +} + +KMap* +kmappa(ulong pa, ulong flag) +{ + ulong k; + + lock(&kmapalloc); + k = kmapalloc.io.addr; + kmapalloc.io.addr += BY2PG; + if((kmapalloc.io.size -= BY2PG) < 0) + panic("kmappa"); + putkmmu(k, PPN(pa)|PTEKERNEL|PTEWRITE|PTEVALID|flag, 3); + flushtlbpage(k); + unlock(&kmapalloc); + return (KMap*)k; +} + +ulong +kmapdma(ulong pa, ulong n) +{ + ulong va0, va; + int i, j; + + + lock(&kmapalloc); + i = (n+(BY2PG-1))/BY2PG; + va0 = kmapalloc.dma.addr; + kmapalloc.dma.addr += i*BY2PG; + if((kmapalloc.dma.size -= i*BY2PG) <= 0) + panic("kmapdma"); + va = va0; + for(j=0; j>PGSHIFT)&(NIOPTE-1)] = PPN(pa)|IOPTEVALID|IOPTEWRITE; + va += BY2PG; + pa += BY2PG; + } + unlock(&kmapalloc); + return va0; +} + +/* + * map the frame buffer + */ +ulong +kmapsbus(int slot) +{ + int i, n; + + lock(&kmapalloc); + n = FBSEGSIZE/BY2PG; + for(i=0; iport + r, (u)->sticky[r] | (v)) +#define uartrdreg(u,r) inb((u)->port + r) + +void ns16552setup(ulong, ulong, char*); + +static void +uartpower(int, int) +{ +} + +/* + * handle an interrupt to a single uart + */ +static void +ns16552intrx(Ureg *ur, void *arg) +{ + USED(ur); + + ns16552intr((ulong)arg); +} + +/* + * install the uarts (called by reset) + */ +void +ns16552install(void) +{ + static int already; + void uartclock(void); + + if(already) + return; + already = 1; + + /* first two ports are always there and always the normal frequency */ + ns16552setup(superiova()+TTYABase, UartFREQ, "eia0"); + ns16552special(0, 38400, &kbdq, &printq, kbdputc); + addclock0link(uartclock, 22); +} + +/* + * If the UART's receiver can be connected to a DMA channel, + * this function does what is necessary to create the + * connection and returns the DMA channel number. + * If the UART's receiver cannot be connected to a DMA channel, + * a -1 is returned. + */ +char +ns16552dmarcv(int dev) +{ + + USED(dev); + return -1; +} + +long +dmasetup(int,void*,long,int) +{ + return 0; +} + +void +dmaend(int) +{ +} + +int +dmacount(int) +{ + return 0; +} diff --git a/os/js/rom.c b/os/js/rom.c new file mode 100644 index 00000000..a477cfe5 --- /dev/null +++ b/os/js/rom.c @@ -0,0 +1,103 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +typedef struct Rom Rom; + +struct Rom +{ + uint magic; + uint version; + uint plugin_version; + uint monitor_id; + + void **physmemlist; + void **virtmemlist; + void **availphysmemlist; + void *config_info; + + char **bootcmd; + + uint (*open)(); + uint (*close)(); + + uint (*read_blocks)(); + uint (*write_blocks)(); + + uint (*transmit_pkt)(); + uint (*poll_pkt)(); + + uint (*read_bytes)(); + uint (*write_bytes)(); + uint (*seek)(); + + uchar *input; + uchar *output; + + uchar (*getchar)(); + uchar (*putchar)(); + uchar (*noblock_getchar)(); + uchar (*noblock_putchar)(); + + uchar (*fb_writestr)(char*); + + void (*boot)(char*); + + void (*printf)(char*,...); + + void (*some_kbd_thing)(); + int *ms_count; + void (*exit)(); + void (**vector)(); + void (**interpret)(char*,...); + void *bootparam; + uint (*mac_addr)(); + char **v2_bootpath; + char ** v2_bootargs; + int *v2_stdin; + int *v2_stdout; + void* (*v2_phandle)(); + char* (*v2_allocphys)(); + char* (*v2_freephys)(); + char* (*v2_map_dev)(); + char* (*v2_unmap_dev)(); + ulong (*v2_open)(); + uint (*v2_close)(); + uint (*v2_read)(); + uint (*v2_write)(); + uint (*v2_seek)(); + void (*v2_chain)(); + void (*v2_release)(); + char *(*v3_alloc)(); + int *reserved[14]; + void (*setctxsegmap)(); + int (*v3_startcpu)(); + int (*v3_stopcpu)(); + int (*v3_idlecpu)(); + int (*v3_resumecpu)(); +}; + +Rom *rom; /* open boot rom vector -- assigned by l.s */ + +void +prom_printf(char *format, ...) +{ + char buf[512]; + int l; + va_list ap; + + va_start(ap, format); + l = vseprint(buf,buf+sizeof(buf),format,ap) - buf; + va_end(ap); + + call_openboot(rom->v2_write,*rom->v2_stdout,buf,l); +} + +void +prom_halt(void) +{ + call_openboot(rom->exit,0xfeedface); +} diff --git a/os/js/rom.h b/os/js/rom.h new file mode 100644 index 00000000..ed4ee2cb --- /dev/null +++ b/os/js/rom.h @@ -0,0 +1,57 @@ +typedef struct ROM ROM; +typedef struct ROMconf ROMconf; + +struct ROM +{ + uint magic; + uint version; + uint plugversion; + uint monid; + uint pad1[3]; + ROMconf *conf; + uint pad2[17]; + void (*boot)(void*); + uint pad3[1]; + void (*enter)(void); + int *msec; + void (*exit)(void); + void (**callback)(void); + uint (*interpret)(void*); + uint pad4[2]; + char **bootpath; + char **bootargs; + uint *stdin; + uint *stdout; + uint (*phandle)(uint); + uint (*alloc)(void*, uint); + void (*free)(void*); + uint (*map)(void*, uint, uint, uint); + void (*unmap)(void*, uint); + uint (*open)(char*); + uint (*close)(uint); + uint (*read)(uint, void*, int); + uint (*write)(uint, void*, int); + uint (*seek)(uint, uint, uint); + void (*chain)(void*, uint, void*, void*, uint); + void (*release)(void*, uint); + uint pad4[15]; + void (*putcxsegm)(int, ulong, int); + int (*startcpu)(uint, uint, uint, uint); + int (*stopcpu)(uint); + int (*idlecpu)(uint); + int (*resumecpu)(uint); +}; + +struct ROMconf +{ + uint (*next)(uint); + uint (*child)(uint); + int (*getproplen)(uint, void*); + int (*getprop)(uint, void*, void*); + int (*setprop)(uint, void*, void*); + void* (*nextprop)(uint, void*); +}; + +#define ROMMAGIC 0x10010407 + +extern ROM *rom; diff --git a/os/js/screen.c b/os/js/screen.c new file mode 100644 index 00000000..17c12376 --- /dev/null +++ b/os/js/screen.c @@ -0,0 +1,483 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "io.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include +#include +#include +#include + +#include "softcursor.h" +#include "screen.h" + +#define Backgnd (0xFF) + + +ulong consbits = 0xC0; +Memdata consdata = { + nil, + &consbits +}; +Memimage conscol = +{ + { 0, 0, 1, 1 }, + { -100000, -100000, 100000, 100000 }, + 3, + 1, + &consdata, + 0, + 1 +}; + +ulong onesbits = ~0; +Memdata onesdata = { + nil, + &onesbits, +}; +Memimage xones = +{ + { 0, 0, 1, 1 }, + { -100000, -100000, 100000, 100000 }, + 3, + 1, + &onesdata, + 0, + 1 +}; +Memimage *memones = &xones; + +ulong zerosbits = 0; +Memdata zerosdata = { + nil, + &zerosbits, +}; +Memimage xzeros = +{ + { 0, 0, 1, 1 }, + { -100000, -100000, 100000, 100000 }, + 3, + 1, + &zerosdata, + 0, + 1 +}; +Memimage *memzeros = &xzeros; + +ulong backbits = (Backgnd<<24)|(Backgnd<<16)|(Backgnd<<8)|Backgnd; +Memdata backdata = { + nil, + &backbits +}; +Memimage xback = +{ + { 0, 0, 1, 1 }, + { -100000, -100000, 100000, 100000 }, + 3, + 1, + &backdata, + 0, + 1 +}; +Memimage *back = &xback; + +Video *vid; +static Memsubfont *memdefont; +static Lock screenlock; +Memimage gscreen; +Memdata gscreendata; +static Point curpos; +static Rectangle window; + +static Vctlr* vctlr; + +static Cursor arrow = { + { -1, -1 }, + { 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04, + 0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04, + 0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40, + }, + { 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0, + 0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8, + 0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8, + 0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00, + }, +}; + + +void +graphicscmap(int invert) +{ + int num, den, i, j; + int r, g, b, cr, cg, cb, v; + + if(vctlr->setcolor == nil) + return; + + for(r=0,i=0;r!=4;r++) for(v=0;v!=4;v++,i+=16){ + for(g=0,j=v-r;g!=4;g++) for(b=0;b!=4;b++,j++){ + den=r; + if(g>den) den=g; + if(b>den) den=b; + if(den==0) /* divide check -- pick grey shades */ + cr=cg=cb=v*17; + else{ + num=17*(4*den+v); + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + if(invert) + vctlr->setcolor(255-i-(j&15), + cr*0x01010101, + cg*0x01010101, + cb*0x01010101); + else + vctlr->setcolor(i+(j&15), + cr*0x01010101, + cg*0x01010101, + cb*0x01010101); + } + } +} + +static char s1[] = +{ + 0x00, 0x00, 0xC0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + + +void +dacinit(void) +{ + int i; + + /* Control registers */ + vid->addr = 0x01 << 24; + vid->color = 0x02 << 24; + for(i = 0; i < sizeof s1; i++) + vid->cntrl = s1[i] << 24; + + /* Cursor programming */ + vid->addr = 0x00 << 24; + vid->color = 0x03 << 24; + vid->cntrl = 0xC0 << 24; + for(i = 0; i < 12; i++) + vid->cntrl = 0 << 24; + + /* Load Cursor Ram */ + vid->addr = 0x00 << 24; + vid->color = 0x04 << 24; + for(i = 0; i < 0x400; i++) + vid->cntrl = 0xff << 24; + + graphicscmap(1); + + /* Overlay Palette Ram */ + vid->addr = 0x00 << 24; + vid->color = 0x01 << 24; + for(i = 0; i < 0x10; i++) { + vid->cntrl = 0xff << 24; + vid->cntrl = 0xff << 24; + vid->cntrl = 0xff << 24; + } + + /* Overlay Palette Ram */ + vid->addr = 0x81; + vid->color = 0x01; + for(i = 0; i < 3; i++) { + vid->cntrl = 0xff << 24; + vid->cntrl = 0xff << 24; + vid->cntrl = 0xff << 24; + } +} + +void +vctlrinit(int x, int y, int d) +{ + int h; + ulong va; + + if(vctlr == nil){ + /* + * find a controller somehow + * and call its init routine + */ + extern Vctlr FSV; + + vctlr = FSV.init(0, x, y, d); + vctlr->load(&arrow); + } + + if(vctlr == nil) + panic("%s",Ebadarg); + + gscreen.data = &gscreendata; + gscreen.r.min = Pt(0, 0); + gscreen.r.max = Pt(vctlr->x, vctlr->y); + gscreen.clipr = gscreen.r; + gscreen.ldepth = vctlr->d; + gscreen.repl = 0; + va = kmapsbus(FSVSLOT); /* FSV is in slot 2 */ + gscreendata.data = (ulong *)(va+0x800000); /* Framebuffer Magic */ + gscreen.width = (vctlr->x *(1<height; + + vid = (Video*)(va+0x240000); /* RAMDAC Magic */ + memset(gscreendata.data, Backgnd, vctlr->x*vctlr->y); + window = gscreen.r; + window.max.x = vctlr->x; + window.max.y = (vctlr->y/h) * h; + curpos = window.min; + if (gscreen.ldepth == 3){ + dacinit(); + } + + memset(gscreendata.data, Backgnd, vctlr->x*vctlr->y); + window = gscreen.r; + window.max.x = vctlr->x; + window.max.y = (vctlr->y/h) * h; + curpos = window.min; +} + +void +screeninit(void) +{ + memdefont = getmemdefont(); + vctlrinit(1024, 768, 3); +} + +ulong* +attachscreen(Rectangle *r, int *ld, int *width, int *softscreen) +{ + *r = gscreen.r; + *ld = gscreen.ldepth; + *width = gscreen.width; + *softscreen = 0; + return gscreendata.data; +} + +void +detachscreen(void) +{ +} + +void +flushmemscreen(Rectangle) +{ +} + +static void +scroll(void) +{ + int o; + Point p; + Rectangle r; + + o = 4*memdefont->height; + r = Rpt(window.min, Pt(window.max.x, window.max.y-o)); + p = Pt(window.min.x, window.min.y+o); + memdraw(&gscreen, r, &gscreen, p, memones, p); + r = Rpt(Pt(window.min.x, window.max.y-o), window.max); + memdraw(&gscreen, r, back, memzeros->r.min, memones, memzeros->r.min); + + curpos.y -= o; +} + +void +screenputc(char *buf) +{ + Point p; + int h, w, pos; + Rectangle r; + static int *xp; + static int xbuf[256]; + + h = memdefont->height; + if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)]) + xp = xbuf; + + switch(buf[0]) { + case '\n': + if(curpos.y+h >= window.max.y) + scroll(); + curpos.y += h; + screenputc("\r"); + break; + case '\r': + xp = xbuf; + curpos.x = window.min.x; + break; + case '\t': + p = memsubfontwidth(memdefont, " "); + w = p.x; + *xp++ = curpos.x; + pos = (curpos.x-window.min.x)/w; + pos = 8-(pos%8); + curpos.x += pos*w; + break; + case '\b': + if(xp <= xbuf) + break; + xp--; + r = Rpt(Pt(*xp, curpos.y), Pt(curpos.x, curpos.y + h)); + memdraw(&gscreen, r, back, back->r.min, memones, back->r.min); + curpos.x = *xp; + break; + default: + p = memsubfontwidth(memdefont, buf); + w = p.x; + + if(curpos.x >= window.max.x-w) + screenputc("\n"); + + *xp++ = curpos.x; + memimagestring(&gscreen, curpos, &conscol, memdefont, buf); + curpos.x += w; + } +} + +void +screenputs(char *s, int n) +{ + int i; + Rune r; + char buf[4]; +extern int cold; + +if(!cold) + return; + + if(islo() == 0) { + /* don't deadlock trying to print in interrupt */ + if(!canlock(&screenlock)) + return; + } else + lock(&screenlock); + + while(n > 0) { + i = chartorune(&r, s); + if(i == 0){ + s++; + --n; + continue; + } + memmove(buf, s, i); + buf[i] = 0; + n -= i; + s += i; + screenputc(buf); + } + + unlock(&screenlock); +} + + +void +cursorenable(void) +{ + if(vctlr->enable == nil) + return; + + vctlr->enable(); + + if(!vctlr->isloaded()) + vctlr->load(&arrow); +} + +void +cursordisable(void) +{ + if(vctlr->disable == nil) + return; + + vctlr->disable(); +} + +static Rectangle cursoroffrect; +static int cursorisoff; +static Point hot; + +void +cursorupdate0(void) +{ + int inrect, x, y; + + x = mouse.x - hot.x; + y = mouse.y - hot.y; + inrect = (x >= cursoroffrect.min.x && x < cursoroffrect.max.x + && y >= cursoroffrect.min.y && y < cursoroffrect.max.y); + if (cursorisoff == inrect) + return; + cursorisoff = inrect; + if (inrect) + cursordisable(); + else + cursorenable(); +} + +void +cursorupdate(Rectangle r) +{ + lock(&screenlock); + r.min.x -= 16; + r.min.y -= 16; + cursoroffrect = r; + if (swcursor) + cursorupdate0(); + unlock(&screenlock); +} + +void +drawcursor(Drawcursor* c) +{ + Cursor curs; + int j, i, h, bpl; + uchar *bc, *bs, *cclr, *cset; + + if(vctlr->load == nil) + return; + + /* Set the default system cursor */ + if(c->data == nil) { + lock(&screenlock); + vctlr->load(&arrow); + unlock(&screenlock); + return; + } + + hot.x = c->hotx; + hot.y = c->hoty; + curs.offset = hot; + bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 0); + + h = (c->maxy-c->miny)/2; + if(h > 16) + h = 16; + + bc = c->data; + bs = c->data + h*bpl; + + cclr = curs.clr; + cset = curs.set; + for(i = 0; i < h; i++) { + for(j = 0; j < 2; j++) { + cclr[j] = bc[j]; + cset[j] = bs[j]; + } + bc += bpl; + bs += bpl; + cclr += 2; + cset += 2; + } + lock(&screenlock); + vctlr->load(&curs); + unlock(&screenlock); +} + diff --git a/os/js/screen.h b/os/js/screen.h new file mode 100644 index 00000000..158ba592 --- /dev/null +++ b/os/js/screen.h @@ -0,0 +1,49 @@ +typedef struct Cursor Cursor; +typedef struct Vctlr Vctlr; +typedef struct Video Video; +typedef struct Thc Thc; + +#define FSVSLOT 2 /* MrCoffee Hard Coded FB Location */ + +struct Cursor +{ + Point offset; + uchar clr[2*16]; + uchar set[2*16]; +}; + +struct Vctlr { + char* name; + Vctlr* (*init)(Vctlr*, int, int, int); + void (*page)(int); + int (*setcolor)(ulong, ulong, ulong, ulong); + + void (*enable)(void); + void (*disable)(void); + void (*move)(int, int); + void (*load)(Cursor*); + int (*isloaded)(void); + int (*cursorintersectsoff)(Rectangle*); + + int x; + int y; + int d; + + Vctlr* link; + + int hidecount; + int loaded; + Cursor cursor; + Lock l; +}; + + +struct Video +{ + /* Brooktree 458/451 */ + ulong addr; /* address register */ + ulong color; /* color palette */ + ulong cntrl; /* control register */ + ulong ovrl; /* overlay palette */ +}; + diff --git a/os/js/softcursor.h b/os/js/softcursor.h new file mode 100644 index 00000000..579914ec --- /dev/null +++ b/os/js/softcursor.h @@ -0,0 +1,13 @@ +/* + * this should be #define'd to nothing if you have a hardware cursor + */ + +void cursormaybeoff(Rectangle*, Memimage*, Rectangle, Memimage*, Point*); + +/* + * also, you should #define cussoron() and cursoroff() to nothing + * if you have a hardware cursor.. This isn't as bad as it sounds, because + * this file is only included in port/devdraw.c, and it doesn't need to + * touch the cursor if it's a hardware cursor + * -Tad + */ diff --git a/os/js/superio.c b/os/js/superio.c new file mode 100644 index 00000000..e3030eab --- /dev/null +++ b/os/js/superio.c @@ -0,0 +1,216 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + + +enum +{ + /* superio configuration registers */ + SioFER = 0x0, /* function enable register */ + SioFAR = 0x1, /* function address register */ + SioPTR = 0x2, /* power and test egister */ + SioFCR = 0x3, /* function control register */ + SioPCR = 0x4, /* printer control register */ + SioKRR = 0x5, /* keyboard and RTC control register */ + SioPMC = 0x6, /* power mgmt control register */ + SioTUP = 0x7, /* tape uart and parallel register */ + SioSID = 0x8, /* SuperIO ID register */ + SioASC = 0x9, /* Advanced SIO Config register */ + SioCS0CF0 = 0xA, /* Chip select 0 config register 0 */ + SioCS0CF1 = 0xB, /* Chip select 0 config register 1 */ + SioCS1CF0 = 0xC, /* Chip select 1 config register 0 */ + SioCS1CF1 = 0xD, /* Chip select 1 config register 1 */ + + /* FER bits */ + PPTEnable = 1<<0, + EnableUART1 = 1<<1, + EnableUART2 = 1<<2, + FDCEnable = 1<<3, + FDC4 = 1<<4, + FDC2ndAddr = 1<<5, + IDEEnable = 1<<6, + IDE2ndAddr = 1<<7, + + /* FAR bits */ + PPTAddr = 3<<0, + UART1Addr = 3<<2, + UART2Addr = 3<<4, + SelectCom3n4 = 3<<6, + + /* PTR bits */ + PWDN = 1<<0, + ClkPWDN = 1<<1, + PWDNSelect = 1<<2, + IRQSelect = 1<<3, + UART1Test = 1<<4, + UART2Test = 1<<5, + LockConfig = 1<<6, + XtndPPTSelect = 1<<7, + + /* FCR bits */ + MediaSense = 1<<0, + DatRateSelect = 1<<0, + IDENTSelect = 1<<1, + PPTFloat = 1<<3, + LogicalDrvXcg = 1<<4, /* logical drive exchange */ + EnaZeroWait = 1<<5, /* zero wait state enable *. + + /* PCR bits */ + EPPEnable = 1<<0, + EPPVersionSel = 1<<1, + ECPEnable = 1<<2, + ECPClkFreeze = 1<<3, + PPTIntPolar = 1<<5, + PPTIntIOCtl = 1<<6, + RTCRamMask = 1<<7, + + /* KRR bits */ + KBCEnable = 1<<0, + KBCSpeedCtl = 1<<1, + EnaProgAccess = 1<<2, + RTCEnable = 1<<3, + RTCClkTst = 1<<4, + RAMSEL = 1<<5, + EnaChipSelect = 1<<6, + KBCClkSource = 1<<7, + + /* PMC bits */ + IDETriStCtl = 1<<0, + FDCTriStCtl = 1<<1, + UARTTriStCtl = 1<<2, + SelectiveLock = 1<<5, + PPTriStEna = 1<<6, + + /* TUP bits */ + EPPToutIntEna = 1<<2, + + /* SID bits are just data values */ + + /* ASC bits */ + IRQ5Select = 1<<0, + DRATE0Select = 1<<0, + DRV2Select = 1<<1, + DR23Select = 1<<1, + EnhancedTDR = 1<<2, + ECPCnfgABit3 = 1<<5, + SystemOpMode0 = 1<<6, + SystemOpMode1 = 1<<7, + + /* CS0CF0 bits are LA0-LA7 */ + /* CS1CF0 bits are LA0-LA7 */ + /* CSxCF1 bits (x=0,1) */ + HA8 = 1<<0, + HA9 = 1<<1, + HA10 = 1<<2, + EnaCSWr = 1<<4, + EnaCSRd = 1<<5, + CSAdrDcode = 1<<6, /* enable full addr decode */ + CSSelectPin = 1<<7, /* CS/CS0 and SYSCLK/CS1 select pin */ +}; + +typedef struct SuperIO SuperIO; + +struct SuperIO +{ + ulong va; + uchar *index; /* superio index register */ + uchar *data; /* superio data register */ + + uchar *mkctl; /* superio mouse/kbd control register */ + uchar *mkdata; /* superio mouse/kbd data register */ +}; + + +static SuperIO sio; + +static void printstatus(uchar status); + +void +superioinit(ulong va, uchar *sindex, uchar *sdata, uchar *mkctl, uchar *mkdata) +{ + sio.va = va; + + sio.index = sindex; + sio.data = sdata; + + sio.mkctl = mkctl; + sio.mkdata = mkdata; +} + + +ulong +superiova(void) +{ + return sio.va; +} + +enum +{ + OBF = 1<<0, + IBF = 1<<1, + SysFlag = 1<<2, + LastWrWasCmd = 1<<3, + KbdEnabled = 1<<4, + FromMouse = 1<<5, + Timeout = 1<<6, + ParityError = 1<<7 +}; + +uchar +superio_readctl(void) +{ + return *sio.mkctl; +} + +uchar +superio_readdata(void) +{ + return *sio.mkdata; +} + +void +superio_writectl(uchar val) +{ + *sio.mkctl = val; +} + +void +superio_writedata(uchar val) +{ + *sio.mkdata = val; +} + + +static void +printstatus(uchar status) +{ + print("0x%2.2ux = <",status); + if(status & OBF) print("OBF|"); + if(status & IBF) print("IBF|"); + if(status & SysFlag) print("SysFlag|"); + if(status & LastWrWasCmd) print("LastWrWasCmd|"); + if(status & KbdEnabled) print("KbdEnabled|"); + if(status & FromMouse) print("FromMouse|"); + if(status & Timeout) print("Timeout|"); + if(status & ParityError) print("ParityErr|"); + print(">"); +} + +void +testit() +{ + uchar status; + uchar val; + + for(;;) { + status = *sio.mkctl; + if(status&OBF) { + printstatus(status); + val = *sio.mkdata; + print(", data = 0x%2.2ux\n",val); + } + } +} diff --git a/os/js/trap.c b/os/js/trap.c new file mode 100644 index 00000000..b9117bfe --- /dev/null +++ b/os/js/trap.c @@ -0,0 +1,472 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" +#include "io.h" +#include "../port/error.h" + +void noted(Ureg**, ulong); +void rfnote(Ureg**); +int domuldiv(ulong, Ureg*); + +extern Label catch; +extern void traplink(void); +extern void syslink(void); +static void faultsparc(Ureg *ur); +static void faultasync(Ureg *ur); + + long ticks; +static char excbuf[64]; /* BUG: not reentrant! */ + +char *trapname[]={ + "reset", + "instruction access exception", + "illegal instruction", + "privileged instruction", + "fp: disabled", + "window overflow", + "window underflow", + "unaligned address", + "fp: exception", + "data access exception", + "tag overflow", + "watchpoint detected", +}; + +char *fptrapname[]={ + "none", + "IEEE 754 exception", + "unfinished FP op", + "unimplemented FP op", + "sequence error", + "hardware error", + "invalid FP register", + "reserved", +} +; + +char* +excname(ulong tbr) +{ + char xx[64]; + char *t; + + switch(tbr){ + case 8: + if(up == 0) + panic("fptrap in kernel\n"); + else{ +panic("fptrap not implemented\n"); +#ifdef notdef + if(m->fpunsafe==0 && up->p->fpstate!=FPactive) + panic("fptrap not active\n"); fsr = up->fpsave.env; + sprint(excbuf, "fp: %s fppc=0x%lux", + fptrapname[(fsr>>14)&7], + up->fpsave.q[0].a, fsr); +#endif + } + return excbuf; + case 36: + return "trap: cp disabled"; + case 37: + return "trap: unimplemented instruction"; + case 40: + return "trap: cp exception"; + case 42: + return "trap: divide by zero"; + case 128: + return "syscall"; + case 129: + return "breakpoint"; + } + t = 0; + if(tbr < sizeof trapname/sizeof(char*)) + t = trapname[tbr]; + if(t == 0){ + if(tbr >= 130) + sprint(xx, "trap instruction %ld", tbr-128); + else if(17<=tbr && tbr<=31) + sprint(xx, "interrupt level %ld", tbr-16); + else + sprint(xx, "unknown trap %ld", tbr); + t = xx; + } + if(strncmp(t, "fp: ", 4) == 0) + strcpy(excbuf, t); + else + sprint(excbuf, "trap: %s", t); + return excbuf; + +} + +void +trap(Ureg *ur) +{ + int user; + ulong tbr, iw; + + tbr = (ur->tbr&0xFFF)>>4; + /* + * Hack to catch bootstrap fault during probe + */ + if(catch.pc) + gotolabel(&catch); + + if(up) + up->dbgreg = ur; + + user = !(ur->psr&PSRPSUPER); + if(user) { + panic("how did we get to user mode???"); + } + if(tbr > 16){ /* interrupt */ + switch(tbr-16) { + case 15: /* asynch mem err */ + faultasync(ur); + break; + case 14: /* processor counter */ + clock(ur); + break; + case 13: /* keyboard/mouse */ + ns16552intr(0); + kbdintr(); + break; + case 6: /* lance */ + lanceintr(); + break; + default: + print("unexp intr lev %ld\n", tbr-16); + goto Error; + } + }else{ + switch(tbr){ + case 1: /* instr. access */ + case 9: /* data access */ + if(up && up->fpstate==FPACTIVE) { + fpquiet(); + fpsave(&up->fpsave); + up->fpstate = FPINACTIVE; + } + faultsparc(ur); + goto Return; + case 2: /* illegal instr, maybe mul */ + iw = *(ulong*)ur->pc; + if((iw&0xC1500000) == 0x80500000){ + if(domuldiv(iw, ur)) + goto Return; + tbr = ur->tbr; + } + break; + case 4: /* floating point disabled */ +panic("some more floating point crapola"); +break; +#ifdef notdef + if(u && u->p){ + if(up->p->fpstate == FPINIT) + restfpregs(initfpp, up->fpsave.fsr); + else if(u->p->fpstate == FPinactive) + restfpregs(&u->fpsave, u->fpsave.fsr); + else + break; + u->p->fpstate = FPactive; + ur->psr |= PSREF; + return; + } + break; +#endif + case 8: /* floating point exception */ +panic("floating point crapola #3"); +break; +#ifdef notdef + /* if unsafe, trap happened shutting down FPU; just return */ + if(m->fpunsafe){ + m->fptrap = (fptrap()==0); + return; + } + if(fptrap()) + goto Return; /* handled the problem */ + break; +#endif + default: + break; + } + Error: + panic("kernel trap: %s pc=0x%lux\n", excname(tbr), ur->pc); + } + Return: + return; +} + +void +trapinit(void) +{ + int i; + long t, a; + + a = ((ulong)traplink-TRAPS)>>2; + a += 0x40000000; /* CALL traplink(SB) */ + t = TRAPS; + for(i=0; i<256; i++){ + *(ulong*)(t+0) = a; /* CALL traplink(SB) */ + *(ulong*)(t+4) = 0xa7480000; /* MOVW PSR, R19 */ + a -= 16/4; + t += 16; + } + +#ifdef notdef + flushpage(TRAPS); +#else + flushicache(); +#endif + + puttbr(TRAPS); + setpsr(getpsr()|PSRET|SPL(15)); /* enable traps, not interrupts */ +} + +void +mulu(ulong u1, ulong u2, ulong *lop, ulong *hip) +{ + ulong lo1, lo2, hi1, hi2, lo, hi, t1, t2, t; + + lo1 = u1 & 0xffff; + lo2 = u2 & 0xffff; + hi1 = u1 >> 16; + hi2 = u2 >> 16; + + lo = lo1 * lo2; + t1 = lo1 * hi2; + t2 = lo2 * hi1; + hi = hi1 * hi2; + t = lo; + lo += t1 << 16; + if(lo < t) + hi++; + t = lo; + lo += t2 << 16; + if(lo < t) + hi++; + hi += (t1 >> 16) + (t2 >> 16); + *lop = lo; + *hip = hi; +} + +void +muls(long l1, long l2, long *lop, long *hip) +{ + ulong t, lo, hi; + ulong mlo, mhi; + int sign; + + sign = 0; + if(l1 < 0){ + sign ^= 1; + l1 = -l1; + } + if(l2 < 0){ + sign ^= 1; + l2 = -l2; + } + mulu(l1, l2, &mlo, &mhi); + lo = mlo; + hi = mhi; + if(sign){ + t = lo = ~lo; + hi = ~hi; + lo++; + if(lo < t) + hi++; + } + *lop = lo; + *hip = hi; +} + +int +domuldiv(ulong iw, Ureg *ur) +{ + long op1, op2; + long *regp; + long *regs; + + regs = (long*)ur; + if(iw & (1<<13)){ /* signed immediate */ + op2 = iw & 0x1FFF; + if(op2 & 0x1000) + op2 |= ~0x1FFF; + }else + op2 = regs[iw&0x1F]; + op1 = regs[(iw>>14)&0x1F]; + regp = ®s[(iw>>25)&0x1F]; + + if(iw & (4<<19)){ /* divide */ + if(ur->y!=0 && ur->y!=~0){ + unimp: + ur->tbr = 37; /* "unimplemented instruction" */ + return 0; /* complex Y is too hard */ + } + if(op2 == 0){ + ur->tbr = 42; /* "zero divide" */ + return 0; + } + if(iw & (1<<19)){ + if(ur->y && (op1&(1<<31))==0) + goto unimp; /* Y not sign extension */ + *regp = op1 / op2; + }else{ + if(ur->y) + goto unimp; + *regp = (ulong)op1 / (ulong)op2; + } + }else{ + if(iw & (1<<19)) + muls(op1, op2, regp, (long*)&ur->y); + else + mulu(op1, op2, (ulong*)regp, &ur->y); + } + if(iw & (16<<19)){ /* set CC */ + ur->psr &= ~(0xF << 20); + if(*regp & (1<<31)) + ur->psr |= 8 << 20; /* N */ + if(*regp == 0) + ur->psr |= 4 << 20; /* Z */ + /* BUG: don't get overflow right on divide */ + } + ur->pc += 4; + ur->npc = ur->pc+4; + return 1; +} + +void +dumpregs(Ureg *ur) +{ + int i; + ulong *l; + + if(up) { + print("registers for %s %ld\n",up->text,up->pid); + if(ur->usp < (ulong)up->kstack || + ur->usp > (ulong)up->kstack+KSTACK-8) + print("invalid stack pointer\n"); + } else + print("registers for kernel\n"); + + print("PSR=%lux PC=%lux TBR=%lux\n", ur->psr, ur->pc, ur->tbr); + l = &ur->r0; + for(i=0; i<32; i+=2, l+=2) + print("R%d\t%.8lux\tR%d\t%.8lux\n", i, l[0], i+1, l[1]); +} + + +/* This routine must save the values of registers the user is not permitted to + * write from devproc and the restore the saved values before returning + */ +void +setregisters(Ureg *xp, char *pureg, char *uva, int n) +{ + ulong psr; + + psr = xp->psr; + memmove(pureg, uva, n); + xp->psr = psr; +} + +void +dumpstack(void) +{ +} + +/* + * Must only be called splhi() when it is safe to spllo(). Because the FP unit + * traps if you touch it when an exception is pending, and because if you + * trap with ET==0 you halt, this routine sets some global flags to enable + * the rest of the system to handle the trap that might occur here without + * upsetting the kernel. Shouldn't be necessary, but safety first. + */ +int +fpquiet(void) +{ + int i, notrap; + ulong fsr; + char buf[128]; + + i = 0; + notrap = 1; + up->fpstate = FPINACTIVE; + for(;;){ + m->fptrap = 0; + fsr = getfsr(); + if(m->fptrap){ + /* trap occurred and up->fpsave contains state */ + sprint(buf, "sys: %s", excname(8)); +#ifdef notdef + postnote(u->p, 1, buf, NDebug); +#else + panic(buf); +#endif + notrap = 0; + break; + } + if((fsr&(1<<13)) == 0) + break; + if(++i > 1000){ + print("fp not quiescent\n"); + break; + } + } + up->fpstate = FPACTIVE; + return notrap; +} + +enum +{ + SE_WRITE = 4<<5, + SE_PROT = 2<<2, +}; + +static void +faultsparc(Ureg *ur) +{ + ulong addr; + char buf[ERRMAX]; + int read; + ulong tbr, ser; + + tbr = (ur->tbr&0xFFF)>>4; + addr = ur->pc; /* assume instr. exception */ + read = 1; + if(tbr == 9){ /* data access exception */ + addr = getrmmu(SFAR); + ser = getrmmu(SFSR); + if(ser&(SE_WRITE)) /* is SE_PROT needed? */ + read = 0; + } + + up->dbgreg = ur; /* for remote acid */ + spllo(); + sprint(buf, "sys: trap: fault %s addr=0x%lux", + read? "read" : "write", addr); + + if(up->type == Interp) + disfault(ur,buf); + dumpregs(ur); + panic("fault: %s", buf); +} + +static void +faultasync(Ureg *ur) +{ + int user; + + print("interrupt 15 AFSR %lux AFAR %lux MFSR %lux MFAR %lux\n", + getphys(AFSR), getphys(AFAR), getphys(MFSR), getphys(MFAR)); + dumpregs(ur); + /* + * Clear interrupt + */ + putphys(PROCINTCLR, 1<<15); + user = !(ur->psr&PSRPSUPER); + if(user) + pexit("Suicide", 0); + panic("interrupt 15"); +} diff --git a/os/js/ureg.h b/os/js/ureg.h new file mode 100644 index 00000000..8433eec9 --- /dev/null +++ b/os/js/ureg.h @@ -0,0 +1,45 @@ +struct Ureg +{ + ulong r0; /* unnecessary; just for symmetry */ + union{ + ulong sp; /* r1 */ + ulong usp; /* r1 */ + ulong r1; + }; + ulong r2; + ulong r3; + ulong r4; + ulong r5; + ulong r6; + ulong r7; + ulong r8; + ulong r9; + ulong r10; + ulong r11; + ulong r12; + ulong r13; + ulong r14; + ulong r15; + ulong r16; + ulong r17; + ulong r18; + ulong r19; + ulong r20; + ulong r21; + ulong r22; + ulong r23; + ulong r24; + ulong r25; + ulong r26; + ulong r27; + ulong r28; + ulong r29; + ulong r30; + ulong r31; + ulong y; + ulong tbr; + ulong psr; + ulong npc; + ulong pc; + ulong pad; /* so structure is double word aligned */ +}; diff --git a/os/ks32/Mk b/os/ks32/Mk new file mode 100755 index 00000000..2a55d43d --- /dev/null +++ b/os/ks32/Mk @@ -0,0 +1,7 @@ +#!/bin/rc +rfork ne +ROOT=/usr/inferno +fn cd +NPROC=3 +path=(/usr/inferno/Plan9/$cputype/bin $path) +exec mk $* diff --git a/os/ks32/NOTICE b/os/ks32/NOTICE new file mode 100644 index 00000000..d82287de --- /dev/null +++ b/os/ks32/NOTICE @@ -0,0 +1,2 @@ +Evaluator 7t Inferno port Copyright © 2000-2003 Vita Nuova Holdings Limited. +Originally implemented by Nigel Roles diff --git a/os/ks32/archevaluator7t.c b/os/ks32/archevaluator7t.c new file mode 100644 index 00000000..223c11f0 --- /dev/null +++ b/os/ks32/archevaluator7t.c @@ -0,0 +1,161 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +extern int cflag; +extern int consoleprint; +extern int redirectconsole; +extern int main_pool_pcnt; +extern int heap_pool_pcnt; +extern int image_pool_pcnt; + +void +archreset(void) +{ +} + +void +archconfinit(void) +{ + conf.topofmem = 512 * 1024; + conf.flashbase = 0x01800000; + conf.cpuspeed = 50000000; + + conf.useminicache = 1; + conf.cansetbacklight = 0; + conf.cansetcontrast = 0; + conf.remaplo = 0; +} + +void +archconsole(void) +{ + uartspecial(0, 57600, 'n', &kbdq, &printq, kbdcr2nl); +} + +void +archreboot(void) +{ +} + +void +setleds(uchar val) +{ + ulong leds = IOPDATA; + IOPDATA = (leds & ~0xf0) | ((val & 0xf) << 4); +} + +static void +setled7(uchar val) +{ + ulong leds = IOPDATA; + IOPDATA = (leds & ~(0x7f << 10)) | ((val & 0x7f) << 10); +} + +#define LEDSEGA 0x01 +#define LEDSEGB 0x02 +#define LEDSEGC 0x04 +#define LEDSEGD 0x08 +#define LEDSEGE 0x10 +#define LEDSEGG 0x20 +#define LEDSEGF 0x40 + +static uchar led7map[] = { +[' '] 0, +['0'] LEDSEGA | LEDSEGB | LEDSEGC | LEDSEGD | LEDSEGE | LEDSEGF, +['1'] LEDSEGB | LEDSEGC, +['2'] LEDSEGA | LEDSEGB | LEDSEGD | LEDSEGE | LEDSEGG, +['3'] LEDSEGA | LEDSEGB | LEDSEGC | LEDSEGD | LEDSEGG, +['4'] LEDSEGB | LEDSEGC | LEDSEGF | LEDSEGG, +['5'] LEDSEGA | LEDSEGC | LEDSEGD | LEDSEGF | LEDSEGG, +['6'] LEDSEGA | LEDSEGC | LEDSEGD | LEDSEGE | LEDSEGF | LEDSEGG, +['7'] LEDSEGA |LEDSEGB | LEDSEGC, +['8'] LEDSEGA | LEDSEGB | LEDSEGC | LEDSEGD | LEDSEGE | LEDSEGF | LEDSEGG, +['9'] LEDSEGA | LEDSEGB | LEDSEGC | LEDSEGD | LEDSEGF | LEDSEGG, +['A'] LEDSEGA | LEDSEGB | LEDSEGC | LEDSEGE | LEDSEGF | LEDSEGG, +['B'] LEDSEGC | LEDSEGD | LEDSEGE | LEDSEGF | LEDSEGG, +['C'] LEDSEGA | LEDSEGD | LEDSEGE | LEDSEGF, +['D'] LEDSEGB | LEDSEGC | LEDSEGD | LEDSEGE | LEDSEGG, +['E'] LEDSEGA | LEDSEGD | LEDSEGE | LEDSEGF | LEDSEGG, +['F'] LEDSEGA | LEDSEGE | LEDSEGF | LEDSEGG, +['H'] LEDSEGC | LEDSEGE | LEDSEGF | LEDSEGG, +['P'] LEDSEGA | LEDSEGB | LEDSEGE | LEDSEGF | LEDSEGG, +['R'] LEDSEGE | LEDSEGG, +['S'] LEDSEGA | LEDSEGC | LEDSEGD | LEDSEGF | LEDSEGG, +['T'] LEDSEGD | LEDSEGE | LEDSEGF | LEDSEGG, +['U'] LEDSEGB | LEDSEGC | LEDSEGD | LEDSEGE | LEDSEGF, +['~'] LEDSEGB | LEDSEGE | LEDSEGG, +}; + +void +setled7ascii(char c) +{ + if (c <= '~') + setled7(led7map[c]); +} + +void +trace(char c) +{ + int i; +// int x = splfhi(); + setled7ascii(c); + for (i = 0; i < 2000000; i++) + ; +// splx(x); +} + +void +ttrace() +{ + static char c = '6'; + + trace(c); + c = '6' + '7' -c; +} + +void +lights(ulong val) +{ + IOPDATA = (IOPDATA & (0x7ff << 4)) | ((val & 0x7ff) << 4); +} + +void +lcd_setbacklight(int) +{ +} + +void +lcd_setbrightness(ushort) +{ +} + +void +lcd_setcontrast(ushort) +{ +} + +void +archflashwp(int /*wp*/) +{ +} + +void +screenputs(char *, int) +{ +} + +void +cursorenable(void) +{ +} + +void +cursordisable(void) +{ +} diff --git a/os/ks32/armv7.h b/os/ks32/armv7.h new file mode 100644 index 00000000..f3f605fa --- /dev/null +++ b/os/ks32/armv7.h @@ -0,0 +1,19 @@ +/* + * PSR + */ +#define PsrMusr 0x10 /* mode */ +#define PsrMfiq 0x11 +#define PsrMirq 0x12 +#define PsrMsvc 0x13 +#define PsrMabt 0x17 +#define PsrMund 0x1B +#define PsrMsys 0x1F +#define PsrMask 0x1F + +#define PsrDfiq 0x00000040 /* disable FIQ interrupts */ +#define PsrDirq 0x00000080 /* disable IRQ interrupts */ + +#define PsrV 0x10000000 /* overflow */ +#define PsrC 0x20000000 /* carry/borrow/extend */ +#define PsrZ 0x40000000 /* zero */ +#define PsrN 0x80000000 /* negative/less than */ diff --git a/os/ks32/clock.c b/os/ks32/clock.c new file mode 100644 index 00000000..ef72988a --- /dev/null +++ b/os/ks32/clock.c @@ -0,0 +1,287 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "ureg.h" + +static ulong timer_incr[2] = { 0, 0, }; + +#define DISABLE(t, x) t->mod &= (x ? ~0x08 : ~0x01) +#define ENABLE(t, x) t->mod = (t->mod & (x ? 0x38 : 0x7)) | (x ? (1 << 3) : (1 << 0)) + +typedef struct Clock0link Clock0link; +typedef struct Clock0link { + void (*clock)(void); + Clock0link* link; +} Clock0link; + +static Clock0link *clock0link; +static Lock clock0lock; +static void (*prof_fcn)(Ureg *, int); + +Timer* +addclock0link(void (*clock)(void), int) +{ + Clock0link *lp; + + if((lp = malloc(sizeof(Clock0link))) == 0){ + print("addclock0link: too many links\n"); + return nil; + } + ilock(&clock0lock); + lp->clock = clock; + lp->link = clock0link; + clock0link = lp; + iunlock(&clock0lock); + return nil; +} + +static void +profintr(Ureg *ur, void*) +{ +#ifdef PROF + OstmrReg *ost = OSTMRREG; + int t; + + if ((ost->osmr[3] - ost->oscr) < 2*TIMER_HZ) + { + /* less than 2 seconds before reset, say something */ + setpanic(); + clockpoll(); + dumpregs(ur); + panic("Watchdog timer will expire"); + } + + /* advance the profile clock tick */ + ost->osmr[2] += timer_incr[2]; + ost->ossr = (1 << 2); /* Clear the SR */ + t = 1; + while((ost->osmr[2] - ost->oscr) > 0x80000000) { + ost->osmr[2] += timer_incr[2]; + t++; + } + if (prof_fcn) + prof_fcn(ur, t); +#else + USED(ur); +#endif +} + +static void +clockintr(Ureg*, void*) +{ + Clock0link *lp; + + m->ticks++; + + checkalarms(); + + if(canlock(&clock0lock)){ + for(lp = clock0link; lp; lp = lp->link) + if (lp->clock) + lp->clock(); + unlock(&clock0lock); + } + intrclear(TIMERbit(0), 0); +} + +/* +int +cticks(void) +{ + return m->ticks; +} +*/ + +/* + * Synchronize to the next SCLK tick boundary at best SPI rate. + */ +void +spi_tsync(void) +{ + /* Why has this been commented out? */ + + // don't need to waste any time here + //ulong t0; + + //t0 = OSTMR->oscr; + //while (OSTMR->oscr == t0); +} + +void +timerdisable( int timer ) +{ + TimerReg *t = TIMERREG; + if ((timer < 0) || (timer > 1)) + return; + intrmask(TIMERbit(timer), 0); + DISABLE(t, timer); +} + +void +timerenable( int timer, int Hz, void (*f)(Ureg *, void*), void* a) +{ + TimerReg *t = TIMERREG; + if ((timer < 0) || (timer > 1)) + return; + timerdisable(timer); + timer_incr[timer] = TIMER_HZ/Hz; /* set up freq */ + t->data[timer] = timer_incr[timer]; + ENABLE(t, timer); + intrenable(TIMERbit(timer), f, a, 0); +} + +void +installprof(void (*pf)(Ureg *, int)) +{ +#ifdef PROF + prof_fcn = pf; + timerenable( 2, HZ+1, profintr, 0); + timer_incr[2] = timer_incr[0]+63; /* fine tuning */ +#else + USED(pf); +#endif +} + +void +clockinit(void) +{ + m->ticks = 0; + timerenable(0, HZ, clockintr, 0); +} + +void +clockpoll(void) +{ +} + +void +clockcheck(void) +{ +} + +// macros for fixed-point math + +ulong _mularsv(ulong m0, ulong m1, ulong a, ulong s); + +/* truncated: */ +#define FXDPTDIV(a,b,n) ((ulong)(((uvlong)(a) << (n)) / (b))) +#define MAXMUL(a,n) ((ulong)((((uvlong)1<<(n))-1)/(a))) +#define MULDIV(x,a,b,n) (((x)*FXDPTDIV(a,b,n)) >> (n)) +#define MULDIV64(x,a,b,n) ((ulong)_mularsv(x, FXDPTDIV(a,b,n), 0, (n))) + +/* rounded: */ +#define FXDPTDIVR(a,b,n) ((ulong)((((uvlong)(a) << (n))+((b)/2)) / (b))) +#define MAXMULR(a,n) ((ulong)((((uvlong)1<<(n))-1)/(a))) +#define MULDIVR(x,a,b,n) (((x)*FXDPTDIVR(a,b,n)+(1<<((n)-1))) >> (n)) +#define MULDIVR64(x,a,b,n) ((ulong)_mularsv(x, FXDPTDIVR(a,b,n), 1<<((n)-1), (n))) + + +// these routines are all limited to a maximum of 1165 seconds, +// due to the wrap-around of the OSTIMER + +ulong +timer_start(void) +{ + return TIMERREG->data[0]; +} + +ulong +timer_ticks(ulong t0) +{ + return TIMERREG->data[0] - t0; +} + +int +timer_devwait(ulong *adr, ulong mask, ulong val, int ost) +{ + int i; + ulong t0 = timer_start(); + while((*adr & mask) != val) + if(timer_ticks(t0) > ost) + return ((*adr & mask) == val) ? 0 : -1; + else + for (i = 0; i < 10; i++); /* don't pound OSCR too hard! (why not?) */ + return 0; +} + +void +timer_setwatchdog(int t) +{ + USED(t); +} + +void +timer_delay(int t) +{ + ulong t0 = timer_start(); + while(timer_ticks(t0) < t) + ; +} + + +ulong +us2tmr(int us) +{ + return MULDIV64(us, TIMER_HZ, 1000000, 24); +} + +int +tmr2us(ulong t) +{ + return MULDIV64(t, 1000000, TIMER_HZ, 24); +} + +void +microdelay(int us) +{ + ulong t0 = timer_start(); + ulong t = us2tmr(us); + while(timer_ticks(t0) <= t) + ; +} + + +ulong +ms2tmr(int ms) +{ + return MULDIV64(ms, TIMER_HZ, 1000, 20); +} + +int +tmr2ms(ulong t) +{ + return MULDIV64(t, 1000, TIMER_HZ, 32); +} + +void +delay(int ms) +{ + ulong t0 = timer_start(); + ulong t = ms2tmr(ms); + while(timer_ticks(t0) <= t) + clockpoll(); +} + +int +srand() +{ + return 0; +} + +int +time() +{ + return 0; +} + +uvlong +fastticks(uvlong *hz) +{ + if(hz) + *hz = HZ; + return m->ticks; +} diff --git a/os/ks32/dat.h b/os/ks32/dat.h new file mode 100644 index 00000000..70422b63 --- /dev/null +++ b/os/ks32/dat.h @@ -0,0 +1,218 @@ +typedef struct Conf Conf; +typedef struct FPU FPU; +typedef struct FPenv FPenv; +typedef struct Label Label; +typedef struct Lock Lock; +typedef struct Mach Mach; +typedef struct Ureg Ureg; +typedef struct ISAConf ISAConf; +typedef struct PCMmap PCMmap; +typedef struct PCIcfg PCIcfg; +typedef struct TouchPnt TouchPnt; +typedef struct TouchTrans TouchTrans; +typedef struct TouchCal TouchCal; +typedef struct Vmode Vmode; + +typedef ulong Instr; + +#define ISAOPTLEN 16 +#define NISAOPT 8 +struct Conf +{ + ulong nmach; /* processors */ + ulong nproc; /* processes */ + ulong npage0; /* total physical pages of memory */ + ulong npage1; /* total physical pages of memory */ + ulong topofmem; /* highest physical address + 1 */ + ulong npage; /* total physical pages of memory */ + ulong base0; /* base of bank 0 */ + ulong base1; /* base of bank 1 */ + ulong ialloc; /* max interrupt time allocation in bytes */ + ulong flashbase; + ulong cpuspeed; + ulong pagetable; + + int useminicache; /* screen.c/lcd.c */ + int cansetbacklight; /* screen.c/lcd.c */ + int cansetcontrast; /* screen.c/lcd.c */ + int remaplo; /* use alt ivec */ + int textwrite; /* writeable text segment, for debug */ +}; + +struct ISAConf { + char type[KNAMELEN]; + ulong port; + ulong irq; + ulong sairq; + ulong dma; + ulong mem; + ulong size; + ulong freq; + + int nopt; + char opt[NISAOPT][ISAOPTLEN]; +}; + +/* + * FPenv.status + */ +enum +{ + FPINIT, + FPACTIVE, + FPINACTIVE, +}; + +struct FPenv +{ + ulong status; + ulong control; + ushort fpistate; /* emulated fp */ + ulong regs[8][3]; /* emulated fp */ +}; + +/* + * This structure must agree with fpsave and fprestore asm routines + */ +struct FPU +{ + FPenv env; + uchar regs[80]; /* floating point registers */ +}; + +struct Label +{ + ulong sp; + ulong pc; +}; + +struct Lock +{ + ulong key; + ulong sr; + ulong pc; + int pri; +}; + +#include "../port/portdat.h" + +/* + * machine dependent definitions not used by ../port/dat.h + */ +struct Mach +{ + ulong ticks; /* of the clock since boot time */ + Proc *proc; /* current process on this processor */ + Label sched; /* scheduler wakeup */ + Lock alarmlock; /* access to alarm list */ + void *alarm; /* alarms bound to this clock */ + int machno; + int nrdy; + + int stack[1]; +}; + +#define MACHP(n) (n == 0 ? (Mach*)(MACHADDR) : (Mach*)0) + +extern Mach Mach0; +extern Mach *m; +extern Proc *up; + +typedef struct MemBank { + uint pbase; + uint plimit; + uint vbase; + uint vlimit; +} MemBank; + +enum { + // DMA configuration parameters + + // DMA Direction + DmaOUT= 0, + DmaIN= 1, + + // dma endianess + DmaLittle= 0, + DmaBig= 1, + + // dma devices + DmaUDC= 0, + DmaSDLC= 2, + DmaUART0= 4, + DmaHSSP= 6, + DmaUART1= 7, // special case (is really 6) + DmaUART2= 8, + DmaMCPaudio= 10, + DmaMCPtelecom= 12, + DmaSSP= 14, +}; + +enum touch_source { + TOUCH_READ_X1, TOUCH_READ_X2, TOUCH_READ_X3, TOUCH_READ_X4, + TOUCH_READ_Y1, TOUCH_READ_Y2, TOUCH_READ_Y3, TOUCH_READ_Y4, + TOUCH_READ_P1, TOUCH_READ_P2, + TOUCH_READ_RX1, TOUCH_READ_RX2, + TOUCH_READ_RY1, TOUCH_READ_RY2, + TOUCH_NUMRAWCAL = 10, +}; + +struct TouchPnt { + int x; + int y; +}; + +struct TouchTrans { + int xxm; + int xym; + int yxm; + int yym; + int xa; + int ya; +}; + +struct TouchCal { + TouchPnt p[4]; // screen points + TouchPnt r[4][4];// raw points + TouchTrans t[4]; // transformations + TouchPnt err; // maximum error + TouchPnt var; // usual maximum variance for readings + int ptp; // pressure threshold for press + int ptr; // pressure threshold for release +}; + +extern TouchCal touchcal; + +struct Vmode { + int wid; /* 0 -> default or any match for all fields */ + int hgt; + uchar d; + uchar hz; + ushort flags; +}; + +enum { + VMODE_MONO = 0x0001, /* monochrome display */ + VMODE_COLOR = 0x0002, /* color (RGB) display */ + VMODE_TFT = 0x0004, /* TFT (active matrix) display */ + VMODE_STATIC = 0x0010, /* fixed palette */ + VMODE_PSEUDO = 0x0020, /* changeable palette */ + VMODE_LINEAR = 0x0100, /* linear frame buffer */ + VMODE_PAGED = 0x0200, /* paged frame buffer */ + VMODE_PLANAR = 0x1000, /* pixel bits split between planes */ + VMODE_PACKED = 0x2000, /* pixel bits packed together */ + VMODE_LILEND = 0x4000, /* little endian pixel layout */ + VMODE_BIGEND = 0x8000, /* big endian pixel layout */ +}; + +/* + * Interface to PCMCIA stubs + */ +enum { + /* argument to pcmpin() */ + PCMready, + PCMeject, + PCMstschng, +}; + +#define swcursor 1 diff --git a/os/ks32/devuart.c b/os/ks32/devuart.c new file mode 100644 index 00000000..2251dd59 --- /dev/null +++ b/os/ks32/devuart.c @@ -0,0 +1,719 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +/* + * currently no DMA or flow control (hardware or software) + */ + +/* + * problems fixed from previous vsn: + * + * - no kick on queue's, so redirections weren't getting + * started until the clock tick + * + * - lots of unnecessary overhead + * + * - initialization sequencing + * + * - uart[n] no longer indexed before calling uartinstall() + */ +#define DEBUG if(0)iprint + +static void uartintr(Ureg*, void*); + +enum +{ + Stagesize= 1024, + Dmabufsize=Stagesize/2, + Nuart=7, /* max per machine */ +}; + +typedef struct Uart Uart; +struct Uart +{ + QLock; + + int opens; + + int enabled; + + int port; /* 0 or 1 */ + int kickme; /* for kick */ + int frame; /* framing errors */ + int overrun; /* rcvr overruns */ + int perror; /* parity error */ + int bps; /* baud rate */ + uchar bits; + char parity; + uchar stop; + + int inters; /* total interrupt count */ + int rinters; /* interrupts due to read */ + int winters; /* interrupts due to write */ + + int rcount; /* total read count */ + int wcount; /* total output count */ + + /* buffers */ + int (*putc)(Queue*, int); + Queue *iq; + Queue *oq; + + UartReg *reg; + + /* staging areas to avoid some of the per character costs */ + uchar *ip; + uchar *ie; + uchar *op; + uchar *oe; + + /* put large buffers last to aid register-offset optimizations: */ + char name[KNAMELEN]; + uchar istage[Stagesize]; + uchar ostage[Stagesize]; +}; + +#define UCON_ENABLEMASK (UCON_RXMDMASK | UCON_TXMDMASK | UCON_SINTMASK) +#define UCON_ENABLESET (UCON_RXMDINT | UCON_TXMDINT | UCON_SINTON) +#define UCON_DISABLESET (UCON_RXMDOFF | UCON_TXMDOFF | UCON_SINTOFF) + +static Uart *uart[Nuart]; +static int nuart; + +static uchar +readstatus(Uart *p) +{ + UartReg *reg = p->reg; + uchar stat = reg->stat; + if (stat & USTAT_OV) + p->overrun++; + if (stat & USTAT_PE) + p->perror++; + if (stat & USTAT_FE) + p->frame++; + return stat; +} + +static void +uartset(Uart *p) +{ + UartReg *reg = p->reg; + ulong denom; + ulong brdiv; + int n; + uchar lcon; + + lcon= ULCON_CLOCKMCLK | ULCON_IROFF; + lcon |= ULCON_WL5 + (p->bits - 5); + lcon |= p->stop == 1 ? ULCON_STOP1 : ULCON_STOP2; + switch (p->parity) { + default: + case 'n': + lcon |= ULCON_PMDNONE; + break; + case 'o': + lcon |= ULCON_PMDODD; + break; + case 'e': + lcon |= ULCON_PMDEVEN; + break; + } + reg->lcon = lcon; + + /* clear the break and loopback bits; leave everything else alone */ + reg->con = (reg->con & ~(UCON_BRKMASK | UCON_LOOPMASK)) | UCON_BRKOFF | UCON_LOOPOFF; + + denom = 2 * 16 * p->bps; + brdiv = (TIMER_HZ + denom / 2) / denom - 1; + reg->brdiv = brdiv << 4; + + /* set buffer length according to speed, to allow + * at most a 200ms delay before dumping the staging buffer + * into the input queue + */ + n = p->bps/(10*1000/200); + p->ie = &p->istage[n < Stagesize ? n : Stagesize]; +} + +/* + * send break + */ +static void +uartbreak(Uart *p, int ms) +{ + UartReg *reg = p->reg; + if(ms == 0) + ms = 200; + reg->con |= UCON_BRKON; + tsleep(&up->sleep, return0, 0, ms); + reg->con &= ~UCON_BRKON; +} + +/* + * turn on a port + */ +static void +uartenable(Uart *p) +{ + UartReg *reg = p->reg; + + if(p->enabled) + return; + + uartset(p); + // enable receive, transmit, and receive interrupt: + reg->con = (reg->con & UCON_ENABLEMASK) | UCON_ENABLESET; + p->enabled = 1; +} + +/* + * turn off a port + */ +static void +uartdisable(Uart *p) +{ + p->reg->con = (p->reg->con & UCON_ENABLEMASK) | UCON_DISABLESET; + p->enabled = 0; +} + +/* + * put some bytes into the local queue to avoid calling + * qconsume for every character + */ +static int +stageoutput(Uart *p) +{ + int n; + Queue *q = p->oq; + + if(!q) + return 0; + n = qconsume(q, p->ostage, Stagesize); + if(n <= 0) + return 0; + p->op = p->ostage; + p->oe = p->ostage + n; + return n; +} + +static void +uartxmit(Uart *p) +{ + UartReg *reg = p->reg; + ulong gag = 1; + while(p->op < p->oe || stageoutput(p)) { + if(readstatus(p) & USTAT_TBE) { + DEBUG("T"); + reg->txbuf = *(p->op++); + p->wcount++; + } else { + DEBUG("F"); + gag = 0; + break; + } + } + if (gag) { + DEBUG("G"); + p->kickme = 1; + intrmask(UARTTXbit(p->port), 0); + } +} + +static void +uartrecvq(Uart *p) +{ + uchar *cp = p->istage; + int n = p->ip - cp; + + if(n == 0) + return; + if(p->putc) + while(n-- > 0) + p->putc(p->iq, *cp++); + else if(p->iq) + if(qproduce(p->iq, p->istage, n) < n) + print("qproduce flow control"); + p->ip = p->istage; +} + +static void +uartrecv(Uart *p) +{ + UartReg *reg = p->reg; + uchar stat = readstatus(p); + +DEBUG("R"); + if (stat & USTAT_RDR) { + int c; + c = reg->rxbuf; + if (c == '?') { + DEBUG("mod 0x%.8lx\n", INTREG->mod); + DEBUG("msk 0x%.8lx\n", INTREG->msk); + DEBUG("pnd 0x%.8lx\n", INTREG->pnd); + } + *p->ip++ = c; +/* if(p->ip >= p->ie) */ + uartrecvq(p); + p->rcount++; + } +} + +static void +uartkick(void *a) +{ + Uart *p = a; + int x = splhi(); + DEBUG("k"); + if (p->kickme) { + p->kickme = 0; + DEBUG("K"); + intrunmask(UARTTXbit(p->port), 0); + } + splx(x); +} + +/* + * UART Interrupt Handler + */ +static void +uarttxintr(Ureg*, void* arg) +{ + Uart *p = arg; + intrclear(UARTTXbit(p->port), 0); + p->inters++; + p->winters++; + uartxmit(p); +} + +static void +uartrxintr(Ureg*, void* arg) +{ + Uart *p = arg; + intrclear(UARTRXbit(p->port), 0); + p->inters++; + p->rinters++; + uartrecv(p); +} + + +static void +uartsetup(ulong port, char *name) +{ + Uart *p; + + if(nuart >= Nuart) + return; + + p = xalloc(sizeof(Uart)); + uart[nuart++] = p; + strcpy(p->name, name); + + p->reg = &UARTREG[port]; + p->bps = 9600; + p->bits = 8; + p->parity = 'n'; + p->stop = 1; + p->kickme = 0; + p->port = port; + + p->iq = qopen(4*1024, 0, 0 , p); + p->oq = qopen(4*1024, 0, uartkick, p); + + p->ip = p->istage; + p->ie = &p->istage[Stagesize]; + p->op = p->ostage; + p->oe = p->ostage; + + intrenable(UARTTXbit(port), uarttxintr, p, 0); + intrenable(UARTRXbit(port), uartrxintr, p, 0); +} + +static void +uartinstall(void) +{ + static int already; + + if(already) + return; + already = 1; + + uartsetup(0, "eia0"); +// uartsetup(1, "eia1"); +} + +/* + * called by main() to configure a duart port as a console or a mouse + */ +void +uartspecial(int port, int bps, char parity, Queue **in, Queue **out, int (*putc)(Queue*, int)) +{ + Uart *p; + + uartinstall(); + if(port >= nuart) + return; + p = uart[port]; + if(bps) + p->bps = bps; + if(parity) + p->parity = parity; + uartenable(p); + p->putc = putc; + if(in) + *in = p->iq; + if(out) + *out = p->oq; + p->opens++; +} + +Dirtab *uartdir; +int ndir; + +static void +setlength(int i) +{ + Uart *p; + + if(i > 0){ + p = uart[i]; + if(p && p->opens && p->iq) + uartdir[1+3*i].length = qlen(p->iq); + } else for(i = 0; i < nuart; i++){ + p = uart[i]; + if(p && p->opens && p->iq) + uartdir[1+3*i].length = qlen(p->iq); + } +} + +/* + * all uarts must be uartsetup() by this point or inside of uartinstall() + */ +static void +uartreset(void) +{ + int i; + Dirtab *dp; + + uartinstall(); + + ndir = 1+3*nuart; + uartdir = xalloc(ndir * sizeof(Dirtab)); + dp = uartdir; + strcpy(dp->name, "."); + mkqid(&dp->qid, 0, 0, QTDIR); + dp->length = 0; + dp->perm = DMDIR|0555; + dp++; + for(i = 0; i < nuart; i++){ + /* 3 directory entries per port */ + strcpy(dp->name, uart[i]->name); + dp->qid.path = NETQID(i, Ndataqid); + dp->perm = 0660; + dp++; + sprint(dp->name, "%sctl", uart[i]->name); + dp->qid.path = NETQID(i, Nctlqid); + dp->perm = 0660; + dp++; + sprint(dp->name, "%sstatus", uart[i]->name); + dp->qid.path = NETQID(i, Nstatqid); + dp->perm = 0444; + dp++; + } +} + +static Chan* +uartattach(char *spec) +{ + return devattach('t', spec); +} + +static Walkqid* +uartwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, uartdir, ndir, devgen); +} + +static int +uartstat(Chan *c, uchar *dp, int n) +{ + if(NETTYPE(c->qid.path) == Ndataqid) + setlength(NETID(c->qid.path)); + return devstat(c, dp, n, uartdir, ndir, devgen); +} + +static Chan* +uartopen(Chan *c, int omode) +{ + Uart *p; + + c = devopen(c, omode, uartdir, ndir, devgen); + + switch(NETTYPE(c->qid.path)){ + case Nctlqid: + case Ndataqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(p->opens++ == 0){ + uartenable(p); + qreopen(p->iq); + qreopen(p->oq); + } + qunlock(p); + break; + } + + return c; +} + +static void +uartclose(Chan *c) +{ + Uart *p; + + if(c->qid.type & QTDIR) + return; + if((c->flag & COPEN) == 0) + return; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + case Nctlqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(--(p->opens) == 0){ + uartdisable(p); + qclose(p->iq); + qclose(p->oq); + p->ip = p->istage; + } + qunlock(p); + break; + } +} + +static long +uartstatus(Chan *c, Uart *p, void *buf, long n, long offset) +{ + char str[256]; + USED(c); + + str[0] = 0; + sprint(str, "opens %d ferr %d oerr %d perr %d baud %d parity %c" + " intr %d rintr %d wintr %d" + " rcount %d wcount %d", + p->opens, p->frame, p->overrun, p->perror, p->bps, p->parity, + p->inters, p->rinters, p->winters, + p->rcount, p->wcount); + + strcat(str, "\n"); + return readstr(offset, buf, n, str); +} + +static long +uartread(Chan *c, void *buf, long n, vlong offset) +{ + Uart *p; + + if(c->qid.type & QTDIR){ + setlength(-1); + return devdirread(c, buf, n, uartdir, ndir, devgen); + } + + p = uart[NETID(c->qid.path)]; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qread(p->iq, buf, n); + case Nctlqid: + return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE); + case Nstatqid: + return uartstatus(c, p, buf, n, offset); + } + + return 0; +} + +static void +uartctl(Uart *p, char *cmd) +{ + int i, n; + + /* let output drain for a while (up to 4 secs) */ + for(i = 0; i < 200 && (qlen(p->oq) || (readstatus(p) & USTAT_TC) == 0); i++) + tsleep(&up->sleep, return0, 0, 20); + + if(strncmp(cmd, "break", 5) == 0){ + uartbreak(p, 0); + return; + } + + n = atoi(cmd+1); + switch(*cmd){ + case 'B': + case 'b': + if(n <= 0) + error(Ebadarg); + p->bps = n; + uartset(p); + break; + case 'f': + case 'F': + qflush(p->oq); + break; + case 'H': + case 'h': + qhangup(p->iq, 0); + qhangup(p->oq, 0); + break; + case 'L': + case 'l': + if(n < 7 || n > 8) + error(Ebadarg); + p->bits = n; + uartset(p); + break; + case 'n': + case 'N': + qnoblock(p->oq, n); + break; + case 'P': + case 'p': + p->parity = *(cmd+1); + uartset(p); + break; + case 'K': + case 'k': + uartbreak(p, n); + break; + case 'Q': + case 'q': + qsetlimit(p->iq, n); + qsetlimit(p->oq, n); + break; + case 's': + case 'S': + if(n < 1 || n > 2) + error(Ebadarg); + p->stop = n; + uartset(p); + break; + } +} + +static long +uartwrite(Chan *c, void *buf, long n, vlong offset) +{ + Uart *p; + char cmd[32]; + + USED(offset); + + if(c->qid.type & QTDIR) + error(Eperm); + + p = uart[NETID(c->qid.path)]; + + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qwrite(p->oq, buf, n); + case Nctlqid: + + if(n >= sizeof(cmd)) + n = sizeof(cmd)-1; + memmove(cmd, buf, n); + cmd[n] = 0; + uartctl(p, cmd); + return n; + } +} + +static int +uartwstat(Chan *c, uchar *dp, int n) +{ + error(Eperm); + return 0; +#ifdef xxx + Dir d; + Dirtab *dt; + + if(!iseve()) + error(Eperm); + if(c->qid.type & QTDIR) + error(Eperm); + if(NETTYPE(c->qid.path) == Nstatqid) + error(Eperm); + + dt = &uartdir[3 * NETID(c->qid.path)]; + convM2D(dp, &d); + d.mode &= 0666; + dt[0].perm = dt[1].perm = d.mode; +#endif +} + +Dev uartdevtab = { + 't', + "uart", + + uartreset, + devinit, + devshutdown, + uartattach, + uartwalk, + uartstat, + uartopen, + devcreate, + uartclose, + uartread, + devbread, + uartwrite, + devbwrite, + devremove, + uartwstat, +}; + +void +uartputc(int c) +{ + UartReg *u; + + if (!c) + return; + u = &UARTREG[1]; + while ((u->stat & USTAT_TBE) == 0) + ; + u->txbuf = c; + if (c == '\n') + while((u->stat & USTAT_TC) == 0) /* flush xmit fifo */ + ; +} + +void +uartputs(char *data, int len) +{ + int x; + + clockpoll(); + x = splfhi(); + while (len--){ + if(*data == '\n') + uartputc('\r'); + uartputc(*data++); + } + splx(x); +} + +int +uartgetc(void) +{ + UartReg *u; + + clockcheck(); + u = &UARTREG[1]; + while((u->stat & USTAT_RDR) == 0) + clockcheck(); + return u->rxbuf; +} diff --git a/os/ks32/download.ps b/os/ks32/download.ps new file mode 100644 index 00000000..8182bc4b --- /dev/null +++ b/os/ks32/download.ps @@ -0,0 +1,1040 @@ +%!PS-Adobe-2.0 +%%Version: 0.1 +%%DocumentFonts: (atend) +%%Pages: (atend) +%%EndComments +% +% Version 3.3.2 prologue for troff files. +% + +/#copies 1 store +/aspectratio 1 def +/formsperpage 1 def +/landscape false def +/linewidth .3 def +/magnification 1 def +/margin 0 def +/orientation 0 def +/resolution 720 def +/rotation 1 def +/xoffset 0 def +/yoffset 0 def + +/roundpage true def +/useclippath true def +/pagebbox [0 0 612 792] def + +/R /Times-Roman def +/I /Times-Italic def +/B /Times-Bold def +/BI /Times-BoldItalic def +/H /Helvetica def +/HI /Helvetica-Oblique def +/HB /Helvetica-Bold def +/HX /Helvetica-BoldOblique def +/CW /Courier def +/CO /Courier def +/CI /Courier-Oblique def +/CB /Courier-Bold def +/CX /Courier-BoldOblique def +/PA /Palatino-Roman def +/PI /Palatino-Italic def +/PB /Palatino-Bold def +/PX /Palatino-BoldItalic def +/Hr /Helvetica-Narrow def +/Hi /Helvetica-Narrow-Oblique def +/Hb /Helvetica-Narrow-Bold def +/Hx /Helvetica-Narrow-BoldOblique def +/KR /Bookman-Light def +/KI /Bookman-LightItalic def +/KB /Bookman-Demi def +/KX /Bookman-DemiItalic def +/AR /AvantGarde-Book def +/AI /AvantGarde-BookOblique def +/AB /AvantGarde-Demi def +/AX /AvantGarde-DemiOblique def +/NR /NewCenturySchlbk-Roman def +/NI /NewCenturySchlbk-Italic def +/NB /NewCenturySchlbk-Bold def +/NX /NewCenturySchlbk-BoldItalic def +/ZD /ZapfDingbats def +/ZI /ZapfChancery-MediumItalic def +/S /S def +/S1 /S1 def +/GR /Symbol def + +/inch {72 mul} bind def +/min {2 copy gt {exch} if pop} bind def + +/setup { + counttomark 2 idiv {def} repeat pop + + landscape {/orientation 90 orientation add def} if + /scaling 72 resolution div def + linewidth setlinewidth + 1 setlinecap + + pagedimensions + xcenter ycenter translate + orientation rotation mul rotate + width 2 div neg height 2 div translate + xoffset inch yoffset inch neg translate + margin 2 div dup neg translate + magnification dup aspectratio mul scale + scaling scaling scale + + addmetrics + 0 0 moveto +} def + +/pagedimensions { + useclippath userdict /gotpagebbox known not and { + /pagebbox [clippath pathbbox newpath] def + roundpage currentdict /roundpagebbox known and {roundpagebbox} if + } if + pagebbox aload pop + 4 -1 roll exch 4 1 roll 4 copy + landscape {4 2 roll} if + sub /width exch def + sub /height exch def + add 2 div /xcenter exch def + add 2 div /ycenter exch def + userdict /gotpagebbox true put +} def + +/addmetrics { + /Symbol /S null Sdefs cf + /Times-Roman /S1 StandardEncoding dup length array copy S1defs cf +} def + +/pagesetup { + /page exch def + currentdict /pagedict known currentdict page known and { + page load pagedict exch get cvx exec + } if +} def + +/decodingdefs [ + {counttomark 2 idiv {y moveto show} repeat} + {neg /y exch def counttomark 2 idiv {y moveto show} repeat} + {neg moveto {2 index stringwidth pop sub exch div 0 32 4 -1 roll widthshow} repeat} + {neg moveto {spacewidth sub 0.0 32 4 -1 roll widthshow} repeat} + {counttomark 2 idiv {y moveto show} repeat} + {neg setfunnytext} +] def + +/setdecoding {/t decodingdefs 3 -1 roll get bind def} bind def + +/w {neg moveto show} bind def +/m {neg dup /y exch def moveto} bind def +/done {/lastpage where {pop lastpage} if} def + +/f { + dup /font exch def findfont exch + dup /ptsize exch def scaling div dup /size exch def scalefont setfont + linewidth ptsize mul scaling 10 mul div setlinewidth + /spacewidth ( ) stringwidth pop def +} bind def + +/changefont { + /fontheight exch def + /fontslant exch def + currentfont [ + 1 0 + fontheight ptsize div fontslant sin mul fontslant cos div + fontheight ptsize div + 0 0 + ] makefont setfont +} bind def + +/sf {f} bind def + +/cf { + dup length 2 idiv + /entries exch def + /chtab exch def + /newencoding exch def + /newfont exch def + + findfont dup length 1 add dict + /newdict exch def + {1 index /FID ne {newdict 3 1 roll put}{pop pop} ifelse} forall + + newencoding type /arraytype eq {newdict /Encoding newencoding put} if + + newdict /Metrics entries dict put + newdict /Metrics get + begin + chtab aload pop + 1 1 entries {pop def} for + newfont newdict definefont pop + end +} bind def + +% +% A few arrays used to adjust reference points and character widths in some +% of the printer resident fonts. If square roots are too high try changing +% the lines describing /radical and /radicalex to, +% +% /radical [0 -75 550 0] +% /radicalex [-50 -75 500 0] +% +% Move braceleftbt a bit - default PostScript character is off a bit. +% + +/Sdefs [ + /bracketlefttp [201 500] + /bracketleftbt [201 500] + /bracketrighttp [-81 380] + /bracketrightbt [-83 380] + /braceleftbt [203 490] + /bracketrightex [220 -125 500 0] + /radical [0 0 550 0] + /radicalex [-50 0 500 0] + /parenleftex [-20 -170 0 0] + /integral [100 -50 500 0] + /infinity [10 -75 730 0] +] def + +/S1defs [ + /underscore [0 80 500 0] + /endash [7 90 650 0] +] def +% +% Tries to round clipping path dimensions, as stored in array pagebbox, so they +% match one of the known sizes in the papersizes array. Lower left coordinates +% are always set to 0. +% + +/roundpagebbox { + 7 dict begin + /papersizes [8.5 inch 11 inch 14 inch 17 inch] def + + /mappapersize { + /val exch def + /slop .5 inch def + /diff slop def + /j 0 def + 0 1 papersizes length 1 sub { + /i exch def + papersizes i get val sub abs + dup diff le {/diff exch def /j i def} {pop} ifelse + } for + diff slop lt {papersizes j get} {val} ifelse + } def + + pagebbox 0 0 put + pagebbox 1 0 put + pagebbox dup 2 get mappapersize 2 exch put + pagebbox dup 3 get mappapersize 3 exch put + end +} bind def + +%%EndProlog +%%BeginSetup +mark +% +% Encoding vector and redefinition of findfont for the ISO Latin1 standard. +% The 18 characters missing from ROM based fonts on older printers are noted +% below. +% + +/ISOLatin1Encoding [ + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /space + /exclam + /quotedbl + /numbersign + /dollar + /percent + /ampersand + /quoteright + /parenleft + /parenright + /asterisk + /plus + /comma + /minus + /period + /slash + /zero + /one + /two + /three + /four + /five + /six + /seven + /eight + /nine + /colon + /semicolon + /less + /equal + /greater + /question + /at + /A + /B + /C + /D + /E + /F + /G + /H + /I + /J + /K + /L + /M + /N + /O + /P + /Q + /R + /S + /T + /U + /V + /W + /X + /Y + /Z + /bracketleft + /backslash + /bracketright + /asciicircum + /underscore + /quoteleft + /a + /b + /c + /d + /e + /f + /g + /h + /i + /j + /k + /l + /m + /n + /o + /p + /q + /r + /s + /t + /u + /v + /w + /x + /y + /z + /braceleft + /bar + /braceright + /asciitilde + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /.notdef + /dotlessi + /grave + /acute + /circumflex + /tilde + /macron + /breve + /dotaccent + /dieresis + /.notdef + /ring + /cedilla + /.notdef + /hungarumlaut + /ogonek + /caron + /space + /exclamdown + /cent + /sterling + /currency + /yen + /brokenbar % missing + /section + /dieresis + /copyright + /ordfeminine + /guillemotleft + /logicalnot + /hyphen + /registered + /macron + /degree % missing + /plusminus % missing + /twosuperior % missing + /threesuperior % missing + /acute + /mu % missing + /paragraph + /periodcentered + /cedilla + /onesuperior % missing + /ordmasculine + /guillemotright + /onequarter % missing + /onehalf % missing + /threequarters % missing + /questiondown + /Agrave + /Aacute + /Acircumflex + /Atilde + /Adieresis + /Aring + /AE + /Ccedilla + /Egrave + /Eacute + /Ecircumflex + /Edieresis + /Igrave + /Iacute + /Icircumflex + /Idieresis + /Eth % missing + /Ntilde + /Ograve + /Oacute + /Ocircumflex + /Otilde + /Odieresis + /multiply % missing + /Oslash + /Ugrave + /Uacute + /Ucircumflex + /Udieresis + /Yacute % missing + /Thorn % missing + /germandbls + /agrave + /aacute + /acircumflex + /atilde + /adieresis + /aring + /ae + /ccedilla + /egrave + /eacute + /ecircumflex + /edieresis + /igrave + /iacute + /icircumflex + /idieresis + /eth % missing + /ntilde + /ograve + /oacute + /ocircumflex + /otilde + /odieresis + /divide % missing + /oslash + /ugrave + /uacute + /ucircumflex + /udieresis + /yacute % missing + /thorn % missing + /ydieresis +] def + +/NewFontDirectory FontDirectory maxlength dict def + +% +% Apparently no guarantee findfont is defined in systemdict so the obvious +% +% systemdict /findfont get exec +% +% can generate an error. So far the only exception is a VT600 (version 48.0). +% + +userdict /@RealFindfont known not { + userdict begin + /@RealFindfont systemdict begin /findfont load end def + end +} if + +/findfont { + dup NewFontDirectory exch known not { + dup + %dup systemdict /findfont get exec % not always in systemdict + dup userdict /@RealFindfont get exec + dup /Encoding get StandardEncoding eq { + dup length dict begin + {1 index /FID ne {def}{pop pop} ifelse} forall + /Encoding ISOLatin1Encoding def + currentdict + end + /DummyFontName exch definefont + } if + NewFontDirectory 3 1 roll put + } if + NewFontDirectory exch get +} bind def + +%%Patch from lp +%%EndPatch from lp + +setup +%%EndSetup +%%Page: 1 1 +/saveobj save def +mark +1 pagesetup +12 /Times-Bold f +(Connecting up the) 1322 1220 w +(Arm) 2293 1220 w +(Evaluator) 2562 1220 w +(7t) 3106 1220 w +(and Downloading Code) 3236 1220 w +10 /Times-Italic f +(Nigel Roles) 2648 1404 w +(Vita Nuova Holdings Limited) 2292 1556 w +(10th November 2000) 2461 1676 w +10 /Times-Bold f +(Introduction) 720 1924 w +10 /Times-Roman f +(This is just a quick note on how to download code to the Evaluator and run it.) 970 2082 w +10 /Times-Bold f +(Health Warning) 720 2330 w +10 /Times-Roman f +(I) 970 2488 w +10 /Times-Italic f +(think) 1033 2488 w +10 /Times-Roman f +(the) 1263 2488 w +(interrupt) 1415 2488 w +(and) 1789 2488 w +(exception) 1963 2488 w +(handling) 2382 2488 w +(is) 2763 2488 w +(OK;) 2861 2488 w +(it's) 3064 2488 w +(just) 3223 2488 w +(that) 3399 2488 w +(I) 3580 2488 w +(had) 3644 2488 w +(problems) 3819 2488 w +(with) 4222 2488 w +(supporting) 4431 2488 w +(two) 4890 2488 w +(serial ports, interrupt driven at the same time. You have been warned.) 720 2608 w +10 /Times-Bold f +(Connecting it up) 720 2856 w +10 /Times-Roman f +(The) 970 3014 w +(board) 1156 3014 w +(has) 1414 3014 w +(Arm's) 1578 3014 w +(standard) 1864 3014 w +(Angel) 2233 3014 w +(debugger) 2508 3014 w +(monitor) 2910 3014 w +(on) 3258 3014 w +(board.) 3389 3014 w +(You) 3672 3014 w +(can) 3875 3014 w +(read) 4044 3014 w +(all) 4246 3014 w +(about) 4377 3014 w +(this) 4631 3014 w +(in) 4808 3014 w +(the) 4918 3014 w +(documentation.) 720 3134 w +(Whether) 1365 3134 w +(you) 1734 3134 w +(go to the trouble of installing the Windows tools is up to you, but do read how) 1910 3134 w +(to operate the monitor.) 720 3254 w +(Anyhow,) 970 3412 w +(the) 1370 3412 w +(board) 1523 3412 w +(has) 1781 3412 w +(two) 1945 3412 w +(serial) 2126 3412 w +(ports.) 2373 3412 w +(The) 2629 3412 w +(one) 2815 3412 w +(nearest) 2990 3412 w +(the) 3304 3412 w +(LEDs) 3458 3412 w +(is) 3723 3412 w +(the) 3822 3412 w +(debug) 3976 3412 w +(and) 4252 3412 w +(download) 4428 3412 w +(port.) 4854 3412 w +(The other is the console port when running Inferno.) 720 3532 w +(You) 970 3690 w +(only) 1172 3690 w +(get) 1380 3690 w +(one) 1532 3690 w +(cable,) 1706 3690 w +(so) 1971 3690 w +(beg) 2090 3690 w +(borrow) 2264 3690 w +(or) 2583 3690 w +(steal) 2697 3690 w +(another) 2911 3690 w +(cable.) 3241 3690 w +(These) 3507 3690 w +(are) 3776 3690 w +(straight\255through) 3928 3690 w +(9) 4603 3690 w +(way) 4684 3690 w +(to) 4881 3690 w +(9) 4990 3690 w +(way male to female cables. I bought a Belkin one from Staples at some horrid price.) 720 3810 w +(Connect) 970 3968 w +(both) 1336 3968 w +(up) 1547 3968 w +(to) 1680 3968 w +(the) 1791 3968 w +(back) 1946 3968 w +(of) 2168 3968 w +(your) 2285 3968 w +(Plan) 2502 3968 w +(9) 2714 3968 w +(machine,) 2798 3968 w +(and) 3195 3968 w +(start) 3373 3968 w +(terminal) 3579 3968 w +(emulators) 3946 3968 w +(on) 4374 3968 w +(both.) 4508 3968 w +(For) 4745 3968 w +(the) 4918 3968 w +(debug) 720 4088 w +(and) 990 4088 w +(download) 1160 4088 w +(you) 1580 4088 w +(need) 1756 4088 w +(to) 1970 4088 w +(run) 2074 4088 w +(a specially modified version of) 2233 4088 w +10 /Courier f +(vt\(1\)) 3495 4088 w +10 /Times-Roman f +(which responds to the peculiar) 3820 4088 w +(answer) 720 4208 w +(back) 1030 4208 w +(sequence) 1246 4208 w +(that) 1639 4208 w +(the) 1817 4208 w +(Arm) 1967 4208 w +(monitor) 2179 4208 w +(has) 2525 4208 w +(decided) 2687 4208 w +(is) 3026 4208 w +(appropriate) 3122 4208 w +(for) 3605 4208 w +(it) 3750 4208 w +(to) 3835 4208 w +(sense) 3942 4208 w +(baud) 4187 4208 w +(rate.) 4410 4208 w +(As) 4638 4208 w +(it) 4778 4208 w +(hap\255) 4863 4208 w +(pens,) 720 4328 w +(I) 968 4328 w +(have) 1041 4328 w +(modified) 1269 4328 w +(the) 1670 4328 w +(version) 1832 4328 w +(on) 2166 4328 w +10 /Courier f +(doppio) 2306 4328 w +10 /Times-Roman f +(.) 2666 4328 w +(It) 2756 4328 w +(should) 2856 4328 w +(be) 3162 4328 w +(possible) 3295 4328 w +(via) 3662 4328 w +(monitor) 3823 4328 w +(commands) 4179 4328 w +(to) 4651 4328 w +(set) 4768 4328 w +(the) 4918 4328 w +(default) 720 4448 w +(baud) 1031 4448 w +(rate) 1259 4448 w +(of) 1442 4448 w +(the) 1559 4448 w +(board) 1715 4448 w +(to) 1976 4448 w +(whatever) 2088 4448 w +(you) 2487 4448 w +(like.) 2671 4448 w +(I) 2880 4448 w +(lost) 2947 4448 w +(patience) 3126 4448 w +(after) 3492 4448 w +(about) 3708 4448 w +(a) 3964 4448 w +(day,) 4042 4448 w +(and) 4245 4448 w +(hacked) 4423 4448 w +10 /Courier f +(vt\(1\)) 4740 4448 w +10 /Times-Roman f +(instead.) 720 4568 w +(It) 1057 4568 w +(was) 1146 4568 w +(easier.) 1329 4568 w +(So,) 1614 4568 w +(the) 1773 4568 w +(board) 1923 4568 w +(will) 2178 4568 w +(auto) 2362 4568 w +(sense) 2562 4568 w +(baud) 2806 4568 w +(rate.) 3028 4568 w +(The) 3230 4568 w +(highest) 3413 4568 w +(baud) 3730 4568 w +(rate) 3952 4568 w +(which) 4129 4568 w +(is) 4401 4568 w +(actually) 4496 4568 w +(valid) 4840 4568 w +(and hence will talk to Plan 9 is 57600. So the commands to configure the download window are) 720 4688 w +9 /Courier-Bold f +(term%) 1008 4852 w +9 /Courier f +(cd /usr/inferno/os/ks32) 1332 4852 w +9 /Courier-Bold f +(term%) 1008 4952 w +9 /Courier f +(vt) 1332 4952 w +9 /Courier-Bold f +(term%) 1008 5052 w +9 /Courier f +(con \255R \255b 57600 /dev/eia0) 1332 5052 w +10 /Times-Roman f +(You) 720 5236 w +(can) 926 5236 w +(choose) 1098 5236 w +(other) 1409 5236 w +(speeds,) 1648 5236 w +(but) 1973 5236 w +(57600) 2135 5236 w +(is) 2419 5236 w +(the) 2520 5236 w +(maximum.) 2676 5236 w +(For) 3141 5236 w +(the) 3314 5236 w +(console) 3470 5236 w +(port,) 3809 5236 w +10 /Courier f +(vt\(1\)) 4029 5236 w +10 /Times-Roman f +(is) 4363 5236 w +(not) 4465 5236 w +(necessary.) 4628 5236 w +(The baud rate is fixed in) 720 5356 w +10 /Courier f +(main.c) 1718 5356 w +10 /Times-Roman f +(at 57600, so the same) 2103 5356 w +10 /Courier f +(con) 2991 5356 w +10 /Times-Roman f +(command is appropriate.) 3196 5356 w +(Now) 970 5514 w +(plug) 1195 5514 w +(in) 1404 5514 w +(the) 1513 5514 w +(power) 1666 5514 w +(to) 1946 5514 w +(the) 2055 5514 w +(board.) 2208 5514 w +(You) 2491 5514 w +(should) 2694 5514 w +(see) 2992 5514 w +(the) 3150 5514 w +(LEDs) 3303 5514 w +(flick) 3567 5514 w +(for) 3781 5514 w +(bit,) 3928 5514 w +(and) 4091 5514 w +(then) 4267 5514 w +(the) 4471 5514 w +(7) 4625 5514 w +(segment) 4707 5514 w +(one shows 11. The download window should show) 720 5634 w +9 /Courier-Bold f +(Arm Evaluator7T Boot Monitor PreRelease 1.00) 1008 5798 w +(Boot:) 1008 5898 w +10 /Times-Roman f +(Pressing) 720 6082 w +(the) 1088 6082 w +(reset) 1239 6082 w +(button) 1456 6082 w +(\(nearer) 1741 6082 w +(of) 2051 6082 w +(the) 2163 6082 w +(two) 2314 6082 w +(buttons) 2493 6082 w +(to) 2817 6082 w +(the) 2924 6082 w +(CPU\),) 3075 6082 w +(should) 3357 6082 w +(cause) 3653 6082 w +(the) 3903 6082 w +(same) 4055 6082 w +(message) 4290 6082 w +(to) 4658 6082 w +(be) 4766 6082 w +(dis\255) 4890 6082 w +(played.) 720 6202 w +10 /Times-Bold f +(Building the code) 720 6450 w +10 /Courier f +(mk\(1\)) 970 6608 w +10 /Times-Roman f +(in directory) 1295 6608 w +10 /Courier f +(/usr/inferno/os/ks32) 1783 6608 w +10 /Times-Roman f +(should build the code. You may need to do) 3008 6608 w +9 /Courier f +(bind \255b /usr/inferno/Plan9/386/bin /bin) 1008 6772 w +10 /Times-Roman f +(to get access to the compilers and tools.) 720 6956 w +(The) 2357 6956 w +(makes) 745 7076 w +(an) 1026 7076 w +(executable,) 1146 7076 w +(converts) 1623 7076 w +(it) 1987 7076 w +(to) 2069 7076 w +(the) 2173 7076 w +(right) 2321 7076 w +(format,) 2536 7076 w +(and) 2853 7076 w +10 /Courier f +(uuencode) 3024 7076 w +10 /Times-Roman f +('s) 3504 7076 w +(it) 3603 7076 w +(to) 3686 7076 w +(create) 3791 7076 w +10 /Courier f +(ievaluator7t.txt) 4055 7076 w +10 /Times-Roman f +(.) 5015 7076 w +(Note) 720 7196 w +(that) 1059 7196 w +(as) 1354 7196 w +(shipped,) 1582 7196 w +(the) 2063 7196 w +(configuration) 2330 7196 w +(file) 3013 7196 w +(\() 3291 7196 w +10 /Courier f +(evaluator7t) 3324 7196 w +10 /Times-Roman f +(\)) 3984 7196 w +(includes) 4162 7196 w +(the) 4640 7196 w +(file) 4907 7196 w +(November 22, 1900) 2482 7560 w +cleartomark +showpage +saveobj restore +%%EndPage: 1 1 +%%Page: 2 2 +/saveobj save def +mark +2 pagesetup +10 /Times-Roman f +(\255 2 \255) 2797 480 w +10 /Courier f +(/usr/inferno/usr/nigel/cb.dis) 720 840 w +10 /Times-Roman f +(as) 2504 840 w +(the) 2631 840 w +(main) 2797 840 w +(application.) 3041 840 w +(This) 3554 840 w +(is) 3776 840 w +(John) 3887 840 w +(Powers') 4121 840 w +(Crackerbarrel) 4493 840 w +(program which I used as a benchmark.) 720 960 w +10 /Times-Bold f +(Downloading the code) 720 1200 w +10 /Times-Roman f +(To download to the Evaluator type the command) 970 1356 w +9 /Courier-Bold f +(Boot:) 1008 1516 w +9 /Courier f +(download) 1332 1516 w +9 /Courier-Bold f +(Ready to download. Use 'transmit' option on terminal emulator to download file.) 1008 1616 w +10 /Times-Roman f +(break back to the) 720 1796 w +10 /Courier f +(con\(1\)) 1429 1796 w +10 /Times-Roman f +(command prompt with Control\255\\ and type the following command) 1814 1796 w +9 /Courier-Bold f +(>>>) 1008 1956 w +9 /Courier f +(!cat ievaluator7t.txt) 1224 1956 w +10 /Times-Roman f +(As a shorthand, you may instead type) 720 2136 w +9 /Courier-Bold f +(>>>) 1008 2296 w +9 /Courier f +(!squirt) 1224 2296 w +10 /Times-Roman f +(The) 970 2476 w +(red) 1152 2476 w +(LED) 1306 2476 w +(should) 1527 2476 w +(light) 1821 2476 w +(indicating) 2032 2476 w +(download.) 2459 2476 w +(After) 2905 2476 w +(a) 3142 2476 w +(couple) 3214 2476 w +(of) 3508 2476 w +(minutes) 3644 2476 w +(the) 3989 2476 w +(light) 4139 2476 w +(goes) 4351 2476 w +(out) 4562 2476 w +(and) 4718 2476 w +(you) 4890 2476 w +(see) 720 2596 w +9 /Courier-Bold f +(Loaded file ievaluator7t.aif at address 00008000, size=254200) 1008 2756 w +(Boot:) 1008 2856 w +10 /Times-Roman f +(You will type) 720 3036 w +9 /Courier-Bold f +(Boot:) 1008 3196 w +9 /Courier f +(gos 8080) 1332 3196 w +10 /Times-Roman f +(to run the code at address 0x8080. This) 720 3376 w +(should) 2316 3376 w +(cause) 2609 3376 w +(Inferno) 2856 3376 w +(to) 3175 3376 w +(boot) 3279 3376 w +(and) 3483 3376 w +(write) 3653 3376 w +(stuff) 3884 3376 w +(on) 4093 3376 w +(the) 4219 3376 w +(console) 4367 3376 w +(port) 4698 3376 w +(\(i.e.) 4885 3376 w +(the other window\).) 720 3496 w +10 /Times-Bold f +(Useful Stuff) 720 3736 w +10 /Times-Roman f +(There is some useful debugging code in) 970 3892 w +10 /Courier f +(archevaluator7t.c) 2587 3892 w +10 /Times-Roman f +(and other places.) 3632 3892 w +10 /Courier f +(setled7ascii\(\)) 720 4048 w +10 /Times-Roman f +(This) 970 4168 w +(function) 1177 4168 w +(takes) 1539 4168 w +(a) 1773 4168 w +(single) 1846 4168 w +(character,) 2115 4168 w +(and) 2534 4168 w +(attempts) 2708 4168 w +(to) 3077 4168 w +(put) 3185 4168 w +(it) 3343 4168 w +(on) 3429 4168 w +(the) 3559 4168 w +(LED.) 3711 4168 w +(Clearly) 3960 4168 w +(you) 4284 4168 w +(won't) 4464 4168 w +(get) 4727 4168 w +(M's) 4879 4168 w +(or N's. Read the source.) 970 4288 w +10 /Courier f +(iprint\(\)) 720 4444 w +10 /Times-Roman f +(Prints on the debug and download port.) 970 4564 w +(November 22, 1900) 2482 7560 w +cleartomark +showpage +saveobj restore +%%EndPage: 2 2 +%%Trailer +done +%%DocumentFonts: Times-Roman Times-Italic Times-Bold Courier Courier-Bold +%%Pages: 2 diff --git a/os/ks32/evaluator7t b/os/ks32/evaluator7t new file mode 100644 index 00000000..93723933 --- /dev/null +++ b/os/ks32/evaluator7t @@ -0,0 +1,110 @@ +dev + root + cons archevaluator7t noscreen not +# kbd +# gpio +# mnt +# pipe + prog +# srv +# draw + uart +# sapcm +# flash +# touch +# ip bootp ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium +# ether netif netaux ethermedium +# ata + +ip +# il +# tcp +# udp +# rudp +# igmp +# ipifc +# icmp +# icmp6 +# ipmux + +lib + interp +# tk +# image +# memlayer +# memimage +# keyring + sec +# mp + kern + +mod + sys +# draw +# tk +# keyring + +port + alarm + alloc + allocb + chan + dev + dial + dis + discall + exception + exportfs + inferno + latin1 + nocache + nodynld + noenv + parse + pgrp + print + proc + qio + qlock + random + sysfile + taslock + xalloc + +link +# lcd +# ether589 +# ethertdk +# pppmedium ppp compress + +code + int main_pool_pcnt = 60; + int heap_pool_pcnt = 40; + int image_pool_pcnt = 0; + int cflag = 0; + + int consoleprint = 1; + int redirectconsole = 1; + char debug_keys = 1; + int panicreset = 0; + void pseudoRandomBytes(uchar *a, int n){memset(a, 0, n);} + int srvf2c(){return -1;} /* dummy */ + Type *Trdchan; + Type *Twrchan; + +init + evalinit + +root + /chan + /dev + /dis +# /usr/jrf/limbo/cb.dis +# /usr/jrf/work/dl/test/test.dis +# /o/abc.o + /dis/sh.dis + /net + /prog + /osinit.dis + /n/remote + diff --git a/os/ks32/fns.h b/os/ks32/fns.h new file mode 100644 index 00000000..581590ab --- /dev/null +++ b/os/ks32/fns.h @@ -0,0 +1,145 @@ +#include "../port/portfns.h" + +ulong aifinit(uchar *aifarr); +void aamloop(int); +void archconfinit(void); +int archflash12v(int); +void archflashwp(int); +void archreboot(void); +void archreset(void); +void catchDref(char *s, void *v); +void catchDval(char *s, ulong v, ulong m); +void catchIref(char *s, void *a); +void cisread(int slotno, void (*f)(int, uchar *)); +int cistrcmp(char *, char *); +void cleanDentry(void *); +void clockcheck(void); +void clockinit(void); +void clockpoll(void); +#define coherence() /* nothing to do for cache coherence for uniprocessor */ +uint cpsrr(void); +void cursorhide(void); +void cursorunhide(void); +void dmasetup(int channel, int device, int direction, int endianess); +void dmastart(int channel, void *b1, int b1siz, void *b2, int b2siz); +int dmacontinue(int channel, void *buf, int bufsize); +void dmastop(int channel); +int dmaerror(int channel); +void dmareset(void); +void drainWBuffer(void); +void dumplongs(char *, ulong *, int); +void dumpregs(Ureg* ureg); +void dumpstk(ulong *); +void flushDcache(void); +void flushIDC(void); +void flushIcache(void); +void flushDentry(void *); +void flushTLB(void); +int fpiarm(Ureg*); +void fpinit(void); +ulong getcallerpc(void*); +void gotopc(ulong); +#define idlehands() /* nothing to do in the runproc */ +void intrenable(int, void (*)(Ureg*, void*), void*, int); +void intrclear(int, int); +void intrmask(int, int); +void intrunmask(int, int); +int iprint(char *fmt, ...); +void installprof(void (*)(Ureg *, int)); +int isvalid_va(void*); +void kbdinit(void); +void lcd_setbacklight(int); +void lcd_setbrightness(ushort); +void lcd_setcontrast(ushort); +void lcd_sethz(int); +void lights(ulong); +void setled7ascii(char); +void links(void); +ulong mcpgettfreq(void); +void mcpinit(void); +void mcpsettfreq(ulong tfreq); +void mcpspeaker(int, int); +void mcptelecomsetup(ulong hz, uchar adm, uchar xint, uchar rint); +ushort mcpadcread(int ts); +void mcptouchsetup(int ts); +void mcptouchintrenable(void); +void mcptouchintrdisable(void); +void mcpgpiowrite(ushort mask, ushort data); +void mcpgpiosetdir(ushort mask, ushort dir); +ushort mcpgpioread(void); +void mmuinit(void); +ulong mmuctlregr(void); +void mmuctlregw(ulong); +ulong mmuregr(int); +void mmuregw(int, ulong); +void mmureset(void); +void mouseinit(void); +void nowriteSeg(void *, void *); +void* pa2va(ulong); +int pcmpin(int slot, int type); +void pcmpower(int slotno, int on); +int pcmpowered(int slotno); +void pcmsetvcc(int slotno, int vcc); +void pcmsetvpp(int slotno, int vpp); +int pcmspecial(char *idstr, ISAConf *isa); +void pcmspecialclose(int slotno); +void pcmintrenable(int, void (*)(Ureg*, void*), void*); +void putcsr(ulong); +#define procsave(p) +#define procrestore(p) +void remaplomem(void); +long rtctime(void); +void* screenalloc(ulong); +void screeninit(void); +void screenputs(char*, int); +int segflush(void*, ulong); +void setpanic(void); +void setr13(int, void*); +uint spsrr(void); +void touchrawcal(int q, int px, int py); +int touchcalibrate(void); +int touchreadxy(int *fx, int *fy); +int touchpressed(void); +int touchreleased(void); +void touchsetrawcal(int q, int n, int v); +int touchgetrawcal(int q, int n); +void trapinit(void); +void trapspecial(int (*)(Ureg *, uint)); +int uartprint(char*, ...); +void uartspecial(int, int, char, Queue**, Queue**, int (*)(Queue*, int)); +void umbfree(ulong addr, int size); +ulong umbmalloc(ulong addr, int size, int align); +void umbscan(void); +ulong va2pa(void*); +void vectors(void); +void vtable(void); +#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) +int wasbusy(int); +void _vfiqcall(void); +void _virqcall(void); +void _vundcall(void); +void _vsvccall(void); +void _vpabcall(void); +void _vdabcall(void); +void vgaputc(char); +void writeBackBDC(void); +void writeBackDC(void); + +#define KADDR(p) ((void *) p) +#define PADDR(v) va2pa((void*)(v)) + +// #define timer_start() (*OSCR) +// #define timer_ticks(t) (*OSCR - (ulong)(t)) +#define DELAY(ms) timer_delay(MS2TMR(ms)) +#define MICRODELAY(us) timer_delay(US2TMR(us)) +ulong timer_start(void); +ulong timer_ticks(ulong); +int timer_devwait(ulong *adr, ulong mask, ulong val, int ost); +void timer_setwatchdog(int ost); +void timer_delay(int ost); +ulong ms2tmr(int ms); +int tmr2ms(ulong t); +void delay(int ms); +ulong us2tmr(int us); +int tmr2us(ulong t); +void microdelay(int us); diff --git a/os/ks32/fpi.h b/os/ks32/fpi.h new file mode 100644 index 00000000..dfb9b1df --- /dev/null +++ b/os/ks32/fpi.h @@ -0,0 +1,61 @@ +typedef long Word; +typedef unsigned long Single; +typedef struct { + unsigned long h; + unsigned long l; +} Double; + +enum { + FractBits = 28, + CarryBit = 0x10000000, + HiddenBit = 0x08000000, + MsBit = HiddenBit, + NGuardBits = 3, + GuardMask = 0x07, + LsBit = (1<e >= ExpInfinity) +#define IsInfinity(n) (IsWeird(n) && (n)->h == HiddenBit && (n)->l == 0) +#define SetInfinity(n) ((n)->e = ExpInfinity, (n)->h = HiddenBit, (n)->l = 0) +#define IsNaN(n) (IsWeird(n) && (((n)->h & ~HiddenBit) || (n)->l)) +#define SetQNaN(n) ((n)->s = 0, (n)->e = ExpInfinity, \ + (n)->h = HiddenBit|(LsBit<<1), (n)->l = 0) +#define IsZero(n) ((n)->e == 1 && (n)->h == 0 && (n)->l == 0) +#define SetZero(n) ((n)->e = 1, (n)->h = 0, (n)->l = 0) + +/* + * fpi.c + */ +extern void fpiround(Internal *); +extern void fpiadd(Internal *, Internal *, Internal *); +extern void fpisub(Internal *, Internal *, Internal *); +extern void fpimul(Internal *, Internal *, Internal *); +extern void fpidiv(Internal *, Internal *, Internal *); +extern int fpicmp(Internal *, Internal *); +extern void fpinormalise(Internal*); + +/* + * fpimem.c + */ +extern void fpis2i(Internal *, void *); +extern void fpid2i(Internal *, void *); +extern void fpiw2i(Internal *, void *); +extern void fpii2s(void *, Internal *); +extern void fpii2d(void *, Internal *); +extern void fpii2w(Word *, Internal *); diff --git a/os/ks32/fpiarm.c b/os/ks32/fpiarm.c new file mode 100644 index 00000000..717965b3 --- /dev/null +++ b/os/ks32/fpiarm.c @@ -0,0 +1,483 @@ +/* + * this doesn't attempt to implement ARM floating-point properties + * that aren't visible in the Inferno environment. + * all arithmetic is done in double precision. + * the FP trap status isn't updated. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "ureg.h" + +#include "fpi.h" + +// #define R13OK undef this if correct kernel r13 isn't in Ureg; check calculation in fpiarm below + +#define REG(x) (*(long*)(((char*)ur)+roff[(x)])) +#define FPENV (*ufp) +#define FR(x) (*(Internal*)ufp->regs[(x)&7]) + +/* BUG: check fetch (not worthwhile in Inferno) */ +#define getubyte(a) (*(uchar*)(a)) +#define getuword(a) (*(ushort*)(a)) +#define getulong(a) (*(ulong*)(a)) + +typedef struct FP2 FP2; +typedef struct FP1 FP1; + +struct FP2 { + char* name; + void (*f)(Internal, Internal, Internal*); +}; + +struct FP1 { + char* name; + void (*f)(Internal*, Internal*); +}; + +enum { + N = 1<<31, + Z = 1<<30, + C = 1<<29, + V = 1<<28, + REGPC = 15, +}; + +int fpemudebug = 0; + +#undef OFR +#define OFR(X) ((ulong)&((Ureg*)0)->X) + +static int roff[] = { + OFR(r0), OFR(r1), OFR(r2), OFR(r3), + OFR(r4), OFR(r5), OFR(r6), OFR(r7), + OFR(r8), OFR(r9), OFR(r10), OFR(r11), +#ifdef R13OK + OFR(r12), OFR(r13), OFR(r14), OFR(pc), +#else + OFR(r12), OFR(type), OFR(r14), OFR(pc), +#endif +}; + +static Internal fpconst[8] = { /* indexed by op&7 */ + /* s, e, l, h */ + {0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */ + {0, 0x3FF, 0x00000000, 0x08000000}, /* 1.0 */ + {0, 0x400, 0x00000000, 0x08000000}, /* 2.0 */ + {0, 0x400, 0x00000000, 0x0C000000}, /* 3.0 */ + {0, 0x401, 0x00000000, 0x08000000}, /* 4.0 */ + {0, 0x401, 0x00000000, 0x0A000000}, /* 5.0 */ + {0, 0x3FE, 0x00000000, 0x08000000}, /* 0.5 */ + {0, 0x402, 0x00000000, 0x0A000000}, /* 10.0 */ +}; + +/* + * arm binary operations + */ + +static void +fadd(Internal m, Internal n, Internal *d) +{ + (m.s == n.s? fpiadd: fpisub)(&m, &n, d); +} + +static void +fsub(Internal m, Internal n, Internal *d) +{ + m.s ^= 1; + (m.s == n.s? fpiadd: fpisub)(&m, &n, d); +} + +static void +fsubr(Internal m, Internal n, Internal *d) +{ + n.s ^= 1; + (n.s == m.s? fpiadd: fpisub)(&n, &m, d); +} + +static void +fmul(Internal m, Internal n, Internal *d) +{ + fpimul(&m, &n, d); +} + +static void +fdiv(Internal m, Internal n, Internal *d) +{ + fpidiv(&m, &n, d); +} + +static void +fdivr(Internal m, Internal n, Internal *d) +{ + fpidiv(&n, &m, d); +} + +/* + * arm unary operations + */ + +static void +fmov(Internal *m, Internal *d) +{ + *d = *m; +} + +static void +fmovn(Internal *m, Internal *d) +{ + *d = *m; + d->s ^= 1; +} + +static void +fabsf(Internal *m, Internal *d) +{ + *d = *m; + d->s = 0; +} + +static void +frnd(Internal *m, Internal *d) +{ + short e; + + (m->s? fsub: fadd)(fpconst[6], *m, d); + if(IsWeird(d)) + return; + fpiround(d); + e = (d->e - ExpBias) + 1; + if(e <= 0) + SetZero(d); + else if(e > FractBits){ + if(e < 2*FractBits) + d->l &= ~((1<<(2*FractBits - e))-1); + }else{ + d->l = 0; + if(e < FractBits) + d->h &= ~((1<<(FractBits-e))-1); + } +} + +static FP1 optab1[16] = { /* Fd := OP Fm */ +[0] {"MOVF", fmov}, +[1] {"NEGF", fmovn}, +[2] {"ABSF", fabsf}, +[3] {"RNDF", frnd}, +[4] {"SQTF", /*fsqt*/0}, +/* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */ +/* URD and NRM aren't implemented */ +}; + +static FP2 optab2[16] = { /* Fd := Fn OP Fm */ +[0] {"ADDF", fadd}, +[1] {"MULF", fmul}, +[2] {"SUBF", fsub}, +[3] {"RSUBF", fsubr}, +[4] {"DIVF", fdiv}, +[5] {"RDIVF", fdivr}, +/* POW, RPW deprecated */ +[8] {"REMF", /*frem*/0}, +[9] {"FMF", fmul}, /* fast multiply */ +[10] {"FDV", fdiv}, /* fast divide */ +[11] {"FRD", fdivr}, /* fast reverse divide */ +/* POL deprecated */ +}; + +static ulong +fcmp(Internal *n, Internal *m) +{ + int i; + + if(IsWeird(m) || IsWeird(n)){ + /* BUG: should trap if not masked */ + return V|C; + } + i = fpicmp(n, m); + if(i > 0) + return C; + else if(i == 0) + return C|Z; + else + return N; +} + +static void +fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPenv *ufp) +{ + void *mem; + + mem = (void*)ea; + (*f)(&FR(d), mem); + if(fpemudebug) + print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d); +} + +static void +fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPenv *ufp) +{ + Internal tmp; + void *mem; + + mem = (void*)ea; + tmp = FR(s); + if(fpemudebug) + print("MOV%c F%d,#%lux\n", n==8? 'D': 'F', s, ea); + (*f)(mem, &tmp); +} + +static int +condok(int cc, int c) +{ + switch(c){ + case 0: /* Z set */ + return cc&Z; + case 1: /* Z clear */ + return (cc&Z) == 0; + case 2: /* C set */ + return cc&C; + case 3: /* C clear */ + return (cc&C) == 0; + case 4: /* N set */ + return cc&N; + case 5: /* N clear */ + return (cc&N) == 0; + case 6: /* V set */ + return cc&V; + case 7: /* V clear */ + return (cc&V) == 0; + case 8: /* C set and Z clear */ + return cc&C && (cc&Z) == 0; + case 9: /* C clear or Z set */ + return (cc&C) == 0 || cc&Z; + case 10: /* N set and V set, or N clear and V clear */ + return (~cc&(N|V))==0 || (cc&(N|V)) == 0; + case 11: /* N set and V clear, or N clear and V set */ + return (cc&(N|V))==N || (cc&(N|V))==V; + case 12: /* Z clear, and either N set and V set or N clear and V clear */ + return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0); + case 13: /* Z set, or N set and V clear or N clear and V set */ + return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V; + case 14: /* always */ + return 1; + case 15: /* never (reserved) */ + return 0; + } + return 0; /* not reached */ +} + +static void +unimp(ulong pc, ulong op) +{ + char buf[60]; + + snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op); + if(fpemudebug) + print("FPE: %s\n", buf); + error(buf); + /* no return */ +} + +static void +fpemu(ulong pc, ulong op, Ureg *ur, FPenv *ufp) +{ + int rn, rd, tag, o; + long off; + ulong ea; + Internal tmp, *fm, *fn; + + /* note: would update fault status here if we noted numeric exceptions */ + + /* + * LDF, STF; 10.1.1 + */ + if(((op>>25)&7) == 6){ + if(op & (1<<22)) + unimp(pc, op); /* packed or extended */ + rn = (op>>16)&0xF; + off = (op&0xFF)<<2; + if((op & (1<<23)) == 0) + off = -off; + ea = REG(rn); + if(rn == REGPC) + ea += 8; + if(op & (1<<24)) + ea += off; + rd = (op>>12)&7; + if(op & (1<<20)){ + if(op & (1<<15)) + fld(fpid2i, rd, ea, 8, ufp); + else + fld(fpis2i, rd, ea, 4, ufp); + }else{ + if(op & (1<<15)) + fst(fpii2d, ea, rd, 8, ufp); + else + fst(fpii2s, ea, rd, 4, ufp); + } + if((op & (1<<24)) == 0) + ea += off; + if(op & (1<<21)) + REG(rn) = ea; + return; + } + + /* + * CPRT/transfer, 10.3 + */ + if(op & (1<<4)){ + rd = (op>>12) & 0xF; + + /* + * compare, 10.3.1 + */ + if(rd == 15 && op & (1<<20)){ + rn = (op>>16)&7; + fn = &FR(rn); + if(op & (1<<3)){ + fm = &fpconst[op&7]; + tag = 'C'; + }else{ + fm = &FR(op&7); + tag = 'F'; + } + switch((op>>21)&7){ + default: + unimp(pc, op); + case 4: /* CMF: Fn :: Fm */ + case 6: /* CMFE: Fn :: Fm (with exception) */ + ur->psr &= ~(N|C|Z|V); + ur->psr |= fcmp(fn, fm); + break; + case 5: /* CNF: Fn :: -Fm */ + case 7: /* CNFE: Fn :: -Fm (with exception) */ + tmp = *fm; + tmp.s ^= 1; + ur->psr &= ~(N|C|Z|V); + ur->psr |= fcmp(fn, &tmp); + break; + } + if(fpemudebug) + print("CMPF %c%d,F%ld =%x\n", tag, rn, op&7, ur->psr>>28); + return; + } + + /* + * other transfer, 10.3 + */ + switch((op>>20)&0xF){ + default: + unimp(pc, op); + case 0: /* FLT */ + rn = (op>>16) & 7; + fpiw2i(&FR(rn), ®(rd)); + if(fpemudebug) + print("MOVW[FD] R%d, F%d\n", rd, rn); + break; + case 1: /* FIX */ + if(op & (1<<3)) + unimp(pc, op); + rn = op & 7; + tmp = FR(rn); + fpii2w(®(rd), &tmp); + if(fpemudebug) + print("MOV[FD]W F%d, R%d =%ld\n", rn, rd, REG(rd)); + break; + case 2: /* FPSR := Rd */ + FPENV.status = REG(rd); + if(fpemudebug) + print("MOVW R%d, FPSR\n", rd); + break; + case 3: /* Rd := FPSR */ + REG(rd) = FPENV.status; + if(fpemudebug) + print("MOVW FPSR, R%d\n", rd); + break; + case 4: /* FPCR := Rd */ + FPENV.control = REG(rd); + if(fpemudebug) + print("MOVW R%d, FPCR\n", rd); + break; + case 5: /* Rd := FPCR */ + REG(rd) = FPENV.control; + if(fpemudebug) + print("MOVW FPCR, R%d\n", rd); + break; + } + return; + } + + /* + * arithmetic + */ + + if(op & (1<<3)){ /* constant */ + fm = &fpconst[op&7]; + tag = 'C'; + }else{ + fm = &FR(op&7); + tag = 'F'; + } + rd = (op>>12)&7; + o = (op>>20)&0xF; + if(op & (1<<15)){ /* monadic */ + FP1 *fp; + fp = &optab1[o]; + if(fp->f == nil) + unimp(pc, op); + if(fpemudebug) + print("%s %c%ld,F%d\n", fp->name, tag, op&7, rd); + (*fp->f)(fm, &FR(rd)); + } else { + FP2 *fp; + fp = &optab2[o]; + if(fp->f == nil) + unimp(pc, op); + rn = (op>>16)&7; + if(fpemudebug) + print("%s %c%ld,F%d,F%d\n", fp->name, tag, op&7, rn, rd); + (*fp->f)(*fm, FR(rn), &FR(rd)); + } +} + +/* + * returns the number of FP instructions emulated + */ +int +fpiarm(Ureg *ur) +{ + ulong op, o; + FPenv *ufp; + int n; + +#ifndef R13OK +/* ur->type = &ur->pc+1; /* calculate kernel sp/R13 and put it here for roff[13] */ + ur->type = (ulong)(ur + 1); +#endif + if (up == nil) + panic("fpiarm not in a process"); + ufp = &up->env->fpu; /* because all the state is in Osenv, it need not be saved/restored */ + if(ufp->fpistate != FPACTIVE) { + ufp->fpistate = FPACTIVE; + ufp->control = 0; + ufp->status = (0x01<<28)|(1<<12); /* software emulation, alternative C flag */ + for(n = 0; n < 8; n++) + FR(n) = fpconst[0]; + } + for(n=0;;n++){ + op = getulong(ur->pc); + o = (op>>24) & 0xF; + if(((op>>8) & 0xF) != 1 || o != 0xE && (o&~1) != 0xC) + break; + if(condok(ur->psr, op>>28)) + fpemu(ur->pc, op, ur, ufp); + ur->pc += 4; + if(anyhigher()) + sched(); + } + return n; +} diff --git a/os/ks32/io.h b/os/ks32/io.h new file mode 100644 index 00000000..5e172140 --- /dev/null +++ b/os/ks32/io.h @@ -0,0 +1,168 @@ +/* + * Memory Map for Samsung ks32c50100 + */ + +#define SFRbase 0x7ff0000 + +#define SYSCFG (*(ulong *)(SFRbase + 0)) + +#define IOPbase (SFRbase + 0x5000) +#define IOPMOD (*(ulong *)(IOPbase + 0)) +#define IOPCON (*(ulong *)(IOPbase + 4)) +#define IOPDATA (*(ulong *)(IOPbase + 8)) + +#define MaxIRQbit 20 /* Maximum IRQ */ +#define EXT0bit 0 +#define EXT1bit 1 +#define EXT2bit 2 +#define EXT3bit 3 +#define UART0TXbit 4 +#define UART0RXbit 5 +#define UART1TXbit 6 +#define UART1RXbit 7 +#define GDMA0 8 +#define GDMA1 9 +#define TIMER0bit 10 +#define TIMER1bit 11 +#define HDLCATXbit 12 +#define HDLCARXbit 13 +#define HDLCBTXbit 14 +#define HDLCBRXbit 15 +#define ETHBDMATXbit 16 +#define ETHBDMARXbit 17 +#define ETHMACRXint 18 +#define ETHMAXTXint 19 +#define IICbit 20 + +#define TIMERbit(n) (TIMER0bit + n) +#define UARTTXbit(n) (UART0TXbit + (n) * 2) +#define UARTRXbit(n) (UART0RXbit + (n) * 2) + +/* + * Interrupt controller + */ + +#define INTbase (SFRbase + 0x4000) +#define INTREG ((IntReg *)INTbase) + +typedef struct IntReg IntReg; +struct IntReg { + ulong mod; /* 00 */ + ulong pnd; /* 04 */ + ulong msk; /* 08 */ + ulong pri[6]; /* 0c */ + ulong offset; /* 24 */ + ulong pndpri; /* 28 */ + ulong pndtst; /* 2c */ + ulong oset_fiq; /* 30 */ + ulong oset_irq; /* 34 */ +}; + +/* + * UARTs + */ +#define UART0base (SFRbase + 0xd000) +#define UART1base (SFRbase + 0xe000) +#define UARTREG ((UartReg *)UART0base) + +typedef struct UartReg UartReg; +struct UartReg { + ulong lcon; /* 00 */ + ulong con; /* 04 */ + ulong stat; /* 08 */ + ulong txbuf; /* 0c */ + ulong rxbuf; /* 10 */ + ulong brdiv; /* 14 */ + ulong pad[(UART1base - UART0base - 0x18) / 4]; +}; + +#define ULCON_WLMASK 0x03 +#define ULCON_WL5 0x00 +#define ULCON_WL6 0x01 +#define ULCON_WL7 0x02 +#define ULCON_WL8 0x03 + +#define ULCON_STOPMASK 0x04 +#define ULCON_STOP1 0x00 +#define ULCON_STOP2 0x04 + +#define ULCON_PMDMASK 0x38 +#define ULCON_PMDNONE 0x00 +#define ULCON_PMDODD (4 << 3) +#define ULCON_PMDEVEN (5 << 3) +#define ULCON_PMDFORCE1 (6 << 3) +#define ULCON_PMDFORCE0 (7 << 3) + +#define ULCON_CLOCKMASK 0x40 +#define ULCON_CLOCKMCLK 0x00 +#define ULCON_CLOCKUCLK (1 << 6) + +#define ULCON_IRMASK 0x80 +#define ULCON_IROFF 0x00 +#define ULCON_IRON 0x80 + +#define UCON_RXMDMASK 0x03 +#define UCON_RXMDOFF 0x00 +#define UCON_RXMDINT 0x01 +#define UCON_RXMDGDMA0 0x02 +#define UCON_RXMDGDMA1 0x03 + +#define UCON_SINTMASK 0x04 +#define UCON_SINTOFF 0x00 +#define UCON_SINTON 0x04 + +#define UCON_TXMDMASK 0x18 +#define UCON_TXMDOFF (0 << 3) +#define UCON_TXMDINT (1 << 3) +#define UCON_TXMDGDMA0 (2 << 3) +#define UCON_TXMDGDMA1 (3 << 3) + +#define UCON_DSRMASK 0x20 +#define UCON_DSRON (1 << 5) +#define UCON_DSROFF (0 << 5) + +#define UCON_BRKMASK 0x40 +#define UCON_BRKON (1 << 6) +#define UCON_BRKOFF (0 << 6) + +#define UCON_LOOPMASK 0x80 +#define UCON_LOOPON 0x80 +#define UCON_LOOPOFF 0x00 + +#define USTAT_OV 0x01 +#define USTAT_PE 0x02 +#define USTAT_FE 0x04 +#define USTAT_BKD 0x08 +#define USTAT_DTR 0x10 +#define USTAT_RDR 0x20 +#define USTAT_TBE 0x40 +#define USTAT_TC 0x80 + +/* + * Timers + */ +#define TIMERbase (SFRbase + 0x6000) +#define TIMERREG ((TimerReg *)TIMERbase) + +typedef struct TimerReg TimerReg; +struct TimerReg { + ulong mod; + ulong data[2]; + ulong cnt[2]; +}; + +/* + * PC compatibility support for PCMCIA drivers + */ + +extern ulong ins(ulong); /* return ulong to prevent unecessary compiler shifting */ +void outs(ulong, int); +#define inb(addr) (*((uchar*)(addr))) +#define inl(addr) (*((ulong*)(addr))) +ulong ins(ulong); +#define outb(addr, val) *((uchar*)(addr)) = (val) +#define outl(addr, val) *((ulong*)(addr)) = (val) + +void inss(ulong, void*, int); +void outss(ulong, void*, int); + diff --git a/os/ks32/l.s b/os/ks32/l.s new file mode 100644 index 00000000..30922a2d --- /dev/null +++ b/os/ks32/l.s @@ -0,0 +1,205 @@ +#include "mem.h" + +/* + * Entered from the boot loader with + * supervisor mode, interrupts disabled; + */ + +TEXT _startup(SB), $-4 + MOVW $setR12(SB), R12 /* static base (SB) */ + MOVW $Mach0(SB), R13 + ADD $(KSTACK-4), R13 /* leave 4 bytes for link */ + + MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R1 /* Switch to SVC mode */ + MOVW R1, CPSR + + BL main(SB) /* jump to kernel */ + +dead: + B dead + BL _div(SB) /* hack to get _div etc loaded */ + +GLOBL Mach0(SB), $KSTACK + +TEXT setr13(SB), $-4 + MOVW 4(FP), R1 + + MOVW CPSR, R2 + BIC $PsrMask, R2, R3 + ORR R0, R3 + MOVW R3, CPSR + + MOVW R13, R0 + MOVW R1, R13 + + MOVW R2, CPSR + RET + +TEXT _vundcall(SB), $-4 +_vund: + MOVM.DB [R0-R3], (R13) + MOVW $PsrMund, R0 + B _vswitch + +TEXT _vsvccall(SB), $-4 +_vsvc: + MOVW.W R14, -4(R13) + MOVW CPSR, R14 + MOVW.W R14, -4(R13) + BIC $PsrMask, R14 + ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14 + MOVW R14, CPSR + MOVW $PsrMsvc, R14 + MOVW.W R14, -4(R13) + B _vsaveu + +TEXT _vpabcall(SB), $-4 +_vpab: + MOVM.DB [R0-R3], (R13) + MOVW $PsrMabt, R0 + B _vswitch + +TEXT _vdabcall(SB), $-4 +_vdab: + MOVM.DB [R0-R3], (R13) + MOVW $(PsrMabt+1), R0 + B _vswitch + +TEXT _vfiqcall(SB), $-4 /* IRQ */ +_vfiq: /* FIQ */ + MOVM.DB [R0-R3], (R13) + MOVW $PsrMfiq, R0 + B _vswitch + +TEXT _virqcall(SB), $-4 /* IRQ */ +_virq: + MOVM.DB [R0-R3], (R13) + MOVW $PsrMirq, R0 + +_vswitch: /* switch to svc mode */ + MOVW SPSR, R1 + MOVW R14, R2 + MOVW R13, R3 + + MOVW CPSR, R14 + BIC $PsrMask, R14 + ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14 + MOVW R14, CPSR + + MOVM.DB.W [R0-R2], (R13) + MOVM.DB (R3), [R0-R3] + +_vsaveu: /* Save Registers */ + MOVW.W R14, -4(R13) /* save link */ +/* MCR CpMMU, 0, R0, C(0), C(0), 0 */ + + SUB $8, R13 + MOVM.DB.W [R0-R12], (R13) + + MOVW R0, R0 /* gratuitous noop */ + + MOVW $setR12(SB), R12 /* static base (SB) */ + MOVW R13, R0 /* argument is ureg */ + SUB $8, R13 /* space for arg+lnk*/ + BL trap(SB) + + +_vrfe: /* Restore Regs */ + MOVW CPSR, R0 /* splhi on return */ + ORR $(PsrDirq|PsrDfiq), R0, R1 + MOVW R1, CPSR + ADD $(8+4*15), R13 /* [r0-R14]+argument+link */ + MOVW (R13), R14 /* restore link */ + MOVW 8(R13), R0 + MOVW R0, SPSR + MOVM.DB.S (R13), [R0-R14] /* restore user registers */ + MOVW R0, R0 /* gratuitous nop */ + ADD $12, R13 /* skip saved link+type+SPSR*/ + RFE /* MOVM.IA.S.W (R13), [R15] */ + +TEXT splhi(SB), $-4 + MOVW CPSR, R0 + ORR $(PsrDirq), R0, R1 + MOVW R1, CPSR + RET + +TEXT spllo(SB), $-4 + MOVW CPSR, R0 + BIC $(PsrDirq|PsrDfiq), R0, R1 + MOVW R1, CPSR + RET + +TEXT splx(SB), $-4 + /* BUG - save PC in m->splpc - JB */ + +TEXT splxpc(SB), $-4 + MOVW R0, R1 + MOVW CPSR, R0 + MOVW R1, CPSR + RET + +TEXT islo(SB), $-4 + MOVW CPSR, R0 + AND $(PsrDirq), R0 + EOR $(PsrDirq), R0 + RET + +TEXT splfhi(SB), $-4 + MOVW CPSR, R0 + ORR $(PsrDfiq|PsrDirq), R0, R1 + MOVW R1, CPSR + RET + +TEXT splflo(SB), $-4 + MOVW CPSR, R0 + BIC $(PsrDfiq), R0, R1 + MOVW R1, CPSR + RET + +TEXT cpsrr(SB), $-4 + MOVW CPSR, R0 + RET + +TEXT spsrr(SB), $-4 + MOVW SPSR, R0 + RET + +TEXT getcallerpc(SB), $-4 + MOVW 0(R13), R0 + RET + +TEXT _tas(SB), $-4 + MOVW R0, R1 + MOVW $0xDEADDEAD, R2 + SWPW R2, (R1), R0 + RET + +TEXT setlabel(SB), $-4 + MOVW R13, 0(R0) /* sp */ + MOVW R14, 4(R0) /* pc */ + MOVW $0, R0 + RET + +TEXT gotolabel(SB), $-4 + MOVW 0(R0), R13 /* sp */ + MOVW 4(R0), R14 /* pc */ + MOVW $1, R0 + BX (R14) + +TEXT outs(SB), $-4 + MOVW 4(FP),R1 + WORD $0xe1c010b0 /* STR H R1,[R0+0] */ + RET + +TEXT ins(SB), $-4 + WORD $0xe1d000b0 /* LDRHU R0,[R0+0] */ + RET + +/* for devboot */ +TEXT gotopc(SB), $-4 +/* + MOVW R0, R1 + MOVW bootparam(SB), R0 + MOVW R1, PC +*/ + RET diff --git a/os/ks32/main.c b/os/ks32/main.c new file mode 100644 index 00000000..f338c5ca --- /dev/null +++ b/os/ks32/main.c @@ -0,0 +1,289 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "io.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "version.h" + +Mach *m = (Mach*)MACHADDR; +Proc *up = 0; +Conf conf; + +extern ulong kerndate; +extern int cflag; +extern int consoleprint; +extern int redirectconsole; +extern int main_pool_pcnt; +extern int heap_pool_pcnt; +extern int image_pool_pcnt; +extern int kernel_pool_pcnt; + +int +segflush(void *p, ulong l) +{ + USED(p, l); + return 1; +} + +static void +poolsizeinit(void) +{ + ulong nb; + + nb = conf.npage*BY2PG; + iprint("free memory %ld\n", nb); + poolsize(mainmem, (nb*main_pool_pcnt)/100, 0); + poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0); + poolsize(imagmem, (nb*image_pool_pcnt)/100, 1); +} + +void +reboot(void) +{ + exit(0); +} + +void +halt(void) +{ + spllo(); + print("cpu halted\n"); + while(1); +} + +void +confinit(void) +{ + ulong base; + + archconfinit(); + + base = PGROUND((ulong)end); + conf.base0 = base; + + conf.base1 = 0; + conf.npage1 = 0; + + conf.npage0 = (conf.topofmem - base)/BY2PG; + + conf.npage = conf.npage0 + conf.npage1; + conf.ialloc = (((conf.npage*(main_pool_pcnt))/100)/2)*BY2PG; + + + conf.nproc = 20; +// conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5; + conf.nmach = 1; +} + +void +machinit(void) +{ + memset(m, 0, sizeof(Mach)); /* clear the mach struct */ +} + +void +cachemode(int size, int cenable, int wbenable) +{ + ulong sc = SYSCFG; + int cm; + + switch (size) { + case 0: + default: + cm = 2; + break; + case 4096: + cm = 0; + break; + case 8192: + cm = 1; + break; + } + sc &= ~((3 << 4) | (1 << 2) | (1 << 1)); + SYSCFG = sc | (cm << 4) | (cenable << 1) | (wbenable << 2); +} + +void +serputc() +{ + // dummy routine +} + +void +main(void) +{ + long *p, *ep; + + /* clear the BSS by hand */ + p = (long*)edata; + ep = (long*)end; + while(p < ep) + *p++ = 0; + // memset(edata, 0, end-edata); /* clear the BSS */ + cachemode(8192, 1, 1); + machinit(); + archreset(); + confinit(); + links(); + xinit(); + poolinit(); + poolsizeinit(); + trapinit(); +// mmuctlregw(mmuctlregr() | CpCDcache | CpCwb | CpCi32 | CpCd32 | CpCIcache); + clockinit(); + printinit(); +// screeninit(); + procinit(); + chandevreset(); + + eve = strdup("inferno"); + + archconsole(); +// else +// kbdinit(); + + print("\nInferno %s\n", VERSION); + print("conf %s (%lud) jit %d\n\n", conffile, kerndate, cflag); + userinit(); +// print("userinit over\n"); + schedinit(); +} + +void +init0(void) +{ + Osenv *o; + +// print("init0\n"); + up->nerrlab = 0; + spllo(); + if(waserror()) + panic("init0 %r"); + + /* + * These are o.k. because rootinit is null. + * Then early kproc's will have a root and dot. + */ + o = up->env; + o->pgrp->slash = namec("#/", Atodir, 0, 0); + cnameclose(o->pgrp->slash->name); + o->pgrp->slash->name = newcname("/"); + o->pgrp->dot = cclone(o->pgrp->slash); + + chandevinit(); + poperror(); +// iprint("init0: disinit\n"); +// print("CXXXYYYYYYYYZZZZZZZ\n"); + disinit("/osinit.dis"); +} + +void +userinit() +{ + Proc *p; + Osenv *o; + + p = newproc(); + o = p->env; + + o->fgrp = newfgrp(nil); + + o->pgrp = newpgrp(); + kstrdup(&o->user, eve); + + strcpy(p->text, "interp"); + + p->fpstate = FPINIT; + + /* + * Kernel Stack + * + * N.B. The -12 for the stack pointer is important. + * 4 bytes for gotolabel's return PC + */ + p->sched.pc = (ulong)init0; + p->sched.sp = (ulong)p->kstack+KSTACK-8; + + ready(p); +} + +void +exit(int inpanic) +{ + up = 0; + + /* Shutdown running devices */ + chandevshutdown(); + + if(inpanic){ + print("Hit the reset button\n"); + for(;;)clockpoll(); + } + archreboot(); +} + +static void +linkproc(void) +{ + spllo(); + if (waserror()) + print("error() underflow: %r\n"); + else + (*up->kpfun)(up->arg); + pexit("end proc", 1); +} + +void +kprocchild(Proc *p, void (*func)(void*), void *arg) +{ + p->sched.pc = (ulong)linkproc; + p->sched.sp = (ulong)p->kstack+KSTACK-8; + + p->kpfun = func; + p->arg = arg; +} + +/* stubs */ +void +setfsr(ulong x) { +USED(x); +} + +ulong +getfsr(){ +return 0; +} + +void +setfcr(ulong x) { +USED(x); +} + +ulong +getfcr(){ +return 0; +} + +void +fpinit(void) +{ +} + +void +FPsave(void*) +{ +} + +void +FPrestore(void*) +{ +} + +ulong +va2pa(void *v) +{ + return (ulong)v; +} + diff --git a/os/ks32/mem.h b/os/ks32/mem.h new file mode 100644 index 00000000..2b815feb --- /dev/null +++ b/os/ks32/mem.h @@ -0,0 +1,54 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ + +/* + * Sizes + */ +#define _K_ 1024 /* 2^10 -> Kilo */ +#define _M_ 1048576 /* 2^20 -> Mega */ +#define _G_ 1073741824 /* 2^30 -> Giga */ +#define _T_ 1099511627776UL /* 2^40 -> Tera */ +#define BI2BY 8 /* bits per byte */ +#define BI2WD 32 /* bits per word */ +#define BY2WD 4 /* bytes per word */ +#define BY2V 8 /* bytes per double word */ +#define BY2PG 4096 /* bytes per page */ +#define WD2PG (BY2PG/BY2WD) /* words per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1)) +#define PGROUND(s) ROUND(s, BY2PG) +#define BIT(n) (1<entry +#define KSTACK 8192 /* Size of kernel stack */ + +#include "armv7.h" diff --git a/os/ks32/mkfile b/os/ks32/mkfile new file mode 100644 index 00000000..c5868299 --- /dev/null +++ b/os/ks32/mkfile @@ -0,0 +1,115 @@ +<../../mkconfig + +#Configurable parameters + +CONF=evaluator7t #default configuration +CONFLIST=evaluator7t + +SYSTARG=$OSTARG +OBJTYPE=arm +INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin #path of directory where kernel is installed +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS + +KTZERO=0x8080 + +OBJ=\ + l.$O\ + clock.$O\ + fpi.$O\ + fpiarm.$O\ + fpimem.$O\ + main.$O\ + trap.$O\ + $CONF.root.$O\ + $IP\ + $DEVS\ + $ETHERS\ + $LINKS\ + $PORT\ + $MISC\ + $OTHERS\ + +LIBNAMES=${LIBS:%=lib%.a} +LIBDIRS=$LIBS + +HFILES=\ + mem.h\ + armv7.h\ + dat.h\ + fns.h\ + io.h\ + fpi.h\ + +CFLAGS=-wFV -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp -r +KERNDATE=`{$NDATE} + +# default:V: i$CONF.gz i$CONF.p9.gz i$CONF.txt +default:V: i$CONF.txt + +install:V: $INSTALLDIR/i$CONF $INSTALLDIR/i$CONF.gz $INSTALLDIR/i$CONF.p9.gz $INSTALLDIR/i$CONF.raw + +i$CONF.txt: i$CONF.aif + x=/bin/pub/uuencode + test -f $x || x=uuencode + $x i$CONF.aif i$CONF.aif >i$CONF.txt + mv i$CONF.txt xyz + +i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD -o $target -T$KTZERO -R4 -l $OBJ $CONF.$O $LIBFILES + +trap.t: trap.5 + cp trap.5 trap.t +trap.5: trap.c + 5c $CFLAGS trap.c + +# old "plan9" format executables for inf2.1 styxmon/sboot +i$CONF.p9: $OBJ $CONF.c $CONF.root.h $LIBNAMES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD -o $target -T0x8020 -R4 -l $OBJ $CONF.$O $LIBFILES + +i$CONF.p9.gz: i$CONF.p9 + rm -f i$CONF.p9.gz + gzip -9 i$CONF.p9.gz + +# "raw" version of kernel for binary comparison testing +i$CONF.raw: $OBJ $CONF.c $CONF.root.h $LIBNAMES + $CC $CFLAGS '-DKERNDATE='0 $CONF.c + $LD -s -o $target -T$KTZERO -R4 -l $OBJ $CONF.$O $LIBFILES + +i$CONF.aif: i$CONF + 5cv -s -H1 -T$KTZERO $prereq $target + +i$CONF.gz: i$CONF.aif + gzip -9 <$prereq >$target + +<../port/portmkfile + +../init/$INIT.dis: ../init/$INIT.b + cd ../init; mk $INIT.dis + +clock.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +devether.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +devsapcm.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +fault386.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +main.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +trap.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h + +devether.$O $ETHERS: etherif.h ../port/netif.h +$IP devip.$O: ../ip/ip.h + +dummy:V: + +# to be moved to port/interp +bench.h:D: ../../module/bench.m + rm -f $target && limbo -a -I../../module ../../module/bench.m > $target +benchmod.h:D: ../../module/bench.m + rm -f $target && limbo -t Bench -I../../module ../../module/bench.m > $target +devbench.$O: bench.h benchmod.h + +devuart.$O: devuart.c + $CC $CFLAGS devuart.c diff --git a/os/ks32/not.c b/os/ks32/not.c new file mode 100644 index 00000000..6546bd5d --- /dev/null +++ b/os/ks32/not.c @@ -0,0 +1,3 @@ +void muxclose(){} +void mntauth(){} +void mntversion(){} diff --git a/os/ks32/squirt b/os/ks32/squirt new file mode 100755 index 00000000..b2da05e4 --- /dev/null +++ b/os/ks32/squirt @@ -0,0 +1,2 @@ +#!/bin/rc +cat ievaluator7t.txt diff --git a/os/ks32/trap.c b/os/ks32/trap.c new file mode 100644 index 00000000..3d738c78 --- /dev/null +++ b/os/ks32/trap.c @@ -0,0 +1,525 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +#define waslo(sr) (!((sr) & (PsrDirq|PsrDfiq))) + +typedef struct IrqEntry { + void (*r)(Ureg*, void*); + void *a; + int v; +} IrqEntry; + +enum { + NumIRQbits = MaxIRQbit+1, + +}; + +static IrqEntry Irq[NumIRQbits]; + +Instr BREAK = 0xE6BAD010; + +int (*breakhandler)(Ureg*, Proc*); +int (*catchdbg)(Ureg *, uint); + +void dumperrstk(void); +/* + * Interrupt sources not masked by splhi() -- these are special + * interrupt handlers (e.g. profiler or watchdog), not allowed + * to share regular kernel data structures. All interrupts are + * masked by splfhi(), which should only be used herein. + */ + +int splfhi(void); /* disable all */ +int splflo(void); /* enable FIQ */ + +static int actIrq = -1; /* Active Irq handler, 0-31, or -1 if none */ +static int wasIrq = -1; /* Interrupted Irq handler */ + +static Proc *iup; /* Interrupted kproc */ + +void +intrmask(int v, int tbdf) +{ + USED(tbdf); + if(v < 0 || v > MaxIRQbit) + panic("intrmask: irq source %d out of range\n", v); + INTREG->msk |= (1 << v); +} + +void +intrunmask(int v, int tbdf) +{ + USED(tbdf); + if(v < 0 || v > MaxIRQbit) + panic("intrunmask: irq source %d out of range\n", v); + INTREG->msk &= ~(1 << v); +} + +void +intrclear(int v, int tbdf) +{ + USED(tbdf); + if(v < 0 || v > MaxIRQbit) + panic("intrclear: irq source %d out of range\n", v); + INTREG->pnd = (1 << v); +} + +void +intrenable(int v, void (*f)(Ureg*, void*), void* a, int tbdf) +{ + int x; + + USED(tbdf); + if(v < 0 || v > MaxIRQbit) + panic("intrenable: irq source %d out of range\n", v); + Irq[v].r = f; + Irq[v].a = a; + + x = splfhi(); + /* Enable the interrupt by clearing the mask bit */ + INTREG->msk &= ~(1 << v); + splx(x); +} + +ulong fiqstack[4]; +ulong irqstack[4]; +ulong abtstack[4]; +ulong undstack[4]; + +static void +safeintr(Ureg *, void *a) +{ + int v = (int)a; + int x; + + /* No handler - clear the mask so we don't loop */ + x = splfhi(); + intrmask(v, 0); + splx(x); + iprint("SPURIOUS INTERRUPT %d\n", v); +} + +static void +trapv(int off, void (*f)(void)) +{ + ulong *vloc; + int offset; + + vloc = (ulong *)off; + offset = (((ulong *) f) - vloc)-2; + *vloc = (0xea << 24) | offset; +} + +static void +maskallints(void) +{ + INTREG->msk = 0x3fffff; /* mask out all interrupts */ +} + +void +trapinit(void) +{ + int v; + IntReg *intr = INTREG; + + intr->mod = 0; /* all interrupts to be done in IRQ mode */ + + /* set up stacks for various exceptions */ + setr13(PsrMfiq, fiqstack+nelem(fiqstack)); + setr13(PsrMirq, irqstack+nelem(irqstack)); + setr13(PsrMabt, abtstack+nelem(abtstack)); + setr13(PsrMund, undstack+nelem(undstack)); + + for (v = 0; v < nelem(Irq); v++) { + Irq[v].r = safeintr; + Irq[v].a = (void *)v; + Irq[v].v = v; + } + + trapv(0x0, _vsvccall); + trapv(0x4, _vundcall); + trapv(0xc, _vpabcall); + trapv(0x10, _vdabcall); + trapv(0x18, _virqcall); + trapv(0x1c, _vfiqcall); + trapv(0x8, _vsvccall); + serwrite = uartputs; +} + +static char *_trap_str[PsrMask+1] = { + [ PsrMfiq ] "Fiq interrupt", + [ PsrMirq ] "Mirq interrupt", + [ PsrMsvc ] "SVC/SWI Exception", + [ PsrMabt ] "Prefetch Abort/Data Abort", + [ PsrMabt+1 ] "Data Abort", + [ PsrMund ] "Undefined instruction", + [ PsrMsys ] "Sys trap" +}; + +static char * +trap_str(int psr) +{ + char *str = _trap_str[psr & PsrMask]; + if (!str) + str = "Undefined trap"; + return(str); +} + +static void +sys_trap_error(int type) +{ + char errbuf[ERRMAX]; + sprint(errbuf, "sys: trap: %s\n", trap_str(type)); + error(errbuf); +} + +void +dflt(Ureg *ureg, ulong far) +{ + char buf[ERRMAX]; + + dumpregs(ureg); + sprint(buf, "trap: fault pc=%8.8lux addr=0x%lux", (ulong)ureg->pc, far); + disfault(ureg, buf); +} + +/* + * All traps come here. It is slower to have all traps ca) + * rather than directly vectoring the handler. + * However, this avoids + * a lot of code dup and possible bugs. + * trap is called splfhi(). + */ + +void +trap(Ureg* ureg) +{ + // + // This is here to make sure that a clock interrupt doesn't + // cause the process we just returned into to get scheduled + // before it single stepped to the next instruction. + // + static struct {int callsched;} c = {1}; + int itype; + /* + * All interrupts/exceptions should be resumed at ureg->pc-4, + * except for Data Abort which resumes at ureg->pc-8. + */ + itype = ureg->type; + if(itype == PsrMabt+1) + ureg->pc -= 8; + else + ureg->pc -= 4; + ureg->sp = (ulong)(ureg+1); + if (itype == PsrMirq || itype == PsrMfiq) { /* Interrupt Request */ + + Proc *saveup; + int t; + + SET(t); + SET(saveup); + + if (itype == PsrMirq) { + splflo(); /* Allow nonmasked interrupts */ + if (saveup = up) { + t = m->ticks; /* CPU time per proc */ + saveup->pc = ureg->pc; /* debug info */ + saveup->dbgreg = ureg; + } + } else { + /* for profiler(wasbusy()): */ + wasIrq = actIrq; /* Save ID of interrupted handler */ + iup = up; /* Save ID of interrupted proc */ + } + + while (1) { /* Use up all the active interrupts */ + ulong hpip; + IrqEntry *curIrq; + IntReg *intr = INTREG; + + hpip = itype == PsrMirq ? intr->oset_irq : intr->oset_fiq; + if (hpip == 0x54) + break; + curIrq = Irq + (hpip >> 2); + actIrq = curIrq->v; /* show active interrupt handler */ + up = 0; /* Make interrupted process invisible */ + curIrq->r(ureg, curIrq->a); /* Call handler */ + } + if (itype == PsrMirq) { + up = saveup; /* Make interrupted process visible */ + actIrq = -1; /* No more interrupt handler running */ + preemption(m->ticks - t); + saveup->dbgreg = nil; + } else { + actIrq = wasIrq; + up = iup; + } + return; + } + + setled7ascii('E'); + /* All other traps */ + if (ureg->psr & PsrDfiq) + goto faultpanic; + if (up) + up->dbgreg = ureg; +// setled7ascii('0' + itype); + switch(itype) { + + case PsrMund: /* Undefined instruction */ + if(*(ulong*)ureg->pc == BREAK && breakhandler) { + int s; + Proc *p; + + p = up; + /* if (!waslo(ureg->psr) || (ureg->pc >= (ulong)splhi && ureg->pc < (ulong)islo)) + p = 0; */ + s = breakhandler(ureg, p); + if(s == BrkSched) { + c.callsched = 1; + sched(); + } else if(s == BrkNoSched) { + c.callsched = 0; + if(up) + up->dbgreg = 0; + return; + } + break; + } + if (!up) + goto faultpanic; + spllo(); + if (waserror()) { + if(waslo(ureg->psr) && (up->type == Interp)) + disfault(ureg, up->env->errstr); + setpanic(); + dumpregs(ureg); + panic("%s", up->env->errstr); + } + if (!fpiarm(ureg)) { + dumpregs(ureg); + sys_trap_error(ureg->type); + } + poperror(); + break; + + case PsrMsvc: /* Jump through 0 or SWI */ + if (waslo(ureg->psr) && up && (up->type == Interp)) { + spllo(); + dumpregs(ureg); + sys_trap_error(ureg->type); + } + goto faultpanic; + + case PsrMabt: /* Prefetch abort */ + if (catchdbg && catchdbg(ureg, 0)) + break; + ureg->pc -= 4; + case PsrMabt+1: { /* Data abort */ + if (waslo(ureg->psr) && up && (up->type == Interp)) { + spllo(); + dflt(ureg, 0); + } + goto faultpanic; + } + default: /* ??? */ +faultpanic: + setpanic(); + dumpregs(ureg); + panic("exception %uX %s\n", ureg->type, trap_str(ureg->type)); + break; + } + + splhi(); + if(up) + up->dbgreg = 0; /* becomes invalid after return from trap */ +} + +void +setpanic(void) +{ + extern void screenon(int); + extern int consoleprint; + + if (breakhandler != 0) /* don't mess up debugger */ + return; + maskallints(); +// spllo(); + /* screenon(!consoleprint); */ + consoleprint = 1; + serwrite = uartputs; +} + +int +isvalid_wa(void *v) +{ + return((ulong)v >= 0x8000 && (ulong)v < conf.topofmem && !((ulong)v & 3)); +} + +int +isvalid_va(void *v) +{ + return((ulong)v >= 0x8000 && (ulong)v < conf.topofmem); +} + +void +dumplongs(char *msg, ulong *v, int n) +{ + int ii; + int ll; + + ll = print("%s at %ulx: ", msg, v); + for (ii = 0; ii < n; ii++) + { + if (ll >= 60) + { + print("\n"); + ll = print(" %ulx: ", v); + } + if (isvalid_va(v)) + ll += print(" %ulx", *v++); + else + { + ll += print(" invalid"); + break; + } + } + print("\n"); + USED(ll); +} + +void +dumpregs(Ureg* ureg) +{ + Proc *p; + + print("TRAP: %s", trap_str(ureg->type)); + if ((ureg->psr & PsrMask) != PsrMsvc) + print(" in %s", trap_str(ureg->psr)); +/* + if ((ureg->type == PsrMabt) || (ureg->type == PsrMabt + 1)) + print(" FSR %8.8luX FAR %8.8luX\n", mmuregr(CpFSR), mmuregr(CpFAR)); +*/ + print("\n"); + print("PSR %8.8uX type %2.2uX PC %8.8uX LINK %8.8uX\n", + ureg->psr, ureg->type, ureg->pc, ureg->link); + print("R14 %8.8uX R13 %8.8uX R12 %8.8uX R11 %8.8uX R10 %8.8uX\n", + ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10); + print("R9 %8.8uX R8 %8.8uX R7 %8.8uX R6 %8.8uX R5 %8.8uX\n", + ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5); + print("R4 %8.8uX R3 %8.8uX R2 %8.8uX R1 %8.8uX R0 %8.8uX\n", + ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0); + print("Stack is at: %8.8luX\n",ureg); + print("CPSR %8.8uX SPSR %8.8uX ", cpsrr(), spsrr()); + print("PC %8.8lux LINK %8.8lux\n", (ulong)ureg->pc, (ulong)ureg->link); + + p = (actIrq >= 0) ? iup : up; + if (p != nil) + print("Process stack: %lux-%lux\n", + p->kstack, p->kstack+KSTACK-4); + else + print("System stack: %lux-%lux\n", + (ulong)(m+1), (ulong)m+KSTACK-4); + dumplongs("stk", (ulong *)(ureg + 1), 16); + print("bl's: "); + dumpstk((ulong *)(ureg + 1)); + if (isvalid_wa((void *)ureg->pc)) + dumplongs("code", (ulong *)ureg->pc - 5, 12); + + dumperrstk(); + /* for(;;) ; */ +} + +void +dumpstack(void) +{ + ulong l; + + if (breakhandler != 0) + dumpstk(&l); +} + +void +dumpstk(ulong *l) +{ + ulong *v, i; + ulong inst; + ulong *estk; + uint len; + + len = KSTACK/sizeof *l; + if (up == 0) + len -= l - (ulong *)m; + else + len -= l - (ulong *)up->kstack; + + if (len > KSTACK/sizeof *l) + len = KSTACK/sizeof *l; + else if (len < 0) + len = 50; + + i = 0; + for(estk = l + len; l= 60) { + print("\n"); + i = print(" "); + } + } + if (i) + print("\n"); +} + +void +dumperrstk(void) +{ + int ii, ll; + + if (!up) + return; + + ll = print("err stk: "); + for (ii = 0; ii < NERR; ii++) { + if (ii == up->nerrlab) + ll += print("* "); + if (up->errlab[ii].pc) { + ll += print(" %lux/%8.8lux", + up->errlab[ii].sp, up->errlab[ii].pc); + if (ll >= 60) { + print("\n"); + ll = 0; + } + } + } + if (ll) + print("\n"); +} + +void +trapspecial(int (*f)(Ureg *, uint)) +{ + catchdbg = f; +} diff --git a/os/manga/Mk b/os/manga/Mk new file mode 100755 index 00000000..51d69a44 --- /dev/null +++ b/os/manga/Mk @@ -0,0 +1,8 @@ +#!/bin/rc +rfork ne +ROOT=/usr/inferno +fn cd +NPROC=3 +path=(/usr/inferno/Plan9/$cputype/bin $path) +#bind /usr/inferno/mkconfig.dist /usr/inferno/mkconfig +exec mk $* diff --git a/os/manga/archmanga.c b/os/manga/archmanga.c new file mode 100644 index 00000000..45b0f68c --- /dev/null +++ b/os/manga/archmanga.c @@ -0,0 +1,164 @@ +/* + * Manga Balance Plus + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +#include "../port/netif.h" +#include "etherif.h" +#include "../port/flashif.h" + +enum { + /* GPIO assignment ... */ + + Maxmac= 4, /* number of MAC addresses taken from EEPROM */ +}; + +static uchar macaddrs[Maxmac][Eaddrlen] = { +[0] {0x00, 0x10, 0xa1, 0x00, 0x10, 0x01}, +[1] {0x00, 0x11, 0x6E, 0x00, 0x4A, 0xD4}, +[2] {0x00, 0x10, 0xa1, 0x00, 0x20, 0x01}, /* TO DO */ +}; + +void +archreset(void) +{ + /* TO DO: set GPIO and other key registers? */ + GPIOREG->iopm |= (1<iopm &= ~(1<iopd &= ~(1<iopd &= ~(1<iopc |= 0x8888; + m->cpuhz = 166000000; /* system clock is 125 = 5*CLOCKFREQ */ + m->delayloop = m->cpuhz/1000; +/* + uartdebuginit(); +*/ +} + +void +ledset(int n) +{ + int s; + + s = splhi(); + if(n) + GPIOREG->iopd |= 1<iopd &= ~(1<>22)<<16)|0xFFFF)+1; +// w = PMGRREG->ppcr & 0x1f; +// m->cpuhz = CLOCKFREQ*(27*2*2); +} + +void +archuartpower(int, int) +{ +} + +void +kbdinit(void) +{ +} + +void +archreboot(void) +{ + dcflushall(); + GPIOREG->iopd |= 1<iopd &= ~(1<rsrr = 1; /* software reset */ + for(;;) + //spllo(); + splhi(); +} + +void +archflashwp(Flash*, int) +{ +} + +/* + * for devflash.c:/^flashreset + * retrieve flash type, virtual base and length and return 0; + * return -1 on error (no flash) + */ +int +archflashreset(int bank, Flash *f) +{ + ulong *p; + int w; + + p = KADDR(PHYSMEMCR+0x10); +iprint("Flash %8.8lux %8.8lux %8.8lux\n", p[0], p[1], p[4]); + w = p[4]&3; + if(bank > 0 || w == 0) + return -1; + if(w == 3) + w = 4; + f->type = "cfi8"; + f->addr = (void*)FLASHMEM; + f->size = 0; + f->width = w; + f->interleave = 0; + return 0; +} + +/* + * set ether parameters: the contents should be derived from EEPROM or NVRAM + */ +int +archether(int ctlno, Ether *ether) +{ + ether->nopt = 0; + ether->itype = IRQ; + switch(ctlno){ + case 0: + sprint(ether->type, "ks8695"); + ether->mem = PHYSWANDMA; + ether->port = 0; + ether->irq = IRQwmrps; + break; + case 1: + sprint(ether->type, "ks8695"); + ether->mem = PHYSLANDMA; + ether->port = 1; + ether->irq = IRQlmrps; + ether->maxmtu = ETHERMAXTU+4; /* 802.1[pQ] tags */ + break; + case 2: + sprint(ether->type, "rtl8139"); + ether->mem = 0; + ether->port = 0; + ether->irq = -1; + break; + default: + return -1; + } + memmove(ether->ea, macaddrs[ctlno], Eaddrlen); + return 1; +} + +/* + * TO DO: extract some boot data from user area of flash + */ + +void +eepromscan(void) +{ +} diff --git a/os/manga/clock.c b/os/manga/clock.c new file mode 100644 index 00000000..282f32d6 --- /dev/null +++ b/os/manga/clock.c @@ -0,0 +1,166 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "ureg.h" + +enum { + Mclk= 25000000 +}; + +typedef struct Clock0link Clock0link; +typedef struct Clock0link { + void (*clock)(void); + Clock0link* link; +} Clock0link; + +static Clock0link *clock0link; +static Lock clock0lock; +static void (*prof_fcn)(Ureg *, int); + +Timer* +addclock0link(void (*clock)(void), int) +{ + Clock0link *lp; + + if((lp = malloc(sizeof(Clock0link))) == 0){ + print("addclock0link: too many links\n"); + return nil; + } + ilock(&clock0lock); + lp->clock = clock; + lp->link = clock0link; + clock0link = lp; + iunlock(&clock0lock); + return nil; +} + +static void +profintr(Ureg *, void*) +{ + /* TO DO: watchdog, profile on Timer 0 */ +} + +static void +clockintr(Ureg*, void*) +{ + Clock0link *lp; + static int blip, led; + + if(++blip >= HZ){ + blip = 0; + ledset(led ^= 1); + } + m->ticks++; + + checkalarms(); + + if(canlock(&clock0lock)){ + for(lp = clock0link; lp; lp = lp->link) + if(lp->clock) + lp->clock(); + unlock(&clock0lock); + } + + /* round robin time slice is done by trap.c and proc.c */ +} + +void +installprof(void (*pf)(Ureg *, int)) +{ + USED(pf); +} + +void +clockinit(void) +{ + TimerReg *tr; + IntrReg *ir; + ulong l, u; + + m->ticks = 0; + tr = TIMERREG; + tr->enable = 0; + tr->pulse1 = 1; + + /* first tune the delay loop parameter (using a search because the counter doesn't decrement) */ + ir = INTRREG; + tr->count1 = Mclk/1000 - tr->pulse1; /* millisecond */ + u = m->cpuhz/(2*1000); /* over-large estimate for a millisecond */ + l = 10000; + while(l+1 < u){ + m->delayloop = l + (u-l)/2; + ir->st = 1<enable = 1<<1; + delay(1); + tr->enable = 0; + if(ir->st & (1<delayloop; + else + l = m->delayloop; + } + + intrenable(IRQ, IRQtm1, clockintr, nil, "timer.1"); + tr->count1 = Mclk/HZ - tr->pulse1; + tr->enable = 1<<1; /* enable only Timer 1 */ +} + +void +clockpoll(void) +{ +} + +void +clockcheck(void) +{ +} + +uvlong +fastticks(uvlong *hz) +{ + if(hz) + *hz = HZ; + return m->ticks; +} + +void +microdelay(int l) +{ + int i; + + l *= m->delayloop; + l /= 1000; + if(l <= 0) + l = 1; + for(i = 0; i < l; i++) + ; +} + +void +delay(int l) +{ + ulong i, j; + + j = m->delayloop; + while(l-- > 0) + for(i=0; i < j; i++) + ; +} + +/* + * for devkprof.c + */ +long +archkprofmicrosecondspertick(void) +{ + return MS2HZ*1000; +} + +void +archkprofenable(int) +{ + /* TO DO */ +} diff --git a/os/manga/dat.h b/os/manga/dat.h new file mode 100644 index 00000000..b53c25e5 --- /dev/null +++ b/os/manga/dat.h @@ -0,0 +1,128 @@ +typedef struct Conf Conf; +typedef struct Dma Dma; +typedef struct FPU FPU; +typedef struct FPenv FPenv; +typedef struct Label Label; +typedef struct Lock Lock; +typedef struct Mach Mach; +typedef struct Ureg Ureg; +typedef struct ISAConf ISAConf; +typedef struct Pcidev Pcidev; + +typedef ulong Instr; + +struct Conf +{ + ulong nmach; /* processors */ + ulong nproc; /* processes */ + ulong npage0; /* total physical pages of memory */ + ulong npage1; /* total physical pages of memory */ + ulong topofmem; /* highest physical address + 1 */ + ulong npage; /* total physical pages of memory */ + ulong base0; /* base of bank 0 */ + ulong base1; /* base of bank 1 */ + ulong ialloc; /* max interrupt time allocation in bytes */ + + int useminicache; /* use mini cache: screen.c/lcd.c */ + int textwrite; /* writeable text segment, for debug */ + int portrait; /* display orientation */ +}; + +#define NISAOPT 8 +struct ISAConf { + char type[KNAMELEN]; + ulong port; + ulong irq; + int itype; + ulong dma; + ulong mem; + ulong size; + ulong freq; + + int nopt; + char *opt[NISAOPT]; +}; + +/* + * FPenv.status + */ +enum +{ + FPINIT, + FPACTIVE, + FPINACTIVE, +}; + +struct FPenv +{ + ulong status; + ulong control; + ushort fpistate; /* emulated fp */ + ulong regs[8][3]; /* emulated fp */ +}; + +/* + * This structure must agree with fpsave and fprestore asm routines + */ +struct FPU +{ + FPenv env; +}; + +struct Label +{ + ulong sp; + ulong pc; +}; + +struct Lock +{ + ulong key; + ulong sr; + ulong pc; + int pri; +}; + +#include "../port/portdat.h" + +/* + * machine dependent definitions not used by ../port/portdat.h + */ +struct Mach +{ + /* OFFSETS OF THE FOLLOWING KNOWN BY l.s */ + ulong splpc; /* pc of last caller to splhi */ + + /* ordering from here on irrelevant */ + + int machno; /* physical id of processor */ + ulong ticks; /* of the clock since boot time */ + Proc *proc; /* current process on this processor */ + Label sched; /* scheduler wakeup */ + Lock alarmlock; /* access to alarm list */ + void *alarm; /* alarms bound to this clock */ + ulong cpuhz; + ulong delayloop; + + /* stacks for exceptions */ + ulong fiqstack[4]; + ulong irqstack[4]; + ulong abtstack[4]; + ulong undstack[4]; + + int stack[1]; +}; + +#define MACHP(n) (n == 0 ? (Mach*)(MACHADDR) : (Mach*)0) + +extern Mach *m; +extern Proc *up; + +/* + * Layout at virtual address 0. + */ +typedef struct Vectorpage { + void (*vectors[8])(void); + uint vtable[8]; +} Vectorpage; +extern Vectorpage *page0; diff --git a/os/manga/devether.c b/os/manga/devether.c new file mode 100644 index 00000000..18f786e4 --- /dev/null +++ b/os/manga/devether.c @@ -0,0 +1,765 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +enum { + Type8021Q= 0x8100, /* value of type field for 802.1[pQ] tags */ +}; + +static Ether *etherxx[MaxEther]; /* real controllers */ +static Ether* vlanalloc(Ether*, int); +static void vlanoq(Ether*, Block*); + +Chan* +etherattach(char* spec) +{ + ulong ctlrno; + char *p; + Chan *chan; + Ether *ether, *vlan; + int vlanid; + + ctlrno = 0; + vlanid = 0; + if(spec && *spec){ + ctlrno = strtoul(spec, &p, 0); + if(ctlrno == 0 && p == spec || ctlrno >= MaxEther || *p && *p != '.') + error(Ebadarg); + if(*p == '.'){ /* vlan */ + vlanid = strtoul(p+1, &p, 0); + if(vlanid <= 0 || vlanid > 0xFFF || *p) + error(Ebadarg); + } + } + if((ether = etherxx[ctlrno]) == 0) + error(Enodev); + rlock(ether); + if(waserror()){ + runlock(ether); + nexterror(); + } + if(vlanid){ + if(ether->maxmtu < ETHERMAXTU+4) + error("interface cannot support 802.1 tags"); + vlan = vlanalloc(ether, vlanid); + chan = devattach('l', spec); + chan->dev = ctlrno + (vlanid<<8); + chan->aux = vlan; + poperror(); + runlock(ether); + return chan; + } + chan = devattach('l', spec); + chan->dev = ctlrno; + chan->aux = ether; + if(ether->attach) + ether->attach(ether); + poperror(); + runlock(ether); + return chan; +} + +static void +ethershutdown(void) +{ + Ether *ether; + int i; + + for(i=0; idetach != nil) + ether->detach(ether); + } +} + +static Walkqid* +etherwalk(Chan* chan, Chan *nchan, char **name, int nname) +{ + Walkqid *wq; + Ether *ether; + + ether = chan->aux; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + wq = netifwalk(ether, chan, nchan, name, nname); + if(wq && wq->clone != nil && wq->clone != chan) + wq->clone->aux = ether; + poperror(); + runlock(ether); + return wq; +} + +static int +etherstat(Chan* chan, uchar* dp, int n) +{ + int s; + Ether *ether; + + ether = chan->aux; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + s = netifstat(ether, chan, dp, n); + poperror(); + runlock(ether); + return s; +} + +static Chan* +etheropen(Chan* chan, int omode) +{ + Chan *c; + Ether *ether; + + ether = chan->aux; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + c = netifopen(ether, chan, omode); + poperror(); + runlock(ether); + return c; +} + +static void +etherclose(Chan* chan) +{ + Ether *ether; + + ether = chan->aux; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + netifclose(ether, chan); + poperror(); + runlock(ether); +} + +static long +etherread(Chan* chan, void* buf, long n, vlong off) +{ + Ether *ether; + ulong offset = off; + long r; + + ether = chan->aux; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + if((chan->qid.type & QTDIR) == 0 && ether->ifstat){ + /* + * With some controllers it is necessary to reach + * into the chip to extract statistics. + */ + if(NETTYPE(chan->qid.path) == Nifstatqid){ + r = ether->ifstat(ether, buf, n, offset); + goto out; + } + if(NETTYPE(chan->qid.path) == Nstatqid) + ether->ifstat(ether, buf, 0, offset); + } + r = netifread(ether, chan, buf, n, offset); +out: + poperror(); + runlock(ether); + return r; +} + +static Block* +etherbread(Chan* chan, long n, ulong offset) +{ + Block *b; + Ether *ether; + + ether = chan->aux; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + b = netifbread(ether, chan, n, offset); + poperror(); + runlock(ether); + return b; +} + +static int +etherwstat(Chan* chan, uchar* dp, int n) +{ + Ether *ether; + int r; + + ether = chan->aux; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + r = netifwstat(ether, chan, dp, n); + poperror(); + runlock(ether); + return r; +} + +static void +etherrtrace(Netfile* f, Etherpkt* pkt, int len) +{ + int i, n; + Block *bp; + + if(qwindow(f->in) <= 0) + return; + if(len > 58) + n = 58; + else + n = len; + bp = iallocb(64); + if(bp == nil) + return; + memmove(bp->wp, pkt->d, n); + i = TK2MS(MACHP(0)->ticks); + bp->wp[58] = len>>8; + bp->wp[59] = len; + bp->wp[60] = i>>24; + bp->wp[61] = i>>16; + bp->wp[62] = i>>8; + bp->wp[63] = i; + bp->wp += 64; + qpass(f->in, bp); +} + +Block* +etheriq(Ether* ether, Block* bp, int fromwire) +{ + Etherpkt *pkt; + ushort type; + int len, multi, tome, fromme, vlanid, i; + Netfile **ep, *f, **fp, *fx; + Block *xbp; + Ether *vlan; + + ether->inpackets++; + + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + type = (pkt->type[0]<<8)|pkt->type[1]; + if(type == Type8021Q && ether->nvlan){ + vlanid = nhgets(bp->rp+2*Eaddrlen+2) & 0xFFF; + if(vlanid){ + for(i = 0; i < nelem(ether->vlans); i++){ + vlan = ether->vlans[i]; + if(vlan != nil && vlan->vlanid == vlanid){ + memmove(bp->rp+4, bp->rp, 2*Eaddrlen); + bp->rp += 4; + return etheriq(vlan, bp, fromwire); + } + } + /* allow normal type handling to accept or discard it */ + } + } + + fx = 0; + ep = ðer->f[Ntypes]; + + multi = pkt->d[0] & 1; + /* check for valid multcast addresses */ + if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){ + if(!activemulti(ether, pkt->d, sizeof(pkt->d))){ + if(fromwire){ + freeb(bp); + bp = 0; + } + return bp; + } + } + + /* is it for me? */ + tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0; + + /* + * Multiplex the packet to all the connections which want it. + * If the packet is not to be used subsequently (fromwire != 0), + * attempt to simply pass it into one of the connections, thereby + * saving a copy of the data (usual case hopefully). + */ + for(fp = ether->f; fp < ep; fp++){ + if((f = *fp) && (f->type == type || f->type < 0)) + if(tome || multi || f->prom){ + /* Don't want to hear bridged packets */ + if(f->bridge && !fromwire && !fromme) + continue; + if(!f->headersonly){ + if(fromwire && fx == 0) + fx = f; + else if(xbp = iallocb(len)){ + memmove(xbp->wp, pkt, len); + xbp->wp += len; + if(qpass(f->in, xbp) < 0) + ether->soverflows++; + } + else + ether->soverflows++; + } + else + etherrtrace(f, pkt, len); + } + } + + if(fx){ + if(qpass(fx->in, bp) < 0) + ether->soverflows++; + return 0; + } + if(fromwire){ + freeb(bp); + return 0; + } + + return bp; +} + +static int +etheroq(Ether* ether, Block* bp) +{ + int len, loopback, s; + Etherpkt *pkt; + + ether->outpackets++; + + /* + * Check if the packet has to be placed back onto the input queue, + * i.e. if it's a loopback or broadcast packet or the interface is + * in promiscuous mode. + * If it's a loopback packet indicate to etheriq that the data isn't + * needed and return, etheriq will pass-on or free the block. + * To enable bridging to work, only packets that were originated + * by this interface are fed back. + */ + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){ + s = splhi(); + etheriq(ether, bp, 0); + splx(s); + } + + if(!loopback){ + if(ether->vlanid){ + /* add tag */ + bp = padblock(bp, 2+2); + memmove(bp->rp, bp->rp+4, 2*Eaddrlen); + hnputs(bp->rp+2*Eaddrlen, Type8021Q); + hnputs(bp->rp+2*Eaddrlen+2, ether->vlanid & 0xFFF); /* prio:3 0:1 vid:12 */ + ether = ether->ctlr; + } + qbwrite(ether->oq, bp); + if(ether->transmit != nil) + ether->transmit(ether); + }else + freeb(bp); + + return len; +} + +static long +etherwrite(Chan* chan, void* buf, long n, vlong) +{ + Ether *ether; + Block *bp; + int onoff; + Cmdbuf *cb; + long l; + + ether = chan->aux; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + if(NETTYPE(chan->qid.path) != Ndataqid) { + l = netifwrite(ether, chan, buf, n); + if(l >= 0) + goto out; + cb = parsecmd(buf, n); + if(strcmp(cb->f[0], "nonblocking") == 0){ + if(cb->nf <= 1) + onoff = 1; + else + onoff = atoi(cb->f[1]); + if(ether->oq != nil) + qnoblock(ether->oq, onoff); + free(cb); + goto out; + } + free(cb); + if(ether->ctl!=nil){ + l = ether->ctl(ether,buf,n); + goto out; + } + error(Ebadctl); + } + + if(n > ether->maxmtu) + error(Etoobig); + if(n < ether->minmtu) + error(Etoosmall); + bp = allocb(n); + if(waserror()){ + freeb(bp); + nexterror(); + } + memmove(bp->rp, buf, n); + memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen); + bp->wp += n; + poperror(); + + l = etheroq(ether, bp); +out: + poperror(); + runlock(ether); + return l; +} + +static long +etherbwrite(Chan* chan, Block* bp, ulong) +{ + Ether *ether; + long n; + + n = BLEN(bp); + if(NETTYPE(chan->qid.path) != Ndataqid){ + if(waserror()) { + freeb(bp); + nexterror(); + } + n = etherwrite(chan, bp->rp, n, 0); + poperror(); + freeb(bp); + return n; + } + ether = chan->aux; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + if(n > ether->maxmtu){ + freeb(bp); + error(Etoobig); + } + if(n < ether->minmtu){ + freeb(bp); + error(Etoosmall); + } + n = etheroq(ether, bp); + poperror(); + runlock(ether); + return n; +} + +static void +nop(Ether*) +{ +} + +static long +vlanctl(Ether *ether, void *buf, long n) +{ + uchar ea[Eaddrlen]; + Ether *master; + Cmdbuf *cb; + int i; + + cb = parsecmd(buf, n); + if(cb->nf >= 2 + && strcmp(cb->f[0], "ea")==0 + && parseether(ea, cb->f[1]) == 0){ + free(cb); + memmove(ether->ea, ea, Eaddrlen); + memmove(ether->addr, ether->ea, Eaddrlen); + return 0; + } + if(cb->nf == 1 && strcmp(cb->f[0], "disable") == 0){ + master = ether->ctlr; + qlock(&master->vlq); + for(i = 0; i < nelem(master->vlans); i++) + if(master->vlans[i] == ether){ + ether->vlanid = 0; + master->nvlan--; + break; + } + qunlock(&master->vlq); + free(cb); + return 0; + } + free(cb); + error(Ebadctl); + return -1; /* not reached */ +} + +static Ether* +vlanalloc(Ether *ether, int id) +{ + Ether *vlan; + int i, fid; + char name[KNAMELEN]; + + qlock(ðer->vlq); + if(waserror()){ + qunlock(ðer->vlq); + nexterror(); + } + fid = -1; + for(i = 0; i < nelem(ether->vlans); i++){ + vlan = ether->vlans[i]; + if(vlan != nil && vlan->vlanid == id){ + poperror(); + qunlock(ðer->vlq); + return vlan; + } + if(fid < 0 && (vlan == nil || vlan->vlanid == 0)) + fid = i; + } + if(fid < 0) + error(Enoifc); + snprint(name, sizeof(name), "ether%d.%d", ether->ctlrno, id); + vlan = ether->vlans[fid]; + if(vlan == nil){ + vlan = mallocz(sizeof(Ether), 1); + if(vlan == nil) + error(Enovmem); + netifinit(vlan, name, Ntypes, ether->limit); + ether->vlans[fid] = vlan; /* id is still zero, can't be matched */ + ether->nvlan++; + }else + memmove(vlan->name, name, KNAMELEN-1); + vlan->attach = nop; + vlan->transmit = nil; + vlan->ctl = vlanctl; + vlan->irq = -1; +// vlan->promiscuous = ether->promiscuous; +// vlan->multicast = ether->multicast; + vlan->arg = vlan; + vlan->mbps = ether->mbps; + vlan->fullduplex = ether->fullduplex; + vlan->encry = ether->encry; + vlan->minmtu = ether->minmtu; + vlan->maxmtu = ether->maxmtu; + vlan->ctlrno = ether->ctlrno; + vlan->vlanid = id; + vlan->alen = Eaddrlen; + memmove(vlan->addr, ether->addr, sizeof(vlan->addr)); + memmove(vlan->bcast, ether->bcast, sizeof(ether->bcast)); + vlan->oq = nil; + vlan->ctlr = ether; + vlan->vlanid = id; + poperror(); + qunlock(ðer->vlq); + return vlan; +} + +static struct { + char* type; + int (*reset)(Ether*); +} cards[MaxEther+1]; + +void +addethercard(char* t, int (*r)(Ether*)) +{ + static int ncard; + + if(ncard == MaxEther) + panic("too many ether cards"); + cards[ncard].type = t; + cards[ncard].reset = r; + ncard++; +} + +int +parseether(uchar *to, char *from) +{ + char nip[4]; + char *p; + int i; + + p = from; + for(i = 0; i < Eaddrlen; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +static void +etherreset(void) +{ + Ether *ether; + int i, n, ctlrno; + char name[KNAMELEN], buf[128]; + + for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){ + if(ether == 0) + ether = malloc(sizeof(Ether)); + memset(ether, 0, sizeof(Ether)); + ether->ctlrno = ctlrno; + ether->mbps = 10; + ether->minmtu = ETHERMINTU; + ether->maxmtu = ETHERMAXTU; + ether->itype = -1; + + if(archether(ctlrno, ether) <= 0) + continue; + + for(n = 0; cards[n].type; n++){ + if(cistrcmp(cards[n].type, ether->type)) + continue; + for(i = 0; i < ether->nopt; i++){ + if(cistrncmp(ether->opt[i], "ea=", 3) == 0){ + if(parseether(ether->ea, ðer->opt[i][3]) == -1) + memset(ether->ea, 0, Eaddrlen); + }else if(cistrcmp(ether->opt[i], "fullduplex") == 0 || + cistrcmp(ether->opt[i], "10BASE-TFD") == 0) + ether->fullduplex = 1; + else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0) + ether->mbps = 100; + } + if(cards[n].reset(ether)) + break; + snprint(name, sizeof(name), "ether%d", ctlrno); + + if(ether->interrupt != nil) + intrenable(ether->itype, ether->irq, ether->interrupt, ether, name); + + i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %lud", + ctlrno, ether->type, ether->mbps, ether->port, ether->irq); + if(ether->mem) + i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem)); + if(ether->size) + i += sprint(buf+i, " size 0x%luX", ether->size); + i += sprint(buf+i, ": %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX", + ether->ea[0], ether->ea[1], ether->ea[2], + ether->ea[3], ether->ea[4], ether->ea[5]); + sprint(buf+i, "\n"); + iprint(buf); + + if(ether->mbps == 100){ + netifinit(ether, name, Ntypes, 256*1024); + if(ether->oq == 0) + ether->oq = qopen(256*1024, Qmsg, 0, 0); + } + else{ + netifinit(ether, name, Ntypes, 64*1024); + if(ether->oq == 0) + ether->oq = qopen(64*1024, Qmsg, 0, 0); + } + if(ether->oq == 0) + panic("etherreset %s", name); + ether->alen = Eaddrlen; + memmove(ether->addr, ether->ea, Eaddrlen); + memset(ether->bcast, 0xFF, Eaddrlen); + + etherxx[ctlrno] = ether; + ether = 0; + break; + } + } + if(ether) + free(ether); +} + +static void +etherpower(int on) +{ + int i; + Ether *ether; + + for(i = 0; i < MaxEther; i++){ + if((ether = etherxx[i]) == nil || ether->power == nil) + continue; + if(on){ + if(canrlock(ether)) + continue; + if(ether->power != nil) + ether->power(ether, on); + wunlock(ether); + }else{ + if(ether->readers) + continue; + wlock(ether); + if(ether->power != nil) + ether->power(ether, on); + /* Keep locked until power goes back on */ + } + } +} + +#define POLY 0xedb88320 + +/* really slow 32 bit crc for ethers */ +ulong +ethercrc(uchar *p, int len) +{ + int i, j; + ulong crc, b; + + crc = 0xffffffff; + for(i = 0; i < len; i++){ + b = *p++; + for(j = 0; j < 8; j++){ + crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0); + b >>= 1; + } + } + return crc; +} + +Dev etherdevtab = { + 'l', + "ether", + + etherreset, + devinit, + ethershutdown, + etherattach, + etherwalk, + etherstat, + etheropen, + devcreate, + etherclose, + etherread, + etherbread, + etherwrite, + etherbwrite, + devremove, + etherwstat, + etherpower, +}; diff --git a/os/manga/devusb.c b/os/manga/devusb.c new file mode 100644 index 00000000..ead04b9a --- /dev/null +++ b/os/manga/devusb.c @@ -0,0 +1,931 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "usb.h" + +static int debug = 0; + +#define Chatty 1 +#define DPRINT if(Chatty)print +#define XPRINT if(debug)iprint + +Usbhost* usbhost[MaxUsb]; + +static char *devstates[] = { + [Disabled] "Disabled", + [Attached] "Attached", + [Enabled] "Enabled", + [Assigned] "Assigned", + [Configured] "Configured", +}; + +static char Ebadusbmsg[] = "invalid parameters to USB ctl message"; + +enum +{ + Qtopdir = 0, + Q2nd, + Qnew, + Qport, + Q3rd, + Qctl, + Qstatus, + Qep0, + /* other endpoint files */ +}; + +/* + * Qid path is: + * 8 bits of file type (qids above) + * 8 bits of slot number; default address 0 used for per-controller files + * 4 bits of controller number + */ +enum { + TYPEBITS = 8, + SLOTBITS = 8, + CTLRBITS = 4, + + SLOTSHIFT = TYPEBITS, + CTLRSHIFT = SLOTSHIFT+SLOTBITS, + + TYPEMASK = (1<>SLOTSHIFT)&SLOTMASK) +#define CTLR(q) ((((ulong)(q).path)>>CTLRSHIFT)&CTLRMASK) +#define PATH(t, s, c) ((t)|((s)< nelem(uh->dev)) + return nil; + return uh->dev[s]; +} + +static Udev* +usbdevice(Chan *c) +{ + int bus; + Udev *d; + Usbhost *uh; + + bus = CTLR(c->qid); + if(bus > nelem(usbhost) || (uh = usbhost[bus]) == nil) { + error(Egreg); + return nil; /* for compiler */ + } + d = usbdeviceofslot(uh, SLOT(c->qid)); + if(d == nil || d->id != c->qid.vers || d->state == Disabled) + error(Ehungup); + return d; +} + +static Endpt * +devendpt(Udev *d, int id, int add) +{ + Usbhost *uh; + Endpt *e, **p; + + p = &d->ep[id&0xF]; + lock(d); + e = *p; + if(e != nil){ + incref(e); + XPRINT("incref(0x%p) in devendpt, new value %ld\n", e, e->ref); + unlock(d); + return e; + } + unlock(d); + if(!add) + return nil; + + e = mallocz(sizeof(*e), 1); + e->ref = 1; + e->x = id&0xF; + e->id = id; + e->sched = -1; + e->maxpkt = 8; + e->nbuf = 1; + e->dev = d; + e->active = 0; + + uh = d->uh; + uh->epalloc(uh, e); + + lock(d); + if(*p != nil){ + incref(*p); + XPRINT("incref(0x%p) in devendpt, new value %ld\n", *p, (*p)->ref); + unlock(d); + uh->epfree(uh, e); + free(e); + return *p; + } + *p = e; + unlock(d); + e->rq = qopen(8*1024, 0, nil, e); + e->wq = qopen(8*1024, 0, nil, e); + return e; +} + +static void +freept(Endpt *e) +{ + Usbhost *uh; + + if(e != nil && decref(e) == 0){ + XPRINT("freept(%d,%d)\n", e->dev->x, e->x); + uh = e->dev->uh; + uh->epclose(uh, e); + e->dev->ep[e->x] = nil; + uh->epfree(uh, e); + free(e); + } +} + +static Udev* +usbnewdevice(Usbhost *uh) +{ + int i; + Udev *d; + Endpt *e; + + d = nil; + qlock(uh); + if(waserror()){ + qunlock(uh); + nexterror(); + } + for(i=0; idev); i++) + if(uh->dev[i] == nil){ + uh->idgen++; + d = mallocz(sizeof(*d), 1); + d->uh = uh; + d->ref = 1; + d->x = i; + d->id = (uh->idgen << 8) | i; + d->state = Enabled; + XPRINT("calling devendpt in usbnewdevice\n"); + e = devendpt(d, 0, 1); /* always provide control endpoint 0 */ + e->mode = ORDWR; + e->iso = 0; + e->sched = -1; + uh->dev[i] = d; + break; + } + poperror(); + qunlock(uh); + return d; +} + +static void +freedev(Udev *d, int ept) +{ + int i; + Endpt *e; + Usbhost *uh; + + uh = d->uh; + if(decref(d) == 0){ + XPRINT("freedev 0x%p, 0\n", d); + for(i=0; iep); i++) + freept(d->ep[i]); + if(d->x >= 0) + uh->dev[d->x] = nil; + free(d); + } else { + if(ept >= 0 && ept < nelem(d->ep)){ + e = d->ep[ept]; + XPRINT("freedev, freept 0x%p\n", e); + if(e != nil) + uh->epclose(uh, e); + } + } +} + +static int +usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp) +{ + Qid q; + Udev *d; + Endpt *e; + Dirtab *tab; + Usbhost *uh; + int t, bus, slot, perm; + + /* + * Top level directory contains the controller names. + */ + if(c->qid.path == Qtopdir){ + if(s == DEVDOTDOT){ + mkqid(&q, Qtopdir, 0, QTDIR); + devdir(c, q, "#U", 0, eve, 0555, dp); + return 1; + } + if(s >= nelem(usbhost) || usbhost[s] == nil) + return -1; + mkqid(&q, PATH(Q2nd, 0, s), 0, QTDIR); + snprint(up->genbuf, sizeof up->genbuf, "usb%d", s); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + bus = CTLR(c->qid); + if(bus >= nelem(usbhost) || (uh = usbhost[bus]) == nil) + return -1; + + /* + * Second level contains "new", "port", and a numbered + * directory for each enumerated device on the bus. + */ + t = TYPE(c->qid); + if(t < Q3rd){ + if(s == DEVDOTDOT){ + mkqid(&q, Qtopdir, 0, QTDIR); + devdir(c, q, "#U", 0, eve, 0555, dp); + return 1; + } + if(s < nelem(usbdir2)){ + d = uh->dev[0]; + if(d == nil) + return -1; + tab = &usbdir2[s]; + mkqid(&q, PATH(tab->qid.path, 0, bus), d->id, QTFILE); + devdir(c, q, tab->name, tab->length, eve, tab->perm, dp); + return 1; + } + s -= nelem(usbdir2); + if(s >= 0 && s < nelem(uh->dev)) { + d = uh->dev[s]; + if(d == nil) + return 0; + sprint(up->genbuf, "%d", s); + mkqid(&q, PATH(Q3rd, s, bus), d->id, QTDIR); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + return -1; + } + + /* + * Third level. + */ + slot = SLOT(c->qid); + if(s == DEVDOTDOT) { + mkqid(&q, PATH(Q2nd, 0, bus), c->qid.vers, QTDIR); + snprint(up->genbuf, sizeof up->genbuf, "usb%d", bus); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + if(s < nelem(usbdir3)) { + tab = &usbdir3[s]; + mkqid(&q, PATH(tab->qid.path, slot, bus), c->qid.vers, QTFILE); + devdir(c, q, tab->name, tab->length, eve, tab->perm, dp); + return 1; + } + s -= nelem(usbdir3); + + /* active endpoints */ + d = usbdeviceofslot(uh, slot); + if(d == nil || s >= nelem(d->ep)) + return -1; + if(s == 0 || (e = d->ep[s]) == nil) /* ep0data is called "setup" */ + return 0; + sprint(up->genbuf, "ep%ddata", s); + mkqid(&q, PATH(Qep0+s, slot, bus), c->qid.vers, QTFILE); + switch(e->mode) { + case OREAD: + perm = 0444; + break; + case OWRITE: + perm = 0222; + break; + default: + perm = 0666; + break; + } + devdir(c, q, up->genbuf, e->buffered, eve, perm, dp); + return 1; +} + +static Usbhost* +usbprobe(int cardno, int ctlrno) +{ + Usbhost *uh; + char buf[128], *ebuf, name[64], *p, *type; + + if(cardno < 0) + return nil; + uh = malloc(sizeof(Usbhost)); + memset(uh, 0, sizeof(Usbhost)); + uh->tbdf = BUSUNKNOWN; + + if(cardno >= MaxUsb || usbtypes[cardno].type == nil){ + free(uh); + return nil; + } + if(usbtypes[cardno].reset(uh) < 0){ + free(uh); + return nil; + } + + snprint(name, sizeof(name), "usb%d", ctlrno); + intrenable(IRQ, uh->irq, uh->interrupt, uh, name); + + ebuf = buf + sizeof buf; + p = seprint(buf, ebuf, "#U/usb%d: %s: port 0x%luX irq %ld", ctlrno, usbtypes[cardno].type, uh->port, uh->irq); + if(uh->mem) + p = seprint(p, ebuf, " addr 0x%luX", PADDR(uh->mem)); + if(uh->size) + seprint(p, ebuf, " size 0x%luX", uh->size); + print("%s\n", buf); + + return uh; +} + +static void +usbreset(void) +{ + int cardno, ctlrno; + Usbhost *uh; + + for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){ + if((uh = usbprobe(-1, ctlrno)) == nil) + continue; + usbhost[ctlrno] = uh; + } + + if(getconf("*nousbprobe")) + return; + + cardno = ctlrno = 0; + while(usbtypes[cardno].type != nil && ctlrno < MaxUsb){ + if(usbhost[ctlrno] != nil){ + ctlrno++; + continue; + } + if((uh = usbprobe(cardno, ctlrno)) == nil){ + cardno++; + continue; + } + usbhost[ctlrno] = uh; + ctlrno++; + } +} + +void +usbinit(void) +{ + Udev *d; + int ctlrno; + Usbhost *uh; + + for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){ + uh = usbhost[ctlrno]; + if(uh == nil) + continue; + if(uh->init != 0) + uh->init(uh); + + /* reserve device for configuration */ + d = usbnewdevice(uh); + incref(d); + d->state = Attached; + } +} + +Chan * +usbattach(char *spec) +{ + return devattach('U', spec); +} + +static Walkqid* +usbwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, usbgen); +} + +static int +usbstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, nil, 0, usbgen); +} + +Chan* +usbopen(Chan *c, int omode) +{ + Udev *d; + Endpt *e; + int f, s, type; + Usbhost *uh; + + if(c->qid.type == QTDIR) + return devopen(c, omode, nil, 0, usbgen); + + f = 0; + type = TYPE(c->qid); + if(type == Qnew){ + d = usbdevice(c); + d = usbnewdevice(d->uh); + XPRINT("usbopen, new dev 0x%p\n", d); + if(d == nil) { + XPRINT("usbopen failed (usbnewdevice)\n"); + error(Enodev); + } + type = Qctl; + mkqid(&c->qid, PATH(type, d->x, CTLR(c->qid)), d->id, QTFILE); + f = 1; + } + + if(type < Q3rd){ + XPRINT("usbopen, devopen < Q3rd\n"); + return devopen(c, omode, nil, 0, usbgen); + } + + d = usbdevice(c); + uh = d->uh; + qlock(uh); + if(waserror()){ + qunlock(uh); + nexterror(); + } + + switch(type){ + case Qctl: + if(0&&d->busy) + error(Einuse); + d->busy = 1; + if(!f) + incref(d); + XPRINT("usbopen, Qctl 0x%p\n", d); + break; + + default: + s = type - Qep0; + XPRINT("usbopen, default 0x%p, %d\n", d, s); + if(s >= 0 && s < nelem(d->ep)){ + if((e = d->ep[s]) == nil) { + XPRINT("usbopen failed (endpoint)\n"); + error(Enodev); + } + XPRINT("usbopen: dev 0x%p, ept 0x%p\n", d, e); + uh->epopen(uh, e); + e->foffset = 0; + e->toffset = 0; + e->poffset = 0; + e->buffered = 0; + } + incref(d); + break; + } + poperror(); + qunlock(uh); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +void +usbclose(Chan *c) +{ + Udev *d; + int ept, type; + Usbhost *uh; + + type = TYPE(c->qid); + if(c->qid.type == QTDIR || type < Q3rd) + return; + d = usbdevice(c); + uh = d->uh; + qlock(uh); + if(waserror()){ + qunlock(uh); + nexterror(); + } + if(type == Qctl) + d->busy = 0; + XPRINT("usbclose: dev 0x%p\n", d); + if(c->flag & COPEN){ + ept = (type != Qctl) ? type - Qep0 : -1; + XPRINT("usbclose: freedev 0x%p\n", d); + freedev(d, ept); + } + poperror(); + qunlock(uh); +} + +static char * +epstatus(char *s, char *se, Endpt *e, int i) +{ + char *p; + + p = seprint(s, se, "%2d %#6.6lux %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks); + if(e->iso){ + p = seprint(p, se, "bufsize %6d buffered %6d", e->maxpkt, e->buffered); + if(e->toffset) + p = seprint(p, se, " offset %10lud time %19lld\n", e->toffset, e->time); + p = seprint(p, se, "\n"); + } + return p; +} + +long +usbread(Chan *c, void *a, long n, vlong offset) +{ + int t, i; + Udev *d; + Endpt *e; + Usbhost *uh; + char *s, *se, *p; + + if(c->qid.type == QTDIR) + return devdirread(c, a, n, nil, 0, usbgen); + + d = usbdevice(c); + uh = d->uh; + t = TYPE(c->qid); + + if(t >= Qep0) { + t -= Qep0; + if(t >= nelem(d->ep)) + error(Eio); + e = d->ep[t]; + if(e == nil || e->mode == OWRITE) + error(Egreg); + if(t == 0) { + if(e->iso) + error(Egreg); + e->data01 = 1; + n = uh->read(uh, e, a, n, 0LL); + if(e->setin){ + e->setin = 0; + e->data01 = 1; + uh->write(uh, e, "", 0, 0LL, TokOUT); + } + return n; + } + return uh->read(uh, e, a, n, offset); + } + + s = smalloc(READSTR); + se = s+READSTR; + if(waserror()){ + free(s); + nexterror(); + } + switch(t){ + case Qport: + uh->portinfo(uh, s, se); + break; + + case Qctl: + seprint(s, se, "%11d %11d\n", d->x, d->id); + break; + + case Qstatus: + if (d->did || d->vid) + p = seprint(s, se, "%s %#6.6lux %#4.4ux %#4.4ux\n", devstates[d->state], d->csp, d->vid, d->did); + else + p = seprint(s, se, "%s %#6.6lux\n", devstates[d->state], d->csp); + for(i=0; iep); i++) { + e = d->ep[i]; + if(e == nil) + continue; + /* TO DO: freeze e */ + p = epstatus(p, se, e, i); + } + } + n = readstr(offset, a, n, s); + poperror(); + free(s); + return n; +} + +long +usbwrite(Chan *c, void *a, long n, vlong offset) +{ + Udev *d; + Endpt *e; + Cmdtab *ct; + Cmdbuf *cb; + Usbhost *uh; + int id, nw, t, i; + char cmd[50]; + + if(c->qid.type == QTDIR) + error(Egreg); + d = usbdevice(c); + uh = d->uh; + t = TYPE(c->qid); + switch(t){ + case Qport: + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + + ct = lookupcmd(cb, usbportmsg, nelem(usbportmsg)); + id = strtol(cb->f[1], nil, 0); + switch(ct->index){ + case PMdisable: + uh->portenable(uh, id, 0); + break; + case PMenable: + uh->portenable(uh, id, 1); + break; + case PMreset: + uh->portreset(uh, id); + break; + } + + poperror(); + free(cb); + return n; + case Qctl: + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + + ct = lookupcmd(cb, usbctlmsg, nelem(usbctlmsg)); + switch(ct->index){ + case CMspeed: + d->ls = strtoul(cb->f[1], nil, 0) == 0; + break; + case CMclass: + if (cb->nf != 4 && cb->nf != 6) + cmderror(cb, Ebadusbmsg); + /* class #ifc ept csp ( == class subclass proto) [vendor product] */ + d->npt = strtoul(cb->f[1], nil, 0); /* # of interfaces */ + i = strtoul(cb->f[2], nil, 0); /* endpoint */ + if (i < 0 || i >= nelem(d->ep) + || d->npt > nelem(d->ep) || i >= d->npt) + cmderror(cb, Ebadusbmsg); + if (cb->nf == 6) { + d->vid = strtoul(cb->f[4], nil, 0); + d->did = strtoul(cb->f[5], nil, 0); + } + if (i == 0) + d->csp = strtoul(cb->f[3], nil, 0); + if(d->ep[i] == nil){ + XPRINT("calling devendpt in usbwrite (CMclass)\n"); + d->ep[i] = devendpt(d, i, 1); + } + d->ep[i]->csp = strtoul(cb->f[3], nil, 0); + break; + case CMdata: + i = strtoul(cb->f[1], nil, 0); + if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) + error(Ebadusbmsg); + e = d->ep[i]; + e->data01 = strtoul(cb->f[2], nil, 0) != 0; + break; + case CMmaxpkt: + i = strtoul(cb->f[1], nil, 0); + if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) + error(Ebadusbmsg); + e = d->ep[i]; + e->maxpkt = strtoul(cb->f[2], nil, 0); + if(e->maxpkt > 1500) + e->maxpkt = 1500; + break; + case CMadjust: + i = strtoul(cb->f[1], nil, 0); + if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) + error(Ebadusbmsg); + e = d->ep[i]; + if (e->iso == 0) + error(Eperm); + i = strtoul(cb->f[2], nil, 0); + /* speed may not result in change of maxpkt */ + if (i < (e->maxpkt-1)/e->samplesz * 1000/e->pollms + || i > e->maxpkt/e->samplesz * 1000/e->pollms){ + snprint(cmd, sizeof(cmd), "%d < %d < %d?", + (e->maxpkt-1)/e->samplesz * 1000/e->pollms, + i, + e->maxpkt/e->samplesz * 1000/e->pollms); + error(cmd); + } + e->hz = i; + break; + case CMdebug: + i = strtoul(cb->f[1], nil, 0); + if(i < -1 || i >= nelem(d->ep) || d->ep[i] == nil) + error(Ebadusbmsg); + if (i == -1) + debug = 0; + else { + debug = 1; + e = d->ep[i]; + e->debug = strtoul(cb->f[2], nil, 0); + } + break; + case CMunstall: + i = strtoul(cb->f[1], nil, 0); + if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) + error(Ebadusbmsg); + e = d->ep[i]; + e->err = nil; + break; + case CMep: + /* ep n `bulk' mode maxpkt nbuf OR + * ep n period mode samplesize Hz + */ + i = strtoul(cb->f[1], nil, 0); + if(i < 0 || i >= nelem(d->ep)) { + XPRINT("field 1: 0 <= %d < %d\n", i, nelem(d->ep)); + error(Ebadarg); + } + if((e = d->ep[i]) == nil){ + XPRINT("calling devendpt in usbwrite (CMep)\n"); + e = devendpt(d, i, 1); + } + qlock(uh); + if(waserror()){ + freept(e); + qunlock(uh); + nexterror(); + } + if(e->active) + error(Eperm); + if(strcmp(cb->f[2], "bulk") == 0){ + /* ep n `bulk' mode maxpkt nbuf */ + e->iso = 0; + i = strtoul(cb->f[4], nil, 0); + if(i < 8 || i > 1023) + i = 8; + e->maxpkt = i; + i = strtoul(cb->f[5], nil, 0); + if(i >= 1 && i <= 32) + e->nbuf = i; + } else { + /* ep n period mode samplesize Hz */ + i = strtoul(cb->f[2], nil, 0); + if(i > 0 && i <= 1000){ + e->pollms = i; + }else { + XPRINT("field 4: 0 <= %d <= 1000\n", i); + error(Ebadarg); + } + i = strtoul(cb->f[4], nil, 0); + if(i >= 1 && i <= 8){ + e->samplesz = i; + }else { + XPRINT("field 4: 0 < %d <= 8\n", i); + error(Ebadarg); + } + i = strtoul(cb->f[5], nil, 0); + if(i >= 1 && i*e->samplesz <= 12*1000*1000){ + /* Hz */ + e->hz = i; + e->remain = 0; + }else { + XPRINT("field 5: 1 < %d <= 100000 Hz\n", i); + error(Ebadarg); + } + e->maxpkt = (e->hz * e->pollms + 999)/1000 * e->samplesz; + e->iso = 1; + } + e->mode = strcmp(cb->f[3],"r") == 0? OREAD : + strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR; + uh->epmode(uh, e); + poperror(); + qunlock(uh); + } + + poperror(); + free(cb); + return n; + + case Qep0: /* SETUP endpoint 0 */ + /* should canqlock etc */ + e = d->ep[0]; + if(e == nil || e->iso) + error(Egreg); + if(n < 8) + error(Eio); + nw = *(uchar*)a & RD2H; + e->data01 = 0; + n = uh->write(uh, e, a, n, 0LL, TokSETUP); + if(nw == 0) { /* host to device: use IN[DATA1] to ack */ + e->data01 = 1; + nw = uh->read(uh, e, cmd, 0LL, 8); + if(nw != 0) + error(Eio); /* could provide more status */ + }else + e->setin = 1; /* two-phase */ + break; + + default: /* sends DATA[01] */ + t -= Qep0; + if(t < 0 || t >= nelem(d->ep)) + error(Egreg); + e = d->ep[t]; + if(e == nil || e->mode == OREAD) + error(Egreg); + n = uh->write(uh, e, a, n, offset, TokOUT); + break; + } + return n; +} + +Dev usbdevtab = { + 'U', + "usb", + + usbreset, + usbinit, + devshutdown, + usbattach, + usbwalk, + usbstat, + usbopen, + devcreate, + usbclose, + usbread, + devbread, + usbwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/manga/eswnotes b/os/manga/eswnotes new file mode 100644 index 00000000..4a4b892a --- /dev/null +++ b/os/manga/eswnotes @@ -0,0 +1,41 @@ +- switch level + - VlanEn vlan enable + - disable tx/rx flow control + - buffer sharing control + - unicast port-VLAN mismatch discard + - fair flow control on/off + - priority buffer reserved + - high prio first, 10:1, 5:1 2:1 + - tag mask enabled + - enable/disable switch? + - TPID mode for direct forwarding from port 5 + - replace null VID with default port VID + - 802.1p base priority + +- port level + - auto negotiation + - spanning tree tx/rx/learn on/off + - priority classification on/off + - diffserve priority classification on/off + - 802.1p classification on/off + - some of those are possibly mutually exclusive + - priority function enabled + - default tag: userprio (3 bits), CFI (mbz), 12-bit VID + - VLAN related + - ingress filter (discard packets from port not in VLAN) + - discard non pvid (discard tagged packets not matching port's default VID) + - receive rate control + - high priority 8-bits, low priority 8-bits + - enable rate and/or flow control, high and low priority, tx/rx + +- FID management + +- static MAC management +- VLAN table (16 entries) + - port membership + - FID + - 802.1Q 12-bit VID + +- stats + +- 8100 or 810x diff --git a/os/manga/ether8139.c b/os/manga/ether8139.c new file mode 100644 index 00000000..d64e0b20 --- /dev/null +++ b/os/manga/ether8139.c @@ -0,0 +1,744 @@ +/* + * Realtek 8139 (but not the 8129). + * Error recovery for the various over/under -flow conditions + * may need work. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +enum { /* registers */ + Idr0 = 0x0000, /* MAC address */ + Mar0 = 0x0008, /* Multicast address */ + Tsd0 = 0x0010, /* Transmit Status Descriptor0 */ + Tsad0 = 0x0020, /* Transmit Start Address Descriptor0 */ + Rbstart = 0x0030, /* Receive Buffer Start Address */ + Erbcr = 0x0034, /* Early Receive Byte Count */ + Ersr = 0x0036, /* Early Receive Status */ + Cr = 0x0037, /* Command Register */ + Capr = 0x0038, /* Current Address of Packet Read */ + Cbr = 0x003A, /* Current Buffer Address */ + Imr = 0x003C, /* Interrupt Mask */ + Isr = 0x003E, /* Interrupt Status */ + Tcr = 0x0040, /* Transmit Configuration */ + Rcr = 0x0044, /* Receive Configuration */ + Tctr = 0x0048, /* Timer Count */ + Mpc = 0x004C, /* Missed Packet Counter */ + Cr9346 = 0x0050, /* 9346 Command Register */ + Config0 = 0x0051, /* Configuration Register 0 */ + Config1 = 0x0052, /* Configuration Register 1 */ + TimerInt = 0x0054, /* Timer Interrupt */ + Msr = 0x0058, /* Media Status */ + Config3 = 0x0059, /* Configuration Register 3 */ + Config4 = 0x005A, /* Configuration Register 4 */ + Mulint = 0x005C, /* Multiple Interrupt Select */ + RerID = 0x005E, /* PCI Revision ID */ + Tsad = 0x0060, /* Transmit Status of all Descriptors */ + + Bmcr = 0x0062, /* Basic Mode Control */ + Bmsr = 0x0064, /* Basic Mode Status */ + Anar = 0x0066, /* Auto-Negotiation Advertisment */ + Anlpar = 0x0068, /* Auto-Negotiation Link Partner */ + Aner = 0x006A, /* Auto-Negotiation Expansion */ + Dis = 0x006C, /* Disconnect Counter */ + Fcsc = 0x006E, /* False Carrier Sense Counter */ + Nwaytr = 0x0070, /* N-way Test */ + Rec = 0x0072, /* RX_ER Counter */ + Cscr = 0x0074, /* CS Configuration */ + Phy1parm = 0x0078, /* PHY Parameter 1 */ + Twparm = 0x007C, /* Twister Parameter */ + Phy2parm = 0x0080, /* PHY Parameter 2 */ +}; + +enum { /* Cr */ + Bufe = 0x01, /* Rx Buffer Empty */ + Te = 0x04, /* Transmitter Enable */ + Re = 0x08, /* Receiver Enable */ + Rst = 0x10, /* Software Reset */ +}; + +enum { /* Imr/Isr */ + Rok = 0x0001, /* Receive OK */ + Rer = 0x0002, /* Receive Error */ + Tok = 0x0004, /* Transmit OK */ + Ter = 0x0008, /* Transmit Error */ + Rxovw = 0x0010, /* Receive Buffer Overflow */ + PunLc = 0x0020, /* Packet Underrun or Link Change */ + Fovw = 0x0040, /* Receive FIFO Overflow */ + Clc = 0x2000, /* Cable Length Change */ + Timerbit = 0x4000, /* Timer */ + Serr = 0x8000, /* System Error */ +}; + +enum { /* Tcr */ + Clrabt = 0x00000001, /* Clear Abort */ + TxrrSHIFT = 4, /* Transmit Retry Count */ + TxrrMASK = 0x000000F0, + MtxdmaSHIFT = 8, /* Max. DMA Burst Size */ + MtxdmaMASK = 0x00000700, + Mtxdma2048 = 0x00000700, + Acrc = 0x00010000, /* Append CRC (not) */ + LbkSHIFT = 17, /* Loopback Test */ + LbkMASK = 0x00060000, + Rtl8139ArevG = 0x00800000, /* RTL8139A Rev. G ID */ + IfgSHIFT = 24, /* Interframe Gap */ + IfgMASK = 0x03000000, + HwveridSHIFT = 26, /* Hardware Version ID */ + HwveridMASK = 0x7C000000, +}; + +enum { /* Rcr */ + Aap = 0x00000001, /* Accept All Packets */ + Apm = 0x00000002, /* Accept Physical Match */ + Am = 0x00000004, /* Accept Multicast */ + Ab = 0x00000008, /* Accept Broadcast */ + Ar = 0x00000010, /* Accept Runt */ + Aer = 0x00000020, /* Accept Error */ + Sel9356 = 0x00000040, /* 9356 EEPROM used */ + Wrap = 0x00000080, /* Rx Buffer Wrap Control */ + MrxdmaSHIFT = 8, /* Max. DMA Burst Size */ + MrxdmaMASK = 0x00000700, + Mrxdmaunlimited = 0x00000700, + RblenSHIFT = 11, /* Receive Buffer Length */ + RblenMASK = 0x00001800, + Rblen8K = 0x00000000, /* 8KB+16 */ + Rblen16K = 0x00000800, /* 16KB+16 */ + Rblen32K = 0x00001000, /* 32KB+16 */ + Rblen64K = 0x00001800, /* 64KB+16 */ + RxfthSHIFT = 13, /* Receive Buffer Length */ + RxfthMASK = 0x0000E000, + Rxfth256 = 0x00008000, + Rxfthnone = 0x0000E000, + Rer8 = 0x00010000, /* Accept Error Packets > 8 bytes */ + MulERINT = 0x00020000, /* Multiple Early Interrupt Select */ + ErxthSHIFT = 24, /* Early Rx Threshold */ + ErxthMASK = 0x0F000000, + Erxthnone = 0x00000000, +}; + +enum { /* Received Packet Status */ + Rcok = 0x0001, /* Receive Completed OK */ + Fae = 0x0002, /* Frame Alignment Error */ + Crc = 0x0004, /* CRC Error */ + Long = 0x0008, /* Long Packet */ + Runt = 0x0010, /* Runt Packet Received */ + Ise = 0x0020, /* Invalid Symbol Error */ + Bar = 0x2000, /* Broadcast Address Received */ + Pam = 0x4000, /* Physical Address Matched */ + Mar = 0x8000, /* Multicast Address Received */ +}; + +enum { /* Media Status Register */ + Rxpf = 0x01, /* Pause Flag */ + Txpf = 0x02, /* Pause Flag */ + Linkb = 0x04, /* Inverse of Link Status */ + Speed10 = 0x08, /* 10Mbps */ + Auxstatus = 0x10, /* Aux. Power Present Status */ + Rxfce = 0x40, /* Receive Flow Control Enable */ + Txfce = 0x80, /* Transmit Flow Control Enable */ +}; + +typedef struct Td Td; +struct Td { /* Soft Transmit Descriptor */ + int tsd; + int tsad; + uchar* data; + Block* bp; +}; + +enum { /* Tsd0 */ + SizeSHIFT = 0, /* Descriptor Size */ + SizeMASK = 0x00001FFF, + Own = 0x00002000, + Tun = 0x00004000, /* Transmit FIFO Underrun */ + Tcok = 0x00008000, /* Transmit COmpleted OK */ + EtxthSHIFT = 16, /* Early Tx Threshold */ + EtxthMASK = 0x001F0000, + NccSHIFT = 24, /* Number of Collisions Count */ + NccMASK = 0x0F000000, + Cdh = 0x10000000, /* CD Heartbeat */ + Owc = 0x20000000, /* Out of Window Collision */ + Tabt = 0x40000000, /* Transmit Abort */ + Crs = 0x80000000, /* Carrier Sense Lost */ +}; + +enum { + Rblen = Rblen64K, /* Receive Buffer Length */ + Ntd = 4, /* Number of Transmit Descriptors */ + Tdbsz = ROUNDUP(sizeof(Etherpkt), 4), +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; + + QLock alock; /* attach */ + Lock ilock; /* init */ + void* alloc; /* base of per-Ctlr allocated data */ + + int rcr; /* receive configuration register */ + uchar* rbstart; /* receive buffer */ + int rblen; /* receive buffer length */ + int ierrs; /* receive errors */ + + Lock tlock; /* transmit */ + Td td[Ntd]; + int ntd; /* descriptors active */ + int tdh; /* host index into td */ + int tdi; /* interface index into td */ + int etxth; /* early transmit threshold */ + int taligned; /* packet required no alignment */ + int tunaligned; /* packet required alignment */ + + int dis; /* disconnect counter */ + int fcsc; /* false carrier sense counter */ + int rec; /* RX_ER counter */ +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +#define csr8r(c, r) (inb((c)->port+(r))) +#define csr16r(c, r) (ins((c)->port+(r))) +#define csr32r(c, r) (inl((c)->port+(r))) +#define csr8w(c, r, b) (outb((c)->port+(r), (int)(b))) +#define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w))) +#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l))) + +static void +rtl8139promiscuous(void* arg, int on) +{ + Ether *edev; + Ctlr * ctlr; + + edev = arg; + ctlr = edev->ctlr; + ilock(&ctlr->ilock); + + if(on) + ctlr->rcr |= Aap; + else + ctlr->rcr &= ~Aap; + csr32w(ctlr, Rcr, ctlr->rcr); + iunlock(&ctlr->ilock); +} + +static long +rtl8139ifstat(Ether* edev, void* a, long n, ulong offset) +{ + int l; + char *p; + Ctlr *ctlr; + + ctlr = edev->ctlr; + p = malloc(READSTR); + l = snprint(p, READSTR, "rcr %8.8uX\n", ctlr->rcr); + l += snprint(p+l, READSTR-l, "ierrs %d\n", ctlr->ierrs); + l += snprint(p+l, READSTR-l, "etxth %d\n", ctlr->etxth); + l += snprint(p+l, READSTR-l, "taligned %d\n", ctlr->taligned); + l += snprint(p+l, READSTR-l, "tunaligned %d\n", ctlr->tunaligned); + ctlr->dis += csr16r(ctlr, Dis); + l += snprint(p+l, READSTR-l, "dis %d\n", ctlr->dis); + ctlr->fcsc += csr16r(ctlr, Fcsc); + l += snprint(p+l, READSTR-l, "fcscnt %d\n", ctlr->fcsc); + ctlr->rec += csr16r(ctlr, Rec); + l += snprint(p+l, READSTR-l, "rec %d\n", ctlr->rec); + + l += snprint(p+l, READSTR-l, "Tcr %8.8luX\n", csr32r(ctlr, Tcr)); + l += snprint(p+l, READSTR-l, "Config0 %2.2uX\n", csr8r(ctlr, Config0)); + l += snprint(p+l, READSTR-l, "Config1 %2.2uX\n", csr8r(ctlr, Config1)); + l += snprint(p+l, READSTR-l, "Msr %2.2uX\n", csr8r(ctlr, Msr)); + l += snprint(p+l, READSTR-l, "Config3 %2.2uX\n", csr8r(ctlr, Config3)); + l += snprint(p+l, READSTR-l, "Config4 %2.2uX\n", csr8r(ctlr, Config4)); + + l += snprint(p+l, READSTR-l, "Bmcr %4.4uX\n", csr16r(ctlr, Bmcr)); + l += snprint(p+l, READSTR-l, "Bmsr %4.4uX\n", csr16r(ctlr, Bmsr)); + l += snprint(p+l, READSTR-l, "Anar %4.4uX\n", csr16r(ctlr, Anar)); + l += snprint(p+l, READSTR-l, "Anlpar %4.4uX\n", csr16r(ctlr, Anlpar)); + l += snprint(p+l, READSTR-l, "Aner %4.4uX\n", csr16r(ctlr, Aner)); + l += snprint(p+l, READSTR-l, "Nwaytr %4.4uX\n", csr16r(ctlr, Nwaytr)); + snprint(p+l, READSTR-l, "Cscr %4.4uX\n", csr16r(ctlr, Cscr)); + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static int +rtl8139reset(Ctlr* ctlr) +{ + int timeo; + + /* + * Soft reset the controller. + */ + csr8w(ctlr, Cr, Rst); + for(timeo = 0; timeo < 1000; timeo++){ + if(!(csr8r(ctlr, Cr) & Rst)) + return 0; + delay(1); + } + + return -1; +} + +static void +rtl8139halt(Ctlr* ctlr) +{ + int i; + + csr8w(ctlr, Cr, 0); + csr16w(ctlr, Imr, 0); + csr16w(ctlr, Isr, ~0); + + for(i = 0; i < Ntd; i++){ + if(ctlr->td[i].bp == nil) + continue; + freeb(ctlr->td[i].bp); + ctlr->td[i].bp = nil; + } +} + +static void +rtl8139init(Ether* edev) +{ + int i; + ulong r; + Ctlr *ctlr; + uchar *alloc; + + ctlr = edev->ctlr; + ilock(&ctlr->ilock); + + rtl8139halt(ctlr); + + /* + * MAC Address. + */ + r = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0]; + csr32w(ctlr, Idr0, r); + r = (edev->ea[5]<<8)|edev->ea[4]; + csr32w(ctlr, Idr0+4, r); + + /* + * Receiver + */ + alloc = mmucacheinhib((char*)ROUNDUP((ulong)ctlr->alloc, CACHELINESZ), ctlr->rblen+16 + Ntd*Tdbsz); + ctlr->rbstart = alloc; + alloc += ctlr->rblen+16; + memset(ctlr->rbstart, 0, ctlr->rblen+16); + csr32w(ctlr, Rbstart, PCIWADDR(ctlr->rbstart)); + ctlr->rcr = Rxfth256|Rblen|Mrxdmaunlimited|Ab|Apm; + + /* + * Transmitter. + */ + for(i = 0; i < Ntd; i++){ + ctlr->td[i].tsd = Tsd0+i*4; + ctlr->td[i].tsad = Tsad0+i*4; + ctlr->td[i].data = alloc; + alloc += Tdbsz; + ctlr->td[i].bp = nil; + } + ctlr->ntd = ctlr->tdh = ctlr->tdi = 0; + ctlr->etxth = 128/32; + + /* + * Interrupts. + */ + csr32w(ctlr, TimerInt, 0); + csr16w(ctlr, Imr, Serr|Timerbit|Fovw|PunLc|Rxovw|Ter|Tok|Rer|Rok); + csr32w(ctlr, Mpc, 0); + + /* + * Enable receiver/transmitter. + * Need to enable before writing the Rcr or it won't take. + */ + csr8w(ctlr, Cr, Te|Re); + csr32w(ctlr, Tcr, Mtxdma2048); + csr32w(ctlr, Rcr, ctlr->rcr); + + iunlock(&ctlr->ilock); +} + +static void +rtl8139attach(Ether* edev) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + qlock(&ctlr->alock); + if(ctlr->alloc == nil){ + ctlr->rblen = 1<<((Rblen>>RblenSHIFT)+13); + ctlr->alloc = mallocz(ctlr->rblen+16 + Ntd*Tdbsz + CACHELINESZ, 0); + rtl8139init(edev); + } + qunlock(&ctlr->alock); +} + +static void +rtl8139txstart(Ether* edev) +{ + Td *td; + int size; + Block *bp; + Ctlr *ctlr; + + ctlr = edev->ctlr; + while(ctlr->ntd < Ntd){ + bp = qget(edev->oq); + if(bp == nil) + break; + size = BLEN(bp); + + td = &ctlr->td[ctlr->tdh]; + if(((int)bp->rp) & 0x03){ + memmove(td->data, bp->rp, size); + dcflush(td->data, size); + freeb(bp); + csr32w(ctlr, td->tsad, PCIWADDR(td->data)); + ctlr->tunaligned++; + } + else{ + td->bp = bp; + csr32w(ctlr, td->tsad, PCIWADDR(bp->rp)); + dcflush(bp->rp, size); + ctlr->taligned++; + } + csr32w(ctlr, td->tsd, (ctlr->etxth<ntd++; + ctlr->tdh = NEXT(ctlr->tdh, Ntd); + } +} + +static void +rtl8139transmit(Ether* edev) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + ilock(&ctlr->tlock); + rtl8139txstart(edev); + iunlock(&ctlr->tlock); +} + +static void +rtl8139receive(Ether* edev) +{ + Block *bp; + Ctlr *ctlr; + ushort capr; + uchar cr, *p; + int l, length, status; + + ctlr = edev->ctlr; + + /* + * Capr is where the host is reading from, + * Cbr is where the NIC is currently writing. + */ + capr = (csr16r(ctlr, Capr)+16) % ctlr->rblen; + while(!(csr8r(ctlr, Cr) & Bufe)){ + p = ctlr->rbstart+capr; + + /* + * Apparently the packet length may be 0xFFF0 if + * the NIC is still copying the packet into memory. + */ + length = (*(p+3)<<8)|*(p+2); + if(length == 0xFFF0) + break; + status = (*(p+1)<<8)|*p; + if(!(status & Rcok)){ + if(status & (Ise|Fae)) + edev->frames++; + if(status & Crc) + edev->crcs++; + if(status & (Runt|Long)) + edev->buffs++; + + /* + * Reset the receiver. + * Also may have to restore the multicast list + * here too if it ever gets used. + */ + cr = csr8r(ctlr, Cr); + csr8w(ctlr, Cr, cr & ~Re); + csr32w(ctlr, Rbstart, PCIWADDR(ctlr->rbstart)); + csr8w(ctlr, Cr, cr); + csr32w(ctlr, Rcr, ctlr->rcr); + + continue; + } + + /* + * Receive Completed OK. + * Very simplistic; there are ways this could be done + * without copying, but the juice probably isn't worth + * the squeeze. + * The packet length includes a 4 byte CRC on the end. + */ + capr = (capr+4) % ctlr->rblen; + p = ctlr->rbstart+capr; + capr = (capr+length) % ctlr->rblen; + + if((bp = iallocb(length)) != nil){ + if(p+length >= ctlr->rbstart+ctlr->rblen){ + l = ctlr->rbstart+ctlr->rblen - p; + memmove(bp->wp, p, l); + bp->wp += l; + length -= l; + p = ctlr->rbstart; + } + if(length > 0){ + memmove(bp->wp, p, length); + bp->wp += length; + } + bp->wp -= 4; + etheriq(edev, bp, 1); + } + + capr = ROUNDUP(capr, 4); + csr16w(ctlr, Capr, capr-16); + } +} + +static void +rtl8139interrupt(Ureg*, void* arg) +{ + Td *td; + Ctlr *ctlr; + Ether *edev; + int isr, msr, tsd; + + edev = arg; + ctlr = edev->ctlr; + + while((isr = csr16r(ctlr, Isr)) != 0){ + csr16w(ctlr, Isr, isr); + if(isr & (Fovw|PunLc|Rxovw|Rer|Rok)){ + rtl8139receive(edev); + if(!(isr & Rok)) + ctlr->ierrs++; + isr &= ~(Fovw|Rxovw|Rer|Rok); + } + + if(isr & (Ter|Tok)){ + ilock(&ctlr->tlock); + while(ctlr->ntd){ + td = &ctlr->td[ctlr->tdi]; + tsd = csr32r(ctlr, td->tsd); + if(!(tsd & (Tabt|Tun|Tcok))) + break; + + if(!(tsd & Tcok)){ + if(tsd & Tun){ + if(ctlr->etxth < ETHERMAXTU/32) + ctlr->etxth++; + } + edev->oerrs++; + } + + if(td->bp != nil){ + freeb(td->bp); + td->bp = nil; + } + + ctlr->ntd--; + ctlr->tdi = NEXT(ctlr->tdi, Ntd); + } + rtl8139txstart(edev); + iunlock(&ctlr->tlock); + isr &= ~(Ter|Tok); + } + + if(isr & PunLc){ + /* + * Maybe the link changed - do we care very much? + */ + msr = csr8r(ctlr, Msr); + if(!(msr & Linkb)){ + if(!(msr & Speed10) && edev->mbps != 100){ + edev->mbps = 100; + qsetlimit(edev->oq, 256*1024); + } + else if((msr & Speed10) && edev->mbps != 10){ + edev->mbps = 10; + qsetlimit(edev->oq, 65*1024); + } + } + isr &= ~(Clc|PunLc); + } + + /* + * Only Serr|Timer should be left by now. + * Should anything be done to tidy up? TimerInt isn't + * used so that can be cleared. A PCI bus error is indicated + * by Serr, that's pretty serious; is there anyhing to do + * other than try to reinitialise the chip? + */ + if(isr != 0){ + iprint("rtl8139interrupt: imr %4.4uX isr %4.4uX\n", + csr16r(ctlr, Imr), isr); + if(isr & Timerbit) + csr32w(ctlr, TimerInt, 0); + if(isr & Serr) + rtl8139init(edev); + } + } +} + +static Ctlr* +rtl8139match(Ether* edev, int id) +{ + int port; + Pcidev *p; + Ctlr *ctlr; + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + p = ctlr->pcidev; + if(((p->did<<16)|p->vid) != id) + continue; + port = p->mem[0].bar & ~0x01; + if(edev->port != 0 && edev->port != port) + continue; + + if(ioalloc(port, p->mem[0].size, 0, "rtl8139") < 0){ + print("rtl8139: port 0x%uX in use\n", port); + continue; + } + + ctlr->port = port; + if(rtl8139reset(ctlr)) + continue; + pcisetbme(p); + + ctlr->active = 1; + return ctlr; + } + return nil; +} + +static struct { + char* name; + int id; +} rtl8139pci[] = { + { "rtl8139", (0x8139<<16)|0x10EC, }, /* generic */ + { "smc1211", (0x1211<<16)|0x1113, }, /* SMC EZ-Card */ + { "dfe-538tx", (0x1300<<16)|0x1186, }, /* D-Link DFE-538TX */ + { "dfe-560txd", (0x1340<<16)|0x1186, }, /* D-Link DFE-560TXD */ + { nil }, +}; + +static int +rtl8139pnp(Ether* edev) +{ + int i, id; + Pcidev *p; + Ctlr *ctlr; + uchar ea[Eaddrlen]; + + /* + * Make a list of all ethernet controllers + * if not already done. + */ + if(ctlrhead == nil){ + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + ctlr = malloc(sizeof(Ctlr)); + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } + } + + /* + * Is it an RTL8139 under a different name? + * Normally a search is made through all the found controllers + * for one which matches any of the known vid+did pairs. + * If a vid+did pair is specified a search is made for that + * specific controller only. + */ + id = 0; + for(i = 0; i < edev->nopt; i++){ + if(cistrncmp(edev->opt[i], "id=", 3) == 0) + id = strtol(&edev->opt[i][3], nil, 0); + } + + ctlr = nil; + if(id != 0) + ctlr = rtl8139match(edev, id); + else for(i = 0; rtl8139pci[i].name; i++){ + if((ctlr = rtl8139match(edev, rtl8139pci[i].id)) != nil) + break; + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pcidev->intl; + edev->tbdf = ctlr->pcidev->tbdf; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the device and set in edev->ea. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, edev->ea, Eaddrlen) == 0){ + i = csr32r(ctlr, Idr0); + edev->ea[0] = i; + edev->ea[1] = i>>8; + edev->ea[2] = i>>16; + edev->ea[3] = i>>24; + i = csr32r(ctlr, Idr0+4); + edev->ea[4] = i; + edev->ea[5] = i>>8; + } + edev->attach = rtl8139attach; + edev->transmit = rtl8139transmit; + edev->interrupt = rtl8139interrupt; + edev->ifstat = rtl8139ifstat; + + edev->arg = edev; + edev->promiscuous = rtl8139promiscuous; + + /* + * This should be much more dynamic but will do for now. + */ + if((csr8r(ctlr, Msr) & (Speed10|Linkb)) == 0) + edev->mbps = 100; + + return 0; +} + +void +ether8139link(void) +{ + addethercard("rtl8139", rtl8139pnp); +} diff --git a/os/manga/etherif.h b/os/manga/etherif.h new file mode 100644 index 00000000..61be9ea0 --- /dev/null +++ b/os/manga/etherif.h @@ -0,0 +1,44 @@ +enum { + MaxEther = 4, + MaxFID= 16, + Ntypes = 8, +}; + +typedef struct Ether Ether; + +struct Ether { +RWlock; /* TO DO */ + ISAConf; /* hardware info */ + int ctlrno; + int tbdf; /* type+busno+devno+funcno */ + int minmtu; + int maxmtu; + uchar ea[Eaddrlen]; + int encry; + + void (*attach)(Ether*); /* filled in by reset routine */ + void (*closed)(Ether*); + void (*detach)(Ether*); + void (*transmit)(Ether*); + void (*interrupt)(Ureg*, void*); + long (*ifstat)(Ether*, void*, long, ulong); + long (*ctl)(Ether*, void*, long); /* custom ctl messages */ + void (*power)(Ether*, int); /* power on/off */ + void (*shutdown)(Ether*); /* shutdown hardware before reboot */ + void *ctlr; + int pcmslot; /* PCMCIA */ + int fullduplex; /* non-zero if full duplex */ + int vlanid; /* non-zero if vlan */ + + Queue* oq; + + QLock vlq; /* array change */ + int nvlan; + Ether* vlans[MaxFID]; + + Netif; +}; + +extern Block* etheriq(Ether*, Block*, int); +extern void addethercard(char*, int(*)(Ether*)); +extern int archether(int, Ether*); diff --git a/os/manga/etherks8695.c b/os/manga/etherks8695.c new file mode 100644 index 00000000..06e7de9e --- /dev/null +++ b/os/manga/etherks8695.c @@ -0,0 +1,1169 @@ +/* + * KS8695P ethernet + * WAN port, LAN port to 4-port switch + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" +#include "ureg.h" + +#define DBG if(0)iprint +#define MIIDBG if(0)iprint + +enum { + Nrdre = 64, /* receive descriptor ring entries */ + Ntdre = 32, /* transmit descriptor ring entries */ + + Rbsize = ROUNDUP(ETHERMAXTU+4, 4), /* ring buffer size (+4 for CRC), must be multiple of 4 */ + Bufsize = ROUNDUP(Rbsize, CACHELINESZ), /* keep start and end at cache lines */ +}; + +typedef struct DmaReg DmaReg; +struct DmaReg { + ulong dtxc; /* transmit control register */ + ulong drxc; /* receive control register */ + ulong dtsc; /* transmit start command register */ + ulong drsc; /* receive start command register */ + ulong tdlb; /* transmit descriptor list base address */ + ulong rdlb; /* receive descriptor list base address */ + ulong mal; /* mac address low (4 bytes) */ + ulong mah; /* mac address high (2 bytes) */ + ulong pad[0x80-0x20]; + + /* pad to 0x80 for */ + ulong maal[16][2]; /* additional mac addresses */ +}; + +enum { + /* dtxc */ + TxSoftReset= 1<<31, + /* 29:24 is burst size in words; 0, 1, 2, 4, 8, 16, 32; 0=unlimited */ + TxUDPck= 1<<18, /* generate UDP, TCP, IP check sum */ + TxTCPck= 1<<17, + TxIPck= 1<<16, + TxFCE= 1<<9, /* transmit flow control enable */ + TxLB= 1<<8, /* loop back */ + TxEP= 1<<2, /* enable padding */ + TxCrc= 1<<1, /* add CRC */ + TxEnable= 1<<0, /* enable Tx block */ + + /* drxc */ + /* 29:24 is burst size in words */ + RxUDPck= 1<<18, /* check UDP, TCP, IP check sum */ + RxTCPck= 1<<17, + RxIPck= 1<<16, + RxFCE= 1<<9, /* flow control enable */ + RxRB= 1<<6, /* receive broadcast */ + RxRM= 1<<5, /* receive multicast (including broadcast) */ + RxRU= 1<<4, /* receive unicast */ + RxAE= 1<<3, /* receive error frames */ + RxRA= 1<<2, /* receive all */ + RxEnable= 1<<0, /* enable Rx block */ + +}; + +typedef struct WanPhy WanPhy; +struct WanPhy { + ulong did; /* device ID */ + ulong rid; /* revision ID */ + ulong pad0; /* miscellaneous control in plain 8695 (not P or X) */ + ulong wmc; /* WAN miscellaneous control */ + ulong wppm; /* phy power management */ + ulong wpc; /* phys ctl */ + ulong wps; /* phys status */ + ulong pps; /* phy power save */ +}; + +enum { + /* wmc */ + WAnc= 1<<30, /* auto neg complete */ + WAnr= 1<<29, /* auto neg restart */ + WAnaP= 1<<28, /* advertise pause */ + WAna100FD= 1<<27, /* advertise 100BASE-TX FD */ + WAna100HD= 1<<26, /* advertise 100BASE-TX */ + WAna10FD= 1<<25, /* advertise 10BASE-TX FD */ + WAna10HD= 1<<24, /* advertise 10BASE-TX */ + WLs= 1<<23, /* link status */ + WDs= 1<<22, /* duplex status (resolved) */ + WSs= 1<<21, /* speed status (resolved) */ + WLparP= 1<<20, /* link partner pause */ + WLpar100FD= 1<<19, /* link partner 100BASE-TX FD */ + WLpar100HD= 1<<18, + WLpar10FD= 1<<17, + WLpar10HD= 1<<16, + WAnDis= 1<<15, /* auto negotiation disable */ + WForce100= 1<<14, + WForceFD= 1<<13, + /* 6:4 LED1 select */ + /* 2:0 LED0 select */ + + /* LED select */ + LedSpeed= 0, + LedLink, + LedFD, /* full duplex */ + LedColl, /* collision */ + LedTxRx, /* activity */ + LedFDColl, /* FD/collision */ + LedLinkTxRx, /* link and activity */ + + /* ppm */ + WLpbk= 1<<14, /* local (MAC) loopback */ + WRlpblk= 1<<13, /* remote (PHY) loopback */ + WPhyIso= 1<<12, /* isolate PHY from MII and Tx+/Tx- */ + WPhyLink= 1<<10, /* force link in PHY */ + WMdix= 1<<9, /* =1, MDIX, =0, MDX */ + WFef= 1<<8, /* far end fault */ + WAmdixp= 1<<7, /* disable IEEE spec for auto-neg MDIX */ + WTxdis= 1<<6, /* disable port's transmitter */ + WDfef= 1<<5, /* disable far end fault detection */ + Wpd= 1<<4, /* power down */ + WDmdx= 1<<3, /* disable auto MDI/MDIX */ + WFmdx= 1<<2, /* if auto disabled, force MDIX */ + WMlpbk= 1<<1, /* local loopback */ + + /* pps */ + Ppsm= 1<<0, /* enable PHY power save mode */ +}; + +#define DMABURST(n) ((n)<<24) + +typedef struct { + Lock; + int port; + int init; + int active; + int reading; /* device read process is active */ + ulong anap; /* auto negotiate result */ + DmaReg* regs; + WanPhy* wphy; + + Ring; + + ulong interrupts; /* statistics */ + ulong deferred; + ulong heartbeat; + ulong latecoll; + ulong retrylim; + ulong underrun; + ulong overrun; + ulong carrierlost; + ulong retrycount; +} Ctlr; + +static void switchinit(uchar*); +static void switchdump(void); + +static void +attach(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + if(!ctlr->active){ + /* TO DO: rx/tx enable */ + ctlr->regs->dtxc |= TxEnable; + ctlr->regs->drxc |= RxEnable; + microdelay(10); + ctlr->regs->drsc = 1; /* start read process */ + microdelay(10); + ctlr->reading = (INTRREG->st & (1<active = 1; + } + iunlock(ctlr); +} + +static void +closed(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + if(ctlr->active){ + ilock(ctlr); +iprint("ether closed\n"); + ctlr->regs->dtxc &= ~TxEnable; + ctlr->regs->drxc &= ~RxEnable; + /* TO DO: reset ring? */ + /* TO DO: could wait? */ + ctlr->active = 0; + iunlock(ctlr); + } +} + +static void +promiscuous(void* arg, int on) +{ + Ether *ether; + Ctlr *ctlr; + ulong w; + + ether = (Ether*)arg; + ctlr = ether->ctlr; + + ilock(ctlr); + /* TO DO: must disable reader */ + w = ctlr->regs->drxc; + if(on != ((w&RxRA)!=0)){ + /* TO DO: must disable reader */ + ctlr->regs->drxc = w ^ RxRA; + /* TO DO: restart reader */ + } + iunlock(ctlr); +} + +static void +multicast(void* arg, uchar *addr, int on) +{ + Ether *ether; + Ctlr *ctlr; + + USED(addr, on); /* if on, could SetGroupAddress; if !on, it's hard */ + + ether = (Ether*)arg; + ctlr = ether->ctlr; + + ilock(ctlr); + /* TO DO: must disable reader */ + /* TO DO: use internal multicast tables? (probably needs LRU or some such) */ + if(ether->nmaddr) + ctlr->regs->drxc |= RxRM; + else + ctlr->regs->drxc &= ~RxRM; + iunlock(ctlr); +} + +static void +txstart(Ether *ether) +{ + int len; + Ctlr *ctlr; + Block *b; + BD *dre; + + ctlr = ether->ctlr; + while(ctlr->ntq < ctlr->ntdre-1){ + b = qget(ether->oq); + if(b == 0) + break; + + dre = &ctlr->tdr[ctlr->tdrh]; + if(dre->ctrl & BdBusy) + panic("ether: txstart"); + + /* + * Give ownership of the descriptor to the chip, increment the + * software ring descriptor pointer and tell the chip to poll. + */ + len = BLEN(b); + if(ctlr->txb[ctlr->tdrh] != nil) + panic("etherks8695: txstart"); + ctlr->txb[ctlr->tdrh] = b; + dcflush(b->rp, len); + dre->addr = PADDR(b->rp); + dre->size = TxIC|TxFS|TxLS | len; + dre->ctrl = BdBusy; + ctlr->regs->dtsc = 1; /* go for it */ + ctlr->ntq++; + ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdre); + } +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + txstart(ether); + iunlock(ctlr); +} + +/* + * allocate receive buffer space on cache-line boundaries + */ +static Block* +clallocb(void) +{ + Block *b; + + b = iallocb(Bufsize+CACHELINESZ-1); + if(b == nil) + return b; + dcflush(b->base, BALLOC(b)); + b->wp = b->rp = (uchar*)(((ulong)b->base + CACHELINESZ - 1) & ~(CACHELINESZ-1)); + return b; +} + + +static void +rxring(Ureg*, void *arg) +{ + Ether *ether; + ulong status; + Ctlr *ctlr; + BD *dre; + Block *b, *rb; + + ether = arg; + ctlr = ether->ctlr; + ctlr->interrupts++; + + /* + * Receiver interrupt: run round the descriptor ring logging + * errors and passing valid receive data up to the higher levels + * until we encounter a descriptor still owned by the chip. + * We rely on the descriptor accesses being uncached. + */ + dre = &ctlr->rdr[ctlr->rdrx]; + while(((status = dre->ctrl) & BdBusy) == 0){ + if(status & RxES || (status & (RxFS|RxLS)) != (RxFS|RxLS)){ + if(status & (RxRF|RxTL)) + ether->buffs++; + if(status & RxRE) + ether->frames++; + if(status & RxCE) + ether->crcs++; + //if(status & RxOverrun) + // ether->overflows++; + iprint("eth rx: %lux\n", status); + }else{ + /* + * We have a packet. Read it in. + */ + b = clallocb(); + if(b != nil){ + rb = ctlr->rxb[ctlr->rdrx]; + rb->wp += (dre->ctrl & RxFL)-4; + etheriq(ether, rb, 1); + ctlr->rxb[ctlr->rdrx] = b; + dre->addr = PADDR(b->wp); + }else + ether->soverflows++; + } + + /* + * Finished with this descriptor, + * give it back to the chip, then on to the next... + */ + dre->ctrl = BdBusy; + + ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdre); + dre = &ctlr->rdr[ctlr->rdrx]; + } +} + +static void +txring(Ureg*, void *arg) +{ + Ether *ether; + Ctlr *ctlr; + BD *dre; + Block *b; + + ether = arg; + ctlr = ether->ctlr; + ctlr->interrupts++; + + /* + * Transmitter interrupt: handle anything queued for a free descriptor. + */ + lock(ctlr); + while(ctlr->ntq){ + dre = &ctlr->tdr[ctlr->tdri]; + if(dre->ctrl & BdBusy) + break; + /* statistics are kept inside the device, but only for LAN */ + /* there seems to be no per-packet error status for transmission */ + b = ctlr->txb[ctlr->tdri]; + if(b == nil) + panic("etherks8695: bufp"); + ctlr->txb[ctlr->tdri] = nil; + freeb(b); + ctlr->ntq--; + ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdre); + } + txstart(ether); + unlock(ctlr); +} + +/* + * receive buffer unavailable (overrun) + */ +static void +rbuintr(Ureg*, void *arg) +{ + Ether *ether; + Ctlr *ctlr; + + ether = arg; + ctlr = ether->ctlr; + + ctlr->interrupts++; + if(ctlr->active) + ctlr->overrun++; + ctlr->reading = 0; +} + +/* + * read process (in device) stopped + */ +static void +rxstopintr(Ureg*, void *arg) +{ + Ether *ether; + Ctlr *ctlr; + + ether = arg; + ctlr = ether->ctlr; + + ctlr->interrupts++; + if(!ctlr->active) + return; + +iprint("rxstopintr\n"); + ctlr->regs->drsc = 1; + /* just restart it? need to fiddle with ring? */ +} + +static void +txstopintr(Ureg*, void *arg) +{ + Ether *ether; + Ctlr *ctlr; + + ether = arg; + ctlr = ether->ctlr; + + ctlr->interrupts++; + if(!ctlr->active) + return; + +iprint("txstopintr\n"); + ctlr->regs->dtsc = 1; + /* just restart it? need to fiddle with ring? */ +} + + +static void +linkchangeintr(Ureg*, void*) +{ + iprint("link change\n"); +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + char *p; + int len; + Ctlr *ctlr; + + if(n == 0) + return 0; + + ctlr = ether->ctlr; + + p = malloc(READSTR); + len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts); + len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", ctlr->carrierlost); + len += snprint(p+len, READSTR-len, "heartbeat: %lud\n", ctlr->heartbeat); + len += snprint(p+len, READSTR-len, "retrylimit: %lud\n", ctlr->retrylim); + len += snprint(p+len, READSTR-len, "retrycount: %lud\n", ctlr->retrycount); + len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", ctlr->latecoll); + len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", ctlr->overrun); + len += snprint(p+len, READSTR-len, "txunderruns: %lud\n", ctlr->underrun); +{DmaReg *d = ctlr->regs; len += snprint(p+len, READSTR-len, "dtxc=%8.8lux drxc=%8.8lux\n", d->dtxc, d->drxc);} + snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->deferred); + n = readstr(offset, a, n, p); + free(p); + + if(ctlr->port == 1) + switchdump(); + return n; +} + +static void +physinit(Ether *ether, int force) +{ + Ctlr *ctlr; + WanPhy *p; + ulong anap; + int i; + + ctlr = ether->ctlr; + p = ctlr->wphy; + if(p == nil){ + if(ctlr->port){ + ether->mbps = 100; + ether->fullduplex = 1; + switchinit(nil); + } + return; + } + iprint("phy%d: wmc=%8.8lux wpm=%8.8lux wpc=%8.8lux wps=%8.8lux pps=%8.8lux\n", ctlr->port, p->wmc, p->wppm, p->wpc, p->wps, p->pps); + + p->wppm = 0; /* enable power, other defaults seem fine */ + if(p->rid & 7) + p->wpc = 0x0200b000; /* magic */ + else + p->wpc = 0xb000; + if(p->wppm & WFef) + iprint("ether%d: far end fault\n", ctlr->port); + + if((p->wmc & WLs) == 0){ + iprint("ether%d: no link\n", ctlr->port); + ether->mbps = 100; /* could use 10, but this is 2005 */ + ether->fullduplex = 0; + return; + } + + if((p->wmc & WAnc) == 0 || force){ + p->wmc = WAnr | WAnaP | WAna100FD | WAna100HD | WAna10FD | WAna10HD | (p->wmc & 0x7F); + microdelay(10); + if(p->wmc & WLs){ + for(i=0;; i++){ + if(i > 600){ + iprint("ether%d: auto negotiation failed\n", ctlr->port); + ether->mbps = 10; /* we'll assume it's stupid */ + ether->fullduplex = 0; + return; + } + if(p->wmc & WAnc){ + microdelay(10); + break; + } + delay(1); + } + } + } + anap = p->wmc; + ether->mbps = anap & WSs? 100: 10; + if(anap & (WLpar100FD|WLpar10FD) && anap & WDs) + ether->fullduplex = 1; + else + ether->fullduplex = 0; + ctlr->anap = anap; + + iprint("ks8695%d mii: fd=%d speed=%d wmc=%8.8lux\n", ctlr->port, ether->fullduplex, ether->mbps, anap); +} + +static void +ctlrinit(Ctlr *ctlr, Ether *ether) +{ + int i; + DmaReg *em; + ulong mode; + + em = ctlr->regs; + + /* soft reset */ + em->dtxc = TxSoftReset; + microdelay(10); + for(i=0; em->dtxc & TxSoftReset; i++){ + if(i > 20){ + iprint("etherks8695.%d: soft reset failed\n", ctlr->port); + i=0; + } + microdelay(100); + } +iprint("%d: rx=%8.8lux tx=%8.8lux\n", ctlr->port, PADDR(ctlr->rdr), PADDR(ctlr->tdr)); + + physinit(ether, 0); + + /* set ether address */ + em->mah = (ether->ea[0]<<8) | ether->ea[1]; + em->mal = (ether->ea[2]<<24) | (ether->ea[3]<<16) | (ether->ea[4]<<8) | ether->ea[5]; + if(ctlr->port == 0){ + /* clear other addresses for now */ + for(i=0; imaal); i++){ + em->maal[i][0] = 0; + em->maal[i][1] = 0; + } + } + + /* transmitter, enabled later by attach */ + em->tdlb = PADDR(ctlr->tdr); + em->dtxc = DMABURST(8) | TxFCE | TxCrc; /* don't set TxEP: there is a h/w bug and it's anyway done by higher levels */ + + /* receiver, enabled later by attach */ + em->rdlb = PADDR(ctlr->rdr); + mode = DMABURST(8) | RxRB | RxRU | RxAE; /* RxAE just there for testing */ + if(ether->fullduplex) + mode |= RxFCE; + em->drxc = mode; + + /* tx/rx enable is deferred until attach */ +} + +static int +reset(Ether* ether) +{ + uchar ea[Eaddrlen]; + char name[KNAMELEN]; + Ctlr *ctlr; + int i, irqdelta; + + snprint(name, sizeof(name), "ether%d", ether->ctlrno); + + /* + * Insist that the platform-specific code provide the Ethernet address + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + print("%s (%s %ld): no ether address", name, ether->type, ether->port); + return -1; + } + + ctlr = malloc(sizeof(*ctlr)); + ctlr->port = ether->port; + + switch(ether->port){ + case 0: + ctlr->regs = KADDR(PHYSWANDMA); + ctlr->wphy = KADDR(PHYSMISC); + ctlr->wphy->wmc = (ctlr->wphy->wmc & ~0x7F) | (LedLinkTxRx<<0) | (LedSpeed<<4); + break; + case 1: + ctlr->regs = KADDR(PHYSLANDMA); + ctlr->wphy = nil; + break; + default: + print("%s: %s ether: no port %lud\n", name, ether->type, ether->port); + free(ctlr); + return -1; + } + + ether->ctlr = ctlr; + irqdelta = ether->irq - IRQwmrps; + + physinit(ether, 0); + + if(ioringinit(ctlr, Nrdre, Ntdre) < 0) + panic("etherks8695 initring"); + + for(i = 0; i < ctlr->nrdre; i++){ + if(ctlr->rxb[i] == nil) + ctlr->rxb[i] = clallocb(); + ctlr->rdr[i].addr = PADDR(ctlr->rxb[i]->wp); + ctlr->rdr[i].size = Rbsize; + ctlr->rdr[i].ctrl = BdBusy; + } + + ctlrinit(ctlr, ether); + + ether->attach = attach; + ether->closed = closed; + ether->transmit = transmit; + ether->ifstat = ifstat; + + /* there is more than one interrupt: we must enable some ourselves */ + ether->irq = irqdelta + IRQwmrs; /* set main IRQ to receive status */ + ether->interrupt = rxring; + intrenable(IRQ, irqdelta+IRQwmts, txring, ether, "ethertx"); +// intrenable(IRQ, irqdelta+IRQwmtbu, tbuintr, ether, "ethertbu"); /* don't care? */ + intrenable(IRQ, irqdelta+IRQwmrbu, rbuintr, ether, "etherrbu"); + intrenable(IRQ, irqdelta+IRQwmrps, rxstopintr, ether, "etherrps"); + intrenable(IRQ, irqdelta+IRQwmtps, txstopintr, ether, "ethertps"); + if(ether->port == 0) + intrenable(IRQ, IRQwmlc, linkchangeintr, ether, "etherwanlink"); + + ether->arg = ether; + ether->promiscuous = promiscuous; + ether->multicast = multicast; + + return 0; +} + +/* + * switch engine registers + * a 10 microsecond delay is required after each (write?) access + */ +typedef struct Switch Switch; +struct Switch { + ulong sec0; /* control register 0 */ + ulong sec1; /* control register 1 */ + ulong sec2; /* control register 2, factory default, do not change */ + ulong cfg[5][3]; /* port configuration registers */ + ulong an[2]; /* ports 1 to 4 auto negotiation [1,2][3,4] */ + ulong seiac; /* indirect access control register */ + ulong seiadh2; /* indirect access data register 2 (4:0 is 68-64 of data) */ + ulong seiadh1; /* indirect access data register 1 (63-32 of data) */ + ulong seiadl; /* indirect access data register low */ + ulong seafc; /* advanced feature control */ + ulong scph; /* services code priority high (ie, TOS priority) */ + ulong scpl; /* services code priority low */ + ulong mah; /* switch MAC address high */ + ulong mal; /* switch MAC address low */ + ulong ppm[2]; /* ports 1 to 4 PHY power management */ +}; + +enum { + /* Sec0 */ + Nbe= 1<<31, /* new backoff (designed for UNH) enable */ + /* 30:28 802.1p base priority */ + /* 27:25 LAN LED1 select */ + /* 24:22 LAN LED0 select */ + Unh= 1<<21, /* =1, drop packets with type 8808 or DA=0180c2000001; =0, drop flow control */ + Lca= 1<<20, /* link change age: faster aging for link->no link transition */ + Paf= 1<<19, /* pass all frames, including bad ones */ + Sfce= 1<<18, /* switch MII full-duplex flow control enable */ + Flfc= 1<<17, /* frame length field check in IEEE (drop invalid ones) */ + Bsm= 1<<16, /* =1, share all buffers; =0, use only 1/5 of pool */ + Age= 1<<15, /* enable age function */ + Agef= 1<<14, /* enable fast ageing */ + Aboe= 1<<13, /* aggressive backoff enable */ + Uvmd= 1<<12, /* unicast port-VLAN mismatch discard */ + Mspd= 1<<11, /* multicast storm protection disable */ + Bpm= 1<<10, /* =1, carrier sense backpressure; =0, collision backpressure */ + Fair= 1<<9, /* fair flow control and back pressure */ + Ncd= 1<<8, /* no excessive collision drop */ + Lmpsd= 1<<7, /* 1=, drop packet sizes over 1536 bytes; =0, 1522 for tagged, 1518 untagged */ + Pbr= 1<<6, /* priority buffer reserved */ + Sbpe= 1<<5, /* switch back pressure enable */ + Shdm= 1<<4, /* switch half duplex mode */ + PrioHi= 0<<2, /* always deliver high priority first */ + Prio10_1= 1<<2, /* high/low at 10:1 */ + Prio5_1= 2<<2, /* high/low at 5:1 */ + Prio2_1= 3<<2, /* high/low at 2:1 */ + Etm= 1<<1, /* enable tag mask */ + Esf= 1<<0, /* enable switch function */ + + /* sec1 */ + /* 31:21 */ /* broadcast storm protection, byte count */ + IEEEneg= 1<<11, /* follow IEEE spec for auto neg */ + Tpid= 1<<10, /* special TPID mode used for direct forwarding from port 5 */ + PhyEn= 1<<8, /* enable PHY MII */ + TfcDis= 1<<7, /* disable IEEE transmit flow control */ + RfcDis= 1<<6, /* disable IEEE receive flow control */ + Hps= 1<<5, /* huge packet support: allow packets up to 1916 bytes */ + VlanEn= 1<<4, /* 802.1Q VLAN enable; recommended when priority queue on */ + Sw10BT= 1<<1, /* switch in 10 Mbps mode not 100 Mbps */ + VIDrep= 1<<0, /* replace null VID with port VID (otherwise no replacement) */ + +}; +#define BASEPRIO(n) (((n)&7)<<28) + + +enum { + /* cfg[n][0] (SEP1C1-SEP4C1) p. 89 */ + /* 31:16 default tag: 31:29=userprio, 28=CFI bit, 27:16=VID[11:0] */ + AnegDis= 1<<15, /* disable auto negotiation */ + Force100= 1<<14, /* force 100BT when auto neg is disabled */ + ForceFD= 1<<13, /* force full duplex when auto neg is disabled */ + /* 12:8 port VLAN membership: bit 8 is port 1, bit 12 is port 5, 1=member */ + STTxEn= 1<<7, /* spanning tree transmit enable */ + STRxEn= 1<<6, /* spanning tree receive enable */ + STLnDis= 1<<5, /* spanning tree learn disnable */ + Bsp= 1<<4, /* enable broadcast storm protection */ + Pce= 1<<3, /* priority classification enable */ + Dpce= 1<<2, /* diffserv priority classification enable */ + IEEEpce= 1<<1, /* IEEE (802.1p) classification enable */ + PrioEn= 1<<0, /* enable priority function on port */ + + /* cfg[n][1] (SEP1C2-SEP4C2) p. 91*/ + IngressFilter= 1<<28, /* discard packets from ingress port not in VLAN */ + DiscardNonPVID= 1<<27, /* discard packets whose VID does not match port default VID */ + ForcePortFC= 1<<26, /* force flow control */ + EnablePortBP= 1<<25, /* enable back pressure */ + /* 23:12 transmit high priority rate control */ + /* 11:0 transmit low priority rate control */ + + /* cfg[n][2] */ + /* 13:20 receive high priority rate control */ + /* 19:8 receive low priority rate control */ + Rdprc= 1<<7, /* receive differential priority rate control */ + Lprrc= 1<<6, /* low priority receive rate control */ + Hprrc= 1<<5, /* high priority receive rate control */ + Lprfce= 1<<4, /* low priority receive flow control enable */ + Hprfce= 1<<3, /* high priority ... */ + Tdprc= 1<<2, /* transmit differential priority rate control */ + Lptrc= 1<<1, /* low priority transmit rate control */ + Hptrc= 1<<0, /* high priority transmit rate control */ + + /* seiac */ + Cread= 1<<12, + Cwrite= 0<<12, + StaticMacs= 0<<10, /* static mac address table used */ + VLANs= 1<<10, /* VLAN table */ + DynMacs= 2<<10, /* dynamic address table */ + MibCounter= 3<<10, /* MIB counter selected */ + /* 0:9, table index */ + + /* seafc */ + /* 26:22 1<<(n+22-1) = removal for port 0 to 4 */ +}; + +/* + * indirect access to + * static MAC address table (3.10.23, p. 107) + * VLAN table (3.10.24, p. 108) + * dynamic MAC address table (3.10.25, p. 109) + * MIB counters (3.10.26, p. 110) + */ +enum { + /* VLAN table */ + VlanValid= 1<<21, /* entry is valid */ + /* 20:16 are bits for VLAN membership */ + /* 15:12 are bits for FID (filter id) for up to 16 active VLANs */ + /* 11:0 has 802.1Q 12 bit VLAN ID */ + + /* Dynamic MAC table (1024 entries) */ + MACempty= 1<<(68-2*32), + /* 67:58 is number of valid entries-1 */ + /* 57:56 ageing time stamp */ + NotReady= 1<<(55-32), + /* 54:52 source port 0 to 5 */ + /* 51:48 FID */ + /* 47:0 MAC */ + + NVlans= 16, + NSMacs= 8, +}; + +/* + * per-port counters, table 3, 3.10.26, p. 110 + * cleared when read + * port counters at n*0x20 [n=0-3] + */ +static char* portmibnames[] = { + "RxLoPriorityByte", + "RxHiPriorityByte", + "RxUndersizePkt", + "RxFragments", + "RxOversize", + "RxJabbers", + "RxSymbolError", + "RxCRCerror", + "RxAlignmentError", + "RxControl8808Pkts", + "RxPausePkts", + "RxBroadcast", + "RxMulticast", + "RxUnicast", + "Rx64Octets", + "Rx65to127Octets", + "Rx128to255Octets", + "Rx256to511Octets", + "Rx512to1023Octets", + "Rx1024to1522Octets", + "TxLoPriorityByte", + "TxHiPriorityByte", + "TxLateCollision", + "TxPausePkts", + "TxBroadcastPkts", + "TxMulticastPkts", + "TxUnicastPkts", + "TxDeferred", + "TxTotalCollision", /* like, totally */ + "TxExcessiveCollision", + "TxSingleCollision", + "TxMultipleCollision", +}; +enum { + /* per-port MIB counter format */ + MibOverflow= 1<<31, + MibValid= 1<<30, + /* 29:0 counter value */ +}; + +/* + * 16 bit `all port' counters, not automatically cleared + * offset 0x100 and up + */ + +static char* allportnames[] = { + "Port1TxDropPackets", + "Port2TxDropPackets", + "Port3TxDropPackets", + "Port4TxDropPackets", + "LanTxDropPackets", /* ie, internal port 5 */ + "Port1RxDropPackets", + "Port2RxDropPackets", + "Port3RxDropPackets", + "Port4RxDropPackets", + "LanRxDropPackets", +}; + +static void +switchinit(uchar *ea) +{ + Switch *sw; + int i; + ulong an; + + /* TO DO: LED gpio setting */ + + GPIOREG->iopm |= 0xF0; /* bits 4-7 are LAN(?) */ +iprint("switch init...\n"); + sw = KADDR(PHYSSWITCH); + if(sw->sec0 & Esf){ + iprint("already inited\n"); + return; + } + sw->seafc = 0; + microdelay(10); + sw->scph = 0; + microdelay(10); + sw->scpl = 0; + microdelay(10); + if(ea != nil){ + sw->mah = (ea[0]<<8) | ea[1]; + microdelay(10); + sw->mal = (ea[2]<<24) | (ea[3]<<16) | (ea[4]<<8) | ea[5]; + microdelay(10); + } + for(i = 0; i < 5; i++){ + sw->cfg[i][0] = (0x1F<<8) | STTxEn | STRxEn | Bsp; /* port is member of all vlans */ + microdelay(10); + sw->cfg[i][1] = 0; + microdelay(10); + sw->cfg[i][2] = 0; + microdelay(10); + } + sw->ppm[0] = 0; /* perhaps soft reset? */ + microdelay(10); + sw->ppm[1] = 0; + microdelay(10); + an = WAnr | WAnaP | WAna100FD | WAna100HD | WAna10FD | WAna10HD; + sw->an[0] = an | (an >> 16); + microdelay(10); + sw->an[1] = an | (an >> 16); + microdelay(10); + sw->sec1 = (0x4A<<21) | PhyEn; + microdelay(10); + sw->sec0 = Nbe | (0<<28) | (LedSpeed<<25) | (LedLinkTxRx<<22) | Sfce | Bsm | Age | Aboe | Bpm | Fair | Sbpe | Shdm | Esf; + microdelay(10); + + /* off we go */ +} + +typedef struct Vidmap Vidmap; +struct Vidmap { + uchar ports; /* bit mask for ports 0 to 4 */ + uchar fid; /* switch's filter id */ + ushort vid; /* 802.1Q vlan id; 0=not valid */ +}; + +static Vidmap +getvidmap(Switch *sw, int i) +{ + ulong w; + Vidmap v; + + v.ports = 0; + v.fid = 0; + v.vid = 0; + if(i < 0 || i >= NVlans) + return v; + sw->seiac = Cread | VLANs | i; + microdelay(10); + w = sw->seiadl; + if((w & VlanValid) == 0) + return v; + v.vid = w & 0xFFFF; + v.fid = (w>>12) & 0xF; + v.ports = (w>>16) & 0x1F; + return v; +} + +static void +putvidmap(Switch *sw, int i, Vidmap v) +{ + ulong w; + + w = ((v.ports & 0x1F)<<16) | ((v.fid & 0xF)<<12) | (v.vid & 0xFFFF); + if(v.vid != 0) + w |= VlanValid; + sw->seiadl = w; + microdelay(10); + sw->seiac = Cwrite | VLANs | i; + microdelay(10); +} + +typedef struct StaticMac StaticMac; +struct StaticMac { + uchar valid; + uchar fid; + uchar usefid; + uchar override; /* override spanning tree tx/rx disable */ + uchar ports; /* forward to this set of ports */ + uchar mac[Eaddrlen]; +}; + +static StaticMac +getstaticmac(Switch *sw, int i) +{ + StaticMac s; + ulong w; + + memset(&s, 0, sizeof(s)); + if(i < 0 || i >= NSMacs) + return s; + sw->seiac = Cread | StaticMacs | i; + microdelay(10); + w = sw->seiadh1; + if((w & (1<<(53-32))) == 0) + return s; /* entry not valid */ + s.valid = 1; + s.fid= (w>>(57-32)) & 0xF; + s.usefid = (w & (1<<(56-32))) != 0; + s.override = (w & (1<<(54-32))) != 0; + s.ports = (w>>(48-32)) & 0x1F; + s.mac[5] = w >> 8; + s.mac[4] = w; + w = sw->seiadl; + s.mac[3] = w>>24; + s.mac[2] = w>>16; + s.mac[1] = w>>8; + s.mac[0] = w; + return s; +} + +static void +putstaticmac(Switch *sw, int i, StaticMac s) +{ + ulong w; + + if(s.valid){ + w = 1<<(53-32); /* entry valid */ + if(s.usefid) + w |= 1<<(55-32); + if(s.override) + w |= 1<<(54-32); + w |= (s.fid & 0xF) << (56-32); + w |= (s.ports & 0x1F) << (48-32); + w |= (s.mac[5] << 8) | s.mac[4]; + sw->seiadh1 = w; + microdelay(10); + w = (s.mac[3]<<24) | (s.mac[2]<<16) | (s.mac[1]<<8) | s.mac[0]; + sw->seiadl = w; + microdelay(10); + }else{ + sw->seiadh1 = 0; /* valid bit is 0; rest doesn't matter */ + microdelay(10); + } + sw->seiac = Cwrite | StaticMacs | i; + microdelay(10); +} + +typedef struct DynMac DynMac; +struct DynMac { + ushort nentry; + uchar valid; + uchar age; + uchar port; /* source port (0 origin) */ + uchar fid; /* filter id */ + uchar mac[Eaddrlen]; +}; + +static DynMac +getdynmac(Switch *sw, int i) +{ + DynMac d; + ulong w; + int n, l; + + memset(&d, 0, sizeof d); + l = 0; + do{ + if(++l > 100) + return d; + sw->seiac = Cread | DynMacs | i; + microdelay(10); + w = sw->seiadh2; + /* peculiar encoding of table size */ + if(w & MACempty) + return d; + n = w & 0xF; + w = sw->seiadh1; + }while(w & NotReady); /* TO DO: how long might it delay? */ + d.nentry = ((n<<6) | (w>>(58-32))) + 1; + if(i < 0 || i >= d.nentry) + return d; + d.valid = 1; + d.age = (w>>(56-32)) & 3; + d.port = (w>>(52-32)) & 7; + d.fid = (w>>(48-32)) & 0xF; + d.mac[5] = w>>8; + d.mac[4] = w; + w = sw->seiadl; + d.mac[3] = w>>24; + d.mac[2] = w>>16; + d.mac[1] = w>>8; + d.mac[0] = w; + return d; +} + +static void +switchdump(void) +{ + Switch *sw; + int i, j; + ulong w; + + sw = KADDR(PHYSSWITCH); + iprint("sec0 %8.8lux\n", sw->sec0); + iprint("sec1 %8.8lux\n", sw->sec1); + for(i = 0; i < 5; i++){ + iprint("cfg%d", i); + for(j = 0; j < 3; j++){ + w = sw->cfg[i][j]; + iprint(" %8.8lux", w); + } + iprint("\n"); + if(i < 2){ + w = sw->an[i]; + iprint(" an=%8.8lux pm=%8.8lux\n", w, sw->ppm[i]); + } + } + for(i = 0; i < 8; i++){ + sw->seiac = Cread | DynMacs | i; + microdelay(10); + w = sw->seiadh2; + microdelay(10); + iprint("dyn%d: %8.8lux", i, w); + w = sw->seiadh1; + microdelay(10); + iprint(" %8.8lux", w); + w = sw->seiadl; + microdelay(10); + iprint(" %8.8lux\n", w); + } + for(i=0; i<0x20; i++){ + sw->seiac = Cread | MibCounter | i; + microdelay(10); + w = sw->seiadl; + microdelay(10); + if(w & (1<<30)) + iprint("%.2ux: %s: %lud\n", i, portmibnames[i], w & ~(3<<30)); + } +} + +static void +switchstatproc(void*) +{ + for(;;){ + tsleep(&up->sleep, return0, nil, 30*1000); + } +} + +void +etherks8695link(void) +{ + addethercard("ks8695", reset); +} + +/* + * notes: + * switch control + * read stats every 30 seconds or so + */ diff --git a/os/manga/flashif.h b/os/manga/flashif.h new file mode 100644 index 00000000..14b6a9e1 --- /dev/null +++ b/os/manga/flashif.h @@ -0,0 +1,82 @@ +typedef struct Flash Flash; + +/* + * structure defining a flash memory card + */ +struct Flash { + QLock; /* interlock on flash operations */ + Flash* next; + + /* the following are filled in by devflash before Flash.reset called */ + char* name; + void* addr; + ulong size; + void * archdata; + int (*reset)(Flash*); + + /* the following are filled in by the reset routine */ + int (*eraseall)(Flash*); + int (*erasezone)(Flash*, int); + int (*read)(Flash*, ulong, void*, long); /* reads of correct width and alignment */ + int (*write)(Flash*, ulong, void*, long); /* writes of correct width and alignment */ + int (*suspend)(Flash*); + int (*resume)(Flash*); + int (*attach)(Flash*); + + uchar id; /* flash manufacturer ID */ + uchar devid; /* flash device ID */ + int width; /* bytes per flash line */ + int erasesize; /* size of erasable unit (accounting for width) */ + void* data; /* flash type routines' private storage, or nil */ + ulong unusable; /* bit mask of unusable sections */ +}; + +/* + * called by link routine of driver for specific flash type: arguments are + * conventional name for card type/model, and card driver's reset routine. + */ +void addflashcard(char*, int (*)(Flash*)); + +/* + * called by devflash.c:/^flashreset; if flash exists, + * sets type, address, and size in bytes of flash + * and returns 0; returns -1 if flash doesn't exist + */ +int archflashreset(int instance, char*, int, void**, long*, void **archdata); + +int archflash12v(int); +void archflashwp(void *archdata, int); + +/* + * Architecture specific routines for managing nand devices + */ + +/* + * do any device spcific initialisation + */ +void archnand_init(void *archdata); + +/* + * if claim is 1, claim device exclusively, and enable it (power it up) + * if claim is 0, release, and disable it (power it down) + * claiming may be as simple as a qlock per device + */ +void archnand_claim(void *archdata, int claim); + +/* + * set command latch enable (CLE) and address latch enable (ALE) + * appropriately + */ +void archnand_setCLEandALE(void *archdata, int cle, int ale); + +/* + * write a sequence of bytes to the device + */ +void archnand_write(void *archdata, void *buf, int len); + +/* + * read a sequence of bytes from the device + * if buf is 0, throw away the data + */ +void archnand_read(void *archdata, void *buf, int len); + diff --git a/os/manga/fns.h b/os/manga/fns.h new file mode 100644 index 00000000..6eb28e26 --- /dev/null +++ b/os/manga/fns.h @@ -0,0 +1,163 @@ +#include "../port/portfns.h" + +ulong aifinit(uchar *aifarr); +int archaudiopower(int); +void archaudiomute(int); +void archaudioamp(int); +int archaudiospeed(int, int); +void archconfinit(void); +void archconsole(void); +int archflash12v(int); +long archkprofmicrosecondspertick(void); +void archkprofenable(int); +void archpowerdown(void); +void archpowerup(void); +void archreboot(void); +void archreset(void); +vlong archrdtsc(void); +ulong archrdtsc32(void); +void archuartpower(int, int); +void blankscreen(int); +void clockcheck(void); +void clockinit(void); +void clockpoll(void); +#define coherence() /* nothing to do for cache coherence for uniprocessor */ +void cursorhide(void); +void cursorunhide(void); +void dcflush(void*, ulong); +void dcflushall(void); +void dcinval(void); +int dmaidle(Dma*); +Dma* dmasetup(int device, void(*)(void*,ulong), void*, ulong); +int dmastart(Dma*, void*, void*, int); +int dmacontinue(Dma*, void*, int); +void dmastop(Dma*); +int dmaerror(Dma*); +void dmafree(Dma*); +void dmareset(void); +void dmawait(Dma*); +void dumplongs(char *, ulong *, int); +void dumpregs(Ureg* ureg); +void dumpstack(void); +int fpiarm(Ureg*); +void fpinit(void); +ulong getcallerpc(void*); +ulong getcclkcfg(void); +char* getconf(char*); +ulong getcpsr(void); +ulong getcpuid(void); +ulong getspsr(void); +void gotopc(ulong); + +void icflush(void*, ulong); +void icflushall(void); +void idle(void); +void idlehands(void); +int inb(ulong); +int ins(ulong); +ulong inl(ulong); +void outb(ulong, int); +void outs(ulong, int); +void outl(ulong, ulong); +void inss(ulong, void*, int); +void outss(ulong, void*, int); +void insb(ulong, void*, int); +void outsb(ulong, void*, int); +void intrdisable(int, int, void (*)(Ureg*, void*), void*, char*); +void intrenable(int, int, void (*)(Ureg*, void*), void*, char*); +void iofree(int); +#define iofree(x) +void ioinit(void); +int iounused(int, int); +int ioalloc(int, int, int, char*); +#define ioalloc(a,b,c,d) 0 +int iprint(char*, ...); +void installprof(void (*)(Ureg *, int)); +int isvalid_va(void*); +void kbdinit(void); +void ledset(int); +void links(void); +void mmuenable(ulong); +void* mmucacheinhib(void*, ulong); +ulong mmugetctl(void); +ulong mmugetdac(void); +ulong mmugetfar(void); +ulong mmugetfsr(void); +void mmuinit(void); +void* mmukaddr(ulong); +void* mmuphysmap(void*, ulong, ulong); +void mmuputctl(ulong); +void mmuputdac(ulong); +void mmuputfsr(ulong); +void mmuputttb(ulong); +void mmureset(void); +void mouseinit(void); +void* pa2va(ulong); +void pcimapinit(void); +int pciscan(int, Pcidev **); +ulong pcibarsize(Pcidev *, int); +int pcicfgr8(Pcidev*, int); +int pcicfgr16(Pcidev*, int); +int pcicfgr32(Pcidev*, int); +void pcicfgw8(Pcidev*, int, int); +void pcicfgw16(Pcidev*, int, int); +void pcicfgw32(Pcidev*, int, int); +void pciclrbme(Pcidev*); +void pcihinv(Pcidev*); +uchar pciipin(Pcidev *, uchar); +Pcidev* pcimatch(Pcidev*, int, int); +Pcidev* pcimatchtbdf(int); +void pcireset(void); +void pcisetbme(Pcidev*); +void powerenable(void (*)(int)); +void powerdisable(void (*)(int)); +void powerdown(void); +void powerinit(void); +void powersuspend(void); +#define procsave(p) +#define procrestore(p) +void putcclkcfg(ulong); +long rtctime(void); +void screeninit(void); +void (*screenputs)(char*, int); +int segflush(void*, ulong); +void setpanic(void); +void setr13(int, void*); +int splfhi(void); +int splflo(void); +void _suspendcode(void); +void tlbinvalidateall(void); +void tlbinvalidateaddr(void*); +void trapinit(void); +void trapstacks(void); +void trapspecial(int (*)(Ureg *, uint)); +void uartconsole(void); +void uartinstall(void); +int uartprint(char*, ...); +ulong va2pa(void*); +void vectors(void); +void vtable(void); +#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) +int wasbusy(int); + +#define KADDR(p) mmukaddr((ulong)(p)) +#define PADDR(v) va2pa((void*)(v)) + +ulong timer_start(void); +ulong timer_ticks(ulong); +int timer_devwait(ulong *adr, ulong mask, ulong val, int ost); +void timer_setwatchdog(int ost); +void timer_delay(int ost); +ulong ms2tmr(int ms); +int tmr2ms(ulong t); +void delay(int ms); +ulong us2tmr(int us); +int tmr2us(ulong t); +void microdelay(int us); + +#define archuartclock(p,rate) 14745600 + +/* debugging */ +extern void serialputs(char*, int); +extern void serialputc(int); +extern void xdelay(int); diff --git a/os/manga/fpi.h b/os/manga/fpi.h new file mode 100644 index 00000000..dfb9b1df --- /dev/null +++ b/os/manga/fpi.h @@ -0,0 +1,61 @@ +typedef long Word; +typedef unsigned long Single; +typedef struct { + unsigned long h; + unsigned long l; +} Double; + +enum { + FractBits = 28, + CarryBit = 0x10000000, + HiddenBit = 0x08000000, + MsBit = HiddenBit, + NGuardBits = 3, + GuardMask = 0x07, + LsBit = (1<e >= ExpInfinity) +#define IsInfinity(n) (IsWeird(n) && (n)->h == HiddenBit && (n)->l == 0) +#define SetInfinity(n) ((n)->e = ExpInfinity, (n)->h = HiddenBit, (n)->l = 0) +#define IsNaN(n) (IsWeird(n) && (((n)->h & ~HiddenBit) || (n)->l)) +#define SetQNaN(n) ((n)->s = 0, (n)->e = ExpInfinity, \ + (n)->h = HiddenBit|(LsBit<<1), (n)->l = 0) +#define IsZero(n) ((n)->e == 1 && (n)->h == 0 && (n)->l == 0) +#define SetZero(n) ((n)->e = 1, (n)->h = 0, (n)->l = 0) + +/* + * fpi.c + */ +extern void fpiround(Internal *); +extern void fpiadd(Internal *, Internal *, Internal *); +extern void fpisub(Internal *, Internal *, Internal *); +extern void fpimul(Internal *, Internal *, Internal *); +extern void fpidiv(Internal *, Internal *, Internal *); +extern int fpicmp(Internal *, Internal *); +extern void fpinormalise(Internal*); + +/* + * fpimem.c + */ +extern void fpis2i(Internal *, void *); +extern void fpid2i(Internal *, void *); +extern void fpiw2i(Internal *, void *); +extern void fpii2s(void *, Internal *); +extern void fpii2d(void *, Internal *); +extern void fpii2w(Word *, Internal *); diff --git a/os/manga/fpiarm.c b/os/manga/fpiarm.c new file mode 100644 index 00000000..4acfcd1d --- /dev/null +++ b/os/manga/fpiarm.c @@ -0,0 +1,483 @@ +/* + * this doesn't attempt to implement ARM floating-point properties + * that aren't visible in the Inferno environment. + * all arithmetic is done in double precision. + * the FP trap status isn't updated. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "ureg.h" + +#include "fpi.h" + +// #define R13OK undef this if correct kernel r13 isn't in Ureg; check calculation in fpiarm below + +#define REG(x) (*(long*)(((char*)(ur))+roff[(x)])) +#define FPENV (*(ufp)) +#define FR(x) (*(Internal*)(ufp)->regs[(x)&7]) + +/* BUG: check fetch (not worthwhile in Inferno) */ +#define getubyte(a) (*(uchar*)(a)) +#define getuword(a) (*(ushort*)(a)) +#define getulong(a) (*(ulong*)(a)) + +typedef struct FP2 FP2; +typedef struct FP1 FP1; + +struct FP2 { + char* name; + void (*f)(Internal, Internal, Internal*); +}; + +struct FP1 { + char* name; + void (*f)(Internal*, Internal*); +}; + +enum { + N = 1<<31, + Z = 1<<30, + C = 1<<29, + V = 1<<28, + REGPC = 15, +}; + +int fpemudebug = 0; + +#undef OFR +#define OFR(X) ((ulong)&((Ureg*)0)->X) + +static int roff[] = { + OFR(r0), OFR(r1), OFR(r2), OFR(r3), + OFR(r4), OFR(r5), OFR(r6), OFR(r7), + OFR(r8), OFR(r9), OFR(r10), OFR(r11), +#ifdef R13OK + OFR(r12), OFR(r13), OFR(r14), OFR(pc), +#else + OFR(r12), OFR(type), OFR(r14), OFR(pc), +#endif +}; + +static Internal fpconst[8] = { /* indexed by op&7 */ + /* s, e, l, h */ + {0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */ + {0, 0x3FF, 0x00000000, 0x08000000}, /* 1.0 */ + {0, 0x400, 0x00000000, 0x08000000}, /* 2.0 */ + {0, 0x400, 0x00000000, 0x0C000000}, /* 3.0 */ + {0, 0x401, 0x00000000, 0x08000000}, /* 4.0 */ + {0, 0x401, 0x00000000, 0x0A000000}, /* 5.0 */ + {0, 0x3FE, 0x00000000, 0x08000000}, /* 0.5 */ + {0, 0x402, 0x00000000, 0x0A000000}, /* 10.0 */ +}; + +/* + * arm binary operations + */ + +static void +fadd(Internal m, Internal n, Internal *d) +{ + (m.s == n.s? fpiadd: fpisub)(&m, &n, d); +} + +static void +fsub(Internal m, Internal n, Internal *d) +{ + m.s ^= 1; + (m.s == n.s? fpiadd: fpisub)(&m, &n, d); +} + +static void +fsubr(Internal m, Internal n, Internal *d) +{ + n.s ^= 1; + (n.s == m.s? fpiadd: fpisub)(&n, &m, d); +} + +static void +fmul(Internal m, Internal n, Internal *d) +{ + fpimul(&m, &n, d); +} + +static void +fdiv(Internal m, Internal n, Internal *d) +{ + fpidiv(&m, &n, d); +} + +static void +fdivr(Internal m, Internal n, Internal *d) +{ + fpidiv(&n, &m, d); +} + +/* + * arm unary operations + */ + +static void +fmov(Internal *m, Internal *d) +{ + *d = *m; +} + +static void +fmovn(Internal *m, Internal *d) +{ + *d = *m; + d->s ^= 1; +} + +static void +fabsf(Internal *m, Internal *d) +{ + *d = *m; + d->s = 0; +} + +static void +frnd(Internal *m, Internal *d) +{ + short e; + + (m->s? fsub: fadd)(fpconst[6], *m, d); + if(IsWeird(d)) + return; + fpiround(d); + e = (d->e - ExpBias) + 1; + if(e <= 0) + SetZero(d); + else if(e > FractBits){ + if(e < 2*FractBits) + d->l &= ~((1<<(2*FractBits - e))-1); + }else{ + d->l = 0; + if(e < FractBits) + d->h &= ~((1<<(FractBits-e))-1); + } +} + +static FP1 optab1[16] = { /* Fd := OP Fm */ +[0] {"MOVF", fmov}, +[1] {"NEGF", fmovn}, +[2] {"ABSF", fabsf}, +[3] {"RNDF", frnd}, +[4] {"SQTF", /*fsqt*/0}, +/* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */ +/* URD and NRM aren't implemented */ +}; + +static FP2 optab2[16] = { /* Fd := Fn OP Fm */ +[0] {"ADDF", fadd}, +[1] {"MULF", fmul}, +[2] {"SUBF", fsub}, +[3] {"RSUBF", fsubr}, +[4] {"DIVF", fdiv}, +[5] {"RDIVF", fdivr}, +/* POW, RPW deprecated */ +[8] {"REMF", /*frem*/0}, +[9] {"FMF", fmul}, /* fast multiply */ +[10] {"FDV", fdiv}, /* fast divide */ +[11] {"FRD", fdivr}, /* fast reverse divide */ +/* POL deprecated */ +}; + +static ulong +fcmp(Internal *n, Internal *m) +{ + int i; + + if(IsWeird(m) || IsWeird(n)){ + /* BUG: should trap if not masked */ + return V|C; + } + i = fpicmp(n, m); + if(i > 0) + return C; + else if(i == 0) + return C|Z; + else + return N; +} + +static void +fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPenv *ufp) +{ + void *mem; + + mem = (void*)ea; + (*f)(&FR(d), mem); + if(fpemudebug) + print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d); +} + +static void +fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPenv *ufp) +{ + Internal tmp; + void *mem; + + mem = (void*)ea; + tmp = FR(s); + if(fpemudebug) + print("MOV%c F%d,#%lux\n", n==8? 'D': 'F', s, ea); + (*f)(mem, &tmp); +} + +static int +condok(int cc, int c) +{ + switch(c){ + case 0: /* Z set */ + return cc&Z; + case 1: /* Z clear */ + return (cc&Z) == 0; + case 2: /* C set */ + return cc&C; + case 3: /* C clear */ + return (cc&C) == 0; + case 4: /* N set */ + return cc&N; + case 5: /* N clear */ + return (cc&N) == 0; + case 6: /* V set */ + return cc&V; + case 7: /* V clear */ + return (cc&V) == 0; + case 8: /* C set and Z clear */ + return cc&C && (cc&Z) == 0; + case 9: /* C clear or Z set */ + return (cc&C) == 0 || cc&Z; + case 10: /* N set and V set, or N clear and V clear */ + return (~cc&(N|V))==0 || (cc&(N|V)) == 0; + case 11: /* N set and V clear, or N clear and V set */ + return (cc&(N|V))==N || (cc&(N|V))==V; + case 12: /* Z clear, and either N set and V set or N clear and V clear */ + return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0); + case 13: /* Z set, or N set and V clear or N clear and V set */ + return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V; + case 14: /* always */ + return 1; + case 15: /* never (reserved) */ + return 0; + } + return 0; /* not reached */ +} + +static void +unimp(ulong pc, ulong op) +{ + char buf[60]; + + snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op); + if(fpemudebug) + print("FPE: %s\n", buf); + error(buf); + /* no return */ +} + +static void +fpemu(ulong pc, ulong op, Ureg *ur, FPenv *ufp) +{ + int rn, rd, tag, o; + long off; + ulong ea; + Internal tmp, *fm, *fn; + + /* note: would update fault status here if we noted numeric exceptions */ + + /* + * LDF, STF; 10.1.1 + */ + if(((op>>25)&7) == 6){ + if(op & (1<<22)) + unimp(pc, op); /* packed or extended */ + rn = (op>>16)&0xF; + off = (op&0xFF)<<2; + if((op & (1<<23)) == 0) + off = -off; + ea = REG(rn); + if(rn == REGPC) + ea += 8; + if(op & (1<<24)) + ea += off; + rd = (op>>12)&7; + if(op & (1<<20)){ + if(op & (1<<15)) + fld(fpid2i, rd, ea, 8, ufp); + else + fld(fpis2i, rd, ea, 4, ufp); + }else{ + if(op & (1<<15)) + fst(fpii2d, ea, rd, 8, ufp); + else + fst(fpii2s, ea, rd, 4, ufp); + } + if((op & (1<<24)) == 0) + ea += off; + if(op & (1<<21)) + REG(rn) = ea; + return; + } + + /* + * CPRT/transfer, 10.3 + */ + if(op & (1<<4)){ + rd = (op>>12) & 0xF; + + /* + * compare, 10.3.1 + */ + if(rd == 15 && op & (1<<20)){ + rn = (op>>16)&7; + fn = &FR(rn); + if(op & (1<<3)){ + fm = &fpconst[op&7]; + tag = 'C'; + }else{ + fm = &FR(op&7); + tag = 'F'; + } + switch((op>>21)&7){ + default: + unimp(pc, op); + case 4: /* CMF: Fn :: Fm */ + case 6: /* CMFE: Fn :: Fm (with exception) */ + ur->psr &= ~(N|C|Z|V); + ur->psr |= fcmp(fn, fm); + break; + case 5: /* CNF: Fn :: -Fm */ + case 7: /* CNFE: Fn :: -Fm (with exception) */ + tmp = *fm; + tmp.s ^= 1; + ur->psr &= ~(N|C|Z|V); + ur->psr |= fcmp(fn, &tmp); + break; + } + if(fpemudebug) + print("CMPF %c%d,F%ld =%x\n", tag, rn, op&7, ur->psr>>28); + return; + } + + /* + * other transfer, 10.3 + */ + switch((op>>20)&0xF){ + default: + unimp(pc, op); + case 0: /* FLT */ + rn = (op>>16) & 7; + fpiw2i(&FR(rn), ®(rd)); + if(fpemudebug) + print("MOVW[FD] R%d, F%d\n", rd, rn); + break; + case 1: /* FIX */ + if(op & (1<<3)) + unimp(pc, op); + rn = op & 7; + tmp = FR(rn); + fpii2w(®(rd), &tmp); + if(fpemudebug) + print("MOV[FD]W F%d, R%d =%ld\n", rn, rd, REG(rd)); + break; + case 2: /* FPSR := Rd */ + FPENV.status = REG(rd); + if(fpemudebug) + print("MOVW R%d, FPSR\n", rd); + break; + case 3: /* Rd := FPSR */ + REG(rd) = FPENV.status; + if(fpemudebug) + print("MOVW FPSR, R%d\n", rd); + break; + case 4: /* FPCR := Rd */ + FPENV.control = REG(rd); + if(fpemudebug) + print("MOVW R%d, FPCR\n", rd); + break; + case 5: /* Rd := FPCR */ + REG(rd) = FPENV.control; + if(fpemudebug) + print("MOVW FPCR, R%d\n", rd); + break; + } + return; + } + + /* + * arithmetic + */ + + if(op & (1<<3)){ /* constant */ + fm = &fpconst[op&7]; + tag = 'C'; + }else{ + fm = &FR(op&7); + tag = 'F'; + } + rd = (op>>12)&7; + o = (op>>20)&0xF; + if(op & (1<<15)){ /* monadic */ + FP1 *fp; + fp = &optab1[o]; + if(fp->f == nil) + unimp(pc, op); + if(fpemudebug) + print("%s %c%ld,F%d\n", fp->name, tag, op&7, rd); + (*fp->f)(fm, &FR(rd)); + } else { + FP2 *fp; + fp = &optab2[o]; + if(fp->f == nil) + unimp(pc, op); + rn = (op>>16)&7; + if(fpemudebug) + print("%s %c%ld,F%d,F%d\n", fp->name, tag, op&7, rn, rd); + (*fp->f)(*fm, FR(rn), &FR(rd)); + } +} + +/* + * returns the number of FP instructions emulated + */ +int +fpiarm(Ureg *ur) +{ + ulong op, o; + FPenv *ufp; + int n; + +#ifndef R13OK +/* ur->type = &ur->pc+1; /* calculate kernel sp/R13 and put it here for roff[13] */ + ur->type = (ulong)(ur + 1); +#endif + if (up == nil) + panic("fpiarm not in a process"); + ufp = &up->env->fpu; /* because all the state is in Osenv, it need not be saved/restored */ + if(ufp->fpistate != FPACTIVE) { + ufp->fpistate = FPACTIVE; + ufp->control = 0; + ufp->status = (0x01<<28)|(1<<12); /* software emulation, alternative C flag */ + for(n = 0; n < 8; n++) + FR(n) = fpconst[0]; + } + for(n=0;;n++){ + op = getulong(ur->pc); + o = (op>>24) & 0xF; + if(((op>>8) & 0xF) != 1 || o != 0xE && (o&~1) != 0xC) + break; + if(condok(ur->psr, op>>28)) + fpemu(ur->pc, op, ur, ufp); + ur->pc += 4; + if(anyhigher()) + sched(); + } + return n; +} diff --git a/os/manga/gpio.c b/os/manga/gpio.c new file mode 100644 index 00000000..c6d0179d --- /dev/null +++ b/os/manga/gpio.c @@ -0,0 +1,75 @@ +#include "u.h" +#include "mem.h" +#include "../port/lib.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +static ulong gpioreserved; +static Lock gpiolock; + +void +gpioreserve(int n) +{ + ulong mask; + + mask = 1<iopm |= 1<iopm &= ~(1<iopd & (1<iopd |= mask; + else + g->iopd &= ~mask; + iunlock(&gpiolock); +} + +void +gpiorelease(int n) +{ + ulong mask; + + mask = 1< 0; ns--) + *addr++ = *(ushort*)KIOP(p); +} + +void +outss(ulong p, void* buf, int ns) +{ + ushort *addr; + + addr = (ushort*)buf; + for(;ns > 0; ns--) + *(ushort*)KIOP(p) = *addr++; +} + +void +insb(ulong p, void* buf, int ns) +{ + uchar *addr; + + addr = (uchar*)buf; + for(;ns > 0; ns--) + *addr++ = *(uchar*)KIOP(p); +} + +void +outsb(ulong p, void* buf, int ns) +{ + uchar *addr; + + addr = (uchar*)buf; + for(;ns > 0; ns--) + *(uchar*)KIOP(p) = *addr++; +} diff --git a/os/manga/io.h b/os/manga/io.h new file mode 100644 index 00000000..6e53acc1 --- /dev/null +++ b/os/manga/io.h @@ -0,0 +1,320 @@ +typedef struct BD BD; +typedef struct Ring Ring; + +/* + * types of interrupts + */ +enum +{ + /* some flags to change polarity and sensitivity */ + IRQmask= 0xFF, /* actual vector address */ + IRQactivelow= 0<<8, + IRQactivehigh= 1<<8, + IRQrising= 2<<8, + IRQfalling= 4<<8, + IRQmode= IRQactivelow | IRQactivehigh | IRQrising | IRQfalling, + IRQsoft= 1<<11, /* configure ext0 to ext3 as GPIO output */ + IRQ= 0, /* notional bus */ +}; + +enum { + IRQwmlc= 31, /* WAN link changed (edge) */ + IRQwmts= 30, /* WAN MAC transmit status (edge) */ + IRQwmrs= 29, /* WAN MAC receive status (edge) */ + IRQwmtbu= 28, /* WAN MAC transmit buffer unavailable (edge) */ + IRQwmrbu= 27, /* WAN MAC receive buffer unavailable (edge) */ + IRQwmtps= 26, /* WAN MAC transmit process stopped (edge) */ + IRQwmrps= 25, /* WAN MAC receive process stopped (edge) */ + IRQaber= 24, /* AMBA bus error (level) */ + IRQlmts= 17, /* LAN MAC transmit status (edge) */ + IRQlmrs= 16, /* LAN MAC receive status (edge) */ + IRQlmtbu= 15, /* LAN AMC transmit buffer unavailable (edge) */ + IRQlmrbu= 14, /* LAN MAC receive buffer unavailable (edge) */ + IRQlmtps= 13, /* LAN MAC transmit process stopped (edge) */ + IRQlmrps= 12, /* LAN MAC receive process stopped (edge) */ + IRQums= 11, /* UART modem status (level) */ + IRQule= 10, /* UART line status (level) */ + IRQurs= 9, /* UART receive status (level) */ + IRQuts= 8, /* UART transmit status (level) */ + IRQtm1= 7, /* timer 1 (edge) */ + IRQtm0= 6, /* timer 0 (edge) */ + IRQext3= 5, /* external interrupts (gpio control selects edge or level) */ + IRQext2= 4, + IRQext1= 3, + IRQext0= 2, + IRQccts= 1, /* comms channel transmit status (level) */ + IRQccrs= 0, /* comms channel receive status (level) */ +}; + +/* + * these are defined to keep the interface compatible with other + * architectures, but only BUSUNKNOWN is currently used + */ +#define MKBUS(t,b,d,f) (((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8)) +#define BUSFNO(tbdf) (((tbdf)>>8)&0x07) +#define BUSDNO(tbdf) (((tbdf)>>11)&0x1F) +#define BUSBNO(tbdf) (((tbdf)>>16)&0xFF) +#define BUSTYPE(tbdf) ((tbdf)>>24) +#define BUSBDF(tbdf) ((tbdf)&0x00FFFF00) +#define BUSUNKNOWN (-1) + +enum { + BusIRQ = IRQ, + BusPCI, + MaxBus +}; + +#define INTRREG ((IntrReg*)PHYSINTR) +typedef struct IntrReg IntrReg; +struct IntrReg { + ulong mc; /* mode control */ + ulong en; /* enable */ + ulong st; /* status */ + ulong pw; /* priority for WAN */ + ulong pad0; + ulong pl; /* priority for LAN */ + ulong pt; /* priority for timer */ + ulong pu; /* priority for UART */ + ulong pe; /* priority for external */ + ulong pc; /* priority for comms channel */ + ulong pbe; /* priority for bus error response */ + ulong ms; /* mask status */ + ulong hpf; /* highest priority for FIQ */ + ulong hpi; /* highest priority for IRQ */ +}; + +#define TIMERREG ((TimerReg*)PHYSTIMER) +typedef struct TimerReg TimerReg; +struct TimerReg { + ulong enable; /* 1<nrdre = nrdre; + if(r->rdr == nil) + r->rdr = bdalloc(nrdre); + if(r->rxb == nil) + r->rxb = malloc(nrdre*sizeof(Block*)); + if(r->rdr == nil || r->rxb == nil) + return -1; + for(i = 0; i < nrdre; i++){ + r->rxb[i] = nil; + r->rdr[i].ctrl = 0; + r->rdr[i].size = 0; + r->rdr[i].addr = 0; + if(i) + r->rdr[i-1].next = PADDR(&r->rdr[i]); + } + r->rdr[i-1].next = PADDR(&r->rdr[0]); + r->rdrx = 0; + + r->ntdre = ntdre; + if(r->tdr == nil) + r->tdr = bdalloc(ntdre); + if(r->txb == nil) + r->txb = malloc(ntdre*sizeof(Block*)); + if(r->tdr == nil || r->txb == nil) + return -1; + for(i = 0; i < ntdre; i++){ + r->txb[i] = nil; + r->tdr[i].ctrl = 0; + r->tdr[i].size = 0; + r->tdr[i].addr = 0; + if(i) + r->tdr[i-1].next = PADDR(&r->tdr[i]); + } + r->tdr[i-1].next = PADDR(&r->tdr[0]); + r->tdrh = 0; + r->tdri = 0; + r->ntq = 0; + return 0; +} diff --git a/os/manga/l.s b/os/manga/l.s new file mode 100644 index 00000000..c9fa24ec --- /dev/null +++ b/os/manga/l.s @@ -0,0 +1,404 @@ +#include "mem.h" + +#define CPWAIT + +/* + * Entered here from the boot loader with + * supervisor mode, interrupts disabled; + * MMU and caches disabled + */ + +#define LED \ + MOVW $(PHYSGPIO+8), R6;\ + MOVW (R6), R7;\ + EOR $(1<<12), R7;\ + MOVW R7, (R6) + +TEXT _startup(SB), $-4 + MOVW $setR12(SB), R12 /* static base (SB) */ + MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R1 /* ensure SVC mode with interrupts disabled */ + MOVW R1, CPSR + + /* build a temporary translation table at 4MB */ + MOVW $0x400000, R0 + MCR CpMMU, 0, R0, C(CpTTB), C(0), 0 /* set TTB */ + MOVW $4096, R1 + MOVW $0, R3 + ORR $(1<<4), R3 /* must be one */ + ORR $(3<<10), R3 /* supervisor rw */ + ORR $(2<<0), R3 /* section */ +startup0: + BIC $0xFC000000, R3 /* wraps round, at least for 0xC00... */ + MOVW R3, (R0) + ADD $4, R0 + ADD $(1<<20), R3 + SUB $1, R1 + CMP $0, R1 + BNE startup0 + MRC CpMMU, 0, R0, C(CpControl), C(0), 0 + ORR $CpCmmu, R0 + + MOVW $3, R1 + MCR CpMMU, 0, R1, C(CpDAC), C(0) /* set domain 0 to manager */ + BL mmuenable(SB) + + MOVW $(MACHADDR+BY2PG-4), R13 /* stack; 4 bytes for link */ + BL _relocate(SB) + BL main(SB) +dead: + B dead + BL _div(SB) /* hack to get _div etc loaded */ + +TEXT _relocate(SB), $-4 + ORR $KZERO, R14 + RET + +TEXT getcpuid(SB), $-4 + MRC CpMMU, 0, R0, C(CpCPUID), C(0) + RET + +TEXT getcacheid(SB), $-4 + MRC CpMMU, 0, R0, C(CpCacheID), C(1) + RET + +TEXT mmugetctl(SB), $-4 + MRC CpMMU, 0, R0, C(CpControl), C(0) + RET + +TEXT mmugetdac(SB), $-4 + MRC CpMMU, 0, R0, C(CpDAC), C(0) + RET + +TEXT mmugetfar(SB), $-4 + MRC CpMMU, 0, R0, C(CpFAR), C(0) + RET + +TEXT mmugetfsr(SB), $-4 + MRC CpMMU, 0, R0, C(CpFSR), C(0) + RET + +TEXT mmuputdac(SB), $-4 + MCR CpMMU, 0, R0, C(CpDAC), C(0) + CPWAIT + RET + +TEXT mmuputfsr(SB), $-4 + MCR CpMMU, 0, R0, C(CpFSR), C(0) + CPWAIT + RET + +TEXT mmuputttb(SB), $-4 + MCR CpMMU, 0, R0, C(CpTTB), C(0) + CPWAIT + RET + +TEXT mmuputctl(SB), $-4 + MCR CpMMU, 0, R0, C(CpControl), C(0) + + /* drain prefetch */ + MOVW R0,R0 + MOVW R0,R0 + RET + +TEXT tlbinvalidateall(SB), $-4 + MCR CpMMU, 0, R0, C(CpTLBops), C(7) + CPWAIT + RET + +TEXT itlbinvalidate(SB), $-4 + MCR CpMMU, 0, R0, C(CpTLBops), C(5), 1 + CPWAIT + RET + +TEXT dtlbinvalidate(SB), $-4 + MCR CpMMU, 0, R0, C(CpTLBops), C(6), 1 + CPWAIT + RET + +TEXT mmuenable(SB), $-4 + + /* disable and invalidate all caches and TLB's before enabling MMU */ + MCR CpMMU, 0, R1, C(CpControl), C(0) + BIC $(CpCDcache | CpCIcache), R1 + MRC CpMMU, 0, R1, C(CpControl), C(0) + CPWAIT + + MOVW $0, R1 /* disable everything */ + MCR CpMMU, 0, R1, C(CpCacheCtl), C(7), 0 /* invalidate I&D Caches and BTB */ + MCR CpMMU, 0, R1, C(CpCacheCtl), C(10), 4 /* drain write buffer */ + MCR CpMMU, 0, R1, C(CpTLBops), C(7), 0 /* invalidate I&D TLB */ + + /* enable desired mmu mode (R0) */ + MCR CpMMU, 0, R0, C(CpControl), C(0) + + /* drain prefetch */ + MOVW R0,R0 + MOVW R0,R0 + RET /* start running in remapped area */ + +TEXT setr13(SB), $-4 + MOVW 4(FP), R1 + + MOVW CPSR, R2 + BIC $PsrMask, R2, R3 + ORR R0, R3 + MOVW R3, CPSR + + MOVW R13, R0 + MOVW R1, R13 + + MOVW R2, CPSR + RET + +TEXT vectors(SB), $-4 + MOVW 0x18(R15), R15 /* reset */ + MOVW 0x18(R15), R15 /* undefined */ + MOVW 0x18(R15), R15 /* SWI */ + MOVW 0x18(R15), R15 /* prefetch abort */ + MOVW 0x18(R15), R15 /* data abort */ + MOVW 0x18(R15), R15 /* reserved */ + MOVW 0x18(R15), R15 /* IRQ */ + MOVW 0x18(R15), R15 /* FIQ */ + +TEXT vtable(SB), $-4 + WORD $_vsvc(SB) /* reset, in svc mode already */ + WORD $_vund(SB) /* undefined, switch to svc mode */ + WORD $_vsvc(SB) /* swi, in svc mode already */ + WORD $_vpab(SB) /* prefetch abort, switch to svc mode */ + WORD $_vdab(SB) /* data abort, switch to svc mode */ + WORD $_vsvc(SB) /* reserved */ + WORD $_virq(SB) /* IRQ, switch to svc mode */ + WORD $_vfiq(SB) /* FIQ, switch to svc mode */ + +TEXT _vund(SB), $-4 + MOVM.DB [R0-R3], (R13) + MOVW $PsrMund, R0 + B _vswitch + +TEXT _vsvc(SB), $-4 + MOVW.W R14, -4(R13) + MOVW CPSR, R14 + MOVW.W R14, -4(R13) + BIC $PsrMask, R14 + ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14 + MOVW R14, CPSR + MOVW $PsrMsvc, R14 + MOVW.W R14, -4(R13) + B _vsaveu + +TEXT _vpab(SB), $-4 + MOVM.DB [R0-R3], (R13) + MOVW $PsrMabt, R0 + B _vswitch + +TEXT _vdab(SB), $-4 + MOVM.DB [R0-R3], (R13) + MOVW $(PsrMabt+1), R0 + B _vswitch + +TEXT _vfiq(SB), $-4 /* FIQ */ + MOVM.DB [R0-R3], (R13) + MOVW $PsrMfiq, R0 + B _vswitch + +TEXT _virq(SB), $-4 /* IRQ */ + MOVM.DB [R0-R3], (R13) + MOVW $PsrMirq, R0 + +_vswitch: /* switch to svc mode */ + MOVW SPSR, R1 + MOVW R14, R2 + MOVW R13, R3 + + MOVW CPSR, R14 + BIC $PsrMask, R14 + ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14 + MOVW R14, CPSR + + MOVM.DB.W [R0-R2], (R13) + MOVM.DB (R3), [R0-R3] + +_vsaveu: /* Save Registers */ + MOVW.W R14, -4(R13) /* save link */ + MCR CpMMU, 0, R0, C(0), C(0), 0 + + SUB $8, R13 + MOVM.DB.W [R0-R12], (R13) + + MOVW R0, R0 /* gratuitous noop */ + + MOVW $setR12(SB), R12 /* static base (SB) */ + MOVW R13, R0 /* argument is ureg */ + SUB $8, R13 /* space for arg+lnk*/ + BL trap(SB) + +_vrfe: /* Restore Regs */ + MOVW CPSR, R0 /* splhi on return */ + ORR $(PsrDirq|PsrDfiq), R0, R1 + MOVW R1, CPSR + ADD $(8+4*15), R13 /* [r0-R14]+argument+link */ + MOVW (R13), R14 /* restore link */ + MOVW 8(R13), R0 + MOVW R0, SPSR + MOVM.DB.S (R13), [R0-R14] /* restore user registers */ + MOVW R0, R0 /* gratuitous nop */ + ADD $12, R13 /* skip saved link+type+SPSR*/ + RFE /* MOVM.IA.S.W (R13), [R15] */ + +TEXT splhi(SB), $-4 + MOVW CPSR, R0 + ORR $(PsrDirq), R0, R1 + MOVW R1, CPSR + MOVW $(MACHADDR), R6 + MOVW R14, (R6) /* m->splpc */ + RET + +TEXT spllo(SB), $-4 + MOVW CPSR, R0 + BIC $(PsrDirq|PsrDfiq), R0, R1 + MOVW R1, CPSR + RET + +TEXT splx(SB), $-4 + MOVW $(MACHADDR), R6 + MOVW R14, (R6) /* m->splpc */ + +TEXT splxpc(SB), $-4 + MOVW R0, R1 + MOVW CPSR, R0 + MOVW R1, CPSR + RET + +TEXT spldone(SB), $-4 + RET + +TEXT islo(SB), $-4 + MOVW CPSR, R0 + AND $(PsrDirq), R0 + EOR $(PsrDirq), R0 + RET + +TEXT splfhi(SB), $-4 + MOVW CPSR, R0 + ORR $(PsrDfiq|PsrDirq), R0, R1 + MOVW R1, CPSR + RET + +TEXT splflo(SB), $-4 + MOVW CPSR, R0 + BIC $(PsrDfiq), R0, R1 + MOVW R1, CPSR + RET + +TEXT getcpsr(SB), $-4 + MOVW CPSR, R0 + RET + +TEXT getspsr(SB), $-4 + MOVW SPSR, R0 + RET + +TEXT getcallerpc(SB), $-4 + MOVW 0(R13), R0 + RET + +TEXT _tas(SB), $-4 + MOVW R0, R1 + MOVW $0xDEADDEAD, R2 + SWPW R2, (R1), R0 + RET + +TEXT setlabel(SB), $-4 + MOVW R13, 0(R0) /* sp */ + MOVW R14, 4(R0) /* pc */ + MOVW $0, R0 + RET + +TEXT gotolabel(SB), $-4 + MOVW 0(R0), R13 /* sp */ + MOVW 4(R0), R14 /* pc */ + MOVW $1, R0 + RET + +/* + * flush (invalidate) the whole icache + */ +TEXT icflushall(SB), $-4 +_icflushall: + MCR CpMMU, 0, R0, C(CpCacheCtl), C(5), 0 /* invalidate i-cache */ + CPWAIT + RET + +/* + * invalidate part of i-cache + */ +TEXT icflush(SB), $-4 + MOVW 4(FP), R1 + CMP $(CACHESIZE/2), R1 + BGE _icflushall /* might as well do the lot */ + ADD R0, R1 + BIC $(CACHELINESZ-1), R0 +icflush1: + MCR CpMMU, 0, R0, C(CpCacheCtl), C(5), 1 /* invalidate entry by address */ + ADD $CACHELINESZ, R0 + CMP R1, R0 + BLO icflush1 + RET + +/* + * write back whole data cache, invalidate, and drain write buffer + */ +TEXT dcflushall(SB), $-4 +_dcflushall: + MOVW $(63<<26), R1 /* index, segment 0 */ +dcflushall0: + MCR CpMMU, 0, R1, C(CpCacheCtl), C(14), 2 /* clean and invalidate, using index */ + ADD $(1<<5), R1 /* segment 1 */ + MCR CpMMU, 0, R1, C(CpCacheCtl), C(14), 2 + ADD $(1<<5), R1 /* segment 2 */ + MCR CpMMU, 0, R1, C(CpCacheCtl), C(14), 2 + ADD $(1<<5), R1 /* segment 3 */ + MCR CpMMU, 0, R1, C(CpCacheCtl), C(14), 2 + EOR $(3<<5), R1 /* back to 0 */ + SUB.S $(1<<26), R1 + BCS dcflushall0 + MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4 /* drain write buffer */ + CPWAIT + RET + +/* + * write back a given region, inavlidate it, and drain write buffer + */ +TEXT dcflush(SB), $-4 + MOVW 4(FP), R1 + CMP $(CACHESIZE/2), R1 + BGE _dcflushall + ADD R0, R1 + BIC $(CACHELINESZ-1), R0 +dcflush1: + MCR CpMMU, 0, R0, C(CpCacheCtl), C(14), 1 /* clean and invalidate entry by address */ + ADD $CACHELINESZ, R0 + CMP R1, R0 + BLO dcflush1 + MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4 /* drain write buffer */ + CPWAIT + RET + +/* + * invalidate data cache + */ +TEXT dcinval(SB), $-4 + MCR CpMMU, 0, R0, C(CpCacheCtl), C(6), 0 + CPWAIT + RET + +/* for devboot */ +TEXT gotopc(SB), $-4 + MOVW R0, R1 + MOVW $0, R0 + MOVW R1, PC + RET + +TEXT idle(SB), $-4 + MCR CpMMU, 0, R0, C(7), C(0), 4 + RET diff --git a/os/manga/main.c b/os/manga/main.c new file mode 100644 index 00000000..3f1adae0 --- /dev/null +++ b/os/manga/main.c @@ -0,0 +1,317 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" +#include "version.h" + +#define MAXCONF 32 + +Mach *m = (Mach*)MACHADDR; +Proc *up = 0; +Vectorpage *page0 = (Vectorpage*)KZERO; /* doubly-mapped to AIVECADDR */ +Conf conf; + +extern ulong kerndate; +extern int cflag; +extern int main_pool_pcnt; +extern int heap_pool_pcnt; +extern int image_pool_pcnt; +ulong cpuidlecount; + +char *confname[MAXCONF]; +char *confval[MAXCONF]; +int nconf; + +void addconf(char *, char *); +void eepromscan(void); +char* getconf(char*); + +void +doc(char *m) +{ + USED(m); + print("%s...\n", m); +} + +void +idoc(char *m) +{ + serialputs(m, strlen(m)); //xdelay(1); +} + +static void +poolsizeinit(void) +{ + ulong nb; + + nb = conf.npage*BY2PG; + poolsize(mainmem, (nb*main_pool_pcnt)/100, 0); + poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0); + poolsize(imagmem, (nb*image_pool_pcnt)/100, 1); +} + +static char *hello = "Inferno\n"; + +void +main(void) +{ + memset(edata, 0, end-edata); /* clear the BSS */ + memset(m, 0, sizeof(Mach)); /* clear the mach struct */ + conf.nmach = 1; + archreset(); + idoc(hello); + /* TO DO: clock speed */ + quotefmtinstall(); + idoc("confinit...\n"); + confinit(); + idoc("xinit...\n"); + xinit(); + idoc("mmuinit...\n"); + mmuinit(); + poolsizeinit(); + poolinit(); + idoc("trapinit...\n"); + trapinit(); +// dmareset(); + idoc("printinit...\n"); + printinit(); + idoc("uartconsole...\n"); + uartconsole(); + eepromscan(); + pcimapinit(); + doc("clockinit"); + clockinit(); + doc("procinit"); + procinit(); +// cpuidprint(); + doc("links"); + links(); + doc("chandevreset"); + chandevreset(); +iprint("delayloop = %lud\n", m->delayloop); + + eve = strdup("inferno"); + + kbdinit(); + + print("%ld MHz id %8.8lux\n", (m->cpuhz+500000)/1000000, getcpuid()); + print("\nInferno %s\n", VERSION); + print("Vita Nuova\n"); + print("conf %s (%lud) jit %d\n\n",conffile, kerndate, cflag); + + userinit(); + schedinit(); +} + +void +reboot(void) +{ + exit(0); +} + +void +halt(void) +{ + spllo(); + print("cpu halted\n"); + for(;;){ + /* nothing to do */ + } +} + +Conf conf; + +void +addconf(char *name, char *val) +{ + if(nconf >= MAXCONF) + return; + confname[nconf] = name; + confval[nconf] = val; + nconf++; +} + +char* +getconf(char *name) +{ + int i; + + for(i = 0; i < nconf; i++) + if(cistrcmp(confname[i], name) == 0) + return confval[i]; + return 0; +} + +void +confinit(void) +{ + ulong base; + + archconfinit(); + + base = PGROUND((ulong)end); + conf.base0 = base; + + conf.base1 = 0; + conf.npage1 = 0; + + conf.npage0 = (conf.topofmem - base)/BY2PG; + + conf.npage = conf.npage0 + conf.npage1; + conf.ialloc = (((conf.npage*(main_pool_pcnt))/100)/2)*BY2PG; + + conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5; + conf.nmach = 1; + +} + +void +init0(void) +{ + Osenv *o; + char buf[2*KNAMELEN]; + + up->nerrlab = 0; + + spllo(); + + if(waserror()) + panic("init0 %r"); + /* + * These are o.k. because rootinit is null. + * Then early kproc's will have a root and dot. + */ + o = up->env; + o->pgrp->slash = namec("#/", Atodir, 0, 0); + cnameclose(o->pgrp->slash->name); + o->pgrp->slash->name = newcname("/"); + o->pgrp->dot = cclone(o->pgrp->slash); + + chandevinit(); + + if(!waserror()){ + ksetenv("cputype", "arm", 0); + snprint(buf, sizeof(buf), "arm %s", conffile); + ksetenv("terminal", buf, 0); + poperror(); + } + + poperror(); + + disinit("/osinit.dis"); +} + +void +userinit(void) +{ + Proc *p; + Osenv *o; + + p = newproc(); + o = p->env; + + o->fgrp = newfgrp(nil); + o->pgrp = newpgrp(); + o->egrp = newegrp(); + kstrdup(&o->user, eve); + + strcpy(p->text, "interp"); + + p->fpstate = FPINIT; + + /* + * Kernel Stack + * + * N.B. The -12 for the stack pointer is important. + * 4 bytes for gotolabel's return PC + */ + p->sched.pc = (ulong)init0; + p->sched.sp = (ulong)p->kstack+KSTACK-8; + + ready(p); +} + +void +exit(int inpanic) +{ + up = 0; + + /* Shutdown running devices */ + chandevshutdown(); + + if(inpanic && 0){ + print("Hit the reset button\n"); + for(;;) + clockpoll(); + } + archreboot(); +} + +static void +linkproc(void) +{ + spllo(); + if (waserror()) + print("error() underflow: %r\n"); + else + (*up->kpfun)(up->arg); + pexit("end proc", 1); +} + +void +kprocchild(Proc *p, void (*func)(void*), void *arg) +{ + p->sched.pc = (ulong)linkproc; + p->sched.sp = (ulong)p->kstack+KSTACK-8; + + p->kpfun = func; + p->arg = arg; +} + +void +idlehands(void) +{ + cpuidlecount++; + idle(); +} + +/* stubs */ +void +setfsr(ulong) +{ +} + +ulong +getfsr() +{ + return 0; +} + +void +setfcr(ulong) +{ +} + +ulong +getfcr() +{ + return 0; +} + +void +fpinit(void) +{ +} + +void +FPsave(void*) +{ +} + +void +FPrestore(void*) +{ +} diff --git a/os/manga/manga b/os/manga/manga new file mode 100644 index 00000000..725e275e --- /dev/null +++ b/os/manga/manga @@ -0,0 +1,137 @@ +dev + root + cons archmanga +# gpio + mnt + pipe + prog + srv + dup + ssl +# cap +# sign + uart + ip ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium netaux + flash + ether netif netaux + env + pci pci inb + usb pci + +ip + il + tcp + udp +# rudp +# igmp + ipifc + icmp + icmp6 + ipmux + +lib + interp + keyring + sec + mp + math + kern + +mod + math + sys + keyring + +port + alarm + alloc + allocb + chan + dev + dial + dis + discall + exception + exportfs + inferno + latin1 + nocache + nodynld + parse + pgrp + print + proc + qio + qlock + random + sysfile + taslock + xalloc + +misc + uartks8695 + +link + flashcfi8 + ether8139 + etherks8695 + ethermedium + usbuhci + +code + int main_pool_pcnt = 50; + int heap_pool_pcnt = 50; + int image_pool_pcnt = 0; + int cflag = 0; /* for JIT */ + + int consoleprint = 1; + int panicreset = 0; + +init + cerf405 + +root + /chan / + /dev / + /boot / + /env / + /fd / + /net / + /prog / + /root / + /nvfs / + /osinit.dis + /tmp / + +# files used by osinit.dis during bootstrap + /boot/n / + /boot/n/local / + /boot/n/remote / + +# authentication + /boot/nvfs/default /usr/inferno/keyring/default + /boot/dis/lib/auth.dis /dis/lib/auth.dis + /boot/dis/lib/ssl.dis /dis/lib/ssl.dis +# dhcp + /boot/dis/lib/dhcpclient.dis /dis/lib/dhcpclient.dis + /boot/dis/lib/ip.dis /dis/lib/ip.dis + +# and other files used to poke round during development + /boot/dis/cat.dis /dis/cat.dis + /boot/dis/echo.dis /dis/echo.dis + /boot/dis/lib/arg.dis /dis/lib/arg.dis + + /boot/dis/sh.dis /dis/sh.dis + /boot/dis/lib/bufio.dis /dis/lib/bufio.dis + /boot/dis/lib/filepat.dis /dis/lib/filepat.dis + /boot/dis/lib/readdir.dis /dis/lib/readdir.dis + /boot/dis/lib/string.dis /dis/lib/string.dis + + /boot/dis/cd.dis /dis/cd.dis + /boot/dis/bind.dis /dis/bind.dis + /boot/dis/dd.dis /dis/dd.dis + /boot/dis/p.dis /dis/p.dis + /boot/dis/ls.dis /dis/ls.dis + /boot/dis/lib/daytime.dis /dis/lib/daytime.dis + /boot/dis/time.dis /dis/time.dis + /boot/dis/xd.dis /dis/xd.dis diff --git a/os/manga/mem.h b/os/manga/mem.h new file mode 100644 index 00000000..61296039 --- /dev/null +++ b/os/manga/mem.h @@ -0,0 +1,133 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ + +/* + * Sizes + */ +#define BI2BY 8 /* bits per byte */ +#define BI2WD 32 /* bits per word */ +#define BY2WD 4 /* bytes per word */ +#define BY2V 8 /* bytes per double word */ +#define BY2PG 4096 /* bytes per page */ +#define WD2PG (BY2PG/BY2WD) /* words per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1)) +#define PGROUND(s) ROUND(s, BY2PG) +#define BIT(n) (1<= PHYSDRAM0 && pa < conf.topofmem) + return (void*)(KZERO+(pa-PHYSDRAM0)); + return (void*)pa; +} + +/* + * Set a 1-1 map of virtual to physical memory, except: + * kernel is mapped to KZERO + * doubly-map page0 at the alternative interrupt vector address, + * doubly-map physical memory at KZERO+256*MB as uncached but buffered, + * map flash to virtual space away from 0, + * disable access to 0 (nil pointers). + * + * Other section maps are added later as required by mmuphysmap. + */ +void +mmuinit(void) +{ + int i; + ulong *ttb, *ptable; + + ttb = (ulong*)KTTB; + memset(ttb, 0, 16384); + + /* assume flash is first in special physical space */ + for(i = L1x(PHYSFLASH0); i < 0x1000; i++) + ttb[i] = (i<<20) | L1krw | (1<<4) | L1section; + + /* cached dram at normal kernel addresses */ + for(i = 0; i < 32*MB; i += MB) + ttb[L1x(KZERO+i)] = (PHYSDRAM0+i) | (1<<4) | L1krw | L1section | L1cached | L1buffered; + + /* aliases for uncached dram */ + for(i = 0; i < 64*MB; i += MB) + ttb[L1x(UCDRAMZERO+i)] = (PHYSDRAM0+i) | L1krw | (1<<4) | L1section; + + /* TO DO: make the text read only */ + + /* remap flash */ + for(i=0; i<8*MB; i+=MB) + ttb[L1x(FLASHMEM+i)] = (PHYSFLASH0+i) | L1krw | (1<<4) | L1section; /* we'll make flash uncached for now */ + + /* + * build page table for alternative vector page, mapping trap vectors in *page0 + */ + ptable = xspanalloc(SectionPages*sizeof(*ptable), PtAlign, 0); + ptable[L2x(AIVECADDR)] = PADDR(page0) | L2AP(APsrw) | L2cached | L2buffered | L2small; + ttb[L1x(AIVECADDR)] = PADDR(ptable) | (1<<4) | L1page; + + mmuputttb(KTTB & ~KZERO); + mmuputdac(Dclient); + mmuputctl(mmugetctl() | CpCaltivec | CpCIcache | CpCsystem | CpCwpd | CpCDcache | CpCmmu); + tlbinvalidateall(); +} + +/* + * flush data in a given address range to memory + * and invalidate the region in the instruction cache. + */ +int +segflush(void *a, ulong n) +{ + dcflush(a, n); + icflush(a, n); + return 0; +} + +/* + * return an uncached alias for the memory at a + */ +void* +mmucacheinhib(void *a, ulong nb) +{ + ulong p; + + if(a == nil) + return nil; + p = PADDR(a); + if(p & (CACHELINESZ-1)) + panic("mmucacheinhib"); + dcflush(a, nb); + return (void*)(UCDRAMZERO|PADDR(a)); +} diff --git a/os/manga/pci.c b/os/manga/pci.c new file mode 100644 index 00000000..7bebf05b --- /dev/null +++ b/os/manga/pci.c @@ -0,0 +1,1007 @@ +/* + * PCI support code. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define DBG if(0) pcilog +#undef DBG +#define DBG if(1) iprint + +typedef struct Pcicfg Pcicfg; +struct Pcicfg { + ulong addr; + ulong data; +}; + +static Pcicfg* pcicfg; +static ulong* pciack; +static ulong* pcimem; + +struct +{ + char output[16384]; + int ptr; +}PCICONS; + +int +pcilog(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + + memmove(PCICONS.output+PCICONS.ptr, buf, n); + PCICONS.ptr += n; + return n; +} + +enum +{ /* configuration mechanism #1 */ + MaxFNO = 7, + MaxUBN = 255, +}; + +enum +{ /* command register */ + IOen = (1<<0), + MEMen = (1<<1), + MASen = (1<<2), + MemWrInv = (1<<4), + PErrEn = (1<<6), + SErrEn = (1<<8), +}; + +static Lock pcicfglock; +static QLock pcicfginitlock; +static int pcicfgmode = -1; +static int pcimaxbno = 7; +static int pcimaxdno; +static Pcidev* pciroot; +static Pcidev* pcilist; +static Pcidev* pcitail; + +static int pcicfgrw32(int, int, int, int); +static int pcicfgrw8(int, int, int, int); +static void pcirouting(void); +static void pcirootmap(Pcidev*); +static void pcidumpdev(ulong); + +static char* bustypes[] = { +[BusIRQ] "IRQ", +[BusPCI] "PCI", +}; + +#pragma varargck type "Y" int + +static int +tbdffmt(Fmt* fmt) +{ + char *p; + int l, r, type, tbdf; + + if((p = malloc(READSTR)) == nil) + return fmtstrcpy(fmt, "(tbdfconv)"); + + switch(fmt->r){ + case 'T': + case 'Y': + tbdf = va_arg(fmt->args, int); + type = BUSTYPE(tbdf); + if(type < nelem(bustypes)) + l = snprint(p, READSTR, bustypes[type]); + else + l = snprint(p, READSTR, "%d", type); + snprint(p+l, READSTR-l, ".%d.%d.%d", + BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf)); + break; + + default: + snprint(p, READSTR, "(tbdfconv)"); + break; + } + r = fmtstrcpy(fmt, p); + free(p); + + return r; +} + +ulong +pcibarsize(Pcidev *p, int rno) +{ + ulong v, size; + + v = pcicfgrw32(p->tbdf, rno, 0, 1); + pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0); + size = pcicfgrw32(p->tbdf, rno, 0, 1); + if(v & 1) + size |= 0xFFFF0000; + pcicfgrw32(p->tbdf, rno, v, 0); + + return -(size & ~0x0F); +} + +static int +pcisizcmp(void *a, void *b) +{ + Pcisiz *aa, *bb; + + aa = a; + bb = b; + return aa->siz - bb->siz; +} + +static ulong +pcimask(ulong v) +{ + ulong m; + + m = BI2BY*sizeof(v); + for(m = 1<<(m-1); m != 0; m >>= 1) { + if(m & v) + break; + } + + m--; + if((v & m) == 0) + return v; + + v |= m; + return v+1; +} + +static void +pcibusmap(Pcidev *root, ulong *pmema, ulong *pioa, int wrreg) +{ + Pcidev *p; + int ntb, i, size, rno, hole; + ulong v, mema, ioa, sioa, smema, base, limit; + Pcisiz *table, *tptr, *mtb, *itb; + extern void qsort(void*, long, long, int (*)(void*, void*)); + + ioa = *pioa; + mema = *pmema; + + DBG("pcibusmap wr=%d %Y mem=%luX io=%luX\n", + wrreg, root->tbdf, mema, ioa); + + ntb = 0; + for(p = root; p != nil; p = p->link) + ntb++; + + ntb *= (PciCIS-PciBAR0)/4; + table = malloc(2*ntb*sizeof(Pcisiz)); + itb = table; + mtb = table+ntb; + + /* + * Build a table of sizes + */ + for(p = root; p != nil; p = p->link) { + if(p->ccrb == 0x06) { + if(p->ccru == 0x04 && p->bridge != nil) { + sioa = ioa; + smema = mema; + pcibusmap(p->bridge, &smema, &sioa, 0); + + hole = pcimask(smema-mema); + if(hole < (1<<20)) + hole = 1<<20; + p->mema.size = hole; + + hole = pcimask(sioa-ioa); + if(hole < (1<<12)) + hole = 1<<12; + + p->ioa.size = hole; + + itb->dev = p; + itb->bar = -1; + itb->siz = p->ioa.size; + itb++; + + mtb->dev = p; + mtb->bar = -1; + mtb->siz = p->mema.size; + mtb++; + } + if((pcicfgr8(p, PciHDT)&0x7f) != 0) + continue; + } + + for(i = 0; i <= 5; i++) { + rno = PciBAR0 + i*4; + v = pcicfgrw32(p->tbdf, rno, 0, 1); + size = pcibarsize(p, rno); + if(size == 0) + continue; + + if(v & 1) { + itb->dev = p; + itb->bar = i; + itb->siz = size; + itb++; + } + else { + mtb->dev = p; + mtb->bar = i; + mtb->siz = size; + mtb++; + } + + p->mem[i].size = size; + } + } + + /* + * Sort both tables IO smallest first, Memory largest + */ + qsort(table, itb-table, sizeof(Pcisiz), pcisizcmp); + tptr = table+ntb; + qsort(tptr, mtb-tptr, sizeof(Pcisiz), pcisizcmp); + + /* + * Allocate IO address space on this bus + */ + for(tptr = table; tptr < itb; tptr++) { + hole = tptr->siz; + if(tptr->bar == -1) + hole = 1<<12; + ioa = (ioa+hole-1) & ~(hole-1); + + p = tptr->dev; + if(tptr->bar == -1) + p->ioa.bar = ioa; + else { + p->pcr |= IOen; + p->mem[tptr->bar].bar = ioa|1; + if(wrreg) + pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), ioa|1, 0); + } + + ioa += tptr->siz; + } + + /* + * Allocate Memory address space on this bus + */ + for(tptr = table+ntb; tptr < mtb; tptr++) { + hole = tptr->siz; + if(tptr->bar == -1) + hole = 1<<20; + mema = (mema+hole-1) & ~(hole-1); + + p = tptr->dev; + if(tptr->bar == -1) + p->mema.bar = mema; + else { + p->pcr |= MEMen; + p->mem[tptr->bar].bar = mema; + if(wrreg) + pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), mema, 0); + } + mema += tptr->siz; + } + + *pmema = mema; + *pioa = ioa; + free(table); + + if(wrreg == 0) + return; + + /* + * Finally set all the bridge addresses & registers + */ + for(p = root; p != nil; p = p->link) { + if(p->bridge == nil) { + pcicfgrw8(p->tbdf, PciLTR, 64, 0); + + p->pcr |= MASen; + pcicfgrw32(p->tbdf, PciPCR, p->pcr, 0); + continue; + } + + base = p->ioa.bar; + limit = base+p->ioa.size-1; + v = pcicfgrw32(p->tbdf, PciBAR3, 0, 1); + v = (v&0xFFFF0000)|(limit & 0xF000)|((base & 0xF000)>>8); + pcicfgrw32(p->tbdf, PciBAR3, v, 0); + v = (limit & 0xFFFF0000)|(base>>16); + pcicfgrw32(p->tbdf, 0x30, v, 0); + + base = p->mema.bar; + limit = base+p->mema.size-1; + v = (limit & 0xFFF00000)|((base & 0xFFF00000)>>16); + pcicfgrw32(p->tbdf, PciBAR4, v, 0); + + /* + * Disable memory prefetch + */ + pcicfgrw32(p->tbdf, PciBAR5, 0x0000FFFF, 0); + pcicfgrw8(p->tbdf, PciLTR, 64, 0); + + /* + * Enable the bridge + */ + v = 0xFFFF0000 | IOen | MEMen | MASen; + pcicfgrw32(p->tbdf, PciPCR, v, 0); + + sioa = p->ioa.bar; + smema = p->mema.bar; + pcibusmap(p->bridge, &smema, &sioa, 1); + } +} + +static int +pcilscan(int bno, Pcidev** list) +{ + Pcidev *p, *head, *tail; + int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn; + + maxubn = bno; + head = nil; + tail = nil; + for(dno = 0; dno <= pcimaxdno; dno++){ + maxfno = 0; + for(fno = 0; fno <= maxfno; fno++){ + /* + * For this possible device, form the + * bus+device+function triplet needed to address it + * and try to read the vendor and device ID. + * If successful, allocate a device struct and + * start to fill it in with some useful information + * from the device's configuration space. + */ + tbdf = MKBUS(BusPCI, bno, dno, fno); + l = pcicfgrw32(tbdf, PciVID, 0, 1); + if(l == 0xFFFFFFFF || l == 0) + continue; + p = malloc(sizeof(*p)); + p->tbdf = tbdf; + p->vid = l; + p->did = l>>16; + + if(pcilist != nil) + pcitail->list = p; + else + pcilist = p; + pcitail = p; + + p->rid = pcicfgr8(p, PciRID); + p->ccrp = pcicfgr8(p, PciCCRp); + p->ccru = pcicfgr8(p, PciCCRu); + p->ccrb = pcicfgr8(p, PciCCRb); + p->pcr = pcicfgr32(p, PciPCR); + + p->intl = pcicfgr8(p, PciINTL); + + /* + * If the device is a multi-function device adjust the + * loop count so all possible functions are checked. + */ + hdt = pcicfgr8(p, PciHDT); + if(hdt & 0x80) + maxfno = MaxFNO; + + /* + * If appropriate, read the base address registers + * and work out the sizes. + */ + switch(p->ccrb) { + case 0x01: /* mass storage controller */ + case 0x02: /* network controller */ + case 0x03: /* display controller */ + case 0x04: /* multimedia device */ + case 0x06: /* bridge device */ + case 0x07: /* simple comm. controllers */ + case 0x08: /* base system peripherals */ + case 0x09: /* input devices */ + case 0x0A: /* docking stations */ + case 0x0B: /* processors */ + case 0x0C: /* serial bus controllers */ + if((hdt & 0x7F) != 0) + break; + rno = PciBAR0 - 4; + for(i = 0; i < nelem(p->mem); i++) { + rno += 4; + p->mem[i].bar = pcicfgr32(p, rno); + p->mem[i].size = pcibarsize(p, rno); + } + break; + + case 0x00: + case 0x05: /* memory controller */ + default: + break; + } + + if(head != nil) + tail->link = p; + else + head = p; + tail = p; + } + } + + *list = head; + for(p = head; p != nil; p = p->link){ + /* + * Find PCI-PCI bridges and recursively descend the tree. + */ + if(p->ccrb != 0x06 || p->ccru != 0x04) + continue; + + /* + * If the secondary or subordinate bus number is not + * initialised try to do what the PCI BIOS should have + * done and fill in the numbers as the tree is descended. + * On the way down the subordinate bus number is set to + * the maximum as it's not known how many buses are behind + * this one; the final value is set on the way back up. + */ + sbn = pcicfgr8(p, PciSBN); + ubn = pcicfgr8(p, PciUBN); + + if(sbn == 0 || ubn == 0) { + sbn = maxubn+1; + /* + * Make sure memory, I/O and master enables are + * off, set the primary, secondary and subordinate + * bus numbers and clear the secondary status before + * attempting to scan the secondary bus. + * + * Initialisation of the bridge should be done here. + */ + pcicfgw32(p, PciPCR, 0xFFFF0000); + l = (MaxUBN<<16)|(sbn<<8)|bno; + pcicfgw32(p, PciPBN, l); + pcicfgw16(p, PciSPSR, 0xFFFF); + maxubn = pcilscan(sbn, &p->bridge); + l = (maxubn<<16)|(sbn<<8)|bno; + + pcicfgw32(p, PciPBN, l); + } + else { + maxubn = ubn; + pcilscan(sbn, &p->bridge); + } + } + + return maxubn; +} + +int +pciscan(int bno, Pcidev **list) +{ + int ubn; + + qlock(&pcicfginitlock); + ubn = pcilscan(bno, list); + qunlock(&pcicfginitlock); + return ubn; +} + +static void +pcicfginit(void) +{ + char *p; + int bno; + Pcidev **list; + ulong mema, ioa; + + qlock(&pcicfginitlock); + if(pcicfgmode != -1) + goto out; + + //pcimmap(); + + pcicfgmode = 1; + pcimaxdno = 31; + + fmtinstall('Y', tbdffmt); + + if(p = getconf("*pcimaxbno")) + pcimaxbno = strtoul(p, 0, 0); + if(p = getconf("*pcimaxdno")) + pcimaxdno = strtoul(p, 0, 0); + + + list = &pciroot; + for(bno = 0; bno <= pcimaxbno; bno++) { + int sbno = bno; + bno = pcilscan(bno, list); + + while(*list) + list = &(*list)->link; + + if (sbno == 0) { + Pcidev *pci; + + /* + * If we have found a PCI-to-Cardbus bridge, make sure + * it has no valid mappings anymore. + */ + pci = pciroot; + while (pci) { + if (pci->ccrb == 6 && pci->ccru == 7) { + ushort bcr; + + /* reset the cardbus */ + bcr = pcicfgr16(pci, PciBCR); + pcicfgw16(pci, PciBCR, 0x40 | bcr); + delay(50); + } + pci = pci->link; + } + } + } + + if(pciroot == nil) + goto out; + + /* + * Work out how big the top bus is + */ + mema = 0; + ioa = 0; + pcibusmap(pciroot, &mema, &ioa, 0); + + DBG("Sizes: mem=%8.8lux size=%8.8lux io=%8.8lux\n", + mema, pcimask(mema), ioa); + + /* + * Align the windows and map it + */ + ioa = 0x1000; + mema = 0; + + pcilog("Mask sizes: mem=%lux io=%lux\n", mema, ioa); + + pcibusmap(pciroot, &mema, &ioa, 1); + DBG("Sizes2: mem=%lux io=%lux\n", mema, ioa); + + pcirootmap(pciroot); + + pcirouting(); + + if(1){ + iprint("pci bridge':\n"); + pcidumpdev(pciroot->tbdf); + } + if(1){ + /* see we've left */ + ulong *p; + int i; + + p = KADDR(PHYSBRIDGE+0x200); + iprint("PCI:\n"); + for(i=0; i<10; i++) + iprint("%8.8lux: %8.8lux\n", p+i, p[i]); + } + +out: + qunlock(&pcicfginitlock); +} + +static int +pcicfgrw8(int tbdf, int rno, int data, int read) +{ + int o, x; + + if(pcicfgmode == -1) + pcicfginit(); + + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + o = (rno & 0x03)<<3; + rno &= ~0x03; + pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno; + if(read) + x = (pcicfg->data>>o) & 0xFF; + else + pcicfg->data = (pcicfg->data & ~(0xFF<addr = 0; + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr8(Pcidev* pcidev, int rno) +{ + return pcicfgrw8(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw8(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw8(pcidev->tbdf, rno, data, 0); +} + +static int +pcicfgrw16(int tbdf, int rno, int data, int read) +{ + int o, x; + + if(pcicfgmode == -1) + pcicfginit(); + + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + o = ((rno >> 1) & 1)<<4; + rno &= ~0x03; + pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno; + if(read) + x = (pcicfg->data>>o) & 0xFFFF; + else + pcicfg->data = (pcicfg->data & ~(0xFFFF<addr = 0; + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr16(Pcidev* pcidev, int rno) +{ + return pcicfgrw16(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw16(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw16(pcidev->tbdf, rno, data, 0); +} + +static int +pcicfgrw32(int tbdf, int rno, int data, int read) +{ + int x; + + if(pcicfgmode == -1) + pcicfginit(); + + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + rno &= ~0x03; + pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno; + if(read) + x = pcicfg->data; + else + pcicfg->data = data; + pcicfg->addr = 0; + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr32(Pcidev* pcidev, int rno) +{ + return pcicfgrw32(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw32(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw32(pcidev->tbdf, rno, data, 0); +} + +Pcidev* +pcimatch(Pcidev* prev, int vid, int did) +{ + if(pcicfgmode == -1) + pcicfginit(); + + if(prev == nil) + prev = pcilist; + else + prev = prev->list; + + while(prev != nil){ + if((vid == 0 || prev->vid == vid) + && (did == 0 || prev->did == did)) + break; + prev = prev->list; + } + return prev; +} + +Pcidev* +pcimatchtbdf(int tbdf) +{ + Pcidev *pcidev; + + if(pcicfgmode == -1) + pcicfginit(); + + for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) { + if(pcidev->tbdf == tbdf) + break; + } + return pcidev; +} + +uchar +pciipin(Pcidev *pci, uchar pin) +{ + if (pci == nil) + pci = pcilist; + + while (pci) { + uchar intl; + + if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff) + return pci->intl; + + if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0) + return intl; + + pci = pci->list; + } + return 0; +} + +static void +pcilhinv(Pcidev* p) +{ + int i; + Pcidev *t; + + if(p == nil) { + putstrn(PCICONS.output, PCICONS.ptr); + p = pciroot; + print("bus dev type vid did intl memory\n"); + } + for(t = p; t != nil; t = t->link) { + print("%d %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d ", + BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf), + t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl); + + for(i = 0; i < nelem(p->mem); i++) { + if(t->mem[i].size == 0) + continue; + print("%d:%.8lux %d ", i, + t->mem[i].bar, t->mem[i].size); + } + if(t->ioa.bar || t->ioa.size) + print("ioa:%.8lux %d ", t->ioa.bar, t->ioa.size); + if(t->mema.bar || t->mema.size) + print("mema:%.8lux %d ", t->mema.bar, t->mema.size); + if(t->bridge) + print("->%d", BUSBNO(t->bridge->tbdf)); + print("\n"); + } + while(p != nil) { + if(p->bridge != nil) + pcilhinv(p->bridge); + p = p->link; + } +} + +void +pcihinv(Pcidev* p) +{ + if(pcicfgmode == -1) + pcicfginit(); + qlock(&pcicfginitlock); + pcilhinv(p); + qunlock(&pcicfginitlock); +} + +void +pcishutdown(void) +{ + Pcidev *p; + + if(pcicfgmode == -1) + pcicfginit(); + + for(p = pcilist; p != nil; p = p->list){ + /* don't mess with the bridges */ + if(p->ccrb == 0x06) + continue; + pciclrbme(p); + } +} + +void +pcisetbme(Pcidev* p) +{ + int pcr; + + pcr = pcicfgr16(p, PciPCR); + pcr |= MASen; + pcicfgw16(p, PciPCR, pcr); +} + +void +pciclrbme(Pcidev* p) +{ + int pcr; + + pcr = pcicfgr16(p, PciPCR); + pcr &= ~MASen; + pcicfgw16(p, PciPCR, pcr); +} + +/* + * KS8695P specific + */ + +typedef struct Pciahb Pciahb; +struct Pciahb { + ulong pbm; /* bridge mode */ + ulong pbcs; /* control and status */ + ulong pmba; /* memory base address */ + ulong pmbac; /* memory base address control */ + ulong pmbam; /* memory base address mask */ + ulong pmbat; /* memory base address translation */ + ulong pioba; /* i/o base address */ + ulong piobac; /* i/o base address control */ + ulong piobam; /* i/o base address mask */ + ulong piobat; /* i/o base address translation */ +}; + +enum { + /* pbm */ + PciHost= 1<<31, /* host bridge mode */ + PciModePCI= 0<<29, + PciModeMini= 1<<29, + PciModeCbus= 2<<29, + + /* pbcs */ + PciReset= 1<<31, + PciPF4= 0<<29, /* prefetch 4, 8 or 16 words */ + PciPF8= 1<<29, + PciPF16= 2<<29, + + /* pmbac, piobac */ + PciTranslate= 1<<31, /* enable downstream address translation */ +}; + +static void +pcidumpdev(ulong tbdf) +{ + int i; + + for(i=0; i<0x40; i+=4) + iprint("[%.2x]=%.8ux\n", i, pcicfgrw32(tbdf, i, 0, 1)); +} + +void +pcimapinit(void) +{ + Pciahb *pm; + int i; + + pm = KADDR(PHYSBRIDGE+0x200); + if(1){ + /* see what the bootstrap left */ + ulong *p; + + p = (ulong*)pm; + iprint("PCI:\n"); + for(i=0; i<10; i++) + iprint("%8.8lux: %8.8lux\n", p+i, p[i]); + } +#ifdef NOT + /* TO DO: soft reset */ + putdcr(Cpc0Srr, Rpci); + delay(1); + putdcr(Cpc0Srr, 0); +#endif + pm->pbcs = 0x30000000; /* prefetch limit 8 words; pci config access disable */ + + pcicfg = KADDR(PHYSBRIDGE+0x100); + + /* + * AHB addresses between PHYSPCIBRIDGE and PHYSPCIBRIDGE+64Mb + * are mapped to PCI memory at 0. + */ + pcimem = mmuphysmap(KADDR(PHYSPCIBRIDGE), PHYSPCIBRIDGE, 0x4000000); + pm->pmbac = 0; /* disable during update */ + pm->pmba = PHYSPCIBRIDGE; + pm->pmbam = 0xFC000000; + pm->pmbat = 0; /* TO DO: check */ + pm->pmbac = PciTranslate; /* enable */ + + /* + * AHB addresses between PHYSPCIIO and PHYSPCIIO+64Mb + * are mapped to physical memory. + */ + mmuphysmap(KADDR(PHYSPCIIO), PHYSPCIIO, 64*1024); + pm->piobac = 0; /* disable during update */ + pm->pioba = PHYSPCIIO; + pm->piobam = ~(64*1024-1); + pm->piobat = 0; /* TO DO: check */ + pm->piobac = PciTranslate; /* enable */ + + pcicfgmode = -1; +} + +static void +pcirootmap(Pcidev *bridge) +{ + ulong pcsr; + + /* + * addresses presented by a PCI device between PCIWINDOW and PCIWINDOW+64Mb + * are mapped to physical memory. + */ + pcsr = pcicfgr32(bridge, PciPSR); +iprint("pcsr0=%8.8lux\n", pcsr); + pcicfgw32(bridge, PciPSR, pcsr); /* reset error status */ + pcsr = pcicfgr32(bridge, PciPSR); +iprint("pcsr1=%8.8lux\n", pcsr); + pcicfgw32(bridge, PciBAR0, PCIWINDOW); +} + +typedef struct Pciroute Pciroute; +struct Pciroute { + int slot; + int pin; + int irq; +}; + +static Pciroute pciroutes[] = { + {0, 1, IRQext0}, /* bridge */ + {5, 1, IRQext3}, /* USB */ + {5, 2, IRQext1}, + {5, 3, IRQext2}, +// {6, 0, IRQext3}, /* miniPCI0 (or RTL8139 for extra WAN?) */ + {6, 1, IRQext0}, /* RTL8139 for extra WAN */ + {7, 0, IRQext3}, /* miniPCI1 */ + {-1, 0, IRQext0}, +}; + +static void +pcirouting(void) +{ + int i, pin, irq; + Pcidev *pci; + Pciroute *r; + + for(pci = pcilist; pci != nil; pci = pci->list){ + pin = pcicfgr8(pci, PciINTP); + if(pin == 0 || pin == 0xff) + continue; + irq = -1; + for(i=0; islot < 0 || r->slot == BUSDNO(pci->tbdf) && (r->pin == 0 || r->pin == pin)){ + irq = r->irq; + break; + } + } + if(irq < 0) + continue; + irq |= IRQactivelow; + iprint("pcirouting: %Y at pin %d ", pci->tbdf, pin); + if(pci->intl != 0 && pci->intl != 0xFF && pci->intl != irq) + iprint("irq %d -> %d\n", pci->intl, irq); + else + iprint("irq %d\n", irq); + pcicfgw8(pci, PciINTL, irq); + pci->intl = irq; + } +} diff --git a/os/manga/pinflate b/os/manga/pinflate new file mode 100644 index 00000000..c6e8428e Binary files /dev/null and b/os/manga/pinflate differ diff --git a/os/manga/trap.c b/os/manga/trap.c new file mode 100644 index 00000000..1ac808ba --- /dev/null +++ b/os/manga/trap.c @@ -0,0 +1,498 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +#define waslo(sr) (!((sr) & (PsrDirq|PsrDfiq))) + +enum +{ + MaxVector= 32, /* determined by bits per word */ + Maxhandler= MaxVector+5 /* max number of interrupt handlers, assuming a few shared */ +}; + +typedef struct Handler Handler; +struct Handler { + void (*r)(Ureg*, void*); + void* a; + char name[KNAMELEN]; + Handler* next; + int edge; + ulong nintr; + ulong ticks; + int maxtick; +}; + +static Lock veclock; + +static struct +{ + Handler *ivec[MaxVector]; + Handler h[Maxhandler]; + int free; + Handler* freelist; +} halloc; + +Instr BREAK = 0xE6BAD010; + +int (*breakhandler)(Ureg*, Proc*); +int (*catchdbg)(Ureg *, uint); + +extern void (*serwrite)(char *, int); + +void +intrenable(int sort, int v, void (*r)(Ureg*, void*), void* a, char *name) +{ + int o, x; + ulong f; + GpioReg *g; + Handler *h; + + USED(sort); + f = v; + v &= IRQmask; + if(v >= nelem(halloc.ivec)) + panic("intrenable(%d)", v); + ilock(&veclock); + if(v >= IRQext0 && v <= IRQtm1){ + /* need to switch GPIO pins, set mode */ + g = GPIOREG; + if(v <= IRQext3){ /* choice of interrupt type */ + o = (v-IRQext0)*4; /* b mmm */ + g->iopc = (g->iopc & ~(7<>8)&7)<iopm |= 1<iopm &= ~(1<iopc |= 1<<(16+(v-IRQtm0)); + if(0) + iprint("v=%d iopc=%8.8lux iopm=%8.8lux\n", v, g->iopc, g->iopm); + } + if((h = halloc.freelist) == nil){ + if(halloc.free >= Maxhandler){ + iunlock(&veclock); + panic("out of interrupt handlers"); /* can't happen */ + } + h = &halloc.h[halloc.free++]; + }else + halloc.freelist = h->next; + h->r = r; + h->a = a; + strncpy(h->name, name, KNAMELEN-1); + h->name[KNAMELEN-1] = 0; + h->next = halloc.ivec[v]; + halloc.ivec[v] = h; + + /* enable the corresponding interrupt in the controller */ + x = splfhi(); + INTRREG->st = 1<en |= 1<= nelem(halloc.ivec)) + panic("intrdisable(%d)", v); + ilock(&veclock); + for(hp = &halloc.ivec[v]; (h = *hp) != nil; hp = &h->next) + if(h->r == r && h->a == a && strcmp(h->name, name) == 0){ + *hp = h->next; + h->r = nil; + h->next = halloc.freelist; + halloc.freelist = h; + break; + } + if(halloc.ivec[v] == nil){ + if(v >= IRQext0 && v <= IRQtm1){ + /* need to reset GPIO pins */ + g = GPIOREG; + if(v <= IRQext3){ /* choice of interrupt type */ + o = (v-IRQext0)*4; /* b mmm */ + g->iopc &= ~(0xF<iopm &= ~(v-IRQext0); /* force to input */ + }else + g->iopc &= ~(1<<(16+(v-IRQtm0))); + } + x = splfhi(); + INTRREG->en &= ~(1<next){ + INTRREG->st = 1<r(ur, h->a); + ibits &= ~(1<en &= ~ibits; + splx(s); + } +} + +/* + * initialise R13 in each trap mode, at the start and after suspend reset. + */ +void +trapstacks(void) +{ + setr13(PsrMfiq, m->fiqstack+nelem(m->fiqstack)); + setr13(PsrMirq, m->irqstack+nelem(m->irqstack)); + setr13(PsrMabt, m->abtstack+nelem(m->abtstack)); + setr13(PsrMund, m->undstack+nelem(m->undstack)); +} + +void +trapinit(void) +{ + IntrReg *intr; + + intr = INTRREG; + intr->mc = 0; /* all IRQ not FIQ */ + intr->en = 0; /* disable everything */ + intr->st = intr->st; /* reset edges */ + + trapstacks(); + + memmove(page0->vectors, vectors, sizeof(page0->vectors)); + memmove(page0->vtable, vtable, sizeof(page0->vtable)); + dcflush(page0, sizeof(*page0)); + + icflushall(); +} + +static char *trapnames[PsrMask+1] = { + [ PsrMfiq ] "Fiq interrupt", + [ PsrMirq ] "Mirq interrupt", + [ PsrMsvc ] "SVC/SWI Exception", + [ PsrMabt ] "Prefetch Abort/Data Abort", + [ PsrMabt+1 ] "Data Abort", + [ PsrMund ] "Undefined instruction", + [ PsrMsys ] "Sys trap" +}; + +static char * +trapname(int psr) +{ + char *s; + + s = trapnames[psr & PsrMask]; + if(s == nil) + s = "Undefined trap"; + return s; +} + +static void +sys_trap_error(int type) +{ + char errbuf[ERRMAX]; + sprint(errbuf, "sys: trap: %s\n", trapname(type)); + error(errbuf); +} + +static void +faultarm(Ureg *ureg, ulong far) +{ + char buf[ERRMAX]; + + sprint(buf, "sys: trap: fault pc=%8.8lux addr=0x%lux", (ulong)ureg->pc, far); + if(1){ + iprint("%s\n", buf); + dumpregs(ureg); + } + if(far == ~0) + disfault(ureg, "dereference of nil"); + disfault(ureg, buf); +} + +/* + * All traps come here. It might be slightly slower to have all traps call trap + * rather than directly vectoring the handler. + * However, this avoids a lot of code duplication and possible bugs. + * trap is called splfhi(). + */ +void +trap(Ureg* ureg) +{ + ulong far, fsr; + int t, itype; + Proc *oup; + + /* + * All interrupts/exceptions should be resumed at ureg->pc-4, + * except for Data Abort which resumes at ureg->pc-8. + */ + itype = ureg->type; + if(itype == PsrMabt+1) + ureg->pc -= 8; + else + ureg->pc -= 4; + ureg->sp = (ulong)(ureg+1); + if(itype == PsrMfiq){ /* fast interrupt (eg, profiler) */ + oup = up; + up = nil; + intrs(ureg, INTRREG->ms & INTRREG->mc); /* just FIQ ones */ + up = oup; + return; + } + + /* All other traps */ + + if(up){ + up->pc = ureg->pc; + up->dbgreg = ureg; + } + switch(itype) { + case PsrMirq: + t = m->ticks; /* CPU time per proc */ + up = nil; /* no process at interrupt level */ + splflo(); /* allow fast interrupts */ + intrs(ureg, INTRREG->ms & ~INTRREG->mc); /* just IRQ */ + up = m->proc; + preemption(m->ticks - t); + break; + + case PsrMund: /* Undefined instruction */ + if(*(ulong*)ureg->pc == BREAK && breakhandler) { + int s; + Proc *p; + + p = up; + /* if(!waslo(ureg->psr) || ureg->pc >= (ulong)splhi && ureg->pc < (ulong)islo) + p = 0; */ + s = breakhandler(ureg, p); + if(s == BrkSched) { + p->preempted = 0; + sched(); + } else if(s == BrkNoSched) { + p->preempted = 1; /* stop it being preempted until next instruction */ + if(up) + up->dbgreg = 0; + return; + } + break; + } + if(up == nil) + goto faultpanic; + spllo(); + if(waserror()) { + if(waslo(ureg->psr) && up->type == Interp) + disfault(ureg, up->env->errstr); + setpanic(); + dumpregs(ureg); + panic("%s", up->env->errstr); + } + if(!fpiarm(ureg)) { + dumpregs(ureg); + sys_trap_error(ureg->type); + } + poperror(); + break; + + case PsrMsvc: /* Jump through 0 or SWI */ + if(waslo(ureg->psr) && up && up->type == Interp) { + spllo(); + dumpregs(ureg); + sys_trap_error(ureg->type); + } + setpanic(); + dumpregs(ureg); + panic("SVC/SWI exception"); + break; + + case PsrMabt: /* Prefetch abort */ + if(catchdbg && catchdbg(ureg, 0)) + break; + /* FALL THROUGH */ + case PsrMabt+1: /* Data abort */ + fsr = mmugetfsr(); + far = mmugetfar(); + if(fsr & (1<<9)) { + mmuputfsr(fsr & ~(1<<9)); + if(catchdbg && catchdbg(ureg, fsr)) + break; + print("Debug/"); + } + if(waslo(ureg->psr) && up && up->type == Interp) { + spllo(); + faultarm(ureg, far); + } + iprint("Data Abort: FSR %8.8luX FAR %8.8luX\n", fsr, far); xdelay(500);serialputs("\n", 1); + /* FALL THROUGH */ + + default: /* ??? */ +faultpanic: + setpanic(); + dumpregs(ureg); + panic("exception %uX %s\n", ureg->type, trapname(ureg->type)); + break; + } + + splhi(); + if(up) + up->dbgreg = 0; /* becomes invalid after return from trap */ +} + +void +setpanic(void) +{ + if(breakhandler != 0) /* don't mess up debugger */ + return; +/* + INTRREG->en = 0; + spllo(); +*/ + splhi(); + GPIOREG->iopd &= ~(1<= KZERO && (ulong)v <= (ulong)KADDR(conf.topofmem-1); +} + +void +dumplongs(char *msg, ulong *v, int n) +{ + int i, l; + + l = 0; + iprint("%s at %.8p: ", msg, v); + for(i=0; i= 4){ + iprint("\n %.8p: ", v); + l = 0; + } + if(isvalid_va(v)){ + iprint(" %.8lux", *v++); + l++; + }else{ + iprint(" invalid"); + break; + } + } + iprint("\n"); +} + +static void +_dumpstack(Ureg *ureg) +{ + ulong v, *l, *estack; + int i; + + l = (ulong*)(ureg+1); + if((ulong)l & 3){ + iprint("invalid ureg/stack: %.8p\n", l); + return; + } + iprint("dumpstack\n"); + print("ktrace /kernel/path %.8ux %.8ux %.8ux\n", ureg->pc, ureg->sp, ureg->r14); + if(up != nil && l >= (ulong*)up->kstack && l <= (ulong*)(up->kstack+KSTACK-4)) + estack = (ulong*)(up->kstack+KSTACK); + else if(l >= (ulong*)m->stack && l <= (ulong*)((ulong)m+BY2PG-4)) + estack = (ulong*)((ulong)m+BY2PG-4); + else{ + iprint("unknown stack %8.8p\n", l); + return; + } + iprint("estackx %8.8p\n", estack); + i = 0; + for(; ltype)); + if((ureg->psr & PsrMask) != PsrMsvc) + print(" in %s", trapname(ureg->psr)); + print("\n"); + print("PSR %8.8uX type %2.2uX PC %8.8uX LINK %8.8uX\n", + ureg->psr, ureg->type, ureg->pc, ureg->link); + print("R14 %8.8uX R13 %8.8uX R12 %8.8uX R11 %8.8uX R10 %8.8uX\n", + ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10); + print("R9 %8.8uX R8 %8.8uX R7 %8.8uX R6 %8.8uX R5 %8.8uX\n", + ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5); + print("R4 %8.8uX R3 %8.8uX R2 %8.8uX R1 %8.8uX R0 %8.8uX\n", + ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0); + print("Stack is at: %8.8luX\n", ureg); + print("PC %8.8lux LINK %8.8lux\n", (ulong)ureg->pc, (ulong)ureg->link); + + if(up) + print("Process stack: %8.8lux-%8.8lux\n", + up->kstack, up->kstack+KSTACK-4); + else + print("System stack: %8.8lux-%8.8lux\n", + (ulong)(m+1), (ulong)m+BY2PG-4); + dumplongs("stack", (ulong *)(ureg + 1), 16); + _dumpstack(ureg); +} + +/* + * Fill in enough of Ureg to get a stack trace, and call a function. + * Used by debugging interface rdb. + */ +void +callwithureg(void (*fn)(Ureg*)) +{ + Ureg ureg; + ureg.pc = getcallerpc(&fn); + ureg.sp = (ulong)&fn; + ureg.r14 = 0; + fn(&ureg); +} + +void +dumpstack(void) +{ +return; + callwithureg(_dumpstack); +} + +void +trapspecial(int (*f)(Ureg *, uint)) +{ + catchdbg = f; +} diff --git a/os/manga/uartks8695.c b/os/manga/uartks8695.c new file mode 100644 index 00000000..dd04f02d --- /dev/null +++ b/os/manga/uartks8695.c @@ -0,0 +1,629 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "../port/uart.h" + +/* + * KS8695 uart; similar to 8250 etc but registers are slightly different, + * and interrupt control is quite different + */ +enum { + UartFREQ = CLOCKFREQ, +}; + +/* + * similar to i8250/16450/16550 (slight differences) + */ + +enum { /* I/O ports */ + Rbr = 0, /* Receiver Buffer (RO) */ + Thr = 1, /* Transmitter Holding (WO) */ + Fcr = 2, /* FIFO Control */ + Lcr = 3, /* Line Control */ + Mcr = 4, /* Modem Control */ + Lsr = 5, /* Line Status */ + Msr = 6, /* Modem Status */ + Div = 7, /* Divisor */ + Usr = 8, /* Status */ +}; + +enum { /* Fcr */ + FIFOena = 0x01, /* FIFO enable */ + FIFOrclr = 0x02, /* clear Rx FIFO */ + FIFOtclr = 0x04, /* clear Tx FIFO */ + FIFO1 = 0x00, /* Rx FIFO trigger level 1 byte */ + FIFO4 = 0x40, /* 4 bytes */ + FIFO8 = 0x80, /* 8 bytes */ + FIFO14 = 0xC0, /* 14 bytes */ +}; + +enum { /* Lcr */ + Wls5 = 0x00, /* Word Length Select 5 bits/byte */ + Wls6 = 0x01, /* 6 bits/byte */ + Wls7 = 0x02, /* 7 bits/byte */ + Wls8 = 0x03, /* 8 bits/byte */ + WlsMASK = 0x03, + Stb = 0x04, /* 2 stop bits */ + Pen = 0x08, /* Parity Enable */ + Eps = 0x10, /* Even Parity Select */ + Stp = 0x20, /* Stick Parity */ + Brk = 0x40, /* Break */ + Dlab = 0x80, /* Divisor Latch Access Bit */ +}; + +enum { /* Mcr */ + Dtr = 0x01, /* Data Terminal Ready */ + Rts = 0x02, /* Ready To Send */ + Out1 = 0x04, /* UART OUT1 asserted */ + Out2 = 0x08, /* UART OUT2 asserted */ + Dm = 0x10, /* Diagnostic Mode loopback */ +}; + +enum { /* Lsr */ + Dr = 0x01, /* Data Ready */ + Oe = 0x02, /* Overrun Error */ + Pe = 0x04, /* Parity Error */ + Fe = 0x08, /* Framing Error */ + Bi = 0x10, /* Break Interrupt */ + Thre = 0x20, /* Thr Empty */ + Temt = 0x40, /* Tramsmitter Empty */ + FIFOerr = 0x80, /* error in receiver FIFO */ + LsrInput = FIFOerr|Oe|Pe|Fe|Dr|Bi, /* input status only */ +}; + +enum { /* Msr */ + Dcts = 0x01, /* Delta Cts */ + Ddsr = 0x02, /* Delta Dsr */ + Teri = 0x04, /* Trailing Edge of Ri */ + Ddcd = 0x08, /* Delta Dcd */ + Cts = 0x10, /* Clear To Send */ + Dsr = 0x20, /* Data Set Ready */ + Ri = 0x40, /* Ring Indicator */ + Dcd = 0x80, /* Data Set Ready */ +}; + +enum { /* Usr */ + Uti = 0x01, /* INTST[9]=1=> =1, interrupt is timeout; =0, receive FIFO trigger */ +}; + +typedef struct Ctlr { + ulong* regs; + int irq; + int iena; + + Lock; + int fena; +} Ctlr; + +extern PhysUart ks8695physuart; + + +static Ctlr ks8695_ctlr[1] = { +{ .regs = (ulong*)PHYSUART, + .irq = IRQuts, /* base: ts then rs, ls, ms */ +}, +}; + +static Uart ks8695_uart[1] = { +{ .regs = &ks8695_ctlr[0], + .name = "eia0", + .freq = UartFREQ, + .phys = &ks8695physuart, + .special= 0, + .next = nil, }, +}; + +#define csr8r(c, r) ((c)->regs[(r)]) +#define csr8w(c, r, v) ((c)->regs[(r)] = (v)) + +static long +ks8695_status(Uart* uart, void* buf, long n, long offset) +{ + char *p; + Ctlr *ctlr; + uchar ier, lcr, mcr, msr; + + ctlr = uart->regs; + p = malloc(READSTR); + mcr = csr8r(ctlr, Mcr); + msr = csr8r(ctlr, Msr); + ier = INTRREG->en; + lcr = csr8r(ctlr, Lcr); + snprint(p, READSTR, + "b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d ier=%ux\n" + "dev(%d) type(%d) framing(%d) overruns(%d)%s%s%s%s\n", + + uart->baud, + uart->hup_dcd, + (msr & Dsr) != 0, + uart->hup_dsr, + (lcr & WlsMASK) + 5, + (ier & (1<fena, + ier, + + uart->dev, + uart->type, + uart->ferr, + uart->oerr, + (msr & Cts) ? " cts": "", + (msr & Dsr) ? " dsr": "", + (msr & Dcd) ? " dcd": "", + (msr & Ri) ? " ring": "" + ); + n = readstr(offset, buf, n, p); + free(p); + + return n; +} + +static void +ks8695_fifo(Uart* uart, int level) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + + /* + * Changing the FIFOena bit in Fcr flushes data + * from both receive and transmit FIFOs; there's + * no easy way to guarantee not losing data on + * the receive side, but it's possible to wait until + * the transmitter is really empty. + */ + ilock(ctlr); + while(!(csr8r(ctlr, Lsr) & Temt)) + ; + + /* + * Set the trigger level, default is the max. + * value. + */ + ctlr->fena = level; + switch(level){ + case 0: + break; + case 1: + level = FIFO1|FIFOena; + break; + case 4: + level = FIFO4|FIFOena; + break; + case 8: + level = FIFO8|FIFOena; + break; + default: + level = FIFO14|FIFOena; + break; + } + csr8w(ctlr, Fcr, level); + iunlock(ctlr); +} + +static void +ks8695_dtr(Uart* uart, int on) +{ + Ctlr *ctlr; + int r; + + /* + * Toggle DTR. + */ + ctlr = uart->regs; + r = csr8r(ctlr, Mcr); + if(on) + r |= Dtr; + else + r &= ~Dtr; + csr8w(ctlr, Mcr, r); +} + +static void +ks8695_rts(Uart* uart, int on) +{ + Ctlr *ctlr; + int r; + + /* + * Toggle RTS. + */ + ctlr = uart->regs; + r = csr8r(ctlr, Mcr); + if(on) + r |= Rts; + else + r &= ~Rts; + csr8w(ctlr, Mcr, r); +} + +static void +ks8695_modemctl(Uart* uart, int on) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + ilock(&uart->tlock); + if(on){ + INTRREG->en |= 1<modem = 1; + uart->cts = csr8r(ctlr, Msr) & Cts; + } + else{ + INTRREG->en &= ~(1<modem = 0; + uart->cts = 1; + } + iunlock(&uart->tlock); + + /* modem needs fifo */ + (*uart->phys->fifo)(uart, on); +} + +static int +ks8695_parity(Uart* uart, int parity) +{ + int lcr; + Ctlr *ctlr; + + ctlr = uart->regs; + lcr = csr8r(ctlr, Lcr) & ~(Eps|Pen); + + switch(parity){ + case 'e': + lcr |= Eps|Pen; + break; + case 'o': + lcr |= Pen; + break; + case 'n': + default: + break; + } + csr8w(ctlr, Lcr, lcr); + + uart->parity = parity; + + return 0; +} + +static int +ks8695_stop(Uart* uart, int stop) +{ + int lcr; + Ctlr *ctlr; + + ctlr = uart->regs; + lcr = csr8r(ctlr, Lcr); + switch(stop){ + case 1: + lcr &= ~Stb; + break; + case 2: + lcr |= Stb; + break; + default: + return -1; + } + csr8w(ctlr, Lcr, lcr); + uart->stop = stop; + return 0; +} + +static int +ks8695_bits(Uart* uart, int bits) +{ + int lcr; + Ctlr *ctlr; + + ctlr = uart->regs; + lcr = csr8r(ctlr, Lcr) & ~WlsMASK; + + switch(bits){ + case 5: + lcr |= Wls5; + break; + case 6: + lcr |= Wls6; + break; + case 7: + lcr |= Wls7; + break; + case 8: + lcr |= Wls8; + break; + default: + return -1; + } + csr8w(ctlr, Lcr, lcr); + + uart->bits = bits; + + return 0; +} + +static int +ks8695_baud(Uart* uart, int baud) +{ + ulong bgc; + Ctlr *ctlr; + + if(uart->freq == 0 || baud <= 0) + return -1; + ctlr = uart->regs; + bgc = (uart->freq+baud-1)/baud; + csr8w(ctlr, Div, bgc); + uart->baud = baud; + return 0; +} + +static void +ks8695_break(Uart* uart, int ms) +{ + Ctlr *ctlr; + int lcr; + + /* + * Send a break. + */ + if(ms == 0) + ms = 200; + + ctlr = uart->regs; + lcr = csr8r(ctlr, Lcr); + csr8w(ctlr, Lcr, lcr|Brk); + tsleep(&up->sleep, return0, 0, ms); + csr8w(ctlr, Lcr, lcr); +} + +static void +ks8695_kick(Uart* uart) +{ + int i; + Ctlr *ctlr; + + if(uart->cts == 0 || uart->blocked) + return; + + ctlr = uart->regs; + for(i = 0; i < 16; i++){ + if(!(csr8r(ctlr, Lsr) & Thre)) + break; + if(uart->op >= uart->oe && uartstageoutput(uart) == 0) + break; + csr8w(ctlr, Thr, *uart->op++); + } +} + +static void +ks8695_modemintr(Ureg*, void *arg) +{ + Ctlr *ctlr; + Uart *uart; + int old, r; + + uart = arg; + ctlr = uart->regs; + r = csr8r(ctlr, Msr); + if(r & Dcts){ + ilock(&uart->tlock); + old = uart->cts; + uart->cts = r & Cts; + if(old == 0 && uart->cts) + uart->ctsbackoff = 2; + iunlock(&uart->tlock); + } + if(r & Ddsr){ + old = r & Dsr; + if(uart->hup_dsr && uart->dsr && !old) + uart->dohup = 1; + uart->dsr = old; + } + if(r & Ddcd){ + old = r & Dcd; + if(uart->hup_dcd && uart->dcd && !old) + uart->dohup = 1; + uart->dcd = old; + } +} + +static void +ks8695_rxintr(Ureg*, void* arg) +{ + Ctlr *ctlr; + Uart *uart; + int lsr, r; + + /* handle line error status here as well */ + uart = arg; + ctlr = uart->regs; + while((lsr = csr8r(ctlr, Lsr) & LsrInput) != 0){ + /* + * Consume any received data. + * If the received byte came in with a break, + * parity or framing error, throw it away; + * overrun is an indication that something has + * already been tossed. + */ + if(lsr & (FIFOerr|Oe)) + uart->oerr++; + if(lsr & Pe) + uart->perr++; + if(lsr & Fe) + uart->ferr++; + if(lsr & Dr){ + r = csr8r(ctlr, Rbr); + if(!(lsr & (Bi|Fe|Pe))) + uartrecv(uart, r); + } + } +} + +static void +ks8695_txintr(Ureg*, void* arg) +{ + uartkick(arg); +} + +static void +ks8695_disable(Uart* uart) +{ + Ctlr *ctlr; + + /* + * Turn off DTR and RTS, disable interrupts and fifos. + */ + (*uart->phys->dtr)(uart, 0); + (*uart->phys->rts)(uart, 0); + (*uart->phys->fifo)(uart, 0); + + ctlr = uart->regs; + + if(ctlr->iena != 0){ + intrdisable(IRQ, ctlr->irq, ks8695_txintr, uart, uart->name); + intrdisable(IRQ, ctlr->irq+1, ks8695_rxintr, uart, uart->name); + intrdisable(IRQ, ctlr->irq+2, ks8695_rxintr, uart, uart->name); + intrdisable(IRQ, ctlr->irq+3, ks8695_modemintr, uart, uart->name); + ctlr->iena = 0; + } +} + +static void +ks8695_enable(Uart* uart, int ie) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + + /* + * Enable interrupts and turn on DTR and RTS. + * Be careful if this is called to set up a polled serial line + * early on not to try to enable interrupts as interrupt- + * -enabling mechanisms might not be set up yet. + */ + if(ctlr->iena == 0 && ie){ + intrenable(IRQ, ctlr->irq, ks8695_txintr, uart, uart->name); + intrenable(IRQ, ctlr->irq+1, ks8695_rxintr, uart, uart->name); + intrenable(IRQ, ctlr->irq+2, ks8695_rxintr, uart, uart->name); + intrenable(IRQ, ctlr->irq+3, ks8695_modemintr, uart, uart->name); + ctlr->iena = 1; + } + + (*uart->phys->dtr)(uart, 1); + (*uart->phys->rts)(uart, 1); +} + +static Uart* +ks8695_pnp(void) +{ + return ks8695_uart; +} + +static int +ks8695_getc(Uart *uart) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + while(!(csr8r(ctlr, Lsr)&Dr)) + delay(1); + return csr8r(ctlr, Rbr); +} + +static void +ks8695_putc(Uart *uart, int c) +{ + serialputc(c); +#ifdef ROT + int i; + Ctlr *ctlr; + + ctlr = uart->regs; + for(i = 0; !(csr8r(ctlr, Lsr)&Thre) && i < 256; i++) + delay(1); + csr8w(ctlr, Thr, c); + if(c == '\n') + while((csr8r(ctlr, Lsr) & Temt) == 0){ /* let fifo drain */ + /* skip */ + } +#endif +} + +PhysUart ks8695physuart = { + .name = "ks8695", + .pnp = ks8695_pnp, + .enable = ks8695_enable, + .disable = ks8695_disable, + .kick = ks8695_kick, + .dobreak = ks8695_break, + .baud = ks8695_baud, + .bits = ks8695_bits, + .stop = ks8695_stop, + .parity = ks8695_parity, + .modemctl = ks8695_modemctl, + .rts = ks8695_rts, + .dtr = ks8695_dtr, + .status = ks8695_status, + .fifo = ks8695_fifo, + .getc = ks8695_getc, + .putc = ks8695_putc, +}; + +void +uartconsole(void) +{ + Uart *uart; + + uart = &ks8695_uart[0]; + (*uart->phys->enable)(uart, 0); + uartctl(uart, "b38400 l8 pn s1"); + consuart = uart; + uart->console = 1; +} + +#define UR(p,r) ((ulong*)(p))[r] + +void +serialputc(int c) +{ + ulong *p; + + if(c == 0) + return; + p = (ulong*)PHYSUART; + while((UR(p,Lsr) & Thre) == 0){ + /* skip */ + } + UR(p,Thr) = c; + if(c == '\n') + while((UR(p,Lsr) & Temt) == 0){ /* let fifo drain */ + /* skip */ + } +} + +/* + * for iprint, just write it + */ +void +serialputs(char *data, int len) +{ + ulong *p; + + p = (ulong*)PHYSUART; + while(--len >= 0){ + if(*data == '\n') + serialputc('\r'); + serialputc(*data++); + } + while((UR(p,Lsr) & Temt) == 0){ /* let fifo drain */ + /* skip */ + } +} +void (*serwrite)(char*, int) = serialputs; diff --git a/os/manga/usb.h b/os/manga/usb.h new file mode 100644 index 00000000..0aad42c5 --- /dev/null +++ b/os/manga/usb.h @@ -0,0 +1,160 @@ +typedef struct Ctlr Ctlr; +typedef struct Endpt Endpt; +typedef struct Udev Udev; +typedef struct Usbhost Usbhost; + +enum +{ + MaxUsb = 4, /* max number of USB Host Controller Interfaces (Usbhost*) */ + MaxUsbDev = 32, /* max number of attached USB devices, including root hub (Udev*) */ + + /* + * USB packet definitions... + */ + TokIN = 0x69, + TokOUT = 0xE1, + TokSETUP = 0x2D, + + /* request type */ + RH2D = 0<<7, + RD2H = 1<<7, + Rstandard = 0<<5, + Rclass = 1<<5, + Rvendor = 2<<5, + Rdevice = 0, + Rinterface = 1, + Rendpt = 2, + Rother = 3, +}; + +#define Class(csp) ((csp)&0xff) +#define Subclass(csp) (((csp)>>8)&0xff) +#define Proto(csp) (((csp)>>16)&0xff) +#define CSP(c, s, p) ((c) | ((s)<<8) | ((p)<<16)) + +/* + * device endpoint + */ +struct Endpt +{ + Ref; + Lock; + int x; /* index in Udev.ep */ + int id; /* hardware endpoint address */ + int maxpkt; /* maximum packet size (from endpoint descriptor) */ + int data01; /* 0=DATA0, 1=DATA1 */ + uchar eof; + ulong csp; + uchar mode; /* OREAD, OWRITE, ORDWR */ + uchar nbuf; /* number of buffers allowed */ + uchar iso; + uchar debug; + uchar active; /* listed for examination by interrupts */ + int setin; + /* ISO related: */ + int hz; + int remain; /* for packet size calculations */ + int samplesz; + int sched; /* schedule index; -1 if undefined or aperiodic */ + int pollms; /* polling interval in msec */ + int psize; /* (remaining) size of this packet */ + int off; /* offset into packet */ + /* Real-time iso stuff */ + ulong foffset; /* file offset (to detect seeks) */ + ulong poffset; /* offset of next packet to be queued */ + ulong toffset; /* offset associated with time */ + vlong time; /* timeassociated with offset */ + int buffered; /* bytes captured but unread, or written but unsent */ + /* end ISO stuff */ + + Udev* dev; /* owning device */ + + ulong nbytes; + ulong nblocks; + + void *private; + + // all the rest could (should?) move to the driver private structure; except perhaps err + QLock rlock; + Rendez rr; + Queue* rq; + QLock wlock; + Rendez wr; + Queue* wq; + + int ntd; + char* err; // needs to be global for unstall; fix? + + Endpt* activef; /* active endpoint list */ +}; + +/* device parameters */ +enum +{ + /* Udev.state */ + Disabled = 0, + Attached, + Enabled, + Assigned, + Configured, + + /* Udev.class */ + Noclass = 0, + Hubclass = 9, +}; + +/* + * active USB device + */ +struct Udev +{ + Ref; + Lock; + Usbhost *uh; + int x; /* index in usbdev[] */ + int busy; + int state; + int id; + uchar port; /* port number on connecting hub */ + ulong csp; + ushort vid; /* vendor id */ + ushort did; /* product id */ + int ls; + int npt; + Endpt* ep[16]; /* active end points */ + Udev* ports; /* active ports, if hub */ + Udev* next; /* next device on this hub */ +}; + +/* + * One of these per active Host Controller Interface (HCI) + */ +struct Usbhost +{ + ISAConf; /* hardware info */ + int tbdf; /* type+busno+devno+funcno */ + + QLock; /* protects namespace state */ + int idgen; /* version number to distinguish new connections */ + Udev* dev[MaxUsbDev]; /* device endpoints managed by this HCI */ + + void (*init)(Usbhost*); + void (*interrupt)(Ureg*, void*); + + void (*portinfo)(Usbhost*, char*, char*); + void (*portreset)(Usbhost*, int); + void (*portenable)(Usbhost*, int, int); + + void (*epalloc)(Usbhost*, Endpt*); + void (*epfree)(Usbhost*, Endpt*); + void (*epopen)(Usbhost*, Endpt*); + void (*epclose)(Usbhost*, Endpt*); + void (*epmode)(Usbhost*, Endpt*); + + long (*read)(Usbhost*, Endpt*, void*, long, vlong); + long (*write)(Usbhost*, Endpt*, void*, long, vlong, int); + + void *ctlr; +}; + +extern void addusbtype(char*, int(*)(Usbhost*)); diff --git a/os/manga/usbuhci.c b/os/manga/usbuhci.c new file mode 100644 index 00000000..12140d04 --- /dev/null +++ b/os/manga/usbuhci.c @@ -0,0 +1,1556 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "usb.h" + +#define todget(x) 0 /* TO DO */ +#define XPRINT if(debug)iprint + +static int Chatty = 0; +static int debug = 0; + +static char Estalled[] = "usb endpoint stalled"; + +/* + * UHCI interface registers and bits + */ +enum +{ + /* i/o space */ + Cmd = 0, + Status = 2, + Usbintr = 4, + Frnum = 6, + Flbaseadd = 8, + SOFMod = 0xC, + Portsc0 = 0x10, + Portsc1 = 0x12, + + /* port status */ + Suspend = 1<<12, + PortReset = 1<<9, + SlowDevice = 1<<8, + ResumeDetect = 1<<6, + PortChange = 1<<3, /* write 1 to clear */ + PortEnable = 1<<2, + StatusChange = 1<<1, /* write 1 to clear */ + DevicePresent = 1<<0, + + NFRAME = 1024, + FRAMESIZE= NFRAME*sizeof(ulong), /* fixed by hardware; aligned to same */ + + Vf = 1<<2, /* TD only */ + IsQH = 1<<1, + Terminate = 1<<0, + + /* TD.status */ + SPD = 1<<29, + ErrLimit0 = 0<<27, + ErrLimit1 = 1<<27, + ErrLimit2 = 2<<27, + ErrLimit3 = 3<<27, + LowSpeed = 1<<26, + IsoSelect = 1<<25, + IOC = 1<<24, + Active = 1<<23, + Stalled = 1<<22, + DataBufferErr = 1<<21, + Babbling = 1<<20, + NAKed = 1<<19, + CRCorTimeout = 1<<18, + BitstuffErr = 1<<17, + AnyError = (Stalled | DataBufferErr | Babbling | NAKed | CRCorTimeout | BitstuffErr), + + /* TD.dev */ + IsDATA1 = 1<<19, + + /* TD.flags (software) */ + CancelTD= 1<<0, + IsoClean= 1<<2, +}; + +static struct +{ + int bit; + char *name; +} +portstatus[] = +{ + { Suspend, "suspend", }, + { PortReset, "reset", }, + { SlowDevice, "lowspeed", }, + { ResumeDetect, "resume", }, + { PortChange, "portchange", }, + { PortEnable, "enable", }, + { StatusChange, "statuschange", }, + { DevicePresent, "present", }, +}; + +typedef struct Ctlr Ctlr; +typedef struct Endptx Endptx; +typedef struct QH QH; +typedef struct TD TD; + +/* + * software structures + */ +struct Ctlr +{ + Lock; /* protects state shared with interrupt (eg, free list) */ + Ctlr* next; + Pcidev* pcidev; + int active; + + int io; + ulong* frames; /* frame list */ + ulong* frameld; /* real time load on each of the frame list entries */ + QLock resetl; /* lock controller during USB reset */ + + TD* tdpool; + TD* freetd; + QH* qhpool; + QH* freeqh; + + QH* ctlq; /* queue for control i/o */ + QH* bwsop; /* empty bandwidth sop (to PIIX4 errata specifications) */ + QH* bulkq; /* queue for bulk i/o (points back to bandwidth sop) */ + QH* recvq; /* receive queues for bulk i/o */ + + Udev* ports[2]; + + struct { + Lock; + Endpt* f; + } activends; + + long usbints; /* debugging */ + long framenumber; + long frameptr; + long usbbogus; +}; + +#define IN(x) ins(ctlr->io+(x)) +#define OUT(x, v) outs(ctlr->io+(x), (v)) + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +struct Endptx +{ + QH* epq; /* queue of TDs for this endpoint */ + + /* ISO related: */ + void* tdalloc; + void* bpalloc; + uchar* bp0; /* first block in array */ + TD * td0; /* first td in array */ + TD * etd; /* pointer into circular list of TDs for isochronous ept */ + TD * xtd; /* next td to be cleaned */ +}; + +/* + * UHCI hardware structures, aligned on 16-byte boundary + */ +struct TD +{ + ulong link; + ulong status; /* controller r/w */ + ulong dev; + ulong buffer; + + /* software */ + ulong flags; + union{ + Block* bp; /* non-iso */ + ulong offset; /* iso */ + }; + Endpt* ep; + TD* next; +}; +#define TFOL(p) ((TD*)KADDR((ulong)(p) & ~(0xF|PCIWINDOW))) + +struct QH +{ + ulong head; + ulong entries; /* address of next TD or QH to process (updated by controller) */ + + /* software */ + QH* hlink; + TD* first; + QH* next; /* free list */ + TD* last; + ulong _d1; /* fillers */ + ulong _d2; +}; +#define QFOL(p) ((QH*)KADDR((ulong)(p) & ~(0xF|PCIWINDOW))) + +static TD * +alloctd(Ctlr *ctlr) +{ + TD *t; + + ilock(ctlr); + t = ctlr->freetd; + if(t == nil) + panic("alloctd"); /* TO DO */ + ctlr->freetd = t->next; + t->next = nil; + iunlock(ctlr); + t->ep = nil; + t->bp = nil; + t->status = 0; + t->link = Terminate; + t->buffer = 0; + t->flags = 0; + return t; +} + +static void +freetd(Ctlr *ctlr, TD *t) +{ + t->ep = nil; + if(t->bp) + freeb(t->bp); + t->bp = nil; + ilock(ctlr); + t->buffer = 0xdeadbeef; + t->next = ctlr->freetd; + ctlr->freetd = t; + iunlock(ctlr); +} + +static void +dumpdata(Block *b, int n) +{ + int i; + + XPRINT("\tb %8.8lux[%d]: ", (ulong)b->rp, n); + if(n > 16) + n = 16; + for(i=0; irp[i]); + XPRINT("\n"); +} + +static void +dumptd(TD *t, int follow) +{ + int i, n; + char buf[20], *s; + TD *t0; + + t0 = t; + while(t){ + i = t->dev & 0xFF; + if(i == TokOUT || i == TokSETUP) + n = ((t->dev>>21) + 1) & 0x7FF; + else if((t->status & Active) == 0) + n = (t->status + 1) & 0x7FF; + else + n = 0; + s = buf; + if(t->status & Active) + *s++ = 'A'; + if(t->status & Stalled) + *s++ = 'S'; + if(t->status & DataBufferErr) + *s++ = 'D'; + if(t->status & Babbling) + *s++ = 'B'; + if(t->status & NAKed) + *s++ = 'N'; + if(t->status & CRCorTimeout) + *s++ = 'T'; + if(t->status & BitstuffErr) + *s++ = 'b'; + if(t->status & LowSpeed) + *s++ = 'L'; + *s = 0; + XPRINT("td %8.8lux: ", t); + XPRINT("l=%8.8lux s=%8.8lux d=%8.8lux b=%8.8lux %8.8lux f=%8.8lux\n", + t->link, t->status, t->dev, t->buffer, t->bp?(ulong)t->bp->rp:0, t->flags); + XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n", + buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1); + if(debug && t->bp && (t->flags & CancelTD) == 0) + dumpdata(t->bp, n); + if(!follow || t->link & Terminate || t->link & IsQH) + break; + t = TFOL(t->link); + if(t == t0) + break; /* looped */ + } +} + +static TD * +alloctde(Ctlr *ctlr, Endpt *e, int pid, int n) +{ + TD *t; + int tog, id; + + t = alloctd(ctlr); + id = (e->x<<7)|(e->dev->x&0x7F); + tog = 0; + if(e->data01 && pid != TokSETUP) + tog = IsDATA1; + t->ep = e; + t->status = ErrLimit3 | Active | IOC; /* or put IOC only on last? */ + if(e->dev->ls) + t->status |= LowSpeed; + t->dev = ((n-1)<<21) | ((id&0x7FF)<<8) | pid | tog; + return t; +} + +static QH * +allocqh(Ctlr *ctlr) +{ + QH *qh; + + ilock(ctlr); + qh = ctlr->freeqh; + if(qh == nil) + panic("allocqh"); /* TO DO */ + ctlr->freeqh = qh->next; + qh->next = nil; + iunlock(ctlr); + qh->head = Terminate; + qh->entries = Terminate; + qh->hlink = nil; + qh->first = nil; + qh->last = nil; + return qh; +} + +static void +freeqh(Ctlr *ctlr, QH *qh) +{ + ilock(ctlr); + qh->next = ctlr->freeqh; + ctlr->freeqh = qh; + iunlock(ctlr); +} + +static void +dumpqh(QH *q) +{ + int i; + QH *q0; + + q0 = q; + for(i = 0; q != nil && i < 10; i++){ + XPRINT("qh %8.8lux: %8.8lux %8.8lux\n", q, q->head, q->entries); + if((q->entries & Terminate) == 0) + dumptd(TFOL(q->entries), 1); + if(q->head & Terminate) + break; + if((q->head & IsQH) == 0){ + XPRINT("head:"); + dumptd(TFOL(q->head), 1); + break; + } + q = QFOL(q->head); + if(q == q0) + break; /* looped */ + } +} + +static void +queuetd(Ctlr *ctlr, QH *q, TD *t, int vf, char *why) +{ + TD *lt; + + for(lt = t; lt->next != nil; lt = lt->next) + lt->link = PCIWADDR(lt->next) | vf; + lt->link = Terminate; + ilock(ctlr); + XPRINT("queuetd %s: t=%p lt=%p q=%p first=%p last=%p entries=%.8lux\n", + why, t, lt, q, q->first, q->last, q->entries); + if(q->first != nil){ + q->last->link = PCIWADDR(t) | vf; + q->last->next = t; + }else{ + q->first = t; + q->entries = PCIWADDR(t); + } + q->last = lt; + XPRINT(" t=%p q=%p first=%p last=%p entries=%.8lux\n", + t, q, q->first, q->last, q->entries); + dumpqh(q); + iunlock(ctlr); +} + +static void +cleantd(Ctlr *ctlr, TD *t, int discard) +{ + Block *b; + int n, err; + + XPRINT("cleanTD: %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); + if(t->ep != nil && t->ep->debug) + dumptd(t, 0); + if(t->status & Active) + panic("cleantd Active"); + err = t->status & (AnyError&~NAKed); + /* TO DO: on t->status&AnyError, q->entries will not have advanced */ + if (err) { + XPRINT("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); +// print("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); + } + switch(t->dev&0xFF){ + case TokIN: + if(discard || (t->flags & CancelTD) || t->ep == nil || t->ep->x!=0&&err){ + if(t->ep != nil){ + if(err != 0) + t->ep->err = err==Stalled? Estalled: Eio; + wakeup(&t->ep->rr); /* in case anyone cares */ + } + break; + } + b = t->bp; + n = (t->status + 1) & 0x7FF; + if(n > b->lim - b->wp) + n = 0; + b->wp += n; + if(Chatty) + dumpdata(b, n); + t->bp = nil; + t->ep->nbytes += n; + t->ep->nblocks++; + qpass(t->ep->rq, b); /* TO DO: flow control */ + wakeup(&t->ep->rr); /* TO DO */ + break; + case TokSETUP: + XPRINT("cleanTD: TokSETUP %lux\n", &t->ep); + /* don't really need to wakeup: subsequent IN or OUT gives status */ + if(t->ep != nil) { + wakeup(&t->ep->wr); /* TO DO */ + XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr); + } + break; + case TokOUT: + /* TO DO: mark it done somewhere */ + XPRINT("cleanTD: TokOut %lux\n", &t->ep); + if(t->ep != nil){ + if(t->bp){ + n = BLEN(t->bp); + t->ep->nbytes += n; + t->ep->nblocks++; + } + if(t->ep->x!=0 && err != 0) + t->ep->err = err==Stalled? Estalled: Eio; + if(--t->ep->ntd < 0) + panic("cleantd ntd"); + wakeup(&t->ep->wr); /* TO DO */ + XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr); + } + break; + } + freetd(ctlr, t); +} + +static void +cleanq(Ctlr *ctlr, QH *q, int discard, int vf) +{ + TD *t, *tp; + + ilock(ctlr); + tp = nil; + for(t = q->first; t != nil;){ + XPRINT("cleanq: %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer, t->flags, t->next); + if(t->status & Active){ + if(t->status & NAKed){ + t->status = (t->status & ~NAKed) | IOC; /* ensure interrupt next frame */ + tp = t; + t = t->next; + continue; + } + if(t->flags & CancelTD){ + XPRINT("cancelTD: %8.8lux\n", (ulong)t); + t->status = (t->status & ~Active) | IOC; /* ensure interrupt next frame */ + tp = t; + t = t->next; + continue; + } + tp = t; + t = t->next; + continue; + } + t->status &= ~IOC; + if (tp == nil) { + q->first = t->next; + if(q->first != nil) + q->entries = PCIWADDR(q->first); + else + q->entries = Terminate; + } else { + tp->next = t->next; + if (t->next != nil) + tp->link = PCIWADDR(t->next) | vf; + else + tp->link = Terminate; + } + if (q->last == t) + q->last = tp; + iunlock(ctlr); + cleantd(ctlr, t, discard); + ilock(ctlr); + if (tp) + t = tp->next; + else + t = q->first; + XPRINT("t = %8.8lux\n", t); + dumpqh(q); + } + if(q->first && q->entries != PCIWADDR(q->first)){ + ctlr->usbbogus++; + q->entries = PCIWADDR(q->first); + } + iunlock(ctlr); +} + +static void +canceltds(Ctlr *ctlr, QH *q, Endpt *e) +{ + TD *t; + + if(q != nil){ + ilock(ctlr); + for(t = q->first; t != nil; t = t->next) + if(t->ep == e) + t->flags |= CancelTD; + iunlock(ctlr); + XPRINT("cancel:\n"); + dumpqh(q); + } +} + +static void +eptcancel(Ctlr *ctlr, Endpt *e) +{ + Endptx *x; + + if(e == nil) + return; + x = e->private; + canceltds(ctlr, x->epq, e); + canceltds(ctlr, ctlr->ctlq, e); + canceltds(ctlr, ctlr->bulkq, e); +} + +static void +eptactivate(Ctlr *ctlr, Endpt *e) +{ + ilock(&ctlr->activends); + if(e->active == 0){ + XPRINT("activate 0x%p\n", e); + e->active = 1; + e->activef = ctlr->activends.f; + ctlr->activends.f = e; + } + iunlock(&ctlr->activends); +} + +static void +eptdeactivate(Ctlr *ctlr, Endpt *e) +{ + Endpt **l; + + /* could be O(1) but not worth it yet */ + ilock(&ctlr->activends); + if(e->active){ + e->active = 0; + XPRINT("deactivate 0x%p\n", e); + for(l = &ctlr->activends.f; *l != e; l = &(*l)->activef) + if(*l == nil){ + iunlock(&ctlr->activends); + panic("usb eptdeactivate"); + } + *l = e->activef; + } + iunlock(&ctlr->activends); +} + +static void +queueqh(Ctlr *ctlr, QH *qh) +{ + QH *q; + + // See if it's already queued + for (q = ctlr->recvq->next; q; q = q->hlink) + if (q == qh) + return; + if ((qh->hlink = ctlr->recvq->next) == nil) + qh->head = Terminate; + else + qh->head = PCIWADDR(ctlr->recvq->next) | IsQH; + ctlr->recvq->next = qh; + ctlr->recvq->entries = PCIWADDR(qh) | IsQH; +} + +static QH* +qxmit(Ctlr *ctlr, Endpt *e, Block *b, int pid) +{ + TD *t; + int n, vf; + QH *qh; + Endptx *x; + + x = e->private; + if(b != nil){ + n = BLEN(b); + dcflush(b->rp, n); + t = alloctde(ctlr, e, pid, n); + t->bp = b; + t->buffer = PCIWADDR(b->rp); + }else + t = alloctde(ctlr, e, pid, 0); + ilock(ctlr); + e->ntd++; + iunlock(ctlr); + if(e->debug) pprint("QTD: %8.8lux n=%ld\n", t, b?BLEN(b): 0); + vf = 0; + if(e->x == 0){ + qh = ctlr->ctlq; + vf = 0; + }else if((qh = x->epq) == nil || e->mode != OWRITE){ + qh = ctlr->bulkq; + vf = Vf; + } + queuetd(ctlr, qh, t, vf, "qxmit"); + return qh; +} + +/* + * allocate receive buffer space on cache-line boundaries + */ +static Block* +clallocb(long n) +{ + Block *b; + + b = allocb(n+CACHELINESZ-1); + if(b == nil) + return b; + dcflush(b->base, BALLOC(b)); + b->wp = b->rp = (uchar*)(((ulong)b->base + CACHELINESZ - 1) & ~(CACHELINESZ-1)); + return b; +} + +static QH* +qrcv(Ctlr *ctlr, Endpt *e) +{ + TD *t; + Block *b; + QH *qh; + int vf; + Endptx *x; + + x = e->private; + t = alloctde(ctlr, e, TokIN, e->maxpkt); + b = clallocb(e->maxpkt); + t->bp = b; + t->buffer = PCIWADDR(b->wp); + vf = 0; + if(e->x == 0){ + qh = ctlr->ctlq; + }else if((qh = x->epq) == nil || e->mode != OREAD){ + qh = ctlr->bulkq; + vf = Vf; + } + queuetd(ctlr, qh, t, vf, "qrcv"); + return qh; +} + +static int +usbsched(Ctlr *ctlr, int pollms, ulong load) +{ + int i, d, q; + ulong best, worst; + + best = 1000000; + q = -1; + for (d = 0; d < pollms; d++){ + worst = 0; + for (i = d; i < NFRAME; i++){ + if (ctlr->frameld[i] + load > worst) + worst = ctlr->frameld[i] + load; + } + if (worst < best){ + best = worst; + q = d; + } + } + return q; +} + +static int +schedendpt(Ctlr *ctlr, Endpt *e) +{ + TD *td; + Endptx *x; + uchar *bp; + int i, id, ix, size, frnum; + + if(!e->iso || e->sched >= 0) + return 0; + + if (e->active){ + return -1; + } + e->off = 0; + e->sched = usbsched(ctlr, e->pollms, e->maxpkt); + if(e->sched < 0) + return -1; + + x = e->private; + if (x->tdalloc || x->bpalloc) + panic("usb: tdalloc/bpalloc"); + x->tdalloc = mallocz(0x10 + NFRAME*sizeof(TD) + CACHELINESZ, 1); + x->bpalloc = mallocz(0x10 + e->maxpkt*NFRAME/e->pollms + CACHELINESZ, 1); + x->td0 = mmucacheinhib((TD*)(((ulong)x->tdalloc + CACHELINESZ-1) & ~(CACHELINESZ-1)), NFRAME*sizeof(TD) + 0x10); + x->bp0 = mmucacheinhib((uchar *)(((ulong)x->bpalloc + CACHELINESZ-1) & ~(CACHELINESZ-1)), e->maxpkt*NFRAME/e->pollms + 0x10); + frnum = (IN(Frnum) + 1) & 0x3ff; + frnum = (frnum & ~(e->pollms - 1)) + e->sched; + x->xtd = &x->td0[(frnum+8)&0x3ff]; /* Next td to finish */ + x->etd = nil; + e->remain = 0; + e->nbytes = 0; + td = x->td0; + for(i = e->sched; i < NFRAME; i += e->pollms){ + bp = x->bp0 + e->maxpkt*i/e->pollms; + td->buffer = PCIWADDR(bp); + td->ep = e; + td->next = &td[1]; + ctlr->frameld[i] += e->maxpkt; + td++; + } + td[-1].next = x->td0; + for(i = e->sched; i < NFRAME; i += e->pollms){ + ix = (frnum+i) & 0x3ff; + td = &x->td0[ix]; + + id = (e->x<<7)|(e->dev->x&0x7F); + if (e->mode == OREAD) + /* enable receive on this entry */ + td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | TokIN; + else{ + size = (e->hz + e->remain)*e->pollms/1000; + e->remain = (e->hz + e->remain)*e->pollms%1000; + size *= e->samplesz; + td->dev = ((size-1)<<21) | ((id&0x7FF)<<8) | TokOUT; + } + td->status = ErrLimit1 | Active | IsoSelect | IOC; + td->link = ctlr->frames[ix]; + td->flags |= IsoClean; + ctlr->frames[ix] = PCIWADDR(td); + } + return 0; +} + +static void +unschedendpt(Ctlr *ctlr, Endpt *e) +{ + int q; + TD *td; + Endptx *x; + ulong *addr; + + if(!e->iso || e->sched < 0) + return; + + x = e->private; + if (x->tdalloc == nil) + panic("tdalloc"); + for (q = e->sched; q < NFRAME; q += e->pollms){ + td = x->td0++; + addr = &ctlr->frames[q]; + while(*addr != PADDR(td)) { + if(*addr & IsQH) + panic("usb: TD expected"); + addr = &TFOL(*addr)->link; + } + *addr = td->link; + ctlr->frameld[q] -= e->maxpkt; + } + free(x->tdalloc); + free(x->bpalloc); + x->tdalloc = nil; + x->bpalloc = nil; + x->etd = nil; + x->td0 = nil; + e->sched = -1; +} + +static void +epalloc(Usbhost *uh, Endpt *e) +{ + Endptx *x; + + x = malloc(sizeof(Endptx)); + e->private = x; + x->epq = allocqh(uh->ctlr); + if(x->epq == nil) + panic("devendptx"); +} + +static void +epfree(Usbhost *uh, Endpt *e) +{ + Ctlr *ctlr; + Endptx *x; + + ctlr = uh->ctlr; + x = e->private; + if(x->epq != nil) + freeqh(ctlr, x->epq); +} + +static void +epopen(Usbhost *uh, Endpt *e) +{ + Ctlr *ctlr; + + ctlr = uh->ctlr; + if(e->iso && e->active) + error("already open"); + if(schedendpt(ctlr, e) < 0){ + if(e->active) + error("cannot schedule USB endpoint, active"); + else + error("cannot schedule USB endpoint"); + } + eptactivate(ctlr, e); +} + +static void +epclose(Usbhost *uh, Endpt *e) +{ + Ctlr *ctlr; + + ctlr = uh->ctlr; + eptdeactivate(ctlr, e); + unschedendpt(ctlr, e); +} + +static void +epmode(Usbhost *uh, Endpt *e) +{ + Ctlr *ctlr; + Endptx *x; + + ctlr = uh->ctlr; + x = e->private; + if(e->iso) { + if(x->epq != nil) { + freeqh(ctlr, x->epq); + x->epq = nil; + } + } + else { + /* Each bulk device gets a queue head hanging off the + * bulk queue head + */ + if(x->epq == nil) { + x->epq = allocqh(ctlr); + if(x->epq == nil) + panic("epbulk: allocqh"); + } + queueqh(ctlr, x->epq); + } +} + +static int ioport[] = {-1, Portsc0, Portsc1}; + +static void +portreset(Usbhost *uh, int port) +{ + int i, p; + Ctlr *ctlr; + + ctlr = uh->ctlr; + if(port != 1 && port != 2) + error(Ebadarg); + + /* should check that device not being configured on other port? */ + p = ioport[port]; + qlock(&ctlr->resetl); + if(waserror()){ + qunlock(&ctlr->resetl); + nexterror(); + } + XPRINT("r: %x\n", IN(p)); + ilock(ctlr); + OUT(p, PortReset); + delay(12); /* BUG */ + XPRINT("r2: %x\n", IN(p)); + OUT(p, IN(p) & ~PortReset); + XPRINT("r3: %x\n", IN(p)); + OUT(p, IN(p) | PortEnable); + microdelay(64); + for(i=0; i<1000 && (IN(p) & PortEnable) == 0; i++) + ; + XPRINT("r': %x %d\n", IN(p), i); + OUT(p, (IN(p) & ~PortReset)|PortEnable); + iunlock(ctlr); + poperror(); + qunlock(&ctlr->resetl); +} + +static void +portenable(Usbhost *uh, int port, int on) +{ + int w, p; + Ctlr *ctlr; + + ctlr = uh->ctlr; + if(port != 1 && port != 2) + error(Ebadarg); + + /* should check that device not being configured on other port? */ + p = ioport[port]; + qlock(&ctlr->resetl); + if(waserror()){ + qunlock(&ctlr->resetl); + nexterror(); + } + ilock(ctlr); + w = IN(p); + if(on) + w |= PortEnable; + else + w &= ~PortEnable; + OUT(p, w); + microdelay(64); + iunlock(ctlr); + XPRINT("e: %x\n", IN(p)); + poperror(); + qunlock(&ctlr->resetl); +} + +static void +portinfo(Usbhost *uh, char *s, char *se) +{ + int x, i, j; + Ctlr *ctlr; + + ctlr = uh->ctlr; + for(i = 1; i <= 2; i++) { + ilock(ctlr); + x = IN(ioport[i]); + if((x & (PortChange|StatusChange)) != 0) + OUT(ioport[i], x); + iunlock(ctlr); + s = seprint(s, se, "%d %ux", i, x); + for(j = 0; j < nelem(portstatus); j++) { + if((x & portstatus[j].bit) != 0) + s = seprint(s, se, " %s", portstatus[j].name); + } + s = seprint(s, se, "\n"); + } +} + +static void +cleaniso(Endpt *e, int frnum) +{ + TD *td; + int id, n, i; + Endptx *x; + uchar *bp; + + x = e->private; + td = x->xtd; + if (td->status & Active) + return; + id = (e->x<<7)|(e->dev->x&0x7F); + do { + if (td->status & AnyError) + XPRINT("usbisoerror 0x%lux\n", td->status); + n = (td->status + 1) & 0x3ff; + e->nbytes += n; + if ((td->flags & IsoClean) == 0) + e->nblocks++; + if (e->mode == OREAD){ + e->buffered += n; + e->poffset += (td->status + 1) & 0x3ff; + td->offset = e->poffset; + td->dev = ((e->maxpkt -1)<<21) | ((id&0x7FF)<<8) | TokIN; + e->toffset = td->offset; + }else{ + if ((td->flags & IsoClean) == 0){ + e->buffered -= n; + if (e->buffered < 0){ +// print("e->buffered %d?\n", e->buffered); + e->buffered = 0; + } + } + e->toffset = td->offset; + n = (e->hz + e->remain)*e->pollms/1000; + e->remain = (e->hz + e->remain)*e->pollms%1000; + n *= e->samplesz; + td->dev = ((n -1)<<21) | ((id&0x7FF)<<8) | TokOUT; + td->offset = e->poffset; + e->poffset += n; + } + td = td->next; + if (x->xtd == td){ + XPRINT("@"); + break; + } + } while ((td->status & Active) == 0); + e->time = todget(nil); + x->xtd = td; + for (n = 2; n < 4; n++){ + i = ((frnum + n)&0x3ff); + td = x->td0 + i; + bp = x->bp0 + e->maxpkt*i/e->pollms; + if (td->status & Active) + continue; + + if (e->mode == OWRITE){ + if (td == x->etd) { + XPRINT("*"); + memset(bp+e->off, 0, e->maxpkt-e->off); + if (e->off == 0) + td->flags |= IsoClean; + else + e->buffered += (((td->dev>>21) +1) & 0x3ff) - e->off; + x->etd = nil; + }else if ((td->flags & IsoClean) == 0){ + XPRINT("-"); + memset(bp, 0, e->maxpkt); + td->flags |= IsoClean; + } + } else { + /* Unread bytes are now lost */ + e->buffered -= (td->status + 1) & 0x3ff; + } + td->status = ErrLimit1 | Active | IsoSelect | IOC; + } + wakeup(&e->wr); +} + +static void +interrupt(Ureg*, void *a) +{ + QH *q; + Ctlr *ctlr; + Endpt *e; + Endptx *x; + int s, frnum; + Usbhost *uh; + + uh = a; + ctlr = uh->ctlr; + s = IN(Status); + ctlr->frameptr = inl(ctlr->io+Flbaseadd); + ctlr->framenumber = IN(Frnum) & 0x3ff; + OUT(Status, s); + if ((s & 0x1f) == 0) + return; + ctlr->usbints++; + frnum = IN(Frnum) & 0x3ff; + if (s & 0x1a) { + XPRINT("cmd #%x sofmod #%x\n", IN(Cmd), inb(ctlr->io+SOFMod)); + XPRINT("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1)); + } + + ilock(&ctlr->activends); + for(e = ctlr->activends.f; e != nil; e = e->activef) { + x = e->private; + if(!e->iso && x->epq != nil) { + XPRINT("cleanq(ctlr, x->epq, 0, 0)\n"); + cleanq(ctlr, x->epq, 0, 0); + } + if(e->iso) { + XPRINT("cleaniso(e)\n"); + cleaniso(e, frnum); + } + } + iunlock(&ctlr->activends); + XPRINT("cleanq(ctlr, ctlr->ctlq, 0, 0)\n"); + cleanq(ctlr, ctlr->ctlq, 0, 0); + XPRINT("cleanq(ctlr, ctlr->bulkq, 0, Vf)\n"); + cleanq(ctlr, ctlr->bulkq, 0, Vf); + XPRINT("clean recvq\n"); + for (q = ctlr->recvq->next; q; q = q->hlink) { + XPRINT("cleanq(ctlr, q, 0, Vf)\n"); + cleanq(ctlr, q, 0, Vf); + } +} + +static int +eptinput(void *arg) +{ + Endpt *e; + + e = arg; + return e->eof || e->err || qcanread(e->rq); +} + +static int +isoreadyx(Endptx *x) +{ + return x->etd == nil || (x->etd != x->xtd && (x->etd->status & Active) == 0); +} + +static int +isoready(void *arg) +{ + int ret; + Ctlr *ctlr; + Endpt *e; + Endptx *x; + + e = arg; + ctlr = e->dev->uh->ctlr; + x = e->private; + ilock(&ctlr->activends); + ret = isoreadyx(x); + iunlock(&ctlr->activends); + return ret; +} + +static long +isoio(Ctlr *ctlr, Endpt *e, void *a, long n, ulong offset, int w) +{ + TD *td; + Endptx *x; + int i, frnum; + uchar *p, *q, *bp; + volatile int isolock; + + x = e->private; + qlock(&e->rlock); + isolock = 0; + if(waserror()){ + if (isolock){ + isolock = 0; + iunlock(&ctlr->activends); + } + qunlock(&e->rlock); + eptcancel(ctlr, e); + nexterror(); + } + p = a; + if (offset != 0 && offset != e->foffset){ + iprint("offset %lud, foffset %lud\n", offset, e->foffset); + /* Seek to a specific position */ + frnum = (IN(Frnum) + 8) & 0x3ff; + td = x->td0 +frnum; + if (offset < td->offset) + error("ancient history"); + while (offset > e->toffset){ + tsleep(&e->wr, return0, 0, 500); + } + while (offset >= td->offset + ((w?(td->dev >> 21):td->status) + 1) & 0x7ff){ + td = td->next; + if (td == x->xtd) + iprint("trouble\n"); + } + ilock(&ctlr->activends); + isolock = 1; + e->off = td->offset - offset; + if (e->off >= e->maxpkt){ + iprint("I can't program: %d\n", e->off); + e->off = 0; + } + x->etd = td; + e->foffset = offset; + } + do { + if (isolock == 0){ + ilock(&ctlr->activends); + isolock = 1; + } + td = x->etd; + if (td == nil || e->off == 0){ + if (td == nil){ + XPRINT("0"); + if (w){ + frnum = (IN(Frnum) + 1) & 0x3ff; + td = x->td0 + frnum; + while(td->status & Active) + td = td->next; + }else{ + frnum = (IN(Frnum) - 4) & 0x3ff; + td = x->td0 + frnum; + while(td->next != x->xtd) + td = td->next; + } + x->etd = td; + e->off = 0; + }else{ + /* New td, make sure it's ready */ + while (isoreadyx(x) == 0){ + isolock = 0; + iunlock(&ctlr->activends); + sleep(&e->wr, isoready, e); + ilock(&ctlr->activends); + isolock = 1; + } + if (x->etd == nil){ + XPRINT("!"); + continue; + } + } + if (w) + e->psize = ((td->dev >> 21) + 1) & 0x7ff; + else + e->psize = (x->etd->status + 1) & 0x7ff; + if(e->psize > e->maxpkt) + panic("packet size > maximum"); + } + if((i = n) >= e->psize) + i = e->psize; + if (w) + e->buffered += i; + else{ + e->buffered -= i; + if (e->buffered < 0) + e->buffered = 0; + } + isolock = 0; + iunlock(&ctlr->activends); + td->flags &= ~IsoClean; + bp = x->bp0 + (td - x->td0) * e->maxpkt / e->pollms; + q = bp + e->off; + if (w){ + memmove(q, p, i); + }else{ + memmove(p, q, i); + } + p += i; + n -= i; + e->off += i; + e->psize -= i; + if (e->psize){ + if (n != 0) + panic("usb iso: can't happen"); + break; + } + if(w) + td->offset = offset + (p-(uchar*)a) - (((td->dev >> 21) + 1) & 0x7ff); + td->status = ErrLimit3 | Active | IsoSelect | IOC; + x->etd = td->next; + e->off = 0; + } while(n > 0); + n = p-(uchar*)a; + e->foffset += n; + poperror(); + if (isolock) + iunlock(&ctlr->activends); + qunlock(&e->rlock); + return n; +} + +static long +read(Usbhost *uh, Endpt *e, void *a, long n, vlong offset) +{ + long l, i; + Block *b; + Ctlr *ctlr; + uchar *p; + + ctlr = uh->ctlr; + if(e->iso) + return isoio(ctlr, e, a, n, (ulong)offset, 0); + + XPRINT("qlock(%p)\n", &e->rlock); + qlock(&e->rlock); + XPRINT("got qlock(%p)\n", &e->rlock); + if(waserror()){ + qunlock(&e->rlock); + eptcancel(ctlr, e); + nexterror(); + } + p = a; + do { + if(e->eof) { + XPRINT("e->eof\n"); + break; + } + if(e->err) + error(e->err); + qrcv(ctlr, e); + if(!e->iso) + e->data01 ^= 1; + sleep(&e->rr, eptinput, e); + if(e->err) + error(e->err); + b = qget(e->rq); /* TO DO */ + if(b == nil) { + XPRINT("b == nil\n"); + break; + } + if(waserror()){ + freeb(b); + nexterror(); + } + l = BLEN(b); + if((i = l) > n) + i = n; + if(i > 0){ + memmove(p, b->rp, i); + p += i; + } + poperror(); + freeb(b); + n -= i; + if (l != e->maxpkt) + break; + } while (n > 0); + poperror(); + qunlock(&e->rlock); + return p-(uchar*)a; +} + +static int +qisempty(void *arg) +{ + return ((QH*)arg)->entries & Terminate; +} + +static long +write(Usbhost *uh, Endpt *e, void *a, long n, vlong offset, int tok) +{ + int i, j; + QH *qh; + Block *b; + Ctlr *ctlr; + uchar *p; + + ctlr = uh->ctlr; + if(e->iso) + return isoio(ctlr, e, a, n, (ulong)offset, 1); + + p = a; + qlock(&e->wlock); + if(waserror()){ + qunlock(&e->wlock); + eptcancel(ctlr, e); + nexterror(); + } + do { + if(e->err) + error(e->err); + if((i = n) >= e->maxpkt) + i = e->maxpkt; + b = allocb(i); + if(waserror()){ + freeb(b); + nexterror(); + } + XPRINT("out [%d]", i); + for (j = 0; j < i; j++) XPRINT(" %.2x", p[j]); + XPRINT("\n"); + memmove(b->wp, p, i); + b->wp += i; + p += i; + n -= i; + poperror(); + qh = qxmit(ctlr, e, b, tok); + tok = TokOUT; + e->data01 ^= 1; + if(e->ntd >= e->nbuf) { +XPRINT("qh %s: q=%p first=%p last=%p entries=%.8lux\n", + "writeusb sleep", qh, qh->first, qh->last, qh->entries); + XPRINT("write: sleep %lux\n", &e->wr); + sleep(&e->wr, qisempty, qh); + XPRINT("write: awake\n"); + } + } while(n > 0); + poperror(); + qunlock(&e->wlock); + return p-(uchar*)a; +} + +static void +init(Usbhost* uh) +{ + Ctlr *ctlr; + + ctlr = uh->ctlr; + ilock(ctlr); + outl(ctlr->io+Flbaseadd, PCIWADDR(ctlr->frames)); + OUT(Frnum, 0); + OUT(Usbintr, 0xF); /* enable all interrupts */ + XPRINT("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(ctlr->io+SOFMod)); + XPRINT("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1)); + if((IN(Cmd)&1)==0) + OUT(Cmd, 1); /* run */ +// pprint("at: c=%x s=%x c0=%x\n", IN(Cmd), IN(Status), IN(Portsc0)); + iunlock(ctlr); +} + +static void +scanpci(void) +{ + int io; + Ctlr *ctlr; + Pcidev *p; + static int already = 0; + + if(already) + return; + already = 1; + p = nil; + while(p = pcimatch(p, 0, 0)) { + /* + * Find UHCI controllers. Class = 12 (serial controller), + * Sub-class = 3 (USB) and Programming Interface = 0. + */ + if(p->ccrb != 0x0C || p->ccru != 0x03 || p->ccrp != 0x00) + continue; + io = p->mem[4].bar & ~0x0F; + if(io == 0) { + print("usbuhci: failed to map registers\n"); + continue; + } + if(ioalloc(io, p->mem[4].size, 0, "usbuhci") < 0){ + print("usbuhci: port %d in use\n", io); + continue; + } + if(p->intl == 0xFF || p->intl == 0) { + print("usbuhci: no irq assigned for port %d\n", io); + continue; + } + + XPRINT("usbuhci: %x/%x port 0x%ux size 0x%x irq %d\n", + p->vid, p->did, io, p->mem[4].size, p->intl); + + ctlr = malloc(sizeof(Ctlr)); + ctlr->pcidev = p; + ctlr->io = io; + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + +static int +reset(Usbhost *uh) +{ + int i; + TD *t; + ulong io; + Ctlr *ctlr; + Pcidev *p; + + scanpci(); + + /* + * Any adapter matches if no uh->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(uh->port == 0 || uh->port == ctlr->io){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + io = ctlr->io; + p = ctlr->pcidev; + + uh->ctlr = ctlr; + uh->port = io; + uh->irq = p->intl; + uh->tbdf = p->tbdf; + + XPRINT("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n", + IN(Cmd), IN(Status), IN(Usbintr), inb(io+Frnum)); + XPRINT("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n", + IN(Flbaseadd), inb(io+SOFMod), IN(Portsc0), IN(Portsc1)); + + OUT(Cmd, 0); /* stop */ + while((IN(Status) & (1<<5)) == 0) /* wait for halt */ + ; + OUT(Status, 0xFF); /* clear pending interrupts */ + pcicfgw16(p, 0xc0, 0x2000); /* legacy support register: turn off lunacy mode */ + + if(0){ + i = inb(io+SOFMod); + OUT(Cmd, 4); /* global reset */ + delay(15); + OUT(Cmd, 0); /* end reset */ + delay(4); + outb(io+SOFMod, i); + } + + ctlr->tdpool = mmucacheinhib(xspanalloc(128*sizeof(TD), /*16*/CACHELINESZ, 0), 128*sizeof(TD)); + for(i=128; --i>=0;){ + ctlr->tdpool[i].next = ctlr->freetd; + ctlr->freetd = &ctlr->tdpool[i]; + } + ctlr->qhpool = mmucacheinhib(xspanalloc(64*sizeof(QH), /*16*/CACHELINESZ, 0), 64*sizeof(QH)); + for(i=64; --i>=0;){ + ctlr->qhpool[i].next = ctlr->freeqh; + ctlr->freeqh = &ctlr->qhpool[i]; + } + + /* + * the last entries of the periodic (interrupt & isochronous) scheduling TD entries + * points to the control queue and the bandwidth sop for bulk traffic. + * this is looped following the instructions in PIIX4 errata 29773804.pdf: + * a QH links to a looped but inactive TD as its sole entry, + * with its head entry leading on to the bulk traffic, the last QH of which + * links back to the empty QH. + */ + ctlr->ctlq = allocqh(ctlr); + ctlr->bwsop = allocqh(ctlr); + ctlr->bulkq = allocqh(ctlr); + ctlr->recvq = allocqh(ctlr); + t = alloctd(ctlr); /* inactive TD, looped */ + t->link = PCIWADDR(t); + ctlr->bwsop->entries = PCIWADDR(t); + + ctlr->ctlq->head = PCIWADDR(ctlr->bulkq) | IsQH; + ctlr->bulkq->head = PCIWADDR(ctlr->recvq) | IsQH; + ctlr->recvq->head = PCIWADDR(ctlr->bwsop) | IsQH; + if (1) /* don't use loop back */ + ctlr->bwsop->head = Terminate; + else /* set up loop back */ + ctlr->bwsop->head = PCIWADDR(ctlr->bwsop) | IsQH; + + ctlr->frames = mmucacheinhib(xspanalloc(FRAMESIZE, FRAMESIZE, 0), FRAMESIZE); + ctlr->frameld = xallocz(FRAMESIZE, 1); + for (i = 0; i < NFRAME; i++) + ctlr->frames[i] = PCIWADDR(ctlr->ctlq) | IsQH; + + /* + * Linkage to the generic USB driver. + */ + uh->init = init; + uh->interrupt = interrupt; + + uh->portinfo = portinfo; + uh->portreset = portreset; + uh->portenable = portenable; + + uh->epalloc = epalloc; + uh->epfree = epfree; + uh->epopen = epopen; + uh->epclose = epclose; + uh->epmode = epmode; + + uh->read = read; + uh->write = write; + + return 0; +} + +void +usbuhcilink(void) +{ + addusbtype("uhci", reset); +} diff --git a/os/mpc/800io.h b/os/mpc/800io.h new file mode 100644 index 00000000..c67ced41 --- /dev/null +++ b/os/mpc/800io.h @@ -0,0 +1,666 @@ +typedef struct BD BD; +typedef struct CPMdev CPMdev; +typedef struct GTimer GTimer; +typedef struct I2Cdev I2Cdev; +typedef struct PCMconftab PCMconftab; +typedef struct PCMmap PCMmap; +typedef struct PCMslot PCMslot; +typedef struct Ring Ring; + +/* + * MPC800 series IO structures + */ + +enum +{ + /* interrupt vectors (SIU and CPM) */ + VectorPIC= 0, /* level 0 to level 7, assigned by software */ + /* vector assignments are determined by the assignments here */ + PITlevel= 2, + CPIClevel= 4, + PCMCIAio= 5, + PCMCIAstatus= 6, + RTClevel= 7, + VectorIRQ= VectorPIC+8, /* IRQ0 to IRQ7 */ + VectorCPIC= VectorIRQ+8, /* 32 CPM interrupts: 0 (error) to 0x1F (PC15) */ + MaxVector= VectorCPIC+32, +}; + +/* + * these are defined to keep the interface compatible with other + * architectures, but only BUSUNKNOWN is currently used + */ +#define MKBUS(t,b,d,f) (((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8)) +#define BUSFNO(tbdf) (((tbdf)>>8)&0x07) +#define BUSDNO(tbdf) (((tbdf)>>11)&0x1F) +#define BUSBNO(tbdf) (((tbdf)>>16)&0xFF) +#define BUSTYPE(tbdf) ((tbdf)>>24) +#define BUSBDF(tbdf) ((tbdf)&0x00FFFF00) +#define BUSUNKNOWN (-1) + +/* + * Buffer Descriptors and IO Rings + */ + +struct BD { + ushort status; + ushort length; + ulong addr; +}; + +BD* bdalloc(int); +void bdfree(BD*, int); +void dumpbd(char*, BD*, int); + +enum { + /* Rx BDs, bits common to all protocols */ + BDEmpty= 1<<15, + BDWrap= 1<<13, + BDInt= 1<<12, + BDLast= 1<<11, + BDFirst= 1<<10, + + /* Tx BDs */ + BDReady= 1<<15, + /* BDWrap, BDInt, BDLast */ +}; + + +struct Ring { + BD* rdr; /* receive descriptor ring */ + void* rrb; /* receive ring buffers */ + int rdrx; /* index into rdr */ + int nrdre; /* length of rdr */ + + BD* tdr; /* transmit descriptor ring */ + Block** txb; /* corresponding transmit ring buffers */ + int tdrh; /* host index into tdr */ + int tdri; /* interface index into tdr */ + int ntdre; /* length of tdr */ + int ntq; /* pending transmit requests */ +}; + +#define NEXT(x, l) (((x)+1)%(l)) +#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1) +#define HOWMANY(x, y) (((x)+((y)-1))/(y)) +#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) + +int ioringinit(Ring*, int, int, int); + +/* + * CPM + */ +enum { + /* commands */ + InitRxTx = 0, + InitRx = 1, + InitTx = 2, + EnterHunt= 3, + StopTx= 4, + GracefulStopTx = 5, + InitIDMA = 5, + RestartTx = 6, + CloseRxBD = 7, + SetGroupAddr = 8, + SetTimer = 8, + GCITimeout = 9, + GCIAbort = 10, + StopIDMA = 11, + StartDSP = 12, + ArmIDMA = 13, + InitDSP = 13, + USBCmd = 15, + + /* bgcr */ + BaudEnable = 1<<16, + + /* sicr */ + CLK1 = 4, /* SCC1,2 */ + CLK2 = 5, + CLK3 = 6, + CLK4 = 7, + CLK5 = CLK1, /* SCC3,4 */ + CLK6 = CLK2, + CLK7 = CLK3, + CLK8 = CLK4, + + /* logical channel IDs mapped to channel ID by cpm.c */ + CPnone = 0, + CPscc1, + CPscc2, + CPscc3, + CPscc4, + CPsmc1, + CPsmc2, + CPdsp1, + CPdsp2, + CPidma1, + CPidma2, + CPtimer, + CPspi, + CPi2c, + CPmax, +}; + +struct CPMdev { + int id; /* CPM channel number */ + int irq; /* CPIC interrupt number */ + int rbase; /* register offset in IO mem */ + int pbase; /* parameter offset in IO mem */ + void* regs; /* kernel address of registers */ + void* param; /* kernel address of parameters */ +}; + +CPMdev* cpmdev(int); +void cpmop(CPMdev*, int, int); +void* cpmalloc(int, int); +void cpmfree(void*, int); +IMM* ioplock(void); +void iopunlock(void); + +int cpmidopen(int, void*); +void cpmidclose(int); +void sccnmsi(int, int, int); +void sccxstop(CPMdev*); +void smcnmsi(int, int); +void smcxstop(CPMdev*); + +/* + * CPM timers + */ +enum { + /* timer modes */ + CaptureRise= 1<<6, + CaptureFall= 2<<6, + CaptureEdge= 3<<6, + TimerToggle= 1<<5, /* toggle TOUTx* pin */ + TimerORI= 1<<4, /* Output Reference Interrupt */ + TimerRestart= 1<<3, + TimerSclk= 1<<1, + TimerSclk16= 2<<1, + TimerTIN= 3<<1, /* clock by falling edge of TINx */ + TimerGate= 1<<0, /* TGATE1* controls timer */ + + /* timer events */ + TimerREF= 1<<1, + TimerCAP= 1<<0 +}; + +struct GTimer{ + int x; + int inuse; + int event; + ushort* tmr; + ushort* trr; + ushort* tcr; + ushort* tcn; + ushort* ter; + void* arg; + void (*interrupt)(Ureg*, void*, GTimer*); +}; +GTimer* gtimer(ushort, ushort, void (*)(Ureg*,void*,GTimer*), void*); +void gtimerset(GTimer*, ushort, int); +void gtimerstart(GTimer*); +void gtimerstop(GTimer*); +void gtimerfree(GTimer*); + +/* + * the structures below follow hardware/firmware layouts in the 8xx manuals: + * mind the data types, offsets and alignment + */ + +/* + * basic IO controller parameters (SMC and SCC) + */ +typedef struct IOCparam IOCparam; +struct IOCparam { + ushort rbase; + ushort tbase; + uchar rfcr; + uchar tfcr; + ushort mrblr; + ulong rstate; + ulong rptr; + ushort rbptr; + ushort rcnt; + ulong rtmp; + ulong tstate; + ulong tptr; + ushort tbptr; + ushort tcnt; + ulong ttmp; +}; + +typedef struct SCCparam SCCparam; +struct SCCparam { + IOCparam; + ulong rcrc; + ulong tcrc; +}; + +typedef struct SCC SCC; +struct SCC { + ulong gsmrl; + ulong gsmrh; + ushort psmr; + uchar rsvscc0[2]; + ushort todr; + ushort dsr; + ushort scce; + uchar rsvscc1[2]; + ushort sccm; + uchar rsvscc3; + uchar sccs; + ushort irmode; + ushort irsip; +}; + +typedef struct SMC SMC; +struct SMC { + uchar pad1[2]; + ushort smcmr; + uchar pad2[2]; + uchar smce; + uchar pad3[3]; + uchar smcm; + uchar pad4[5]; +}; + +typedef struct SPI SPI; +struct SPI { + ushort spmode; + uchar res1[4]; + uchar spie; + uchar res2[3]; + uchar spim; + uchar res3[2]; + uchar spcom; + uchar res4[10]; +}; + +typedef struct USB USB; +struct USB { /* 823 only */ + uchar usmod; + uchar usadr; + uchar uscom; + uchar rsvu1; + ushort usep[4]; + uchar rsvu2[4]; + ushort usber; + uchar rsvu3[2]; + ushort usbmr; + uchar rsvu4; + uchar usbs; + uchar rsvu5[8]; +}; + +typedef struct IMM IMM; +struct IMM { + struct { /* general SIU */ + ulong siumcr; + ulong sypcr; + uchar rsv0[0xE-0x8]; + ushort swsr; + ulong sipend; + ulong simask; + ulong siel; + uchar sivec; + uchar padv[3]; + ulong tesr; + uchar rsv1[0x30-0x24]; + ulong sdcr; + uchar rsv2[0x80-0x34]; + }; + struct { /* PCMCIA */ + struct { + ulong base; + ulong option; + } pcmr[8]; + uchar rsv3[0xe0-0xc0]; + ulong pgcr[2]; + ulong pscr; + uchar rsv4[0xf0-0xec]; + ulong pipr; + uchar rsv5[4]; + ulong per; + uchar rsv6[4]; + }; + struct { /* MEMC */ + struct { + ulong base; + ulong option; + } memc[8]; + uchar rsv7a[0x24]; + ulong mar; + ulong mcr; + uchar rsv7b[4]; + ulong mamr; + ulong mbmr; + ushort mstat; + ushort mptpr; + ulong mdr; + uchar rsv7c[0x80]; + }; + struct { /* system integration timers */ + ushort tbscr; + uchar rsv8a[2]; + ulong tbrefu; + ulong tbrefl; + uchar rsv8b[0x14]; + ushort rtcsc; + uchar rsv8c[2]; + ulong rtc; + ulong rtsec; + ulong rtcal; + uchar rsv8d[0x10]; + ushort piscr; + ushort rsv8e; + ulong pitc; + ulong pitr; + uchar rsv8f[0x34]; + }; + struct { /* 280: clocks and resets */ + ulong sccr; + ulong plprcr; + ulong rsr; + uchar rsv9[0x300-0x28c]; + }; + struct { /* 300: system integration timers keys */ + ulong tbscrk; + ulong tbrefuk; + ulong tbreflk; + ulong tbk; + uchar rsv10a[0x10]; + ulong rtcsck; + ulong rtck; + ulong rtseck; + ulong rtcalk; + uchar rsv10b[0x10]; + ulong piscrk; + ulong pitck; + uchar rsv10c[0x38]; + }; + struct { /* 380: clocks and resets keys */ + ulong sccrk; + ulong plprcrk; + ulong rsrk; + uchar rsv11[0x800-0x38C]; + }; + struct { /* 800: video controller */ + ushort vccr; + ushort pad11a; + uchar vsr; + uchar pad11b; + uchar vcmr; + uchar pad11c; + ulong vbcb; + ulong pad11d; + ulong vfcr0; + ulong vfaa0; + ulong vfba0; + ulong vfcr1; + ulong vfaa1; + ulong vfba1; + uchar rsv11a[0x840-0x828]; + }; + struct { /* 840: LCD */ + ulong lccr; + ulong lchcr; + ulong lcvcr; + ulong rsv11b; + ulong lcfaa; + ulong lcfba; + uchar lcsr; + uchar rsv11c[0x860-0x859]; + }; + struct { /* 860: I2C */ + uchar i2mod; + uchar rsv12a[3]; + uchar i2add; + uchar rsv12b[3]; + uchar i2brg; + uchar rsv12c[3]; + uchar i2com; + uchar rsv12d[3]; + uchar i2cer; + uchar rsv12e[3]; + uchar i2cmr; + uchar rsv12[0x900-0x875]; + }; + struct { /* 900: DMA */ + uchar rsv13[4]; + ulong sdar; + uchar sdsr; + uchar pad1[3]; + uchar sdmr; + uchar pad2[3]; + uchar idsr1; + uchar pad3[3]; + uchar idmr1; + uchar pad4[3]; + uchar idsr2; + uchar pad5[3]; + uchar idmr2; + uchar pad6[0x930-0x91D]; + }; + struct { /* CPM interrupt control */ + ushort civr; + uchar pad7[0x940-0x932]; + ulong cicr; + ulong cipr; + ulong cimr; + ulong cisr; + }; + struct { /* input/output port */ + ushort padir; + ushort papar; + ushort paodr; + ushort padat; + uchar pad8[8]; + ushort pcdir; + ushort pcpar; + ushort pcso; + ushort pcdat; + ushort pcint; + uchar pad9[6]; + ushort pddir; + ushort pdpar; + ushort rsv14a; + ushort pddat; + uchar rsv14[0x980-0x978]; + }; + struct { /* CPM timers */ + ushort tgcr; + uchar rsv15a[0x990-0x982]; + ushort tmr1; + ushort tmr2; + ushort trr1; + ushort trr2; + ushort tcr1; + ushort tcr2; + ushort tcn1; + ushort tcn2; + ushort tmr3; + ushort tmr4; + ushort trr3; + ushort trr4; + ushort tcr3; + ushort tcr4; + ushort tcn3; + ushort tcn4; + ushort ter1; + ushort ter2; + ushort ter3; + ushort ter4; + uchar rsv15[0x9C0-0x9B8]; + }; + struct { /* CPM */ + ushort cpcr; + uchar res0[2]; + ushort rccr; + uchar res1; + uchar rmds; + uchar res2a[4]; + ushort rctr1; + ushort rctr2; + ushort rctr3; + ushort rctr4; + uchar res2[2]; + ushort rter; + uchar res3[2]; + ushort rtmr; + uchar rsv16[0x9F0-0x9DC]; + }; + union { /* BRG */ + struct { + ulong brgc1; + ulong brgc2; + ulong brgc3; + ulong brgc4; + }; + ulong brgc[4]; + }; + uchar skip0[0xAB2-0xA00]; /* USB, SCC, SMC, SPI: address using cpmdev(CP...)->regs */ + struct { /* PIP */ + ushort pipc; /* not 823 */ + ushort ptpr; /* not 823 */ + ulong pbdir; + ulong pbpar; + uchar pad10[2]; + ushort pbodr; + ulong pbdat; + uchar pad11[0xAE0-0xAC8]; + }; + struct { /* SI */ + ulong simode; + uchar sigmr; + uchar pad12; + uchar sistr; + uchar sicmr; + uchar pad13[4]; + ulong sicr; + ulong sirp; + uchar pad14[0xB00-0xAF4]; + }; + ulong vcram[64]; + ushort siram[256]; + ushort lcdmap[256]; +}; + +/* + * PCMCIA structures known by both ../port/cis.c and the pcmcia driver + */ + +/* + * Map between physical memory space and PCMCIA card memory space. + */ +struct PCMmap { + ulong ca; /* card address */ + ulong cea; /* card end address */ + ulong isa; /* local virtual address */ + int len; /* length of the ISA area */ + int attr; /* attribute memory */ + int slotno; /* owning slot */ + int ref; +}; + +/* + * a PCMCIA configuration entry + */ +struct PCMconftab +{ + int index; + ushort irqs; /* legal irqs */ + uchar irqtype; + uchar bit16; /* true for 16 bit access */ + uchar nlines; + struct { + ulong start; + ulong len; + PCMmap* map; + } io[16]; + int nio; + int vcc; + int vpp1; + int vpp2; + uchar memwait; + ulong maxwait; + ulong readywait; + ulong otherwait; +}; + +/* + * PCMCIA card slot + */ +struct PCMslot +{ +// RWlock; + +// Ref ref; +Ref; + + void* ctlr; /* controller for this slot */ + + long memlen; /* memory length */ + uchar slotno; /* slot number */ + uchar slotshift; /* >> register to meet mask; << mask to meet register */ + void *regs; /* i/o registers */ + void *mem; /* memory */ + void *attr; /* attribute memory */ + + /* status */ + uchar occupied; /* card in the slot */ + uchar configed; /* card configured */ + uchar busy; + uchar powered; + uchar battery; + uchar wrprot; + uchar enabled; + uchar special; + uchar dsize; + uchar v3_3; + uchar voltage; + + /* cis info */ + int cisread; /* set when the cis has been read */ + char verstr[512]; /* version string */ + uchar cpresent; /* config registers present */ + ulong caddr; /* relative address of config registers */ + int nctab; /* number of config table entries */ + PCMconftab ctab[8]; + PCMconftab *def; /* default conftab */ + + /* maps are fixed */ + PCMmap memmap; + PCMmap attrmap; + + struct { + void (*f)(Ureg*, void*); + void *arg; + } intr; + struct { + void (*f)(void*, int); + void *arg; + } notify; +}; + +/* ../port/cis.c */ +void pcmcisread(PCMslot*); +int pcmcistuple(int, int, int, void*, int); + +/* devpcmcia.c */ +PCMmap* pcmmap(int, ulong, int, int); +void pcmunmap(int, PCMmap*); + +/* + * used by ../port/devi2c.c and i2c.c + */ +struct I2Cdev { + int addr; + int salen; /* length in bytes of subaddress, if used; 0 otherwise */ + int tenbit; /* 10-bit addresses */ +}; + +long i2crecv(I2Cdev*, void*, long, ulong); +long i2csend(I2Cdev*, void*, long, ulong); +void i2csetup(int); diff --git a/os/mpc/NOTICE b/os/mpc/NOTICE new file mode 100644 index 00000000..1ba3456b --- /dev/null +++ b/os/mpc/NOTICE @@ -0,0 +1,3 @@ +Inferno® Copyright © 1996-1999 Lucent Technologies Inc. All rights reserved. +PowerPC support Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net). All rights reserved. +MPC8xx Inferno PowerPC port Copyright © 1998-2003 Vita Nuova Holdings Limited. All rights reserved. diff --git a/os/mpc/clock.c b/os/mpc/clock.c new file mode 100644 index 00000000..f8ed362f --- /dev/null +++ b/os/mpc/clock.c @@ -0,0 +1,145 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +#include +#include + +typedef struct Clock0link Clock0link; +typedef struct Clock0link { + void (*clock)(void); + Clock0link* link; +} Clock0link; + +static Clock0link *clock0link; +static Lock clock0lock; +ulong clkrelinq; +void (*kproftick)(ulong); /* set by devkprof.c when active */ +void (*archclocktick)(void); /* set by arch*.c when desired */ + +Timer* +addclock0link(void (*clock)(void), int) +{ + Clock0link *lp; + + if((lp = malloc(sizeof(Clock0link))) == 0){ + print("addclock0link: too many links\n"); + return nil; + } + ilock(&clock0lock); + lp->clock = clock; + lp->link = clock0link; + clock0link = lp; + iunlock(&clock0lock); + return nil; +} + +void +delay(int l) +{ + ulong i, j; + + j = m->delayloop; + while(l-- > 0) + for(i=0; i < j; i++) + ; +} + +void +microdelay(int l) +{ + ulong i; + + l *= m->delayloop; + l /= 1000; + if(l <= 0) + l = 1; + for(i = 0; i < l; i++) + ; +} + +enum { + Timebase = 4, /* system clock cycles per time base cycle */ +}; + +static ulong clkreload; + +void +clockinit(void) +{ + long x; + + m->delayloop = m->cpuhz/1000; /* initial estimate */ + do { + x = gettbl(); + delay(10); + x = gettbl() - x; + } while(x < 0); + + /* + * fix count + */ + m->delayloop = ((vlong)m->delayloop*(10*m->clockgen/1000))/(x*Timebase); + if(m->delayloop == 0) + m->delayloop = 1; + + clkreload = (m->clockgen/Timebase)/HZ-1; + putdec(clkreload); +} + +void +clockintr(Ureg *ur) +{ + Clock0link *lp; + long v; + + v = -getdec(); + if(v > clkreload/2){ + if(v > clkreload) + m->ticks += v/clkreload; + v = 0; + } + putdec(clkreload-v); + + /* watchdog */ + if(m->iomem->sypcr & (1<<2)){ + m->iomem->swsr = 0x556c; + m->iomem->swsr = 0xaa39; + } + + m->ticks++; + if(archclocktick != nil) + archclocktick(); + + if(up) + up->pc = ur->pc; + + checkalarms(); + if(m->machno == 0) { + if(kproftick != nil) + (*kproftick)(ur->pc); + if(canlock(&clock0lock)){ + for(lp = clock0link; lp; lp = lp->link) + lp->clock(); + unlock(&clock0lock); + } + } + + if(up && up->state == Running){ + if(cflag && up->type == Interp && tready(nil)) + ur->cr |= 1; /* set flag in condition register for ../../libinterp/comp-power.c:/^schedcheck */ + } + /* other preemption checks are done by trap.c */ +} + +uvlong +fastticks(uvlong *hz) +{ + if(hz) + *hz = HZ; + return m->ticks; +} diff --git a/os/mpc/cpm.c b/os/mpc/cpm.c new file mode 100644 index 00000000..e985d947 --- /dev/null +++ b/os/mpc/cpm.c @@ -0,0 +1,695 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +typedef struct Chanuse Chanuse; +struct Chanuse { + Lock; + void* owner; +} ; + +enum { + BDSIZE= 1024, /* IO memory reserved for buffer descriptors */ + CPMSIZE= 1024, /* IO memory reserved for other uses */ + + /* channel IDs */ + SCC1ID= 0, + I2CID= 1, + IDMA1ID= 1, + SCC2ID= 4, + SPIID= 5, + IDMA2ID= 5, + TIMERID= 5, + SCC3ID= 8, + SMC1ID= 9, + DSP1ID= 9, + SCC4ID= 12, + SMC2ID= 13, + DSP2ID= 13, + NCPMID= 16, + + NSCC = 4, + + /* SCC.gsmr_l */ + ENR = 1<<5, /* enable receiver */ + ENT = 1<<4, /* enable transmitter */ + + NSMC = 2, + + /* SMC.smcmr */ + TEN = 1<<1, /* transmitter enable */ + REN = 1<<0, /* receiver enable */ +}; + +static Map bdmapv[BDSIZE/sizeof(BD)]; +static RMap bdmap = {"buffer descriptors"}; + +static Map cpmmapv[CPMSIZE/sizeof(ulong)]; +static RMap cpmmap = {"CPM memory"}; + +static Lock cpmlock; + +static struct { + Lock; + ulong avail; +} brgens; + +static Chanuse cpmids[NCPMID]; +static CPMdev cpmdevinfo[] = { + [CPscc1] {SCC1ID, 0x1E, 0xA00, 0x3C00}, + [CPscc2] {SCC2ID, 0x1D, 0xA20, 0x3D00}, + [CPscc3] {SCC3ID, 0x1C, 0xA40, 0x3E00}, + [CPscc4] {SCC4ID, 0x1B, 0xA60, 0x3F00}, + [CPsmc1] {SMC1ID, 0x04, 0xA80, 0x3E80}, + [CPsmc2] {SMC2ID, 0x03, 0xA90, 0x3F80}, + [CPdsp1] {DSP1ID, 0x16, 0, 0x3EC0}, + [CPdsp2] {DSP2ID, 0x16, 0, 0x3FC0}, + [CPidma1] {IDMA1ID, 0x15, 0, 0x3CC0}, + [CPidma2] {IDMA2ID, 0x14, 0, 0x3DC0}, + [CPtimer] {TIMERID, 0x11, 0, 0x3DB0}, + [CPspi] {SPIID, 0x05, 0xAA0, 0x3D80}, /* parameters relocated below */ + [CPi2c] {I2CID, 0x10, 0x860, 0x3C80}, /* parameters relocated below */ +}; + +static void i2cspireloc(void); +static void* relocateparam(ulong, int); + +/* + * initialise the communications processor module + * and associated device registers + */ +void +cpminit(void) +{ + IMM *io; + + io = m->iomem; + io->sdcr = 1; + io->rccr = 0; + io->rmds = 0; + io->lccr = 0; /* disable LCD */ + io->vccr = 0; /* disable video */ + io->i2mod = 0; /* stop I2C */ + io->pcint = 0; /* disable all port C interrupts */ + io->pcso = 0; + io->pcdir =0; + io->pcpar = 0; + io->pcdat = 0; + io->papar = 0; + io->padir = 0; + io->paodr = 0; + io->padat = 0; + io->pbpar = 0; + io->pbdir = 0; + io->pbodr = 0; + io->pbdat = 0; + io->tgcr = 0x2222; /* reset timers, low-power stop */ + eieio(); + + for(io->cpcr = 0x8001; io->cpcr & 1;) /* reset all CPM channels */ + eieio(); + + mapinit(&bdmap, bdmapv, sizeof(bdmapv)); + mapfree(&bdmap, DPBASE, BDSIZE); + mapinit(&cpmmap, cpmmapv, sizeof(cpmmapv)); + mapfree(&cpmmap, DPBASE+BDSIZE, CPMSIZE); + + if(m->cputype == 0x50 && (getimmr() & 0xFFFF) <= 0x2001) + brgens.avail = 0x3; + else + brgens.avail = 0xF; + i2cspireloc(); +} + +/* + * return parameters defining a CPM device, given logical ID + */ +CPMdev* +cpmdev(int n) +{ + CPMdev *d; + + if(n < 0 || n >= nelem(cpmdevinfo)) + panic("cpmdev"); + d = &cpmdevinfo[n]; + if(d->param == nil && d->pbase != 0){ + if((n == CPi2c || n == CPspi)){ + d->param = relocateparam(d->pbase, 0xB0-0x80); /* relocate */ + if(d->param == nil) + return nil; + } else + d->param = (char*)m->iomem+d->pbase; + } + if(d->rbase != 0) + d->regs = (char*)m->iomem+d->rbase; + return d; +} + +/* + * issue a request to a CPM device + */ +void +cpmop(CPMdev *cpd, int op, int param) +{ + IMM *io; + + ilock(&cpmlock); + io = m->iomem; + while(io->cpcr & 1) + eieio(); + io->cpcr = (op<<8)|(cpd->id<<4)|(param<<1)|1; + eieio(); + while(io->cpcr & 1) + eieio(); + iunlock(&cpmlock); +} + +/* + * lock the shared IO memory and return a reference to it + */ +IMM* +ioplock(void) +{ + ilock(&cpmlock); + return m->iomem; +} + +/* + * release the lock on the shared IO memory + */ +void +iopunlock(void) +{ + eieio(); + iunlock(&cpmlock); +} + +/* + * connect SCCx clocks in NSMI mode (x=1 for USB) + */ +void +sccnmsi(int x, int rcs, int tcs) +{ + IMM *io; + ulong v; + int sh; + + sh = (x-1)*8; /* each SCCx field in sicr is 8 bits */ + v = (((rcs&7)<<3) | (tcs&7)) << sh; + io = ioplock(); + io->sicr = (io->sicr & ~(0xFF<simode = (io->simode & ~(0xF000<owner != nil && use->owner != owner){ + iunlock(use); + return -1; + } + use->owner = owner; + iunlock(use); + return 0; +} + +/* + * release a previously claimed CPM ID + */ +void +cpmidclose(int id) +{ + Chanuse *use; + + use = &cpmids[id]; + ilock(use); + use->owner = nil; + iunlock(use); +} + +/* + * if SCC d is currently enabled, shut it down + */ +void +sccxstop(CPMdev *d) +{ + SCC *scc; + + if(d == nil) + return; + scc = d->regs; + if(scc->gsmrl & (ENT|ENR)){ + if(scc->gsmrl & ENT) + cpmop(d, GracefulStopTx, 0); + if(scc->gsmrl & ENR) + cpmop(d, CloseRxBD, 0); + delay(1); + scc->gsmrl &= ~(ENT|ENR); /* disable current use */ + eieio(); + } + scc->sccm = 0; /* mask interrupts */ +} + +/* + * if SMC d is currently enabled, shut it down + */ +void +smcxstop(CPMdev *d) +{ + SMC *smc; + + if(d == nil) + return; + smc = d->regs; + if(smc->smcmr & (TEN|REN)){ + if(smc->smcmr & TEN) + cpmop(d, StopTx, 0); + if(smc->smcmr & REN) + cpmop(d, CloseRxBD, 0); + delay(1); + smc->smcmr &= ~(TEN|REN); + eieio(); + } + smc->smcm = 0; /* mask interrupts */ +} + +/* + * allocate a buffer descriptor + */ +BD * +bdalloc(int n) +{ + ulong a; + + a = rmapalloc(&bdmap, 0, n*sizeof(BD), sizeof(BD)); + if(a == 0) + panic("bdalloc"); + return KADDR(a); +} + +/* + * free a buffer descriptor + */ +void +bdfree(BD *b, int n) +{ + if(b){ + eieio(); + mapfree(&bdmap, PADDR(b), n*sizeof(BD)); + } +} + +/* + * print a buffer descriptor and its data (when debugging) + */ +void +dumpbd(char *name, BD *b, int maxn) +{ + uchar *d; + int i; + + print("%s #%4.4lux: s=#%4.4ux l=%ud a=#%8.8lux", name, PADDR(b)&0xFFFF, b->status, b->length, b->addr); + if(maxn > b->length) + maxn = b->length; + if(b->addr != 0){ + d = KADDR(b->addr); + for(i=0; ilength) + print(" ..."); + } + print("\n"); +} + +/* + * allocate memory from the shared IO memory space + */ +void * +cpmalloc(int n, int align) +{ + ulong a; + + a = rmapalloc(&cpmmap, 0, n, align); + if(a == 0) + panic("cpmalloc"); + return KADDR(a); +} + +/* + * free previously allocated shared memory + */ +void +cpmfree(void *p, int n) +{ + if(p != nil && n > 0){ + eieio(); + mapfree(&cpmmap, PADDR(p), n); + } +} + +/* + * allocate a baud rate generator, returning its index + * (or -1 if none is available) + */ +int +brgalloc(void) +{ + int n; + + lock(&brgens); + for(n=0; brgens.avail!=0; n++) + if(brgens.avail & (1<= 0){ + if(n > 3 || brgens.avail & (1<cpuhz+rate)/(2*rate) - 1; + if(d < 0) + d = 0; + if(d >= (1<<12)) + return ((d+15)>>(4-1))|1; /* divider too big: enable prescale by 16 */ + return d<<1; +} + +/* + * initialise receive and transmit buffer rings. + */ +int +ioringinit(Ring* r, int nrdre, int ntdre, int bufsize) +{ + int i, x; + + /* the ring entries must be aligned on sizeof(BD) boundaries */ + r->nrdre = nrdre; + if(r->rdr == nil) + r->rdr = bdalloc(nrdre); + /* the buffer size must align with cache lines since the cache doesn't snoop */ + bufsize = (bufsize+CACHELINESZ-1)&~(CACHELINESZ-1); + if(r->rrb == nil) + r->rrb = malloc(nrdre*bufsize); + if(r->rdr == nil || r->rrb == nil) + return -1; + dcflush(r->rrb, nrdre*bufsize); + x = PADDR(r->rrb); + for(i = 0; i < nrdre; i++){ + r->rdr[i].length = 0; + r->rdr[i].addr = x; + r->rdr[i].status = BDEmpty|BDInt; + x += bufsize; + } + r->rdr[i-1].status |= BDWrap; + r->rdrx = 0; + + r->ntdre = ntdre; + if(r->tdr == nil) + r->tdr = bdalloc(ntdre); + if(r->txb == nil) + r->txb = malloc(ntdre*sizeof(Block*)); + if(r->tdr == nil || r->txb == nil) + return -1; + for(i = 0; i < ntdre; i++){ + r->txb[i] = nil; + r->tdr[i].addr = 0; + r->tdr[i].length = 0; + r->tdr[i].status = 0; + } + r->tdr[i-1].status |= BDWrap; + r->tdrh = 0; + r->tdri = 0; + r->ntq = 0; + return 0; +} + +/* + * Allocate a new parameter block for I2C or SPI, + * and plant a pointer to it for the microcode, returning the kernel address. + * See Motorola errata and microcode package: + * the design botch is that the parameters for the SCC2 ethernet overlap the + * SPI/I2C parameter space; this compensates by relocating the latter. + * This routine may be used iff i2cspireloc is used (and it is, above). + */ +static void* +relocateparam(ulong olda, int nb) +{ + void *p; + + if(olda < (ulong)m->iomem) + olda += (ulong)m->iomem; + p = cpmalloc(nb, 32); /* ``RPBASE must be multiple of 32'' */ + if(p == nil) + return p; + *(ushort*)KADDR(olda+0x2C) = PADDR(p); /* set RPBASE */ + eieio(); + return p; +} + +/* + * I2C/SPI microcode package from Motorola + * (to relocate I2C/SPI parameters), which was distributed + * on their web site in S-record format. + * + * May 1998 + */ + +/*S00600004844521B*/ +static ulong ubase1 = 0x2000; +static ulong ucode1[] = { + /* #02202000 */ 0x7FFFEFD9, + /* #02202004 */ 0x3FFD0000, + /* #02202008 */ 0x7FFB49F7, + /* #0220200C */ 0x7FF90000, + /* #02202010 */ 0x5FEFADF7, + /* #02202014 */ 0x5F89ADF7, + /* #02202018 */ 0x5FEFAFF7, + /* #0220201C */ 0x5F89AFF7, + /* #02202020 */ 0x3A9CFBC8, + /* #02202024 */ 0xE7C0EDF0, + /* #02202028 */ 0x77C1E1BB, + /* #0220202C */ 0xF4DC7F1D, + /* #02202030 */ 0xABAD932F, + /* #02202034 */ 0x4E08FDCF, + /* #02202038 */ 0x6E0FAFF8, + /* #0220203C */ 0x7CCF76CF, + /* #02202040 */ 0xFD1FF9CF, + /* #02202044 */ 0xABF88DC6, + /* #02202048 */ 0xAB5679F7, + /* #0220204C */ 0xB0937383, + /* #02202050 */ 0xDFCE79F7, + /* #02202054 */ 0xB091E6BB, + /* #02202058 */ 0xE5BBE74F, + /* #0220205C */ 0xB3FA6F0F, + /* #02202060 */ 0x6FFB76CE, + /* #02202064 */ 0xEE0DF9CF, + /* #02202068 */ 0x2BFBEFEF, + /* #0220206C */ 0xCFEEF9CF, + /* #02202070 */ 0x76CEAD24, + /* #02202074 */ 0x90B2DF9A, + /* #02202078 */ 0x7FDDD0BF, + /* #0220207C */ 0x4BF847FD, + /* #02202080 */ 0x7CCF76CE, + /* #02202084 */ 0xCFEF7E1F, + /* #02202088 */ 0x7F1D7DFD, + /* #0220208C */ 0xF0B6EF71, + /* #02202090 */ 0x7FC177C1, + /* #02202094 */ 0xFBC86079, + /* #02202098 */ 0xE722FBC8, + /* #0220209C */ 0x5FFFDFFF, + /* #022020A0 */ 0x5FB2FFFB, + /* #022020A4 */ 0xFBC8F3C8, + /* #022020A8 */ 0x94A67F01, + /* #022020AC */ 0x7F1D5F39, + /* #022020B0 */ 0xAFE85F5E, + /* #022020B4 */ 0xFFDFDF96, + /* #022020B8 */ 0xCB9FAF7D, + /* #022020BC */ 0x5FC1AFED, + /* #022020C0 */ 0x8C1C5FC1, + /* #022020C4 */ 0xAFDD5FC3, + /* #022020C8 */ 0xDF9A7EFD, + /* #022020CC */ 0xB0B25FB2, + /* #022020D0 */ 0xFFFEABAD, + /* #022020D4 */ 0x5FB2FFFE, + /* #022020D8 */ 0x5FCE600B, + /* #022020DC */ 0xE6BB600B, + /* #022020E0 */ 0x5FCEDFC6, + /* #022020E4 */ 0x27FBEFDF, + /* #022020E8 */ 0x5FC8CFDE, + /* #022020EC */ 0x3A9CE7C0, + /* #022020F0 */ 0xEDF0F3C8, + /* #022020F4 */ 0x7F0154CD, + /* #022020F8 */ 0x7F1D2D3D, + /* #022020FC */ 0x363A7570, + /* #02202100 */ 0x7E0AF1CE, + /* #02202104 */ 0x37EF2E68, + /* #02202108 */ 0x7FEE10EC, + /* #0220210C */ 0xADF8EFDE, + /* #02202110 */ 0xCFEAE52F, + /* #02202114 */ 0x7D0FE12B, + /* #02202118 */ 0xF1CE5F65, + /* #0220211C */ 0x7E0A4DF8, + /* #02202120 */ 0xCFEA5F72, + /* #02202124 */ 0x7D0BEFEE, + /* #02202128 */ 0xCFEA5F74, + /* #0220212C */ 0xE522EFDE, + /* #02202130 */ 0x5F74CFDA, + /* #02202134 */ 0x0B627385, + /* #02202138 */ 0xDF627E0A, + /* #0220213C */ 0x30D8145B, + /* #02202140 */ 0xBFFFF3C8, + /* #02202144 */ 0x5FFFDFFF, + /* #02202148 */ 0xA7F85F5E, + /* #0220214C */ 0xBFFE7F7D, + /* #02202150 */ 0x10D31450, + /* #02202154 */ 0x5F36BFFF, + /* #02202158 */ 0xAF785F5E, + /* #0220215C */ 0xBFFDA7F8, + /* #02202160 */ 0x5F36BFFE, + /* #02202164 */ 0x77FD30C0, + /* #02202168 */ 0x4E08FDCF, + /* #0220216C */ 0xE5FF6E0F, + /* #02202170 */ 0xAFF87E1F, + /* #02202174 */ 0x7E0FFD1F, + /* #02202178 */ 0xF1CF5F1B, + /* #0220217C */ 0xABF80D5E, + /* #02202180 */ 0x5F5EFFEF, + /* #02202184 */ 0x79F730A2, + /* #02202188 */ 0xAFDD5F34, + /* #0220218C */ 0x47F85F34, + /* #02202190 */ 0xAFED7FDD, + /* #02202194 */ 0x50B24978, + /* #02202198 */ 0x47FD7F1D, + /* #0220219C */ 0x7DFD70AD, + /* #022021A0 */ 0xEF717EC1, + /* #022021A4 */ 0x6BA47F01, + /* #022021A8 */ 0x2D267EFD, + /* #022021AC */ 0x30DE5F5E, + /* #022021B0 */ 0xFFFD5F5E, + /* #022021B4 */ 0xFFEF5F5E, + /* #022021B8 */ 0xFFDF0CA0, + /* #022021BC */ 0xAFED0A9E, + /* #022021C0 */ 0xAFDD0C3A, + /* #022021C4 */ 0x5F3AAFBD, + /* #022021C8 */ 0x7FBDB082, + /* #022021CC */ 0x5F8247F8, +}; + +/*S00600004844521B*/ +static ulong ubase2 = 0x2F00; +static ulong ucode2[] = { + /* #02202F00 */ 0x3E303430, + /* #02202F04 */ 0x34343737, + /* #02202F08 */ 0xABF7BF9B, + /* #02202F0C */ 0x994B4FBD, + /* #02202F10 */ 0xBD599493, + /* #02202F14 */ 0x349FFF37, + /* #02202F18 */ 0xFB9B177D, + /* #02202F1C */ 0xD9936956, + /* #02202F20 */ 0xBBFDD697, + /* #02202F24 */ 0xBDD2FD11, + /* #02202F28 */ 0x31DB9BB3, + /* #02202F2C */ 0x63139637, + /* #02202F30 */ 0x93733693, + /* #02202F34 */ 0x193137F7, + /* #02202F38 */ 0x331737AF, + /* #02202F3C */ 0x7BB9B999, + /* #02202F40 */ 0xBB197957, + /* #02202F44 */ 0x7FDFD3D5, + /* #02202F48 */ 0x73B773F7, + /* #02202F4C */ 0x37933B99, + /* #02202F50 */ 0x1D115316, + /* #02202F54 */ 0x99315315, + /* #02202F58 */ 0x31694BF4, + /* #02202F5C */ 0xFBDBD359, + /* #02202F60 */ 0x31497353, + /* #02202F64 */ 0x76956D69, + /* #02202F68 */ 0x7B9D9693, + /* #02202F6C */ 0x13131979, + /* #02202F70 */ 0x79376935, +}; + +/* + * compensate for chip design botch by installing + * microcode to relocate I2C and SPI parameters away + * from the ethernet parameters + */ +static void +i2cspireloc(void) +{ + IMM *io; + static int done; + + if(done) + return; + io = m->iomem; + io->rccr &= ~3; + memmove((uchar*)m->iomem+ubase1, ucode1, sizeof(ucode1)); + memmove((uchar*)m->iomem+ubase2, ucode2, sizeof(ucode2)); + io->rctr1 = 0x802a; /* relocate SPI */ + io->rctr2 = 0x8028; /* relocate SPI */ + io->rctr3 = 0x802e; /* relocate I2C */ + io->rctr4 = 0x802c; /* relocate I2C */ + io->rccr |= 1; + done = 1; +} diff --git a/os/mpc/cpmtimer.c b/os/mpc/cpmtimer.c new file mode 100644 index 00000000..bda25dcf --- /dev/null +++ b/os/mpc/cpmtimer.c @@ -0,0 +1,179 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +enum { + Ntimer = 4 /* maximum allowed by hardware */ +}; + +static struct { + Lock; + int init; + int ntimer; /* actual timers on this chip revision */ + GTimer t[Ntimer]; +} cpmtimers; + +static uchar timerirq[] = {0x19, 0x12, 0x0C, 0x07}; + +static void gtimerinit(int, ushort*, ushort*); + +static void +gtimerreset(void) +{ + IMM *io; + int i; + + ilock(&cpmtimers); + if(!cpmtimers.init){ + if(m->cputype == 0x50 && (getimmr() & 0xFFFF) <= 0x2001) + cpmtimers.ntimer = 2; + else + cpmtimers.ntimer = Ntimer; + io = m->iomem; + io->tgcr = 0x2222; /* reset timers, low-power stop */ + for(i=0; itmr1+i, &io->ter1+i); + cpmtimers.init = 1; + } + iunlock(&cpmtimers); +} + +static void +gtimerintr(Ureg *ur, void *arg) +{ + GTimer *t; + + t = arg; + t->event = *t->ter; + *t->ter = t->event; + if(t->inuse && t->interrupt != nil) + t->interrupt(ur, t->arg, t); +} + +static void +gtimerinit(int i, ushort *tmr, ushort *ter) +{ + GTimer *t; + char name[KNAMELEN]; + + snprint(name, sizeof(name), "timer.%d", i); + t = &cpmtimers.t[i]; + t->x = i*4; /* field in tgcr */ + t->inuse = 0; + t->interrupt = nil; + t->tmr = tmr; + t->trr = tmr+2; + t->tcr = tmr+4; + t->tcn = tmr+6; + t->ter = ter; + intrenable(VectorCPIC+timerirq[i], gtimerintr, t, BUSUNKNOWN, name); +} + +GTimer* +gtimer(ushort mode, ushort ref, void (*intr)(Ureg*,void*,GTimer*), void *arg) +{ + GTimer *t; + int i; + + t = cpmtimers.t; + if(!cpmtimers.init) + gtimerreset(); + ilock(&cpmtimers); + for(i=0; ; i++){ + if(i >= cpmtimers.ntimer){ + iunlock(&cpmtimers); + return nil; + } + if(t->inuse == 0) + break; + t++; + } + t->inuse = 1; + t->interrupt = intr; + t->arg = arg; + m->iomem->tgcr &= ~(0xF<x); /* reset */ + *t->tmr = mode; + *t->tcn = 0; + *t->trr = ref; + *t->ter = 0xFFFF; + iunlock(&cpmtimers); + return t; +} + +void +gtimerset(GTimer *t, ushort mode, int usec) +{ + ulong ref, ps; + int clk; + + if(usec <= 0) + return; + ref = usec*m->speed; + clk = mode & (3<<1); + if(ref >= 0x1000000 && clk == TimerSclk){ + mode = (mode & ~clk) | TimerSclk16; + ref >>= 4; + } else if(clk == TimerSclk16) + ref >>= 4; + ps = (ref+(1<<16))/(1<<16); /* round up */ + ref /= ps; + *t->tmr = ((ps-1)<<8) | (mode&0xFF); + *t->trr = ref; +} + +void +gtimerstart(GTimer *t) +{ + if(t){ + ilock(&cpmtimers); + m->iomem->tgcr = (m->iomem->tgcr & ~(0xF<x)) | (1<x); /* enable */ + iunlock(&cpmtimers); + } +} + +void +gtimerstop(GTimer *t) +{ + if(t){ + ilock(&cpmtimers); + m->iomem->tgcr |= 2<x; /* stop */ + iunlock(&cpmtimers); + } +} + +void +gtimerfree(GTimer *t) +{ + if(t){ + ilock(&cpmtimers); + t->inuse = 0; + *t->tmr = 0; /* disable interrupts */ + *t->ter = 0xFFFF; + m->iomem->tgcr = (m->iomem->tgcr & ~(0xF<x)) | (2<x); /* reset and stop */ + iunlock(&cpmtimers); + } +} + +#ifdef GTIMETEST +static void +gtintr(Ureg*, void*, GTimer*) +{ + m->bcsr[4] ^= DisableVideoLamp; /* toggle an LED */ +} + +void +gtimetest(void) +{ + GTimer *g; + + g = gtimer(0, 0, gtintr, nil); + gtimerset(g, TimerORI|TimerRestart|TimerSclk, 64000); + gtimerstart(g); + delay(1); +print("started timer: #%4.4ux #%4.4ux %8.8lux #%4.4ux #%4.4ux\n", *g->tmr, *g->trr, m->iomem->tgcr, *g->tcn, *g->ter); +print("ter=#%8.8lux tmr=#%8.8lux trr=#%8.8lux\n", g->ter, g->tmr, g->trr); +} +#endif diff --git a/os/mpc/devata.c b/os/mpc/devata.c new file mode 100644 index 00000000..69665e38 --- /dev/null +++ b/os/mpc/devata.c @@ -0,0 +1,1194 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "pcmcia.h" + +#define DPRINT if(0)print + +typedef struct Drive Drive; +typedef struct Ident Ident; +typedef struct Controller Controller; +typedef struct Partition Partition; +typedef struct Repl Repl; + +enum +{ + /* ports */ + Pbase= 0x1F0, + Pdata= 0, /* data port (16 bits) */ + Perror= 1, /* error port (read) */ + Pprecomp= 1, /* buffer mode port (write) */ + Pcount= 2, /* sector count port */ + Psector= 3, /* sector number port */ + Pcyllsb= 4, /* least significant byte cylinder # */ + Pcylmsb= 5, /* most significant byte cylinder # */ + Pdh= 6, /* drive/head port */ + Pstatus= 7, /* status port (read) */ + Sbusy= (1<<7), + Sready= (1<<6), + Sdrq= (1<<3), + Serr= (1<<0), + Pcmd= 7, /* cmd port (write) */ + + /* commands */ + Crecal= 0x10, + Cread= 0x20, + Cwrite= 0x30, + Cident= 0xEC, + Cident2= 0xFF, /* pseudo command for post Cident interrupt */ + Csetbuf= 0xEF, + Cinitparam= 0x91, + + /* conner specific commands */ + Cstandby= 0xE2, + Cidle= 0xE1, + Cpowerdown= 0xE3, + + /* disk states */ + Sspinning, + Sstandby, + Sidle, + Spowerdown, + + /* something we have to or into the drive/head reg */ + DHmagic= 0xA0, + + /* file types */ + Qdir= 0, + + Maxxfer= BY2PG, /* maximum transfer size/cmd */ + Npart= 8+2, /* 8 sub partitions, disk, and partition */ + Nrepl= 64, /* maximum replacement blocks */ +}; +#define PART(x) ((x)&0xF) +#define DRIVE(x) (((x)>>4)&0x7) +#define MKQID(d,p) (((d)<<4) | (p)) + +struct Partition +{ + ulong start; + ulong end; + char name[NAMELEN+1]; +}; + +struct Repl +{ + Partition *p; + int nrepl; + ulong blk[Nrepl]; +}; + +#define PARTMAGIC "plan9 partitions" +#define REPLMAGIC "block replacements" + +/* + * an ata drive + */ +struct Drive +{ + QLock; + + Controller *cp; + int drive; + int confused; /* needs to be recalibrated (or worse) */ + int online; + int npart; /* number of real partitions */ + Partition p[Npart]; + Repl repl; + ulong usetime; + int state; + char vol[NAMELEN]; + + ulong cap; /* total bytes */ + int bytes; /* bytes/sector */ + int sectors; /* sectors/track */ + int heads; /* heads/cyl */ + long cyl; /* cylinders/drive */ + + char lba; /* true if drive has logical block addressing */ + char multi; /* non-zero if drive does multiple block xfers */ +}; + +/* + * a controller for 2 drives + */ +struct Controller +{ + QLock; /* exclusive access to the controller */ + ISAConf; /* interface to pcmspecial */ + + Lock reglock; /* exclusive access to the registers */ + + int confused; /* needs to be recalibrated (or worse) */ + ulong pbase; /* base port (copied from ISAConf) */ + + /* + * current operation + */ + int cmd; /* current command */ + int lastcmd; /* debugging info */ + Rendez r; /* wait here for command termination */ + char *buf; /* xfer buffer */ + int nsecs; /* length of transfer (sectors) */ + int sofar; /* sectors transferred so far */ + int status; + int error; + Drive *dp; /* drive being accessed */ +}; + +Controller *atac; +Drive *ata; +static char* ataerr; +static int nhard; +static int spindowntime; + +static void ataintr(Ureg*, void*); +static long ataxfer(Drive*, Partition*, int, long, long, char*); +static void ataident(Drive*); +static void atasetbuf(Drive*, int); +static void ataparams(Drive*); +static void atapart(Drive*); +static int ataprobe(Drive*, int, int, int); + +static int +atagen(Chan *c, Dirtab*, int, int s, Dir *dirp) +{ + Qid qid; + int drive; + Drive *dp; + Partition *pp; + ulong l; + + qid.vers = 0; + drive = s/Npart; + s = s % Npart; + if(drive >= nhard) + return -1; + dp = &ata[drive]; + + if(dp->online == 0 || s >= dp->npart) + return 0; + + pp = &dp->p[s]; + sprint(up->genbuf, "%s%s", dp->vol, pp->name); + qid.path = MKQID(drive, s); + l = (pp->end - pp->start) * dp->bytes; + devdir(c, qid, up->genbuf, l, eve, 0660, dirp); + return 1; +} + +static void +atainit(void) +{ + Drive *dp; + Controller *cp; + uchar equip; + int pcmslot; + + if (atac) + return; /* already done */ + + equip = 0x10; /* hard coded */ + + print("ata init\n"); + cp = malloc(sizeof(*cp)); + if (!cp) + error(Enomem); + + cp->port = Pbase; + cp->irq = 14; + + if((pcmslot = pcmspecial("SunDisk", cp)) < 0) { + print("No ATA card\n"); + free(cp); + ataerr = Enoifc; + return; + } + ata = malloc(2 * sizeof(*ata)); + if(ata == nil) { + pcmspecialclose(pcmslot); + free(cp); + error(Enomem); + } + + atac = cp; + cp->buf = 0; + cp->lastcmd = cp->cmd; + cp->cmd = 0; + cp->pbase = cp->port; + pcmintrenable(pcmslot, ataintr, cp); + + dp = ata; + if(equip & 0xf0){ + dp->drive = 0; + dp->online = 0; + dp->cp = cp; + dp++; + } + if((equip & 0x0f)){ + dp->drive = 1; + dp->online = 0; + dp->cp = cp; + dp++; + } + nhard = dp - ata; + + spindowntime = 1; +} + + +/* + * Get the characteristics of each drive. Mark unresponsive ones + * off line. + */ +static Chan* +ataattach(char *spec) +{ + Drive *dp; + + atainit(); + if (!ata) + error(ataerr ? ataerr : Enoifc); + for(dp = ata; dp < &ata[nhard]; dp++){ + if(waserror()){ + dp->online = 0; + qunlock(dp); + continue; + } + qlock(dp); + if(!dp->online){ + /* + * Make sure ataclock() doesn't + * interfere. + */ + dp->usetime = m->ticks; + ataparams(dp); + dp->online = 1; + atasetbuf(dp, 1); + } + + /* + * read Plan 9 partition table + */ + atapart(dp); + qunlock(dp); + poperror(); + } + return devattach('H', spec); +} + +static int +atawalk(Chan *c, char *name) +{ + return devwalk(c, name, 0, 0, atagen); +} + +static void +atastat(Chan *c, char *dp) +{ + devstat(c, dp, 0, 0, atagen); +} + +static Chan* +ataopen(Chan *c, int omode) +{ + return devopen(c, omode, 0, 0, atagen); +} + +static void +ataclose(Chan *c) +{ + Drive *d; + Partition *p; + + if(c->mode != OWRITE && c->mode != ORDWR) + return; + + d = &ata[DRIVE(c->qid.path)]; + p = &d->p[PART(c->qid.path)]; + if(strcmp(p->name, "partition") != 0) + return; + + if(waserror()){ + qunlock(d); + nexterror(); + } + qlock(d); + atapart(d); + qunlock(d); + poperror(); +} + +static long +ataread(Chan *c, void *a, long n, vlong offset) +{ + Drive *dp; + long rv, i; + int skip; + uchar *aa = a; + Partition *pp; + char *buf; + + if(c->qid.path == CHDIR) + return devdirread(c, a, n, 0, 0, atagen); + + buf = smalloc(Maxxfer); + if(waserror()){ + free(buf); + nexterror(); + } + + dp = &ata[DRIVE(c->qid.path)]; + pp = &dp->p[PART(c->qid.path)]; + + skip = offset % dp->bytes; + for(rv = 0; rv < n; rv += i){ + i = ataxfer(dp, pp, Cread, offset+rv-skip, n-rv+skip, buf); + if(i == 0) + break; + i -= skip; + if(i > n - rv) + i = n - rv; + memmove(aa+rv, buf + skip, i); + skip = 0; + } + + free(buf); + poperror(); + + return rv; +} + +static long +atawrite(Chan *c, void *a, long n, vlong offset) +{ + Drive *dp; + long rv, i, partial; + uchar *aa = a; + Partition *pp; + char *buf; + + if(c->qid.path == CHDIR) + error(Eisdir); + + dp = &ata[DRIVE(c->qid.path)]; + pp = &dp->p[PART(c->qid.path)]; + buf = smalloc(Maxxfer); + if(waserror()){ + free(buf); + nexterror(); + } + + /* + * if not starting on a sector boundary, + * read in the first sector before writing + * it out. + */ + partial = offset % dp->bytes; + if(partial){ + ataxfer(dp, pp, Cread, offset-partial, dp->bytes, buf); + if(partial+n > dp->bytes) + rv = dp->bytes - partial; + else + rv = n; + memmove(buf+partial, aa, rv); + ataxfer(dp, pp, Cwrite, offset-partial, dp->bytes, buf); + } else + rv = 0; + + /* + * write out the full sectors + */ + partial = (n - rv) % dp->bytes; + n -= partial; + for(; rv < n; rv += i){ + i = n - rv; + if(i > Maxxfer) + i = Maxxfer; + memmove(buf, aa+rv, i); + i = ataxfer(dp, pp, Cwrite, offset+rv, i, buf); + if(i == 0) + break; + } + + /* + * if not ending on a sector boundary, + * read in the last sector before writing + * it out. + */ + if(partial){ + ataxfer(dp, pp, Cread, offset+rv, dp->bytes, buf); + memmove(buf, aa+rv, partial); + ataxfer(dp, pp, Cwrite, offset+rv, dp->bytes, buf); + rv += partial; + } + + free(buf); + poperror(); + + return rv; +} + +/* + * did an interrupt happen? + */ +static int +cmddone(void *a) +{ + Controller *cp = a; + + return cp->cmd == 0; +} + +/* + * Wait for the controller to be ready to accept a command. + * This is protected from intereference by ataclock() by + * setting dp->usetime before it is called. + */ +static void +cmdreadywait(Drive *dp) +{ + long start; + int period; + Controller *cp = dp->cp; + + /* give it 2 seconds to spin down and up */ + if(dp->state == Sspinning) + period = 10; + else + period = 2000; + + start = m->ticks; + while((inb(cp->pbase+Pstatus) & (Sready|Sbusy)) != Sready) + if(TK2MS(m->ticks - start) > period){ + DPRINT("cmdreadywait failed\n"); + error(Eio); + } +} + +static void +atarepl(Drive *dp, long bblk) +{ + int i; + + if(dp->repl.p == 0) + return; + for(i = 0; i < dp->repl.nrepl; i++){ + if(dp->repl.blk[i] == bblk) + DPRINT("found bblk %ld at offset %ld\n", bblk, i); + } +} + +static void +atasleep(Controller *cp, int ms) +{ + tsleep(&cp->r, cmddone, cp, ms); + if(cp->cmd && cp->cmd != Cident2){ + DPRINT("ata: cmd 0x%uX timeout, status=%lux\n", + cp->cmd, inb(cp->pbase+Pstatus)); + error("ata drive timeout"); + } +} + +/* + * transfer a number of sectors. ataintr will perform all the iterative + * parts. + */ +static long +ataxfer(Drive *dp, Partition *pp, int cmd, long start, long len, char *buf) +{ + Controller *cp; + long lblk; + int cyl, sec, head; + int loop, stat; + + if(dp->online == 0) + error(Eio); + + /* + * cut transfer size down to disk buffer size + */ + start = start / dp->bytes; + if(len > Maxxfer) + len = Maxxfer; + len = (len + dp->bytes - 1) / dp->bytes; + if(len == 0) + return 0; + + /* + * calculate physical address + */ + lblk = start + pp->start; + if(lblk >= pp->end) + return 0; + if(lblk+len > pp->end) + len = pp->end - lblk; + if(dp->lba){ + sec = lblk & 0xff; + cyl = (lblk>>8) & 0xffff; + head = (lblk>>24) & 0xf; + } else { + cyl = lblk/(dp->sectors*dp->heads); + sec = (lblk % dp->sectors) + 1; + head = ((lblk/dp->sectors) % dp->heads); + } + + DPRINT("<%s %d>", (cmd == Cwrite) ? "W" : "R", lblk); + cp = dp->cp; + qlock(cp); + if(waserror()){ + cp->buf = 0; + qunlock(cp); + nexterror(); + } + + /* + * Make sure hardclock() doesn't + * interfere. + */ + dp->usetime = m->ticks; + cmdreadywait(dp); + + ilock(&cp->reglock); + cp->sofar = 0; + cp->buf = buf; + cp->nsecs = len; + cp->cmd = cmd; + cp->dp = dp; + cp->status = 0; + + outb(cp->pbase+Pcount, cp->nsecs); + outb(cp->pbase+Psector, sec); + outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4) | (dp->lba<<6) | head); + outb(cp->pbase+Pcyllsb, cyl); + outb(cp->pbase+Pcylmsb, cyl>>8); + outb(cp->pbase+Pcmd, cmd); + + if(cmd == Cwrite){ + loop = 0; + microdelay(1); + while((stat = inb(cp->pbase+Pstatus) & (Serr|Sdrq)) == 0) + if(++loop > 10000) + panic("ataxfer"); + outss(cp->pbase+Pdata, cp->buf, dp->bytes/2); + } else + stat = 0; + iunlock(&cp->reglock); + + if(stat & Serr) + error(Eio); + + /* + * wait for command to complete. if we get a note, + * remember it but keep waiting to let the disk finish + * the current command. + */ + loop = 0; + while(waserror()){ + DPRINT("interrupted ataxfer\n"); + if(loop++ > 10){ + print("ata disk error\n"); + nexterror(); + } + } + atasleep(cp, 3000); + dp->state = Sspinning; + dp->usetime = m->ticks; + poperror(); + if(loop) + nexterror(); + + if(cp->status & Serr){ + DPRINT("hd%d err: lblk %ld status %lux, err %lux\n", + dp-ata, lblk, cp->status, cp->error); + DPRINT("\tcyl %d, sec %d, head %d\n", cyl, sec, head); + DPRINT("\tnsecs %d, sofar %d\n", cp->nsecs, cp->sofar); + atarepl(dp, lblk+cp->sofar); + error(Eio); + } + cp->buf = 0; + len = cp->sofar*dp->bytes; + qunlock(cp); + poperror(); + + return len; +} + +/* + * set read ahead mode + */ +static void +atasetbuf(Drive *dp, int on) +{ + Controller *cp = dp->cp; + + qlock(cp); + if(waserror()){ + qunlock(cp); + nexterror(); + } + + cmdreadywait(dp); + + ilock(&cp->reglock); + cp->cmd = Csetbuf; + outb(cp->pbase+Pprecomp, on ? 0xAA : 0x55); /* read look ahead */ + outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4)); + outb(cp->pbase+Pcmd, Csetbuf); + iunlock(&cp->reglock); + + atasleep(cp, 5000); + +/* if(cp->status & Serr) + DPRINT("hd%d setbuf err: status %lux, err %lux\n", + dp-ata, cp->status, cp->error);/**/ + + poperror(); + qunlock(cp); +} + +/* + * ident sector from drive. this is from ANSI X3.221-1994 + */ +struct Ident +{ + ushort config; /* general configuration info */ + ushort cyls; /* # of cylinders (default) */ + ushort reserved0; + ushort heads; /* # of heads (default) */ + ushort b2t; /* unformatted bytes/track */ + ushort b2s; /* unformated bytes/sector */ + ushort s2t; /* sectors/track (default) */ + ushort reserved1[3]; +/* 10 */ + ushort serial[10]; /* serial number */ + ushort type; /* buffer type */ + ushort bsize; /* buffer size/512 */ + ushort ecc; /* ecc bytes returned by read long */ + ushort firm[4]; /* firmware revision */ + ushort model[20]; /* model number */ +/* 47 */ + ushort s2i; /* number of sectors/interrupt */ + ushort dwtf; /* double word transfer flag */ + ushort capabilities; + ushort reserved2; + ushort piomode; + ushort dmamode; + ushort cvalid; /* (cvald&1) if next 4 words are valid */ + ushort ccyls; /* current # cylinders */ + ushort cheads; /* current # heads */ + ushort cs2t; /* current sectors/track */ + ushort ccap[2]; /* current capacity in sectors */ + ushort cs2i; /* current number of sectors/interrupt */ +/* 60 */ + ushort lbasecs[2]; /* # LBA user addressable sectors */ + ushort dmasingle; + ushort dmadouble; +/* 64 */ + ushort reserved3[64]; + ushort vendor[32]; /* vendor specific */ + ushort reserved4[96]; +}; + +/* + * get parameters from the drive + */ +static void +ataident(Drive *dp) +{ + Controller *cp; + char *buf; + Ident *ip; + char id[21]; + + cp = dp->cp; + buf = smalloc(Maxxfer); + qlock(cp); + if(waserror()){ + cp->buf = 0; + qunlock(cp); + free(buf); + nexterror(); + } + + cmdreadywait(dp); + + ilock(&cp->reglock); + cp->nsecs = 1; + cp->sofar = 0; + cp->cmd = Cident; + cp->dp = dp; + cp->buf = buf; + outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4)); + outb(cp->pbase+Pcmd, Cident); + iunlock(&cp->reglock); + + atasleep(cp, 5000); + if(cp->status & Serr){ + DPRINT("bad disk ident status\n"); + error(Eio); + } + ip = (Ident*)buf; + + /* + * this function appears to respond with an extra interrupt after + * the ident information is read, except on the safari. The following + * delay gives this extra interrupt a chance to happen while we are quiet. + * Otherwise, the interrupt may come during a subsequent read or write, + * causing a panic and much confusion. + */ + if (cp->cmd == Cident2) + tsleep(&cp->r, return0, 0, 10); + + memmove(id, ip->model, sizeof(id)-1); + id[sizeof(id)-1] = 0; + + if(ip->capabilities & (1<<9)){ + dp->lba = 1; + dp->sectors = (ip->lbasecs[0]) | (ip->lbasecs[1]<<16); + dp->cap = dp->bytes * dp->sectors; +/*print("\nata%d model %s with %d lba sectors\n", dp->drive, id, dp->sectors);/**/ + } else { + dp->lba = 0; + + /* use default (unformatted) settings */ + dp->cyl = ip->cyls; + dp->heads = ip->heads; + dp->sectors = ip->s2t; +/*print("\nata%d model %s with default %d cyl %d head %d sec\n", dp->drive, + id, dp->cyl, dp->heads, dp->sectors);/**/ + + if(ip->cvalid&(1<<0)){ + /* use current settings */ + dp->cyl = ip->ccyls; + dp->heads = ip->cheads; + dp->sectors = ip->cs2t; +/*print("\tchanged to %d cyl %d head %d sec\n", dp->cyl, dp->heads, dp->sectors);/**/ + } + dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors; + } + cp->lastcmd = cp->cmd; + cp->cmd = 0; + cp->buf = 0; + free(buf); + poperror(); + qunlock(cp); +} + +/* + * probe the given sector to see if it exists + */ +static int +ataprobe(Drive *dp, int cyl, int sec, int head) +{ + Controller *cp; + char *buf; + int rv; + + cp = dp->cp; + buf = smalloc(Maxxfer); + qlock(cp); + if(waserror()){ + free(buf); + qunlock(cp); + nexterror(); + } + + cmdreadywait(dp); + + ilock(&cp->reglock); + cp->cmd = Cread; + cp->dp = dp; + cp->status = 0; + cp->nsecs = 1; + cp->sofar = 0; + + outb(cp->pbase+Pcount, 1); + outb(cp->pbase+Psector, sec+1); + outb(cp->pbase+Pdh, DHmagic | head | (dp->lba<<6) | (dp->drive<<4)); + outb(cp->pbase+Pcyllsb, cyl); + outb(cp->pbase+Pcylmsb, cyl>>8); + outb(cp->pbase+Pcmd, Cread); + iunlock(&cp->reglock); + + atasleep(cp, 5000); + + if(cp->status & Serr) + rv = -1; + else + rv = 0; + + cp->buf = 0; + free(buf); + poperror(); + qunlock(cp); + return rv; +} + +/* + * figure out the drive parameters + */ +static void +ataparams(Drive *dp) +{ + int i, hi, lo; + + /* + * first try the easy way, ask the drive and make sure it + * isn't lying. + */ + dp->bytes = 512; + ataident(dp); + if(dp->lba){ + i = dp->sectors - 1; + if(ataprobe(dp, (i>>8)&0xffff, (i&0xff)-1, (i>>24)&0xf) == 0) + return; + } else { + if(ataprobe(dp, dp->cyl-1, dp->sectors-1, dp->heads-1) == 0) + return; + } + + /* + * the drive lied, determine parameters by seeing which ones + * work to read sectors. + */ + dp->lba = 0; + for(i = 0; i < 32; i++) + if(ataprobe(dp, 0, 0, i) < 0) + break; + dp->heads = i; + for(i = 0; i < 128; i++) + if(ataprobe(dp, 0, i, 0) < 0) + break; + dp->sectors = i; + for(i = 512; ; i += 512) + if(ataprobe(dp, i, dp->sectors-1, dp->heads-1) < 0) + break; + lo = i - 512; + hi = i; + for(; hi-lo > 1;){ + i = lo + (hi - lo)/2; + if(ataprobe(dp, i, dp->sectors-1, dp->heads-1) < 0) + hi = i; + else + lo = i; + } + dp->cyl = lo + 1; + dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors; +} + +/* + * Read block replacement table. + * The table is just ascii block numbers. + */ +static void +atareplinit(Drive *dp) +{ + char *line[Nrepl+1]; + char *field[1]; + ulong n; + int i; + char *buf; + + /* + * check the partition is big enough + */ + if(dp->repl.p->end - dp->repl.p->start < Nrepl+1){ + dp->repl.p = 0; + return; + } + + buf = smalloc(Maxxfer); + if(waserror()){ + free(buf); + nexterror(); + } + + /* + * read replacement table from disk, null terminate + */ + ataxfer(dp, dp->repl.p, Cread, 0, dp->bytes, buf); + buf[dp->bytes-1] = 0; + + /* + * parse replacement table. + */ + n = getfields(buf, line, Nrepl+1, 1, "\n"); + if(strncmp(line[0], REPLMAGIC, sizeof(REPLMAGIC)-1)){ + dp->repl.p = 0; + } else { + for(dp->repl.nrepl = 0, i = 1; i < n; i++, dp->repl.nrepl++){ + if(getfields(line[i], field, 1, 1, " ") != 1) + break; + dp->repl.blk[dp->repl.nrepl] = strtoul(field[0], 0, 0); + if(dp->repl.blk[dp->repl.nrepl] <= 0) + break; + } + } + free(buf); + poperror(); +} + +/* + * read partition table. The partition table is just ascii strings. + */ +static void +atapart(Drive *dp) +{ + Partition *pp; + char *line[Npart+1]; + char *field[3]; + ulong n; + int i; + char *buf; + + sprint(dp->vol, "hd%d", dp - ata); + + /* + * we always have a partition for the whole disk + * and one for the partition table + */ + pp = &dp->p[0]; + strcpy(pp->name, "disk"); + pp->start = 0; + pp->end = dp->cap / dp->bytes; + pp++; + strcpy(pp->name, "partition"); + pp->start = dp->p[0].end - 1; + pp->end = dp->p[0].end; + pp++; + dp->npart = 2; + + /* + * initialise the bad-block replacement info + */ + dp->repl.p = 0; + + buf = smalloc(Maxxfer); + if(waserror()){ + free(buf); + nexterror(); + } + + /* + * read last sector from disk, null terminate. This used + * to be the sector we used for the partition tables. + * However, this sector is special on some PC's so we've + * started to use the second last sector as the partition + * table instead. To avoid reconfiguring all our old systems + * we first look to see if there is a valid partition + * table in the last sector. If so, we use it. Otherwise + * we switch to the second last. + */ + ataxfer(dp, dp->p+1, Cread, 0, dp->bytes, buf); + buf[dp->bytes-1] = 0; + n = getfields(buf, line, Npart+1, 1, "\n"); + if(n == 0 || strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1)){ + dp->p[0].end--; + dp->p[1].start--; + dp->p[1].end--; + ataxfer(dp, dp->p+1, Cread, 0, dp->bytes, buf); + buf[dp->bytes-1] = 0; + n = getfields(buf, line, Npart+1, 1, "\n"); + } + + /* + * parse partition table. + */ + if(n > 0 && strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1) == 0){ + for(i = 1; i < n; i++){ + switch(getfields(line[i], field, 3, 1, " ")) { + case 2: + if(strcmp(field[0], "unit") == 0) + strncpy(dp->vol, field[1], NAMELEN); + break; + case 3: + strncpy(pp->name, field[0], NAMELEN); + if(strncmp(pp->name, "repl", NAMELEN) == 0) + dp->repl.p = pp; + pp->start = strtoul(field[1], 0, 0); + pp->end = strtoul(field[2], 0, 0); + if(pp->start > pp->end || pp->end > dp->p[0].end) + break; + dp->npart++; + pp++; + } + } + } + free(buf); + poperror(); + + if(dp->repl.p) + atareplinit(dp); +} + +enum +{ + Maxloop= 10000, +}; + +/* + * we get an interrupt for every sector transferred + */ +static void +ataintr(Ureg*, void *arg) +{ + Controller *cp; + Drive *dp; + long loop; + char *addr; + + cp = arg; + dp = cp->dp; + + ilock(&cp->reglock); + + loop = 0; + while((cp->status = inb(cp->pbase+Pstatus)) & Sbusy){ + if(++loop > Maxloop) { + DPRINT("cmd=%lux status=%lux\n", + cp->cmd, inb(cp->pbase+Pstatus)); + panic("ataintr: wait busy"); + } + } + + switch(cp->cmd){ + case Cwrite: + if(cp->status & Serr){ + cp->lastcmd = cp->cmd; + cp->cmd = 0; + cp->error = inb(cp->pbase+Perror); + wakeup(&cp->r); + break; + } + cp->sofar++; + if(cp->sofar < cp->nsecs){ + loop = 0; + while(((cp->status = inb(cp->pbase+Pstatus)) & Sdrq) == 0) + if(++loop > Maxloop) { + DPRINT("cmd=%lux status=%lux\n", + cp->cmd, inb(cp->pbase+Pstatus)); + panic("ataintr: write"); + } + addr = cp->buf; + if(addr){ + addr += cp->sofar*dp->bytes; + outss(cp->pbase+Pdata, addr, dp->bytes/2); + } + } else{ + cp->lastcmd = cp->cmd; + cp->cmd = 0; + wakeup(&cp->r); + } + break; + case Cread: + case Cident: + loop = 0; + while((cp->status & (Serr|Sdrq)) == 0){ + if(++loop > Maxloop) { + DPRINT("cmd=%lux status=%lux\n", + cp->cmd, inb(cp->pbase+Pstatus)); + panic("ataintr: read/ident"); + } + cp->status = inb(cp->pbase+Pstatus); + } + if(cp->status & Serr){ + cp->lastcmd = cp->cmd; + cp->cmd = 0; + cp->error = inb(cp->pbase+Perror); + wakeup(&cp->r); + break; + } + addr = cp->buf; + if(addr){ + addr += cp->sofar*dp->bytes; + inss(cp->pbase+Pdata, addr, dp->bytes/2); + } + cp->sofar++; + if(cp->sofar > cp->nsecs) + print("ataintr %d %d\n", cp->sofar, cp->nsecs); + if(cp->sofar >= cp->nsecs){ + cp->lastcmd = cp->cmd; + if (cp->cmd == Cread) + cp->cmd = 0; + else + cp->cmd = Cident2; + wakeup(&cp->r); + } + break; + case Cinitparam: + case Csetbuf: + case Cidle: + case Cstandby: + case Cpowerdown: + cp->lastcmd = cp->cmd; + cp->cmd = 0; + wakeup(&cp->r); + break; + case Cident2: + cp->lastcmd = cp->cmd; + cp->cmd = 0; + break; + default: + print("weird disk interrupt, cmd=%.2ux, lastcmd= %.2ux status=%.2ux\n", + cp->cmd, cp->lastcmd, cp->status); + break; + } + + iunlock(&cp->reglock); +} + +void +hardclock(void) +{ + int drive; + Drive *dp; + Controller *cp; + int diff; + + if(spindowntime <= 0) + return; + + for(drive = 0; drive < nhard; drive++){ + dp = &ata[drive]; + cp = dp->cp; + + diff = TK2SEC(m->ticks - dp->usetime); + if((dp->state == Sspinning) && (diff >= spindowntime)){ + ilock(&cp->reglock); + cp->cmd = Cstandby; + outb(cp->pbase+Pcount, 0); + outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4) | 0); + outb(cp->pbase+Pcmd, cp->cmd); + iunlock(&cp->reglock); + dp->state = Sstandby; + } + } +} + +Dev atadevtab = { + 'H', + "ata", + + devreset, + atainit, + ataattach, + devdetach, + devclone, + atawalk, + atastat, + ataopen, + devcreate, + ataclose, + ataread, + devbread, + atawrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/mpc/devbench.c b/os/mpc/devbench.c new file mode 100644 index 00000000..a35a57b0 --- /dev/null +++ b/os/mpc/devbench.c @@ -0,0 +1,243 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +typedef struct Psync Psync; + +enum { + Maxprocs=2, +}; + +struct Psync { + Rendez r; + int flag; +}; +static Psync timesync[Maxprocs]; +static Ref nactive; +static Ref nbusy; + +static int +timev(void *a) +{ + return *(int*)a; +} + +static void +timesched0(void *ap) +{ + long tot, t, i, lim, low, max; + Psync *ps; + + ps = ap; + sleep(&ps->r, timev, &ps->flag); + setpri(PriRealtime); + incref(&nbusy); + while(nbusy.ref < nactive.ref) + sched(); + lim = 1000; + low = 64000000; + max = 0; + tot = 0; + for(i=0; ipid); + do{ + t = gettbl(); + sched(); + t = gettbl()-t; + }while(t < 0); + if(t < low) + low = t; + if(t > max) + max = t; + tot += t; + } + print("%lud %lud %lud %lud %lud\n", up->pid, lim, tot, low, max); + decref(&nactive); + pexit("", 0); +} + +static void +timesched(void) +{ + int i, np; + + for(np=1; np<=Maxprocs; np++){ + nactive.ref = np; + print("%d procs\n", np); + setpri(PriRealtime); + for(i=0; i0) + sched(); + } +} + +typedef struct Ictr Ictr; +struct Ictr { + ulong base; + ulong sleep; + ulong spllo; + ulong intr; + ulong isave; + ulong arrive; + ulong wakeup; + ulong awake; +}; +static Ictr counters[100], *curct; +static int intrwant; +static Rendez vous; +int spltbl; /* set by spllo */ +int intrtbl; /* set by intrvec() */ +int isavetbl; /* set by intrvec() */ + +static void +intrwake(Ureg*, void*) +{ + m->iomem->tgcr &= ~1; /* reset the timer */ + curct->spllo = spltbl; + curct->intr = intrtbl; + curct->isave = isavetbl; + curct->arrive = gettbl(); + intrwant = 0; + wakeup(&vous); + curct->wakeup = gettbl(); +} + +/* + * sleep calls intrtest with splhi (under lock): + * provoke the interrupt now, so that it is guaranteed + * not to happen until sleep has queued the process, + * forcing wakeup to do something. + */ +static int +intrtest(void*) +{ + m->iomem->tgcr |= 1; /* enable timer: allow interrupt */ + curct->sleep = gettbl(); + return intrwant==0; +} + +static void +intrtime(void) +{ + IMM *io; + Ictr *ic; + long t; + int i; + + sched(); + curct = counters; + io = ioplock(); + io->tgcr &= ~3; + iopunlock(); + intrenable(VectorCPIC+0x19, intrwake, nil, BUSUNKNOWN, "bench"); + for(i=0; iiomem; /* don't lock, to save time */ + io->tmr1 = (0<<8)|TimerORI|TimerSclk; + io->trr1 = 1; + curct->base = gettbl(); + sleep(&vous, intrtest, nil); + curct->awake = gettbl(); + sched(); /* just to slow it down between trials */ + } + m->iomem->tmr1 = 0; + print("interrupt\n"); + for(i=0; i<20; i++){ + ic = &counters[i]; + t = ic->awake - ic->base; + ic->awake -= ic->wakeup; + ic->wakeup -= ic->arrive; + ic->arrive -= ic->isave; + ic->isave -= ic->intr; + ic->intr -= ic->spllo; + ic->spllo -= ic->sleep; + ic->sleep -= ic->base; + print("%ld\t%ld\t%ld\t%ld\t%ld\t%ld\t%ld\t%ld\n", ic->sleep, ic->spllo, ic->intr, ic->isave, ic->arrive, ic->wakeup, ic->awake, t); + } +} + +static Chan* +benchattach(char *spec) +{ + timesched(); + intrtime(); + USED(spec); + error(Eperm); + return nil; +} + +static Walkqid* +benchwalk(Chan*, Chan*, char**, int) +{ + error(Enonexist); + return 0; +} + +static Chan* +benchopen(Chan*, int) +{ + error(Eperm); + return nil; +} + +static int +benchstat(Chan*, uchar*, int) +{ + error(Eperm); + return 0; +} + +static void +benchclose(Chan*) +{ +} + +static long +benchread(Chan *c, void *buf, long n, vlong offset) +{ + USED(c, buf, n, offset); + error(Eperm); + return 0; +} + +static long +benchwrite(Chan *c, void *buf, long n, vlong offset) +{ + USED(c, buf, n, offset); + error(Eperm); + return 0; +} + + +Dev benchdevtab = { + 'x', + "bench", + + devreset, + devinit, + devshutdown, + benchattach, + benchwalk, + benchstat, + benchopen, + devcreate, + benchclose, + benchread, + devbread, + benchwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/mpc/devboot.c b/os/mpc/devboot.c new file mode 100644 index 00000000..126fe6e6 --- /dev/null +++ b/os/mpc/devboot.c @@ -0,0 +1,132 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +enum{ + Qdir, + Qboot, + Qmem, +}; + +Dirtab bootdir[]={ + ".", {Qdir,0,QTDIR}, 0, 0555, + "boot", {Qboot}, 0, 0666, + "mem", {Qmem}, 0, 0666, +}; + +#define NBOOT (sizeof bootdir/sizeof(Dirtab)) + +static void +bootreset(void) +{ +} + +static Chan* +bootattach(char *spec) +{ + return devattach('B', spec); +} + +static Walkqid* +bootwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, bootdir, NBOOT, devgen); +} + +static int +bootstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, bootdir, NBOOT, devgen); +} + +static Chan* +bootopen(Chan *c, int omode) +{ + return devopen(c, omode, bootdir, NBOOT, devgen); +} + +static void +bootclose(Chan*) +{ +} + +static long +bootread(Chan *c, void *buf, long n, vlong off) +{ + ulong offset = off; + + switch((ulong)c->qid.path){ + + case Qdir: + return devdirread(c, buf, n, bootdir, NBOOT, devgen); + + case Qmem: + /* kernel memory */ + if(offset>=KZERO && offset KZERO+conf.npage*BY2PG) + n = KZERO+conf.npage*BY2PG - offset; + memmove(buf, (char*)offset, n); + return n; + } + error(Ebadarg); + } + + error(Egreg); + return 0; /* not reached */ +} + +static long +bootwrite(Chan *c, void *buf, long n, vlong off) +{ + ulong offset = off; + ulong pc; + uchar *p; + + switch((ulong)c->qid.path){ + case Qmem: + /* kernel memory */ + if(offset>=KZERO && offset KZERO+conf.npage*BY2PG) + n = KZERO+conf.npage*BY2PG - offset; + memmove((char*)offset, buf, n); + segflush((void*)offset, n); + return n; + } + error(Ebadarg); + + case Qboot: + p = (uchar*)buf; + pc = (((((p[0]<<8)|p[1])<<8)|p[2])<<8)|p[3]; + if(pc < KZERO || pc >= KZERO+conf.npage*BY2PG) + error(Ebadarg); + splhi(); + segflush((void*)pc, 64*1024); + gotopc(pc); + } + error(Ebadarg); + return 0; /* not reached */ +} + +Dev bootdevtab = { + 'B', + "boot", + + bootreset, + devinit, + devshutdown, + bootattach, + bootwalk, + bootstat, + bootopen, + devcreate, + bootclose, + bootread, + devbread, + bootwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/mpc/devether.c b/os/mpc/devether.c new file mode 100644 index 00000000..eb4ed283 --- /dev/null +++ b/os/mpc/devether.c @@ -0,0 +1,617 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +static Ether *etherxx[MaxEther]; + +Chan* +etherattach(char* spec) +{ + ulong ctlrno; + char *p; + Chan *chan; + Ether *ether; + + ctlrno = 0; + if(spec && *spec){ + ctlrno = strtoul(spec, &p, 0); + if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther)) + error(Ebadarg); + } + if((ether = etherxx[ctlrno]) == 0) + error(Enodev); + rlock(ether); + if(waserror()){ + runlock(ether); + nexterror(); + } + chan = devattach('l', spec); + chan->dev = ctlrno; + if(ether->attach) + ether->attach(etherxx[ctlrno]); + poperror(); + runlock(ether); + return chan; +} + +static void +ethershutdown(void) +{ + Ether *ether; + int i; + + for(i=0; idetach != nil) + ether->detach(ether); + } +} + +static Walkqid* +etherwalk(Chan* chan, Chan *nchan, char **name, int nname) +{ + Walkqid *wq; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + wq = netifwalk(etherxx[chan->dev], chan, nchan, name, nname); + poperror(); + runlock(ether); + return wq; +} + +static int +etherstat(Chan* chan, uchar* dp, int n) +{ + int s; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + s = netifstat(ether, chan, dp, n); + poperror(); + runlock(ether); + return s; +} + +static Chan* +etheropen(Chan* chan, int omode) +{ + Chan *c; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + c = netifopen(ether, chan, omode); + poperror(); + runlock(ether); + return c; +} + +static void +ethercreate(Chan*, char*, int, ulong) +{ +} + +static void +etherclose(Chan* chan) +{ + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + netifclose(ether, chan); + poperror(); + runlock(ether); +} + +static long +etherread(Chan* chan, void* buf, long n, vlong off) +{ + Ether *ether; + ulong offset = off; + long r; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + if((chan->qid.type & QTDIR) == 0 && ether->ifstat){ + /* + * With some controllers it is necessary to reach + * into the chip to extract statistics. + */ + if(NETTYPE(chan->qid.path) == Nifstatqid){ + r = ether->ifstat(ether, buf, n, offset); + goto out; + } + if(NETTYPE(chan->qid.path) == Nstatqid) + ether->ifstat(ether, buf, 0, offset); + } + r = netifread(ether, chan, buf, n, offset); +out: + poperror(); + runlock(ether); + return r; +} + +static Block* +etherbread(Chan* chan, long n, ulong offset) +{ + Block *b; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + b = netifbread(ether, chan, n, offset); + poperror(); + runlock(ether); + return b; +} + +static int +etherwstat(Chan* chan, uchar* dp, int n) +{ + Ether *ether; + int r; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + r = netifwstat(ether, chan, dp, n); + poperror(); + runlock(ether); + return r; +} + +static void +etherrtrace(Netfile* f, Etherpkt* pkt, int len) +{ + int i, n; + Block *bp; + + if(qwindow(f->in) <= 0) + return; + if(len > 58) + n = 58; + else + n = len; + bp = iallocb(64); + if(bp == nil) + return; + memmove(bp->wp, pkt->d, n); + i = TK2MS(MACHP(0)->ticks); + bp->wp[58] = len>>8; + bp->wp[59] = len; + bp->wp[60] = i>>24; + bp->wp[61] = i>>16; + bp->wp[62] = i>>8; + bp->wp[63] = i; + bp->wp += 64; + qpass(f->in, bp); +} + +Block* +etheriq(Ether* ether, Block* bp, int fromwire) +{ + Etherpkt *pkt; + ushort type; + int len, multi, tome, fromme; + Netfile **ep, *f, **fp, *fx; + Block *xbp; + + ether->inpackets++; + + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + type = (pkt->type[0]<<8)|pkt->type[1]; + fx = 0; + ep = ðer->f[Ntypes]; + + multi = pkt->d[0] & 1; + /* check for valid multcast addresses */ + if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){ + if(!activemulti(ether, pkt->d, sizeof(pkt->d))){ + if(fromwire){ + freeb(bp); + bp = 0; + } + return bp; + } + } + + /* is it for me? */ + tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0; + + /* + * Multiplex the packet to all the connections which want it. + * If the packet is not to be used subsequently (fromwire != 0), + * attempt to simply pass it into one of the connections, thereby + * saving a copy of the data (usual case hopefully). + */ + for(fp = ether->f; fp < ep; fp++){ + if((f = *fp) && (f->type == type || f->type < 0)) + if(tome || multi || f->prom){ + /* Don't want to hear bridged packets */ + if(f->bridge && !fromwire && !fromme) + continue; + if(!f->headersonly){ + if(fromwire && fx == 0) + fx = f; + else if(xbp = iallocb(len)){ + memmove(xbp->wp, pkt, len); + xbp->wp += len; + if(qpass(f->in, xbp) < 0) + ether->soverflows++; + } + else + ether->soverflows++; + } + else + etherrtrace(f, pkt, len); + } + } + + if(fx){ + if(qpass(fx->in, bp) < 0) + ether->soverflows++; + return 0; + } + if(fromwire){ + freeb(bp); + return 0; + } + + return bp; +} + +static int +etheroq(Ether* ether, Block* bp) +{ + int len, loopback, s; + Etherpkt *pkt; + + ether->outpackets++; + + /* + * Check if the packet has to be placed back onto the input queue, + * i.e. if it's a loopback or broadcast packet or the interface is + * in promiscuous mode. + * If it's a loopback packet indicate to etheriq that the data isn't + * needed and return, etheriq will pass-on or free the block. + * To enable bridging to work, only packets that were originated + * by this interface are fed back. + */ + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){ + s = splhi(); + etheriq(ether, bp, 0); + splx(s); + } + + if(!loopback){ + qbwrite(ether->oq, bp); + if(ether->transmit != nil) + ether->transmit(ether); + }else + freeb(bp); + + return len; +} + +static long +etherwrite(Chan* chan, void* buf, long n, vlong) +{ + Ether *ether; + Block *bp; + int onoff; + Cmdbuf *cb; + long l; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + if(NETTYPE(chan->qid.path) != Ndataqid) { + l = netifwrite(ether, chan, buf, n); + if(l >= 0) + goto out; + cb = parsecmd(buf, n); + if(strcmp(cb->f[0], "nonblocking") == 0){ + if(cb->nf <= 1) + onoff = 1; + else + onoff = atoi(cb->f[1]); + qnoblock(ether->oq, onoff); + free(cb); + goto out; + } + free(cb); + if(ether->ctl!=nil){ + l = ether->ctl(ether,buf,n); + goto out; + } + error(Ebadctl); + } + + if(n > ether->maxmtu) + error(Etoobig); + if(n < ether->minmtu) + error(Etoosmall); + bp = allocb(n); + if(waserror()){ + freeb(bp); + nexterror(); + } + memmove(bp->rp, buf, n); + memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen); + bp->wp += n; + poperror(); + + l = etheroq(ether, bp); +out: + poperror(); + runlock(ether); + return l; +} + +static long +etherbwrite(Chan* chan, Block* bp, ulong) +{ + Ether *ether; + long n; + + n = BLEN(bp); + if(NETTYPE(chan->qid.path) != Ndataqid){ + if(waserror()) { + freeb(bp); + nexterror(); + } + n = etherwrite(chan, bp->rp, n, 0); + poperror(); + freeb(bp); + return n; + } + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + if(n > ether->maxmtu){ + freeb(bp); + error(Etoobig); + } + if(n < ether->minmtu){ + freeb(bp); + error(Etoosmall); + } + n = etheroq(ether, bp); + poperror(); + runlock(ether); + return n; +} + +static struct { + char* type; + int (*reset)(Ether*); +} cards[MaxEther+1]; + +void +addethercard(char* t, int (*r)(Ether*)) +{ + static int ncard; + + if(ncard == MaxEther) + panic("too many ether cards"); + cards[ncard].type = t; + cards[ncard].reset = r; + ncard++; +} + +int +parseether(uchar *to, char *from) +{ + char nip[4]; + char *p; + int i; + + p = from; + for(i = 0; i < Eaddrlen; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +static void +etherreset(void) +{ + Ether *ether; + int i, n, ctlrno; + char name[KNAMELEN], buf[128]; + + for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){ + if(ether == 0) + ether = malloc(sizeof(Ether)); + memset(ether, 0, sizeof(Ether)); + ether->ctlrno = ctlrno; + ether->mbps = 10; + ether->minmtu = ETHERMINTU; + ether->maxmtu = ETHERMAXTU; + ether->tbdf = BUSUNKNOWN; + + if(archether(ctlrno, ether) <= 0) + continue; + + for(n = 0; cards[n].type; n++){ + if(cistrcmp(cards[n].type, ether->type)) + continue; + for(i = 0; i < ether->nopt; i++){ + if(cistrncmp(ether->opt[i], "ea=", 3) == 0){ + if(parseether(ether->ea, ðer->opt[i][3]) == -1) + memset(ether->ea, 0, Eaddrlen); + }else if(cistrcmp(ether->opt[i], "fullduplex") == 0 || + cistrcmp(ether->opt[i], "10BASE-TFD") == 0) + ether->fullduplex = 1; + else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0) + ether->mbps = 100; + } + if(cards[n].reset(ether)) + break; + snprint(name, sizeof(name), "ether%d", ctlrno); + + if(ether->interrupt != nil) + intrenable(ether->irq, ether->interrupt, ether, ether->tbdf, name); + + i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %lud", + ctlrno, ether->type, ether->mbps, ether->port, ether->irq); + if(ether->mem) + i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem)); + if(ether->size) + i += sprint(buf+i, " size 0x%luX", ether->size); + i += sprint(buf+i, ": %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX", + ether->ea[0], ether->ea[1], ether->ea[2], + ether->ea[3], ether->ea[4], ether->ea[5]); + sprint(buf+i, "\n"); + print(buf); + + if(ether->mbps == 100){ + netifinit(ether, name, Ntypes, 256*1024); + if(ether->oq == 0) + ether->oq = qopen(256*1024, Qmsg, 0, 0); + } + else{ + netifinit(ether, name, Ntypes, 64*1024); + if(ether->oq == 0) + ether->oq = qopen(64*1024, Qmsg, 0, 0); + } + if(ether->oq == 0) + panic("etherreset %s", name); + ether->alen = Eaddrlen; + memmove(ether->addr, ether->ea, Eaddrlen); + memset(ether->bcast, 0xFF, Eaddrlen); + + etherxx[ctlrno] = ether; + ether = 0; + break; + } + } + if(ether) + free(ether); +} + +static void +etherpower(int on) +{ + int i; + Ether *ether; + + for(i = 0; i < MaxEther; i++){ + if((ether = etherxx[i]) == nil || ether->power == nil) + continue; + if(on){ + if(canrlock(ether)) + continue; + if(ether->power != nil) + ether->power(ether, on); + wunlock(ether); + }else{ + if(!canrlock(ether)) + continue; + wlock(ether); + if(ether->power != nil) + ether->power(ether, on); + /* Keep locked until power goes back on */ + } + } +} + +#define POLY 0xedb88320 + +/* really slow 32 bit crc for ethers */ +ulong +ethercrc(uchar *p, int len) +{ + int i, j; + ulong crc, b; + + crc = 0xffffffff; + for(i = 0; i < len; i++){ + b = *p++; + for(j = 0; j < 8; j++){ + crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0); + b >>= 1; + } + } + return crc; +} + +Dev etherdevtab = { + 'l', + "ether", + + etherreset, + devinit, + ethershutdown, + etherattach, + etherwalk, + etherstat, + etheropen, + ethercreate, + etherclose, + etherread, + etherbread, + etherwrite, + etherbwrite, + devremove, + etherwstat, + etherpower, +}; diff --git a/os/mpc/devpcmcia.c b/os/mpc/devpcmcia.c new file mode 100644 index 00000000..4c1d8311 --- /dev/null +++ b/os/mpc/devpcmcia.c @@ -0,0 +1,1076 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* + * MPC821/3 PCMCIA driver (prototype) + * + * unlike the i82365 adapter, there isn't an offset register: + * card addresses are simply the lower order 26 bits of the host address. + * + * to do: + * split allocation of memory/attrib (all 26 bits valid) and io space (typically 10 or 12 bits) + * correct config + * interrupts and i/o space access + * DMA? + * power control + */ + +enum +{ + Maxctlr= 1, + Maxslot= 2, + Slotashift= 16, + + /* pipr */ + Cbvs1= 1<<15, + Cbvs2= 1<<14, + Cbwp= 1<<13, + Cbcd2= 1<<12, + Cbcd1= 1<<11, + Cbbvd2= 1<<10, + Cbbvd1= 1<<9, + Cbrdy= 1<<8, + + /* pscr and per */ + Cbvs1_c= 1<<15, + Cbvs2_c= 1<<14, + Cbwp_c= 1<<13, + Cbcd2_c= 1<<12, + Cbcd1_c= 1<<11, + Cbbvd2_c= 1<<10, + Cbbvd1_c= 1<<9, + Cbrdy_l= 1<<7, + Cbrdy_h= 1<<6, + Cbrdy_r= 1<<5, + Cbrdy_f= 1<<4, + + /* pgcr[n] */ + Cbdreq_int= 0<<14, + Cbdreq_iois16= 2<<14, + Cbdreq_spkr= 3<<14, + Cboe= 1<<7, + Cbreset= 1<<6, + + /* porN */ + Rport8= 0<<6, + Rport16= 1<<6, + Rmtype= 7<<3, /* memory type field */ + Rmem= 0<<3, /* common memory space */ + Rattrib= 2<<3, /* attribute space */ + Rio= 3<<3, + Rdma= 4<<3, /* normal DMA */ + Rdmalx= 5<<3, /* DMA, last transaction */ + RA22_23= 6<<3, /* ``drive A22 and A23 signals on CE2 and CE1'' */ + RslotB= 1<<2, /* select slot B (always, on MPC823) */ + Rwp= 1<<1, /* write protect */ + Rvalid= 1<<0, /* region valid */ + + Nmap= 8, /* max number of maps to use */ + + /* + * configuration registers - they start at an offset in attribute + * memory found in the CIS. + */ + Rconfig= 0, + Creset= (1<<7), /* reset device */ + Clevel= (1<<6), /* level sensitive interrupt line */ + Rccsr= 2, + Ciack = (1<<0), + Cipend = (1<<1), + Cpwrdown= (1<<2), + Caudioen= (1<<3), + Ciois8= (1<<5), + Cchgena= (1<<6), + Cchange= (1<<7), + Rpin= 4, /* pin replacement register */ + Rscpr= 6, /* socket and copy register */ + Riob0= 10, + Riob1= 12, + Riob2= 14, + Riob3= 16, + Riolim= 18, + + Maxctab= 8, /* maximum configuration table entries */ + MaxCIS = 8192, /* maximum CIS size in bytes */ + Mgran = 8192, /* maximum size of reads and writes */ + + Statusbounce=20, /* msec debounce time */ +}; + +typedef struct Ctlr Ctlr; + +/* a controller (there's only one) */ +struct Ctlr +{ + int dev; + int nslot; + + /* memory maps */ + Lock mlock; /* lock down the maps */ + PCMmap mmap[Nmap]; /* maps */ + + /* IO port allocation */ + ulong nextport; +}; +static Ctlr controller[Maxctlr]; + +static PCMslot *slot; +static PCMslot *lastslot; +static int nslot; + +static Map pcmmapv[Nmap+1]; +static RMap pcmmaps = {"PCMCIA mappings"}; + +static void pcmciaintr(Ureg*, void*); +static void pcmciareset(void); +static int pcmio(int, ISAConf*); +static long pcmread(int, int, void*, long, ulong); +static long pcmwrite(int, int, void*, long, ulong); +static void slotdis(PCMslot*); + +static ulong pcmmalloc(ulong, long); +static void pcmfree(ulong, long); +static void pcmciaproc(void*); + +static void pcmciadump(PCMslot*); + +/* + * get info about card + */ +static void +slotinfo(PCMslot *pp) +{ + ulong pipr; + + pipr = (m->iomem->pipr >> pp->slotshift) & 0xFF00; +print("pipr=%8.8lux/%lux\n", m->iomem->pipr, pipr); + pp->v3_3 = (pipr&Cbvs1)!=0; + pp->voltage = (((pipr & Cbvs2)!=0)<<1) | ((pipr & Cbvs1)!=0); + pp->occupied = (pipr&(Cbcd1|Cbcd2))==0; + pp->powered = pcmpowered(pp->slotno); + pp->battery = (pipr & (Cbbvd1|Cbbvd2))>>9; + pp->wrprot = (pipr&Cbwp)!=0; + pp->busy = (pipr&Cbrdy)==0; +} + +static void +pcmdelay(int ms) +{ + if(up == nil) + delay(ms); + else + tsleep(&up->sleep, return0, nil, ms); +} + +/* + * enable the slot card + */ +static void +slotena(PCMslot *pp) +{ + IMM *io; + + if(pp->enabled) + return; + m->iomem->pgcr[pp->slotno] &= ~Cboe; + pcmpower(pp->slotno, 1); + eieio(); + pcmdelay(300); + io = m->iomem; + io->pgcr[pp->slotno] |= Cbreset; /* active high */ + eieio(); + pcmdelay(100); + io->pgcr[pp->slotno] &= ~Cbreset; + eieio(); + pcmdelay(500); /* ludicrous delay */ + + /* get configuration */ + slotinfo(pp); + if(pp->occupied){ + if(pp->cisread == 0){ + pcmcisread(pp); + pp->cisread = 1; + } + pp->enabled = 1; + } else{ + print("empty slot\n"); + slotdis(pp); + } +} + +/* + * disable the slot card + */ +static void +slotdis(PCMslot *pp) +{ + int i; + Ctlr *ctlr; + PCMmap *pm; + +iprint("slotdis %d\n", pp->slotno); + pcmpower(pp->slotno, 0); + m->iomem->pgcr[pp->slotno] |= Cboe; + ctlr = pp->ctlr; + for(i = 0; i < nelem(ctlr->mmap); i++){ + pm = &ctlr->mmap[i]; + if(m->iomem->pcmr[i].option & Rvalid && pm->slotno == pp->slotno) + pcmunmap(pp->slotno, pm); + } + pp->enabled = 0; + pp->cisread = 0; +} + +static void +pcmciardy(Ureg *ur, void *a) +{ + PCMslot *pp; + ulong w; + + pp = a; + w = (m->iomem->pipr >> pp->slotshift) & 0xFF00; + if(pp->occupied && (w & Cbrdy) == 0){ /* interrupt */ +print("PCM.%dI#%lux|", pp->slotno, w); + if(pp->intr.f != nil) + pp->intr.f(ur, pp->intr.arg); + } +} + +void +pcmintrenable(int slotno, void (*f)(Ureg*, void*), void *arg) +{ + PCMslot *pp; + IMM *io; + char name[KNAMELEN]; + + if(slotno < 0 || slotno >= nslot) + panic("pcmintrenable"); + snprint(name, sizeof(name), "pcmcia.irq%d", slotno); + io = ioplock(); + pp = slot+slotno; + pp->intr.f = f; + pp->intr.arg = arg; + intrenable(PCMCIAio, pcmciardy, pp, BUSUNKNOWN, name); + io->per |= Cbrdy_l; /* assumes used for irq, not rdy; assumes level interrupt always right */ + iopunlock(); +} + +void +pcmintrdisable(int slotno, void (*f)(Ureg*, void*), void *arg) +{ + PCMslot *pp; + IMM *io; + char name[KNAMELEN]; + + if(slotno < 0 || slotno >= nslot) + panic("pcmintrdisable"); + snprint(name, sizeof(name), "pcmcia.irq%d", slotno); + io = ioplock(); + pp = slot+slotno; + if(pp->intr.f == f && pp->intr.arg == arg){ + pp->intr.f = nil; + pp->intr.arg = nil; + intrdisable(PCMCIAio, pcmciardy, pp, BUSUNKNOWN, name); + io->per &= ~Cbrdy_l; + } + iopunlock(); +} + +/* + * status change interrupt + * + * this wakes a monitoring process to read the CIS, + * rather than holding other interrupts out here. + */ + +static Rendez pcmstate; + +static int +statechanged(void *a) +{ + PCMslot *pp; + int in; + + pp = a; + in = (m->iomem->pipr & (Cbcd1|Cbcd2))==0; + return in != pp->occupied; +} + +static void +pcmciaintr(Ureg*, void*) +{ + ulong events; + + if(slot == 0) + return; + events = m->iomem->pscr & (Cbvs1_c|Cbvs2_c|Cbwp_c|Cbcd2_c|Cbcd1_c|Cbbvd2_c|Cbbvd1_c); + eieio(); + m->iomem->pscr = events; + /* TO DO: other slot */ +iprint("PCM: #%lux|", events); +iprint("pipr=#%lux|", m->iomem->pipr & 0xFF00); + wakeup(&pcmstate); +} + +static void +pcmciaproc(void*) +{ + ulong csc; + PCMslot *pp; + int was; + + for(;;){ + sleep(&pcmstate, statechanged, slot+1); + tsleep(&up->sleep, return0, nil, Statusbounce); + /* + * voltage change 1,2 + * write protect change + * card detect 1,2 + * battery voltage 1 change (or SPKR-bar) + * battery voltage 2 change (or STSCHG-bar) + * card B rdy / IRQ-bar low + * card B rdy / IRQ-bar high + * card B rdy / IRQ-bar rising edge + * card B rdy / IRQ-bar falling edge + * + * TO DO: currently only handle card-present changes + */ + + for(pp = slot; pp < lastslot; pp++){ + if(pp->memlen == 0) + continue; + csc = (m->iomem->pipr>>pp->slotshift) & (Cbcd1|Cbcd2); + was = pp->occupied; + slotinfo(pp); + if(csc == 0 && was != pp->occupied){ + if(!pp->occupied){ + slotdis(pp); + if(pp->special && pp->notify.f != nil) + pp->notify.f(pp->notify.arg, 1); + } + } + } + } +} + +static uchar greycode[] = { + 0, 1, 3, 2, 6, 7, 5, 4, 014, 015, 017, 016, 012, 013, 011, 010, + 030, 031, 033, 032, 036, 037, 035, 034, 024, 025, 027 +}; + +/* + * get a map for pc card region, return corrected len + */ +PCMmap* +pcmmap(int slotno, ulong offset, int len, int attr) +{ + Ctlr *ctlr; + PCMslot *pp; + PCMmap *pm, *nm; + IMM *io; + int i; + ulong e, bsize, code, opt; + + if(0) + print("pcmmap: %d #%lux %d #%x\n", slotno, offset, len, attr); + pp = slot + slotno; + if(!pp->occupied) + return nil; + if(attr == 1){ /* account for ../port/cis.c's conventions */ + attr = Rattrib; + if(len <= 0) + len = MaxCIS*2; /* TO DO */ + } + ctlr = pp->ctlr; + + /* convert offset to granularity */ + if(len <= 0) + len = 1; + e = offset+len; + for(i=0;; i++){ + if(i >= nelem(greycode)) + return nil; + bsize = 1<mlock); + + /* look for an existing map that covers the right area */ + io = m->iomem; + nm = nil; + for(i=0; immap[i]; + if(io->pcmr[i].option & Rvalid && + pm->slotno == slotno && + pm->attr == attr && + offset >= pm->ca && e <= pm->cea){ + pm->ref++; + unlock(&ctlr->mlock); + return pm; + } + if(nm == 0 && pm->ref == 0) + nm = pm; + } + pm = nm; + if(pm == nil){ + unlock(&ctlr->mlock); + return nil; + } + + /* set up new map */ + pm->isa = pcmmalloc(offset, len); + if(pm->isa == 0){ + /* address not available: in use, or too much to map */ + unlock(&ctlr->mlock); + return 0; + } + if(0) + print("mx=%d isa=#%lux\n", (int)(pm - ctlr->mmap), pm->isa); + + pm->len = len; + pm->ca = offset; + pm->cea = pm->ca + pm->len; + pm->attr = attr; + i = pm - ctlr->mmap; + io->pcmr[i].option &= ~Rvalid; /* disable map before changing it */ + io->pcmr[i].base = pm->isa; + opt = attr; + opt |= code<<27; + if((attr&Rmtype) == Rio){ + opt |= 4<<12; /* PSST */ + opt |= 8<<7; /* PSL */ + opt |= 2<<16; /* PSHT */ + }else{ + opt |= 6<<12; /* PSST */ + opt |= 24<<7; /* PSL */ + opt |= 8<<16; /* PSHT */ + } + if((attr & Rport16) == 0) + opt |= Rport8; + if(slotno == 1) + opt |= RslotB; + io->pcmr[i].option = opt | Rvalid; + pm->slotno = slotno; + pm->ref = 1; + + unlock(&ctlr->mlock); + return pm; +} + +static void +pcmiomap(PCMslot *pp, PCMconftab *ct, int i) +{ + int n, attr; + Ctlr *ctlr; + + if(0) + print("pcm iomap #%lux %lud\n", ct->io[i].start, ct->io[i].len); + if(ct->io[i].len <= 0) + return; + if(ct->io[i].start == 0){ + n = 1<nlines; + ctlr = pp->ctlr; + lock(&ctlr->mlock); + if(ctlr->nextport == 0) + ctlr->nextport = 0xF000; + ctlr->nextport = (ctlr->nextport + n - 1) & ~(n-1); + ct->io[i].start = ctlr->nextport; + ct->io[i].len = n; + ctlr->nextport += n; + unlock(&ctlr->mlock); + } + attr = Rio; + if(ct->bit16) + attr |= Rport16; + ct->io[i].map = pcmmap(pp->slotno, ct->io[i].start, ct->io[i].len, attr); +} + +void +pcmunmap(int slotno, PCMmap* pm) +{ + int i; + PCMslot *pp; + Ctlr *ctlr; + + pp = slot + slotno; + if(pp->memlen == 0) + return; + ctlr = pp->ctlr; + lock(&ctlr->mlock); + if(pp->slotno == pm->slotno && --pm->ref == 0){ + i = pm - ctlr->mmap; + m->iomem->pcmr[i].option = 0; + m->iomem->pcmr[i].base = 0; + pcmfree(pm->isa, pm->len); + } + unlock(&ctlr->mlock); +} + +static void +increfp(PCMslot *pp) +{ + if(incref(pp) == 1) + slotena(pp); +} + +static void +decrefp(PCMslot *pp) +{ + if(decref(pp) == 0) + slotdis(pp); +} + +/* + * look for a card whose version contains 'idstr' + */ +int +pcmspecial(char *idstr, ISAConf *isa) +{ + PCMslot *pp; + extern char *strstr(char*, char*); + + pcmciareset(); + for(pp = slot; pp < lastslot; pp++){ + if(pp->special || pp->memlen == 0) + continue; /* already taken */ + increfp(pp); + if(pp->occupied && strstr(pp->verstr, idstr)){ + print("PCMslot #%d: Found %s - ",pp->slotno, idstr); + if(isa == 0 || pcmio(pp->slotno, isa) == 0){ + print("ok.\n"); + pp->special = 1; + return pp->slotno; + } + print("error with isa io\n"); + } + decrefp(pp); + } + return -1; +} + +void +pcmspecialclose(int slotno) +{ + PCMslot *pp; + + if(slotno >= nslot) + panic("pcmspecialclose"); + pp = slot + slotno; + pp->special = 0; + decrefp(pp); +} + +void +pcmnotify(int slotno, void (*f)(void*, int), void* a) +{ + PCMslot *pp; + + if(slotno < 0 || slotno >= nslot) + panic("pcmnotify"); + pp = slot + slotno; + if(pp->occupied && pp->special){ + pp->notify.f = f; + pp->notify.arg = a; + } +} + +/* + * reserve pcmcia slot address space [addr, addr+size[, + * returning a pointer to it, or nil if the space was already reserved. + */ +static ulong +pcmmalloc(ulong addr, long size) +{ + return rmapalloc(&pcmmaps, PHYSPCMCIA+addr, size, size); +} + +static void +pcmfree(ulong a, long size) +{ + if(a != 0 && size > 0) + mapfree(&pcmmaps, a, size); +} + +enum +{ + Qdir, + Qmem, + Qattr, + Qctl, +}; + +#define SLOTNO(c) ((c->qid.path>>8)&0xff) +#define TYPE(c) (c->qid.path&0xff) +#define QID(s,t) (((s)<<8)|(t)) + +static int +pcmgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp) +{ + int slotno; + Qid qid; + long len; + PCMslot *pp; + + if(i>=3*nslot) + return -1; + slotno = i/3; + pp = slot + slotno; + if(pp->memlen == 0) + return 0; + len = 0; + switch(i%3){ + case 0: + qid.path = QID(slotno, Qmem); + sprint(up->genbuf, "pcm%dmem", slotno); + len = pp->memlen; + break; + case 1: + qid.path = QID(slotno, Qattr); + sprint(up->genbuf, "pcm%dattr", slotno); + len = pp->memlen; + break; + case 2: + qid.path = QID(slotno, Qctl); + sprint(up->genbuf, "pcm%dctl", slotno); + break; + } + qid.vers = 0; + devdir(c, qid, up->genbuf, len, eve, 0660, dp); + return 1; +} + +/* + * used only when debugging + */ +static void +pcmciadump(PCMslot *) +{ + IMM *io; + int i; + + io = m->iomem; + print("pipr #%4.4lux pscr #%4.4lux per #%4.4lux pgcr[1] #%8.8lux\n", + io->pipr & 0xFFFF, io->pscr & 0xFFFF, io->per & 0xFFFF, io->pgcr[1]); + for(i=0; i<8; i++) + print("pbr%d #%8.8lux por%d #%8.8lux\n", i, io->pcmr[i].base, i, io->pcmr[i].option); +} + +/* + * set up for slot cards + */ +static void +pcmciareset(void) +{ + static int already; + int i; + Ctlr *cp; + IMM *io; + PCMslot *pp; + + if(already) + return; + already = 1; + + cp = controller; + /* TO DO: set low power mode? ... */ + + mapinit(&pcmmaps, pcmmapv, sizeof(pcmmapv)); + mapfree(&pcmmaps, PHYSPCMCIA, PCMCIALEN); + + io = m->iomem; + + for(i=0; i<8; i++){ + io->pcmr[i].option = 0; + io->pcmr[i].base = 0; + } + + io->pscr = ~0; /* reset status */ + /* TO DO: Cboe, Cbreset */ + /* TO DO: two slots except on 823 */ + pcmenable(); + /* TO DO: if the card is there turn on 5V power to keep its battery alive */ + slot = xalloc(Maxslot * sizeof(PCMslot)); + lastslot = slot; + slot[0].slotshift = Slotashift; + slot[1].slotshift = 0; + for(i=0; imemlen = 0; + continue; + } + io->per |= (Cbvs1_c|Cbvs2_c|Cbwp_c|Cbcd2_c|Cbcd1_c|Cbbvd2_c|Cbbvd1_c)<slotshift; /* enable status interrupts */ + io->pgcr[i] = (1<<(31-PCMCIAio)) | (1<<(23-PCMCIAstatus)); + pp->slotno = i; + pp->memlen = 8*MB; + pp->ctlr = cp; + //slotdis(pp); + lastslot = slot; + nslot = i+1; + } + if(1) + pcmciadump(slot); + intrenable(PCMCIAstatus, pcmciaintr, cp, BUSUNKNOWN, "pcmcia"); + print("pcmcia reset\n"); +} + +static void +pcmciainit(void) +{ + kproc("pcmcia", pcmciaproc, nil, 0); +} + +static Chan* +pcmciaattach(char *spec) +{ + return devattach('y', spec); +} + +static Walkqid* +pcmciawalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, pcmgen); +} + +static int +pcmciastat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, 0, 0, pcmgen); +} + +static Chan* +pcmciaopen(Chan *c, int omode) +{ + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Eperm); + } else + increfp(slot + SLOTNO(c)); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +pcmciaclose(Chan *c) +{ + if(c->flag & COPEN) + if((c->qid.type & QTDIR) == 0) + decrefp(slot+SLOTNO(c)); +} + +/* a memmove using only bytes */ +static void +memmoveb(uchar *to, uchar *from, int n) +{ + while(n-- > 0) + *to++ = *from++; +} + +static long +pcmread(int slotno, int attr, void *a, long n, ulong offset) +{ + int i, len; + PCMmap *m; + void *ka; + uchar *ac; + PCMslot *pp; + + pp = slot + slotno; + if(pp->memlen < offset) + return 0; + if(pp->memlen < offset + n) + n = pp->memlen - offset; + + ac = a; + for(len = n; len > 0; len -= i){ + if((i = len) > Mgran) + i = Mgran; + m = pcmmap(pp->slotno, offset, i, attr? Rattrib: Rmem); + if(m == 0) + error("can't map PCMCIA card"); + if(waserror()){ + if(m) + pcmunmap(pp->slotno, m); + nexterror(); + } + if(offset + len > m->cea) + i = m->cea - offset; + else + i = len; + ka = (char*)KADDR(m->isa) + (offset - m->ca); + memmoveb(ac, ka, i); + poperror(); + pcmunmap(pp->slotno, m); + offset += i; + ac += i; + } + + return n; +} + +static long +pcmciaread(Chan *c, void *a, long n, vlong offset) +{ + char *cp, *buf; + ulong p; + PCMslot *pp; + + p = TYPE(c); + switch(p){ + case Qdir: + return devdirread(c, a, n, 0, 0, pcmgen); + case Qmem: + case Qattr: + return pcmread(SLOTNO(c), p==Qattr, a, n, offset); + case Qctl: + buf = malloc(READSTR); + if(buf == nil) + error(Enomem); + if(waserror()){ + free(buf); + nexterror(); + } + cp = buf; + pp = slot + SLOTNO(c); + if(pp->occupied) + cp += sprint(cp, "occupied\n"); + if(pp->enabled) + cp += sprint(cp, "enabled\n"); + if(pp->powered) + cp += sprint(cp, "powered\n"); + if(pp->configed) + cp += sprint(cp, "configed\n"); + if(pp->wrprot) + cp += sprint(cp, "write protected\n"); + if(pp->busy) + cp += sprint(cp, "busy\n"); + if(pp->v3_3) + cp += sprint(cp, "3.3v ok\n"); + cp += sprint(cp, "battery lvl %d\n", pp->battery); + cp += sprint(cp, "voltage select %d\n", pp->voltage); + /* TO DO: could return pgcr[] values for debugging */ + *cp = 0; + n = readstr(offset, a, n, buf); + poperror(); + free(buf); + break; + default: + n=0; + break; + } + return n; +} + +static long +pcmwrite(int dev, int attr, void *a, long n, ulong offset) +{ + int i, len; + PCMmap *m; + void *ka; + uchar *ac; + PCMslot *pp; + + pp = slot + dev; + if(pp->memlen < offset) + return 0; + if(pp->memlen < offset + n) + n = pp->memlen - offset; + + ac = a; + for(len = n; len > 0; len -= i){ + if((i = len) > Mgran) + i = Mgran; + m = pcmmap(pp->slotno, offset, i, attr? Rattrib: Rmem); + if(m == 0) + error("can't map PCMCIA card"); + if(waserror()){ + if(m) + pcmunmap(pp->slotno, m); + nexterror(); + } + if(offset + len > m->cea) + i = m->cea - offset; + else + i = len; + ka = (char*)KADDR(m->isa) + (offset - m->ca); + memmoveb(ka, ac, i); + poperror(); + pcmunmap(pp->slotno, m); + offset += i; + ac += i; + } + + return n; +} + +static long +pcmciawrite(Chan *c, void *a, long n, vlong offset) +{ + ulong p; + PCMslot *pp; + char buf[32]; + + p = TYPE(c); + switch(p){ + case Qctl: + if(n >= sizeof(buf)) + n = sizeof(buf) - 1; + strncpy(buf, a, n); + buf[n] = 0; + pp = slot + SLOTNO(c); + if(!pp->occupied) + error(Eio); + + /* set vpp on card */ + if(strncmp(buf, "vpp", 3) == 0){ + p = strtol(buf+3, nil, 0); + pcmsetvpp(pp->slotno, p); + } + break; + case Qmem: + case Qattr: + pp = slot + SLOTNO(c); + if(pp->occupied == 0 || pp->enabled == 0) + error(Eio); + n = pcmwrite(pp->slotno, p == Qattr, a, n, offset); + if(n < 0) + error(Eio); + break; + default: + error(Ebadusefd); + } + return n; +} + +Dev pcmciadevtab = { + 'y', + "pcmcia", + + pcmciareset, + pcmciainit, + devshutdown, + pcmciaattach, + pcmciawalk, + pcmciastat, + pcmciaopen, + devcreate, + pcmciaclose, + pcmciaread, + devbread, + pcmciawrite, + devbwrite, + devremove, + devwstat, +}; + +/* + * configure the PCMslot for IO. We assume very heavily that we can read + * configuration info from the CIS. If not, we won't set up correctly. + */ +static int +pcmio(int slotno, ISAConf *isa) +{ + PCMslot *pp; + PCMconftab *ct, *et, *t; + PCMmap *pm; + uchar *p; + int irq, i, x; + + irq = isa->irq; + if(irq == 2) + irq = 9; + + if(slotno > nslot) + return -1; + pp = slot + slotno; + + if(!pp->occupied) + return -1; + + et = &pp->ctab[pp->nctab]; + + /* assume default is right */ + if(pp->def) + ct = pp->def; + else + ct = pp->ctab; + /* try for best match */ + if(ct->nlines == 0 || ct->io[0].start != isa->port || ((1<irqs) == 0){ + for(t = pp->ctab; t < et; t++) + if(t->nlines && t->io[0].start == isa->port && ((1<irqs)){ + ct = t; + break; + } + } + if(ct->nlines == 0 || ((1<irqs) == 0){ + for(t = pp->ctab; t < et; t++) + if(t->nlines && ((1<irqs)){ + ct = t; + break; + } + } + if(ct->nlines == 0){ + for(t = pp->ctab; t < et; t++) + if(t->nlines){ + ct = t; + break; + } + } +print("slot %d: nlines=%d iolen=%lud irq=%d ct->index=%d nport=%d ct->port=#%lux/%lux\n", slotno, ct->nlines, ct->io[0].len, irq, ct->index, ct->nio, ct->io[0].start, isa->port); + if(ct == et || ct->nlines == 0) + return -1; + /* route interrupts */ + isa->irq = irq; + //wrreg(pp, Rigc, irq | Fnotreset | Fiocard); + delay(2); + + /* set power and enable device */ + pcmsetvcc(pp->slotno, ct->vcc); + pcmsetvpp(pp->slotno, ct->vpp1); + + delay(2); /* could poll BSY during power change */ + + for(i=0; inio; i++) + pcmiomap(pp, ct, i); + + if(ct->nio) + isa->port = ct->io[0].start; + + /* only touch Rconfig if it is present */ + if(pp->cpresent & (1<caddr+Rconfig); + /* Reset adapter */ + pm = pcmmap(slotno, pp->caddr + Rconfig, 1, Rattrib); + if(pm == nil) + return -1; + + p = (uchar*)KADDR(pm->isa) + (pp->caddr + Rconfig - pm->ca); + + /* set configuration and interrupt type */ + x = ct->index; + if((ct->irqtype & 0x20) && ((ct->irqtype & 0x40)==0 || isa->irq>7)) + x |= Clevel; + *p = x; + delay(5); + + pcmunmap(pp->slotno, pm); +print("Adapter reset\n"); + } + + return 0; +} diff --git a/os/mpc/devrtc.c b/os/mpc/devrtc.c new file mode 100644 index 00000000..90193764 --- /dev/null +++ b/os/mpc/devrtc.c @@ -0,0 +1,258 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "io.h" + +/* + * MPC8xx real time clock + * optional board option switch + * optional nvram + * interrupt statistics + */ + +enum{ + Qdir, + Qrtc, + Qswitch, + Qnvram, + Qintstat, + + Qporta, + Qportb, + Qportc, + + /* sccr */ + RTDIV= 1<<24, + RTSEL= 1<<23, + + /* rtcsc */ + RTE= 1<<0, + R38K= 1<<4, +}; + +static QLock rtclock; /* mutex on clock operations */ + +static Dirtab rtcdir[]={ + ".", {Qdir,0,QTDIR}, 0, 0555, + "rtc", {Qrtc, 0}, 12, 0664, + "switch", {Qswitch, 0}, 0, 0444, + "intstat", {Qintstat, 0}, 0, 0444, + "porta", {Qporta, 0}, 0, 0444, + "portb", {Qportb, 0}, 0, 0444, + "portc", {Qportc, 0}, 0, 0444, + "nvram", {Qnvram, 0}, 0, 0660, +}; +static long nrtc = nelem(rtcdir)-1; /* excludes nvram */ + +static long readport(int, ulong, char*, long); + +static void +rtcreset(void) +{ + IMM *io; + int n; + + io = m->iomem; + io->rtcsck = KEEP_ALIVE_KEY; + n = (RTClevel<<8)|RTE; + if(m->clockgen == 5*MHz) + n |= R38K; + io->rtcsc = n; + io->rtcsck = ~KEEP_ALIVE_KEY; + if(conf.nvramsize != 0){ + rtcdir[nrtc].length = conf.nvramsize; + nrtc++; + } +} + +static Chan* +rtcattach(char *spec) +{ + return devattach('r', spec); +} + +static Walkqid* +rtcwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, rtcdir, nrtc, devgen); +} + +static int +rtcstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, rtcdir, nrtc, devgen); +} + +static Chan* +rtcopen(Chan *c, int omode) +{ + return devopen(c, omode, rtcdir, nrtc, devgen); +} + +static void +rtcclose(Chan*) +{ +} + +static long +rtcread(Chan *c, void *buf, long n, vlong off) +{ + ulong offset = off; + ulong t; + char *b; + + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, rtcdir, nrtc, devgen); + + switch((ulong)c->qid.path){ + case Qrtc: + t = m->iomem->rtc; + n = readnum(offset, buf, n, t, 12); + return n; + case Qswitch: + return readnum(offset, buf, n, archoptionsw(), 12); + case Qintstat: + b = malloc(2048); + if(waserror()){ + free(b); + nexterror(); + } + intrstats(b, 2048); + t = readstr(offset, buf, n, b); + poperror(); + free(b); + return t; + case Qporta: + case Qportb: + case Qportc: + return readport(c->qid.path, offset, buf, n); + case Qnvram: + if(offset < 0 || offset >= conf.nvramsize) + return 0; + if(offset + n > conf.nvramsize) + n = conf.nvramsize - offset; + memmove(buf, (char*)conf.nvrambase+offset, n); + return n; + } + error(Egreg); + return 0; /* not reached */ +} + +static long +rtcwrite(Chan *c, void *buf, long n, vlong off) +{ + ulong offset = off; + ulong secs; + char *cp, sbuf[32]; + IMM *io; + + switch((ulong)c->qid.path){ + case Qrtc: + /* + * read the time + */ + if(offset != 0 || n >= sizeof(sbuf)-1) + error(Ebadarg); + memmove(sbuf, buf, n); + sbuf[n] = '\0'; + cp = sbuf; + while(*cp){ + if(*cp>='0' && *cp<='9') + break; + cp++; + } + secs = strtoul(cp, 0, 0); + /* + * set it + */ + io = ioplock(); + io->rtck = KEEP_ALIVE_KEY; + io->rtc = secs; + io->rtck = ~KEEP_ALIVE_KEY; + iopunlock(); + return n; + case Qnvram: + if(offset < 0 || offset >= conf.nvramsize) + return 0; + if(offset + n > conf.nvramsize) + n = conf.nvramsize - offset; + memmove((char*)conf.nvrambase+offset, buf, n); + return n; + } + error(Egreg); + return 0; /* not reached */ +} + +static long +readport(int p, ulong offset, char *buf, long n) +{ + long t; + char *b; + int v[4], i; + IMM *io; + + io = m->iomem; + for(i=0;ipadat; + v[1] = io->padir; + v[2] = io->papar; + break; + case Qportb: + v[0] = io->pbdat; + v[1] = io->pbdir; + v[2] = io->pbpar; + break; + case Qportc: + v[0] = io->pcdat; + v[1] = io->pcdir; + v[2] = io->pcpar; + v[3] = io->pcso; + break; + } + b = malloc(READSTR); + if(waserror()){ + free(b); + nexterror(); + } + t = 0; + for(i=0; iiomem->rtc; +} + +Dev rtcdevtab = { + 'r', + "rtc", + + rtcreset, + devinit, + devshutdown, + rtcattach, + rtcwalk, + rtcstat, + rtcopen, + devcreate, + rtcclose, + rtcread, + devbread, + rtcwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/mpc/devtouch.c b/os/mpc/devtouch.c new file mode 100644 index 00000000..b11aecc8 --- /dev/null +++ b/os/mpc/devtouch.c @@ -0,0 +1,497 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define DPRINT if(0)print +#define THREEBUT 0 /* !=0, enable 3-button emulation (see below) */ + +/* + * DynaPro touch panel and Maxim MAX192 A/D converter on + * York Electronics Centre's BRD/98/024 (Version A) interface, + * accessed via mpc8xx SPI interface (see spi.c). + * + * The highest level of the driver is derived from the ARM/UCB touch panel driver, + * simplified because of the differences between the panels. + */ + +/* + * Values determined by interface board + */ +enum { + /* MAX192 control words */ + MeasureX = (1<<7)|(0<<6)|(1<<3)|(1<<2)|3, /* start, channel 0, unipolar, single-ended, external clock */ + MeasureY = (1<<7)|(1<<6)|(1<<3)|(1<<2)|3, /* start, channel 1, unipolar, single-ended, external clock */ + + /* port B bits */ + ADselect = IBIT(16), /* chip select to MAX192, active low */ + + /* port C bits */ + Xenable = 1<<2, /* PC13: TOUCH_XEN, active low */ + Yenable = 1<<3, /* PC12: TOUCH_YEN, active low */ + Touched = 1<<10, /* PC5: contact detect, active low */ + + /* interrupt control via port C */ + TouchIRQ= 2, /* parallel i/o - PC5 */ + TouchEnable= 1<pbdat &= ~ADselect; + iopunlock(); + nr = spioutin(tbuf, sizeof(tbuf), rbuf); + io = ioplock(); + io->pbdat |= ADselect; + iopunlock(); + if(nr != 3) + return -1; + return ((rbuf[1]<<8)|rbuf[2])>>5; +} + +/* + * keep reading the a/d until the value stabilises + */ +static int +dejitter(int enable, int cw) +{ + int i, diff, prev, v; + IMM *io; + + io = ioplock(); + io->pcdat &= ~enable; /* active low */ + iopunlock(); + + i = 0; + v = getcoord(cw); + do{ + prev = v; + v = getcoord(cw); + diff = v - prev; + if(diff < 0) + diff = -diff; + }while(diff >= MaxDelta && ++i <= Nconverge); + + io = ioplock(); + io->pcdat |= enable; + iopunlock(); + return v; +} + +static void +adcreset(void) +{ + IMM *io; + + /* select port pins */ + io = ioplock(); + io->pcdir &= ~(Xenable|Yenable); /* ensure set to input before changing state */ + io->pcpar &= ~(Xenable|Yenable); + io->pcdat |= Xenable|Yenable; + io->pcdir |= Xenable; /* change enable bits to output one at a time to avoid both being low at once (could damage panel) */ + io->pcdat |= Xenable; /* ensure it's high after making it an output */ + io->pcdir |= Yenable; + io->pcdat |= Yenable; /* ensure it's high after making it an output */ + io->pcso &= ~(Xenable|Yenable); + io->pbdat |= ADselect; + io->pbpar &= ~ADselect; + io->pbdir |= ADselect; + iopunlock(); +} + +/* + * high-level touch panel interface + */ + +/* to and from fixed point */ +#define FX(n) ((n)<<16) +#define XF(v) ((v)>>16) + +typedef struct Touch Touch; + +struct Touch { + Lock; + Rendez r; + int m[2][3]; /* transformation matrix */ + int rate; + int down; + int raw_count; + int valid_count; + int wake_time; + int sleep_time; +}; + +static Touch touch = { + {0}, + .r {0}, + .m {{FX(1), 0, 0},{0, FX(1), 0}}, /* default is 1:1 */ + .rate 20, /* milliseconds */ +}; + +/* + * panel-touched state and interrupt + */ + +static int +touching(void) +{ + eieio(); + return (m->iomem->pcdat & Touched) == 0; +} + +static int +ispendown(void*) +{ + return touch.down || touching(); +} + +static void +touchintr(Ureg*, void*) +{ + if((m->iomem->pcdat & Touched) == 0){ + m->iomem->cimr &= ~TouchEnable; /* mask interrupts when reading pen */ + touch.down = 1; + wakeup(&touch.r); + } +} + +static void +touchenable(void) +{ + IMM *io; + + io = ioplock(); + io->cimr |= TouchEnable; + iopunlock(); +} + +/* + * touchctl commands: + * X a b c - set X transformation + * Y d e f - set Y transformation + * s - set sample delay in millisec per sample + * r - set read delay in microsec + * R - set log2 of number of readings to average + */ + +enum{ + Qdir, + Qtouchctl, + Qtouchstat, + Qtouch, +}; + +Dirtab touchdir[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "touchctl", {Qtouchctl, 0}, 0, 0666, + "touchstat", {Qtouchstat, 0}, 0, 0444, + "touch", {Qtouch, 0}, 0, 0444, +}; + +static int +ptmap(int *m, int x, int y) +{ + return XF(m[0]*x + m[1]*y + m[2]); +} + +/* + * read a point from the touch panel; + * returns true iff the point is valid, otherwise x, y aren't changed + */ +static int +touchreadxy(int *fx, int *fy) +{ + int rx, ry; + + if(touching()){ + rx = dejitter(Xenable, MeasureX); + ry = dejitter(Yenable, MeasureY); + microdelay(40); + if(rx >=0 && ry >= 0){ + if(0) + print("touch %d %d\n", rx, ry); + *fx = ptmap(touch.m[0], rx, ry); + *fy = ptmap(touch.m[1], rx, ry); + touch.raw_count++; + return 1; + } + } + return 0; +} + +#define timer_start() 0 /* could use TBL if necessary */ +#define tmr2us(n) 0 + +static void +touchproc(void*) +{ + int b, i, x, y; + ulong t1, t2; + + t1 = timer_start(); + b = 1; + for(;;) { + //setpri(PriHi); + do{ + touch.down = 0; + touch.wake_time += (t2 = timer_start())-t1; + touchenable(); + sleep(&touch.r, ispendown, nil); + touch.sleep_time += (t1 = timer_start())-t2; + }while(!touchreadxy(&x, &y)); + + /* 640x480-specific 3-button emulation hack: */ + if(THREEBUT){ + if(y > 481) { + b = ((639-x) >> 7); + continue; + } else if(y < -2) { + b = (x >> 7)+3; + continue; + } + } + + DPRINT("#%d %d", x, y); + mousetrack(b, x, y, 0); + setpri(PriNormal); + while(touching()) { + for(i=0; i<3; i++) + if(touchreadxy(&x, &y)) { + DPRINT("*%d %d", x, y); + mousetrack(b, x, y, 0); + break; + } + touch.wake_time += (t2 = timer_start())-t1; + tsleep(&touch.r, return0, nil, touch.rate); + touch.sleep_time += (t1 = timer_start())-t2; + } + mousetrack(0, x, y, 0); + b = 1; /* go back to just button one for next press */ + } +} + +static void +touchreset(void) +{ + IMM *io; + + spireset(); + adcreset(); + intrenable(VectorCPIC+TouchIRQ, touchintr, &touch, BUSUNKNOWN, "touch"); + + /* set i/o pin to interrupt when panel touched */ + io = ioplock(); + io->pcdat &= ~Touched; + io->pcpar &= ~Touched; + io->pcdir &= ~Touched; + io->pcso &= ~Touched; + io->pcint |= Touched; /* high-to-low trigger */ + io->cimr &= ~TouchEnable; /* touchproc will enable when ready */ + iopunlock(); +} + +static void +touchinit(void) +{ + static int done; + + if(!done){ + done = 1; + kproc( "touchscreen", touchproc, nil, 0); + } +} + +static Chan* +touchattach(char* spec) +{ + return devattach('T', spec); +} + +static Walkqid* +touchwalk(Chan* c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, touchdir, nelem(touchdir), devgen); +} + +static int +touchstat(Chan* c, uchar* dp, int n) +{ + return devstat(c, dp, n, touchdir, nelem(touchdir), devgen); +} + +static Chan* +touchopen(Chan* c, int omode) +{ + omode = openmode(omode); + switch((ulong)c->qid.path){ + case Qtouchctl: + case Qtouchstat: + if(!iseve()) + error(Eperm); + break; + } + return devopen(c, omode, touchdir, nelem(touchdir), devgen); +} + +static void +touchclose(Chan*) +{ +} + +static long +touchread(Chan* c, void* buf, long n, vlong offset) +{ + char *tmp; + int x, y; + + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, touchdir, nelem(touchdir), devgen); + + tmp = malloc(READSTR); + if(waserror()){ + free(tmp); + nexterror(); + } + switch((ulong)c->qid.path){ + case Qtouch: + if(!touchreadxy(&x, &y)) + x = y = -1; + snprint(tmp, READSTR, "%d %d", x, y); + break; + case Qtouchctl: + snprint(tmp, READSTR, "s%d\nr%d\nR%d\nX %d %d %d\nY %d %d %d\n", + touch.rate, 0, 1, + touch.m[0][0], touch.m[0][1], touch.m[0][2], + touch.m[1][0], touch.m[1][1], touch.m[1][2]); + break; + case Qtouchstat: + snprint(tmp, READSTR, "%d %d\n%d %d\n", + touch.raw_count, touch.valid_count, tmr2us(touch.sleep_time), tmr2us(touch.wake_time)); + touch.raw_count = 0; + touch.valid_count = 0; + touch.sleep_time = 0; + touch.wake_time = 0; + break; + default: + error(Ebadarg); + return 0; + } + n = readstr(offset, buf, n, tmp); + poperror(); + free(tmp); + return n; +} + +static void +dotouchwrite(Chan *c, char *buf) +{ + char *field[8]; + int nf, cmd, pn, m[3], n; + + nf = getfields(buf, field, nelem(field), 1, " \t\n"); + if(nf <= 0) + return; + switch((ulong)c->qid.path){ + case Qtouchctl: + cmd = *(field[0])++; + pn = *field[0] == 0; + switch(cmd) { + case 's': + n = strtol(field[pn], 0, 0); + if(n <= 0) + error(Ebadarg); + touch.rate = n; + break; + case 'r': + /* touch read delay */ + break; + case 'X': + case 'Y': + if(nf < pn+2) + error(Ebadarg); + m[0] = strtol(field[pn], 0, 0); + m[1] = strtol(field[pn+1], 0, 0); + m[2] = strtol(field[pn+2], 0, 0); + memmove(touch.m[cmd=='Y'], m, sizeof(touch.m[0])); + break; + case 'c': + case 'C': + case 'v': + case 't': + case 'e': + /* not used */ + /* break; */ + default: + error(Ebadarg); + } + break; + default: + error(Ebadarg); + return; + } +} + +static long +touchwrite(Chan* c, void* vp, long n, vlong) +{ + char buf[64]; + char *cp, *a; + int n0 = n; + int bn; + + a = vp; + while(n) { + bn = (cp = memchr(a, '\n', n))!=nil ? cp-a+1 : n; + n -= bn; + bn = bn > sizeof(buf)-1 ? sizeof(buf)-1 : bn; + memmove(buf, a, bn); + buf[bn] = '\0'; + a = cp; + dotouchwrite(c, buf); + } + return n0-n; +} + +Dev touchdevtab = { + 'T', + "touch", + + touchreset, + touchinit, + devshutdown, + touchattach, + touchwalk, + touchstat, + touchopen, + devcreate, + touchclose, + touchread, + devbread, + touchwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/mpc/devuart.c b/os/mpc/devuart.c new file mode 100644 index 00000000..13adf351 --- /dev/null +++ b/os/mpc/devuart.c @@ -0,0 +1,1450 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "../port/netif.h" + +enum { + Nbuf= 2, /* double buffered */ + Rbufsize= 512, + Bufsize= (Rbufsize+CACHELINESZ-1)&~(CACHELINESZ-1), + Nuart= 2+4, /* max in any 8xx architecture (2xSMC, 4xSCC) */ + CTLS= 's'&037, + CTLQ= 'q'&037, +}; + +enum { + /* status bits in SCC receive buffer descriptors */ + RxBRK= 1<<7, /* break ended frame (async hdlc) */ + RxDE= 1<<7, /* DPLL error (hdlc) */ + RxBOF= 1<<6, /* BOF ended frame (async hdlc) */ + RxLG= 1<<5, /* frame too large (hdlc) */ + RxNO= 1<<4, /* bad bit alignment (hdlc) */ + RxBR= 1<<5, /* break received during frame (uart) */ + RxFR= 1<<4, /* framing error (uart) */ + RxPR= 1<<3, /* parity error (uart) */ + RxAB= 1<<3, /* frame aborted (hdlc, async hdlc) */ + RxCR= 1<<2, /* bad CRC (hdlc, async hdlc) */ + RxOV= 1<<1, /* receiver overrun (all) */ + RxCD= 1<<0, /* CD lost (all) */ + + /* hdlc-specific Rx/Tx BDs */ + TxTC= 1<<10, +}; + +/* + * SMC in UART mode + */ + +typedef struct Uartsmc Uartsmc; +struct Uartsmc { + IOCparam; + ushort maxidl; + ushort idlc; + ushort brkln; + ushort brkec; + ushort brkcr; + ushort rmask; +}; + +/* + * SCC2 UART parameters + */ +enum { + /* special mode bits */ + SccAHDLC = 1<<0, + SccHDLC = 1<<1, + SccIR = 1<<2, + SccPPP = 1<<3, +}; + +typedef struct Uartscc Uartscc; +struct Uartscc { + SCCparam; + uchar rsvd[8]; + ushort max_idl; + ushort idlc; + ushort brkcr; + ushort parec; + ushort frmec; + ushort nosec; + ushort brkec; + ushort brkln; + ushort uaddr1; + ushort uaddr2; + ushort rtemp; + ushort toseq; + ushort character[8]; + ushort rccm; + ushort rccrp; + ushort rlbc; +}; + +typedef struct UartAHDLC UartAHDLC; +struct UartAHDLC { + SCCparam; + ulong rsvd1; + ulong c_mask; + ulong c_pres; + ushort bof; + ushort eof; + ushort esc; + ushort rsvd2[2]; + ushort zero; + ushort rsvd3; + ushort rfthr; + ushort resvd4[2]; + ulong txctl_tbl; + ulong rxctl_tbl; + ushort nof; + ushort rsvd5; +}; + +typedef struct UartHDLC UartHDLC; +struct UartHDLC { + SCCparam; + ulong rsvd1; + ulong c_mask; + ulong c_pres; + ushort disfc; + ushort crcec; + ushort abtsc; + ushort nmarc; + ushort retrc; + ushort mflr; + ushort max_cnt; + ushort rfthr; + ushort rfcnt; + ushort hmask; + ushort haddr[4]; + ushort tmp; + ushort tmp_mb; +}; + +enum { + /* SCC events of possible interest here eventually */ + AB= 1<<9, /* autobaud detected */ + GRA= 1<<7, /* graceful stop completed */ + CCR= 1<<3, /* control character detected */ + + /* SCC, SMC common interrupt events */ + BSY= 1<<2, /* receive buffer was busy (overrun) */ + TXB= 1<<1, /* block sent */ + RXB= 1<<0, /* block received */ + + /* SCC events */ + TXE = 1<<4, /* transmission error */ + RXF = 1<<3, /* final block received */ + + /* gsmr_l */ + ENR = 1<<5, /* enable receiver */ + ENT = 1<<4, /* enable transmitter */ + + /* port A */ + RXD1= SIBIT(15), + TXD1= SIBIT(14), + + /* port B */ + RTS1B= IBIT(19), + + /* port C */ + RTS1C= SIBIT(15), + CTS1= SIBIT(11), + CD1= SIBIT(10), +}; + +typedef struct Uart Uart; +struct Uart +{ + QLock; + + Uart *elist; /* next enabled interface */ + char name[KNAMELEN]; + + int x; /* index: x in SMCx or SCCx */ + int cpmid; /* eg, SCC1ID, SMC1ID */ + CPMdev* cpm; + int opens; + uchar bpc; /* bits/char */ + uchar parity; + uchar stopb; + uchar setup; + uchar enabled; + int dev; + + ulong frame; /* framing errors */ + ulong perror; + ulong overrun; /* rcvr overruns */ + ulong crcerr; + ulong interrupts; + int baud; /* baud rate */ + + /* flow control */ + int xonoff; /* software flow control on */ + int blocked; + int modem; /* hardware flow control on */ + int cts; /* ... cts state */ + int rts; /* ... rts state */ + Rendez r; + + /* buffers */ + int (*putc)(Queue*, int); + Queue *iq; + Queue *oq; + + /* staging areas to avoid some of the per character costs */ + /* TO DO: should probably use usual Ring */ + Block* istage[Nbuf]; /* double buffered */ + int rdrx; /* last buffer read */ + + Lock plock; /* for output variables */ + Block* outb; /* currently transmitting Block */ + + BD* rxb; + BD* txb; + + SMC* smc; + SCC* scc; + IOCparam* param; + ushort* brkcr; /* brkcr location in appropriate block */ + int brgc; + int mode; + Block* partial; + int loopback; +}; + +static Uart *uart[Nuart]; +static int nuart; + +struct Uartalloc { + Lock; + Uart *elist; /* list of enabled interfaces */ +} uartalloc; + +static void uartintr(Uart*, int); +static void smcuintr(Ureg*, void*); +static void sccuintr(Ureg*, void*); + +static void +uartsetbuf(Uart *up) +{ + IOCparam *p; + BD *bd; + int i; + Block *bp; + + p = up->param; + p->rfcr = 0x18; + p->tfcr = 0x18; + p->mrblr = Rbufsize; + + if((bd = up->rxb) == nil){ + bd = bdalloc(Nbuf); + up->rxb = bd; + } + p->rbase = (ushort)bd; + for(i=0; istatus = BDEmpty|BDInt; + bd->length = 0; + if((bp = up->istage[i]) == nil) + up->istage[i] = bp = allocb(Bufsize); + bd->addr = PADDR(bp->wp); + dcflush(bp->wp, Bufsize); + bd++; + } + (bd-1)->status |= BDWrap; + up->rdrx = 0; + + if((bd = up->txb) == nil){ + bd = bdalloc(1); + up->txb = bd; + } + p->tbase = (ushort)bd; + bd->status = BDWrap|BDInt; + bd->length = 0; + bd->addr = 0; +} + +static void +smcsetup(Uart *up) +{ + IMM *io; + Uartsmc *p; + SMC *smc; + ulong txrx; + + archdisableuart(up->cpmid); + up->brgc = brgalloc(); + if(up->brgc < 0) + error(Eio); + m->iomem->brgc[up->brgc] = baudgen(up->baud, 16) | BaudEnable; + smcnmsi(up->x, up->brgc); + + archenableuart(up->cpmid, 0); + + if(up->x == 1) + txrx = IBIT(24)|IBIT(25); /* SMC1 RX/TX */ + else + txrx = IBIT(20)|IBIT(21); /* SMC2 */ + io = ioplock(); + io->pbpar |= txrx; + io->pbdir &= ~txrx; + iopunlock(); + + up->param = up->cpm->param; + uartsetbuf(up); + + cpmop(up->cpm, InitRxTx, 0); + + /* SMC protocol parameters */ + p = (Uartsmc*)up->param; + up->brkcr = &p->brkcr; + p->maxidl = 1; /* non-zero so buffer closes when idle before mrblr reached */ + p->brkln = 0; + p->brkec = 0; + p->brkcr = 1; + smc = up->cpm->regs; + smc->smce = 0xff; /* clear events */ + smc->smcm = BSY|RXB|TXB; /* enable all possible interrupts */ + up->smc = smc; + smc->smcmr = ((1+8+1-1)<<11)|(2<<4); /* 8-bit, 1 stop, no parity; UART mode */ + intrenable(VectorCPIC+up->cpm->irq, smcuintr, up, BUSUNKNOWN, up->name); + /* enable when device opened */ +} + +static void +smcuintr(Ureg*, void *a) +{ + Uart *up; + int events; + + up = a; + events = up->smc->smce; + eieio(); + up->smc->smce = events; + uartintr(up, events&(BSY|RXB|TXB)); +} + +/* + * set the IO ports to enable the control signals for SCCx + */ +static void +sccuartpins(int x, int mode) +{ + IMM *io; + int i, w; + + x--; + io = ioplock(); + i = 2*x; + w = (TXD1|RXD1)<papar |= w; /* enable TXDn and RXDn pins */ + io->padir &= ~w; + if((mode & SccIR) == 0) + io->paodr |= TXD1<paodr &= ~w; /* not open drain */ + + w = (CD1|CTS1)<pcpar &= ~w; + io->pcdir &= ~w; + if(conf.nocts2 || mode) + io->pcso &= ~w; /* force CTS and CD on */ + else + io->pcso |= w; + + w = RTS1B<pbpar &= ~w; + io->pbdir &= ~w; + + w = RTS1C<pcpar |= w; + else + io->pcpar &= ~w; /* don't use for IR */ + iopunlock(); +} + +static void +sccsetup(Uart *up) +{ + SCC *scc; + int i; + + scc = up->cpm->regs; + up->scc = scc; + up->param = up->cpm->param; + sccxstop(up->cpm); + archdisableuart(up->cpmid); + if(up->brgc < 0){ + up->brgc = brgalloc(); + if(up->brgc < 0) + error(Eio); + } + m->iomem->brgc[up->brgc] = baudgen(up->baud, 16) | BaudEnable; + sccnmsi(up->x, up->brgc, up->brgc); + sccuartpins(up->x, up->mode); + + uartsetbuf(up); + + cpmop(up->cpm, InitRxTx, 0); + + /* SCC protocol parameters */ + if((up->mode & (SccAHDLC|SccHDLC)) == 0){ + Uartscc *sp; + sp = (Uartscc*)up->param; + sp->max_idl = 1; + sp->brkcr = 1; + sp->parec = 0; + sp->frmec = 0; + sp->nosec = 0; + sp->brkec = 0; + sp->brkln = 0; + sp->brkec = 0; + sp->uaddr1 = 0; + sp->uaddr2 = 0; + sp->toseq = 0; + for(i=0; i<8; i++) + sp->character[i] = 0x8000; + sp->rccm = 0xC0FF; + up->brkcr = &sp->brkcr; + scc->irmode = 0; + scc->dsr = ~0; + scc->gsmrh = 1<<5; /* 8-bit oriented receive fifo */ + scc->gsmrl = 0x28004; /* UART mode */ + }else{ + UartAHDLC *hp; + hp = (UartAHDLC*)up->param; + hp->c_mask = 0x0000F0B8; + hp->c_pres = 0x0000FFFF; + if(up->mode & SccIR){ + hp->bof = 0xC0; + hp->eof = 0xC1; + //scc->dsr = 0xC0C0; + scc->dsr = 0x7E7E; + }else{ + hp->bof = 0x7E; + hp->eof = 0x7E; + scc->dsr = 0x7E7E; + } + hp->esc = 0x7D; + hp->zero = 0; + if(up->mode & SccHDLC) + hp->rfthr = 1; + else + hp->rfthr = 0; /* receive threshold of 1 doesn't work properly for Async HDLC */ + hp->txctl_tbl = 0; + hp->rxctl_tbl = 0; + if(up->mode & SccIR){ + /* low-speed infrared */ + hp->nof = 12-1; /* 12 flags */ + scc->irsip = 0; + scc->irmode = (2<<8) | 1; + archsetirxcvr(0); + if(up->loopback) + scc->irmode = (3<<4)|1; /* loopback */ + }else{ + scc->irmode = 0; + hp->txctl_tbl = ~0; + hp->rxctl_tbl = ~0; + hp->nof = 1-1; /* one opening flag */ + } + up->brkcr = nil; + scc->gsmrh = 1<<5; /* 8-bit oriented receive fifo */ + if(up->mode & SccHDLC) + scc->gsmrl = 0x28000; /* HDLC */ + else + scc->gsmrl = 0x28006; /* async HDLC/IrDA */ + } + archenableuart(up->cpmid, (up->mode&SccIR)!=0); + scc->scce = ~0; /* clear events */ + scc->sccm = TXE|BSY|RXF|TXB|RXB; /* enable all interesting interrupts */ + intrenable(VectorCPIC+up->cpm->irq, sccuintr, up, BUSUNKNOWN, up->name); + scc->psmr = 3<<12; /* 8-bit, 1 stop, no parity; UART mode */ + if(up->loopback && (up->mode & SccIR) == 0) + scc->gsmrl |= 1<<6; /* internal loop back */ + scc->gsmrl |= ENT|ENR; /* enable rx/tx */ + if(0){ + print("gsmrl=%8.8lux gsmrh=%8.8lux dsr=%4.4ux irmode=%4.4ux\n", scc->gsmrl, scc->gsmrh, scc->dsr, scc->irmode); + for(i=0; iparam+i)); + } +} + +static void +sccuintr(Ureg*, void *a) +{ + Uart *up; + int events; + + up = a; + if(up->scc == nil) + return; + events = up->scc->scce; + eieio(); + up->scc->scce = events; + if(up->enabled){ + if(0) + print("#%ux|", events); + uartintr(up, events); + } +} + +static void +uartsetbaud(Uart *p, int rate) +{ + if(rate <= 0 || p->brgc < 0) + return; + p->baud = rate; + m->iomem->brgc[p->brgc] = baudgen(rate, 16) | BaudEnable; +} + +static void +uartsetmode(Uart *p) +{ + int r, clen; + + ilock(&p->plock); + clen = p->bpc; + if(p->parity == 'e' || p->parity == 'o') + clen++; + clen++; /* stop bit */ + if(p->stopb == 2) + clen++; + if(p->smc){ + r = p->smc->smcmr & 0x3F; /* keep mode, enable bits */ + r |= (clen<<11); + if(p->parity == 'e') + r |= 3<<8; + else if(p->parity == 'o') + r |= 2<<8; + if(p->stopb == 2) + r |= 1<<10; + eieio(); + p->smc->smcmr = r; + }else if(p->scc && p->mode == 0){ + r = p->scc->psmr & 0x8FE0; /* keep mode bits */ + r |= ((p->bpc-5)&3)<<12; + if(p->parity == 'e') + r |= (6<<2)|2; + else if(p->parity == 'o') + r |= (4<<2)|0; + if(p->stopb == 2) + r |= 1<<14; + eieio(); + p->scc->psmr = r; + } + iunlock(&p->plock); +} + +static void +uartparity(Uart *p, char type) +{ + ilock(&p->plock); + p->parity = type; + iunlock(&p->plock); + uartsetmode(p); +} + +/* + * set bits/character + */ +static void +uartbits(Uart *p, int bits) +{ + if(bits < 5 || bits > 14 || bits > 8 && p->scc) + error(Ebadarg); + + ilock(&p->plock); + p->bpc = bits; + iunlock(&p->plock); + uartsetmode(p); +} + + +/* + * toggle DTR + */ +static void +uartdtr(Uart *p, int n) +{ + if(p->scc == nil) + return; /* not possible */ + USED(n); /* not possible on FADS */ +} + +/* + * toggle RTS + */ +static void +uartrts(Uart *p, int n) +{ + p->rts = n; + if(p->scc == nil) + return; /* not possible */ + USED(n); /* not possible on FADS */ +} + +/* + * send break + */ +static void +uartbreak(Uart *p, int ms) +{ + if(p->brkcr == nil) + return; + + if(ms <= 0) + ms = 200; + + if(waserror()){ + ilock(&p->plock); + *p->brkcr = 1; + cpmop(p->cpm, RestartTx, 0); + iunlock(&p->plock); + nexterror(); + } + ilock(&p->plock); + *p->brkcr = ((p->baud/(p->bpc+2))*ms+500)/1000; + cpmop(p->cpm, StopTx, 0); + iunlock(&p->plock); + + tsleep(&up->sleep, return0, 0, ms); + + poperror(); + ilock(&p->plock); + *p->brkcr = 1; + cpmop(p->cpm, RestartTx, 0); + iunlock(&p->plock); +} + +/* + * modem flow control on/off (rts/cts) + */ +static void +uartmflow(Uart *p, int n) +{ + if(p->scc == nil) + return; /* not possible */ + if(n){ + p->modem = 1; + /* enable status interrupts ... */ + p->scc->psmr |= 1<<15; /* enable async flow control */ + p->cts = 1; + /* could change maxidl */ + }else{ + p->modem = 0; + /* stop status interrupts ... */ + p->scc->psmr &= ~(1<<15); + p->cts = 1; + } +} + +/* + * turn on a port's interrupts. set DTR and RTS + */ +void +uartenable(Uart *p) +{ + Uart **l; + + if(p->enabled) + return; + + if(p->setup == 0){ + if(p->cpmid == CPsmc1 || p->cpmid == CPsmc2) + smcsetup(p); + else + sccsetup(p); + p->setup = 1; + } + + /* + * turn on interrupts + */ + if(p->smc){ + cpmop(p->cpm, RestartTx, 0); + p->smc->smcmr |= 3; + p->smc->smcm = BSY|TXB|RXB; + eieio(); + }else if(p->scc){ + cpmop(p->cpm, RestartTx, 0); + p->scc->gsmrl |= ENT|ENR; + p->scc->sccm = BSY|TXB|RXB; + eieio(); + } + + /* + * turn on DTR and RTS + */ + uartdtr(p, 1); + uartrts(p, 1); + + /* + * assume we can send + */ + p->cts = 1; + p->blocked = 0; + + /* + * set baud rate to the last used + */ + uartsetbaud(p, p->baud); + + lock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p) + break; + } + if(*l == 0){ + p->elist = uartalloc.elist; + uartalloc.elist = p; + } + p->enabled = 1; + unlock(&uartalloc); + p->cts = 1; + p->blocked = 0; + p->xonoff = 0; + p->enabled = 1; +} + +/* + * turn off a port's interrupts. reset DTR and RTS + */ +void +uartdisable(Uart *p) +{ + Uart **l; + + /* + * turn off interrpts + */ + if(p->smc) + smcxstop(p->cpm); + else if(p->scc) + sccxstop(p->cpm); + + /* + * revert to default settings + */ + p->bpc = 8; + p->parity = 0; + p->stopb = 0; + + /* + * turn off DTR, RTS, hardware flow control & fifo's + */ + uartdtr(p, 0); + uartrts(p, 0); + uartmflow(p, 0); + p->xonoff = p->blocked = 0; + + lock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p){ + *l = p->elist; + break; + } + } + p->enabled = 0; + unlock(&uartalloc); +} + +/* + * set the next output buffer going + */ +static void +txstart(Uart *p) +{ + Block *b; + int n, flags; + + if(!p->cts || p->blocked || p->txb->status & BDReady) + return; + if((b = p->outb) == nil){ + if((b = qget(p->oq)) == nil) + return; + if(p->mode & SccPPP && + p->mode & SccAHDLC && + BLEN(b) >= 8){ /* strip framing data */ + UartAHDLC *hp; + hp = (UartAHDLC*)p->param; + if(hp != nil && (p->mode & SccIR) == 0){ + hp->txctl_tbl = nhgetl(b->rp); + hp->rxctl_tbl = nhgetl(b->rp+4); + } + b->rp += 8; + if(0) + print("tx #%lux rx #%lux\n", hp->txctl_tbl, hp->rxctl_tbl); + } + } + n = BLEN(b); + if(n <= 0) + print("txstart: 0\n"); + if(p->bpc > 8){ + /* half-word alignment and length if chars are long */ + if(PADDR(b->rp)&1){ /* must be even if chars are long */ + memmove(b->base, b->rp, n); + b->rp = b->base; + b->wp = b->rp+n; + } + if(n & 1) + n++; + } + dcflush(b->rp, n); + p->outb = b; + if(n > 0xFFFF) + n = 0xFFFE; + if(p->mode & SccHDLC) + flags = BDLast | TxTC; + else if(p->mode) + flags = BDLast; + else + flags = 0; + p->txb->addr = PADDR(b->rp); + p->txb->length = n; + eieio(); + p->txb->status = (p->txb->status & BDWrap) | flags | BDReady|BDInt; + eieio(); +} + +/* + * (re)start output + */ +static void +uartkick(void *v) +{ + Uart *p; + + p = v; + ilock(&p->plock); + if(p->outb == nil) + txstart(p); + iunlock(&p->plock); +} + +/* + * restart input if it's off + */ +static void +uartflow(void *v) +{ + Uart *p; + + p = v; + if(p->modem) + uartrts(p, 1); +} + +static void +uartsetup(int x, int lid, char *name) +{ + Uart *p; + + if(nuart >= Nuart) + return; + + p = xalloc(sizeof(Uart)); + uart[nuart] = p; + strcpy(p->name, name); + p->dev = nuart; + nuart++; + p->x = x; + p->cpmid = lid; + p->cpm = cpmdev(lid); + p->brgc = -1; + p->mode = 0; + + /* + * set rate to 9600 baud. + * 8 bits/character. + * 1 stop bit. + * interrupts enabled. + */ + p->bpc = 8; + p->parity = 0; + p->baud = 9600; + + p->iq = qopen(4*1024, Qcoalesce, uartflow, p); + p->oq = qopen(4*1024, 0, uartkick, p); +} + +/* + * called by main() to configure a duart port as a console or a mouse + */ +void +uartspecial(int port, int baud, Queue **in, Queue **out, int (*putc)(Queue*, int)) +{ + Uart *p; + + if(port < 0 || port >= nuart || (p = uart[port]) == nil) + return; /* specified port not implemented */ + uartenable(p); + if(baud) + uartsetbaud(p, baud); + p->putc = putc; + if(in) + *in = p->iq; + if(out) + *out = p->oq; + p->opens++; +} + +static int +uartinput(Uart *p, BD *bd) +{ + int ch, dokick, i, l; + uchar *bp; + + dokick = 0; + if(bd->status & RxFR) + p->frame++; + if(bd->status & RxOV) + p->overrun++; + l = bd->length; + if(bd->status & RxPR){ + p->perror++; + l--; /* it's the last character */ + } + bp = KADDR(bd->addr); + if(p->xonoff || p->putc && p->opens==1){ + for(i=0; ixonoff){ + if(ch == CTLS){ + p->blocked = 1; + cpmop(p->cpm, StopTx, 0); + }else if (ch == CTLQ){ + p->blocked = 0; + dokick = 1; + } + /* BUG? should discard on/off char? */ + } + if(p->putc) + (*p->putc)(p->iq, ch); + } + } + if(l > 0 && (p->putc == nil || p->opens>1)) + qproduce(p->iq, bp, l); + return dokick; +} + +static void +framedinput(Uart *p, BD *bd) +{ + Block *pkt; + int l; + + pkt = p->partial; + p->partial = nil; + if(bd->status & RxOV){ + p->overrun++; + goto Discard; + } + if(bd->status & (RxAB|RxCR|RxCD|RxLG|RxNO|RxDE|RxBOF|RxBRK)){ + if(bd->status & RxCR) + p->crcerr++; + else + p->frame++; + goto Discard; + } + if(pkt == nil){ + pkt = iallocb(1500); /* TO DO: allocate less if possible */ + if(pkt == nil) + return; + } + l = bd->length; + if(bd->status & BDLast) + l -= BLEN(pkt); /* last one gives size of entire frame */ + if(l > 0){ + if(pkt->wp+l > pkt->lim) + goto Discard; + memmove(pkt->wp, KADDR(bd->addr), l); + pkt->wp += l; + } + if(0) + print("#%ux|", bd->status); + if(bd->status & BDLast){ + if(p->mode & (SccHDLC|SccAHDLC)){ + if(BLEN(pkt) <= 2){ + p->frame++; + goto Discard; + } + pkt->wp -= 2; /* strip CRC */ + } + qpass(p->iq, pkt); + }else + p->partial = pkt; + return; + +Discard: + if(pkt != nil) + freeb(pkt); +} + +/* + * handle an interrupt to a single uart + */ +static void +uartintr(Uart *p, int events) +{ + int dokick; + BD *bd; + Block *b; + + if(events & BSY) + p->overrun++; + p->interrupts++; + dokick = 0; + while(p->rxb != nil && ((bd = &p->rxb[p->rdrx])->status & BDEmpty) == 0){ + dcinval(KADDR(bd->addr), bd->length); + if(p->mode) + framedinput(p, bd); + else if(uartinput(p, bd)) + dokick = 1; + bd->status = (bd->status & BDWrap) | BDEmpty|BDInt; + eieio(); + if(++p->rdrx >= Nbuf) + p->rdrx = 0; + } + if((bd = p->txb) != nil){ + if((bd->status & BDReady) == 0){ + ilock(&p->plock); + if((b = p->outb) != nil){ + b->rp += bd->length; + if(b->rp >= b->wp){ + p->outb = nil; + freeb(b); + } + } + txstart(p); + iunlock(&p->plock); + } + } + eieio(); + /* TO DO: modem status isn't available on 82xFADS */ + if(dokick && p->cts && !p->blocked){ + if(p->outb == nil){ + ilock(&p->plock); + txstart(p); + iunlock(&p->plock); + } + cpmop(p->cpm, RestartTx, 0); + } else if (events & TXE) + cpmop(p->cpm, RestartTx, 0); +} + +/* + * used to ensure uart console output when debugging + */ +void +uartwait(void) +{ + Uart *p = uart[0]; + int s; + + while(p && (p->outb||qlen(p->oq))){ + if(islo()) + continue; + s = splhi(); + if((p->txb->status & BDReady) == 0){ + p->blocked = 0; + p->cts = 1; + if(p->scc == nil) + smcuintr(nil, p); + else + sccuintr(nil, p); + } + splx(s); + } +} + +static Dirtab *uartdir; +static int ndir; + +static void +setlength(int i) +{ + Uart *p; + + if(i >= 0){ + p = uart[i]; + if(p && p->opens && p->iq) + uartdir[1+4*i].length = qlen(p->iq); + } else for(i = 0; i < nuart; i++){ + p = uart[i]; + if(p && p->opens && p->iq) + uartdir[1+4*i].length = qlen(p->iq); + } + +} + +void +uartinstall(void) +{ + static int already; + int i, n; + char name[2*KNAMELEN]; + if(already) + return; + already = 1; + n = 0; + for(i=0; i<2; i++) + if(conf.smcuarts & (1<name, "."); + mkqid(&dp->qid, 0, 0, QTDIR); + dp->length = 0; + dp->perm = DMDIR|0555; + dp++; + for(i = 0; i < nuart; i++){ + /* 4 directory entries per port */ + strcpy(dp->name, uart[i]->name); + dp->qid.path = NETQID(i, Ndataqid); + dp->perm = 0660; + dp++; + sprint(dp->name, "%sctl", uart[i]->name); + dp->qid.path = NETQID(i, Nctlqid); + dp->perm = 0660; + dp++; + sprint(dp->name, "%sstatus", uart[i]->name); + dp->qid.path = NETQID(i, Nstatqid); + dp->perm = 0444; + dp++; + sprint(dp->name, "%smode", uart[i]->name); + dp->qid.path = NETQID(i, Ntypeqid); + dp->perm = 0660; + dp++; + } +} + +static Chan* +uartattach(char *spec) +{ + return devattach('t', spec); +} + +static Walkqid* +uartwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, uartdir, ndir, devgen); +} + +static int +uartstat(Chan *c, uchar *dp, int n) +{ + if(NETTYPE(c->qid.path) == Ndataqid) + setlength(NETID(c->qid.path)); + return devstat(c, dp, n, uartdir, ndir, devgen); +} + +static Chan* +uartopen(Chan *c, int omode) +{ + Uart *p; + + c = devopen(c, omode, uartdir, ndir, devgen); + + switch(NETTYPE(c->qid.path)){ + case Nctlqid: + case Ndataqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(p->opens++ == 0){ + uartenable(p); + qreopen(p->iq); + qreopen(p->oq); + } + qunlock(p); + break; + } + + return c; +} + +static void +uartclose(Chan *c) +{ + Uart *p; + + if(c->qid.type & QTDIR) + return; + if((c->flag & COPEN) == 0) + return; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + case Nctlqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(--(p->opens) == 0){ + uartdisable(p); + qclose(p->iq); + qclose(p->oq); + } + qunlock(p); + break; + } +} + +static long +uartstatus(Chan*, Uart *p, void *buf, long n, long offset) +{ + IMM *io; + char str[256]; + +// TO DO: change to standard format for first line: +//"b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n" + sprint(str, "opens %d ferr %lud oerr %lud crcerr %lud baud %ud perr %lud intr %lud", p->opens, + p->frame, p->overrun, p->crcerr, p->baud, p->perror, p->interrupts); + /* TO DO: cts, dsr, ring, dcd, dtr, rts aren't all available on 82xFADS */ + io = m->iomem; + if(p->scc){ + if((io->pcdat & SIBIT(9)) == 0) + strcat(str, " cts"); + if((io->pcdat & SIBIT(8)) == 0) + strcat(str, " dcd"); + if((io->pbdat & IBIT(22)) == 0) + strcat(str, " dtr"); + }else if(p->smc){ + if((io->pbdat & IBIT(23)) == 0) + strcat(str, " dtr"); + } + strcat(str, "\n"); + return readstr(offset, buf, n, str); +} + +static long +uartread(Chan *c, void *buf, long n, vlong offset) +{ + Uart *p; + + if(c->qid.type & QTDIR){ + setlength(-1); + return devdirread(c, buf, n, uartdir, ndir, devgen); + } + + p = uart[NETID(c->qid.path)]; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qread(p->iq, buf, n); + case Nctlqid: + return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE); + case Nstatqid: + return uartstatus(c, p, buf, n, offset); + case Ntypeqid: + return readnum(offset, buf, n, p->mode, NUMSIZE); + } + + return 0; +} + +static Block* +uartbread(Chan *c, long n, ulong offset) +{ + if(c->qid.type & QTDIR || NETTYPE(c->qid.path) != Ndataqid) + return devbread(c, n, offset); + return qbread(uart[NETID(c->qid.path)]->iq, n); +} + +static void +uartctl(Uart *p, char *cmd) +{ + int i, n; + + /* let output drain for a while */ + for(i = 0; i < 16 && qlen(p->oq); i++) + tsleep(&p->r, (int(*)(void*))qlen, p->oq, 125); + + if(strncmp(cmd, "break", 5) == 0){ + uartbreak(p, 0); + return; + } + + n = atoi(cmd+1); + switch(*cmd){ + case 'B': + case 'b': + uartsetbaud(p, n); + break; + case 'D': + case 'd': + uartdtr(p, n); + break; + case 'f': + case 'F': + qflush(p->oq); + break; + case 'H': + case 'h': + qhangup(p->iq, 0); + qhangup(p->oq, 0); + break; + case 'L': + case 'l': + uartbits(p, n); + break; + case 'm': + case 'M': + uartmflow(p, n); + break; + case 'n': + case 'N': + qnoblock(p->oq, n); + break; + case 'P': + case 'p': + uartparity(p, *(cmd+1)); + break; + case 'K': + case 'k': + uartbreak(p, n); + break; + case 'R': + case 'r': + uartrts(p, n); + break; + case 'Q': + case 'q': + qsetlimit(p->iq, n); + qsetlimit(p->oq, n); + break; + case 'W': + case 'w': + /* obsolete */ + break; + case 'X': + case 'x': + p->xonoff = n; + break; + case 'Z': + case 'z': + p->loopback = n; + break; + } +} + +static long +uartwrite(Chan *c, void *buf, long n, vlong offset) +{ + Uart *p; + char cmd[32]; + int m, inuse; + + USED(offset); + + if(c->qid.type & QTDIR) + error(Eperm); + + p = uart[NETID(c->qid.path)]; + + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qwrite(p->oq, buf, n); + case Nctlqid: + if(n >= sizeof(cmd)) + n = sizeof(cmd)-1; + memmove(cmd, buf, n); + cmd[n] = 0; + uartctl(p, cmd); + return n; + case Ntypeqid: + if(p->smc || p->putc) + error(Ebadarg); + if(n >= sizeof(cmd)) + n = sizeof(cmd)-1; + memmove(cmd, buf, n); + cmd[n] = 0; + m = strtoul(cmd, nil, 0); + inuse = 0; + qlock(p); + if(p->opens == 0){ + p->mode = m & 0x7F; + p->loopback = (m&0x80)!=0; + p->setup = 0; + }else + inuse = 1; + qunlock(p); + if(inuse) + error(Einuse); + return n; + } +} + +static long +uartbwrite(Chan *c, Block *bp, ulong offset) +{ + if(c->qid.type & QTDIR || NETTYPE(c->qid.path) != Ndataqid) + return devbwrite(c, bp, offset); + return qbwrite(uart[NETID(c->qid.path)]->oq, bp); +} + +static int +uartwstat(Chan *c, uchar *dp, int n) +{ + Dir d; + Dirtab *dt; + + if(!iseve()) + error(Eperm); + if(c->qid.type & QTDIR) + error(Eperm); + if(NETTYPE(c->qid.path) == Nstatqid) + error(Eperm); + + dt = &uartdir[1+4 * NETID(c->qid.path)]; + n = convM2D(dp, n, &d, nil); + if(d.mode != ~0UL){ + d.mode &= 0666; + dt[0].perm = dt[1].perm = d.mode; + } + return n; +} + +Dev uartdevtab = { + 't', + "uart", + + uartreset, + devinit, + devshutdown, + uartattach, + uartwalk, + uartstat, + uartopen, + devcreate, + uartclose, + uartread, + uartbread, + uartwrite, + uartbwrite, + devremove, + uartwstat, +}; diff --git a/os/mpc/dsp.c b/os/mpc/dsp.c new file mode 100644 index 00000000..ffe906e9 --- /dev/null +++ b/os/mpc/dsp.c @@ -0,0 +1,289 @@ +/* + * DSP support functions + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "io.h" + +#include "dsp.h" + +enum { + Ndsp = 2, /* determined by hardware */ + + NHOLES= 64 +}; + +typedef struct DSPparam DSPparam; +struct DSPparam { + ulong fdbase; /* function descriptor table physical base address */ + ulong fd_ptr; /* function descriptor pointer */ + ulong dstate; /* DSP state */ + ulong resvd[2]; + ushort dstatus; /* current function descriptor status */ + ushort i; /* number of iterations */ + ushort tap; /* number of TAPs */ + ushort cbase; + ushort anon1; /* sample buffer size-1 */ + ushort xptr; /* pointer to sample */ + ushort anon2; /* output buffer size-1 */ + ushort yptr; /* pointer to output */ + ushort m; /* sample buffer size-1 */ + ushort anon3; /* sample buffer pointer */ + ushort n; /* output buffer size -1 */ + ushort anon4; /* output buffer pointer */ + ushort k; /* coefficient buffer size - 1 */ + ushort anon5; /* coefficient buffer pointer */ +}; + +struct DSP { + Lock; /* protects state */ + void (*done)(void*); + void* arg; + DSPparam* par; + CPMdev* cpm; + + QLock; /* protects busyr */ + int busy; + Rendez busyr; +}; + +static DSP dsps[Ndsp]; +static Lock dsplock; +static int dspinit; +static struct { + QLock; + ulong avail; + Rendez wantr; +} dspalloc; + +static Map fndmapv[NHOLES]; +static RMap fndmap = {"DSP function descriptors"}; + +static void +dspinterrupt(Ureg*, void*) +{ + int i; + ushort events; + DSP *dsp; + + events = m->iomem->sdsr; + m->iomem->sdsr = events; + if(events & (1<<7)) + panic("dsp: SDMA channel bus error sdar=#%lux", m->iomem->sdar); + for(i=0; ibusy){ + dsp->busy = 0; + if(dsp->done) + dsp->done(dsp->arg); + else + wakeup(&dsp->busyr); + }else + print("dsp%d: empty interrupt\n", i); + } +} + +/* + * called by system initialisation to set up the DSPs + */ +void +dspinitialise(void) +{ + CPMdev *d; + + ilock(&dsplock); + if(dspinit == 0){ + mapinit(&fndmap, fndmapv, sizeof(fndmapv)); + d = cpmdev(CPdsp1); + dsps[0].cpm = d; + dsps[0].par = d->param; + d = cpmdev(CPdsp2); + dsps[1].cpm = d; + dsps[1].par = d->param; + intrenable(VectorCPIC+d->irq, dspinterrupt, nil, BUSUNKNOWN, "dsp"); + dspalloc.avail = (1<= Ndsp){ + sleep(&dspalloc.wantr, dspavail, nil); + i = 0; + } + if(dspalloc.avail & (1<busy) + panic("dspacquire"); + dsp->done = done; + dsp->arg = arg; + poperror(); + qunlock(&dspalloc); + return dsp; +} + +/* + * relinquish access to the given DSP + */ +void +dsprelease(DSP *dsp) +{ + ulong bit; + + if(dsp == nil) + return; + bit = 1 << (dsp-dsps); + if(dspalloc.avail & bit) + panic("dsprelease"); + dspalloc.avail |= bit; + wakeup(&dspalloc.wantr); +} + +/* + * execute f[0] to f[n-1] on the given DSP + */ +void +dspexec(DSP *dsp, FnD *f, ulong n) +{ + dspsetfn(dsp, f, n); + dspstart(dsp); +} + +/* + * set the DSP to execute f[0] to f[n-1] + */ +void +dspsetfn(DSP *dsp, FnD *f, ulong n) +{ + f[n-1].status |= FnWrap; + ilock(dsp); + dsp->par->fdbase = PADDR(f); + iunlock(dsp); + cpmop(dsp->cpm, InitDSP, 0); +} + +/* + * start execution of the preset function(s) + */ +void +dspstart(DSP *dsp) +{ + ilock(dsp); + dsp->busy = 1; + iunlock(dsp); + cpmop(dsp->cpm, StartDSP, 0); +} + +static int +dspdone(void *a) +{ + return ((DSP*)a)->busy; +} + +/* + * wait until the DSP has completed execution + */ +void +dspsleep(DSP *dsp) +{ + sleep(&dsp->busyr, dspdone, dsp); +} + +/* + * allocate n function descriptors + */ +FnD* +fndalloc(ulong n) +{ + ulong a, nb, pgn; + FnD *f; + + if(n == 0) + return nil; + if(dspinit == 0) + dspinitialise(); + nb = n*sizeof(FnD); + while((a = rmapalloc(&fndmap, 0, nb, sizeof(FnD))) != 0){ + /* expected to loop just once, but might lose a race with another dsp user */ + pgn = (nb+BY2PG-1)&~(BY2PG-1); + a = PADDR(xspanalloc(pgn, sizeof(FnD), 0)); + if(a == 0) + return nil; + mapfree(&fndmap, a, pgn); + } + f = KADDR(a); + f[n-1].status = FnWrap; + return f; +} + +/* + * free n function descriptors + */ +void +fndfree(FnD *f, ulong n) +{ + if(f != nil) + mapfree(&fndmap, PADDR(f), n*sizeof(FnD)); +} + +/* + * allocate an IO buffer region in shared memory for use by the DSP + */ +void* +dspmalloc(ulong n) +{ + ulong i; + + n = (n+3)&~4; + i = n; + if(n & (n-1)){ + /* align on a power of two */ + for(i=1; i < n; i <<= 1) + ; + } + return cpmalloc(n, i); /* this seems to be what 16.3.3.2 is trying to say */ +} + +/* + * free DSP buffer memory + */ +void +dspfree(void *p, ulong n) +{ + if(p != nil) + cpmfree(p, (n+3)&~4); +} diff --git a/os/mpc/dsp.h b/os/mpc/dsp.h new file mode 100644 index 00000000..9838757f --- /dev/null +++ b/os/mpc/dsp.h @@ -0,0 +1,62 @@ +/* + * MPC82x/QUICC DSP support + */ + +typedef struct DSP DSP; +typedef struct FnD FnD; + +typedef short Real; +typedef struct Complex Complex; + +struct Complex { + Real im; + Real re; +}; + +struct FnD { + ushort status; + ushort param[7]; +}; + +enum { + FnDsize = 8*2, /* each function descriptor is 8 shorts */ + + /* standard bits in FnD.status */ + FnStop = 1<<15, + FnWrap = 1<<13, + FnInt = 1<<12, + + /* optional bits */ + FnZ = 1<<11, /* FIR[35], MOD */ + FnIALL = 1<<10, /* FIRx */ + FnXinc0 = 0<<8, /* FIRx, IRR */ + FnXinc1 = 1<<8, + FnXinc2 = 2<<8, + FnXinc3 = 3<<8, + FnPC = 1<<7, /* FIRx */ + + + /* DSP functions (table 16-6) */ + FnFIR1 = 0x01, + FnFIR2 = 0x02, + FnFIR3 = 0x03, + FnFIR5 = 0x03, + FnFIR6 = 0x06, + FnIIR = 0x07, + FnMOD = 0x08, + FnDEMOD = 0x09, + FnLMS1 = 0x0A, + FnLMS2 = 0x0B, + FnWADD = 0x0C, +}; + +void dspinitialise(void); +DSP* dspacquire(void (*)(void*), void*); +void dspexec(DSP*, FnD*, ulong); +void* dspmalloc(ulong); +void dspfree(void*, ulong); +void dspsetfn(DSP*, FnD*, ulong); +void dspstart(DSP*); +void dsprelease(DSP*); +FnD* fndalloc(ulong); +void fndfree(FnD*, ulong); diff --git a/os/mpc/etherif.h b/os/mpc/etherif.h new file mode 100644 index 00000000..65b22c40 --- /dev/null +++ b/os/mpc/etherif.h @@ -0,0 +1,37 @@ +enum { + MaxEther = 6, + Ntypes = 8, +}; + +typedef struct Ether Ether; +struct Ether { +RWlock; /* TO DO */ + ISAConf; /* hardware info */ + int ctlrno; + int tbdf; /* type+busno+devno+funcno */ + int minmtu; + int maxmtu; + uchar ea[Eaddrlen]; + int encry; + + void (*attach)(Ether*); /* filled in by reset routine */ + void (*closed)(Ether*); + void (*detach)(Ether*); + void (*transmit)(Ether*); + void (*interrupt)(Ureg*, void*); + long (*ifstat)(Ether*, void*, long, ulong); + long (*ctl)(Ether*, void*, long); /* custom ctl messages */ + void (*power)(Ether*, int); /* power on/off */ + void (*shutdown)(Ether*); /* shutdown hardware before reboot */ + void *ctlr; + int pcmslot; /* PCMCIA */ + int fullduplex; /* non-zero if full duplex */ + + Queue* oq; + + Netif; +}; + +extern Block* etheriq(Ether*, Block*, int); +extern void addethercard(char*, int(*)(Ether*)); +extern int archether(int, Ether*); diff --git a/os/mpc/etherscc.c b/os/mpc/etherscc.c new file mode 100644 index 00000000..8180140e --- /dev/null +++ b/os/mpc/etherscc.c @@ -0,0 +1,528 @@ +/* + * SCCn ethernet + */ + +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +enum { + Nrdre = 16, /* receive descriptor ring entries */ + Ntdre = 16, /* transmit descriptor ring entries */ + + Rbsize = ETHERMAXTU+4, /* ring buffer size (+4 for CRC) */ + Bufsize = (Rbsize+7)&~7, /* aligned */ +}; + +enum { + /* ether-specific Rx BD bits */ + RxMiss= 1<<8, + RxeLG= 1<<5, + RxeNO= 1<<4, + RxeSH= 1<<3, + RxeCR= 1<<2, + RxeOV= 1<<1, + RxeCL= 1<<0, + RxError= (RxeLG|RxeNO|RxeSH|RxeCR|RxeOV|RxeCL), /* various error flags */ + + /* ether-specific Tx BD bits */ + TxPad= 1<<14, /* pad short frames */ + TxTC= 1<<10, /* transmit CRC */ + TxeDEF= 1<<9, + TxeHB= 1<<8, + TxeLC= 1<<7, + TxeRL= 1<<6, + TxeUN= 1<<1, + TxeCSL= 1<<0, + + /* scce */ + RXB= 1<<0, + TXB= 1<<1, + BSY= 1<<2, + RXF= 1<<3, + TXE= 1<<4, + + /* psmr */ + PRO= 1<<9, /* promiscuous mode */ + + /* gsmrl */ + ENR= 1<<5, + ENT= 1<<4, + + /* port A */ + RXD1= SIBIT(15), + TXD1= SIBIT(14), + + /* port B */ + RTS1= IBIT(19), + + /* port C */ + CTS1= SIBIT(11), + CD1= SIBIT(10), +}; + +typedef struct Etherparam Etherparam; +struct Etherparam { + SCCparam; + ulong c_pres; /* preset CRC */ + ulong c_mask; /* constant mask for CRC */ + ulong crcec; /* CRC error counter */ + ulong alec; /* alighnment error counter */ + ulong disfc; /* discard frame counter */ + ushort pads; /* short frame PAD characters */ + ushort ret_lim; /* retry limit threshold */ + ushort ret_cnt; /* retry limit counter */ + ushort mflr; /* maximum frame length reg */ + ushort minflr; /* minimum frame length reg */ + ushort maxd1; /* maximum DMA1 length reg */ + ushort maxd2; /* maximum DMA2 length reg */ + ushort maxd; /* rx max DMA */ + ushort dma_cnt; /* rx dma counter */ + ushort max_b; /* max bd byte count */ + ushort gaddr[4]; /* group address filter */ + ulong tbuf0_data0; /* save area 0 - current frm */ + ulong tbuf0_data1; /* save area 1 - current frm */ + ulong tbuf0_rba0; + ulong tbuf0_crc; + ushort tbuf0_bcnt; + ushort paddr[3]; /* physical address LSB to MSB increasing */ + ushort p_per; /* persistence */ + ushort rfbd_ptr; /* rx first bd pointer */ + ushort tfbd_ptr; /* tx first bd pointer */ + ushort tlbd_ptr; /* tx last bd pointer */ + ulong tbuf1_data0; /* save area 0 - next frame */ + ulong tbuf1_data1; /* save area 1 - next frame */ + ulong tbuf1_rba0; + ulong tbuf1_crc; + ushort tbuf1_bcnt; + ushort tx_len; /* tx frame length counter */ + ushort iaddr[4]; /* individual address filter*/ + ushort boff_cnt; /* back-off counter */ + ushort taddr[3]; /* temp address */ +}; + +typedef struct { + Lock; + int port; + int init; + int active; + SCC* scc; + CPMdev* cpm; + + Ring; + + ulong interrupts; /* statistics */ + ulong deferred; + ulong heartbeat; + ulong latecoll; + ulong retrylim; + ulong underrun; + ulong overrun; + ulong carrierlost; + ulong retrycount; +} Ctlr; + +static int sccid[] = {-1, CPscc1, CPscc2, CPscc3, CPscc4}; + +static void +attach(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ctlr->active = 1; + ctlr->scc->gsmrl |= ENR|ENT; + eieio(); +} + +static void +closed(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + if(ctlr->active){ + sccxstop(ctlr->cpm); + ilock(ctlr); + ctlr->active = 0; + iunlock(ctlr); + } +} + +static void +promiscuous(void* arg, int on) +{ + Ether *ether; + Ctlr *ctlr; + + ether = (Ether*)arg; + ctlr = ether->ctlr; + + ilock(ctlr); + if(on || ether->nmaddr) + ctlr->scc->psmr |= PRO; + else + ctlr->scc->psmr &= ~PRO; + iunlock(ctlr); +} + +static void +multicast(void* arg, uchar *addr, int on) +{ + Ether *ether; + Ctlr *ctlr; + + USED(addr, on); /* if on, could SetGroupAddress; if !on, it's hard */ + + ether = (Ether*)arg; + ctlr = ether->ctlr; + + ilock(ctlr); + if(ether->prom || ether->nmaddr) + ctlr->scc->psmr |= PRO; + else + ctlr->scc->psmr &= ~PRO; + iunlock(ctlr); +} + +static void +txstart(Ether *ether) +{ + int len; + Ctlr *ctlr; + Block *b; + BD *dre; + + ctlr = ether->ctlr; + while(ctlr->ntq < Ntdre-1){ + b = qget(ether->oq); + if(b == 0) + break; + + dre = &ctlr->tdr[ctlr->tdrh]; + if(dre->status & BDReady) + panic("ether: txstart"); + + /* + * Give ownership of the descriptor to the chip, increment the + * software ring descriptor pointer and tell the chip to poll. + */ + len = BLEN(b); + dcflush(b->rp, len); + if(ctlr->txb[ctlr->tdrh] != nil) + panic("scc/ether: txstart"); + ctlr->txb[ctlr->tdrh] = b; + if((ulong)b->rp&1) + panic("scc/ether: txstart align"); /* TO DO: ensure alignment */ + dre->addr = PADDR(b->rp); + dre->length = len; + eieio(); + dre->status = (dre->status & BDWrap) | BDReady|TxPad|BDInt|BDLast|TxTC; + eieio(); + ctlr->scc->todr = 1<<15; /* transmit now */ + eieio(); + ctlr->ntq++; + ctlr->tdrh = NEXT(ctlr->tdrh, Ntdre); + } +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + txstart(ether); + iunlock(ctlr); +} + +static void +interrupt(Ureg*, void *arg) +{ + Ether *ether; + int len, events, status; + Ctlr *ctlr; + BD *dre; + Block *b; + + ether = arg; + ctlr = ether->ctlr; + if(!ctlr->active) + return; /* not ours */ + + /* + * Acknowledge all interrupts and whine about those that shouldn't + * happen. + */ + events = ctlr->scc->scce; + eieio(); + ctlr->scc->scce = events; + eieio(); + ctlr->interrupts++; + + if(events & (TXE|BSY|RXB)){ + if(events & RXB) + ctlr->overrun++; + if(events & TXE) + ether->oerrs++; + if(0 || events & TXE) + print("ETHER.SCC#%d: scce = 0x%uX\n", ether->ctlrno, events); + } + /* + * Receiver interrupt: run round the descriptor ring logging + * errors and passing valid receive data up to the higher levels + * until we encounter a descriptor still owned by the chip. + */ + if(events & (RXF|RXB) || 1){ + dre = &ctlr->rdr[ctlr->rdrx]; + while(((status = dre->status) & BDEmpty) == 0){ + if(status & RxError || (status & (BDFirst|BDLast)) != (BDFirst|BDLast)){ + if(status & (RxeLG|RxeSH)) + ether->buffs++; + if(status & RxeNO) + ether->frames++; + if(status & RxeCR) + ether->crcs++; + if(status & RxeOV) + ether->overflows++; + //print("eth rx: %ux\n", status); + } + else{ + /* + * We have a packet. Read it in. + */ + len = dre->length-4; + if((b = iallocb(len)) != 0){ + dcinval(KADDR(dre->addr), len); + memmove(b->wp, KADDR(dre->addr), len); + b->wp += len; + etheriq(ether, b, 1); + }else + ether->soverflows++; + } + + /* + * Finished with this descriptor, reinitialise it, + * give it back to the chip, then on to the next... + */ + dre->length = 0; + dre->status = (status & BDWrap) | BDEmpty | BDInt; + eieio(); + + ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre); + dre = &ctlr->rdr[ctlr->rdrx]; + } + } + + /* + * Transmitter interrupt: handle anything queued for a free descriptor. + */ + if(events & TXB){ + lock(ctlr); + while(ctlr->ntq){ + dre = &ctlr->tdr[ctlr->tdri]; + status = dre->status; + if(status & BDReady) + break; + if(status & TxeDEF) + ctlr->deferred++; + if(status & TxeHB) + ctlr->heartbeat++; + if(status & TxeLC) + ctlr->latecoll++; + if(status & TxeRL) + ctlr->retrylim++; + if(status & TxeUN) + ctlr->underrun++; + if(status & TxeCSL) + ctlr->carrierlost++; + ctlr->retrycount += (status>>2)&0xF; + b = ctlr->txb[ctlr->tdri]; + if(b == nil) + panic("scce/interrupt: bufp"); + ctlr->txb[ctlr->tdri] = nil; + freeb(b); + ctlr->ntq--; + ctlr->tdri = NEXT(ctlr->tdri, Ntdre); + } + txstart(ether); + unlock(ctlr); + } + if(events & TXE) + cpmop(ctlr->cpm, RestartTx, 0); +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + char *p; + int len; + Ctlr *ctlr; + + if(n == 0) + return 0; + + ctlr = ether->ctlr; + + p = malloc(READSTR); + len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts); + len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", ctlr->carrierlost); + len += snprint(p+len, READSTR-len, "heartbeat: %lud\n", ctlr->heartbeat); + len += snprint(p+len, READSTR-len, "retrylimit: %lud\n", ctlr->retrylim); + len += snprint(p+len, READSTR-len, "retrycount: %lud\n", ctlr->retrycount); + len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", ctlr->latecoll); + len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", ctlr->overrun); + len += snprint(p+len, READSTR-len, "txunderruns: %lud\n", ctlr->underrun); + snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->deferred); + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +/* + * This follows the MPC823 user guide: section16.9.23.7's initialisation sequence, + * except that it sets the right bits for the MPC823ADS board when SCC2 is used, + * and those for the 860/821 development board for SCC1. + */ +static void +sccsetup(Ctlr *ctlr, SCC *scc, Ether *ether) +{ + int i, rcs, tcs, w; + Etherparam *p; + IMM *io; + + + i = 2*(ctlr->port-1); + io = ioplock(); + w = (TXD1|RXD1)<papar |= w; /* enable TXDn and RXDn pins */ + io->padir &= ~w; + io->paodr &= ~w; /* not open drain */ + + w = (CD1|CTS1)<pcpar &= ~w; /* enable CLSN (CTSn) and RENA (CDn) */ + io->pcdir &= ~w; + io->pcso |= w; + iopunlock(); + + /* clocks and transceiver control: details depend on the board's wiring */ + archetherenable(sccid[ctlr->port], &rcs, &tcs, ether->mbps, ether->fullduplex); + + sccnmsi(ctlr->port, rcs, tcs); /* connect the clocks */ + + p = ctlr->cpm->param; + memset(p, 0, sizeof(*p)); + p->rfcr = 0x18; + p->tfcr = 0x18; + p->mrblr = Bufsize; + p->rbase = PADDR(ctlr->rdr); + p->tbase = PADDR(ctlr->tdr); + + cpmop(ctlr->cpm, InitRxTx, 0); + + p->c_pres = ~0; + p->c_mask = 0xDEBB20E3; + p->crcec = 0; + p->alec = 0; + p->disfc = 0; + p->pads = 0x8888; + p->ret_lim = 0xF; + p->mflr = Rbsize; + p->minflr = ETHERMINTU+4; + p->maxd1 = Bufsize; + p->maxd2 = Bufsize; + p->p_per = 0; /* only moderate aggression */ + + for(i=0; ipaddr[2-i/2] = (ether->ea[i+1]<<8)|ether->ea[i]; /* it's not the obvious byte order */ + + scc->psmr = (2<<10)|(5<<1); /* 32-bit CRC, ignore 22 bits before SFD */ + scc->dsr = 0xd555; + scc->gsmrh = 0; /* normal operation */ + scc->gsmrl = (1<<28)|(4<<21)|(1<<19)|0xC; /* transmit clock invert, 48 bit preamble, repetitive 10 preamble, ethernet */ + eieio(); + scc->scce = ~0; /* clear all events */ + eieio(); + scc->sccm = TXE | RXF | TXB; /* enable interrupts */ + eieio(); + + io = ioplock(); + w = RTS1<<(ctlr->port-1); /* enable TENA pin (RTSn) */ + io->pbpar |= w; + io->pbdir |= w; + iopunlock(); + + /* gsmrl enable is deferred until attach */ +} + +static int +reset(Ether* ether) +{ + uchar ea[Eaddrlen]; + CPMdev *cpm; + Ctlr *ctlr; + SCC *scc; + + if(m->speed < 24){ + print("%s ether: system speed must be >= 24MHz for ether use\n", ether->type); + return -1; + } + + if(!(ether->port >= 1 && ether->port <= 4)){ + print("%s ether: no SCC port %lud\n", ether->type, ether->port); + return -1; + } + + /* + * Insist that the platform-specific code provide the Ethernet address + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + print("no ether address"); + return -1; + } + + cpm = cpmdev(sccid[ether->port]); + if(cpm == nil) + return -1; + ether->irq = VectorCPIC + cpm->irq; + scc = cpm->regs; + ctlr = malloc(sizeof(*ctlr)); + ether->ctlr = ctlr; + memset(ctlr, 0, sizeof(*ctlr)); + ctlr->cpm = cpm; + ctlr->scc = scc; + ctlr->port = ether->port; + + if(ioringinit(ctlr, Nrdre, Ntdre, Bufsize) < 0) + panic("etherscc init"); + + sccsetup(ctlr, scc, ether); + + ether->attach = attach; + ether->closed = closed; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = ifstat; + + ether->arg = ether; + ether->promiscuous = promiscuous; + ether->multicast = multicast; + + return 0; +} + +void +etherscclink(void) +{ + addethercard("SCC", reset); +} diff --git a/os/mpc/faultpower.c b/os/mpc/faultpower.c new file mode 100644 index 00000000..6d3b163b --- /dev/null +++ b/os/mpc/faultpower.c @@ -0,0 +1,49 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" +#include "io.h" + +enum +{ + MC_IFETCH = (1<<30), + MC_STORE = (1<<11), /* bit 23 if X-form, bit 3 if D-form => write */ + DSI_STORE = (1<<25), + DSI_PROT = (1<<27), +}; + +void +faultpower(Ureg *ur) +{ + ulong addr; + char buf[ERRMAX]; + int read, i; + + addr = ur->pc; /* assume instr. exception */ + read = 1; + i = ur->cause >> 8; + if(i == CDSI || i == CDTLBE || i == CMCHECK && (ur->status&MC_IFETCH) == 0) { /* data access error including machine check load/store */ + addr = getdar(); + if(getdsisr() & (DSI_STORE|MC_STORE)) + read = 0; + } else if(i == CDMISS) /* DTLB miss */ + addr = getdepn() & ~0x3FF; /* can't distinguish read/write, but Inferno doesn't care */ +/* +print("fault %lux %lux %lux %d\n", ur->pc, ur->cause, addr, read); +print("imiss %lux dmiss %lux hash1 %lux dcmp %lux hash2 %lux\n", + getimiss(), getdmiss(), gethash1(), getdcmp(), gethash2()); +print("up %lux %lux %lux\n", m->upage, m->upage->virt, m->upage->phys); +*/ + + up->dbgreg = ur; /* For remote ACID */ + + spllo(); + sprint(buf, "trap: fault %s pc=0x%lux addr=0x%lux", + read ? "read" : "write", ur->pc, addr); + if(up->type == Interp) + disfault(ur, buf); + dumpregs(ur); + panic("fault: %s\n", buf); +} diff --git a/os/mpc/fp.s b/os/mpc/fp.s new file mode 100644 index 00000000..e37bb63a --- /dev/null +++ b/os/mpc/fp.s @@ -0,0 +1,205 @@ +/* + * support for floating-point hardware + */ + +#include "mem.h" + +/* on some models mtmsr doesn't synchronise enough (eg, 603e) */ +#define MSRSYNC SYNC; ISYNC + +#define FPON(X, Y)\ + MOVW MSR, X;\ + OR $FPE, X, Y;\ + SYNC;\ + ISYNC;\ + MOVW Y, MSR;\ + MSRSYNC + +#define FPOFF(X,Y)\ + MOVW MSR, X;\ + RLWNM $0, X, $~FPE, Y;\ + SYNC;\ + ISYNC;\ + MOVW Y, MSR;\ + MSRSYNC + +#define FPPREV(X)\ + SYNC;\ + ISYNC;\ + MOVW X, MSR;\ + MSRSYNC + +TEXT kfpinit(SB), $0 + MOVFL $0,FPSCR(7) + MOVFL $0xD,FPSCR(6) /* VE, OE, ZE */ + MOVFL $0, FPSCR(5) + MOVFL $0, FPSCR(3) + MOVFL $0, FPSCR(2) + MOVFL $0, FPSCR(1) + MOVFL $0, FPSCR(0) + + FMOVD $4503601774854144.0, F27 + FMOVD $0.5, F29 + FSUB F29, F29, F28 + FADD F29, F29, F30 + FADD F30, F30, F31 + FMOVD F28, F0 + FMOVD F28, F1 + FMOVD F28, F2 + FMOVD F28, F3 + FMOVD F28, F4 + FMOVD F28, F5 + FMOVD F28, F6 + FMOVD F28, F7 + FMOVD F28, F8 + FMOVD F28, F9 + FMOVD F28, F10 + FMOVD F28, F11 + FMOVD F28, F12 + FMOVD F28, F13 + FMOVD F28, F14 + FMOVD F28, F15 + FMOVD F28, F16 + FMOVD F28, F17 + FMOVD F28, F18 + FMOVD F28, F19 + FMOVD F28, F20 + FMOVD F28, F21 + FMOVD F28, F22 + FMOVD F28, F23 + FMOVD F28, F24 + FMOVD F28, F25 + FMOVD F28, F26 + RETURN + +TEXT getfpscr(SB), $8 + FPON(R4, R5) + MOVFL FPSCR, F3 + FMOVD F3, -8(SP) + MOVW -4(SP), R3 + FPPREV(R4) + RETURN + +TEXT fpsave(SB), $0 + FPON(R4, R4) + + FMOVD F0,0(R3) + FMOVD F1,8(R3) + FMOVD F2,16(R3) + FMOVD F3,24(R3) + FMOVD F4,32(R3) + FMOVD F5,40(R3) + FMOVD F6,48(R3) + FMOVD F7,56(R3) + FMOVD F8,64(R3) + FMOVD F9,72(R3) + FMOVD F10,80(R3) + FMOVD F11,88(R3) + FMOVD F12,96(R3) + FMOVD F13,104(R3) + FMOVD F14,112(R3) + FMOVD F15,120(R3) + FMOVD F16,128(R3) + FMOVD F17,136(R3) + FMOVD F18,144(R3) + FMOVD F19,152(R3) + FMOVD F20,160(R3) + FMOVD F21,168(R3) + FMOVD F22,176(R3) + FMOVD F23,184(R3) + FMOVD F24,192(R3) + FMOVD F25,200(R3) + FMOVD F26,208(R3) + FMOVD F27,216(R3) + FMOVD F28,224(R3) + FMOVD F29,232(R3) + FMOVD F30,240(R3) + FMOVD F31,248(R3) + + MOVFL FPSCR, F0 + FMOVD F0, 256(R3) + MOVFL $0,FPSCR(7) + MOVFL $0xD,FPSCR(6) /* VE, OE, ZE */ + MOVFL $0, FPSCR(5) + MOVFL $0, FPSCR(4) + MOVFL $0, FPSCR(3) + MOVFL $0, FPSCR(2) + MOVFL $0, FPSCR(1) + MOVFL $0, FPSCR(0) + + FPOFF(R4, R4) + RETURN + +TEXT fprestore(SB), $0 + FPON(R4, R4) + + FMOVD 256(R3), F0 + MOVFL F0, FPSCR + FMOVD 0(R3), F0 + FMOVD 8(R3), F1 + FMOVD 16(R3), F2 + FMOVD 24(R3), F3 + FMOVD 32(R3), F4 + FMOVD 40(R3), F5 + FMOVD 48(R3), F6 + FMOVD 56(R3), F7 + FMOVD 64(R3), F8 + FMOVD 72(R3), F9 + FMOVD 80(R3), F10 + FMOVD 88(R3), F11 + FMOVD 96(R3), F12 + FMOVD 104(R3), F13 + FMOVD 112(R3), F14 + FMOVD 120(R3), F15 + FMOVD 128(R3), F16 + FMOVD 136(R3), F17 + FMOVD 144(R3), F18 + FMOVD 152(R3), F19 + FMOVD 160(R3), F20 + FMOVD 168(R3), F21 + FMOVD 176(R3), F22 + FMOVD 184(R3), F23 + FMOVD 192(R3), F24 + FMOVD 200(R3), F25 + FMOVD 208(R3), F26 + FMOVD 216(R3), F27 + FMOVD 224(R3), F28 + FMOVD 232(R3), F29 + FMOVD 240(R3), F30 + FMOVD 248(R3), F31 + + RETURN + +TEXT clrfptrap(SB), $0 + FPON(R4, R5) + MOVFL $0, FPSCR(5) + MOVFL $0, FPSCR(3) + MOVFL $0, FPSCR(2) + MOVFL $0, FPSCR(1) + MOVFL $0, FPSCR(0) + FPPREV(R4) + RETURN + +TEXT fpinit(SB), $0 + FPON(R4, R5) + BL kfpinit(SB) + RETURN + +TEXT fpoff(SB), $0 + FPOFF(R4, R5) + RETURN + + +TEXT FPsave(SB), 1, $0 + FPON(R4, R5) + MOVFL FPSCR, F0 + FMOVD F0, 0(R3) + FPPREV(R4) + RETURN + +TEXT FPrestore(SB), 1, $0 + FPON(R4, R5) + FMOVD 0(R3), F0 + MOVFL F0, FPSCR + FPPREV(R4) + RETURN diff --git a/os/mpc/fpi.h b/os/mpc/fpi.h new file mode 100644 index 00000000..dfb9b1df --- /dev/null +++ b/os/mpc/fpi.h @@ -0,0 +1,61 @@ +typedef long Word; +typedef unsigned long Single; +typedef struct { + unsigned long h; + unsigned long l; +} Double; + +enum { + FractBits = 28, + CarryBit = 0x10000000, + HiddenBit = 0x08000000, + MsBit = HiddenBit, + NGuardBits = 3, + GuardMask = 0x07, + LsBit = (1<e >= ExpInfinity) +#define IsInfinity(n) (IsWeird(n) && (n)->h == HiddenBit && (n)->l == 0) +#define SetInfinity(n) ((n)->e = ExpInfinity, (n)->h = HiddenBit, (n)->l = 0) +#define IsNaN(n) (IsWeird(n) && (((n)->h & ~HiddenBit) || (n)->l)) +#define SetQNaN(n) ((n)->s = 0, (n)->e = ExpInfinity, \ + (n)->h = HiddenBit|(LsBit<<1), (n)->l = 0) +#define IsZero(n) ((n)->e == 1 && (n)->h == 0 && (n)->l == 0) +#define SetZero(n) ((n)->e = 1, (n)->h = 0, (n)->l = 0) + +/* + * fpi.c + */ +extern void fpiround(Internal *); +extern void fpiadd(Internal *, Internal *, Internal *); +extern void fpisub(Internal *, Internal *, Internal *); +extern void fpimul(Internal *, Internal *, Internal *); +extern void fpidiv(Internal *, Internal *, Internal *); +extern int fpicmp(Internal *, Internal *); +extern void fpinormalise(Internal*); + +/* + * fpimem.c + */ +extern void fpis2i(Internal *, void *); +extern void fpid2i(Internal *, void *); +extern void fpiw2i(Internal *, void *); +extern void fpii2s(void *, Internal *); +extern void fpii2d(void *, Internal *); +extern void fpii2w(Word *, Internal *); diff --git a/os/mpc/fpipower.c b/os/mpc/fpipower.c new file mode 100644 index 00000000..ec4363b2 --- /dev/null +++ b/os/mpc/fpipower.c @@ -0,0 +1,970 @@ +/* + * this doesn't attempt to implement Power architecture floating-point properties + * that aren't visible in the Inferno environment. + * all arithmetic is done in double precision. + * the FP trap status isn't updated. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "ureg.h" + +#include "fpi.h" + +#define REG(x) (*(long*)(((char*)em->ur)+roff[(x)])) +#define FR(x) (*(Internal*)em->fr[(x)&0x1F]) +#define REGSP 1 /* stack pointer */ + +/* BUG: check fetch (not worthwhile in Inferno) */ +#define getulong(a) (*(ulong*)(a)) + +enum { + CRLT = 1<<31, + CRGT = 1<<30, + CREQ = 1<<29, + CRSO = 1<<28, + CRFU = CRSO, + + CRFX = 1<<27, + CRFEX = 1<<26, + CRVX = 1<<25, + CROX = 1<<24, +}; + +#define getCR(x,w) (((w)>>(28-(x*4)))&0xF) +#define mkCR(x,v) (((v)&0xF)<<(28-(x*4))) + +#define simm(xx, ii) xx = (short)(ii&0xFFFF); +#define getairr(i) rd = (i>>21)&0x1f; ra = (i>>16)&0x1f; simm(imm,i) +#define getarrr(i) rd = (i>>21)&0x1f; ra = (i>>16)&0x1f; rb = (i>>11)&0x1f; +#define getop(i) ((i>>26)&0x3F) +#define getxo(i) ((i>>1)&0x3FF) + +#define FPS_FX (1<<31) /* exception summary (sticky) */ +#define FPS_EX (1<<30) /* enabled exception summary */ +#define FPS_VX (1<<29) /* invalid operation exception summary */ +#define FPS_OX (1<<28) /* overflow exception OX (sticky) */ +#define FPS_UX (1<<27) /* underflow exception UX (sticky) */ +#define FPS_ZX (1<<26) /* zero divide exception ZX (sticky) */ +#define FPS_XX (1<<25) /* inexact exception XX (sticky) */ +#define FPS_VXSNAN (1<<24) /* invalid operation exception for SNaN (sticky) */ +#define FPS_VXISI (1<<23) /* invalid operation exception for ∞-∞ (sticky) */ +#define FPS_VXIDI (1<<22) /* invalid operation exception for ∞/∞ (sticky) */ +#define FPS_VXZDZ (1<<21) /* invalid operation exception for 0/0 (sticky) */ +#define FPS_VXIMZ (1<<20) /* invalid operation exception for ∞*0 (sticky) */ +#define FPS_VXVC (1<<19) /* invalid operation exception for invalid compare (sticky) */ +#define FPS_FR (1<<18) /* fraction rounded */ +#define FPS_FI (1<<17) /* fraction inexact */ +#define FPS_FPRF (1<<16) /* floating point result class */ +#define FPS_FPCC (0xF<<12) /* <, >, =, unordered */ +#define FPS_VXCVI (1<<8) /* enable exception for invalid integer convert (sticky) */ +#define FPS_VE (1<<7) /* invalid operation exception enable */ +#define FPS_OE (1<<6) /* enable overflow exceptions */ +#define FPS_UE (1<<5) /* enable underflow */ +#define FPS_ZE (1<<4) /* enable zero divide */ +#define FPS_XE (1<<3) /* enable inexact exceptions */ +#define FPS_RN (3<<0) /* rounding mode */ + +typedef struct Emreg Emreg; + +struct Emreg { + Ureg* ur; + ulong (*fr)[3]; + FPenv* ufp; + ulong ir; + char* name; +}; + +int fpemudebug = 0; + +#undef OFR +#define OFR(X) ((ulong)&((Ureg*)0)->X) + +static int roff[] = { + OFR(r0), OFR(r1), OFR(r2), OFR(r3), + OFR(r4), OFR(r5), OFR(r6), OFR(r7), + OFR(r8), OFR(r9), OFR(r10), OFR(r11), + OFR(r12), OFR(r13), OFR(r14), OFR(r15), + OFR(r16), OFR(r17), OFR(r18), OFR(r19), + OFR(r20), OFR(r21), OFR(r22), OFR(r23), + OFR(r24), OFR(r25), OFR(r26), OFR(r27), + OFR(r28), OFR(r29), OFR(r30), OFR(r31), +}; + +/* + * initial FP register values assumed by qc's code + */ +static Internal fpreginit[] = { + /* s, e, l, h */ + {0, 0x400, 0x00000000, 0x08000000}, /* F31=2.0 */ + {0, 0x3FF, 0x00000000, 0x08000000}, /* F30=1.0 */ + {0, 0x3FE, 0x00000000, 0x08000000}, /* F29=0.5 */ + {0, 0x1, 0x00000000, 0x00000000}, /* F28=0.0 */ + {0, 0x433, 0x00000000, 0x08000040}, /* F27=FREGCVI */ +}; + +static void +fadd(Emreg *em, Internal *d, int ra, int rb) +{ + Internal a, b; + + a = FR(ra); + b = FR(rb); + (a.s == b.s? fpiadd: fpisub)(&b, &a, d); +} + +static void +fsub(Emreg *em, Internal *d, int ra, int rb) +{ + Internal a, b; + + a = FR(ra); + b = FR(rb); + b.s ^= 1; + (b.s == a.s? fpiadd: fpisub)(&b, &a, d); +} + +static void +fmul(Emreg *em, Internal *d, int ra, int rb) +{ + Internal a, b; + + a = FR(ra); + b = FR(rb); + fpimul(&b, &a, d); +} + +static void +fdiv(Emreg *em, Internal *d, int ra, int rb) +{ + Internal a, b; + + a = FR(ra); + b = FR(rb); + fpidiv(&b, &a, d); +} + +static void +fmsub(Emreg *em, Internal *d, int ra, int rc, int rb) +{ + Internal a, c, b, t; + + a = FR(ra); + c = FR(rc); + b = FR(rb); + fpimul(&a, &c, &t); + b.s ^= 1; + (b.s == t.s? fpiadd: fpisub)(&b, &t, d); +} + +static void +fmadd(Emreg *em, Internal *d, int ra, int rc, int rb) +{ + Internal a, c, b, t; + + a = FR(ra); + c = FR(rc); + b = FR(rb); + fpimul(&a, &c, &t); + (t.s == b.s? fpiadd: fpisub)(&b, &t, d); +} + +static ulong setfpscr(Emreg*); +static void setfpcc(Emreg*, int); + +static void +unimp(Emreg *em, ulong op) +{ + char buf[60]; + + snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", em->ur->pc, op); + if(fpemudebug) + print("FPE: %s\n", buf); + error(buf); + /* no return */ +} + +/* + * floating load/store + */ + +static void +fpeairr(Emreg *em, ulong ir, void **eap, int *rdp) +{ + ulong ea; + long imm; + int ra, rd, upd; + + getairr(ir); + ea = imm; + upd = (ir&(1L<<26))!=0; + if(ra) { + ea += REG(ra); + if(upd){ + if(ra == REGSP) + panic("fpemu: r1 update"); /* can't do it because we're running on the same stack */ + REG(ra) = ea; + } + } else { + if(upd) + unimp(em, ir); + } + *rdp = rd; + *eap = (void*)ea; + if(fpemudebug) + print("%8.8lux %s\tf%d,%ld(r%d) ea=%lux upd=%d\n", em->ur->pc, em->name, rd, imm, ra, ea, upd); +} + +static void +fpearrr(Emreg *em, ulong ir, int upd, void **eap, int *rdp) +{ + ulong ea; + int ra, rb, rd; + + getarrr(ir); + ea = REG(rb); + if(ra){ + ea += REG(ra); + if(upd){ + if(ra == REGSP) + panic("fpemu: r1 update"); + REG(ra) = ea; + } + if(fpemudebug) + print("%8.8lux %s\tf%d,(r%d+r%d) ea=%lux upd=%d\n", em->ur->pc, em->name, rd, ra, rb, ea, upd); + } else { + if(upd) + unimp(em, ir); + if(fpemudebug) + print("%8.8lux %s\tf%d,(r%d) ea=%lux\n", em->ur->pc, em->name, rd, rb, ea); + } + *eap = (void*)ea; + *rdp = rd; +} + +static void +lfs(Emreg *em, ulong ir) +{ + void *ea; + int rd; + + em->name = "lfs"; + fpeairr(em, ir, &ea, &rd); + fpis2i(&FR(rd), (void*)ea); +} + +static void +lfsx(Emreg *em, ulong ir) +{ + void *ea; + int rd; + + em->name = "lfsx"; + fpearrr(em, ir, ((ir>>1)&0x3FF)==567, &ea, &rd); + fpis2i(&FR(rd), (void*)ea); +} + +static void +lfd(Emreg *em, ulong ir) +{ + void *ea; + int rd; + + em->name = "lfd"; + fpeairr(em, ir, &ea, &rd); + fpid2i(&FR(rd), (void*)ea); +} + +static void +lfdx(Emreg *em, ulong ir) +{ + void *ea; + int rd; + + em->name = "lfdx"; + fpearrr(em, ir, ((ir>>1)&0x3FF)==631, &ea, &rd); + fpid2i(&FR(rd), (void*)ea); +} + +static void +stfs(Emreg *em, ulong ir) +{ + void *ea; + int rd; + Internal tmp; + + em->name = "stfs"; + fpeairr(em, ir, &ea, &rd); + tmp = FR(rd); + fpii2s(ea, &tmp); +} + +static void +stfsx(Emreg *em, ulong ir) +{ + void *ea; + int rd; + Internal tmp; + + em->name = "stfsx"; + fpearrr(em, ir, getxo(ir)==695, &ea, &rd); + tmp = FR(rd); + fpii2s(ea, &tmp); +} + +static void +stfd(Emreg *em, ulong ir) +{ + void *ea; + int rd; + Internal tmp; + + em->name = "stfd"; + fpeairr(em, ir, &ea, &rd); + tmp = FR(rd); + fpii2d(ea, &tmp); +} + +static void +stfdx(Emreg *em, ulong ir) +{ + void *ea; + int rd; + Internal tmp; + + em->name = "stfdx"; + fpearrr(em, ir, ((ir>>1)&0x3FF)==759, &ea, &rd); + tmp = FR(rd); + fpii2d(ea, &tmp); +} + +static void +mcrfs(Emreg *em, ulong ir) +{ + int rd, ra, rb; + static ulong fpscr0[] ={ + FPS_FX|FPS_OX, + FPS_UX|FPS_ZX|FPS_XX|FPS_VXSNAN, + FPS_VXISI|FPS_VXIDI|FPS_VXZDZ|FPS_VXIMZ, + FPS_VXVC, + 0, + FPS_VXCVI, + }; + + getarrr(ir); + if(rb || ra&3 || rd&3) + unimp(em, ir); + ra >>= 2; + rd >>= 2; + em->ur->cr = (em->ur->cr & ~mkCR(rd, 0xF)) | mkCR(rd, getCR(ra, em->ufp->fpscr)); + em->ufp->fpscr &= ~fpscr0[ra]; + if(fpemudebug) + print("%8.8lux mcrfs\tcrf%d,crf%d\n", em->ur->pc, rd, ra); +} + +static void +mffs(Emreg *em, ulong ir) +{ + int rd, ra, rb; + Double dw; + + getarrr(ir); + if(ra || rb) + unimp(em, ir); + dw.h = 0; + dw.l = ((uvlong)0xFFF8000L<<16)|em->ufp->fpscr; + fpid2i(&FR(rd), &dw); + /* it's anyone's guess how CR1 should be set when ir&1 */ + em->ur->cr &= ~mkCR(1, 0xE); /* leave SO, reset others */ + if(fpemudebug) + print("%8.8lux mffs%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd); +} + +static void +mtfsb1(Emreg *em, ulong ir) +{ + int rd, ra, rb; + + getarrr(ir); + if(ra || rb) + unimp(em, ir); + em->ufp->fpscr |= (1L << (31-rd)); + /* BUG: should set summary bits */ + if(ir & 1) + em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ + if(fpemudebug) + print("%8.8lux mtfsb1%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd); +} + +static void +mtfsb0(Emreg *em, ulong ir) +{ + int rd, ra, rb; + + getarrr(ir); + if(ra || rb) + unimp(em, ir); + em->ufp->fpscr &= ~(1L << (31-rd)); + if(ir & 1) + em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ + if(fpemudebug) + print("%8.8lux mtfsb0%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd); +} + +static void +mtfsf(Emreg *em, ulong ir) +{ + int fm, rb, i; + ulong v; + Internal b; + Double db; + + if(ir & ((1L << 25)|(1L << 16))) + unimp(em, ir); + rb = (ir >> 11) & 0x1F; + fm = (ir >> 17) & 0xFF; + b = FR(rb); + fpii2d(&db, &b); /* reconstruct hi/lo format to recover low word */ + v = db.l; + for(i=0; i<8; i++) + if(fm & (1 << (7-i))) + em->ufp->fpscr = (em->ufp->fpscr & ~mkCR(i, 0xF)) | mkCR(i, getCR(i, v)); + /* BUG: should set FEX and VX `according to the usual rule' */ + if(ir & 1) + em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ + if(fpemudebug) + print("%8.8lux mtfsf%s\t#%.2x,fr%d\n", em->ur->pc, ir&1?".":"", fm, rb); +} + +static void +mtfsfi(Emreg *em, ulong ir) +{ + int imm, rd; + + if(ir & ((0x7F << 16)|(1L << 11))) + unimp(em, ir); + rd = (ir >> 23) & 0xF; + imm = (ir >> 12) & 0xF; + em->ufp->fpscr = (em->ufp->fpscr & ~mkCR(rd, 0xF)) | mkCR(rd, imm); + /* BUG: should set FEX and VX `according to the usual rule' */ + if(ir & 1) + em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ + if(fpemudebug) + print("%8.8lux mtfsfi%s\tcrf%d,#%x\n", em->ur->pc, ir&1?".":"", rd, imm); +} + +static void +fcmp(Emreg *em, ulong ir) +{ + int fc, rd, ra, rb, sig, i; + + getarrr(ir); + if(rd & 3) + unimp(em, ir); + rd >>= 2; + sig = 0; + switch(getxo(ir)) { + default: + unimp(em, ir); + case 32: + if(fpemudebug) + print("%8.8lux fcmpo\tcr%d,f%d,f%d\n", em->ur->pc, rd, ra, rb); + sig = 1; + break; + case 0: + if(fpemudebug) + print("%8.8lux fcmpu\tcr%d,f%d,f%d\n", em->ur->pc, rd, ra, rb); + break; + } + if(IsWeird(&FR(ra)) || IsWeird(&FR(rb))) { + if(sig){ + ; /* BUG: should trap if not masked ... */ + } + fc = CRFU; + } else { + i = fpicmp(&FR(ra), &FR(rb)); + if(i > 0) + fc = CRGT; + else if(i == 0) + fc = CREQ; + else + fc = CRLT; + } + fc >>= 28; + em->ur->cr = (em->ur->cr & ~mkCR(rd,~0)) | mkCR(rd, fc); + em->ufp->fpscr = (em->ufp->fpscr & ~0xF800) | (fc<<11); + /* BUG: update FX, VXSNAN, VXVC */ +} + +static void +fariths(Emreg *em, ulong ir) +{ + int rd, ra, rb, rc, fmt; + char *cc, *n; + ulong fpscr; + Internal *d; + + fmt = 0; + rc = (ir>>6)&0x1F; + getarrr(ir); + d = &FR(rd); + switch(getxo(ir)&0x1F) { /* partial XO decode */ + case 22: /* fsqrts */ + case 24: /* fres */ + default: + unimp(em, ir); + return; + case 18: + if(IsZero(&FR(rb))) { + em->ufp->fpscr |= FPS_ZX | FPS_FX; + error("sys: fp: zero divide"); + } + fdiv(em, d, ra, rb); + n = "fdivs"; + break; + case 20: + fsub(em, d, ra, rb); + n = "fsubs"; + break; + case 21: + fadd(em, d, ra, rb); + n = "fadds"; + break; + case 25: + fmul(em, d, ra, rc); + rb = rc; + n = "fmuls"; + break; + case 28: + fmsub(em, d, ra, rc, rb); + fmt = 2; + n = "fmsubs"; + break; + case 29: + fmadd(em, d, ra, rc, rb); + fmt = 2; + n = "fmadds"; + break; + case 30: + fmsub(em, d, ra, rc, rb); + d->s ^= 1; + fmt = 2; + n = "fnmsubs"; + break; + case 31: + fmadd(em, d, ra, rc, rb); + d->s ^= 1; + fmt = 2; + n = "fnmadds"; + break; + } + if(fmt==1 && ra) + unimp(em, ir); + fpscr = setfpscr(em); + setfpcc(em, rd); + cc = ""; + if(ir & 1) { + cc = "."; + em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28)); + } + if(fpemudebug) { + switch(fmt) { + case 0: + print("%8.8lux %s%s\tfr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rb); + break; + case 1: + print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb); + break; + case 2: + print("%8.8lux %s%s\tfr%d,fr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rc, rb); + break; + } + } +} + +static void +farith(Emreg *em, ulong ir) +{ + Word w; + Double dv; + int rd, ra, rb, rc, fmt; + char *cc, *n; + ulong fpscr; + int nocc; + Internal *d; + + fmt = 0; + nocc = 0; + rc = (ir>>6)&0x1F; + getarrr(ir); + d = &FR(rd); + switch(getxo(ir)&0x1F) { /* partial XO decode */ + case 22: /* frsqrt */ + case 23: /* fsel */ + case 26: /* fsqrte */ + default: + unimp(em, ir); + return; + case 12: /* frsp */ + *d = FR(rb); /* BUG: doesn't round to single precision */ + fmt = 1; + n = "frsp"; + break; + case 14: /* fctiw */ /* BUG: ignores rounding mode */ + case 15: /* fctiwz */ + fpii2w(&w, &FR(rb)); + dv.h = 0; + dv.l = w; + fpid2i(d, &dv); + fmt = 1; + nocc = 1; + n = "fctiw"; + break; + case 18: + if(IsZero(&FR(rb))) { + em->ufp->fpscr |= FPS_ZX | FPS_FX; + error("sys: fp: zero divide"); + } + fdiv(em, d, ra, rb); + n = "fdiv"; + break; + case 20: + fsub(em, d, ra, rb); + n = "fsub"; + break; + case 21: + fadd(em, d, ra, rb); + n = "fadd"; + break; + case 25: + fmul(em, d, ra, rc); + rb = rc; + n = "fmul"; + break; + case 28: + fmsub(em, d, ra, rc, rb); + fmt = 2; + n = "fmsub"; + break; + case 29: + fmadd(em, d, ra, rc, rb); + fmt = 2; + n = "fmadd"; + break; + case 30: + fmsub(em, d, ra, rc, rb); + d->s ^= 1; + fmt = 2; + n = "fnmsub"; + break; + case 31: + fmadd(em, d, ra, rc, rb); + d->s ^= 1; + fmt = 2; + n = "fnmadd"; + break; + } + if(fmt==1 && ra) + unimp(em, ir); + fpscr = setfpscr(em); + if(nocc == 0) + setfpcc(em, rd); + cc = ""; + if(ir & 1) { + cc = "."; + em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28)); + } + if(fpemudebug) { + switch(fmt) { + case 0: + print("%8.8lux %s%s\tfr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rb); + break; + case 1: + print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb); + break; + case 2: + print("%8.8lux %s%s\tfr%d,fr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rc, rb); + break; + } + } +} + +static void +farith2(Emreg *em, ulong ir) +{ + int rd, ra, rb; + char *cc, *n; + ulong fpscr; + Internal *d, *b; + + getarrr(ir); + if(ra) + unimp(em, ir); + d = &FR(rd); + b = &FR(rb); + switch(getxo(ir)) { /* full XO decode */ + default: + unimp(em, ir); + case 40: + *d = *b; + d->s ^= 1; + n = "fneg"; + break; + case 72: + *d = *b; + n = "fmr"; + break; + case 136: + *d = *b; + d->s = 1; + n = "fnabs"; + break; + case 264: + *d = *b; + d->s = 0; + n = "fabs"; + break; + } + fpscr = setfpscr(em); + setfpcc(em, rd); + cc = ""; + if(ir & 1) { + cc = "."; + em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28)); + } + if(fpemudebug) + print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb); +} + +static ulong +setfpscr(Emreg *em) +{ + ulong fps, fpscr; + + fps = 0; /* BUG: getfsr() */ + fpscr = em->ufp->fpscr; + if(fps & FPAOVFL) + fpscr |= FPS_OX; + if(fps & FPAINEX) + fpscr |= FPS_XX; + if(fps & FPAUNFL) + fpscr |= FPS_UX; + if(fps & FPAZDIV) + fpscr |= FPS_ZX; + if(fpscr != em->ufp->fpscr) { + fpscr |= FPS_FX; + em->ufp->fpscr = fpscr; + } + return fpscr; +} + +static void +setfpcc(Emreg *em, int r) +{ + int c; + Internal *d; + + d = &FR(r); + c = 0; + if(IsZero(d)) + c |= 2; + else if(d->s == 1) + c |= 4; + else + c |= 8; + if(IsNaN(d)) + c |= 1; + em->ufp->fpscr = (em->ufp->fpscr & ~0xF800) | (0<<15) | (c<<11); /* unsure about class bit */ +} + +static uchar op63flag[32] = { +[12] 1, [14] 1, [15] 1, [18] 1, [20] 1, [21] 1, [22] 1, +[23] 1, [25] 1, [26] 1, [28] 1, [29] 1, [30] 1, [31] 1, +}; + +/* + * returns the number of FP instructions emulated + */ +int +fpipower(Ureg *ur) +{ + ulong op; + int xo; + Emreg emreg, *em; + FPenv *ufp; + int n; + + ufp = &up->env->fpu; /* because all the state is in Osenv, it need not be saved/restored */ + em = &emreg; + em->ur = ur; + em->fr = ufp->emreg; + em->ufp = ufp; + em->name = nil; + if(em->ufp->fpistate != FPACTIVE) { + em->ufp->fpistate = FPACTIVE; + em->ufp->fpscr = 0; /* TO DO */ + for(n = 0; n < nelem(fpreginit); n++) + FR(31-n) = fpreginit[n]; + } + for(n=0;;n++){ + op = getulong(ur->pc); + em->ir = op; + if(fpemudebug > 1) + print("%8.8lux %8.8lux: ", ur->pc, op); + switch(op>>26){ + default: + return n; + case 48: /* lfs */ + case 49: /* lfsu */ + lfs(em, op); + break; + case 50: /* lfd */ + case 51: /* lfdu */ + lfd(em, op); + break; + case 52: /* stfs */ + case 53: /* stfsu */ + stfs(em, op); + break; + case 54: /* stfd */ + case 55: /* stfdu */ + stfd(em, op); + break; + case 31: /* indexed load/store */ + xo = getxo(op); + if((xo & 0x300) != 0x200) + return n; + switch(xo){ + default: + return n; + case 535: /* lfsx */ + case 567: /* lfsux */ + lfsx(em, op); + break; + case 599: /* lfdx */ + case 631: /* lfdux */ + lfdx(em, op); + break; + case 663: /* stfsx */ + case 695: /* stfsux */ + stfsx(em, op); + break; + case 727: /* stfdx */ + case 759: /* stfdux */ + stfdx(em, op); + break; + } + break; + case 63: /* double precision */ + xo = getxo(op); + if(op63flag[xo & 0x1F]){ + farith(em, op); + break; + } + switch(xo){ + default: + return n; + case 0: /* fcmpu */ + case 32: /* fcmpo */ + fcmp(em, op); + break; + case 40: /* fneg */ + case 72: /* fmr */ + case 136: /* fnabs */ + case 264: /* fabs */ + farith2(em, op); + break; + case 38: + mtfsb1(em, op); + break; + case 64: + mcrfs(em, op); + break; + case 70: + mtfsb0(em, op); + break; + case 134: + mtfsfi(em, op); + break; + case 583: + mffs(em, op); + break; + case 711: + mtfsf(em, op); + break; + } + break; + case 59: /* single precision */ + fariths(em, op); + break; + } + ur->pc += 4; + if(anyhigher()) + sched(); + } + return n; +} + +/* +50: lfd frD,d(rA) +51: lfdu frD,d(rA) +31,631: lfdux frD,rA,rB +31,599: lfdx frD,rA,rB +48: lfs frD,d(rA) +49: lfsu frD,d(rA) +31,567: lfsux frD,rA,rB +31,535: lfsx frD,rA,rB + +54: stfd frS,d(rA) +55: stfdu frS,d(rA) +31,759: stfdux frS,rA,rB +31,727: stfdx frS,rA,rB +52: stfs frS,d(rA) +53: stfsu frS,d(rA) +31,695: stfsux frS,rA,rB +31,663: stfsx frS,rA,rB + +63,64: mcrfs crfD,crfS +63,583: mffs[.] frD +63,70: mtfsb0[.] crbD +63,38: mtfsb1[.] crbD +63,711: mtfsf[.] FM,frB +63,134: mtfsfi[.] crfD,IMM +*/ + +/* +float to int: + FMOVD g+0(SB),F1 + FCTIWZ F1,F4 + FMOVD F4,.rathole+0(SB) + MOVW .rathole+4(SB),R7 + MOVW R7,l+0(SB) +*/ + +/* +int to float: + MOVW $1127219200,R9 + MOVW l+0(SB),R7 + MOVW R9,.rathole+0(SB) + XOR $-2147483648,R7,R6 + MOVW R6,.rathole+4(SB) + FMOVD .rathole+0(SB),F0 + FSUB F27,F0 + +unsigned to float: + MOVW ul+0(SB),R5 + MOVW R9,.rathole+0(SB) + XOR $-2147483648,R5,R4 + MOVW R4,.rathole+4(SB) + FMOVD .rathole+0(SB),F3 + FSUB F27,F3 + FCMPU F3,F28 + BGE ,3(PC) + FMOVD $4.29496729600000000e+09,F2 + FADD F2,F3 + FMOVD F3,g+0(SB) +*/ diff --git a/os/mpc/i2c.c b/os/mpc/i2c.c new file mode 100644 index 00000000..5218b1c0 --- /dev/null +++ b/os/mpc/i2c.c @@ -0,0 +1,439 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* + * basic read/write interface to mpc8xx I2C bus (master mode) + */ + +typedef struct Ctlr Ctlr; +typedef struct I2C I2C; + +struct I2C { + uchar i2mod; + uchar rsv12a[3]; + uchar i2add; + uchar rsv12b[3]; + uchar i2brg; + uchar rsv12c[3]; + uchar i2com; + uchar rsv12d[3]; + uchar i2cer; + uchar rsv12e[3]; + uchar i2cmr; +}; + +enum { + /* i2c-specific BD flags */ + RxeOV= 1<<1, /* overrun */ + TxS= 1<<10, /* transmit start condition */ + TxeNAK= 1<<2, /* last transmitted byte not acknowledged */ + TxeUN= 1<<1, /* underflow */ + TxeCL= 1<<0, /* collision */ + TxERR= (TxeNAK|TxeUN|TxeCL), + + /* i2cmod */ + REVD= 1<<5, /* =1, LSB first */ + GCD= 1<<4, /* =1, general call address disabled */ + FLT= 1<<3, /* =0, not filtered; =1, filtered */ + PDIV= 3<<1, /* predivisor field */ + EN= 1<<0, /* enable */ + + /* i2com */ + STR= 1<<7, /* start transmit */ + I2CM= 1<<0, /* master */ + I2CS= 0<<0, /* slave */ + + /* i2cer */ + TXE = 1<<4, + BSY = 1<<2, + TXB = 1<<1, + RXB = 1<<0, + + /* port B bits */ + I2CSDA = IBIT(27), + I2CSCL = IBIT(26), + + Rbit = 1<<0, /* bit in address byte denoting read */ + + /* maximum I2C I/O (can change) */ + MaxIO = 128, + MaxSA = 2, /* longest subaddress */ + Bufsize = (MaxIO+MaxSA+1+4)&~3, /* extra space for subaddress/clock bytes and alignment */ + Freq = 100000, + I2CTimeout = 250, /* msec */ + + Chatty = 0, +}; + +#define DPRINT if(Chatty)print + +/* data cache needn't be flushed if buffers allocated in uncached PHYSIMM */ +#define DCFLUSH(a,n) + +/* + * I2C software structures + */ + +struct Ctlr { + Lock; + QLock io; + int init; + int busywait; /* running before system set up */ + I2C* i2c; + IOCparam* sp; + + BD* rd; + BD* td; + int phase; + Rendez r; + char* addr; + char* txbuf; + char* rxbuf; +}; + +static Ctlr i2ctlr[1]; + +static void interrupt(Ureg*, void*); + +static void +enable(void) +{ + I2C *i2c; + + i2c = i2ctlr->i2c; + i2c->i2cer = ~0; /* clear events */ + eieio(); + i2c->i2mod |= EN; + eieio(); + i2c->i2cmr = TXE|BSY|TXB|RXB; /* enable all interrupts */ + eieio(); +} + +static void +disable(void) +{ + I2C *i2c; + + i2c = i2ctlr->i2c; + i2c->i2cmr = 0; /* mask all interrupts */ + i2c->i2mod &= ~EN; +} + +/* + * called by the reset routine of any driver using the I2C + */ +void +i2csetup(int busywait) +{ + IMM *io; + I2C *i2c; + IOCparam *sp; + CPMdev *cpm; + Ctlr *ctlr; + long f, e, emin; + int p, d, dmax; + + ctlr = i2ctlr; + ctlr->busywait = busywait; + if(ctlr->init) + return; + print("i2c setup...\n"); + ctlr->init = 1; + cpm = cpmdev(CPi2c); + i2c = cpm->regs; + ctlr->i2c = i2c; + sp = cpm->param; + if(sp == nil) + panic("I2C: can't allocate new parameter memory\n"); + ctlr->sp = sp; + disable(); + + if(ctlr->txbuf == nil){ + ctlr->txbuf = cpmalloc(Bufsize, 2); + ctlr->addr = ctlr->txbuf+MaxIO; + } + if(ctlr->rxbuf == nil) + ctlr->rxbuf = cpmalloc(Bufsize, 2); + if(ctlr->rd == nil){ + ctlr->rd = bdalloc(1); + ctlr->rd->addr = PADDR(ctlr->rxbuf); + ctlr->rd->length = 0; + ctlr->rd->status = BDWrap; + } + if(ctlr->td == nil){ + ctlr->td = bdalloc(2); + ctlr->td->addr = PADDR(ctlr->txbuf); + ctlr->td->length = 0; + ctlr->td->status = BDWrap|BDLast; + } + + /* select port pins */ + io = ioplock(); + io->pbdir |= I2CSDA | I2CSCL; + io->pbodr |= I2CSDA | I2CSCL; + io->pbpar |= I2CSDA | I2CSCL; + iopunlock(); + + /* explicitly initialise parameters, because InitRxTx can't be used (see i2c/spi relocation errata) */ + sp = ctlr->sp; + sp->rbase = PADDR(ctlr->rd); + sp->tbase = PADDR(ctlr->td); + sp->rfcr = 0x18; + sp->tfcr = 0x18; + sp->mrblr = Bufsize; + sp->rstate = 0; + sp->rptr = 0; + sp->rbptr = sp->rbase; + sp->rcnt = 0; + sp->tstate = 0; + sp->tbptr = sp->tbase; + sp->tptr = 0; + sp->tcnt = 0; + eieio(); + + i2c->i2com = I2CM; + i2c->i2mod = 0; /* normal mode */ + i2c->i2add = 0; + + emin = Freq; + dmax = (m->cpuhz/Freq)/2-3; + for(d=0; d < dmax; d++){ + for(p=3; p>=0; p--){ + f = (m->cpuhz>>(p+2))/(2*(d+3)); + e = Freq - f; + if(e < 0) + e = -e; + if(e < emin){ + emin = e; + i2c->i2brg = d; + i2c->i2mod = (i2c->i2mod&~PDIV)|((3-p)<<1); /* set PDIV */ + } + } + } + //print("i2brg=%d i2mod=#%2.2ux\n", i2c->i2brg, i2c->i2mod); + intrenable(VectorCPIC+cpm->irq, interrupt, i2ctlr, BUSUNKNOWN, "i2c"); +} + +enum { + Idling, + Done, + Busy, + Sending, + Recving, +}; + +static void +interrupt(Ureg*, void *arg) +{ + int events; + Ctlr *ctlr; + I2C *i2c; + + ctlr = arg; + i2c = ctlr->i2c; + events = i2c->i2cer; + eieio(); + i2c->i2cer = events; + if(events & (BSY|TXE)){ + //print("I2C#%x\n", events); + if(ctlr->phase != Idling){ + ctlr->phase = Idling; + wakeup(&ctlr->r); + } + }else{ + if(events & TXB){ + //print("i2c: xmt %d %4.4ux %4.4ux\n", ctlr->phase, ctlr->td->status, ctlr->td[1].status); + if(ctlr->phase == Sending){ + ctlr->phase = Done; + wakeup(&ctlr->r); + } + } + if(events & RXB){ + //print("i2c: rcv %d %4.4ux %d\n", ctlr->phase, ctlr->rd->status, ctlr->rd->length); + if(ctlr->phase == Recving){ + ctlr->phase = Done; + wakeup(&ctlr->r); + } + } + } +} + +static int +done(void *a) +{ + return ((Ctlr*)a)->phase < Busy; +} + +static void +i2cwait(Ctlr *ctlr) +{ + int i; + + if(up == nil || ctlr->busywait){ + for(i=0; i < 5 && !done(ctlr); i++){ + delay(2); + interrupt(nil, ctlr); + } + }else + tsleep(&ctlr->r, done, ctlr, I2CTimeout); +} + +static int +i2cerror(char *s) +{ + if(up) + error(s); + /* no current process, don't call error */ + DPRINT("i2c error: %s\n", s); + return -1; +} + +long +i2csend(I2Cdev *d, void *buf, long n, ulong offset) +{ + Ctlr *ctlr; + int i, p, s; + + ctlr = i2ctlr; + if(up){ + if(n > MaxIO) + error(Etoobig); + qlock(&ctlr->io); + if(waserror()){ + qunlock(&ctlr->io); + nexterror(); + } + } + ctlr->txbuf[0] = d->addr<<1; + i = 1; + if(d->salen > 1) + ctlr->txbuf[i++] = offset>>8; + if(d->salen) + ctlr->txbuf[i++] = offset; + memmove(ctlr->txbuf+i, buf, n); + if(Chatty){ + print("tx: %8.8lux: ", PADDR(ctlr->txbuf)); + for(s=0; stxbuf[s]&0xFF); + print("\n"); + } + DCFLUSH(ctlr->txbuf, Bufsize); + ilock(ctlr); + ctlr->phase = Sending; + ctlr->rd->status = BDEmpty|BDWrap|BDInt; + ctlr->td->addr = PADDR(ctlr->txbuf); + ctlr->td->length = n+i; + ctlr->td->status = BDReady|BDWrap|BDLast|BDInt; + enable(); + ctlr->i2c->i2com = STR|I2CM; + eieio(); + iunlock(ctlr); + i2cwait(ctlr); + disable(); + p = ctlr->phase; + s = ctlr->td->status; + if(up){ + poperror(); + qunlock(&ctlr->io); + } + if(s & BDReady) + return i2cerror("timed out"); + if(s & TxERR){ + sprint(up->genbuf, "write error: status %.4ux", s); + return i2cerror(up->genbuf); + } + if(p != Done) + return i2cerror("phase error"); + return n; +} + +long +i2crecv(I2Cdev *d, void *buf, long n, ulong offset) +{ + Ctlr *ctlr; + int p, s, flag, i; + BD *td; + long nr; + + ctlr = i2ctlr; + if(up){ + if(n > MaxIO) + error(Etoobig); + qlock(&ctlr->io); + if(waserror()){ + qunlock(&ctlr->io); + nexterror(); + } + } + ctlr->txbuf[0] = (d->addr<<1)|Rbit; + if(d->salen){ /* special write to set address */ + ctlr->addr[0] = d->addr<<1; + i = 1; + if(d->salen > 1) + ctlr->addr[i++] = offset >> 8; + ctlr->addr[i] = offset; + } + DCFLUSH(ctlr->txbuf, Bufsize); + DCFLUSH(ctlr->rxbuf, Bufsize); + ilock(ctlr); + ctlr->phase = Recving; + ctlr->rd->addr = PADDR(ctlr->rxbuf); + ctlr->rd->status = BDEmpty|BDWrap|BDInt; + flag = 0; + td = ctlr->td; + td[1].status = 0; + if(d->salen){ + /* special select sequence */ + td->addr = PADDR(ctlr->addr); + i = d->salen+1; + if(i > 3) + i = 3; + td->length = i; + /* td->status made BDReady below */ + td++; + flag = TxS; + } + td->addr = PADDR(ctlr->txbuf); + td->length = n+1; + td->status = BDReady|BDWrap|BDLast | flag; /* not BDInt: leave that to receive */ + if(flag) + ctlr->td->status = BDReady; + enable(); + ctlr->i2c->i2com = STR|I2CM; + eieio(); + iunlock(ctlr); + i2cwait(ctlr); + disable(); + p = ctlr->phase; + s = ctlr->td->status; + if(flag) + s |= ctlr->td[1].status; + nr = ctlr->rd->length; + if(up){ + poperror(); + qunlock(&ctlr->io); + } + DPRINT("nr=%ld %4.4ux %8.8lux\n", nr, ctlr->rd->status, ctlr->rd->addr); + if(nr > n) + nr = n; /* shouldn't happen */ + if(s & TxERR){ + sprint(up->genbuf, "read: tx status: %.4ux", s); + return i2cerror(up->genbuf); + } + if(s & BDReady || ctlr->rd->status & BDEmpty) + return i2cerror("timed out"); + if(p != Done) + return i2cerror("phase error"); + memmove(buf, ctlr->rxbuf, nr); + if(Chatty){ + for(s=0; srxbuf[s]&0xFF); + print("\n"); + } + return nr; +} diff --git a/os/mpc/i2c_spi.srx b/os/mpc/i2c_spi.srx new file mode 100644 index 00000000..6587549d --- /dev/null +++ b/os/mpc/i2c_spi.srx @@ -0,0 +1,149 @@ +S00600004844521B +S309022020007FFFEFD96E +S309022020043FFD000074 +S309022020087FFB49F7F2 +S3090220200C7FF9000030 +S309022020105FEFADF7B2 +S309022020145F89ADF714 +S309022020185FEFAFF7A8 +S3090220201C5F89AFF70A +S309022020203A9CFBC8FB +S30902202024E7C0EDF00C +S3090220202877C1E1BBB8 +S3090220202CF4DC7F1D1C +S30902202030ABAD932F6A +S309022020344E08FDCF5E +S309022020386E0FAFF858 +S3090220203C7CCF76CFE8 +S30902202040FD1FF9CF90 +S30902202044ABF88DC67A +S30902202048AB5679F7FB +S3090220204CB09373832F +S30902202050DFCE79F747 +S30902202054B091E6BB7E +S30902202058E5BBE74F86 +S3090220205CB3FA6F0F2D +S309022020606FFB76CEA6 +S30902202064EE0DF9CF8D +S309022020682BFBEFEF48 +S3090220206CCFEEF9CFC3 +S3090220207076CEAD242F +S3090220207490B2DF9A85 +S309022020787FDDD0BF51 +S3090220207C4BF847FDB1 +S309022020807CCF76CEA5 +S30902202084CFEF7E1FD5 +S309022020887F1D7DFD16 +S3090220208CF0B6EF7122 +S309022020907FC177C1AC +S30902202094FBC8607984 +S30902202098E722FBC850 +S3090220209C5FFFDFFFDC +S309022020A05FB2FFFB09 +S309022020A4FBC8F3C892 +S309022020A894A67F0152 +S309022020AC7F1D5F39D4 +S309022020B0AFE85F5EB0 +S309022020B4FFDFDF96AD +S309022020B8CB9FAF7D66 +S309022020BC5FC1AFED3C +S309022020C08C1C5FC12C +S309022020C4AFDD5FC342 +S309022020C8DF9A7EFDF8 +S309022020CCB0B25FB275 +S309022020D0FFFEABAD8F +S309022020D45FB2FFFED2 +S309022020D85FCE600B44 +S309022020DCE6BB600BCC +S309022020E05FCEDFC602 +S309022020E427FBEFDFE0 +S309022020E85FC8CFDEF8 +S309022020EC3A9CE7C04B +S309022020F0EDF0F3C82C +S309022020F47F0154CD1F +S309022020F87F1D2D3DB6 +S309022020FC363A757063 +S309022021007E0AF1CE6C +S3090220210437EF2E68F3 +S309022021087FEE10EC42 +S3090220210CADF8EFDE35 +S30902202110CFEAE52FD6 +S309022021147D0FE12B07 +S30902202118F1CE5F6518 +S3090220211C7E0A4DF8CA +S30902202120CFEA5F7209 +S309022021247D0BEFEE2A +S30902202128CFEA5F74FF +S3090220212CE522EFDEB3 +S309022021305F74CFDA07 +S309022021340B6273851A +S30902202138DF627E0AB2 +S3090220213C30D8145B00 +S30902202140BFFFF3C8FA +S309022021445FFFDFFF33 +S30902202148A7F85F5E0F +S3090220214CBFFE7F7DAE +S3090220215010D314501C +S309022021545F36BFFF0C +S30902202158AF785F5E77 +S3090220215CBFFDA7F8FC +S309022021605F36BFFE01 +S3090220216477FD30C0EB +S309022021684E08FDCF29 +S3090220216CE5FF6E0FE6 +S30902202170AFF87E1FFF +S309022021747E0FFD1F96 +S30902202178F1CF5F1B01 +S3090220217CABF80D5E29 +S309022021805F5EFFEF88 +S3090220218479F730A2ED +S30902202188AFDD5F340C +S3090220218C47F85F3455 +S30902202190AFED7FDD2B +S3090220219450B249785C +S3090220219847FD7F1D3B +S3090220219C7DFD70AD80 +S309022021A0EF717EC174 +S309022021A46BA47F0180 +S309022021A82D267EFD3D +S309022021AC30DE5F5E3C +S309022021B0FFFD5F5E4A +S309022021B4FFEF5F5E54 +S309022021B8FFDF0CA071 +S309022021BCAFED0A9EB3 +S309022021C0AFDD0C3A21 +S309022021C45F3AAFBDEA +S309022021C87FBDB0827D +S309022021CC5F8247F8C7 +S9030000FC +S00600004844521B +S30902202F003E303430D3 +S30902202F0434343737CB +S30902202F08ABF7BF9BA1 +S30902202F0C994B4FBDA9 +S30902202F10BD59949358 +S30902202F14349FFF3788 +S30902202F18FB9B177D63 +S30902202F1CD99369565E +S30902202F20BBFDD69760 +S30902202F24BDD2FD11E4 +S30902202F2831DB9BB323 +S30902202F2C6313963736 +S30902202F3093733693A6 +S30902202F34193137F7F9 +S30902202F38331737AF3D +S30902202F3C7BB9B999E3 +S30902202F40BB197957C1 +S30902202F447FDFD3D55B +S30902202F4873B773F7C9 +S30902202F4C37933B99BB +S30902202F501D115316BE +S30902202F54993153151F +S30902202F5831694BF474 +S30902202F5CFBDBD35947 +S30902202F603149735305 +S30902202F6476956D6960 +S30902202F687B9D9693FC +S30902202F6C1313197981 +S30902202F7079376935E7 +S9030000FC \ No newline at end of file diff --git a/os/mpc/inb.s b/os/mpc/inb.s new file mode 100644 index 00000000..4dcd7fe4 --- /dev/null +++ b/os/mpc/inb.s @@ -0,0 +1,120 @@ +#include "mem.h" + +#define BDNZ BC 16,0, + +TEXT inb(SB), $0 + OR $ISAIO, R3 + EIEIO + MOVBZ (R3), R3 + RETURN + +TEXT insb(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $1, R4 +insb1: + EIEIO + MOVBZ (R3), R7 + MOVBU R7, 1(R4) + BDNZ insb1 + RETURN + +TEXT outb(SB), $0 + MOVW v+4(FP), R4 + OR $ISAIO, R3 + EIEIO + MOVB R4, (R3) + RETURN + +TEXT outsb(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $1, R4 +outsb1: + EIEIO + MOVBZU 1(R4), R7 + MOVB R7, (R3) + BDNZ outsb1 + RETURN + +TEXT ins(SB), $0 + OR $ISAIO, R3 + EIEIO + MOVHBR (R3), R3 + RETURN + +TEXT inss(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $2, R4 +inss1: + EIEIO + MOVHZ (R3), R7 + MOVHU R7, 2(R4) + BDNZ inss1 + RETURN + +TEXT outs(SB), $0 + MOVW v+4(FP), R4 + OR $ISAIO, R3 + EIEIO + MOVHBR R4, (R3) + RETURN + +TEXT outss(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $2, R4 +outss1: + EIEIO + MOVHZU 2(R4), R7 + MOVH R7, (R3) + BDNZ outss1 + RETURN + +TEXT inl(SB), $0 + OR $ISAIO, R3 + EIEIO + MOVWBR (R3), R3 + RETURN + +TEXT insl(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $4, R4 +insl1: + EIEIO + MOVW (R3), R7 + MOVWU R7, 4(R4) + BDNZ insl1 + RETURN + +TEXT outl(SB), $0 + MOVW v+4(FP), R4 + OR $ISAIO, R3 + EIEIO + MOVWBR R4, (R3) + RETURN + +TEXT outsl(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $4, R4 +outsl1: + EIEIO + MOVWU 4(R4), R7 + MOVW R7, (R3) + BDNZ outsl1 + RETURN diff --git a/os/mpc/kbd.c b/os/mpc/kbd.c new file mode 100644 index 00000000..81e2384c --- /dev/null +++ b/os/mpc/kbd.c @@ -0,0 +1,20 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* + * initialise the keyboard Queue if uartinstall hasn't already done so + */ +void +kbdinit(void) +{ + if(kbdq == nil){ + kbdq = qopen(4*1024, 0, 0, 0); + qnoblock(kbdq, 1); + } + archkbdinit(); +} diff --git a/os/mpc/l.s b/os/mpc/l.s new file mode 100644 index 00000000..8a2ae66d --- /dev/null +++ b/os/mpc/l.s @@ -0,0 +1,685 @@ +#include "mem.h" + +#define MB (1024*1024) + +/* + * options + */ +#undef MMUTWC /* we don't map enough memory to need table walk */ +#undef SHOWCYCLE /* might be needed for BDM debugger to keep control */ + +/* + * common ppc special purpose registers + */ +#define DSISR 18 +#define DAR 19 /* Data Address Register */ +#define DEC 22 /* Decrementer */ +#define SRR0 26 /* Saved Registers (exception) */ +#define SRR1 27 +#define SPRG0 272 /* Supervisor Private Registers */ +#define SPRG1 273 +#define SPRG2 274 +#define SPRG3 275 +#define TBRU 269 /* Time base Upper/Lower (Reading) */ +#define TBRL 268 +#define TBWU 285 /* Time base Upper/Lower (Writing) */ +#define TBWL 284 +#define PVR 287 /* Processor Version */ + +/* + * mpc8xx-specific special purpose registers of interest here + */ +#define EIE 80 +#define EID 81 +#define NRI 82 +#define IMMR 638 +#define IC_CSR 560 +#define IC_ADR 561 +#define IC_DAT 562 +#define DC_CSR 568 +#define DC_ADR 569 +#define DC_DAT 570 +#define MI_CTR 784 +#define MI_AP 786 +#define MI_EPN 787 +#define MI_TWC 789 +#define MI_RPN 790 +#define MI_DBCAM 816 +#define MI_DBRAM0 817 +#define MI_DBRAM1 818 +#define MD_CTR 792 +#define M_CASID 793 +#define MD_AP 794 +#define MD_EPN 795 +#define M_TWB 796 +#define MD_TWC 797 +#define MD_RPN 798 +#define M_TW 799 +#define MD_DBCAM 824 +#define MD_DBRAM0 825 +#define MD_DBRAM1 826 + +/* use of SPRG registers in save/restore */ +#define SAVER0 SPRG0 +#define SAVER1 SPRG1 +#define SAVELR SPRG2 +#define SAVEXX SPRG3 + +/* special instruction definitions */ +#define BDNZ BC 16,0, +#define BDNE BC 0,2, +#define TLBIA WORD $((31<<26)|(370<<1)) +#define MFTB(tbr,d) WORD $((31<<26)|((d)<<21)|((tbr&0x1f)<<16)|(((tbr>>5)&0x1f)<<11)|(371<<1)) + +/* on some models mtmsr doesn't synchronise enough (eg, 603e) */ +#define MSRSYNC SYNC; ISYNC + +#define UREGSPACE (UREGSIZE+8) + +/* could define STEP to set an LED to mark progress */ +#define STEP(x) + +/* + * Boot first processor + */ + TEXT start(SB), $-4 + + MOVW MSR, R3 + RLWNM $0, R3, $~EE, R3 + RLWNM $0, R3, $~FPE, R3 + OR $ME, R3 + ISYNC + MOVW R3, MSR /* turn off interrupts but enable traps */ + MSRSYNC + MOVW $0, R0 /* except during trap handling, R0 is zero from now on */ + MOVW R0, CR + MOVW $setSB(SB), R2 + +/* + * reset the caches and disable them for now + */ + MOVW SPR(IC_CSR), R4 /* read and clear */ + MOVW $(5<<25), R4 + MOVW R4, SPR(IC_CSR) /* unlock all */ + ISYNC + MOVW $(6<<25), R4 + MOVW R4, SPR(IC_CSR) /* invalidate all */ + ISYNC + MOVW $(2<<25), R4 + MOVW R4, SPR(IC_CSR) /* disable i-cache */ + ISYNC + + SYNC + MOVW SPR(DC_CSR), R4 /* read and clear */ + MOVW $(10<<24), R4 + SYNC + MOVW R4, SPR(DC_CSR) /* unlock all */ + ISYNC + MOVW $(12<<24), R4 + SYNC + MOVW R4, SPR(DC_CSR) /* invalidate all */ + ISYNC + MOVW $(4<<24), R4 + SYNC + MOVW R4, SPR(DC_CSR) /* disable d-cache */ + ISYNC + +#ifdef SHOWCYCLE + MOVW $0, R4 +#else + MOVW $7, R4 +#endif + MOVW R4, SPR(158) /* cancel `show cycle' for normal instruction execution */ + ISYNC + +/* + * set other system configuration values + */ + MOVW $PHYSIMM, R4 + MOVW R4, SPR(IMMR) /* set internal memory base */ + +STEP(1) + + BL kernelmmu(SB) + +STEP(2) + /* no kfpinit on 82x */ + + MOVW $mach0(SB), R(MACH) + ADD $(MACHSIZE-8), R(MACH), R1 + SUB $4, R(MACH), R3 + ADD $4, R1, R4 +clrmach: + MOVWU R0, 4(R3) + CMP R3, R4 + BNE clrmach + + MOVW R0, R(USER) + MOVW R0, 0(R(MACH)) + + MOVW $edata(SB), R3 + MOVW $end(SB), R4 + ADD $4, R4 + SUB $4, R3 +clrbss: + MOVWU R0, 4(R3) + CMP R3, R4 + BNE clrbss + +STEP(3) + BL main(SB) + BR 0(PC) + +TEXT kernelmmu(SB), $0 + TLBIA + ISYNC + + MOVW $0, R4 + MOVW R4, SPR(M_CASID) /* set supervisor space */ + MOVW $(0<<29), R4 /* allow i-cache when IR=0 */ + MOVW R4, SPR(MI_CTR) /* i-mmu control */ + ISYNC + MOVW $((1<<29)|(1<<28)), R4 /* cache inhibit when DR=0, write-through */ + SYNC + MOVW R4, SPR(MD_CTR) /* d-mmu control */ + ISYNC + TLBIA + + /* map various things 1:1 */ + MOVW $tlbtab-KZERO(SB), R4 + MOVW $tlbtabe-KZERO(SB), R5 + SUB R4, R5 + MOVW $(3*4), R6 + DIVW R6, R5 + SUB $4, R4 + MOVW R5, CTR +ltlb: + MOVWU 4(R4), R5 + MOVW R5, SPR(MD_EPN) + MOVW R5, SPR(MI_EPN) + MOVWU 4(R4), R5 + MOVW R5, SPR(MI_TWC) + MOVW R5, SPR(MD_TWC) + MOVWU 4(R4), R5 + MOVW R5, SPR(MD_RPN) + MOVW R5, SPR(MI_RPN) + BDNZ ltlb + + MOVW $(1<<25), R4 + MOVW R4, SPR(IC_CSR) /* enable i-cache */ + ISYNC + + MOVW $(3<<24), R4 + SYNC + MOVW R4, SPR(DC_CSR) /* clear force write through mode */ + MOVW $(2<<24), R4 + SYNC + MOVW R4, SPR(DC_CSR) /* enable d-cache */ + ISYNC + + /* enable MMU and set kernel PC to virtual space */ + MOVW $((0<<29)|(0<<28)), R4 /* cache when DR=0, write back */ + SYNC + MOVW R4, SPR(MD_CTR) /* d-mmu control */ + MOVW LR, R3 + OR $KZERO, R3 + MOVW R3, SPR(SRR0) + MOVW MSR, R4 + OR $(ME|IR|DR), R4 /* had ME|FPE|FE0|FE1 */ + MOVW R4, SPR(SRR1) + RFI /* resume in kernel mode in caller */ + +TEXT splhi(SB), $0 + MOVW MSR, R3 + RLWNM $0, R3, $~EE, R4 + SYNC + MOVW R4, MSR + MSRSYNC + MOVW LR, R31 + MOVW R31, 4(R(MACH)) /* save PC in m->splpc */ + RETURN + +TEXT splx(SB), $0 + MOVW MSR, R4 + RLWMI $0, R3, $EE, R4 + RLWNMCC $0, R3, $EE, R5 + BNE splx0 + MOVW LR, R31 + MOVW R31, 4(R(MACH)) /* save PC in m->splpc */ +splx0: + SYNC + MOVW R4, MSR + MSRSYNC + RETURN + +TEXT splxpc(SB), $0 + MOVW MSR, R4 + RLWMI $0, R3, $EE, R4 + RLWNMCC $0, R3, $EE, R5 + SYNC + MOVW R4, MSR + MSRSYNC + RETURN + +TEXT spllo(SB), $0 + MFTB(TBRL, 3) + MOVW R3, spltbl(SB) + MOVW MSR, R3 + OR $EE, R3, R4 + SYNC + MOVW R4, MSR + MSRSYNC + RETURN + +TEXT spldone(SB), $0 + RETURN + +TEXT islo(SB), $0 + MOVW MSR, R3 + RLWNM $0, R3, $EE, R3 + RETURN + +TEXT setlabel(SB), $-4 + MOVW LR, R31 + MOVW R1, 0(R3) + MOVW R31, 4(R3) + MOVW $0, R3 + RETURN + +TEXT gotolabel(SB), $-4 + MOVW 4(R3), R31 + MOVW R31, LR + MOVW 0(R3), R1 + MOVW $1, R3 + RETURN + +/* + * enter with stack set and mapped. + * on return, SB (R2) has been set, and R3 has the Ureg*, + * the MMU has been re-enabled, kernel text and PC are in KSEG, + * R(MACH) has been set, and R0 contains 0. + * + * this can be simplified in the Inferno regime + */ +TEXT saveureg(SB), $-4 +/* + * save state + */ + MOVMW R2, 48(R1) /* r2:r31 */ + MOVW $setSB(SB), R2 + MOVW SPR(SAVER1), R4 + MOVW R4, 44(R1) + MOVW SPR(SAVER0), R5 + MOVW R5, 40(R1) + MOVW CTR, R6 + MOVW R6, 36(R1) + MOVW XER, R4 + MOVW R4, 32(R1) + MOVW CR, R5 + MOVW R5, 28(R1) + MOVW SPR(SAVELR), R6 /* LR */ + MOVW R6, 24(R1) + /* pad at 20(R1) */ + /* old PC(16) and status(12) saved earlier */ + MOVW SPR(SAVEXX), R0 + MOVW R0, 8(R1) /* cause/vector */ + ADD $8, R1, R3 /* Ureg* */ + STWCCC R3, (R1) /* break any pending reservations */ + MOVW $0, R0 /* compiler/linker expect R0 to be zero */ + + MOVW MSR, R5 + OR $(IR|DR), R5 /* enable MMU */ + MOVW R5, SPR(SRR1) + MOVW LR, R31 + OR $KZERO, R31 /* return PC in KSEG0 */ + MOVW R31, SPR(SRR0) + SYNC + ISYNC + RFI /* returns to trap handler */ + +TEXT icflush(SB), $-4 /* icflush(virtaddr, count) */ + MOVW n+4(FP), R4 + RLWNM $0, R3, $~(CACHELINESZ-1), R5 + SUB R5, R3 + ADD R3, R4 + ADD $(CACHELINESZ-1), R4 + SRAW $CACHELINELOG, R4 + MOVW R4, CTR +icf0: ICBI (R5) + ADD $CACHELINESZ, R5 + BDNZ icf0 + ISYNC + RETURN + +/* + * flush to store and invalidate globally + */ +TEXT dcflush(SB), $-4 /* dcflush(virtaddr, count) */ + SYNC + MOVW n+4(FP), R4 + RLWNM $0, R3, $~(CACHELINESZ-1), R5 + CMP R4, $0 + BLE dcf1 + SUB R5, R3 + ADD R3, R4 + ADD $(CACHELINESZ-1), R4 + SRAW $CACHELINELOG, R4 + MOVW R4, CTR +dcf0: DCBF (R5) + ADD $CACHELINESZ, R5 + BDNZ dcf0 + SYNC + ISYNC +dcf1: + RETURN + +/* + * invalidate without flush, globally + */ +TEXT dcinval(SB), $-4 /* dcinval(virtaddr, count) */ + SYNC + MOVW n+4(FP), R4 + RLWNM $0, R3, $~(CACHELINESZ-1), R5 + CMP R4, $0 + BLE dci1 + SUB R5, R3 + ADD R3, R4 + ADD $(CACHELINESZ-1), R4 + SRAW $CACHELINELOG, R4 + MOVW R4, CTR +dci0: DCBI (R5) + ADD $CACHELINESZ, R5 + BDNZ dci0 + SYNC + ISYNC +dci1: + RETURN + +TEXT _tas(SB), $0 + SYNC + MOVW R3, R4 + MOVW $0xdeaddead,R5 +tas1: + DCBF (R4) /* fix for 603x bug */ + LWAR (R4), R3 + CMP R3, $0 + BNE tas0 + STWCCC R5, (R4) + BNE tas1 +tas0: + SYNC + ISYNC + RETURN + +TEXT gettbl(SB), $0 + MFTB(TBRL, 3) + RETURN + +TEXT gettbu(SB), $0 + MFTB(TBRU, 3) + RETURN + +TEXT getpvr(SB), $0 + MOVW SPR(PVR), R3 + RETURN + +TEXT getimmr(SB), $0 + MOVW SPR(IMMR), R3 + RETURN + +TEXT getdec(SB), $0 + MOVW SPR(DEC), R3 + RETURN + +TEXT putdec(SB), $0 + MOVW R3, SPR(DEC) + RETURN + +TEXT getcallerpc(SB), $-4 + MOVW 0(R1), R3 + RETURN + +TEXT getdar(SB), $0 + MOVW SPR(DAR), R3 + RETURN + +TEXT getdsisr(SB), $0 + MOVW SPR(DSISR), R3 + RETURN + +TEXT getdepn(SB), $0 + MOVW SPR(MD_EPN), R3 + RETURN + +TEXT getmsr(SB), $0 + MOVW MSR, R3 + RETURN + +TEXT putmsr(SB), $0 + SYNC + MOVW R3, MSR + MSRSYNC + RETURN + +TEXT eieio(SB), $0 + EIEIO + RETURN + +TEXT gotopc(SB), $0 + MOVW R3, CTR + MOVW LR, R31 /* for trace back */ + BR (CTR) + +TEXT firmware(SB), $0 + MOVW MSR, R3 + MOVW $(EE|ME), R4 + ANDN R4, R3 + OR $(MSR_IP), R3 + ISYNC + MOVW R3, MSR /* turn off interrupts and machine checks */ + MSRSYNC + MOVW $(RI|IR|DR|ME), R4 + ANDN R4, R3 + MOVW R3, SPR(SRR1) + MOVW $(0xFF00<<16), R4 + MOVW R4, SPR(IMMR) + MOVW $(0x0800<<16), R4 + MOVW R4, SPR(SRR0) /* force bad address */ + MOVW R0, SPR(149) /* ensure checkstop on machine check */ + MOVW R4, R1 + MOVW R4, R2 + EIEIO + ISYNC + RFI + +/* + * byte swapping of arrays of long and short; + * could possibly be avoided with more changes to drivers + */ +TEXT swabl(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + SRAW $2, R5, R5 + MOVW R5, CTR + SUB $4, R4 + SUB $4, R3 +swabl1: + ADD $4, R3 + MOVWU 4(R4), R7 + MOVWBR R7, (R3) + BDNZ swabl1 + RETURN + +TEXT swabs(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + SRAW $1, R5, R5 + MOVW R5, CTR + SUB $2, R4 + SUB $2, R3 +swabs1: + ADD $2, R3 + MOVHZU 2(R4), R7 + MOVHBR R7, (R3) + BDNZ swabs1 + RETURN + +TEXT legetl(SB), $0 + MOVWBR (R3), R3 + RETURN + +TEXT lesetl(SB), $0 + MOVW v+4(FP), R4 + MOVWBR R4, (R3) + RETURN + +TEXT legets(SB), $0 + MOVHBR (R3), R3 + RETURN + +TEXT lesets(SB), $0 + MOVW v+4(FP), R4 + MOVHBR R4, (R3) + RETURN + +#ifdef MMUTWC +/* + * ITLB miss + * avoid references that might need the right SB value; + * IR and DR are off. + */ +TEXT itlbmiss(SB), $-4 + MOVW R1, SPR(M_TW) + MOVW SPR(SRR0), R1 /* instruction miss address */ + MOVW R1, SPR(MD_EPN) + MOVW SPR(M_TWB), R1 /* level one pointer */ + MOVW (R1), R1 + MOVW R1, SPR(MI_TWC) /* save level one attributes */ + MOVW R1, SPR(MD_TWC) /* save base and attributes */ + MOVW SPR(MD_TWC), R1 /* level two pointer */ + MOVW (R1), R1 /* level two entry */ + MOVW R1, SPR(MI_RPN) /* write TLB */ + MOVW SPR(M_TW), R1 + RFI + +/* + * DTLB miss + * avoid references that might need the right SB value; + * IR and DR are off. + */ +TEXT dtlbmiss(SB), $-4 + MOVW R1, SPR(M_TW) + MOVW SPR(M_TWB), R1 /* level one pointer */ + MOVW (R1), R1 /* level one entry */ + MOVW R1, SPR(MD_TWC) /* save base and attributes */ + MOVW SPR(MD_TWC), R1 /* level two pointer */ + MOVW (R1), R1 /* level two entry */ + MOVW R1, SPR(MD_RPN) /* write TLB */ + MOVW SPR(M_TW), R1 + RFI +#else +TEXT itlbmiss(SB), $-4 + BR traps +TEXT dtlbmiss(SB), $-4 + BR traps +#endif + +/* + * traps force memory mapping off. + * this code goes to too much effort (for the Inferno environment) to restore it. + */ +TEXT trapvec(SB), $-4 +traps: + MOVW LR, R0 + +pagefault: + +/* + * map data virtually and make space to save + */ + MOVW R0, SPR(SAVEXX) /* vector */ + MOVW R1, SPR(SAVER1) + SYNC + ISYNC + MOVW MSR, R0 + OR $(DR|ME), R0 /* make data space usable */ + SYNC + MOVW R0, MSR + MSRSYNC + SUB $UREGSPACE, R1 + + MOVW SPR(SRR0), R0 /* save SRR0/SRR1 now, since DLTB might be missing stack page */ + MOVW R0, LR + MOVW SPR(SRR1), R0 + MOVW R0, 12(R1) /* save status: could take DLTB miss here */ + MOVW LR, R0 + MOVW R0, 16(R1) /* old PC */ + BL saveureg(SB) + BL trap(SB) + BR restoreureg + +TEXT intrvec(SB), $-4 + MOVW LR, R0 + +/* + * map data virtually and make space to save + */ + MOVW R0, SPR(SAVEXX) /* vector */ + MOVW R1, SPR(SAVER1) + SYNC + ISYNC + MOVW MSR, R0 + OR $DR, R0 /* make data space usable */ + SYNC + MOVW R0, MSR + MSRSYNC + SUB $UREGSPACE, R1 + + MFTB(TBRL, 0) + MOVW R0, intrtbl(SB) + + MOVW SPR(SRR0), R0 + MOVW R0, LR + MOVW SPR(SRR1), R0 + MOVW R0, 12(R1) + MOVW LR, R0 + MOVW R0, 16(R1) + BL saveureg(SB) + + MFTB(TBRL, 5) + MOVW R5, isavetbl(SB) + + BL intr(SB) + +/* + * restore state from Ureg and return from trap/interrupt + */ +restoreureg: + MOVMW 48(R1), R2 /* r2:r31 */ + /* defer R1 */ + MOVW 40(R1), R0 + MOVW R0, SPR(SAVER0) + MOVW 36(R1), R0 + MOVW R0, CTR + MOVW 32(R1), R0 + MOVW R0, XER + MOVW 28(R1), R0 + MOVW R0, CR /* CR */ + MOVW 24(R1), R0 + MOVW R0, SPR(SAVELR) /* LR */ + /* pad, skip */ + MOVW 16(R1), R0 + MOVW R0, SPR(SRR0) /* old PC */ + MOVW 12(R1), R0 + MOVW R0, SPR(SRR1) /* old MSR */ + /* cause, skip */ + MOVW 44(R1), R1 /* old SP */ + MOVW SPR(SAVELR), R0 + MOVW R0, LR + MOVW SPR(SAVER0), R0 + RFI + +GLOBL mach0+0(SB), $MACHSIZE +GLOBL spltbl+0(SB), $4 +GLOBL intrtbl+0(SB), $4 +GLOBL isavetbl+0(SB), $4 diff --git a/os/mpc/nofp.s b/os/mpc/nofp.s new file mode 100644 index 00000000..f23d49b0 --- /dev/null +++ b/os/mpc/nofp.s @@ -0,0 +1,31 @@ +/* + * stubs when no floating-point hardware + */ + +TEXT kfpinit(SB), $0 + RETURN + +TEXT getfpscr(SB), $8 + MOVW $0, R3 + RETURN + +TEXT fpsave(SB), $0 + RETURN + +TEXT fprestore(SB), $0 + RETURN + +TEXT clrfptrap(SB), $0 + RETURN + +TEXT fpinit(SB), $0 + RETURN + +TEXT fpoff(SB), $0 + RETURN + +TEXT FPsave(SB), 1, $0 + RETURN + +TEXT FPrestore(SB), 1, $0 + RETURN diff --git a/os/mpc/pcmcia.h b/os/mpc/pcmcia.h new file mode 100644 index 00000000..2e22042f --- /dev/null +++ b/os/mpc/pcmcia.h @@ -0,0 +1,19 @@ +/* + * PCMCIA support code. + */ +int inb(int); +int inb(int); +ulong inl(int); +ushort ins(int); +void insb(int, void*, int); +void insl(int, void*, int); +void inss(int, void*, int); +void outb(int, int); +void outl(int, ulong); +void outs(int, ushort); +void outsb(int, void*, int); +void outsl(int, void*, int); +void outss(int, void*, int); +void pcmintrenable(int, void(*)(Ureg*,void*), void*); +int pcmspecial(char*, ISAConf*); +void pcmspecialclose(int); diff --git a/os/mpc/pit.c b/os/mpc/pit.c new file mode 100644 index 00000000..bfc8bc8d --- /dev/null +++ b/os/mpc/pit.c @@ -0,0 +1,68 @@ +/* + * programmable interrupt timer + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +enum { + /* piscr */ + PTE = 1<<0, + PITF = 1<<1, + PIE = 1<<2, + PS = 1<<7, +}; + +static void +pitinterrupt(Ureg*, void*) +{ + IMM *io; + + io = m->iomem; + if(io->piscr & PS){ + io->piscr |= PS; /* clear by writing 1 */ + /* do whatever is required */ + } +} + +static void +pitreset(void) +{ + IMM *io; + + io = ioplock(); + io->piscrk = KEEP_ALIVE_KEY; + io->piscr = (PITlevel<<8) | PS | PITF; + if(0) + io->piscrk = ~KEEP_ALIVE_KEY; + /* piscrk is left unlocked for interrupt routine */ + iopunlock(); + intrenable(PITlevel, pitinterrupt, nil, BUSUNKNOWN, "pit"); +} + +static ulong +pitload(ulong usec) +{ + IMM *io; + ulong v; + + v = ((usec*m->oscclk)/512); + if(v == 0 || v >= (1<<16)) + return 0; /* can't do */ + io = ioplock(); + io->pitck = KEEP_ALIVE_KEY; + io->pitc = (v-1)<<16; + io->pitck = ~KEEP_ALIVE_KEY; + io->piscrk = KEEP_ALIVE_KEY; + io->piscr = (PITlevel<<8) | PS | PIE | PITF | PTE; + if(0) + io->piscrk = ~KEEP_ALIVE_KEY; + /* piscrk is left unlocked for interrupt routine */ + iopunlock(); + return (v*512)/m->oscclk; +} diff --git a/os/mpc/powerbreak.c b/os/mpc/powerbreak.c new file mode 100644 index 00000000..fc5970ef --- /dev/null +++ b/os/mpc/powerbreak.c @@ -0,0 +1,123 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "ureg.h" + +extern int (*breakhandler)(Ureg *ur, Proc*); /* trap.c */ +extern Instr BREAK; /* trap.c */ +extern void portbreakinit(void); + +#define getop(i) ((i>>26)&0x3F) +#define getxo(i) ((i>>1)&0x3FF) +#define getbobi(i) bo = (i>>21)&0x1f; bi = (i>>16)&0x1f; xx = (i>>11)&0x1f; + +void +machbreakinit(void) +{ + portbreakinit(); + breakhandler = breakhit; +} + +Instr +machinstr(ulong addr) +{ + if (addr < KTZERO) + error(Ebadarg); + return *(Instr*)addr; +} + +void +machbreakset(ulong addr) +{ + if (addr < KTZERO) + error(Ebadarg); + *(Instr*)addr = BREAK; + segflush((void*)addr, sizeof(Instr)); +} + +void +machbreakclear(ulong addr, Instr i) +{ + if (addr < KTZERO) + error(Ebadarg); + *(Instr*)addr = i; + segflush((void*)addr, sizeof(Instr)); +} + +static int +condok(Ureg *ur, ulong ir, int ctrok) +{ + int bo, bi, xx; + ulong ctrval; + + ctrval = ur->ctr; + getbobi(ir); + if(xx) + return 0; /* illegal */ + if((bo & 0x4) == 0) { + if(!ctrok) + return 0; /* illegal */ + ctrval--; + } + if(bo & 0x4 || (ctrval!=0)^((bo>>1)&1)) { + if(bo & 0x10 || (((ur->cr & (1L<<(31-bi))!=0)==((bo>>3)&1)))) + return 1; + } + return 0; +} + +/* + * Return the address of the instruction that will be executed after the + * instruction at ur->pc, accounting for current branch conditions. + */ +ulong +machnextaddr(Ureg *ur) +{ + long imm; + ulong ir; + + ir = *(ulong*)ur->pc; + switch(getop(ir)) { + case 18: /* branch */ + imm = ir & 0x03FFFFFC; + if(ir & 0x02000000) + imm |= 0xFC000000; /* sign extended */ + if((ir & 2) == 0) /* relative address */ + return ur->pc + imm; + return imm; + + case 16: /* conditional branch */ + if(condok(ur, ir&0xFFFF0000, 1)){ + imm = ir & 0xFFFC; + if(ir & 0x08000) + imm |= 0xFFFF0000; /* sign extended */ + if((ir & 2) == 0) /* relative address */ + return ur->pc + imm; + return imm; + } + break; + + case 19: /* conditional branch to register */ + switch(getxo(ir)){ + case 528: /* bcctr */ + if(condok(ur, ir, 0)) + return ur->ctr & ~3; + break; + case 16: /* bclr */ + if(condok(ur, ir, 1)) + return ur->lr & ~3; + break; + } + break; + } + return ur->pc+4; /* next instruction */ +} + +int +isvalid_va(void *v) +{ + return (ulong)v >= KTZERO; +} diff --git a/os/mpc/rmap.c b/os/mpc/rmap.c new file mode 100644 index 00000000..f521c24d --- /dev/null +++ b/os/mpc/rmap.c @@ -0,0 +1,106 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +void +mapinit(RMap *rmap, Map *map, int size) +{ + lock(rmap); + rmap->map = map; + rmap->mapend = map+(size/sizeof(Map)); + unlock(rmap); +} + +void +mapfree(RMap* rmap, ulong addr, int size) +{ + Map *mp; + ulong t; + + if(size <= 0) + return; + + lock(rmap); + for(mp = rmap->map; mp->addr <= addr && mp->size; mp++) + ; + + if(mp > rmap->map && (mp-1)->addr+(mp-1)->size == addr){ + (mp-1)->size += size; + if(addr+size == mp->addr){ + (mp-1)->size += mp->size; + while(mp->size){ + mp++; + (mp-1)->addr = mp->addr; + (mp-1)->size = mp->size; + } + } + } + else{ + if(addr+size == mp->addr && mp->size){ + mp->addr -= size; + mp->size += size; + } + else do{ + if(mp >= rmap->mapend){ + print("mapfree: %s: losing 0x%luX, %d\n", + rmap->name, addr, size); + break; + } + t = mp->addr; + mp->addr = addr; + addr = t; + t = mp->size; + mp->size = size; + mp++; + }while(size = t); + } + unlock(rmap); +} + +ulong +rmapalloc(RMap* rmap, ulong addr, int size, int align) +{ + Map *mp; + ulong maddr, oaddr; + + lock(rmap); + for(mp = rmap->map; mp->size; mp++){ + maddr = mp->addr; + + if(addr){ + if(maddr > addr) + break; + if(maddr+mp->size < addr) + continue; + if(addr+size > maddr+mp->size) + break; + maddr = addr; + } + + if(align > 0) + maddr = ((maddr+align-1)/align)*align; + if(mp->addr+mp->size-maddr < size) + continue; + + oaddr = mp->addr; + mp->addr = maddr+size; + mp->size -= maddr-oaddr+size; + if(mp->size == 0){ + do{ + mp++; + (mp-1)->addr = mp->addr; + }while((mp-1)->size = mp->size); + } + + unlock(rmap); + if(oaddr != maddr) + mapfree(rmap, oaddr, maddr-oaddr); + + return maddr; + } + unlock(rmap); + + return 0; +} diff --git a/os/mpc/screen.c b/os/mpc/screen.c new file mode 100644 index 00000000..59fff7f6 --- /dev/null +++ b/os/mpc/screen.c @@ -0,0 +1,832 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include +#include +#include + +#include "screen.h" + +enum { + Backgnd = 0xFF, /* white */ + Foregnd = 0x00, /* black */ +}; + +Cursor arrow = { + { -1, -1 }, + { 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04, + 0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04, + 0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40, + }, + { 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0, + 0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8, + 0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8, + 0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00, + }, +}; + +static Memdata xgdata; +static Memimage xgscreen = +{ + {0, 0, 0, 0}, /* r */ + {0, 0, 0, 0}, /* clipr */ + 8, /* depth */ + 1, /* nchan */ + CMAP8, /* chan */ + nil, /* cmap */ + &xgdata, /* data */ + 0, /* zero */ + 0, /* width */ + nil, /* layer */ + 0, /* flags */ +}; + +int novgascreen; /* optionally set by configuration file */ +static int lcdpdpar; /* value to load into io->pdpar */ + +Memimage *gscreen; +Memimage *conscol; +Memimage *back; + +static Memsubfont *memdefont; +static Lock palettelock; /* access to DAC registers */ +static Lock screenlock; +static int h; +static Point curpos; +static Rectangle window; + +typedef struct SWcursor SWcursor; +static SWcursor *swc = nil; +SWcursor* swcurs_create(ulong *, int, int, Rectangle, int); +void swcurs_destroy(SWcursor*); +void swcurs_enable(SWcursor*); +void swcurs_disable(SWcursor*); +void swcurs_hide(SWcursor*); +void swcurs_unhide(SWcursor*); +void swcurs_load(SWcursor*, Cursor*); + +static void screenputc(char*); +static void scroll(void); +static void setscreen(Mode*); +static void cursorlock(Rectangle); +static void cursorunlock(void); +static void lcdinit(Mode*); +static void lcdsetrgb(int, ulong, ulong, ulong); + +/* + * Called by main(). + */ +void +screeninit(void) +{ + Mode m; + +novgascreen=1; return; + + /* default size and parameters */ + memset(&m.lcd, 0, sizeof(m.lcd)); + m.x = 640; + m.y = 480; + m.d = 3; + if(novgascreen == 0 && archlcdmode(&m) >= 0){ + memdefont = getmemdefont(); + setscreen(&m); + } +} + +/* + * On 8 bit displays, load the default color map + */ +void +graphicscmap(int invert) +{ + int num, den, i, j; + int r, g, b, cr, cg, cb, v; + + for(r=0,i=0;r!=4;r++) for(v=0;v!=4;v++,i+=16){ + for(g=0,j=v-r;g!=4;g++) for(b=0;b!=4;b++,j++){ + den=r; + if(g>den) den=g; + if(b>den) den=b; + if(den==0) /* divide check -- pick grey shades */ + cr=cg=cb=v*17; + else{ + num=17*(4*den+v); + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + if(invert) + setcolor(255-i-(j&15), + cr*0x01010101, cg*0x01010101, cb*0x01010101); + else + setcolor(i+(j&15), + cr*0x01010101, cg*0x01010101, cb*0x01010101); + } + } +} + +/* + * reconfigure screen shape + */ +static void +setscreen(Mode *mode) +{ + int h; + + if(swc) + swcurs_destroy(swc); + + gscreen = &xgscreen; + xgdata.ref = 1; + lcdinit(mode); + xgdata.bdata = (uchar*)mode->aperture; + if(xgdata.bdata == nil) + panic("setscreen: vga soft memory"); + + gscreen->r = Rect(0, 0, mode->x, mode->y); + gscreen->clipr = gscreen->r; + gscreen->depth = 1<d; + gscreen->width = wordsperline(gscreen->r, gscreen->depth); + memimageinit(); + memdefont = getmemdefont(); + + memsetchan(gscreen, CMAP8); + back = memwhite; + conscol = memblack; + memimagedraw(gscreen, gscreen->r, memwhite, ZP, memopaque, ZP, SoverD); + graphicscmap(0); + + /* get size for a system window */ + h = memdefont->height; + window = insetrect(gscreen->r, 4); + window.max.y = window.min.y+(Dy(window)/h)*h; + curpos = window.min; +// screenclear(); + + graphicscmap(0); + +// swc = swcurs_create(gscreendata.data, gscreen.width, gscreen.ldepth, gscreen.r, 1); + + drawcursor(nil); +} + +enum { + ScreenCached = 1 /* non-zero if screen region not write-through */ +}; + +void +flushmemscreen(Rectangle r) +{ + if(rectclip(&r, gscreen->r) == 0) + return; + if(r.min.x >= r.max.x || r.min.y >= r.max.y) + return; + if(ScreenCached) + dcflush((ulong*)gscreen->data->bdata + gscreen->width*r.min.y, gscreen->width*Dy(r)); +} + +/* + * export screen to interpreter + */ +uchar* +attachscreen(Rectangle *r, ulong *chan, int* d, int *width, int *softscreen) +{ + *r = gscreen->r; + *d = gscreen->depth; + *chan = gscreen->chan; + *width = gscreen->width; + *softscreen = ScreenCached; + + return (uchar*)gscreen->data->bdata; +} + +void +detachscreen(void) +{ +} + +/* + * write a string to the screen + */ +void +screenputs(char *s, int n) +{ + int i; + Rune r; + char buf[4]; + + if(novgascreen || xgdata.bdata == nil || memdefont == nil) + return; + if(islo() == 0) { + /* don't deadlock trying to print in interrupt */ + if(!canlock(&screenlock)) + return; + } else + lock(&screenlock); + + while(n > 0) { + i = chartorune(&r, s); + if(i == 0){ + s++; + --n; + continue; + } + memmove(buf, s, i); + buf[i] = 0; + n -= i; + s += i; + screenputc(buf); + } + /* Only OK for now */ + flushmemscreen(gscreen->r); + + unlock(&screenlock); +} + +static void +scroll(void) +{ + int o; + Point p; + Rectangle r; + + o = 4*memdefont->height; + r = Rpt(window.min, Pt(window.max.x, window.max.y-o)); + p = Pt(window.min.x, window.min.y+o); + memimagedraw(gscreen, r, gscreen, p, nil, p, SoverD); + flushmemscreen(r); + r = Rpt(Pt(window.min.x, window.max.y-o), window.max); + memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD); + flushmemscreen(r); + + curpos.y -= o; +} + +static void +clearline(void) +{ + Rectangle r; + int yloc = curpos.y; + + r = Rpt(Pt(window.min.x, window.min.y + yloc), + Pt(window.max.x, window.min.y+yloc+memdefont->height)); + memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD); +} + +static void +screenputc(char *buf) +{ + Point p; + int h, w, pos; + Rectangle r; + static int *xp; + static int xbuf[256]; + + h = memdefont->height; + if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)]) + xp = xbuf; + + switch(buf[0]) { + case '\n': + if(curpos.y+h >= window.max.y) + scroll(); + curpos.y += h; + /* fall through */ + case '\r': + xp = xbuf; + curpos.x = window.min.x; + break; + case '\t': + if(curpos.x == window.min.x) + clearline(); + p = memsubfontwidth(memdefont, " "); + w = p.x; + *xp++ = curpos.x; + pos = (curpos.x-window.min.x)/w; + pos = 8-(pos%8); + r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y+h); + memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD); + flushmemscreen(r); + curpos.x += pos*w; + break; + case '\b': + if(xp <= xbuf) + break; + xp--; + r = Rpt(Pt(*xp, curpos.y), Pt(curpos.x, curpos.y + h)); + memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD); + flushmemscreen(r); + curpos.x = *xp; + break; + case '\0': + break; + default: + p = memsubfontwidth(memdefont, buf); + w = p.x; + + if(curpos.x >= window.max.x-w) + screenputc("\n"); + + if(curpos.x == window.min.x) + clearline(); + if(xp < xbuf+nelem(xbuf)) + *xp++ = curpos.x; + r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y+h); + memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD); + memimagestring(gscreen, curpos, conscol, ZP, memdefont, buf); + flushmemscreen(r); + curpos.x += w; + } +} + +int +setcolor(ulong p, ulong r, ulong g, ulong b) +{ + ulong x; + + if(gscreen->depth >= 8) + x = 0xFF; + else + x = 0xF; + p &= x; + p ^= x; + lock(&palettelock); + lcdsetrgb(p, r, g, b); + unlock(&palettelock); + return ~0; +} + +void +getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb) +{ + /* TO DO */ + *pr = *pg = *pb = 0; +} + +/* + * See section 5.2.1 (page 5-6) of the MPC823 manual + */ +static uchar lcdclock[17] = { /* (a<<2)|b => divisor of (1<iomem; + mode->aperture = xspanalloc(mode->x*mode->y, 16, 0); + mode->apsize = mode->x*mode->y; + + io->sdcr = 1; /* MPC823 errata: turn off LAM before disabling controller */ + eieio(); + io->lcfaa = PADDR(mode->aperture); + io->lccr = (((mode->x*mode->y*(1<d)+127)/128) << 17) | (mode->d << 5) | mode->lcd.flags; + switch(mode->d){ + default: + case 0: + /* monochrome/greyscale identity map */ + for(i=0; i<16; i++) + io->lcdmap[i] = i; + break; + case 2: + /* 4-bit grey scale map */ + for(i=0; i<16; i++) + io->lcdmap[0] = (i<<8)|(i<<4)|i; + break; + case 3: + /* 8-bit linear map */ + for(i=0; i<256; i++) + io->lcdmap[i] = (i<<8)|(i<<4)|i; + break; + } + + io->lcvcr = (mode->y << 11) | (mode->lcd.vpw<<28) | (mode->lcd.ac<<21) | mode->lcd.wbf; + io->lchcr = (mode->x<<10) | BigEndian | mode->lcd.wbl; + + hz = m->cpuhz; + d = hz/mode->lcd.freq; + if(hz/d > mode->lcd.freq) + d++; + if(d >= 16) + d = 16; + + /* + * enable LCD outputs + */ + io->pddat = 0; + lcdpdpar = 0x1fff & ~mode->lcd.notpdpar; + io->pdpar = lcdpdpar; + io->pddir = 0x1fff; + io->pbpar |= IBIT(31) | IBIT(19) | IBIT(17); + io->pbdir |= IBIT(31) | IBIT(19) | IBIT(17); + io->pbodr &= ~(IBIT(31) | IBIT(19) | IBIT(17)); + + /* + * with the data cache off, early revisions of the 823 did not require + * the `aggressive' DMA priority to avoid flicker, but flicker is obvious + * on the 823A when the cache is on, so LAM is now set + */ + io->sdcr = (io->sdcr & ~0xF) | LAM; /* LAM=1, LAID=0, RAID=0 */ + +// gscreen.width = gscreen.width; /* access external memory before enabling (mpc823 errata) */ + eieio(); + io->sccrk = KEEP_ALIVE_KEY; + eieio(); + io->sccr = (io->sccr & ~0x1F) | lcdclock[d]; + eieio(); + io->sccrk= ~KEEP_ALIVE_KEY; + io->lcsr = 7; /* clear status */ + eieio(); + io->lccr |= Enable; + archbacklight(1); +} + +static void +lcdsetrgb(int p, ulong r, ulong g, ulong b) +{ + r >>= 28; + g >>= 28; + b >>= 28; + m->iomem->lcdmap[p&0xFF] = (r<<8) | (g<<4) | b; +} + +void +blankscreen(int blank) +{ + USED(blank); /* TO DO */ +} + +/* + * enable/disable LCD panel (eg, when using video subsystem) + */ +void +lcdpanel(int on) +{ + IMM *io; + + if(on){ + archbacklight(1); + io = ioplock(); + io->pddat = 0; + io->pdpar = lcdpdpar; + io->pddir = 0x1fff; + io->lccr |= Enable; + iopunlock(); + }else{ + io = ioplock(); + io->sdcr = 1; /* MPC823 errata: turn off LAM before disabling controller */ + eieio(); + io->pddir = 0; + eieio(); + io->lccr &= ~Enable; + iopunlock(); + archbacklight(0); + } +} + +/* + * Software cursor code. Interim version (for baseline). + * we may want to replace code here by memdraw primitives. + */ + +enum { + CUR_ENA = 0x01, /* cursor is enabled */ + CUR_DRW = 0x02, /* cursor is currently drawn */ + CUR_SWP = 0x10, /* bit swap */ + CURSWID = 16, + CURSHGT = 16, +}; + +typedef struct SWcursor { + ulong *fb; /* screen frame buffer */ + Rectangle r; + int d; /* ldepth of screen */ + int width; /* width of screen in ulongs */ + int x; + int y; + int hotx; + int hoty; + uchar cbwid; /* cursor byte width */ + uchar f; /* flags */ + uchar cwid; + uchar chgt; + int hidecount; + uchar data[CURSWID*CURSHGT]; + uchar mask[CURSWID*CURSHGT]; + uchar save[CURSWID*CURSHGT]; +} SWcursor; + +static Rectangle cursoroffrect; +static int cursorisoff; + +static void swcursorflush(int, int); +static void swcurs_draw_or_undraw(SWcursor *); + +static void +cursorupdate0(void) +{ + int inrect, x, y; + Point m; + + m = mousexy(); + x = m.x - swc->hotx; + y = m.y - swc->hoty; + inrect = (x >= cursoroffrect.min.x && x < cursoroffrect.max.x + && y >= cursoroffrect.min.y && y < cursoroffrect.max.y); + if (cursorisoff == inrect) + return; + cursorisoff = inrect; + if (inrect) + swcurs_hide(swc); + else { + swc->hidecount = 0; + swcurs_draw_or_undraw(swc); + } + swcursorflush(m.x, m.y); +} + +void +cursorupdate(Rectangle r) +{ + lock(&screenlock); + r.min.x -= 16; + r.min.y -= 16; + cursoroffrect = r; + if (swc) + cursorupdate0(); + unlock(&screenlock); +} + +void +cursorenable(void) +{ + Point m; + + lock(&screenlock); + if(swc) { + swcurs_enable(swc); + m = mousexy(); + swcursorflush(m.x, m.y); + } + unlock(&screenlock); +} + +void +cursordisable(void) +{ + Point m; + + lock(&screenlock); + if(swc) { + swcurs_disable(swc); + m = mousexy(); + swcursorflush(m.x, m.y); + } + unlock(&screenlock); +} + +void +drawcursor(Drawcursor* c) +{ + Point p; + Cursor curs, *cp; + int j, i, h, bpl; + uchar *bc, *bs, *cclr, *cset; + + if(!swc) + return; + + /* Set the default system cursor */ + if(!c || c->data == nil) + cp = &arrow /*&crosshair_black*/; + else { + cp = &curs; + p.x = c->hotx; + p.y = c->hoty; + cp->offset = p; + bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1); + + h = (c->maxy-c->miny)/2; + if(h > 16) + h = 16; + + bc = c->data; + bs = c->data + h*bpl; + + cclr = cp->clr; + cset = cp->set; + for(i = 0; i < h; i++) { + for(j = 0; j < 2; j++) { + cclr[j] = bc[j]; + cset[j] = bs[j]; + } + bc += bpl; + bs += bpl; + cclr += 2; + cset += 2; + } + } + + if(swc) { + swcurs_load(swc, cp); + p = mousexy(); + swcursorflush(p.x, p.y); + } +} + +SWcursor* +swcurs_create(ulong *fb, int width, int ldepth, Rectangle r, int bitswap) +{ + SWcursor *swc = (SWcursor*)malloc(sizeof(SWcursor)); + swc->fb = fb; + swc->r = r; + swc->d = ldepth; + swc->width = width; + swc->f = bitswap ? CUR_SWP : 0; + swc->x = swc->y = 0; + swc->hotx = swc->hoty = 0; + swc->hidecount = 0; + return swc; +} + +void +swcurs_destroy(SWcursor *swc) +{ + swcurs_disable(swc); + free(swc); +} + +static void +swcursorflush(int x, int y) +{ + Rectangle r; + + /* XXX a little too paranoid here */ + r.min.x = x-16; + r.min.y = y-16; + r.max.x = x+17; + r.max.y = y+17; + flushmemscreen(r); +} + +static void +swcurs_draw_or_undraw(SWcursor *swc) +{ + uchar *p; + uchar *cs; + int w, vw; + int x1 = swc->r.min.x; + int y1 = swc->r.min.y; + int x2 = swc->r.max.x; + int y2 = swc->r.max.y; + int xp = swc->x - swc->hotx; + int yp = swc->y - swc->hoty; + int ofs; + + if(((swc->f & CUR_ENA) && (swc->hidecount <= 0)) + == ((swc->f & CUR_DRW) != 0)) + return; + w = swc->cbwid*BI2BY/(1 << swc->d); + x1 = xp < x1 ? x1 : xp; + y1 = yp < y1 ? y1 : yp; + x2 = xp+w >= x2 ? x2 : xp+w; + y2 = yp+swc->chgt >= y2 ? y2 : yp+swc->chgt; + if(x2 <= x1 || y2 <= y1) + return; + p = (uchar*)(swc->fb + swc->width*y1) + + x1*(1 << swc->d)/BI2BY; + y2 -= y1; + x2 = (x2-x1)*(1 << swc->d)/BI2BY; + vw = swc->width*BY2WD - x2; + w = swc->cbwid - x2; + ofs = swc->cbwid*(y1-yp)+(x1-xp); + cs = swc->save + ofs; + if((swc->f ^= CUR_DRW) & CUR_DRW) { + uchar *cm = swc->mask + ofs; + uchar *cd = swc->data + ofs; + while(y2--) { + x1 = x2; + while(x1--) { + *p = ((*cs++ = *p) & *cm++) ^ *cd++; + p++; + } + cs += w; + cm += w; + cd += w; + p += vw; + } + } else { + while(y2--) { + x1 = x2; + while(x1--) + *p++ = *cs++; + cs += w; + p += vw; + } + } +} + +void +swcurs_hide(SWcursor *swc) +{ + ++swc->hidecount; + swcurs_draw_or_undraw(swc); +} + +void +swcurs_unhide(SWcursor *swc) +{ + if (--swc->hidecount < 0) + swc->hidecount = 0; + swcurs_draw_or_undraw(swc); +} + +void +swcurs_enable(SWcursor *swc) +{ + swc->f |= CUR_ENA; + swcurs_draw_or_undraw(swc); +} + +void +swcurs_disable(SWcursor *swc) +{ + swc->f &= ~CUR_ENA; + swcurs_draw_or_undraw(swc); +} + +void +swcurs_load(SWcursor *swc, Cursor *c) +{ + int i, k; + uchar *bc, *bs, *cd, *cm; + static uchar bdv[4] = {0,Backgnd,Foregnd,0xff}; + static uchar bmv[4] = {0xff,0,0,0xff}; + int bits = 1<d; + uchar mask = (1<f&CUR_SWP) ? 8-bits : 0; + + bc = c->clr; + bs = c->set; + + swcurs_hide(swc); + cd = swc->data; + cm = swc->mask; + swc->hotx = c->offset.x; + swc->hoty = c->offset.y; + swc->chgt = CURSHGT; + swc->cwid = CURSWID; + swc->cbwid = CURSWID*(1<d)/BI2BY; + for(i = 0; i < CURSWID/BI2BY*CURSHGT; i++) { + uchar bcb = *bc++; + uchar bsb = *bs++; + for(k=0; k>7; + int s = z^bswp; + cdv |= (bdv[n]&mask) << s; + cmv |= (bmv[n]&mask) << s; + bcb <<= 1; + bsb <<= 1; + k++; + } + *cd++ = cdv; + *cm++ = cmv; + } + } + swcurs_unhide(swc); +} + diff --git a/os/mpc/screen.h b/os/mpc/screen.h new file mode 100644 index 00000000..8f8288a8 --- /dev/null +++ b/os/mpc/screen.h @@ -0,0 +1,60 @@ +enum { + Pcolours = 256, /* Palette */ + Pred = 0, + Pgreen = 1, + Pblue = 2, + + Pblack = 0x00, + Pwhite = 0xFF, +}; + +typedef struct Cursor Cursor; +struct Cursor +{ + Point offset; + uchar clr[2*16]; + uchar set[2*16]; +}; + +/* + * MPC8xx LCD controller + */ +typedef struct LCDconfig { + long freq; /* ideal panel frequency in Hz */ + int wbl; /* wait between lines (shift/clk cycles) */ + int vpw; /* vertical sync pulse width (lines) */ + int wbf; /* wait between frames (lines) */ + int ac; /* AC timing (frames) */ + ulong flags; + ulong notpdpar; /* reset mask for pdpar */ +} LCDconfig; + +enum { + /* lccr flags stored in LCDconfig.flags */ + ClockLow = 1<<11, + OELow = 1<<10, + HsyncLow = 1<<9, + VsyncLow = 1<<8, + DataLow = 1<<7, + Passive8 = 1<<4, + DualScan = 1<<3, + IsColour = 1<<2, + IsTFT = 1<<1, +}; + +/* + * physical graphics device properties set by archlcdmode + */ +typedef struct Mode { + int x; + int y; + int d; + + uchar* aperture; + int apsize; + LCDconfig lcd; +} Mode; + +int archlcdmode(Mode*); +extern Point mousexy(void); +extern void blankscreen(int); \ No newline at end of file diff --git a/os/mpc/spi.c b/os/mpc/spi.c new file mode 100644 index 00000000..ce662533 --- /dev/null +++ b/os/mpc/spi.c @@ -0,0 +1,243 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* + * basic read/write interface to mpc8xx Serial Peripheral Interface; + * used by devtouch.c and devspi.c + */ + +typedef struct Ctlr Ctlr; + +enum { + /* spi-specific BD flags */ + BDContin= 1<<9, /* continuous mode */ + RxeOV= 1<<1, /* overrun */ + TxeUN= 1<<1, /* underflow */ + BDme= 1<<0, /* multimaster error */ + BDrxerr= RxeOV|BDme, + BDtxerr= TxeUN|BDme, + + /* spmod */ + MLoop= 1<<14, /* loopback mode */ + MClockInv= 1<<13, /* inactive state of SPICLK is high */ + MClockPhs= 1<<12, /* SPCLK starts toggling at beginning of transfer */ + MDiv16= 1<<11, /* use BRGCLK/16 as input to SPI baud rate */ + MRev= 1<<10, /* normal operation */ + MMaster= 1<<9, + MSlave= 0<<9, + MEnable= 1<<8, + /* LEN, PS fields */ + + /* spcom */ + STR= 1<<7, /* start transmit */ + + /* spie */ + MME = 1<<5, + TXE = 1<<4, + BSY = 1<<2, + TXB = 1<<1, + RXB = 1<<0, + + /* port B bits */ + SPIMISO = IBIT(28), /* master mode input */ + SPIMOSI = IBIT(29), /* master mode output */ + SPICLK = IBIT(30), + + /* maximum SPI I/O (can change) */ + Bufsize = 64, +}; + +/* + * SPI software structures + */ + +struct Ctlr { + Lock; + QLock io; + int init; + SPI* spi; + IOCparam* sp; + + BD* rd; + BD* td; + int phase; + Rendez r; + char* txbuf; + char* rxbuf; +}; + +static Ctlr spictlr[1]; + +/* dcflush isn't needed if rxbuf and txbuf allocated in uncached IMMR memory */ +#define DCFLUSH(a,n) + +static void interrupt(Ureg*, void*); + +/* + * called by the reset routine of any driver using the SPI + */ +void +spireset(void) +{ + IMM *io; + SPI *spi; + IOCparam *sp; + CPMdev *cpm; + Ctlr *ctlr; + + ctlr = spictlr; + if(ctlr->init) + return; + ctlr->init = 1; + cpm = cpmdev(CPspi); + spi = cpm->regs; + ctlr->spi = spi; + sp = cpm->param; + if(sp == nil){ + print("SPI: can't allocate new parameter memory\n"); + return; + } + ctlr->sp = sp; + + if(ctlr->rxbuf == nil) + ctlr->rxbuf = cpmalloc(Bufsize, 2); + if(ctlr->txbuf == nil) + ctlr->txbuf = cpmalloc(Bufsize, 2); + + if(ctlr->rd == nil){ + ctlr->rd = bdalloc(1); + ctlr->rd->addr = PADDR(ctlr->rxbuf); + ctlr->rd->length = 0; + ctlr->rd->status = BDWrap; + } + if(ctlr->td == nil){ + ctlr->td = bdalloc(1); + ctlr->td->addr = PADDR(ctlr->txbuf); + ctlr->td->length = 0; + ctlr->td->status = BDWrap|BDLast; + } + + /* select port pins */ + io = ioplock(); + io->pbdir |= SPICLK | SPIMOSI | SPIMISO; + io->pbpar |= SPICLK | SPIMOSI | SPIMISO; + iopunlock(); + + /* explicitly initialise parameters, because InitRxTx can't be used (see i2c/spi relocation errata) */ + sp = ctlr->sp; + sp->rbase = PADDR(ctlr->rd); + sp->tbase = PADDR(ctlr->td); + sp->rfcr = 0x18; + sp->tfcr = 0x18; + sp->mrblr = Bufsize; + sp->rstate = 0; + sp->rptr = 0; + sp->rbptr = sp->rbase; + sp->rcnt = 0; + sp->tstate = 0; + sp->tbptr = sp->tbase; + sp->tptr = 0; + sp->tcnt = 0; + eieio(); + + spi->spmode = MDiv16 | MRev | MMaster | ((8-1)<<4) | 1; /* 8 bit characters */ + if(0) + spi->spmode |= MLoop; /* internal loop back mode for testing */ + + spi->spie = ~0; /* clear events */ + eieio(); + spi->spim = MME|TXE|BSY|TXB|RXB; + eieio(); + spi->spmode |= MEnable; + + intrenable(VectorCPIC+cpm->irq, interrupt, spictlr, BUSUNKNOWN, "spi"); + +} + +enum { + Idling, + Waitval, + Readyval +}; + +static void +interrupt(Ureg*, void *arg) +{ + int events; + Ctlr *ctlr; + SPI *spi; + + ctlr = arg; + spi = ctlr->spi; + events = spi->spie; + eieio(); + spi->spie = events; + if(events & (MME|BSY|TXE)){ + print("SPI#%x\n", events); + if(ctlr->phase != Idling){ + ctlr->phase = Idling; + wakeup(&ctlr->r); + } + }else if(events & RXB){ + if(ctlr->phase == Waitval){ + ctlr->phase = Readyval; + wakeup(&ctlr->r); + } + } +} + +static int +done(void *a) +{ + return ((Ctlr*)a)->phase != Waitval; +} + +/* + * send `nout' bytes on SPI from `out' and read as many bytes of reply into buffer `in'; + * return the number of bytes received, or -1 on error. + */ +long +spioutin(void *out, long nout, void *in) +{ + Ctlr *ctlr; + int nb, p; + + ctlr = spictlr; + if(nout > Bufsize) + return -1; + qlock(&ctlr->io); + if(waserror()){ + qunlock(&ctlr->io); + return -1; + } + if(ctlr->phase != Idling) + sleep(&ctlr->r, done, ctlr); + memmove(ctlr->txbuf, out, nout); + DCFLUSH(ctlr->txbuf, Bufsize); + DCFLUSH(ctlr->rxbuf, Bufsize); + ilock(ctlr); + ctlr->phase = Waitval; + ctlr->td->length = nout; + ctlr->rd->status = BDEmpty|BDWrap|BDInt; + ctlr->td->status = BDReady|BDWrap|BDLast; + eieio(); + ctlr->spi->spcom = STR; + eieio(); + iunlock(ctlr); + sleep(&ctlr->r, done, ctlr); + nb = ctlr->rd->length; + if(nb > nout) + nb = nout; /* shouldn't happen */ + p = ctlr->phase; + poperror(); + qunlock(&ctlr->io); + if(p != Readyval) + return -1; + memmove(in, ctlr->rxbuf, nb); + return nb; +} diff --git a/os/mpc/trap.c b/os/mpc/trap.c new file mode 100644 index 00000000..843d7ad4 --- /dev/null +++ b/os/mpc/trap.c @@ -0,0 +1,554 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" +#include "io.h" +#include "../port/error.h" + + +enum +{ + Maxhandler= MaxVector /* max number of interrupt handlers */ +}; + +typedef struct Handler Handler; +struct Handler +{ + void (*r)(Ureg*, void*); + void *arg; + char name[KNAMELEN]; + Handler *next; + ulong nintr; + ulong ticks; + int maxtick; +}; + +struct +{ + Handler *ivec[MaxVector]; + Handler h[Maxhandler]; + int free; + Handler* freelist; +} halloc; + +Instr BREAK = 0x7fe00008; +int (*breakhandler)(Ureg*, Proc*); + +void kernfault(Ureg*, int); + +char *excname[] = +{ + "reserved 0", + "system reset", + "machine check", + "data access", + "instruction access", + "external interrupt", + "alignment", + "program exception", + "floating-point unavailable", + "decrementer", + "i/o controller interface error", + "reserved B", + "system call", + "trace trap", + "floating point assist", + "reserved F", + "software emulation", + "ITLB miss", + "DTLB miss", + "ITLB error", + "DTLB error", + "reserved 15", + "reserved 16", + "reserved 17", + "reserved 18", + "reserved 19", + "reserved 1A", + "reserved 1B", + "data breakpoint", + "instruction breakpoint", + "peripheral breakpoint", + "development port", + /* the following are made up on a program exception */ + "floating point exception", /* 20: FPEXC */ + "illegal instruction", /* 21 */ + "privileged instruction", /* 22 */ + "trap", /* 23 */ + "illegal operation", /* 24 */ + "breakpoint", /* 25 */ +}; + +char *fpcause[] = +{ + "inexact operation", + "division by zero", + "underflow", + "overflow", + "invalid operation", +}; +char *fpexcname(Ureg*, ulong, char*); +#define FPEXPMASK 0xfff80300 /* Floating exception bits in fpscr */ + + +char *regname[]={ + "CAUSE", "SRR1", + "PC", "GOK", + "LR", "CR", + "XER", "CTR", + "R0", "R1", + "R2", "R3", + "R4", "R5", + "R6", "R7", + "R8", "R9", + "R10", "R11", + "R12", "R13", + "R14", "R15", + "R16", "R17", + "R18", "R19", + "R20", "R21", + "R22", "R23", + "R24", "R25", + "R26", "R27", + "R28", "R29", + "R30", "R31", +}; + +void +sethvec(int v, void (*r)(void)) +{ + ulong *vp, pa, o; + + vp = (ulong*)KADDR(v); + vp[0] = 0x7c1043a6; /* MOVW R0, SPR(SPRG0) */ + vp[1] = 0x7c0802a6; /* MOVW LR, R0 */ + vp[2] = 0x7c1243a6; /* MOVW R0, SPR(SPRG2) */ + pa = PADDR(r); + o = pa >> 25; + if(o != 0 && o != 0x7F){ + /* a branch too far: running from ROM */ + vp[3] = (15<<26)|(pa>>16); /* MOVW $r&~0xFFFF, R0 */ + vp[4] = (24<<26)|(pa&0xFFFF); /* OR $r&0xFFFF, R0 */ + vp[5] = 0x7c0803a6; /* MOVW R0, LR */ + vp[6] = 0x4e800021; /* BL (LR) */ + }else + vp[3] = (18<<26)|(pa&0x3FFFFFC)|3; /* bla */ + dcflush(vp, 8*sizeof(ulong)); +} + +void +sethvec2(int v, void (*r)(void)) +{ + ulong *vp; + + vp = (ulong*)KADDR(v); + vp[0] = (18<<26)|((ulong)r&~KSEGM)|2; /* ba */ + dcflush(vp, sizeof(*vp)); +} + +void +trap(Ureg *ur) +{ + int ecode, s; + ulong w; + char buf[ERRMAX]; + + ecode = ur->cause >> 8; + if(ecode < 0 || ecode >= 0x1F) + ecode = 0x1F; + switch(ecode){ + case CDEC: + clockintr(ur); + preemption(1); + break; + + case CMCHECK: + case CDSI: + case CISI: + case CIMISS: + case CDMISS: + case CITLBE: + case CDTLBE: + faultpower(ur); + break; + + case CEMU: + if(up == nil) + goto Default; + if((ulong)(ur+1) != ur->r1) + panic("fp emu stack"); + spllo(); + if(waserror()){ + if(up->type == Interp) + disfault(ur, up->env->errstr); + panic("%s", up->env->errstr); + } + if(fpipower(ur) == 0){ + splhi(); + poperror(); + print("pc=#%lux op=#%8.8lux\n", ur->pc, *(ulong*)ur->pc); + goto Default; + } + poperror(); + break; + + case CPROG: + if(ur->status & (1<<19)) { + ecode = 0x20; + w = ur->pc; + if(ur->status & (1<<16)) + w += 4; + if(*(ulong*)w == 0x7fe00008){ /* tw 31,0,0 */ + if(breakhandler){ + s = (*breakhandler)(ur, up); + if(s == BrkSched){ + if(up){ + up->preempted = 0; + sched(); + splhi(); + } + }else if(s == BrkNoSched){ + if(up){ + up->preempted = 1; /* stop it being preempted until next instruction */ + up->dbgreg = 0; + } + } + break; + } + ecode = 0x1D; /* breakpoint */ + } + } + if(ur->status & (1<<18)) + ecode = 0x21; + if(ur->status & (1<<17)) + ecode = 0x22; + /* FALL THROUGH */ + + Default: + default: + if(up && up->type == Interp) { + spllo(); + snprint(buf, sizeof buf, "sys: trap: %s pc=0x%lux", excname[ecode], ur->pc); + error(buf); + break; + } + print("kernel %s pc=0x%lux\n", excname[ecode], ur->pc); + dumpregs(ur); + dumpstack(); + if(m->machno == 0) + spllo(); + exit(1); + } + + splhi(); +} + +void +spurious(Ureg *ur, void *a) +{ + USED(a); + print("SPURIOUS interrupt pc=0x%lux cause=0x%lux\n", + ur->pc, ur->cause); + panic("bad interrupt"); +} + +#define LEV(n) (((n)<<1)|1) +#define IRQ(n) (((n)<<1)|0) + +Lock veclock; + +void +trapinit(void) +{ + int i; + IMM *io; + + + io = m->iomem; + io->simask = 0; /* mask all */ + io->siel = ~0; /* edge sensitive, wake on all */ + io->cicr = 0; /* disable CPM interrupts */ + io->cipr = ~0; /* clear all interrupts */ + io->cimr = 0; /* mask all events */ + io->cicr = (0xE1<<16)|(CPIClevel<<13)|(0x1F<<8); + io->cicr |= 1 << 7; /* enable */ + io->tbscrk = KEEP_ALIVE_KEY; + io->tbscr = 1; /* TBE */ + io->simask |= 1<<(31-LEV(CPIClevel)); /* CPM's level */ + io->tbk = KEEP_ALIVE_KEY; + eieio(); + putdec(~0); + + /* + * set all exceptions to trap + */ + for(i = 0x0; i < 0x3000; i += 0x100) + sethvec(i, trapvec); + + sethvec(CEI<<8, intrvec); + //sethvec2(CIMISS<<8, itlbmiss); + //sethvec2(CDMISS<<8, dtlbmiss); +} + +void +intrenable(int v, void (*r)(Ureg*, void*), void *arg, int, char *name) +{ + Handler *h; + IMM *io; + + v -= VectorPIC; + if(v < 0 || v >= nelem(halloc.ivec)) + panic("intrenable(%d)", v+VectorPIC); + ilock(&veclock); + if((h = halloc.freelist) == nil){ + if(halloc.free >= Maxhandler){ + iunlock(&veclock); + panic("out of interrupt handlers"); + } + h = &halloc.h[halloc.free++]; + }else + halloc.freelist = h->next; + h->r = r; + h->arg = arg; + strncpy(h->name, name, KNAMELEN-1); + h->name[KNAMELEN-1] = 0; + h->next = halloc.ivec[v]; + halloc.ivec[v] = h; + + /* + * enable corresponding interrupt in SIU/CPM + */ + + eieio(); + io = m->iomem; + if(v >= VectorCPIC){ + v -= VectorCPIC; + io->cimr |= 1<<(v&0x1F); + } + else if(v >= VectorIRQ) + io->simask |= 1<<(31-IRQ(v&7)); + else + io->simask |= 1<<(31-LEV(v)); + eieio(); + iunlock(&veclock); +} + +static void +irqdisable(int v) +{ + IMM *io; + + io = m->iomem; + if(v >= VectorCPIC){ + v -= VectorCPIC; + io->cimr &= ~(1<<(v&0x1F)); + } + else if(v >= VectorIRQ) + io->simask &= ~(1<<(31-IRQ(v&7))); + else + io->simask &= ~(1<<(31-LEV(v))); +} + +void +intrdisable(int v, void (*r)(Ureg*, void*), void *arg, int, char *name) +{ + Handler *h, **hp; + + v -= VectorPIC; + if(v < 0 || v >= nelem(halloc.ivec)) + panic("intrenable(%d)", v+VectorPIC); + ilock(&veclock); + for(hp = &halloc.ivec[v]; (h = *hp) != nil; hp = &h->next) + if(h->r == r && h->arg == arg && strcmp(h->name, name) == 0){ + *hp = h->next; + h->next = halloc.freelist; + halloc.freelist = h; + break; + } + if(halloc.ivec[v] == nil) + irqdisable(v); + iunlock(&veclock); +} + +/* + * called directly by l.s:/intrvec. on a multiprocessor we'd need to lock veclock. + */ +void +intr(Ureg *ur) +{ + int b, v; + IMM *io; + Handler *h; + long t0; + Proc *oup; + + ur->cause &= ~0xff; + io = m->iomem; + b = io->sivec>>2; + v = b>>1; + if(b & 1) { + if(v == CPIClevel){ + io->civr = 1; + eieio(); + v = VectorCPIC+(io->civr>>11); + } + }else{ + v += VectorIRQ; + if(io->siel & (1<<(31-b))){ + io->sipend |= 1<<(31-b); + eieio(); + } + } + + ur->cause |= v; + h = halloc.ivec[v]; + if(h == nil){ + iprint("unknown interrupt %d pc=0x%lux\n", v, ur->pc); + irqdisable(v); + return; + } + + /* + * call the interrupt handlers + */ + oup = up; + up = nil; /* no process at interrupt level */ + do { + h->nintr++; + t0 = getdec(); + (*h->r)(ur, h->arg); + t0 -= getdec(); + h->ticks += t0; + if(h->maxtick < t0) + h->maxtick = t0; + h = h->next; + } while(h != nil); + if(v >= VectorCPIC) + io->cisr |= 1<<(v-VectorCPIC); + eieio(); + up = oup; + preemption(0); +} + +int +intrstats(char *buf, int bsize) +{ + Handler *h; + int i, n; + + n = 0; + for(i=0; inintr) + n += snprint(buf+n, bsize-n, "%3d %lud %lud %ud\n", i, h->nintr, h->ticks, h->maxtick); + return n; +} + +char* +fpexcname(Ureg *ur, ulong fpscr, char *buf) +{ + int i; + char *s; + ulong fppc; + + fppc = ur->pc; + s = 0; + fpscr >>= 3; /* trap enable bits */ + fpscr &= (fpscr>>22); /* anded with exceptions */ + for(i=0; i<5; i++) + if(fpscr & (1<status, ur->pc, ur->sp); + dumpregs(ur); + l.sp = ur->sp; + l.pc = ur->pc; + dumpstack(); + setpri(PriBackground); /* Let the debugger in */ + for(;;) + sched(); +} + +void +dumpstack(void) +{ + ulong l, v; + int i; + + if(up == 0) + return; + i = 0; + for(l=(ulong)&l; l<(ulong)(up->kstack+KSTACK); l+=4){ + v = *(ulong*)l; + if(KTZERO < v && v < (ulong)etext){ + print("%lux=%lux, ", l, v); + if(i++ == 4){ + print("\n"); + i = 0; + } + } + } +} + +void +dumpregs(Ureg *ur) +{ + int i; + ulong *l; + if(up) { + print("registers for %s %ld\n", up->text, up->pid); + if(ur->usp < (ulong)up->kstack || + ur->usp > (ulong)up->kstack+KSTACK) + print("invalid stack ptr\n"); + } + else + print("registers for kernel\n"); + + l = &ur->cause; + for(i=0; ikpfun)(up->arg); + pexit("", 0); +} + +void +kprocchild(Proc *p, void (*func)(void*), void *arg) +{ + p->sched.pc = (ulong)linkproc; + p->sched.sp = (ulong)p->kstack+KSTACK; + + p->kpfun = func; + p->arg = arg; +} + +void +setpanic(void) +{ + consoleprint = 1; +} + +void +dumplongs(char*, ulong*, int) +{ +} diff --git a/os/mpc/usb.h b/os/mpc/usb.h new file mode 100644 index 00000000..1d750f3f --- /dev/null +++ b/os/mpc/usb.h @@ -0,0 +1,62 @@ +/* + * USB packet definitions + */ + +#define GET2(p) ((((p)[1]&0xFF)<<8)|((p)[0]&0xFF)) +#define PUT2(p,v) {((p)[0] = (v)); ((p)[1] = (v)>>8);} + +enum { + /* request type */ + RH2D = 0<<7, + RD2H = 1<<7, + Rstandard = 0<<5, + Rclass = 1<<5, + Rvendor = 2<<5, + Rdevice = 0, + Rinterface = 1, + Rendpt = 2, + Rother = 3, + + /* standard requests */ + GET_STATUS = 0, + CLEAR_FEATURE = 1, + SET_FEATURE = 3, + SET_ADDRESS = 5, + GET_DESCRIPTOR = 6, + SET_DESCRIPTOR = 7, + GET_CONFIGURATION = 8, + SET_CONFIGURATION = 9, + GET_INTERFACE = 10, + SET_INTERFACE = 11, + SYNCH_FRAME = 12, + + /* hub class feature selectors */ + C_HUB_LOCAL_POWER = 0, + C_HUB_OVER_CURRENT, + PORT_CONNECTION = 0, + PORT_ENABLE = 1, + PORT_SUSPEND = 2, + PORT_OVER_CURRENT = 3, + PORT_RESET = 4, + PORT_POWER = 8, + PORT_LOW_SPEED = 9, + C_PORT_CONNECTION = 16, + C_PORT_ENABLE, + C_PORT_SUSPEND, + C_PORT_OVER_CURRENT, + C_PORT_RESET, + + /* descriptor types */ + DEVICE = 1, + CONFIGURATION = 2, + STRING = 3, + INTERFACE = 4, + ENDPOINT = 5, + HID = 0x21, + REPORT = 0x22, + PHYSICAL = 0x23, + + /* feature selectors */ + DEVICE_REMOTE_WAKEUP = 1, + ENDPOINT_STALL = 0, +}; diff --git a/os/omap/README b/os/omap/README new file mode 100644 index 00000000..404f364b --- /dev/null +++ b/os/omap/README @@ -0,0 +1,4 @@ +TI OMAP platform + +This is not currently being distributed (mainly for lack of a commonly-available +target). Ask us about it if interested. diff --git a/os/pc/NOTICE b/os/pc/NOTICE new file mode 100644 index 00000000..95b421c4 --- /dev/null +++ b/os/pc/NOTICE @@ -0,0 +1,8 @@ +Most of these files have been issued at some time under the Lucent Public License 1.02, +except devbench.c, fpi387.c, flashzpc.c, devtv.c, devmouse.c, devmpeg.c (which are Vita Nuova MIT-template). + +The copyright is not necessarily Lucent's in all cases (eg, ether83815.c, sd53c8xx.c, +and much of the USB and wavelan support) because some things were written outside Lucent. +A more accurate summary will appear here shortly. +Meanwhile, if you'd like to use the software in some other program, +you can always ask us to work out the detailed appropriate copyright notice. diff --git a/os/pc/README b/os/pc/README new file mode 100644 index 00000000..092eceb6 --- /dev/null +++ b/os/pc/README @@ -0,0 +1,9 @@ +PC kernel + +The PC kernel is currently being updated to bring it up-to-date with +Plan 9 support (the original Inferno PC kernel was derived from an +extended subset of an earlier edition of Plan 9). In particular, +although it can be configured as a server, graphics support is +still being provided. (The kernel level code in vga*.c, devvga.c and screen.c +is probably near the final version, but some Limbo code is needed to +set the right graphics modes.) diff --git a/os/pc/apbootstrap.h b/os/pc/apbootstrap.h new file mode 100644 index 00000000..61aca70d --- /dev/null +++ b/os/pc/apbootstrap.h @@ -0,0 +1,15 @@ +uchar apbootstrap[]={ +0xea,0x14,0x10,0x00,0x00,0x90,0x90,0x90,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x8c,0xc8,0x8e,0xd8,0x0f,0x01,0x16,0xac,0x10,0x0f,0x20,0xc0, +0x83,0xc8,0x01,0x0f,0x22,0xc0,0xeb,0x00,0xb8,0x08,0x00,0x8e,0xd8,0x8e,0xc0,0x8e, +0xe0,0x8e,0xe8,0x8e,0xd0,0x66,0xea,0x3d,0x10,0x00,0x00,0x10,0x00,0x8b,0x0d,0x0c, +0x10,0x00,0x00,0x8b,0x91,0x00,0x08,0x00,0x00,0x89,0x11,0x0f,0x22,0xd9,0x0f,0x20, +0xc2,0x81,0xca,0x00,0x00,0x01,0x80,0x81,0xe2,0xf5,0xff,0xff,0x9f,0xb8,0x67,0x10, +0x00,0x80,0x0f,0x22,0xc2,0xff,0xe0,0x89,0xc8,0x0d,0x00,0x00,0x00,0x80,0xc7,0x00, +0x00,0x00,0x00,0x00,0x0f,0x22,0xd9,0xbc,0xfc,0x5f,0x00,0x80,0x31,0xc0,0x50,0x9d, +0x8b,0x05,0x10,0x10,0x00,0x80,0x89,0x04,0x24,0x8b,0x05,0x08,0x10,0x00,0x80,0xff, +0xd0,0xf4,0xeb,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00, +0x00,0x92,0xcf,0x00,0xff,0xff,0x00,0x00,0x00,0x9a,0xcf,0x00,0x17,0x00,0x94,0x10, +0x00,0x00, + +}; diff --git a/os/pc/apbootstrap.s b/os/pc/apbootstrap.s new file mode 100644 index 00000000..3887d71d --- /dev/null +++ b/os/pc/apbootstrap.s @@ -0,0 +1,110 @@ +#include "mem.h" + +#define NOP BYTE $0x90 /* NOP */ +#define LGDT(gdtptr) BYTE $0x0F; /* LGDT */ \ + BYTE $0x01; BYTE $0x16; \ + WORD $gdtptr +#define FARJUMP16(s, o) BYTE $0xEA; /* far jump to ptr16:16 */ \ + WORD $o; WORD $s; \ + NOP; NOP; NOP +#define FARJUMP32(s, o) BYTE $0x66; /* far jump to ptr32:16 */ \ + BYTE $0xEA; LONG $o; WORD $s + +#define DELAY BYTE $0xEB; /* JMP .+2 */ \ + BYTE $0x00 +#define INVD BYTE $0x0F; BYTE $0x08 +#define WBINVD BYTE $0x0F; BYTE $0x09 + +/* + * Macros for calculating offsets within the page directory base + * and page tables. Note that these are assembler-specific hence + * the '<<2'. + */ +#define PDO(a) (((((a))>>22) & 0x03FF)<<2) +#define PTO(a) (((((a))>>12) & 0x03FF)<<2) + +/* + * Start an Application Processor. This must be placed on a 4KB boundary + * somewhere in the 1st MB of conventional memory (APBOOTSTRAP). However, + * due to some shortcuts below it's restricted further to within the 1st + * 64KB. The AP starts in real-mode, with + * CS selector set to the startup memory address/16; + * CS base set to startup memory address; + * CS limit set to 64KB; + * CPL and IP set to 0. + */ +TEXT apbootstrap(SB), $0 + FARJUMP16(0, _apbootstrap(SB)) +TEXT _apvector(SB), $0 /* address APBOOTSTRAP+0x08 */ + LONG $0 +TEXT _appdb(SB), $0 /* address APBOOTSTRAP+0x0C */ + LONG $0 +TEXT _apapic(SB), $0 /* address APBOOTSTRAP+0x10 */ + LONG $0 +TEXT _apbootstrap(SB), $0 /* address APBOOTSTRAP+0x14 */ + MOVW CS, AX + MOVW AX, DS /* initialise DS */ + + LGDT(gdtptr(SB)) /* load a basic gdt */ + + MOVL CR0, AX + ORL $1, AX + MOVL AX, CR0 /* turn on protected mode */ + DELAY /* JMP .+2 */ + + BYTE $0xB8; WORD $SELECTOR(1, SELGDT, 0)/* MOVW $SELECTOR(1, SELGDT, 0), AX */ + MOVW AX, DS + MOVW AX, ES + MOVW AX, FS + MOVW AX, GS + MOVW AX, SS + + FARJUMP32(SELECTOR(2, SELGDT, 0), _ap32-KZERO(SB)) + +/* + * For Pentiums and higher, the code that enables paging must come from + * pages that are identity mapped. + * To this end double map KZERO at virtual 0 and undo the mapping once virtual + * nirvana has been obtained. + */ +TEXT _ap32(SB), $0 + MOVL _appdb-KZERO(SB), CX /* physical address of PDB */ + MOVL (PDO(KZERO))(CX), DX /* double-map KZERO at 0 */ + MOVL DX, (PDO(0))(CX) + MOVL CX, CR3 /* load and flush the mmu */ + + MOVL CR0, DX + ORL $0x80010000, DX /* PG|WP */ + ANDL $~0x6000000A, DX /* ~(CD|NW|TS|MP) */ + + MOVL $_appg(SB), AX + MOVL DX, CR0 /* turn on paging */ + JMP* AX + +TEXT _appg(SB), $0 + MOVL CX, AX /* physical address of PDB */ + ORL $KZERO, AX + MOVL $0, (PDO(0))(AX) /* undo double-map of KZERO at 0 */ + MOVL CX, CR3 /* load and flush the mmu */ + + MOVL $(MACHADDR+MACHSIZE-4), SP + + MOVL $0, AX + PUSHL AX + POPFL + + MOVL _apapic(SB), AX + MOVL AX, (SP) + MOVL _apvector(SB), AX + CALL* AX +_aphalt: + HLT + JMP _aphalt + +TEXT gdt(SB), $0 + LONG $0x0000; LONG $0 + LONG $0xFFFF; LONG $(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW) + LONG $0xFFFF; LONG $(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR) +TEXT gdtptr(SB), $0 + WORD $(3*8-1) + LONG $gdt-KZERO(SB) diff --git a/os/pc/apic.c b/os/pc/apic.c new file mode 100644 index 00000000..ffb24584 --- /dev/null +++ b/os/pc/apic.c @@ -0,0 +1,378 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "mp.h" + +enum { /* Local APIC registers */ + LapicID = 0x0020, /* ID */ + LapicVER = 0x0030, /* Version */ + LapicTPR = 0x0080, /* Task Priority */ + LapicAPR = 0x0090, /* Arbitration Priority */ + LapicPPR = 0x00A0, /* Processor Priority */ + LapicEOI = 0x00B0, /* EOI */ + LapicLDR = 0x00D0, /* Logical Destination */ + LapicDFR = 0x00E0, /* Destination Format */ + LapicSVR = 0x00F0, /* Spurious Interrupt Vector */ + LapicISR = 0x0100, /* Interrupt Status (8 registers) */ + LapicTMR = 0x0180, /* Trigger Mode (8 registers) */ + LapicIRR = 0x0200, /* Interrupt Request (8 registers) */ + LapicESR = 0x0280, /* Error Status */ + LapicICRLO = 0x0300, /* Interrupt Command */ + LapicICRHI = 0x0310, /* Interrupt Command [63:32] */ + LapicTIMER = 0x0320, /* Local Vector Table 0 (TIMER) */ + LapicPCINT = 0x0340, /* Performance COunter LVT */ + LapicLINT0 = 0x0350, /* Local Vector Table 1 (LINT0) */ + LapicLINT1 = 0x0360, /* Local Vector Table 2 (LINT1) */ + LapicERROR = 0x0370, /* Local Vector Table 3 (ERROR) */ + LapicTICR = 0x0380, /* Timer Initial Count */ + LapicTCCR = 0x0390, /* Timer Current Count */ + LapicTDCR = 0x03E0, /* Timer Divide Configuration */ +}; + +enum { /* LapicSVR */ + LapicENABLE = 0x00000100, /* Unit Enable */ + LapicFOCUS = 0x00000200, /* Focus Processor Checking Disable */ +}; + +enum { /* LapicICRLO */ + /* [14] IPI Trigger Mode Level (RW) */ + LapicDEASSERT = 0x00000000, /* Deassert level-sensitive interrupt */ + LapicASSERT = 0x00004000, /* Assert level-sensitive interrupt */ + + /* [17:16] Remote Read Status */ + LapicINVALID = 0x00000000, /* Invalid */ + LapicWAIT = 0x00010000, /* In-Progress */ + LapicVALID = 0x00020000, /* Valid */ + + /* [19:18] Destination Shorthand */ + LapicFIELD = 0x00000000, /* No shorthand */ + LapicSELF = 0x00040000, /* Self is single destination */ + LapicALLINC = 0x00080000, /* All including self */ + LapicALLEXC = 0x000C0000, /* All Excluding self */ +}; + +enum { /* LapicESR */ + LapicSENDCS = 0x00000001, /* Send CS Error */ + LapicRCVCS = 0x00000002, /* Receive CS Error */ + LapicSENDACCEPT = 0x00000004, /* Send Accept Error */ + LapicRCVACCEPT = 0x00000008, /* Receive Accept Error */ + LapicSENDVECTOR = 0x00000020, /* Send Illegal Vector */ + LapicRCVVECTOR = 0x00000040, /* Receive Illegal Vector */ + LapicREGISTER = 0x00000080, /* Illegal Register Address */ +}; + +enum { /* LapicTIMER */ + /* [17] Timer Mode (RW) */ + LapicONESHOT = 0x00000000, /* One-shot */ + LapicPERIODIC = 0x00020000, /* Periodic */ + + /* [19:18] Timer Base (RW) */ + LapicCLKIN = 0x00000000, /* use CLKIN as input */ + LapicTMBASE = 0x00040000, /* use TMBASE */ + LapicDIVIDER = 0x00080000, /* use output of the divider */ +}; + +enum { /* LapicTDCR */ + LapicX2 = 0x00000000, /* divide by 2 */ + LapicX4 = 0x00000001, /* divide by 4 */ + LapicX8 = 0x00000002, /* divide by 8 */ + LapicX16 = 0x00000003, /* divide by 16 */ + LapicX32 = 0x00000008, /* divide by 32 */ + LapicX64 = 0x00000009, /* divide by 64 */ + LapicX128 = 0x0000000A, /* divide by 128 */ + LapicX1 = 0x0000000B, /* divide by 1 */ +}; + +static ulong* lapicbase; + +struct +{ + uvlong hz; + ulong max; + ulong min; + ulong div; +} lapictimer; + +static int +lapicr(int r) +{ + return *(lapicbase+(r/sizeof(*lapicbase))); +} + +static void +lapicw(int r, int data) +{ + *(lapicbase+(r/sizeof(*lapicbase))) = data; + data = *(lapicbase+(LapicID/sizeof(*lapicbase))); + USED(data); +} + +void +lapiconline(void) +{ + /* + * Reload the timer to de-synchronise the processors, + * then lower the task priority to allow interrupts to be + * accepted by the APIC. + */ + microdelay((TK2MS(1)*1000/conf.nmach) * m->machno); + lapicw(LapicTICR, lapictimer.max); + lapicw(LapicTIMER, LapicCLKIN|LapicPERIODIC|(VectorPIC+IrqTIMER)); + + lapicw(LapicTPR, 0); +} + +/* + * use the i8253 clock to figure out our lapic timer rate. + */ +static void +lapictimerinit(void) +{ + uvlong x, v, hz; + + v = m->cpuhz/1000; + lapicw(LapicTDCR, LapicX1); + lapicw(LapicTIMER, ApicIMASK|LapicCLKIN|LapicONESHOT|(VectorPIC+IrqTIMER)); + + if(lapictimer.hz == 0ULL){ + x = fastticks(&hz); + x += hz/10; + lapicw(LapicTICR, 0xffffffff); + do{ + v = fastticks(nil); + }while(v < x); + + lapictimer.hz = (0xffffffffUL-lapicr(LapicTCCR))*10; + lapictimer.max = lapictimer.hz/HZ; + lapictimer.min = lapictimer.hz/(100*HZ); + + if(lapictimer.hz > hz) + panic("lapic clock faster than cpu clock"); + lapictimer.div = hz/lapictimer.hz; + } +} + +void +lapicinit(Apic* apic) +{ + ulong r, lvt; + + if(lapicbase == 0) + lapicbase = apic->addr; + + lapicw(LapicDFR, 0xFFFFFFFF); + r = (lapicr(LapicID)>>24) & 0xFF; + lapicw(LapicLDR, (1<cpuidax & 0xFFF){ + case 0x526: /* stepping cB1 */ + case 0x52B: /* stepping E0 */ + case 0x52C: /* stepping cC0 */ + wrmsr(0x0E, 1<<14); /* TR12 */ + break; + } + + /* + * Set the local interrupts. It's likely these should just be + * masked off for SMP mode as some Pentium Pros have problems if + * LINT[01] are set to ExtINT. + * Acknowledge any outstanding interrupts. + lapicw(LapicLINT0, apic->lintr[0]); + lapicw(LapicLINT1, apic->lintr[1]); + */ + lapiceoi(0); + + lvt = (lapicr(LapicVER)>>16) & 0xFF; + if(lvt >= 4) + lapicw(LapicPCINT, ApicIMASK); + lapicw(LapicERROR, VectorPIC+IrqERROR); + lapicw(LapicESR, 0); + lapicr(LapicESR); + + /* + * Issue an INIT Level De-Assert to synchronise arbitration ID's. + */ + lapicw(LapicICRHI, 0); + lapicw(LapicICRLO, LapicALLINC|ApicLEVEL|LapicDEASSERT|ApicINIT); + while(lapicr(LapicICRLO) & ApicDELIVS) + ; + + /* + * Do not allow acceptance of interrupts until all initialisation + * for this processor is done. For the bootstrap processor this can be + * early duing initialisation. For the application processors this should + * be after the bootstrap processor has lowered priority and is accepting + * interrupts. + lapicw(LapicTPR, 0); + */ +} + +void +lapicstartap(Apic* apic, int v) +{ + int crhi, i; + + crhi = apic->apicno<<24; + lapicw(LapicICRHI, crhi); + lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicASSERT|ApicINIT); + microdelay(200); + lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicDEASSERT|ApicINIT); + delay(10); + + for(i = 0; i < 2; i++){ + lapicw(LapicICRHI, crhi); + lapicw(LapicICRLO, LapicFIELD|ApicEDGE|ApicSTARTUP|(v/BY2PG)); + microdelay(200); + } +} + +void +lapicerror(Ureg*, void*) +{ + int esr; + + lapicw(LapicESR, 0); + esr = lapicr(LapicESR); + switch(m->cpuidax & 0xFFF){ + case 0x526: /* stepping cB1 */ + case 0x52B: /* stepping E0 */ + case 0x52C: /* stepping cC0 */ + return; + } + print("cpu%d: lapicerror: 0x%8.8uX\n", m->machno, esr); +} + +void +lapicspurious(Ureg*, void*) +{ + print("cpu%d: lapicspurious\n", m->machno); +} + +int +lapicisr(int v) +{ + int isr; + + isr = lapicr(LapicISR + (v/32)); + + return isr & (1<<(v%32)); +} + +int +lapiceoi(int v) +{ + lapicw(LapicEOI, 0); + + return v; +} + +void +lapicicrw(int hi, int lo) +{ + lapicw(LapicICRHI, hi); + lapicw(LapicICRLO, lo); +} + +void +ioapicrdtr(Apic* apic, int sel, int* hi, int* lo) +{ + ulong *iowin; + + iowin = apic->addr+(0x10/sizeof(ulong)); + sel = IoapicRDT + 2*sel; + + lock(apic); + *apic->addr = sel+1; + if(hi) + *hi = *iowin; + *apic->addr = sel; + if(lo) + *lo = *iowin; + unlock(apic); +} + +void +ioapicrdtw(Apic* apic, int sel, int hi, int lo) +{ + ulong *iowin; + + iowin = apic->addr+(0x10/sizeof(ulong)); + sel = IoapicRDT + 2*sel; + + lock(apic); + *apic->addr = sel+1; + *iowin = hi; + *apic->addr = sel; + *iowin = lo; + unlock(apic); +} + +void +ioapicinit(Apic* apic, int apicno) +{ + int hi, lo, v; + ulong *iowin; + + /* + * Initialise the I/O APIC. + * The MultiProcessor Specification says it is the responsibility + * of the O/S to set the APIC id. + * Make sure interrupts are all masked off for now. + */ + iowin = apic->addr+(0x10/sizeof(ulong)); + lock(apic); + *apic->addr = IoapicVER; + apic->mre = (*iowin>>16) & 0xFF; + + *apic->addr = IoapicID; + *iowin = apicno<<24; + unlock(apic); + + hi = 0; + lo = ApicIMASK; + for(v = 0; v <= apic->mre; v++) + ioapicrdtw(apic, v, hi, lo); +} + +void +lapictimerset(uvlong next) +{ + vlong period; + int x; + + x = splhi(); + lock(&m->apictimerlock); + + period = lapictimer.max; + if(next != 0){ + period = next - fastticks(nil); + period /= lapictimer.div; + + if(period < lapictimer.min) + period = lapictimer.min; + else if(period > lapictimer.max - lapictimer.min) + period = lapictimer.max; + } + lapicw(LapicTICR, period); + + unlock(&m->apictimerlock); + splx(x); +} + +void +lapicclock(Ureg *u, void*) +{ + timerintr(u, 0); +} diff --git a/os/pc/apm.c b/os/pc/apm.c new file mode 100644 index 00000000..2ea18b4d --- /dev/null +++ b/os/pc/apm.c @@ -0,0 +1,151 @@ +/* + * Interface to Advanced Power Management 1.2 BIOS + * + * This is, in many ways, a giant hack, and when things settle down + * a bit and standardize, hopefully we can write a driver that deals + * more directly with the hardware and thus might be a bit cleaner. + * + * ACPI might be the answer, but at the moment this is simpler + * and more widespread. + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +extern int apmfarcall(ushort, ulong, Ureg*); /* apmjump.s */ + +static int +getreg(ulong *reg, ISAConf *isa, char *name) +{ + int i; + int nl; + + nl = strlen(name); + for(i=0; inopt; i++){ + if(cistrncmp(isa->opt[i], name, nl)==0 && isa->opt[i][nl] == '='){ + *reg = strtoul(isa->opt[i]+nl+1, nil, 16); + return 0; + } + } + return -1; +} + +/* + * Segment descriptors look like this. + * + * d1: [base 31:24] [gran] [is32bit] [0] [unused] [limit 19:16] + [present] [privlev] [type 3:0] [base 23:16] + * d0: [base 15:00] [limit 15:00] + * + * gran is 0 for 1-byte granularity, 1 for 4k granularity + * type is 0 for system segment, 1 for code/data. + * + * clearly we know way too much about the memory unit. + * however, knowing this much about the memory unit + * means that the memory unit need not know anything + * about us. + * + * what a crock. + */ +static void +setgdt(int sel, ulong base, ulong limit, int flag) +{ + if(sel < 0 || sel >= NGDT) + panic("setgdt"); + + base = (ulong)KADDR(base); + m->gdt[sel].d0 = (base<<16) | (limit&0xFFFF); + m->gdt[sel].d1 = (base&0xFF000000) | (limit&0x000F0000) | + ((base>>16)&0xFF) | SEGP | SEGPL(0) | flag; +} + +static ulong ax, cx, dx, di, ebx, esi; +static Ureg apmu; +static long +apmread(Chan*, void *a, long n, vlong off) +{ + if(off < 0) + error("badarg"); + + if(n+off > sizeof apmu) + n = sizeof apmu - off; + if(n <= 0) + return 0; + memmove(a, (char*)&apmu+off, n); + return n; +} + +static long +apmwrite(Chan*, void *a, long n, vlong off) +{ + int s; + if(off || n != sizeof apmu) + error("write a Ureg"); + + memmove(&apmu, a, sizeof apmu); + s = splhi(); + apmfarcall(APMCSEL, ebx, &apmu); + splx(s); + return n; +} + +void +apmlink(void) +{ + ISAConf isa; + char *s; + + if(isaconfig("apm", 0, &isa) == 0) + return; + + /* + * APM info passed from boot loader. + * Now we need to set up the GDT entries for APM. + * + * AX = 32-bit code segment base address + * EBX = 32-bit code segment offset + * CX = 16-bit code segment base address + * DX = 32-bit data segment base address + * ESI = <16-bit code segment length> <32-bit code segment length> (hi then lo) + * DI = 32-bit data segment length + */ + + if(getreg(&ax, &isa, s="ax") < 0 + || getreg(&ebx, &isa, s="ebx") < 0 + || getreg(&cx, &isa, s="cx") < 0 + || getreg(&dx, &isa, s="dx") < 0 + || getreg(&esi, &isa, s="esi") < 0 + || getreg(&di, &isa, s="di") < 0){ + print("apm: missing register %s\n", s); + return; + } + + /* + * The NEC Versa SX bios does not report the correct 16-bit code + * segment length when loaded directly from mbr -> 9load (as compared + * with going through ld.com). We'll make both code segments 64k-1 bytes. + */ + esi = 0xFFFFFFFF; + + /* + * We are required by the BIOS to set up three consecutive segments, + * one for the APM 32-bit code, one for the APM 16-bit code, and + * one for the APM data. The BIOS handler uses the code segment it + * get called with to determine the other two segment selector. + */ + setgdt(APMCSEG, ax<<4, ((esi&0xFFFF)-1)&0xFFFF, SEGEXEC|SEGR|SEGD); + setgdt(APMCSEG16, cx<<4, ((esi>>16)-1)&0xFFFF, SEGEXEC|SEGR); + setgdt(APMDSEG, dx<<4, (di-1)&0xFFFF, SEGDATA|SEGW|SEGD); + + addarchfile("apm", 0660, apmread, apmwrite); + +print("apm0: configured cbase %.8lux off %.8lux\n", ax<<4, ebx); + + return; +} + diff --git a/os/pc/apmjump.s b/os/pc/apmjump.s new file mode 100644 index 00000000..6dbd19d0 --- /dev/null +++ b/os/pc/apmjump.s @@ -0,0 +1,98 @@ +/* + * Far call, absolute indirect. + * The argument is the offset. + * We use a global structure for the jump params, + * so this is *not* reentrant or thread safe. + */ + +#include "mem.h" + +#define SSOVERRIDE BYTE $0x36 +#define CSOVERRIDE BYTE $0x2E +#define RETF BYTE $0xCB + +GLOBL apmjumpstruct+0(SB), $8 + +TEXT fortytwo(SB), $0 + MOVL $42, AX + RETF + +TEXT getcs(SB), $0 + PUSHL CS + POPL AX + RET + +TEXT apmfarcall(SB), $0 + /* + * We call push and pop ourselves. + * As soon as we do the first push or pop, + * we can't use FP anymore. + */ + MOVL off+4(FP), BX + MOVL seg+0(FP), CX + MOVL BX, apmjumpstruct+0(SB) + MOVL CX, apmjumpstruct+4(SB) + + /* load necessary registers from Ureg */ + MOVL ureg+8(FP), DI + MOVL 28(DI), AX + MOVL 16(DI), BX + MOVL 24(DI), CX + MOVL 20(DI), DX + + /* save registers, segments */ + PUSHL DS + PUSHL ES + PUSHL FS + PUSHL GS + PUSHL BP + PUSHL DI + + /* + * paranoia: zero the segments, since it's the + * BIOS's responsibility to initialize them. + * (trick picked up from Linux driver). + PUSHL DX + XORL DX, DX + PUSHL DX + POPL DS + PUSHL DX + POPL ES + PUSHL DX + POPL FS + PUSHL DX + POPL GS + POPL DX + */ + + PUSHL $APMDSEG + POPL DS + + /* + * The actual call. + */ + CSOVERRIDE; BYTE $0xFF; BYTE $0x1D + LONG $apmjumpstruct+0(SB) + + /* restore segments, registers */ + POPL DI + POPL BP + POPL GS + POPL FS + POPL ES + POPL DS + + PUSHFL + POPL 64(DI) + + /* store interesting registers back in Ureg */ + MOVL AX, 28(DI) + MOVL BX, 16(DI) + MOVL CX, 24(DI) + MOVL DX, 20(DI) + MOVL SI, 4(DI) + + PUSHFL + POPL AX + ANDL $1, AX /* carry flag */ + RET diff --git a/os/pc/archmp.c b/os/pc/archmp.c new file mode 100644 index 00000000..fbbda091 --- /dev/null +++ b/os/pc/archmp.c @@ -0,0 +1,138 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "mp.h" + +#define cpuserver 1 + +_MP_ *_mp_; + +static _MP_* +mpscan(uchar *addr, int len) +{ + uchar *e, *p, sum; + int i; + + e = addr+len; + for(p = addr; p < e; p += sizeof(_MP_)){ + if(memcmp(p, "_MP_", 4)) + continue; + sum = 0; + for(i = 0; i < sizeof(_MP_); i++) + sum += p[i]; + if(sum == 0) + return (_MP_*)p; + } + return 0; +} + +static _MP_* +mpsearch(void) +{ + uchar *bda; + ulong p; + _MP_ *mp; + + /* + * Search for the MP Floating Pointer Structure: + * 1) in the first KB of the EBDA; + * 2) in the last KB of system base memory; + * 3) in the BIOS ROM between 0xE0000 and 0xFFFFF. + */ + bda = KADDR(0x400); + if((p = (bda[0x0F]<<8)|bda[0x0E])){ + if(mp = mpscan(KADDR(p), 1024)) + return mp; + } + else{ + p = ((bda[0x14]<<8)|bda[0x13])*1024; + if(mp = mpscan(KADDR(p-1024), 1024)) + return mp; + } + return mpscan(KADDR(0xF0000), 0x10000); +} + +static int identify(void); + +PCArch archmp = { +.id= "_MP_", +.ident= identify, +.reset= mpshutdown, +.intrinit= mpinit, +.intrenable= mpintrenable, +.fastclock= i8253read, +.timerset= lapictimerset, +}; + +static int +identify(void) +{ + PCMP *pcmp; + uchar *p, sum; + ulong length; + + if(getconf("*nomp")) + return 1; + + /* + * Search for an MP configuration table. For now, + * don't accept the default configurations (physaddr == 0). + * Check for correct signature, calculate the checksum and, + * if correct, check the version. + * To do: check extended table checksum. + */ + if((_mp_ = mpsearch()) == 0 || _mp_->physaddr == 0) + return 1; + + pcmp = KADDR(_mp_->physaddr); + if(memcmp(pcmp, "PCMP", 4)) + return 1; + + length = pcmp->length; + sum = 0; + for(p = (uchar*)pcmp; length; length--) + sum += *p++; + + if(sum || (pcmp->version != 1 && pcmp->version != 4)) + return 1; + + if(cpuserver && m->havetsc) + archmp.fastclock = tscticks; + return 0; +} + +Lock mpsynclock; + +void +syncclock(void) +{ + uvlong x; + + if(arch->fastclock != tscticks) + return; + + if(m->machno == 0){ + wrmsr(0x10, 0); + m->tscticks = 0; + } else { + x = MACHP(0)->tscticks; + while(x == MACHP(0)->tscticks) + ; + wrmsr(0x10, MACHP(0)->tscticks); + cycles(&m->tscticks); + } +} + +uvlong +tscticks(uvlong *hz) +{ + if(hz != nil) + *hz = m->cpuhz; + + cycles(&m->tscticks); /* Uses the rdtsc instruction */ + return m->tscticks; +} diff --git a/os/pc/audio.h b/os/pc/audio.h new file mode 100644 index 00000000..d06c7bcc --- /dev/null +++ b/os/pc/audio.h @@ -0,0 +1,15 @@ +enum +{ + Bufsize = 1024, /* 5.8 ms each, must be power of two */ + Nbuf = 128, /* .74 seconds total */ + Dma = 6, + IrqAUDIO = 7, + SBswab = 0, +}; + +#define seteisadma(a, b) dmainit(a, Bufsize); +#define CACHELINESZ 8 +#define UNCACHED(type, v) (type*)((ulong)(v)) + +#define Int0vec +#define setvec(v, f, a) intrenable(v, f, a, BUSUNKNOWN, "audio") diff --git a/os/pc/cga.c b/os/pc/cga.c new file mode 100644 index 00000000..6365d44c --- /dev/null +++ b/os/pc/cga.c @@ -0,0 +1,127 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +enum { + Black, + Blue, + Green, + Cyan, + Red, + Magenta, + Brown, + Grey, + + Bright = 0x08, + Blinking = 0x80, + + Yellow = Bright|Brown, + White = Bright|Grey, +}; + +enum { + Width = 80*2, + Height = 25, + + Attr = (Black<<4)|Grey, /* high nibble background + * low foreground + */ +}; + +#define CGASCREENBASE ((uchar*)KADDR(0xB8000)) + +static int cgapos; +static Lock cgascreenlock; + +static uchar +cgaregr(int index) +{ + outb(0x3D4, index); + return inb(0x3D4+1) & 0xFF; +} + +static void +cgaregw(int index, int data) +{ + outb(0x3D4, index); + outb(0x3D4+1, data); +} + +static void +movecursor(void) +{ + cgaregw(0x0E, (cgapos/2>>8) & 0xFF); + cgaregw(0x0F, cgapos/2 & 0xFF); + CGASCREENBASE[cgapos+1] = Attr; +} + +static void +cgascreenputc(int c) +{ + int i; + uchar *p; + + if(c == '\n'){ + cgapos = cgapos/Width; + cgapos = (cgapos+1)*Width; + } + else if(c == '\t'){ + i = 8 - ((cgapos/2)&7); + while(i-->0) + cgascreenputc(' '); + } + else if(c == '\b'){ + if(cgapos >= 2) + cgapos -= 2; + cgascreenputc(' '); + cgapos -= 2; + } + else{ + CGASCREENBASE[cgapos++] = c; + CGASCREENBASE[cgapos++] = Attr; + } + if(cgapos >= Width*Height){ + memmove(CGASCREENBASE, &CGASCREENBASE[Width], Width*(Height-1)); + p = &CGASCREENBASE[Width*(Height-1)]; + for(i=0; i 0) + cgascreenputc(*s++); + + unlock(&cgascreenlock); +} + +void +screeninit(void) +{ + + cgapos = cgaregr(0x0E)<<8; + cgapos |= cgaregr(0x0F); + cgapos *= 2; + + screenputs = cgascreenputs; +} diff --git a/os/pc/cgamemscr.c b/os/pc/cgamemscr.c new file mode 100644 index 00000000..7509614a --- /dev/null +++ b/os/pc/cgamemscr.c @@ -0,0 +1,203 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include +#include +#include + +enum { + Width = 160, + Height = 25, + + Attr = 7, /* white on black */ +}; + +#define CGASCREENBASE ((uchar*)KADDR(0xB8000)) + +static int cgapos; +static int screeninitdone; +static Lock cgascreenlock; +void (*vgascreenputc)(char*); + +static uchar +cgaregr(int index) +{ + outb(0x3D4, index); + return inb(0x3D4+1) & 0xFF; +} + +static void +cgaregw(int index, int data) +{ + outb(0x3D4, index); + outb(0x3D4+1, data); +} + +static void +movecursor(void) +{ + cgaregw(0x0E, (cgapos/2>>8) & 0xFF); + cgaregw(0x0F, cgapos/2 & 0xFF); + CGASCREENBASE[cgapos+1] = Attr; +} + +static void +cgascreenputc(int c) +{ + int i; + + if(c == '\n'){ + cgapos = cgapos/Width; + cgapos = (cgapos+1)*Width; + } + else if(c == '\t'){ + i = 8 - ((cgapos/2)&7); + while(i-->0) + cgascreenputc(' '); + } + else if(c == '\b'){ + if(cgapos >= 2) + cgapos -= 2; + cgascreenputc(' '); + cgapos -= 2; + } + else{ + CGASCREENBASE[cgapos++] = c; + CGASCREENBASE[cgapos++] = Attr; + } + if(cgapos >= Width*Height){ + memmove(CGASCREENBASE, &CGASCREENBASE[Width], Width*(Height-1)); + memset(&CGASCREENBASE[Width*(Height-1)], 0, Width); + cgapos = Width*(Height-1); + } + movecursor(); +} + +void +screeninit(void) +{ + memimageinit(); + cgapos = cgaregr(0x0E)<<8; + cgapos |= cgaregr(0x0F); + cgapos *= 2; + screeninitdone = 1; +} + +void +cgascreenputs(char* s, int n) +{ + int i; + Rune r; + char buf[4]; + + if(!islo()){ + if(!canlock(&cgascreenlock)) + return; + } + else + lock(&cgascreenlock); + + if(vgascreenputc == nil){ + while(n-- > 0) + cgascreenputc(*s++); + unlock(&cgascreenlock); + return; + } + + while(n > 0) { + i = chartorune(&r, s); + if(i == 0){ + s++; + --n; + continue; + } + memmove(buf, s, i); + buf[i] = 0; + n -= i; + s += i; + vgascreenputc(buf); + } + + unlock(&cgascreenlock); +} + +void +cursorenable(void) +{ +} + +void +cursordisable(void) +{ +} + +typedef struct Drawcursor Drawcursor; + + + +void +cursorupdate(Rectangle r) +{ + USED(r); +} + +void +drawcursor(Drawcursor *c) +{ + USED(c); +} + +uchar* +attachscreen(Rectangle *r, ulong *chan, int* d, int *width, int *softscreen) +{ + static Rectangle screenr = {0, 0, 0, 0}; + static uchar *bdata; + if (bdata == nil) + if ((bdata = malloc(1)) == nil) + return nil; + *r = screenr; + *chan = RGB24; + *d = chantodepth(RGB24); + *width = 0; + *softscreen = 0; + return bdata; +} + +void +flushmemscreen(Rectangle r) +{ + USED(r); +} + +void +blankscreen(int i) +{ + USED(i); +} + +void +getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb) +{ + USED(p); + USED(pr); + USED(pg); + USED(pb); + +} + +int +setcolor(ulong p, ulong r, ulong g, ulong b) +{ + USED(p); + USED(r); + USED(g); + USED(b); + return ~0; +} + +void (*screenputs)(char*, int) = cgascreenputs; diff --git a/os/pc/crystal.h b/os/pc/crystal.h new file mode 100644 index 00000000..63152c8f --- /dev/null +++ b/os/pc/crystal.h @@ -0,0 +1,1118 @@ +/* + * Micro code for the crystal musicam decoder on the Boffin MPEG decoder + */ +static +uchar crystal[] = +{ + 0x10,0x10,0x06,0x00,0x00,0x00,0x00,0x00,0x03,0xa0,0x00,0x1a,0x9f,0x00,0x39,0x5f, + 0x00,0xfe,0x9f,0x02,0x84,0x1f,0x03,0x35,0xbf,0x12,0x4e,0x1f,0x24,0xa3,0xc0,0xed, + 0xb1,0xe1,0x03,0x35,0xbf,0xfd,0x7b,0xe1,0x00,0xfe,0x9f,0xff,0xc6,0xa1,0x00,0x1a, + 0x9f,0xff,0xfc,0x60,0xff,0xff,0xe0,0x00,0x03,0x40,0x00,0x19,0xff,0x00,0x32,0x1f, + 0x01,0x01,0xe0,0x02,0x56,0x7f,0x03,0x7b,0xbf,0x11,0x66,0xff,0x24,0x9d,0xff,0xec, + 0xcb,0x00,0x02,0xe8,0xdf,0xfd,0x4e,0x61,0x00,0xf9,0xff,0xff,0xbf,0x20,0x00,0x1b, + 0x3f,0xff,0xfc,0x21,0xff,0xff,0xe0,0x00,0x03,0x00,0x00,0x19,0x3f,0x00,0x2b,0x60, + 0x01,0x03,0xff,0x02,0x29,0x20,0x03,0xba,0xff,0x10,0x7f,0xdf,0x24,0x8c,0xff,0xeb, + 0xe5,0x01,0x02,0x95,0x00,0xfd,0x21,0x20,0x00,0xf3,0xff,0xff,0xb7,0x61,0x00,0x1b, + 0xbf,0xff,0xfb,0xa0,0xff,0xff,0xe0,0x00,0x02,0xa0,0x00,0x18,0x80,0x00,0x24,0xc0, + 0x01,0x04,0xe0,0x01,0xfb,0xe0,0x03,0xf3,0x7f,0x0f,0x99,0x5f,0x24,0x70,0xc0,0xeb, + 0x00,0x41,0x02,0x3a,0x20,0xfc,0xf4,0x61,0x00,0xec,0xa0,0xff,0xaf,0x60,0x00,0x1c, + 0x20,0xff,0xfb,0x40,0xff,0xff,0xe0,0x00,0x02,0x60,0x00,0x17,0xc0,0x00,0x1e,0x80, + 0x01,0x04,0x9f,0x01,0xcf,0x1f,0x04,0x25,0x80,0x0e,0xb3,0xff,0x24,0x49,0x20,0xea, + 0x1d,0x60,0x01,0xd7,0xff,0xfc,0xc8,0x61,0x00,0xe3,0xc0,0xff,0xa7,0x21,0x00,0x1c, + 0x5f,0xff,0xfa,0xe1,0xff,0xff,0xe0,0x00,0x02,0x1f,0x00,0x16,0xdf,0x00,0x18,0x9f, + 0x01,0x03,0x5f,0x01,0xa2,0xdf,0x04,0x50,0xff,0x0d,0xd0,0x20,0x24,0x16,0x7f,0xe9, + 0x3c,0xe0,0x01,0x6e,0xe0,0xfc,0x9d,0x21,0x00,0xd9,0x5f,0xff,0x9e,0xa0,0x00,0x1c, + 0x80,0xff,0xfa,0x60,0xff,0xff,0xe0,0x00,0x02,0x00,0x00,0x16,0x00,0x00,0x13,0x20, + 0x01,0x01,0x1f,0x01,0x77,0x7f,0x04,0x76,0x5f,0x0c,0xee,0x40,0x23,0xd8,0xdf,0xe8, + 0x5f,0x40,0x00,0xfe,0x9f,0xfc,0x73,0x21,0x00,0xcd,0x7f,0xff,0x96,0x01,0x00,0x1c, + 0x80,0xff,0xf9,0xe0,0xff,0xff,0xc0,0x00,0x01,0xbf,0x00,0x15,0x1f,0x00,0x0d,0xe0, + 0x00,0xfd,0xff,0x01,0x4c,0xdf,0x04,0x95,0xa0,0x0c,0x0e,0xbf,0x23,0x90,0x5f,0xe7, + 0x84,0xe1,0x00,0x87,0x40,0xfc,0x4a,0x60,0x00,0xbf,0xdf,0xff,0x8d,0x21,0x00,0x1c, + 0x5f,0xff,0xf9,0x60,0xff,0xff,0xc0,0x00,0x01,0x9f,0x00,0x14,0x1f,0x00,0x09,0x00, + 0x00,0xfa,0x20,0x01,0x23,0x40,0x04,0xaf,0x00,0x0b,0x32,0x1f,0x23,0x3d,0x20,0xe6, + 0xae,0x61,0x00,0x08,0xbf,0xfc,0x23,0x41,0x00,0xb0,0xc0,0xff,0x84,0x20,0x00,0x1c, + 0x00,0xff,0xf8,0xc0,0xff,0xff,0xc0,0x00,0x01,0x60,0x00,0x13,0x40,0x00,0x04,0x7f, + 0x00,0xf5,0x3f,0x00,0xfa,0xc0,0x04,0xc2,0xbf,0x0a,0x58,0x9f,0x22,0xdf,0x80,0xe5, + 0xdc,0x40,0xff,0x83,0x41,0xfb,0xfd,0xe1,0x00,0xa0,0x00,0xff,0x7b,0x00,0x00,0x1b, + 0x9f,0xff,0xf8,0x20,0xff,0xff,0xc0,0x00,0x01,0x40,0x00,0x12,0x60,0x00,0x00,0x40, + 0x00,0xef,0xdf,0x00,0xd3,0x7f,0x04,0xd0,0xe0,0x09,0x82,0xbf,0x22,0x77,0xc0,0xe5, + 0x0e,0xc0,0xfe,0xf6,0xc1,0xfb,0xda,0xa0,0x00,0x8d,0x5f,0xff,0x71,0xe1,0x00,0x1a, + 0xe0,0xff,0xf7,0x80,0xff,0xff,0xa1,0x00,0x01,0x1f,0x00,0x11,0x60,0xff,0xfc,0x60, + 0x00,0xe9,0xc0,0x00,0xad,0x7f,0x04,0xd9,0xdf,0x08,0xb0,0xe0,0x22,0x05,0xdf,0xe4, + 0x46,0xc1,0xfe,0x63,0x80,0xfb,0xb9,0xa1,0x00,0x79,0x3f,0xff,0x68,0xc0,0x00,0x19, + 0xff,0xff,0xf6,0xe0,0xff,0xff,0xa1,0x00,0x00,0xff,0x00,0x10,0x7f,0xff,0xf8,0xe0, + 0x00,0xe3,0x20,0x00,0x88,0xdf,0x04,0xdd,0xc0,0x07,0xe3,0x5f,0x21,0x8a,0x7f,0xe3, + 0x84,0x61,0xfd,0xc9,0x60,0xfb,0x9b,0x40,0x00,0x63,0x40,0xff,0x5f,0xa1,0x00,0x19, + 0x00,0xff,0xf6,0x21,0xff,0xff,0x81,0x00,0x00,0xe0,0x00,0x0f,0xa0,0xff,0xf5,0xa1, + 0x00,0xdb,0xe0,0x00,0x65,0xbf,0x04,0xdc,0xdf,0x07,0x1a,0x7f,0x21,0x05,0xa0,0xe2, + 0xc8,0x40,0xfd,0x28,0xc0,0xfb,0x7f,0xa1,0x00,0x4b,0x9f,0xff,0x56,0x80,0x00,0x17, + 0x9f,0xff,0xf5,0x61,0xff,0xff,0x81,0x00,0x00,0xe0,0x00,0x0e,0x9f,0xff,0xf2,0xc0, + 0x00,0xd4,0x40,0x00,0x44,0x1f,0x04,0xd7,0x7f,0x06,0x56,0x7f,0x20,0x77,0xc0,0xe2, + 0x12,0xe0,0xfc,0x81,0xc0,0xfb,0x67,0x00,0x00,0x32,0x3f,0xff,0x4d,0x80,0x00,0x16, + 0x20,0xff,0xf4,0xa0,0xff,0xff,0x60,0x00,0x00,0xc0,0x00,0x0d,0xe0,0xff,0xf0,0x21, + 0x00,0xcc,0x3f,0x00,0x23,0xff,0x04,0xcd,0xc0,0x05,0x97,0xe0,0x1f,0xe1,0x40,0xe1, + 0x64,0x80,0xfb,0xd4,0x80,0xfb,0x51,0xe1,0x00,0x17,0x20,0xff,0x44,0xc1,0x00,0x14, + 0x60,0xff,0xf3,0xe0,0xff,0xff,0x60,0x00,0x00,0xa0,0x00,0x0c,0xff,0xff,0xed,0xc1, + 0x00,0xc3,0xdf,0x00,0x05,0xa0,0x04,0xbf,0xdf,0x04,0xde,0xe0,0x1f,0x42,0x60,0xe0, + 0xbd,0xa0,0xfb,0x21,0x20,0xfb,0x40,0x21,0xff,0xfa,0x60,0xff,0x3c,0x21,0x00,0x12, + 0x3f,0xff,0xf3,0x01,0xff,0xff,0x40,0x00,0x00,0xa0,0x00,0x0c,0x20,0xff,0xeb,0xa0, + 0x00,0xbb,0x3f,0xff,0xe8,0xe0,0x04,0xae,0x1f,0x04,0x2b,0x80,0x1e,0x9b,0x80,0xe0, + 0x1e,0xc0,0xfa,0x68,0x20,0xfb,0x32,0x40,0xff,0xdc,0x01,0xff,0x33,0xc1,0x00,0x0f, + 0xdf,0xff,0xf2,0x20,0xff,0xff,0x20,0x00,0x00,0x7f,0x00,0x0b,0x60,0xff,0xe9,0xe0, + 0x00,0xb2,0x80,0xff,0xcd,0xc1,0x04,0x99,0x00,0x03,0x7e,0x40,0x1d,0xed,0x20,0xdf, + 0x88,0x40,0xf9,0xa9,0x81,0xfb,0x28,0x81,0xff,0xbb,0xe1,0xff,0x2b,0xc0,0x00,0x0d, + 0x40,0xff,0xf1,0x61,0xff,0xff,0x20,0x00,0x00,0x7f,0x00,0x0a,0x9f,0xff,0xe8,0x61, + 0x00,0xa9,0x80,0xff,0xb4,0x61,0x04,0x80,0x5f,0x02,0xd7,0x40,0x1d,0x37,0xc0,0xde, + 0xfa,0x60,0xf8,0xe5,0x81,0xfb,0x23,0x21,0xff,0x9a,0x41,0xff,0x24,0x20,0x00,0x0a, + 0x5f,0xff,0xf0,0x60,0xff,0xff,0x01,0x00,0x00,0x5f,0x00,0x09,0xdf,0xff,0xe7,0x00, + 0x00,0xa0,0x5f,0xff,0x9c,0xc0,0x04,0x64,0xc0,0x02,0x36,0xa0,0x1c,0x7b,0x9f,0xde, + 0x75,0x81,0xf8,0x1c,0xa1,0xfb,0x22,0x40,0xff,0x77,0x21,0xff,0x1c,0xe0,0x00,0x07, + 0x20,0xff,0xef,0x81,0xff,0xfe,0xe1,0x00,0x00,0x5f,0x00,0x09,0x20,0xff,0xe6,0x01, + 0x00,0x97,0x40,0xff,0x86,0xc1,0x04,0x46,0x5f,0x01,0x9c,0x80,0x1b,0xb9,0x3f,0xdd, + 0xfa,0x21,0xf7,0x4f,0x20,0xfb,0x26,0x21,0xff,0x52,0x81,0xff,0x16,0x40,0x00,0x03, + 0xa0,0xff,0xee,0xa0,0xff,0xfe,0xc0,0x00,0x00,0x40,0x00,0x08,0x80,0xff,0xe5,0x20, + 0x00,0x8e,0x1f,0xff,0x72,0xa1,0x04,0x25,0x60,0x01,0x09,0x3f,0x1a,0xf1,0x40,0xdd, + 0x88,0x40,0xf6,0x7d,0x41,0xfb,0x2f,0x20,0xff,0x2c,0x81,0xff,0x10,0x21,0xff,0xff, + 0xc0,0xff,0xed,0xa0,0xff,0xfe,0xa0,0x00,0x00,0x40,0x00,0x07,0xe0,0xff,0xe4,0x61, + 0x00,0x85,0x00,0xff,0x60,0x00,0x04,0x02,0x1f,0x00,0x7c,0xbf,0x1a,0x23,0xc0,0xdd, + 0x20,0x80,0xf5,0xa7,0x61,0xfb,0x3d,0x41,0xff,0x05,0x40,0xff,0x0a,0xc1,0xff,0xfb, + 0x81,0xff,0xec,0xc0,0xff,0xfe,0x61,0x00,0x00,0x40,0x00,0x07,0x40,0xff,0xe4,0x00, + 0x00,0x7b,0xe0,0xff,0x4f,0x40,0x03,0xdc,0xbf,0xff,0xf7,0x41,0x19,0x51,0x9f,0xdc, + 0xc2,0xe0,0xf4,0xcd,0xe1,0xfb,0x51,0x00,0xfe,0xdc,0xc0,0xff,0x05,0xe0,0xff,0xf7, + 0x00,0xff,0xeb,0xe1,0xff,0xfe,0x41,0x00,0x00,0x40,0x00,0x06,0xa0,0xff,0xe3,0xa1, + 0x00,0x72,0xdf,0xff,0x40,0x21,0x03,0xb5,0xa0,0xff,0x78,0xc0,0x18,0x7b,0x1f,0xdc, + 0x6f,0xa1,0xf3,0xf1,0x41,0xfb,0x6a,0x60,0xfe,0xb3,0x21,0xff,0x02,0x01,0xff,0xf2, + 0x20,0xff,0xea,0xe1,0xff,0xfe,0x00,0x00,0x00,0x20,0x00,0x06,0x20,0xff,0xe3,0x80, + 0x00,0x69,0xff,0xff,0x32,0x81,0x03,0x8c,0xdf,0xff,0x01,0x61,0x17,0xa0,0xc0,0xdc, + 0x27,0x21,0xf3,0x11,0xc0,0xfb,0x89,0xa1,0xfe,0x88,0x81,0xfe,0xfe,0xe1,0xff,0xec, + 0xe0,0xff,0xea,0x00,0xff,0xfd,0xe1,0x00,0x00,0x20,0x00,0x05,0xa0,0xff,0xe3,0x80, + 0x00,0x61,0x60,0xff,0x26,0xa1,0x03,0x62,0xdf,0xfe,0x91,0x20,0x16,0xc3,0x20,0xdb, + 0xe9,0x81,0xf2,0x2f,0xe0,0xfb,0xaf,0x01,0xfe,0x5d,0x21,0xfe,0xfc,0xa1,0xff,0xe7, + 0x61,0xff,0xe9,0x21,0xff,0xfd,0xa0,0x00,0x00,0x20,0x00,0x05,0x1f,0xff,0xe3,0xa1, + 0x00,0x58,0xdf,0xff,0x1c,0x40,0x03,0x37,0x9f,0xfe,0x28,0x01,0x15,0xe2,0xa0,0xdb, + 0xb6,0xe0,0xf1,0x4c,0x01,0xfb,0xda,0x80,0xfe,0x30,0xe1,0xfe,0xfb,0x61,0xff,0xe1, + 0x80,0xff,0xe8,0x40,0xff,0xfd,0x60,0x00,0x00,0x20,0x00,0x04,0xc0,0xff,0xe3,0xe0, + 0x00,0x50,0xa0,0xff,0x13,0x60,0x03,0x0b,0x9f,0xfd,0xc5,0xe0,0x14,0xff,0xbf,0xdb, + 0x8f,0x40,0xf0,0x66,0xa1,0xfc,0x0c,0x81,0xfe,0x04,0x20,0xfe,0xfb,0x20,0xff,0xdb, + 0x40,0xff,0xe7,0x80,0xff,0xfd,0x00,0x00,0x00,0x20,0x00,0x04,0x60,0xff,0xe4,0x41, + 0x00,0x48,0x9f,0xff,0x0c,0x01,0x02,0xde,0xe0,0xfd,0x6b,0x00,0x14,0x1a,0xff,0xdb, + 0x73,0x01,0xef,0x80,0x21,0xfc,0x45,0x01,0xfd,0xd6,0xe0,0xfe,0xfc,0x01,0xff,0xd4, + 0xa0,0xff,0xe6,0xc1,0xff,0xfc,0xc0,0x00,0x00,0x20,0x00,0x03,0xdf,0xff,0xe4,0xc1, + 0x00,0x40,0xe0,0xff,0x06,0x01,0x02,0xb1,0x9f,0xfd,0x17,0x21,0x13,0x35,0x00,0xdb, + 0x62,0x01,0xee,0x99,0x01,0xfc,0x84,0x41,0xfd,0xa9,0x81,0xfe,0xfe,0x20,0xff,0xcd, + 0xe1,0xff,0xe6,0x01,0x12,0x28,0x00,0xba,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00, + 0x04,0x00,0x00,0x05,0x00,0x00,0x06,0x00,0x00,0x07,0x00,0x00,0x08,0x00,0x00,0x09, + 0x00,0x00,0x0a,0x00,0x00,0x0b,0x00,0x00,0x0c,0x00,0x00,0x0d,0x00,0x00,0x0e,0x00, + 0x00,0x0f,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x02,0x00,0x00, + 0x03,0x00,0x00,0x04,0x00,0x00,0x05,0x00,0x00,0x06,0x00,0x00,0x07,0x00,0x00,0x08, + 0x00,0x00,0x09,0x00,0x00,0x0a,0x00,0x00,0x0b,0x00,0x00,0x0c,0x00,0x00,0x0d,0x00, + 0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x02,0x00,0x00,0x03,0x00,0x00, + 0x04,0x00,0x00,0x05,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x10, + 0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x03,0x00,0x00,0x04,0x00,0x00,0x05,0x00, + 0x00,0x06,0x00,0x00,0x07,0x00,0x00,0x08,0x00,0x00,0x09,0x00,0x00,0x0a,0x00,0x00, + 0x0b,0x00,0x00,0x0c,0x00,0x00,0x0d,0x00,0x00,0x0e,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x00,0x00,0x01,0x00,0x00,0x03,0x00,0x00,0x04,0x00,0x00,0x05,0x00,0x00,0x06,0x00, + 0x00,0x07,0x12,0x66,0x00,0xbd,0x40,0x00,0x00,0x32,0xcb,0xfd,0x28,0x51,0x45,0x20, + 0x00,0x00,0x19,0x65,0xfe,0x14,0x28,0xa2,0x10,0x00,0x00,0x0c,0xb2,0xff,0x0a,0x14, + 0x51,0x08,0x00,0x00,0x06,0x59,0x7f,0x05,0x0a,0x28,0x04,0x00,0x00,0x03,0x2c,0xbf, + 0x02,0x85,0x14,0x02,0x00,0x00,0x01,0x96,0x5f,0x01,0x42,0x8a,0x01,0x00,0x00,0x00, + 0xcb,0x2f,0x00,0xa1,0x45,0x00,0x80,0x00,0x00,0x65,0x97,0x00,0x50,0xa2,0x00,0x40, + 0x00,0x00,0x32,0xcb,0x00,0x28,0x51,0x00,0x20,0x00,0x00,0x19,0x65,0x00,0x14,0x28, + 0x00,0x10,0x00,0x00,0x0c,0xb2,0x00,0x0a,0x14,0x00,0x08,0x00,0x00,0x06,0x59,0x00, + 0x05,0x0a,0x00,0x04,0x00,0x00,0x03,0x2c,0x00,0x02,0x85,0x00,0x02,0x00,0x00,0x01, + 0x96,0x00,0x01,0x42,0x00,0x01,0x00,0x00,0x00,0xcb,0x00,0x00,0xa1,0x00,0x00,0x80, + 0x00,0x00,0x65,0x00,0x00,0x50,0x00,0x00,0x40,0x00,0x00,0x32,0x00,0x00,0x28,0x00, + 0x00,0x1f,0x00,0x00,0x19,0x00,0x00,0x14,0x00,0x00,0x0f,0x00,0x00,0x0c,0x00,0x00, + 0x0a,0x00,0x00,0x08,0x00,0x00,0x06,0x00,0x00,0x05,0x00,0x00,0x03,0x00,0x00,0x03, + 0x00,0x00,0x02,0x12,0x10,0x00,0x48,0x00,0x00,0x01,0x00,0x00,0x02,0x00,0x00,0x04, + 0x00,0x00,0x08,0x00,0x00,0x10,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x80,0x00, + 0x01,0x00,0x00,0x02,0x00,0x00,0x04,0x00,0x00,0x08,0x00,0x00,0x10,0x00,0x00,0x20, + 0x00,0x00,0x40,0x00,0x00,0x80,0x00,0x01,0x00,0x00,0x02,0x00,0x00,0x04,0x00,0x00, + 0x08,0x00,0x00,0x10,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x80,0x00,0x00,0x12, + 0xa5,0x00,0x33,0xff,0xff,0xfb,0xff,0xff,0xf9,0x00,0x00,0x03,0xff,0xff,0xf6,0x00, + 0x00,0x04,0x00,0x00,0x05,0x00,0x00,0x06,0x00,0x00,0x07,0x00,0x00,0x08,0x00,0x00, + 0x09,0x00,0x00,0x0a,0x00,0x00,0x0b,0x00,0x00,0x0c,0x00,0x00,0x0d,0x00,0x00,0x0e, + 0x00,0x00,0x0f,0x00,0x00,0x10,0x12,0xb6,0x00,0x7e,0x00,0x00,0x04,0x00,0x00,0x04, + 0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00, + 0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x03,0x00,0x00, + 0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03, + 0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00, + 0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00, + 0x02,0x00,0x00,0x02,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x03,0x00,0x00,0x03, + 0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00, + 0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x12,0xe0,0x00,0x0c,0x00,0x00,0x19,0x00, + 0x00,0x1c,0x00,0x00,0x06,0x00,0x00,0x0a,0x12,0xe4,0x00,0x0c,0x00,0x00,0x03,0x00, + 0x00,0x07,0x00,0x00,0x0b,0x00,0x00,0x0f,0x12,0xe8,0x00,0x0c,0x00,0x02,0xb6,0x00, + 0x02,0xb6,0x00,0x02,0xd4,0x00,0x02,0xd4,0x12,0xec,0x00,0x0c,0x00,0x02,0x28,0x00, + 0x02,0x28,0x00,0x02,0x50,0x00,0x02,0x50,0x12,0xf0,0x00,0x33,0x00,0x00,0x03,0x00, + 0x00,0x05,0x00,0x00,0x07,0x00,0x00,0x09,0x00,0x00,0x0f,0x00,0x00,0x1f,0x00,0x00, + 0x3f,0x00,0x00,0x7f,0x00,0x00,0xff,0x00,0x01,0xff,0x00,0x03,0xff,0x00,0x07,0xff, + 0x00,0x0f,0xff,0x00,0x1f,0xff,0x00,0x3f,0xff,0x00,0x7f,0xff,0x00,0xff,0xff,0x13, + 0x01,0x00,0x2a,0x00,0x01,0x40,0x00,0x01,0xe0,0x00,0x02,0x30,0x00,0x02,0x80,0x00, + 0x03,0x20,0x00,0x03,0xc0,0x00,0x04,0x60,0x00,0x05,0x00,0x00,0x06,0x40,0x00,0x07, + 0x80,0x00,0x08,0xc0,0x00,0x0a,0x00,0x00,0x0c,0x80,0x00,0x0f,0x00,0x13,0x0f,0x00, + 0x09,0x00,0x01,0xb9,0x00,0x01,0xe0,0x00,0x01,0x40,0x13,0x12,0x00,0x0c,0x00,0x00, + 0x58,0x00,0x00,0x5e,0x00,0x00,0x1a,0x00,0x00,0x26,0x13,0x16,0x00,0x6f,0x16,0xa0, + 0x9e,0xe9,0x5f,0x62,0x1d,0x90,0x6b,0xe2,0x6f,0x95,0x0c,0x3e,0xf1,0xf3,0xc1,0x0f, + 0x1f,0x62,0x97,0xe0,0x9d,0x69,0x1a,0x9b,0x66,0x11,0xc7,0x3b,0xee,0x38,0xc5,0x06, + 0x3e,0x2e,0x1f,0xd8,0x8d,0x1e,0x9f,0x41,0x1c,0x38,0xb2,0x18,0xbc,0x80,0x14,0x4c, + 0xf3,0x0f,0x15,0xae,0x09,0x4a,0x03,0x03,0x22,0xf4,0x01,0x91,0xf6,0x17,0xb5,0xdf, + 0x0d,0xae,0x88,0x1e,0x21,0x21,0x07,0xc6,0x7e,0x1b,0x72,0x83,0x13,0x0f,0xf7,0x1f, + 0xa7,0x55,0x1f,0xf6,0x21,0x15,0x7d,0x69,0x1c,0xed,0x7a,0x0a,0xc7,0xcd,0x1f,0x0a, + 0x7e,0x10,0x73,0x87,0x19,0xb3,0xe0,0x04,0xb2,0x04,0x00,0x00,0x00,0x1d,0x40,0x05, + 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x1f,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x01,0x5f,0x00,0x00,0x01,0x00,0x00,0x03,0x00, + 0x00,0x07,0x00,0x00,0x0f,0x00,0x00,0x1f,0x00,0x00,0x3f,0x00,0x00,0x7f,0x00,0x00, + 0xff,0x00,0x01,0xff,0x00,0x03,0xff,0x00,0x07,0xff,0x00,0x0f,0xff,0x00,0x1f,0xff, + 0x00,0x3f,0xff,0x00,0x7f,0xff,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x08,0x00, + 0x00,0x10,0xff,0xff,0xfd,0xff,0xff,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00, + 0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00, + 0x00,0x0d,0x40,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x05,0xc0,0x00,0x05,0xc0,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x0f,0x00,0x00,0x0f,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x05,0xe0,0x00,0x05,0xe0,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xc0,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x06,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x04,0x80,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x04,0xa0,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0xc0,0x00,0x60,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0xe0,0x00,0x60, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x05,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x05,0x20,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x40,0x00,0x60,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x60,0x00,0x60, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x07,0xc0,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x05,0xc0,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0xe0,0x00,0x30,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x05,0x40, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x05,0x80,0x00,0x66,0x2a,0xaa,0xaa,0x33,0x33,0x33,0x24,0x92,0x49,0x38,0xe3,0x8e, + 0x22,0x22,0x22,0x21,0x08,0x42,0x20,0x82,0x08,0x20,0x40,0x81,0x20,0x20,0x20,0x20, + 0x10,0x08,0x20,0x08,0x02,0x20,0x04,0x00,0x20,0x02,0x00,0x20,0x01,0x00,0x20,0x00, + 0x80,0x20,0x00,0x40,0x20,0x00,0x20,0x10,0x00,0x00,0x10,0x00,0x00,0x08,0x00,0x00, + 0x10,0x00,0x00,0x04,0x00,0x00,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x80,0x00,0x00, + 0x40,0x00,0x00,0x20,0x00,0x00,0x10,0x00,0x00,0x08,0x00,0x00,0x04,0x00,0x00,0x02, + 0x00,0x00,0x00,0xff,0x00,0x00,0x80,0x00,0x00,0x40,0x04,0x78,0x00,0x18,0x01,0xe8, + 0x0d,0x03,0x6c,0x16,0x02,0x40,0x0a,0x00,0x10,0x03,0xa8,0x00,0x00,0x00,0xcc,0x00, + 0x30,0x00,0x00,0x00,0x00,0x02,0x10,0x00,0x00,0x1b,0xcf,0x83,0x40,0x00,0x00,0x00, + 0xc7,0x8b,0x17,0x70,0x41,0xc1,0xc7,0x8c,0x26,0xc7,0x8c,0x26,0xc7,0x8b,0x36,0xc7, + 0x8b,0xf2,0xc7,0x8c,0x52,0x13,0x40,0x1b,0xea,0x97,0x80,0xc0,0x00,0x04,0x00,0x97, + 0x80,0xc8,0x00,0x04,0x00,0x70,0x78,0xcf,0x70,0x7b,0xcd,0x0f,0xd7,0xc2,0x00,0x00, + 0x01,0xc9,0x83,0x46,0x70,0x02,0xec,0x97,0x80,0xdc,0x00,0x00,0x05,0x97,0x80,0xe0, + 0x00,0x07,0xbf,0xd7,0x02,0x8f,0x70,0x7f,0xd7,0x70,0x7c,0xc7,0x70,0x7d,0xcb,0x70, + 0x7e,0xd5,0x97,0x80,0x4c,0x00,0x06,0x00,0x97,0x80,0x4d,0x00,0x0d,0x40,0x70,0x50, + 0x51,0x70,0x10,0x52,0x70,0x10,0x55,0x70,0x5a,0x5b,0x70,0x5a,0x5c,0x70,0x4e,0x62, + 0x70,0x10,0x3e,0x0f,0xcd,0xcd,0xff,0xdf,0xff,0x0f,0x4b,0x4b,0xff,0xff,0xf7,0x70, + 0x10,0x46,0xd5,0x03,0x3a,0x70,0x10,0xc2,0xd4,0x40,0x00,0xd1,0x80,0x00,0xd5,0x80, + 0x00,0x70,0x10,0xd4,0xc7,0x8a,0x47,0x70,0x00,0x1e,0x70,0x10,0x1b,0xc7,0x89,0xb0, + 0x23,0x10,0xc2,0xc9,0x83,0x70,0xcf,0x83,0x5a,0x27,0x1b,0x1b,0x00,0x00,0x01,0x37, + 0x1b,0xc2,0x00,0x00,0x0c,0xc9,0x83,0x6c,0x70,0x10,0x34,0xc7,0x89,0xb0,0x0b,0x00, + 0x20,0xc9,0x03,0x6a,0xc7,0x89,0xb0,0x0b,0x00,0xc2,0xc9,0x03,0x6a,0xc7,0x89,0xb0, + 0x0b,0x00,0xc2,0xc9,0x83,0x7c,0x97,0x80,0x21,0x00,0x00,0x02,0x70,0x00,0x1e,0xc7, + 0x89,0xb0,0x0b,0x00,0x22,0x70,0x12,0x1e,0xc7,0x89,0xb0,0x0b,0x0f,0x61,0x47,0x61, + 0x23,0x00,0x10,0x00,0x70,0x24,0x6b,0x47,0x61,0xc2,0x00,0x40,0x00,0x0b,0x01,0x24, + 0x47,0x61,0xc2,0x00,0x80,0x00,0x0b,0x00,0x25,0x47,0x61,0xc2,0x01,0x00,0x00,0x0b, + 0x00,0x26,0x47,0x61,0xc2,0x04,0x00,0x00,0x0b,0x01,0x27,0x47,0x61,0xc2,0x10,0x00, + 0x00,0x0b,0x01,0x28,0x47,0x61,0xc2,0x20,0x00,0x00,0x0b,0x00,0x29,0x47,0x61,0xc2, + 0x40,0x00,0x00,0x0b,0x00,0x2a,0x0f,0x61,0x2b,0x00,0x00,0x03,0x27,0x24,0xc2,0x00, + 0x03,0x0f,0xd1,0x40,0x00,0x90,0x00,0x1f,0x4f,0x1f,0x1f,0x00,0x10,0x00,0x27,0x23, + 0xc2,0x00,0x03,0x00,0xd1,0x40,0x00,0x90,0x00,0x30,0x4f,0x30,0x30,0x00,0x00,0x90, + 0x40,0x10,0xc2,0x70,0x30,0xc2,0xd6,0x00,0x17,0x7b,0x1f,0xc2,0x58,0x10,0x3d,0x47, + 0x3d,0x3d,0x00,0x20,0x00,0x4f,0x3d,0x3d,0x00,0x00,0x08,0x4f,0x25,0x25,0x00,0x00, + 0x08,0x70,0x25,0xc2,0x23,0x3d,0x3d,0x37,0x3d,0x3e,0x00,0x00,0x20,0xca,0x03,0x6a, + 0x27,0x3e,0xc2,0x00,0x35,0xe0,0xca,0x83,0x6a,0x27,0x23,0xc2,0x00,0x00,0x00,0xc9, + 0x03,0xeb,0x37,0x27,0xc2,0x00,0x00,0x03,0xc9,0x03,0xcc,0x37,0x23,0x1f,0x00,0x00, + 0x06,0x37,0x1f,0xc2,0x00,0x00,0x00,0xcb,0x83,0xd9,0xcf,0x83,0xd1,0x37,0x23,0x1f, + 0x00,0x00,0x02,0x37,0x1f,0xc2,0x00,0x00,0x00,0xcb,0x83,0xd9,0x37,0x24,0xc2,0x00, + 0x00,0x02,0xc9,0x03,0xd7,0x97,0x80,0x2d,0x00,0x00,0x02,0xcf,0x83,0xef,0x70,0x01, + 0x2d,0xcf,0x83,0xef,0x37,0x27,0xc2,0x00,0x00,0x03,0xc9,0x03,0xe3,0x37,0x23,0x1f, + 0x00,0x00,0x0a,0xcb,0x83,0xee,0x37,0x24,0xc2,0x00,0x00,0x01,0xc9,0x03,0xee,0xcf, + 0x83,0xe9,0x37,0x23,0xc2,0x00,0x00,0x06,0xcb,0x83,0xee,0x37,0x24,0xc2,0x00,0x00, + 0x01,0xc9,0x03,0xee,0x70,0x00,0x2d,0xcf,0x83,0xef,0x37,0x24,0xc2,0x00,0x00,0x01, + 0xc9,0x83,0xe9,0x70,0x10,0x2d,0x27,0x22,0xc2,0x00,0x00,0x00,0xc9,0x83,0xf5,0x70, + 0x12,0x1e,0xc7,0x89,0xb0,0x0b,0x0f,0x2c,0x27,0x62,0x62,0x00,0x00,0x00,0xc9,0x03, + 0xfb,0x27,0x62,0x62,0xff,0xff,0xff,0xcb,0x83,0x66,0x37,0x27,0xc2,0x00,0x00,0x03, + 0xc9,0x84,0x01,0x70,0x00,0x2e,0x70,0x00,0x2f,0xcf,0x84,0x0a,0x97,0x80,0x2e,0x00, + 0x00,0x02,0x37,0x27,0xc2,0x00,0x00,0x01,0xc9,0x84,0x08,0x70,0x01,0x2f,0xcf,0x84, + 0x0a,0x97,0x80,0x2f,0x00,0x00,0x02,0x70,0x1d,0x37,0x70,0x3e,0x38,0x70,0x1c,0x39, + 0x70,0x5b,0x5c,0x27,0x2d,0xc2,0x00,0x03,0x12,0xd1,0x40,0x00,0x90,0x00,0x5d,0x37, + 0x27,0xc2,0x00,0x00,0x03,0xc9,0x04,0x17,0x4f,0x5d,0x5d,0x00,0x00,0x02,0x70,0x2e, + 0x32,0x27,0x2d,0xc2,0x00,0x02,0xe8,0xd1,0x40,0x00,0x90,0x00,0x30,0x97,0x80,0xd8, + 0x00,0x04,0x80,0x27,0x28,0xc2,0x00,0x02,0xe4,0xd5,0x40,0x00,0x94,0x00,0x31,0x94, + 0x00,0xc2,0xd6,0x40,0x00,0xc7,0x89,0x85,0x70,0x2f,0x32,0x27,0x2d,0xc2,0x00,0x02, + 0xe0,0xd1,0x40,0x00,0x30,0x31,0xc2,0xd6,0x40,0x00,0xc7,0x89,0x85,0x70,0x2e,0x32, + 0x97,0x80,0xf0,0x00,0x04,0x80,0x97,0x80,0xd8,0x00,0x04,0xa0,0x97,0x80,0x1e,0x00, + 0x00,0x02,0x27,0x28,0xc2,0x00,0x02,0xe4,0xd5,0x40,0x00,0x94,0x00,0x31,0x94,0x00, + 0xc2,0xd6,0x40,0x00,0xc7,0x89,0x61,0x70,0x2f,0x32,0x27,0x2d,0xc2,0x00,0x02,0xe0, + 0xd1,0x40,0x00,0x30,0x31,0xc2,0xd6,0x40,0x00,0xc7,0x89,0x61,0x27,0x64,0xc2,0x00, + 0x00,0x00,0xc9,0x84,0x6d,0x27,0x22,0xc2,0x00,0x00,0x00,0xc9,0x84,0x6d,0x70,0x37, + 0x1d,0x70,0x38,0x3e,0x70,0x39,0x1c,0x97,0x80,0x63,0x00,0xff,0xff,0x0f,0x61,0xc2, + 0x00,0xff,0x00,0xc7,0x8a,0x19,0x4f,0x61,0x5f,0x00,0x01,0x00,0x0f,0x5f,0xc2,0x00, + 0xff,0x00,0xc7,0x8a,0x19,0x70,0x11,0x1e,0x27,0x5d,0x5d,0xff,0xff,0xf8,0xca,0x84, + 0x5a,0xc7,0x8a,0x15,0xcf,0x84,0x55,0x70,0x00,0x1e,0x27,0x5d,0xc2,0x00,0x00,0x08, + 0xc9,0x04,0x61,0x23,0x14,0xc2,0xd6,0x40,0x00,0xc7,0x8a,0x3a,0x0f,0x63,0x63,0x00, + 0xff,0xff,0x70,0x2c,0xc2,0x33,0x63,0xc2,0xc9,0x04,0x6d,0x0f,0x4b,0x4b,0xff,0xff, + 0xf7,0x17,0x4b,0x4b,0x00,0x00,0x07,0x27,0x59,0x59,0x00,0x00,0x01,0xcf,0x83,0x66, + 0x70,0x5c,0x5b,0x70,0x24,0xc2,0x33,0x6b,0xc2,0xc9,0x83,0x5a,0x27,0x24,0xc2,0x00, + 0x04,0x74,0xcf,0xc0,0x00,0xcf,0x84,0x78,0xcf,0x84,0x7c,0xcf,0x84,0x80,0xcf,0x83, + 0x5a,0x70,0x79,0xcf,0x0f,0xcd,0xcd,0xff,0xfb,0xff,0xcf,0x84,0x83,0x70,0x78,0xcf, + 0x0f,0xcd,0xcd,0xff,0xfb,0xff,0xcf,0x84,0x83,0x70,0x7a,0xcf,0x17,0xcd,0xcd,0x00, + 0x04,0x00,0x0f,0xcd,0xc2,0x00,0x20,0x00,0xc9,0x84,0x8d,0x27,0x57,0xc2,0x00,0x00, + 0x00,0xc9,0x84,0x8d,0x0f,0x4b,0x4b,0xff,0xff,0xfb,0x17,0x4b,0x4b,0x00,0x00,0x0b, + 0x70,0x2e,0x32,0x97,0x80,0xf0,0x00,0x04,0x80,0x97,0x80,0xd8,0x00,0x04,0xa0,0x97, + 0x80,0xe8,0x00,0x07,0xc0,0x97,0x80,0x1e,0x00,0x00,0x06,0x27,0x28,0xc2,0x00,0x02, + 0xe4,0xd5,0x40,0x00,0x94,0x00,0x31,0x94,0x00,0xc2,0xd6,0x40,0x00,0xc7,0x89,0x06, + 0x70,0x2f,0x32,0x27,0x2d,0xc2,0x00,0x02,0xe0,0xd1,0x40,0x00,0x30,0x31,0xc2,0xd6, + 0x40,0x00,0xc7,0x89,0x06,0x70,0x2e,0x32,0x70,0x10,0x1b,0x97,0x80,0xf0,0x00,0x04, + 0x80,0x27,0x28,0xc2,0x00,0x02,0xe4,0xd5,0x40,0x00,0x94,0x00,0x31,0x94,0x00,0xc2, + 0xd6,0x40,0x00,0xc7,0x88,0xba,0x70,0x2f,0x32,0x27,0x2d,0xc2,0x00,0x02,0xe0,0xd1, + 0x40,0x00,0x30,0x31,0xc2,0xd6,0x40,0x00,0xc7,0x88,0xba,0x70,0x2e,0x32,0x70,0x10, + 0x1b,0x97,0x80,0xf0,0x00,0x04,0x80,0x27,0x28,0xc2,0x00,0x02,0xe4,0xd5,0x40,0x00, + 0x94,0x00,0x31,0x94,0x00,0xc2,0xd6,0x40,0x00,0xc7,0x86,0xbd,0x70,0x2f,0x32,0x27, + 0x2d,0xc2,0x00,0x02,0xe0,0xd1,0x40,0x00,0x30,0x31,0xc2,0x90,0x00,0x31,0xd6,0x40, + 0x00,0xc7,0x86,0xbd,0x37,0x31,0xc2,0x00,0x00,0x1d,0xd6,0x40,0x00,0xc7,0x88,0x74, + 0x97,0x80,0x1f,0x00,0x00,0x06,0x37,0x27,0xc2,0x00,0x00,0x03,0xc9,0x84,0xd5,0x97, + 0x80,0x30,0x00,0x00,0x02,0xcf,0x84,0xd6,0x70,0x00,0x30,0x27,0x1f,0xc2,0x00,0x04, + 0xd9,0xcf,0xc0,0x00,0xcf,0x85,0x18,0xcf,0x85,0x13,0xcf,0x85,0x01,0xcf,0x84,0xfc, + 0xcf,0x84,0xea,0xcf,0x84,0xe5,0xcf,0x84,0xe0,0x97,0x80,0x37,0x00,0x04,0xc0,0x97, + 0x80,0x1a,0x00,0x00,0x00,0xcf,0x85,0x34,0x97,0x80,0x37,0x00,0x05,0x20,0x97,0x80, + 0x1a,0x00,0x02,0x00,0xcf,0x85,0x34,0x22,0x14,0x16,0x0f,0x16,0x16,0x00,0x00,0x0f, + 0x27,0x17,0x17,0x00,0x00,0x01,0x27,0x19,0x19,0x00,0x00,0x01,0x0f,0x19,0x19,0x00, + 0x00,0x0f,0x97,0x80,0x37,0x00,0x04,0xe0,0x97,0x80,0x1a,0x00,0x00,0x00,0x27,0x4f, + 0x4f,0x00,0x00,0x00,0xc9,0x05,0x34,0x70,0x10,0x4f,0xcf,0x85,0x34,0x97,0x80,0x37, + 0x00,0x05,0x40,0x97,0x80,0x1a,0x00,0x02,0x00,0xcf,0x85,0x34,0x22,0x14,0x16,0x0f, + 0x16,0x16,0x00,0x00,0x0f,0x27,0x17,0x17,0x00,0x00,0x01,0x27,0x19,0x19,0x00,0x00, + 0x01,0x0f,0x19,0x19,0x00,0x00,0x0f,0x97,0x80,0x37,0x00,0x05,0x00,0x97,0x80,0x1a, + 0x00,0x00,0x00,0x27,0x4f,0x4f,0x00,0x00,0x00,0xc9,0x05,0x34,0x70,0x10,0x4f,0xcf, + 0x85,0x34,0x97,0x80,0x37,0x00,0x05,0x60,0x97,0x80,0x1a,0x00,0x02,0x00,0xcf,0x85, + 0x34,0x22,0x14,0x16,0x0f,0x16,0x16,0x00,0x00,0x0f,0x27,0x17,0x17,0x00,0x00,0x01, + 0x27,0x19,0x19,0x00,0x00,0x01,0x0f,0x19,0x19,0x00,0x00,0x0f,0x27,0x4f,0x4f,0x00, + 0x00,0x00,0xc9,0x05,0x25,0x70,0x10,0x4f,0x97,0x80,0xf0,0x00,0x04,0xc0,0x97,0x80, + 0xd0,0x00,0x05,0x20,0xd5,0x03,0x3a,0xd1,0x80,0x00,0xd6,0x00,0x02,0xc7,0x8a,0x9f, + 0xd1,0x80,0x04,0x27,0x34,0x34,0x00,0x00,0x01,0x37,0x34,0xc2,0x00,0x00,0x0c,0xc9, + 0x84,0xb6,0xcf,0x83,0x66,0x70,0x37,0xf0,0x27,0x37,0xd8,0x00,0x00,0x1f,0xd6,0x00, + 0x07,0xc7,0x8c,0x5a,0x70,0x37,0xf0,0x27,0x37,0xd8,0x00,0x00,0x0f,0xd6,0x00,0x03, + 0xc7,0x8c,0x5a,0x27,0x37,0xf0,0x00,0x00,0x1b,0x27,0x37,0xd8,0x00,0x00,0x14,0xd1, + 0x03,0x16,0xd5,0x03,0x16,0xc7,0x8c,0x61,0xc7,0x8c,0x61,0xc7,0x8c,0x61,0xc7,0x8c, + 0x61,0x70,0x37,0xf0,0x27,0x37,0xd8,0x00,0x00,0x07,0xd6,0x00,0x01,0xc7,0x8c,0x5a, + 0x27,0x37,0xf0,0x00,0x00,0x0d,0x27,0x37,0xd8,0x00,0x00,0x0a,0xd6,0x00,0x01,0xc7, + 0x8c,0x61,0x27,0x37,0xf0,0x00,0x00,0x10,0x27,0x37,0xd8,0x00,0x00,0x17,0xd6,0x00, + 0x01,0xc7,0x8c,0x5a,0x27,0x37,0xf0,0x00,0x00,0x1c,0x27,0x37,0xd8,0x00,0x00,0x1b, + 0xd6,0x00,0x01,0xc7,0x8c,0x5a,0x70,0x37,0xf0,0x27,0x37,0xd8,0x00,0x00,0x03,0xc7, + 0x8c,0x5a,0x27,0x37,0xf0,0x00,0x00,0x06,0x27,0x37,0xd8,0x00,0x00,0x05,0xc7,0x8c, + 0x61,0x27,0x37,0xf0,0x00,0x00,0x08,0x27,0x37,0xd8,0x00,0x00,0x0b,0xc7,0x8c,0x5a, + 0x27,0x37,0xf0,0x00,0x00,0x0e,0x27,0x37,0xd8,0x00,0x00,0x0d,0xc7,0x8c,0x5a,0x27, + 0x37,0xf0,0x00,0x00,0x1d,0x27,0x37,0xd8,0x00,0x00,0x12,0xd1,0x03,0x1a,0xd5,0x03, + 0x18,0xd6,0x00,0x01,0xc7,0x8c,0x61,0xd1,0x03,0x19,0xd5,0x03,0x1a,0xd6,0x00,0x01, + 0xc7,0x8c,0x61,0x70,0x37,0xf0,0x27,0x37,0xd8,0x00,0x00,0x01,0xd1,0x03,0x16,0xd5, + 0x03,0x16,0xc7,0x8c,0x61,0x27,0x37,0xf0,0x00,0x00,0x03,0x27,0x37,0xd8,0x00,0x00, + 0x02,0xd1,0x03,0x1a,0xd5,0x03,0x18,0xc7,0x8c,0x6e,0x27,0x37,0xf0,0x00,0x00,0x04, + 0x27,0x37,0xd8,0x00,0x00,0x07,0xc7,0x8c,0x7b,0x27,0x37,0xf0,0x00,0x00,0x0e,0x27, + 0x37,0xd8,0x00,0x00,0x09,0xd1,0x03,0x1a,0xd5,0x03,0x18,0xc7,0x8c,0x61,0xd1,0x03, + 0x19,0xd5,0x03,0x1a,0xc7,0x8c,0x61,0x27,0x37,0xf0,0x00,0x00,0x10,0x27,0x37,0xd8, + 0x00,0x00,0x13,0xc7,0x8c,0x5a,0x27,0x37,0xf0,0x00,0x00,0x16,0x27,0x37,0xd8,0x00, + 0x00,0x15,0xc7,0x8c,0x5a,0x27,0x37,0xf0,0x00,0x00,0x18,0x27,0x37,0xd8,0x00,0x00, + 0x1b,0xc7,0x8c,0x5a,0x27,0x37,0xf0,0x00,0x00,0x1e,0x27,0x37,0xd8,0x00,0x00,0x1d, + 0xc7,0x8c,0x5a,0x27,0x37,0xf0,0x00,0x00,0x07,0x27,0x37,0xd8,0x00,0x00,0x04,0xd1, + 0x03,0x21,0xd5,0x03,0x1c,0xc7,0x8c,0x6e,0xd1,0x03,0x1e,0xd5,0x03,0x1f,0xc7,0x8c, + 0x6e,0x27,0x37,0xf0,0x00,0x00,0x08,0x27,0x37,0xd8,0x00,0x00,0x0b,0xc7,0x8c,0x7b, + 0x27,0x37,0xf0,0x00,0x00,0x0c,0x27,0x37,0xd8,0x00,0x00,0x0f,0xc7,0x8c,0x7b,0x27, + 0x37,0xf0,0x00,0x00,0x1e,0x27,0x37,0xd8,0x00,0x00,0x11,0xd1,0x03,0x21,0xd5,0x03, + 0x1c,0xc7,0x8c,0x61,0xd1,0x03,0x1d,0xd5,0x03,0x21,0xc7,0x8c,0x61,0x27,0x37,0xf0, + 0x00,0x00,0x1a,0x27,0x37,0xd8,0x00,0x00,0x15,0xd1,0x03,0x1e,0xd5,0x03,0x1f,0xc7, + 0x8c,0x61,0xd1,0x03,0x20,0xd5,0x03,0x1e,0xc7,0x8c,0x61,0x27,0x37,0xf0,0x00,0x00, + 0x0f,0x27,0x37,0xd8,0x00,0x00,0x08,0xd1,0x03,0x29,0xd5,0x03,0x22,0xc7,0x8c,0x6e, + 0xd1,0x03,0x25,0xd5,0x03,0x26,0xc7,0x8c,0x6e,0xd1,0x03,0x27,0xd5,0x03,0x24,0xc7, + 0x8c,0x6e,0xd1,0x03,0x23,0xd5,0x03,0x28,0xc7,0x8c,0x6e,0x27,0x37,0xf0,0x00,0x00, + 0x10,0x27,0x37,0xd8,0x00,0x00,0x13,0xc7,0x8c,0x7b,0x27,0x37,0xf0,0x00,0x00,0x14, + 0x27,0x37,0xd8,0x00,0x00,0x17,0xc7,0x8c,0x7b,0x27,0x37,0xf0,0x00,0x00,0x18,0x27, + 0x37,0xd8,0x00,0x00,0x1b,0xc7,0x8c,0x7b,0x27,0x37,0xf0,0x00,0x00,0x1c,0x27,0x37, + 0xd8,0x00,0x00,0x1f,0xc7,0x8c,0x7b,0xd1,0x80,0x00,0xd5,0x80,0x00,0x70,0x37,0xf0, + 0x97,0x80,0xd0,0x00,0x04,0xa0,0x97,0x80,0xd4,0x00,0x00,0x05,0x47,0xb2,0xc2,0x2d, + 0x41,0x3c,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0x96,0xd6,0x00,0x0e,0x70,0xb2, + 0x96,0x27,0x37,0xd8,0x00,0x00,0x1f,0xd1,0x03,0x2a,0xd5,0x03,0x32,0xd6,0x00,0x07, + 0xc7,0x8c,0x82,0xd1,0x03,0x31,0xd5,0x03,0x39,0x97,0x00,0xc2,0x13,0x12,0xc2,0xd7, + 0x40,0x00,0xd6,0x00,0x07,0xc7,0x8c,0x88,0x97,0x00,0xc2,0x1b,0x12,0xc2,0xd7,0x40, + 0x00,0x97,0x80,0xd4,0x00,0x00,0x09,0x97,0x80,0xd0,0x00,0x04,0xa0,0x70,0x1a,0xc2, + 0x23,0x19,0xf0,0xc7,0x8b,0x07,0x0f,0x17,0x17,0x00,0x00,0x01,0xc9,0x86,0x27,0x97, + 0x80,0xd0,0x00,0x04,0xbf,0xd6,0x00,0x1e,0xc7,0x8b,0x03,0xcf,0x86,0x29,0xd6,0x00, + 0x1e,0xc7,0x8b,0x07,0x70,0x37,0xe8,0xd1,0x80,0x04,0xd5,0x80,0x01,0x0f,0x17,0x17, + 0x00,0x00,0x01,0xc9,0x86,0x44,0x27,0x16,0xc2,0x00,0x00,0x00,0xd1,0x40,0x00,0x27, + 0x1a,0xf0,0x00,0x01,0x20,0xd6,0x00,0x0f,0xc7,0x86,0x5e,0x27,0x16,0xc2,0x00,0x01, + 0x10,0xd1,0x40,0x00,0x27,0x1a,0xf0,0x00,0x00,0x01,0xc7,0x86,0x99,0x27,0x16,0xc2, + 0x00,0x01,0x10,0xd1,0x40,0x00,0x27,0x1a,0xf0,0x00,0x00,0x10,0xd6,0x00,0x0e,0xc7, + 0x86,0x89,0xcf,0x86,0x57,0x27,0x16,0xc2,0x00,0x00,0x00,0xd1,0x40,0x00,0x27,0x1a, + 0xf0,0x00,0x01,0x00,0xd6,0x00,0x0f,0xc7,0x86,0x74,0x27,0x16,0xc2,0x00,0x01,0x10, + 0xd1,0x40,0x00,0x70,0x1a,0xf0,0xc7,0x86,0xab,0x27,0x16,0xc2,0x00,0x01,0x10,0xd1, + 0x40,0x00,0x27,0x1a,0xf0,0x00,0x02,0x10,0xd6,0x00,0x0e,0xc7,0x86,0x90,0x27,0x1a, + 0xc2,0x00,0x00,0x00,0xc9,0x06,0x5b,0xcf,0x86,0x5b,0x70,0x1f,0xc2,0x33,0x30,0x1f, + 0xcf,0x84,0xd6,0x22,0x12,0xc2,0xd1,0x40,0x00,0x27,0xf0,0xf0,0xff,0xff,0xe0,0x41, + 0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2, + 0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2, + 0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x86, + 0x00,0xaa,0xdf,0x80,0x0a,0x22,0x12,0xc2,0xd1,0x40,0x00,0x40,0x10,0xc2,0x61,0xb2, + 0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2, + 0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51, + 0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x86,0x00, + 0xaa,0xdf,0x80,0x0a,0x22,0x12,0xc2,0xd1,0x40,0x00,0x40,0x10,0xc2,0xd6,0x00,0x0f, + 0x61,0xb2,0xc2,0x86,0x00,0xaa,0xdf,0x80,0x0a,0x22,0x12,0xc2,0xd1,0x40,0x00,0x27, + 0xf0,0xf0,0xff,0xff,0xe0,0x40,0x10,0xc2,0xd6,0x00,0x0f,0x61,0xb2,0xc2,0x86,0x00, + 0xaa,0xdf,0x80,0x0a,0x41,0x10,0xc2,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x61,0xb2,0xc2, + 0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x61, + 0xb2,0xc2,0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2, + 0x39,0x60,0xb2,0xc2,0x86,0x00,0xaa,0xdf,0x80,0x0a,0x40,0x10,0xc2,0x61,0xb2,0xc2, + 0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x61, + 0xb2,0xc2,0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2, + 0x39,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x60,0xb2,0xc2,0x86,0x00,0xaa,0xdf,0x80,0x0a, + 0x0f,0xb0,0x1f,0x00,0x0f,0xff,0xc9,0x06,0xdf,0x27,0x1b,0xc2,0x00,0x07,0xc0,0x23, + 0x1b,0xe8,0x37,0x34,0xc2,0x00,0x00,0x03,0xca,0x06,0xd1,0x37,0x34,0xc2,0x00,0x00, + 0x07,0xca,0x06,0xd8,0x47,0xa8,0xc2,0x00,0x01,0x00,0x0b,0x05,0x18,0x27,0x18,0xc2, + 0x00,0x02,0x66,0xd1,0x40,0x00,0x20,0x10,0x18,0xcf,0x86,0xdf,0x0f,0xa8,0x18,0x00, + 0x00,0x3f,0x27,0x18,0xc2,0x00,0x02,0x66,0xd1,0x40,0x00,0x20,0x10,0x18,0xcf,0x86, + 0xdf,0x47,0xa8,0xc2,0x01,0x00,0x00,0x0b,0x05,0x18,0x27,0x18,0xc2,0x00,0x02,0x66, + 0xd1,0x40,0x00,0x20,0x10,0x18,0x0f,0xb0,0xc2,0xff,0xf0,0x00,0xc9,0x07,0x01,0x27, + 0x1b,0xc2,0x00,0x07,0xc1,0x23,0x1b,0xe8,0x37,0x34,0xc2,0x00,0x00,0x03,0xca,0x06, + 0xf3,0x37,0x34,0xc2,0x00,0x00,0x07,0xca,0x06,0xfa,0x47,0xa8,0xc2,0x00,0x01,0x00, + 0x0b,0x05,0x36,0x27,0x36,0xc2,0x00,0x02,0x66,0xd1,0x40,0x00,0x20,0x10,0x36,0xcf, + 0x87,0x01,0x0f,0xa8,0x36,0x00,0x00,0x3f,0x27,0x36,0xc2,0x00,0x02,0x66,0xd1,0x40, + 0x00,0x20,0x10,0x36,0xcf,0x87,0x01,0x47,0xa8,0xc2,0x01,0x00,0x00,0x0b,0x05,0x36, + 0x27,0x36,0xc2,0x00,0x02,0x66,0xd1,0x40,0x00,0x20,0x10,0x36,0x0f,0xb0,0x1f,0x00, + 0x0f,0xff,0xc9,0x08,0x23,0x27,0x1b,0x37,0x00,0x04,0xc0,0x27,0x1b,0x38,0x00,0x04, + 0xe0,0x27,0x1b,0x39,0x00,0x05,0x00,0x27,0x1f,0xc2,0x00,0x02,0xa4,0xd1,0x40,0x00, + 0x20,0x10,0x1e,0xca,0x87,0x31,0x70,0x37,0xe8,0xc7,0x89,0xb0,0xc7,0x88,0xaa,0x57, + 0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2,0x23,0x10,0x3a,0x43,0x18,0xc2,0x57,0x11, + 0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0x70,0x38,0xe8,0xc7,0x89,0xb0,0xc7,0x88,0xaa, + 0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2,0x23,0x10,0x3b,0x43,0x18,0xc2,0x57, + 0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0x70,0x39,0xe8,0xc7,0x89,0xb0,0xc7,0x88, + 0xaa,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2,0x23,0x10,0x3c,0x43,0x18,0xc2, + 0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0xcf,0x88,0x2c,0x37,0x1e,0x1e,0x00, + 0x00,0x00,0x27,0x1e,0xd8,0x00,0x03,0xff,0x70,0x37,0xe8,0xc7,0x89,0xb0,0x0b,0x98, + 0xa8,0x37,0x1f,0xc2,0x00,0x00,0x04,0xc9,0x07,0x89,0x37,0x1f,0xc2,0x00,0x00,0x02, + 0xc9,0x07,0xd6,0x27,0x10,0x35,0x00,0x00,0x30,0x40,0x10,0xc2,0x70,0xa8,0xc2,0xd6, + 0x00,0x05,0x7b,0x35,0xc2,0x5f,0x10,0x30,0x00,0x00,0x00,0x4f,0x30,0x30,0x00,0x00, + 0x02,0x47,0x30,0x30,0x40,0x00,0x00,0x47,0x35,0xc2,0xf0,0x00,0x00,0x4b,0x30,0x35, + 0x70,0xa8,0xc2,0x23,0x35,0xa8,0xc7,0x88,0x9f,0x57,0x11,0xc2,0x02,0x00,0x00,0x86, + 0x00,0xc2,0x23,0x10,0x3a,0x43,0x18,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00, + 0xa8,0x70,0x38,0xe8,0x70,0x30,0xa8,0x27,0x10,0x35,0x00,0x00,0x18,0x40,0x10,0xc2, + 0x70,0x30,0xc2,0xd6,0x00,0x03,0x7b,0x35,0xc2,0x5f,0x10,0x30,0x00,0x00,0x00,0x47, + 0x35,0xc2,0xe0,0x00,0x00,0x4b,0x30,0x35,0x70,0xa8,0xc2,0x23,0x35,0xa8,0xc7,0x88, + 0x9f,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2,0x23,0x10,0x3b,0x43,0x18,0xc2, + 0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0x70,0x39,0xe8,0x70,0x30,0xa8,0x27, + 0x10,0x35,0x00,0x00,0x0c,0x40,0x10,0xc2,0x70,0x30,0xc2,0xd6,0x00,0x02,0x7b,0x35, + 0xc2,0x5f,0x10,0x30,0x00,0x00,0x00,0x47,0x35,0xc2,0xc0,0x00,0x00,0x4b,0x30,0x35, + 0x70,0xa8,0xc2,0x23,0x35,0xa8,0xc7,0x88,0x9f,0x57,0x11,0xc2,0x02,0x00,0x00,0x86, + 0x00,0xc2,0x23,0x10,0x3c,0x43,0x18,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00, + 0xa8,0xcf,0x88,0x2c,0x27,0x10,0x35,0x00,0x04,0x80,0x40,0x10,0xc2,0x70,0xa8,0xc2, + 0xd6,0x00,0x0a,0x7b,0x35,0xc2,0x5f,0x10,0x30,0x00,0x00,0x00,0x47,0x30,0x30,0x20, + 0x00,0x00,0x47,0x35,0xc2,0xfe,0x00,0x00,0x4b,0x30,0x35,0x70,0xa8,0xc2,0x23,0x35, + 0xa8,0xc7,0x88,0x94,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2,0x23,0x10,0x3a, + 0x43,0x18,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0x70,0x38,0xe8,0x70, + 0x30,0xa8,0x27,0x10,0x35,0x00,0x00,0x90,0x40,0x10,0xc2,0x70,0x30,0xc2,0xd6,0x00, + 0x07,0x7b,0x35,0xc2,0x5f,0x10,0x30,0x00,0x00,0x00,0x47,0x30,0x30,0x20,0x00,0x00, + 0x47,0x35,0xc2,0xf0,0x00,0x00,0x4b,0x30,0x35,0x70,0xa8,0xc2,0x23,0x35,0xa8,0xc7, + 0x88,0x94,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2,0x23,0x10,0x3b,0x43,0x18, + 0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0x70,0x39,0xe8,0x70,0x30,0xa8, + 0x27,0x10,0x35,0x00,0x00,0x12,0x40,0x10,0xc2,0x70,0x30,0xc2,0xd6,0x00,0x05,0x7b, + 0x35,0xc2,0x5f,0x10,0x30,0x00,0x00,0x00,0x47,0x30,0x30,0x08,0x00,0x00,0x47,0x35, + 0xc2,0x80,0x00,0x00,0x4b,0x30,0x35,0x70,0xa8,0xc2,0x23,0x35,0xa8,0xc7,0x88,0x94, + 0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2,0x23,0x10,0x3c,0x43,0x18,0xc2,0x57, + 0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0xcf,0x88,0x2c,0x27,0x10,0x35,0x00,0x00, + 0xa0,0x40,0x10,0xc2,0x70,0xa8,0xc2,0xd6,0x00,0x07,0x7b,0x35,0xc2,0x5f,0x10,0x30, + 0x00,0x00,0x00,0x47,0x30,0x30,0x40,0x00,0x00,0x47,0x35,0xc2,0xf8,0x00,0x00,0x4b, + 0x30,0x35,0x70,0xa8,0xc2,0x23,0x35,0xa8,0xc7,0x88,0x89,0x57,0x11,0xc2,0x02,0x00, + 0x00,0x86,0x00,0xc2,0x23,0x10,0x3a,0x43,0x18,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00, + 0x86,0x00,0xa8,0x70,0x38,0xe8,0x70,0x30,0xa8,0x27,0x10,0x35,0x00,0x00,0x28,0x40, + 0x10,0xc2,0x70,0x30,0xc2,0xd6,0x00,0x05,0x7b,0x35,0xc2,0x5f,0x10,0x30,0x00,0x00, + 0x00,0x47,0x30,0x30,0x40,0x00,0x00,0x47,0x35,0xc2,0xe0,0x00,0x00,0x4b,0x30,0x35, + 0x70,0xa8,0xc2,0x23,0x35,0xa8,0xc7,0x88,0x89,0x57,0x11,0xc2,0x02,0x00,0x00,0x86, + 0x00,0xc2,0x23,0x10,0x3b,0x43,0x18,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00, + 0xa8,0x70,0x39,0xe8,0x70,0x30,0xa8,0x27,0x10,0x35,0x00,0x00,0x0a,0x40,0x10,0xc2, + 0x70,0x30,0xc2,0xd6,0x00,0x03,0x7b,0x35,0xc2,0x5f,0x10,0x30,0x00,0x00,0x00,0x47, + 0x30,0x30,0x40,0x00,0x00,0x47,0x35,0xc2,0x80,0x00,0x00,0x4b,0x30,0x35,0x70,0xa8, + 0xc2,0x23,0x35,0xa8,0xc7,0x88,0x89,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2, + 0x23,0x10,0x3c,0x43,0x18,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0xcf, + 0x88,0x2c,0x27,0x1b,0xe8,0x00,0x04,0xc0,0x70,0x10,0xa8,0x27,0x1b,0xe8,0x00,0x04, + 0xe0,0x70,0x10,0xa8,0x27,0x1b,0xe8,0x00,0x05,0x00,0x70,0x10,0xa8,0x37,0x32,0xc2, + 0x00,0x00,0x02,0xc9,0x88,0x44,0x0f,0xe8,0xc2,0x00,0x00,0x20,0xc9,0x88,0x3f,0x27, + 0x1b,0x37,0x00,0x05,0x20,0x27,0x1b,0x38,0x00,0x05,0x40,0x27,0x1b,0x39,0x00,0x05, + 0x60,0x70,0x36,0x18,0x0f,0xb0,0x1f,0xff,0xf0,0x00,0x47,0x1f,0x1f,0x00,0x10,0x00, + 0xc9,0x87,0x0a,0xcf,0x88,0x66,0x27,0xf0,0xf0,0x00,0x00,0x01,0x27,0x1b,0x1b,0x00, + 0x00,0x01,0xdf,0x80,0x0a,0x37,0x32,0xc2,0x00,0x00,0x03,0xc9,0x88,0x66,0x0f,0xb0, + 0x1f,0xff,0xf0,0x00,0x47,0x1f,0x1f,0x00,0x10,0x00,0xc9,0x08,0x66,0x70,0x3a,0xc2, + 0x27,0x1b,0xe8,0x00,0x05,0x20,0x43,0x36,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86, + 0x00,0xa8,0x70,0x3b,0xc2,0x27,0x1b,0xe8,0x00,0x05,0x40,0x43,0x36,0xc2,0x57,0x11, + 0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0x70,0x3c,0xc2,0x27,0x1b,0xe8,0x00,0x05,0x60, + 0x43,0x36,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0x27,0x1b,0x1b,0x00, + 0x00,0x01,0x27,0xf0,0xf0,0x00,0x00,0x01,0xdf,0x80,0x0a,0x27,0x1b,0xe8,0x00,0x05, + 0x20,0x70,0x10,0xa8,0x27,0x1b,0xe8,0x00,0x05,0x40,0x70,0x10,0xa8,0x27,0x1b,0xe8, + 0x00,0x05,0x60,0x70,0x10,0xa8,0x27,0x1b,0x1b,0x00,0x00,0x01,0x27,0xf0,0xf0,0x00, + 0x00,0x01,0xdf,0x80,0x0a,0x27,0x1b,0xe8,0x00,0x04,0xc0,0x70,0x10,0xa8,0x27,0x1b, + 0xe8,0x00,0x04,0xe0,0x70,0x10,0xa8,0x27,0x1b,0xe8,0x00,0x05,0x00,0x70,0x10,0xa8, + 0x27,0x1b,0xe8,0x00,0x05,0x20,0x70,0x10,0xa8,0x27,0x1b,0xe8,0x00,0x05,0x40,0x70, + 0x10,0xa8,0x27,0x1b,0xe8,0x00,0x05,0x60,0x70,0x10,0xa8,0x27,0x1b,0x1b,0x00,0x00, + 0x01,0xdf,0x80,0x0a,0x27,0xa8,0xa8,0xff,0xff,0xfc,0x4f,0xa8,0xc2,0x08,0x00,0x00, + 0x27,0x1f,0xf8,0x00,0x05,0x90,0x23,0xb8,0xc2,0x27,0x1f,0xf8,0x00,0x05,0x7f,0x43, + 0xb8,0xc2,0xdf,0x80,0x0a,0x27,0xa8,0xa8,0xff,0xff,0xf8,0x4f,0xa8,0xc2,0x04,0x00, + 0x00,0x27,0x1f,0xf8,0x00,0x05,0x90,0x23,0xb8,0xc2,0x27,0x1f,0xf8,0x00,0x05,0x7f, + 0x43,0xb8,0xc2,0xdf,0x80,0x0a,0x27,0xa8,0xa8,0xff,0xff,0xfe,0x4f,0xa8,0xc2,0x10, + 0x00,0x00,0x27,0x1f,0xf8,0x00,0x05,0x90,0x23,0xb8,0xc2,0x27,0x1f,0xf8,0x00,0x05, + 0x7f,0x43,0xb8,0xc2,0xdf,0x80,0x0a,0x93,0x00,0xa8,0x37,0x1e,0xc2,0x00,0x02,0x28, + 0xd1,0x40,0x00,0x48,0xa8,0xa8,0x1f,0xa8,0xa8,0x80,0x00,0x00,0x47,0xa8,0xc2,0x40, + 0x00,0x00,0x27,0x1f,0xf8,0x00,0x05,0x90,0x23,0xb8,0xc2,0x27,0x1f,0xf8,0x00,0x05, + 0x7f,0x43,0xb8,0xc2,0xdf,0x80,0x0a,0x37,0x32,0xc2,0x00,0x00,0x01,0xc9,0x88,0xc6, + 0x0f,0xb0,0x30,0x00,0x0f,0xff,0x70,0x10,0xb0,0xc9,0x08,0xdb,0xc7,0x88,0xdf,0x20, + 0x00,0xb2,0x27,0x1b,0x1b,0x00,0x00,0x01,0xdf,0x80,0x0a,0x70,0xb0,0x1f,0x70,0x10, + 0xb0,0x0f,0x1f,0x30,0x00,0x0f,0xff,0xc9,0x08,0xcd,0xc7,0x88,0xdf,0x20,0x00,0xb0, + 0x0f,0x1f,0x30,0xff,0xf0,0x00,0xc9,0x08,0xdb,0x47,0x30,0x30,0x00,0x10,0x00,0xc7, + 0x88,0xdf,0x20,0x00,0x1f,0x47,0x1f,0xc2,0x00,0x10,0x00,0x5f,0xb0,0xb2,0x00,0x00, + 0x01,0x27,0x1b,0x1b,0x00,0x00,0x01,0xdf,0x80,0x0a,0x70,0xb0,0xb2,0x27,0x1b,0x1b, + 0x00,0x00,0x01,0xdf,0x80,0x0a,0x37,0x2d,0xc2,0x00,0x00,0x01,0xca,0x88,0xfb,0x37, + 0x1b,0xc2,0x00,0x00,0x02,0xca,0x08,0xf7,0x37,0x1b,0xc2,0x00,0x00,0x0a,0xca,0x08, + 0xf3,0x37,0x1b,0xc2,0x00,0x00,0x16,0xca,0x08,0xef,0x27,0x30,0xc2,0x00,0x02,0x4c, + 0xd1,0x40,0x00,0xdf,0x80,0x0a,0x27,0x30,0xc2,0x00,0x02,0x45,0xd1,0x40,0x00,0xdf, + 0x80,0x0a,0x27,0x30,0xc2,0x00,0x02,0x36,0xd1,0x40,0x00,0xdf,0x80,0x0a,0x27,0x30, + 0xc2,0x00,0x02,0x27,0xd1,0x40,0x00,0xdf,0x80,0x0a,0x37,0x1b,0xc2,0x00,0x00,0x01, + 0xca,0x09,0x02,0x27,0x30,0xc2,0x00,0x02,0x5e,0xd1,0x40,0x00,0xdf,0x80,0x0a,0x27, + 0x30,0xc2,0x00,0x02,0x4f,0xd1,0x40,0x00,0xdf,0x80,0x0a,0x37,0x32,0xc2,0x00,0x00, + 0x01,0xc9,0x89,0x13,0x0f,0xb2,0xc2,0x00,0x0f,0xff,0xc9,0x09,0x2b,0x0f,0x98,0x30, + 0x00,0x0f,0xff,0x27,0x30,0xc2,0x00,0x09,0x2f,0xc7,0xc0,0x00,0xcf,0x89,0x2b,0xdf, + 0x80,0x0a,0x70,0xb2,0x1f,0x0f,0x1f,0xc2,0x00,0x0f,0xff,0xc9,0x09,0x1c,0x0f,0x98, + 0x30,0x00,0x0f,0xff,0x27,0x30,0xc2,0x00,0x09,0x2f,0xc7,0xc0,0x00,0x0f,0x1f,0xc2, + 0xff,0xf0,0x00,0xc9,0x09,0x2b,0x27,0xe8,0xe8,0x00,0x00,0x01,0x0f,0x9a,0x30,0xff, + 0xf0,0x00,0x47,0x30,0x30,0x00,0x10,0x00,0x27,0x30,0xc2,0x00,0x09,0x2f,0xc7,0xc0, + 0x00,0x27,0xe8,0xe8,0x00,0x00,0x01,0xdf,0x80,0x0a,0x27,0xe8,0xe8,0x00,0x00,0x02, + 0x70,0x98,0x9a,0xdf,0x80,0x0a,0xcf,0x89,0x52,0xcf,0x89,0x47,0xcf,0x89,0x3e,0xcf, + 0x89,0x33,0xc7,0x89,0xb0,0x0b,0x05,0x30,0xc7,0x89,0xb0,0x0b,0x05,0xa8,0x47,0x30, + 0xc2,0x00,0x00,0x01,0x57,0xa8,0xc2,0x00,0x01,0x00,0x5f,0xa8,0xa8,0x01,0x00,0x00, + 0xdf,0x80,0x0a,0xc7,0x89,0xb0,0x0b,0x05,0xa8,0x47,0xa8,0xc2,0x00,0x00,0x01,0x57, + 0xa8,0xc2,0x00,0x01,0x00,0x5f,0xa8,0xa8,0x01,0x00,0x00,0xdf,0x80,0x0a,0xc7,0x89, + 0xb0,0x0b,0x05,0x30,0xc7,0x89,0xb0,0x0b,0x05,0xa8,0x47,0x30,0xc2,0x00,0x00,0x01, + 0x57,0x30,0xc2,0x00,0x01,0x00,0x5f,0xa8,0xa8,0x01,0x00,0x00,0xdf,0x80,0x0a,0xc7, + 0x89,0xb0,0x0b,0x05,0x30,0xc7,0x89,0xb0,0x0b,0x05,0xa8,0x47,0x30,0xc2,0x00,0x00, + 0x01,0x5f,0xa8,0x30,0x00,0x01,0x00,0xc7,0x89,0xb0,0x0b,0x05,0xa8,0x47,0x30,0xc2, + 0x00,0x00,0x01,0x5f,0xa8,0xa8,0x01,0x00,0x00,0xdf,0x80,0x0a,0x37,0x32,0xc2,0x00, + 0x00,0x01,0xc9,0x89,0x6c,0x0f,0xb2,0xc2,0x00,0x0f,0xff,0xc9,0x09,0x83,0xc7,0x89, + 0xd8,0x0b,0x01,0x9a,0x27,0x5d,0x5d,0x00,0x00,0x02,0xdf,0x80,0x0a,0x70,0xb2,0x1f, + 0x70,0x10,0x98,0x0f,0x1f,0xc2,0x00,0x0f,0xff,0xc9,0x09,0x75,0xc7,0x89,0xd8,0x0b, + 0x01,0x98,0x27,0x5d,0x5d,0x00,0x00,0x02,0x0f,0x1f,0xc2,0xff,0xf0,0x00,0xc9,0x09, + 0x81,0xc7,0x89,0xd8,0x0b,0x01,0x1f,0x47,0x1f,0xc2,0x00,0x10,0x00,0x5f,0x98,0x9a, + 0x00,0x00,0x01,0x27,0x5d,0x5d,0x00,0x00,0x02,0xdf,0x80,0x0a,0x70,0x98,0x9a,0xdf, + 0x80,0x0a,0x70,0x10,0x9a,0xdf,0x80,0x0a,0x37,0x32,0xc2,0x00,0x00,0x02,0xc9,0x09, + 0x94,0x37,0x32,0xc2,0x00,0x00,0x03,0xc9,0x09,0xa3,0x70,0x30,0xc2,0xd1,0x40,0x00, + 0x21,0x10,0x1e,0x22,0x10,0x30,0x27,0x1e,0xf0,0x00,0x03,0xff,0xc7,0x89,0xd8,0x0b, + 0xb0,0x9a,0xdf,0x80,0x0a,0x70,0x30,0xc2,0xd1,0x40,0x00,0x21,0x10,0x1e,0x22,0x10, + 0x30,0x27,0x1e,0xf0,0x00,0x03,0xff,0xc7,0x89,0xd8,0x0b,0xb0,0x98,0xc7,0x89,0xd8, + 0x0b,0xb0,0x1f,0x47,0x1f,0xc2,0x00,0x10,0x00,0x5f,0x98,0x9a,0x00,0x00,0x01,0xdf, + 0x80,0x0a,0x70,0x30,0xc2,0xd1,0x40,0x00,0x21,0x10,0x1e,0x22,0x10,0x30,0x27,0x1e, + 0xf0,0x00,0x03,0xff,0xc7,0x89,0xd8,0x0b,0xb0,0x98,0x47,0x98,0xc2,0x00,0x10,0x00, + 0x5f,0x98,0x9a,0x00,0x00,0x01,0xdf,0x80,0x0a,0x70,0x1e,0xc2,0x23,0x3e,0x3e,0x27, + 0x1e,0xc2,0x00,0x02,0x10,0xd1,0x40,0x00,0x70,0x1d,0xc2,0x33,0x1e,0x1d,0xca,0x09, + 0xd4,0x37,0x1d,0xd0,0x00,0x03,0xff,0x37,0x1d,0xc2,0x00,0x02,0x10,0xd5,0x40,0x00, + 0x40,0x1c,0x1c,0x70,0x5b,0xc2,0xd1,0x40,0x00,0x32,0x5a,0xc2,0xc9,0x89,0xc4,0xc7, + 0x8a,0x0d,0xcf,0x89,0xc0,0xc7,0x8a,0x00,0x90,0x00,0x33,0x22,0x00,0x5b,0x37,0x5b, + 0xc2,0x00,0x0f,0xff,0xca,0x09,0xcc,0x97,0x80,0x5b,0x00,0x0f,0x00,0x44,0x33,0xc2, + 0x0b,0x90,0xc2,0x13,0x1c,0xc2,0x27,0x1d,0x1d,0x00,0x00,0x18,0x5f,0x10,0x1c,0x00, + 0x00,0x00,0xdf,0x80,0x0a,0x40,0x1c,0xc2,0x5f,0x10,0x1c,0x00,0x00,0x00,0xdf,0x80, + 0x0a,0x70,0x1e,0xc2,0x23,0x3e,0x3e,0x27,0x1e,0xc2,0x00,0x02,0x10,0xd1,0x40,0x00, + 0x70,0x1d,0xc2,0x33,0x1e,0x1d,0xca,0x09,0xfc,0x37,0x1d,0xd0,0x00,0x03,0xff,0x37, + 0x1d,0xc2,0x00,0x02,0x10,0xd5,0x40,0x00,0x40,0x1c,0x1c,0x70,0x5c,0xc2,0xd1,0x40, + 0x00,0x32,0x5a,0xc2,0xc9,0x89,0xec,0xc7,0x8a,0x0d,0xcf,0x89,0xe8,0xc7,0x8a,0x00, + 0x90,0x00,0x33,0x22,0x00,0x5c,0x37,0x5c,0xc2,0x00,0x0f,0xff,0xca,0x09,0xf4,0x97, + 0x80,0x5c,0x00,0x0f,0x00,0x44,0x33,0xc2,0x0b,0x90,0xc2,0x13,0x1c,0xc2,0x27,0x1d, + 0x1d,0x00,0x00,0x18,0x5f,0x10,0x1c,0x00,0x00,0x00,0xdf,0x80,0x0a,0x40,0x1c,0xc2, + 0x5f,0x10,0x1c,0x00,0x00,0x00,0xdf,0x80,0x0a,0x4b,0x6c,0xc2,0xd9,0x00,0x02,0xcb, + 0x8a,0x04,0x23,0x6e,0xc2,0x33,0x6d,0xc2,0xdb,0x80,0x02,0x0f,0x73,0x73,0x00,0xff, + 0xff,0x17,0x73,0x58,0x0e,0x00,0x00,0xc7,0x8b,0x0b,0x70,0x10,0x6c,0xdf,0x80,0x0a, + 0x27,0x6c,0x6c,0x00,0x00,0x00,0xd9,0x00,0x02,0x97,0x80,0x58,0x0e,0x00,0x00,0xc7, + 0x8b,0x0b,0x70,0x10,0x6c,0xdf,0x80,0x0a,0xc7,0x89,0xb0,0x0b,0x07,0x5e,0x4f,0x5e, + 0xc2,0x00,0x01,0x00,0x1b,0x63,0x5e,0x0f,0x5e,0x5e,0x00,0xff,0x00,0x47,0x5e,0x60, + 0x04,0x00,0x00,0x47,0x5e,0xc2,0x02,0x00,0x00,0x1b,0x60,0x60,0x47,0x5e,0x5e,0x01, + 0x00,0x00,0x70,0x5e,0xc2,0x80,0x00,0xc2,0x1b,0x5e,0x5f,0x80,0x00,0xc2,0x1b,0x5f, + 0x5f,0x80,0x00,0xc2,0x1b,0x5f,0x5f,0x80,0x00,0xc2,0x1b,0x5f,0x5f,0x80,0x00,0xc2, + 0x1b,0x5f,0x5f,0x80,0x00,0xc2,0x1b,0x5f,0x5f,0x80,0x00,0xc2,0x1b,0x5f,0xc2,0x0b, + 0x00,0x5f,0xc9,0x0a,0x36,0x1f,0x60,0x60,0x00,0x80,0x03,0x4f,0x63,0xc2,0x00,0x01, + 0x00,0x1b,0x60,0x63,0xdf,0x80,0x0a,0xc7,0x89,0xb0,0x0b,0x00,0x5e,0x40,0x10,0xc2, + 0x70,0x63,0xc2,0x82,0x00,0x63,0x47,0x63,0xc2,0x00,0x01,0x00,0x0b,0x00,0xc2,0x1b, + 0x5e,0xc2,0xc9,0x0a,0x46,0x1f,0x63,0x63,0x00,0x80,0x05,0xdf,0x80,0x0a,0x70,0x66, + 0xc2,0x23,0x3e,0xc2,0xcb,0x8a,0x67,0x27,0x66,0xc2,0x00,0x02,0x10,0xd1,0x40,0x00, + 0x48,0x68,0x68,0x70,0x66,0x1e,0xc7,0x89,0xb0,0x27,0x66,0xd8,0x00,0x03,0xff,0x0b, + 0x98,0xc2,0x13,0x68,0x68,0xc7,0x8a,0x74,0x70,0x11,0x66,0x70,0x11,0x1e,0x37,0x3e, + 0xc2,0xff,0xff,0xf8,0xca,0x8a,0x5e,0xc7,0x89,0xb0,0x0b,0x07,0x68,0xc7,0x8a,0x74, + 0xcf,0x8a,0x57,0x37,0x3e,0x1e,0x00,0x00,0x00,0x37,0x1e,0x66,0x00,0x00,0x08,0xc7, + 0x89,0xb0,0x27,0x1e,0xd8,0x00,0x03,0xff,0x0b,0x98,0x68,0xdf,0x80,0x0a,0x93,0x00, + 0x66,0x37,0x3e,0x1e,0x00,0x00,0x00,0x27,0x1e,0xc2,0x00,0x02,0x10,0xd1,0x40,0x00, + 0x48,0x68,0x68,0xc7,0x89,0xb0,0x27,0x1e,0xd8,0x00,0x03,0xff,0x0b,0x98,0xc2,0x13, + 0x68,0x68,0xdf,0x80,0x0a,0x70,0x69,0xd8,0x37,0x67,0xc2,0x00,0x00,0x02,0xca,0x83, + 0x5a,0x27,0x67,0xc2,0x00,0x0a,0x7b,0xcf,0xc0,0x00,0xcf,0x8a,0x7e,0xcf,0x8a,0x94, + 0xcf,0x8a,0x9a,0x97,0x80,0x67,0x00,0x00,0x02,0x70,0x9a,0xc2,0x0f,0xd8,0xd8,0x00, + 0x00,0x0f,0x27,0xd8,0xc2,0x00,0x05,0xe0,0x33,0x6a,0xc2,0xc9,0x8a,0x8c,0x70,0x9a, + 0xc2,0x0f,0xd8,0xd8,0x00,0x00,0x0f,0x27,0xd8,0x6a,0x00,0x05,0xe0,0x70,0x69,0xd8, + 0x70,0x68,0xc2,0x13,0x98,0x9a,0x0f,0xd8,0xd8,0x00,0x00,0x0f,0x27,0xd8,0x69,0x00, + 0x05,0xe0,0xdf,0x80,0x0a,0x97,0x80,0x67,0x00,0x00,0x00,0x4f,0x68,0xc2,0x00,0x01, + 0x00,0x13,0x98,0x98,0xdf,0x80,0x0a,0x97,0x80,0x67,0x00,0x00,0x01,0x4f,0x68,0x98, + 0x01,0x00,0x00,0xdf,0x80,0x0a,0x70,0x4c,0xc2,0x33,0xe0,0x31,0xcb,0x0a,0xa4,0x27, + 0x31,0x31,0xff,0xfe,0x40,0x27,0x31,0x31,0x00,0x00,0x20,0xca,0x8a,0xa8,0xcf,0x8a, + 0x9f,0xc7,0x8a,0xc1,0x70,0x4c,0xf8,0x70,0x46,0xc2,0xd6,0x00,0x1f,0x43,0xb2,0xba, + 0x70,0xf8,0x4c,0x70,0x4d,0xc2,0xd1,0x40,0x00,0xd6,0x00,0x1f,0xc7,0x8a,0xbe,0x91, + 0x00,0x4d,0x37,0x4c,0xc2,0x00,0x07,0xc0,0xc9,0x8a,0xb8,0x97,0x80,0x4c,0x00,0x06, + 0x00,0x37,0x4d,0xc2,0x00,0x0f,0x00,0xc9,0x8a,0xbd,0x97,0x80,0x4d,0x00,0x0d,0x40, + 0xdf,0x80,0x0a,0x44,0x92,0xc2,0xd0,0xc0,0x00,0xdf,0x80,0x0a,0x0f,0x4b,0xc2,0x00, + 0x00,0x04,0xc9,0x0a,0xcf,0x70,0x10,0x47,0x70,0x10,0x48,0x27,0x46,0x46,0x00,0x00, + 0x00,0xc9,0x8a,0xcf,0x24,0x10,0xc2,0xc9,0x8a,0xcf,0x0f,0xcd,0xcd,0xff,0xdf,0xff, + 0x0f,0x4b,0x4b,0xff,0xff,0xfb,0x0f,0x4b,0xc2,0x00,0x00,0x08,0xc9,0x0a,0xdb,0x0f, + 0xd7,0xc2,0x00,0x00,0x01,0xc9,0x8a,0xdb,0x70,0x49,0x47,0x70,0x4a,0x48,0x17,0xcd, + 0xcd,0x00,0x20,0x00,0x0f,0x4b,0x4b,0xff,0xff,0xf7,0x0f,0x4b,0xc2,0x00,0x00,0x01, + 0xc9,0x0a,0xee,0x70,0x47,0xc2,0x33,0x46,0xc2,0xcb,0x8a,0xe6,0x23,0x0b,0xc2,0xca, + 0x0a,0xeb,0x27,0x46,0x46,0xff,0xf0,0x00,0xcf,0x8a,0xee,0x33,0x0b,0xc2,0xcb,0x0a, + 0xeb,0x27,0x46,0x46,0x00,0x0f,0xff,0xcf,0x8a,0xee,0x0f,0x4b,0x4b,0xff,0xff,0xfe, + 0x70,0x47,0x46,0x0f,0x4b,0xc2,0x00,0x00,0x02,0xc9,0x0b,0x02,0xd5,0x03,0x3a,0x34, + 0x48,0xc2,0xca,0x8a,0xf9,0x33,0x0b,0xc2,0xcb,0x0a,0xfe,0x24,0x15,0xc2,0xd4,0x40, + 0x00,0xcf,0x8b,0x02,0x23,0x0b,0xc2,0xca,0x0a,0xfe,0x24,0x0b,0xc2,0xd4,0x40,0x00, + 0xcf,0x8b,0x02,0x0f,0x4b,0x4b,0xff,0xff,0xfd,0x70,0x48,0xc2,0xd4,0x40,0x00,0xdf, + 0x80,0x0a,0x70,0x93,0xb0,0x27,0xf0,0xf0,0x00,0x00,0x10,0xdf,0x80,0x0a,0x70,0x92, + 0xb0,0x27,0xf0,0xf0,0x00,0x00,0x10,0xdf,0x80,0x0a,0x70,0xd8,0x6f,0x70,0x51,0xd8, + 0x70,0x58,0x9a,0x70,0xd8,0x51,0x0f,0xcb,0xd8,0x00,0x20,0x00,0xd9,0x00,0x02,0x47, + 0x58,0xc9,0x00,0x01,0x00,0x70,0x00,0x52,0x70,0x6f,0xd8,0xdf,0x80,0x0a,0x93,0x00, + 0x43,0x91,0x00,0x45,0x70,0xa2,0xc2,0x37,0xe0,0xc2,0x00,0x07,0xc0,0xcb,0x8b,0x1f, + 0x97,0x80,0xe0,0x00,0x06,0x00,0x27,0xe0,0xc2,0x00,0x07,0x40,0xd1,0x40,0x00,0x32, + 0x4d,0xc2,0xc9,0x0b,0x2b,0x48,0x11,0x41,0x4f,0xa0,0xc1,0x00,0x00,0x08,0x70,0x45, + 0xc2,0xd1,0x40,0x00,0x70,0x43,0xc2,0xdf,0xc0,0x0b,0x70,0xa3,0xc2,0x37,0xe0,0xc2, + 0x00,0x06,0x00,0xcb,0x0b,0x31,0x97,0x80,0xe0,0x00,0x07,0xbf,0x70,0x00,0x4f,0x70, + 0x45,0xc2,0xd1,0x40,0x00,0x70,0x43,0xc2,0xdf,0xc0,0x0b,0x93,0x00,0x43,0x5b,0x10, + 0x44,0x70,0xd8,0x45,0x27,0x55,0xc2,0x00,0x0b,0x3c,0xcf,0xc0,0x00,0xcf,0x8b,0x42, + 0xcf,0x8b,0x47,0xcf,0x8b,0x4e,0xcf,0x8b,0x55,0xcf,0x8b,0x5a,0xcf,0x8b,0x61,0x4f, + 0xc9,0x53,0x01,0x00,0x00,0x27,0x55,0x55,0x00,0x00,0x01,0xcf,0x8b,0xed,0x4f,0x53, + 0xc2,0x00,0x00,0x01,0x5f,0xc9,0x53,0x00,0x01,0x00,0x27,0x55,0x55,0x00,0x00,0x01, + 0xcf,0x8b,0xed,0x4f,0x53,0xc2,0x00,0x00,0x01,0x5f,0xc9,0x53,0x00,0x00,0x01,0x27, + 0x55,0x55,0x00,0x00,0x01,0xcf,0x8b,0xed,0x4f,0xc9,0x54,0x01,0x00,0x00,0x27,0x55, + 0x55,0x00,0x00,0x01,0xcf,0x8b,0xed,0x4f,0x54,0xc2,0x00,0x00,0x01,0x5f,0xc9,0x54, + 0x00,0x01,0x00,0x27,0x55,0x55,0x00,0x00,0x01,0xcf,0x8b,0xed,0x4f,0x54,0xc2,0x00, + 0x00,0x01,0x5f,0xc9,0x54,0x00,0x00,0x01,0x70,0x53,0xc2,0x33,0x54,0xc2,0xc9,0x8b, + 0xe2,0x0f,0x53,0x56,0x7f,0x00,0x00,0x37,0x56,0xc2,0x10,0x00,0x00,0xcb,0x0b,0xe2, + 0x97,0x80,0xc2,0x00,0x0b,0x72,0x57,0x53,0xc2,0x00,0x01,0x00,0xcf,0xc0,0x00,0xcf, + 0x8b,0x82,0xcf,0x8b,0x88,0xcf,0x8b,0x90,0xcf,0x8b,0xa0,0xcf,0x8b,0xa8,0xcf,0x8b, + 0xae,0xcf,0x8b,0xb0,0xcf,0x8b,0xb4,0xcf,0x8b,0xb7,0xcf,0x8b,0xbd,0xcf,0x8b,0xce, + 0xcf,0x8b,0xd0,0xcf,0x8b,0xd3,0xcf,0x8b,0xd7,0xcf,0x8b,0xe2,0xcf,0x8b,0xe0,0x17, + 0x4b,0x4b,0x00,0x00,0x01,0x4f,0x53,0x47,0x00,0x00,0x80,0x70,0x47,0x49,0xcf,0x8b, + 0xec,0x17,0x4b,0x4b,0x00,0x00,0x02,0x0f,0x53,0x48,0x00,0xff,0xff,0x4f,0x48,0x48, + 0x00,0x00,0x80,0x70,0x48,0x4a,0xcf,0x8b,0xec,0x0f,0x53,0xc2,0x00,0x00,0x01,0xc9, + 0x8b,0x9a,0x27,0x57,0xc2,0x00,0x00,0x00,0xc9,0x0b,0x9f,0x17,0x4b,0x4b,0x00,0x00, + 0x0b,0x70,0x10,0x57,0xcf,0x8b,0x9f,0x17,0x4b,0x4b,0x00,0x00,0x07,0x70,0x00,0x57, + 0x70,0x10,0x47,0x70,0x10,0x48,0xcf,0x8b,0xec,0x0f,0x53,0xd8,0x00,0xff,0xff,0x27, + 0xd8,0xd8,0x00,0x04,0x20,0x17,0x98,0x58,0x03,0x00,0x00,0xc7,0x8b,0x0b,0xcf,0x8b, + 0xec,0x0f,0x59,0x59,0x00,0xff,0xff,0x17,0x59,0x58,0x04,0x00,0x00,0xc7,0x8b,0x0b, + 0xcf,0x8b,0xec,0x70,0x10,0x59,0xcf,0x8b,0xec,0x0f,0x53,0xc2,0x00,0x00,0x01,0x93, + 0x00,0x64,0xcf,0x8b,0xec,0x0f,0x53,0x4e,0x00,0xff,0xff,0xcf,0x8b,0xec,0x0f,0xd7, + 0x56,0x00,0x00,0x01,0x17,0x56,0x58,0x08,0x00,0x00,0xc7,0x8b,0x0b,0xcf,0x8b,0xec, + 0x70,0x69,0xc2,0x33,0x6a,0x56,0xca,0x0b,0xc2,0x27,0x56,0x56,0x00,0x00,0x10,0x17, + 0x56,0x58,0x09,0x00,0x00,0xc7,0x8b,0x0b,0x27,0x56,0xc2,0xff,0xff,0xff,0x70,0xf0, + 0x56,0x70,0x6a,0xf0,0xd6,0x40,0x00,0xc7,0x8c,0x23,0x70,0xf0,0x6a,0x70,0x56,0xf0, + 0xcf,0x8b,0xec,0xd7,0x01,0x00,0xcf,0x8b,0xec,0x0f,0x53,0x6d,0x00,0xff,0xff,0xcf, + 0x8b,0xec,0x0f,0x53,0xc2,0x00,0x00,0x01,0x4b,0x14,0x6c,0xcf,0x8b,0xec,0x70,0x5a, + 0xc2,0x33,0x5b,0xc2,0xca,0x0b,0xdb,0x23,0x6e,0xc2,0x0b,0x0f,0x56,0x17,0x56,0x58, + 0x0d,0x00,0x00,0xc7,0x8b,0x0b,0xcf,0x8b,0xec,0x70,0x10,0x55,0xd7,0x00,0x20,0x40, + 0x10,0xc2,0x4f,0x53,0x53,0x00,0x01,0x00,0x70,0x53,0xc2,0x57,0x54,0x53,0x00,0x01, + 0x00,0x5b,0x10,0x54,0x27,0x55,0x55,0xff,0xff,0xff,0xcf,0x8b,0xed,0x70,0x10,0x55, + 0x70,0x45,0xd8,0x47,0x44,0xc2,0x00,0x00,0x01,0x70,0x43,0xc2,0xdf,0xc0,0x0b,0x93, + 0x00,0x43,0x5b,0x10,0x44,0x70,0xd8,0x45,0x27,0x52,0xc2,0x00,0x00,0x00,0xc9,0x8b, + 0xfb,0x70,0x51,0xc2,0x33,0x50,0xc2,0xc9,0x0c,0x01,0x27,0x52,0xc2,0x00,0x0b,0xfe, + 0xcf,0xc0,0x00,0xcf,0x8c,0x06,0xcf,0x8c,0x10,0xcf,0x8c,0x1a,0x70,0x45,0xd8,0x47, + 0x44,0xc2,0x00,0x00,0x01,0x70,0x43,0xc2,0xdf,0xc0,0x0b,0x70,0x50,0xd8,0x47,0x98, + 0xc9,0x00,0x01,0x00,0x27,0x52,0x52,0x00,0x00,0x01,0x70,0x45,0xd8,0x47,0x44,0xc2, + 0x00,0x00,0x01,0x70,0x43,0xc2,0xdf,0xc0,0x0b,0x70,0x50,0xd8,0x47,0x98,0xc9,0x01, + 0x00,0x00,0x27,0x52,0x52,0x00,0x00,0x01,0x70,0x45,0xd8,0x47,0x44,0xc2,0x00,0x00, + 0x01,0x70,0x43,0xc2,0xdf,0xc0,0x0b,0x70,0x50,0xd8,0x70,0x9a,0xc9,0x70,0xd8,0x50, + 0x70,0x10,0x52,0x70,0x45,0xd8,0x47,0x44,0xc2,0x00,0x00,0x01,0x70,0x43,0xc2,0xdf, + 0xc0,0x0b,0x70,0xb2,0x58,0xc7,0x8b,0x0b,0xdf,0x80,0x0a,0x95,0x00,0x45,0x93,0x00, + 0x43,0x5b,0x10,0x44,0x70,0x5a,0xc2,0xd5,0x40,0x00,0x27,0x72,0xc2,0x00,0x0c,0x2e, + 0xcf,0xc0,0x00,0xcf,0x8c,0x31,0xcf,0x8c,0x38,0xcf,0x8c,0x41,0x0f,0xc5,0x70,0x00, + 0xff,0xff,0x4f,0x70,0x70,0x00,0x01,0x00,0x27,0x72,0x72,0x00,0x00,0x01,0xcf,0x8c, + 0x4c,0x0f,0xc5,0x71,0x00,0xff,0xff,0x47,0x71,0xc2,0x01,0x00,0x00,0x13,0x70,0xc2, + 0x5b,0x10,0x70,0x27,0x72,0x72,0x00,0x00,0x01,0xcf,0x8c,0x45,0x0f,0xc5,0xc2,0x00, + 0xff,0xff,0x13,0x70,0xc2,0x70,0x10,0x72,0xd4,0x40,0x00,0x26,0x00,0x5a,0x37,0x5a, + 0xc2,0x00,0x0f,0xff,0xca,0x0c,0x4c,0x97,0x80,0x5a,0x00,0x0f,0x00,0x70,0x45,0xc2, + 0xd5,0x40,0x00,0x47,0x44,0xc2,0x00,0x00,0x01,0x70,0x43,0xc2,0xdf,0xc0,0x0b,0x70, + 0xd8,0x45,0x0f,0xd7,0xc2,0x00,0x00,0x01,0xc9,0x0c,0x58,0x0f,0xcd,0xcd,0xff,0xdf, + 0xff,0x70,0x45,0xd8,0xdf,0xc0,0x0b,0x70,0xb0,0xc2,0x23,0x98,0xb2,0x33,0x98,0x9b, + 0x70,0xb0,0xc2,0x23,0x98,0xb2,0x33,0x98,0x9b,0xdf,0x80,0x0a,0x70,0xb0,0x38,0x70, + 0x98,0x39,0x40,0x38,0xc2,0x64,0x39,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00, + 0x9a,0x44,0x38,0xc2,0x50,0x39,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xb3, + 0xdf,0x80,0x0a,0x70,0xb0,0x38,0x70,0x98,0x39,0x40,0x38,0xc2,0x64,0x39,0xc2,0x57, + 0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xb3,0x44,0x38,0xc2,0x50,0x39,0xc2,0x57,0x11, + 0xc2,0x02,0x00,0x00,0x86,0x00,0x9a,0xdf,0x80,0x0a,0x70,0xb2,0xc2,0x23,0xb3,0xb2, + 0x33,0xb0,0xb0,0x70,0x9b,0xc2,0x23,0x9a,0x9b,0x33,0x98,0x98,0xdf,0x80,0x0a,0x41, + 0xb2,0xc2,0x55,0x9b,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0x96,0xdf,0x80, + 0x0a,0x41,0xb2,0xc2,0x65,0x9b,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0x96, + 0xdf,0x80,0x0a,0xff,0xff,0x30,0xdf,0xde +}; diff --git a/os/pc/dat.h b/os/pc/dat.h new file mode 100644 index 00000000..78a0e4a8 --- /dev/null +++ b/os/pc/dat.h @@ -0,0 +1,258 @@ +typedef struct Conf Conf; +typedef struct FPU FPU; +typedef struct FPenv FPenv; +typedef ulong Instr; +typedef struct ISAConf ISAConf; +typedef struct Label Label; +typedef struct Lock Lock; +typedef struct MMU MMU; +typedef struct Mach Mach; +typedef struct Notsave Notsave; +typedef struct PCArch PCArch; +typedef struct Pcidev Pcidev; +typedef struct PCMmap PCMmap; +typedef struct PCMslot PCMslot; +typedef struct Page Page; +typedef struct PMMU PMMU; +typedef struct Segdesc Segdesc; +typedef struct Ureg Ureg; +typedef struct Vctl Vctl; + +#pragma incomplete Ureg +#pragma incomplete Vctl + + +struct Lock +{ + ulong key; + ulong sr; + ulong pc; + ulong pri; +}; + +struct Label +{ + ulong sp; + ulong pc; +}; + +/* + * FPenv.status + */ +enum +{ + FPINIT, + FPACTIVE, + FPINACTIVE, +}; + +/* + * This structure must agree with FPsave and FPrestore asm routines + */ +struct FPenv +{ + ushort control; + ushort r1; + ushort status; + ushort r2; + ushort tag; + ushort r3; + ulong pc; + ushort selector; + ushort r4; + ulong operand; + ushort oselector; + ushort r5; +}; + +/* + * This structure must agree with fpsave and fprestore asm routines + */ +struct FPU +{ + FPenv env; + uchar regs[80]; /* floating point registers */ +}; + +struct Conf +{ + ulong nmach; /* processors */ + ulong nproc; /* processes */ + ulong monitor; /* has monitor? */ + ulong npage0; /* total physical pages of memory */ + ulong npage1; /* total physical pages of memory */ + ulong npage; /* total physical pages of memory */ + ulong base0; /* base of bank 0 */ + ulong base1; /* base of bank 1 */ + ulong copymode; /* 0 is copy on write, 1 is copy on reference */ + ulong ialloc; /* max interrupt time allocation in bytes */ + ulong pipeqsize; /* size in bytes of pipe queues */ + int nuart; /* number of uart devices */ +}; + +#include "../port/portdat.h" + +typedef struct { + ulong link; /* link (old TSS selector) */ + ulong esp0; /* privilege level 0 stack pointer */ + ulong ss0; /* privilege level 0 stack selector */ + ulong esp1; /* privilege level 1 stack pointer */ + ulong ss1; /* privilege level 1 stack selector */ + ulong esp2; /* privilege level 2 stack pointer */ + ulong ss2; /* privilege level 2 stack selector */ + ulong cr3; /* page directory base register */ + ulong eip; /* instruction pointer */ + ulong eflags; /* flags register */ + ulong eax; /* general registers */ + ulong ecx; + ulong edx; + ulong ebx; + ulong esp; + ulong ebp; + ulong esi; + ulong edi; + ulong es; /* segment selectors */ + ulong cs; + ulong ss; + ulong ds; + ulong fs; + ulong gs; + ulong ldt; /* selector for task's LDT */ + ulong iomap; /* I/O map base address + T-bit */ +} Tss; + +struct Segdesc +{ + ulong d0; + ulong d1; +}; + +struct Mach +{ + int machno; /* physical id of processor (KNOWN TO ASSEMBLY) */ + ulong splpc; /* pc of last caller to splhi */ + + ulong* pdb; /* page directory base for this processor (va) */ + Tss* tss; /* tss for this processor */ + Segdesc *gdt; /* gdt for this processor */ + + Proc* externup; /* extern register Proc *up */ + + ulong ticks; /* of the clock since boot time */ + Proc* proc; /* current process on this processor */ + Label sched; /* scheduler wakeup */ + Lock alarmlock; /* access to alarm list */ + void* alarm; /* alarms bound to this clock */ + int inclockintr; + + int nrdy; + int ilockdepth; + + int loopconst; + + Lock apictimerlock; + int cpumhz; + uvlong cyclefreq; /* Frequency of user readable cycle counter */ + uvlong cpuhz; + int cpuidax; + int cpuiddx; + char cpuidid[16]; + char* cpuidtype; + int havetsc; + int havepge; + uvlong tscticks; + uvlong tscoff; + int intr; + ulong spuriousintr; + int lastintr; + + vlong mtrrcap; + vlong mtrrdef; + vlong mtrrfix[11]; + vlong mtrrvar[32]; /* 256 max. */ + + int stack[1]; +}; + +struct +{ + Lock; + int machs; /* bitmap of active CPUs */ + int exiting; /* shutdown */ + int ispanic; /* shutdown in response to a panic */ + int thunderbirdsarego; /* lets the added processors continue to schedinit */ +}active; + + +/* + * routines for things outside the PC model, like power management + */ +struct PCArch +{ + char* id; + int (*ident)(void); /* this should be in the model */ + void (*reset)(void); /* this should be in the model */ + int (*serialpower)(int); /* 1 == on, 0 == off */ + int (*modempower)(int); /* 1 == on, 0 == off */ + + void (*intrinit)(void); + int (*intrenable)(Vctl*); + int (*intrvecno)(int); + int (*intrdisable)(int); + + void (*clockenable)(void); + uvlong (*fastclock)(uvlong*); + void (*timerset)(uvlong); +}; + +/* + * a parsed plan9.ini line + */ +#define NISAOPT 8 + +struct ISAConf { + char *type; + ulong port; + int irq; + ulong dma; + ulong mem; + ulong size; + ulong freq; + + int nopt; + char *opt[NISAOPT]; +}; + +extern PCArch *arch; /* PC architecture */ + +/* + * Each processor sees its own Mach structure at address MACHADDR. + * However, the Mach structures must also be available via the per-processor + * MMU information array machp, mainly for disambiguation and access to + * the clock which is only maintained by the bootstrap processor (0). + */ +Mach* machp[MAXMACH]; + +#define MACHP(n) (machp[n]) + +extern Mach *m; +//extern Proc *up; +#define up (((Mach*)MACHADDR)->externup) + +extern int swcursor; + +/* + * hardware info about a device + */ +typedef struct { + ulong port; + int size; +} Devport; + +struct DevConf +{ + ulong intnum; /* interrupt number */ + char *type; /* card type, malloced */ + int nports; /* Number of ports */ + Devport *ports; /* The ports themselves */ +}; diff --git a/os/pc/devarch.c b/os/pc/devarch.c new file mode 100644 index 00000000..6a7b52fe --- /dev/null +++ b/os/pc/devarch.c @@ -0,0 +1,940 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +typedef struct IOMap IOMap; +struct IOMap +{ + IOMap *next; + int reserved; + char tag[13]; + ulong start; + ulong end; +}; + +static struct +{ + Lock; + IOMap *m; + IOMap *free; + IOMap maps[32]; // some initial free maps + + QLock ql; // lock for reading map +} iomap; + +enum { + Qdir = 0, + Qioalloc = 1, + Qiob, + Qiow, + Qiol, + Qbase, + + Qmax = 16, +}; + +typedef long Rdwrfn(Chan*, void*, long, vlong); + +static Rdwrfn *readfn[Qmax]; +static Rdwrfn *writefn[Qmax]; + +static Dirtab archdir[Qmax] = { + ".", { Qdir, 0, QTDIR }, 0, 0555, + "ioalloc", { Qioalloc, 0 }, 0, 0444, + "iob", { Qiob, 0 }, 0, 0660, + "iow", { Qiow, 0 }, 0, 0660, + "iol", { Qiol, 0 }, 0, 0660, +}; +Lock archwlock; /* the lock is only for changing archdir */ +int narchdir = Qbase; +int (*_pcmspecial)(char*, ISAConf*); +void (*_pcmspecialclose)(int); + +static int doi8253set = 1; + +/* + * Add a file to the #P listing. Once added, you can't delete it. + * You can't add a file with the same name as one already there, + * and you get a pointer to the Dirtab entry so you can do things + * like change the Qid version. Changing the Qid path is disallowed. + */ +Dirtab* +addarchfile(char *name, int perm, Rdwrfn *rdfn, Rdwrfn *wrfn) +{ + int i; + Dirtab d; + Dirtab *dp; + + memset(&d, 0, sizeof d); + strcpy(d.name, name); + d.perm = perm; + + lock(&archwlock); + if(narchdir >= Qmax){ + unlock(&archwlock); + return nil; + } + + for(i=0; inext){ + m = *l; + if (m->start < 0x400) continue; + i = m->start - port; + if(i > size) + break; + if(align > 0) + port = ((port+align-1)/align)*align; + else + port = m->end; + } + if(*l == nil){ + unlock(&iomap); + return -1; + } + m = iomap.free; + if(m == nil){ + print("ioalloc: out of maps"); + unlock(&iomap); + return port; + } + iomap.free = m->next; + m->next = *l; + m->start = port; + m->end = port + size; + m->reserved = 1; + strncpy(m->tag, tag, sizeof(m->tag)); + m->tag[sizeof(m->tag)-1] = 0; + *l = m; + + archdir[0].qid.vers++; + + unlock(&iomap); + return m->start; +} + +// +// alloc some io port space and remember who it was +// alloced to. if port < 0, find a free region. +// +int +ioalloc(int port, int size, int align, char *tag) +{ + IOMap *m, **l; + int i; + + lock(&iomap); + if(port < 0){ + // find a free port above 0x400 and below 0x1000 + port = 0x400; + for(l = &iomap.m; *l; l = &(*l)->next){ + m = *l; + if (m->start < 0x400) continue; + i = m->start - port; + if(i > size) + break; + if(align > 0) + port = ((port+align-1)/align)*align; + else + port = m->end; + } + if(*l == nil){ + unlock(&iomap); + return -1; + } + } else { + // Only 64KB I/O space on the x86. + if((port+size) > 0x10000){ + unlock(&iomap); + return -1; + } + // see if the space clashes with previously allocated ports + for(l = &iomap.m; *l; l = &(*l)->next){ + m = *l; + if(m->end <= port) + continue; + if(m->reserved && m->start == port && m->end == port + size) { + m->reserved = 0; + unlock(&iomap); + return m->start; + } + if(m->start >= port+size) + break; + unlock(&iomap); + return -1; + } + } + m = iomap.free; + if(m == nil){ + print("ioalloc: out of maps"); + unlock(&iomap); + return port; + } + iomap.free = m->next; + m->next = *l; + m->start = port; + m->end = port + size; + strncpy(m->tag, tag, sizeof(m->tag)); + m->tag[sizeof(m->tag)-1] = 0; + *l = m; + + archdir[0].qid.vers++; + + unlock(&iomap); + return m->start; +} + +void +iofree(int port) +{ + IOMap *m, **l; + + lock(&iomap); + for(l = &iomap.m; *l; l = &(*l)->next){ + if((*l)->start == port){ + m = *l; + *l = m->next; + m->next = iomap.free; + iomap.free = m; + break; + } + if((*l)->start > port) + break; + } + archdir[0].qid.vers++; + unlock(&iomap); +} + +int +iounused(int start, int end) +{ + IOMap *m; + + for(m = iomap.m; m; m = m->next){ + if(start >= m->start && start < m->end + || start <= m->start && end > m->start) + return 0; + } + return 1; +} + +static void +checkport(int start, int end) +{ + /* standard vga regs are OK */ + if(start >= 0x2b0 && end <= 0x2df+1) + return; + if(start >= 0x3c0 && end <= 0x3da+1) + return; + + if(iounused(start, end)) + return; + error(Eperm); +} + +static Chan* +archattach(char* spec) +{ + return devattach('P', spec); +} + +Walkqid* +archwalk(Chan* c, Chan *nc, char** name, int nname) +{ + return devwalk(c, nc, name, nname, archdir, narchdir, devgen); +} + +static int +archstat(Chan* c, uchar* dp, int n) +{ + return devstat(c, dp, n, archdir, narchdir, devgen); +} + +static Chan* +archopen(Chan* c, int omode) +{ + return devopen(c, omode, archdir, narchdir, devgen); +} + +static void +archclose(Chan*) +{ +} + +enum +{ + Linelen= 31, +}; + +static long +archread(Chan *c, void *a, long n, vlong offset) +{ + char *buf, *p; + int port; + ushort *sp; + ulong *lp; + IOMap *m; + Rdwrfn *fn; + + switch((ulong)c->qid.path){ + + case Qdir: + return devdirread(c, a, n, archdir, narchdir, devgen); + + case Qiob: + port = offset; + checkport(offset, offset+n); + for(p = a; port < offset+n; port++) + *p++ = inb(port); + return n; + + case Qiow: + if(n & 1) + error(Ebadarg); + checkport(offset, offset+n); + sp = a; + for(port = offset; port < offset+n; port += 2) + *sp++ = ins(port); + return n; + + case Qiol: + if(n & 3) + error(Ebadarg); + checkport(offset, offset+n); + lp = a; + for(port = offset; port < offset+n; port += 4) + *lp++ = inl(port); + return n; + + case Qioalloc: + break; + + default: + if(c->qid.path < narchdir && (fn = readfn[c->qid.path])) + return fn(c, a, n, offset); + error(Eperm); + break; + } + + if((buf = malloc(n)) == nil) + error(Enomem); + p = buf; + n = n/Linelen; + offset = offset/Linelen; + + lock(&iomap); + for(m = iomap.m; n > 0 && m != nil; m = m->next){ + if(offset-- > 0) + continue; + sprint(p, "%8lux %8lux %-12.12s\n", m->start, m->end-1, m->tag); + p += Linelen; + n--; + } + unlock(&iomap); + + n = p - buf; + memmove(a, buf, n); + free(buf); + + return n; +} + +static long +archwrite(Chan *c, void *a, long n, vlong offset) +{ + char *p; + int port; + ushort *sp; + ulong *lp; + Rdwrfn *fn; + + switch((ulong)c->qid.path){ + + case Qiob: + p = a; + checkport(offset, offset+n); + for(port = offset; port < offset+n; port++) + outb(port, *p++); + return n; + + case Qiow: + if(n & 1) + error(Ebadarg); + checkport(offset, offset+n); + sp = a; + for(port = offset; port < offset+n; port += 2) + outs(port, *sp++); + return n; + + case Qiol: + if(n & 3) + error(Ebadarg); + checkport(offset, offset+n); + lp = a; + for(port = offset; port < offset+n; port += 4) + outl(port, *lp++); + return n; + + default: + if(c->qid.path < narchdir && (fn = writefn[c->qid.path])) + return fn(c, a, n, offset); + error(Eperm); + break; + } + return 0; +} + +Dev archdevtab = { + 'P', + "arch", + + devreset, + devinit, + devshutdown, + archattach, + archwalk, + archstat, + archopen, + devcreate, + archclose, + archread, + devbread, + archwrite, + devbwrite, + devremove, + devwstat, +}; + +/* + * the following is a generic version of the + * architecture specific stuff + */ + +static int +unimplemented(int) +{ + return 0; +} + +static void +nop(void) +{ +} + +/* + * On a uniprocessor, you'd think that coherence could be nop, + * but it can't. We still need a barrier when using coherence() in + * device drivers. + * + * On VMware, it's safe (and a huge win) to set this to nop. + * Aux/vmware does this via the #P/archctl file. + */ +void (*coherence)(void) = nop; + +PCArch* arch; +extern PCArch* knownarch[]; + +PCArch archgeneric = { +.id= "generic", +.ident= 0, +.reset= i8042reset, +.serialpower= unimplemented, +.modempower= unimplemented, + +.intrinit= i8259init, +.intrenable= i8259enable, +.intrvecno= i8259vecno, +.intrdisable= i8259disable, + +.clockenable= i8253enable, +.fastclock= i8253read, +.timerset= i8253timerset, +}; + +typedef struct X86type X86type; +struct X86type { + int family; + int model; + int aalcycles; + char* name; +}; + +static X86type x86intel[] = +{ + { 4, 0, 22, "486DX", }, /* known chips */ + { 4, 1, 22, "486DX50", }, + { 4, 2, 22, "486SX", }, + { 4, 3, 22, "486DX2", }, + { 4, 4, 22, "486SL", }, + { 4, 5, 22, "486SX2", }, + { 4, 7, 22, "DX2WB", }, /* P24D */ + { 4, 8, 22, "DX4", }, /* P24C */ + { 4, 9, 22, "DX4WB", }, /* P24CT */ + { 5, 0, 23, "P5", }, + { 5, 1, 23, "P5", }, + { 5, 2, 23, "P54C", }, + { 5, 3, 23, "P24T", }, + { 5, 4, 23, "P55C MMX", }, + { 5, 7, 23, "P54C VRT", }, + { 6, 1, 16, "PentiumPro", },/* trial and error */ + { 6, 3, 16, "PentiumII", }, + { 6, 5, 16, "PentiumII/Xeon", }, + { 6, 6, 16, "Celeron", }, + { 6, 7, 16, "PentiumIII/Xeon", }, + { 6, 8, 16, "PentiumIII/Xeon", }, + { 6, 0xB, 16, "PentiumIII/Xeon", }, + { 0xF, 1, 16, "P4", }, /* P4 */ + { 0xF, 2, 16, "PentiumIV/Xeon", }, + + { 3, -1, 32, "386", }, /* family defaults */ + { 4, -1, 22, "486", }, + { 5, -1, 23, "P5", }, + { 6, -1, 16, "P6", }, + { 0xF, -1, 16, "P4", }, /* P4 */ + + { -1, -1, 16, "unknown", }, /* total default */ +}; + +/* + * The AMD processors all implement the CPUID instruction. + * The later ones also return the processor name via functions + * 0x80000002, 0x80000003 and 0x80000004 in registers AX, BX, CX + * and DX: + * K5 "AMD-K5(tm) Processor" + * K6 "AMD-K6tm w/ multimedia extensions" + * K6 3D "AMD-K6(tm) 3D processor" + * K6 3D+ ? + */ +static X86type x86amd[] = +{ + { 5, 0, 23, "AMD-K5", }, /* guesswork */ + { 5, 1, 23, "AMD-K5", }, /* guesswork */ + { 5, 2, 23, "AMD-K5", }, /* guesswork */ + { 5, 3, 23, "AMD-K5", }, /* guesswork */ + { 5, 6, 11, "AMD-K6", }, /* trial and error */ + { 5, 7, 11, "AMD-K6", }, /* trial and error */ + { 5, 8, 11, "AMD-K6-2", }, /* trial and error */ + { 5, 9, 11, "AMD-K6-III", },/* trial and error */ + + { 6, 1, 11, "AMD-Athlon", },/* trial and error */ + { 6, 2, 11, "AMD-Athlon", },/* trial and error */ + + { 4, -1, 22, "Am486", }, /* guesswork */ + { 5, -1, 23, "AMD-K5/K6", }, /* guesswork */ + { 6, -1, 11, "AMD-Athlon", },/* guesswork */ + { 0xF, -1, 11, "AMD64", }, /* guesswork */ + + { -1, -1, 11, "unknown", }, /* total default */ +}; + +/* + * WinChip 240MHz + */ +static X86type x86winchip[] = +{ + {5, 4, 23, "Winchip",}, /* guesswork */ + {6, 7, 23, "Via C3 Samuel 2 or Ezra",}, + {6, 8, 23, "Via C3 Ezra-T",}, + { -1, -1, 23, "unknown", }, /* total default */ +}; + +/* + * SiS 55x + */ +static X86type x86sis[] = +{ + {5, 0, 23, "SiS 55x",}, /* guesswork */ + { -1, -1, 23, "unknown", }, /* total default */ +}; + +static X86type *cputype; + +static void simplecycles(uvlong*); +void (*cycles)(uvlong*) = simplecycles; +void _cycles(uvlong*); /* in l.s */ + +static void +simplecycles(uvlong*x) +{ + *x = m->ticks; +} + +void +cpuidprint(void) +{ + int i; + char buf[128]; + + i = sprint(buf, "cpu%d: %dMHz ", m->machno, m->cpumhz); + if(m->cpuidid[0]) + i += sprint(buf+i, "%12.12s ", m->cpuidid); + sprint(buf+i, "%s (cpuid: AX 0x%4.4uX DX 0x%4.4uX)\n", + m->cpuidtype, m->cpuidax, m->cpuiddx); + print(buf); +} + +/* + * figure out: + * - cpu type + * - whether or not we have a TSC (cycle counter) + * - whether or not it supports page size extensions + * (if so turn it on) + * - whether or not it supports machine check exceptions + * (if so turn it on) + * - whether or not it supports the page global flag + * (if so turn it on) + */ +int +cpuidentify(void) +{ + char *p; + int family, model, nomce; + X86type *t, *tab; + ulong cr4; + vlong mca, mct; + + cpuid(m->cpuidid, &m->cpuidax, &m->cpuiddx); + if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0) + tab = x86amd; + else if(strncmp(m->cpuidid, "CentaurHauls", 12) == 0) + tab = x86winchip; + else if(strncmp(m->cpuidid, "SiS SiS SiS ", 12) == 0) + tab = x86sis; + else + tab = x86intel; + + family = X86FAMILY(m->cpuidax); + model = X86MODEL(m->cpuidax); + for(t=tab; t->name; t++) + if((t->family == family && t->model == model) + || (t->family == family && t->model == -1) + || (t->family == -1)) + break; + + m->cpuidtype = t->name; + + /* + * if there is one, set tsc to a known value + */ + if(m->cpuiddx & 0x10){ + m->havetsc = 1; + cycles = _cycles; + if(m->cpuiddx & 0x20) + wrmsr(0x10, 0); + } + + /* + * use i8253 to guess our cpu speed + */ + guesscpuhz(t->aalcycles); + + /* + * If machine check exception, page size extensions or page global bit + * are supported enable them in CR4 and clear any other set extensions. + * If machine check was enabled clear out any lingering status. + */ + if(m->cpuiddx & 0x2088){ + cr4 = 0; + if(m->cpuiddx & 0x08) + cr4 |= 0x10; /* page size extensions */ + if(p = getconf("*nomce")) + nomce = strtoul(p, 0, 0); + else + nomce = 0; + if((m->cpuiddx & 0x80) && !nomce){ + cr4 |= 0x40; /* machine check enable */ + if(family == 5){ + rdmsr(0x00, &mca); + rdmsr(0x01, &mct); + } + } + + /* + * Detect whether the chip supports the global bit + * in page directory and page table entries. When set + * in a particular entry, it means ``don't bother removing + * this from the TLB when CR3 changes.'' + * + * We flag all kernel pages with this bit. Doing so lessens the + * overhead of switching processes on bare hardware, + * even more so on VMware. See mmu.c:/^memglobal. + * + * For future reference, should we ever need to do a + * full TLB flush, it can be accomplished by clearing + * the PGE bit in CR4, writing to CR3, and then + * restoring the PGE bit. + */ + if(m->cpuiddx & 0x2000){ + cr4 |= 0x80; /* page global enable bit */ + m->havepge = 1; + } + + putcr4(cr4); + if(m->cpuiddx & 0x80) + rdmsr(0x01, &mct); + } + + cputype = t; + return t->family; +} + +static long +cputyperead(Chan*, void *a, long n, vlong offset) +{ + char str[32]; + ulong mhz; + + mhz = (m->cpuhz+999999)/1000000; + + snprint(str, sizeof(str), "%s %lud\n", cputype->name, mhz); + return readstr(offset, a, n, str); +} + +static long +archctlread(Chan*, void *a, long nn, vlong offset) +{ + char buf[256]; + int n; + + n = snprint(buf, sizeof buf, "cpu %s %lud%s\n", + cputype->name, (ulong)(m->cpuhz+999999)/1000000, + m->havepge ? " pge" : ""); + n += snprint(buf+n, sizeof buf-n, "pge %s\n", getcr4()&0x80 ? "on" : "off"); + n += snprint(buf+n, sizeof buf-n, "coherence "); + if(coherence == mb386) + n += snprint(buf+n, sizeof buf-n, "mb386\n"); + else if(coherence == mb586) + n += snprint(buf+n, sizeof buf-n, "mb586\n"); + else if(coherence == nop) + n += snprint(buf+n, sizeof buf-n, "nop\n"); + else + n += snprint(buf+n, sizeof buf-n, "0x%p\n", coherence); + n += snprint(buf+n, sizeof buf-n, "i8253set %s\n", doi8253set ? "on" : "off"); + buf[n] = 0; + return readstr(offset, a, nn, buf); +} + +enum +{ + CMpge, + CMcoherence, + CMi8253set, +}; + +static Cmdtab archctlmsg[] = +{ + CMpge, "pge", 2, + CMcoherence, "coherence", 2, + CMi8253set, "i8253set", 2, +}; + +static long +archctlwrite(Chan*, void *a, long n, vlong) +{ + Cmdbuf *cb; + Cmdtab *ct; + + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + ct = lookupcmd(cb, archctlmsg, nelem(archctlmsg)); + switch(ct->index){ + case CMpge: + if(!m->havepge) + error("processor does not support pge"); + if(strcmp(cb->f[1], "on") == 0) + putcr4(getcr4() | 0x80); + else if(strcmp(cb->f[1], "off") == 0) + putcr4(getcr4() & ~0x80); + else + cmderror(cb, "invalid pge ctl"); + break; + case CMcoherence: + if(strcmp(cb->f[1], "mb386") == 0) + coherence = mb386; + else if(strcmp(cb->f[1], "mb586") == 0){ + if(X86FAMILY(m->cpuidax) < 5) + error("invalid coherence ctl on this cpu family"); + coherence = mb586; + } + else if(strcmp(cb->f[1], "nop") == 0){ + /* only safe on vmware */ + if(conf.nmach > 1) + error("cannot disable coherence on a multiprocessor"); + coherence = nop; + }else + cmderror(cb, "invalid coherence ctl"); + break; + case CMi8253set: + if(strcmp(cb->f[1], "on") == 0) + doi8253set = 1; + else if(strcmp(cb->f[1], "off") == 0){ + doi8253set = 0; + (*arch->timerset)(0); + }else + cmderror(cb, "invalid i2853set ctl"); + break; + } + free(cb); + poperror(); + return n; +} + +void +archinit(void) +{ + PCArch **p; + + arch = 0; + for(p = knownarch; *p; p++){ + if((*p)->ident && (*p)->ident() == 0){ + arch = *p; + break; + } + } + if(arch == 0) + arch = &archgeneric; + else{ + if(arch->id == 0) + arch->id = archgeneric.id; + if(arch->reset == 0) + arch->reset = archgeneric.reset; + if(arch->serialpower == 0) + arch->serialpower = archgeneric.serialpower; + if(arch->modempower == 0) + arch->modempower = archgeneric.modempower; + if(arch->intrinit == 0) + arch->intrinit = archgeneric.intrinit; + if(arch->intrenable == 0) + arch->intrenable = archgeneric.intrenable; + } + + /* + * Decide whether to use copy-on-reference (386 and mp). + * We get another chance to set it in mpinit() for a + * multiprocessor. + */ + if(X86FAMILY(m->cpuidax) == 3) + conf.copymode = 1; + + if(X86FAMILY(m->cpuidax) >= 5) + coherence = mb586; + + addarchfile("cputype", 0444, cputyperead, nil); + addarchfile("archctl", 0664, archctlread, archctlwrite); +} + +/* + * call either the pcmcia or pccard device setup + */ +int +pcmspecial(char *idstr, ISAConf *isa) +{ + return (_pcmspecial != nil)? _pcmspecial(idstr, isa): -1; +} + +/* + * call either the pcmcia or pccard device teardown + */ +void +pcmspecialclose(int a) +{ + if (_pcmspecialclose != nil) + _pcmspecialclose(a); +} + +/* + * return value and speed of timer set in arch->clockenable + */ +uvlong +fastticks(uvlong *hz) +{ + return (*arch->fastclock)(hz); +} + +/* + * set next timer interrupt + */ +void +timerset(uvlong x) +{ + if(doi8253set) + (*arch->timerset)(x); +} diff --git a/os/pc/devds1620.c b/os/pc/devds1620.c new file mode 100644 index 00000000..b5e6fc83 --- /dev/null +++ b/os/pc/devds1620.c @@ -0,0 +1,368 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +enum { + // Ziatech 5512 Digital I/O ASIC register info + PortSelect = 0xE7, + Port = 0xE1, + DQ = 1<<0, + CLK = 1<<1, + RST = 1<<2, + TL = 1<<3, + TH = 1<<4, + + // ds1620 Masks + Mread = 0xA0, + Mwrite = 0, + + // ds1620 Registers + Rtemp = 0x0A, + Rcounter = 0x00, + Rslope = 0x09, + Rhi = 0x01, + Rlo = 0x02, + Rconfig = 0x0C, + Cdone = 1<<7, // conversion done + Cthf = 1<<6, // temp >= Rhi + Ctlf = 1<<5, // temp <= Rlo + Cnvb = 1<<4, // e^2 nvram busy (write may take up to 10ms) + Ccpu = 1<<1, // cpu use (0=clk starts conversion when rst lo) + C1shot = 1<<0, // perform one conversion then stop + + // ds1620 Commands + Startconv = 0xEE, + Stopconv = 0x22, + + ALOTEMP = 0, + AHITEMP = 1, +}; + +#define send(v) outb(Port, v); delay(1) +#define recv() (!(inb(Port) & 1)) + +enum { + Qdir = 0, + Qtemp, + Qalarm, +}; + +Dirtab ds1620tab[]={ + "temp", {Qtemp, 0}, 0, 0666, + "alarm", {Qalarm, 0}, 0, 0444, +}; + +typedef struct Temp Temp; +struct Temp +{ + Lock; + int lo; + int cur; + int hi; + + int alo; + int ahi; + int atime; + Queue *aq; +}; + +static Temp t; + +static void +sendreg(int r) +{ + int d, i; + + r = ~r; + for(i=0;i<8;i++) { + d = (r >> i) & 1; + send(CLK|d); + send(d); + send(CLK); + } +} + +static int +ds1620rdreg(int r, int nb) +{ + int i, s; + + s = splhi(); + + outb(PortSelect, 0); + send(RST|CLK); + sendreg(r|Mread); + r = 0; + for(i=0; i < nb; i++) { + r |= recv() << i; + delay(1); + send(0); + send(CLK); + } + send(RST); + + splx(s); + return r; +} + +static void +ds1620wrreg(int r, int v, int nb) +{ + int d, i, s; + + s = splhi(); + + outb(PortSelect, 0); + send(RST|CLK); + sendreg(r|Mwrite); + v = ~v; + for(i=0; i < nb; i++) { + d = (v >> i) & 1; + send(CLK|d); + send(0); + send(CLK); + } + send(RST); + + splx(s); +} + +static void +ds1620cmd(int r) +{ + int s; + + s = splhi(); + outb(PortSelect, 0); + send(RST|CLK); + sendreg(r); + send(RST); + splx(s); +} + +static char* +t2s(int t) +{ + static char s[16]; + + sprint(s, "%4d.", t>>1); + if(t&1) + strcat(s, "5"); + else + strcat(s, "0"); + return s; +} + +static int +s2t(char *s) +{ + int v; + char *p; + p = strchr(s, '.'); + if(p != nil) + *p++ = '\0'; + v = strtoul(s, nil, 0); + v <<= 1; + if(p != nil && *p != '\0' && *p >= '5') + v |= 1; + return v; +} + +static void +alarm(int code, Temp *tt) +{ + char buf[256], *end; + int s; + + s = seconds(); + + if(s - tt->atime < 60) + return; + tt->atime = s; + + end = buf; + end += sprint(buf, "(alarm) %8.8uX %uld temp ", code, seconds()); + switch(code) { + case ALOTEMP: + end += sprint(end, "%s below threshold ", t2s(tt->lo)); + end += sprint(end, "%s.\n", t2s(tt->alo)); + break; + case AHITEMP: + end += sprint(end, "%s above threshold ", t2s(tt->hi)); + end += sprint(end, "%s.\n", t2s(tt->ahi)); + break; + } + + qproduce(tt->aq, buf, end-buf); +} + +void +tmon(void *a) +{ + int r; + Temp *t; + + t = a; + r = ds1620rdreg(Rtemp, 9); + lock(t); + t->lo = t->cur = t->hi = r; + unlock(t); + for(;;) { + tsleep(&up->sleep, return0, nil, 1000); + r = ds1620rdreg(Rtemp, 9); + lock(t); + t->cur = r; + if(r < t->lo) + t->lo = r; + if(r > t->hi) + t->hi = r; + if(t->lo < t->alo) + alarm(ALOTEMP, t); + if(t->hi > t->ahi) + alarm(AHITEMP, t); + unlock(t); + } + pexit("", 0); +} + +static void +ds1620init(void) +{ + int r; + + t.aq = qopen(8*1024, Qmsg, nil, nil); + if(t.aq == nil) + error(Enomem); + + ds1620wrreg(Rconfig, Ccpu, 8); // continuous sample mode + ds1620cmd(Startconv); + r = ds1620rdreg(Rtemp, 9); + t.alo = ds1620rdreg(Rlo, 9); + t.ahi = ds1620rdreg(Rhi, 9); + + print("#L: temp %s (c) ", t2s(r)); + print("low threshold %s (c) ", t2s(t.alo)); + print("high threshold %s (c)\n", t2s(t.ahi)); + + kproc("tempmon", tmon, &t, 0); +} + +static Chan* +ds1620attach(char *spec) +{ + return devattach('L', spec); +} + +static int +ds1620walk(Chan *c, char* name) +{ + return devwalk(c, name, ds1620tab, nelem(ds1620tab), devgen); +} + +static void +ds1620stat(Chan *c, char* db) +{ + ds1620tab[1].length = qlen(t.aq); + devstat(c, db, ds1620tab, nelem(ds1620tab), devgen); +} + +static Chan* +ds1620open(Chan *c, int omode) +{ + return devopen(c, omode, ds1620tab, nelem(ds1620tab), devgen); +} + +static void +ds1620close(Chan*) +{ +} + +static long +ds1620read(Chan *c, void *a, long n, vlong offset) +{ + Temp tt; + char buf[64]; + char *s; + if(c->qid.path & CHDIR) + return devdirread(c, a, n, ds1620tab, nelem(ds1620tab), devgen); + buf[0] = 0; + switch(c->qid.path) { + case Qtemp: + lock(&t); + tt = t; + unlock(&t); + s = buf; + s+= sprint(s, "%s ", t2s(tt.lo)); + s+= sprint(s, "%s ", t2s(tt.cur)); + s+= sprint(s, "%s ", t2s(tt.hi)); + s+= sprint(s, "%s ", t2s(tt.alo)); + sprint(s, "%s", t2s(tt.ahi)); + return readstr(offset, a, n, buf); + case Qalarm: + return qread(t.aq, a, n); + default: + error(Egreg); + return 0; + } +} + +static long +ds1620write(Chan *c, void *a, long n, vlong) +{ + char buf[64]; + char *f[2]; + int lo, hi; + int nf; + + if(c->qid.path & CHDIR) + error(Eperm); + + if(c->qid.path == Qtemp) { + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = '\0'; + nf = getfields(buf, f, 2, 1, " \t"); + if(nf != 2) + error(Ebadarg); + lo = s2t(f[0]); + hi = s2t(f[1]); + lock(&t); + t.alo = lo; + t.ahi = hi; + t.atime = 0; + ds1620wrreg(Rlo, lo, 9); + delay(1); + ds1620wrreg(Rhi, hi, 9); + unlock(&t); + return n; + } else + error(Eio); + return 0; + +} + +Dev ds1620devtab = { + 'L', + "ds1620", + devreset, + ds1620init, + ds1620attach, + devdetach, + devclone, + ds1620walk, + ds1620stat, + ds1620open, + devcreate, + ds1620close, + ds1620read, + devbread, + ds1620write, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/pc/devether.c b/os/pc/devether.c new file mode 100644 index 00000000..4bdc693c --- /dev/null +++ b/os/pc/devether.c @@ -0,0 +1,539 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +static Ether *etherxx[MaxEther]; + +Chan* +etherattach(char* spec) +{ + ulong ctlrno; + char *p; + Chan *chan; + + ctlrno = 0; + if(spec && *spec){ + ctlrno = strtoul(spec, &p, 0); + if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther)) + error(Ebadarg); + } + if(etherxx[ctlrno] == 0) + error(Enodev); + + chan = devattach('l', spec); + chan->dev = ctlrno; + if(etherxx[ctlrno]->attach) + etherxx[ctlrno]->attach(etherxx[ctlrno]); + return chan; +} + +static Walkqid* +etherwalk(Chan* chan, Chan* nchan, char** name, int nname) +{ + return netifwalk(etherxx[chan->dev], chan, nchan, name, nname); +} + +static int +etherstat(Chan* chan, uchar* dp, int n) +{ + return netifstat(etherxx[chan->dev], chan, dp, n); +} + +static Chan* +etheropen(Chan* chan, int omode) +{ + return netifopen(etherxx[chan->dev], chan, omode); +} + +static void +ethercreate(Chan*, char*, int, ulong) +{ +} + +static void +etherclose(Chan* chan) +{ + netifclose(etherxx[chan->dev], chan); +} + +static long +etherread(Chan* chan, void* buf, long n, vlong off) +{ + Ether *ether; + ulong offset = off; + + ether = etherxx[chan->dev]; + if((chan->qid.type & QTDIR) == 0 && ether->ifstat){ + /* + * With some controllers it is necessary to reach + * into the chip to extract statistics. + */ + if(NETTYPE(chan->qid.path) == Nifstatqid) + return ether->ifstat(ether, buf, n, offset); + else if(NETTYPE(chan->qid.path) == Nstatqid) + ether->ifstat(ether, buf, 0, offset); + } + + return netifread(ether, chan, buf, n, offset); +} + +static Block* +etherbread(Chan* chan, long n, ulong offset) +{ + return netifbread(etherxx[chan->dev], chan, n, offset); +} + +static int +etherwstat(Chan* chan, uchar* dp, int n) +{ + return netifwstat(etherxx[chan->dev], chan, dp, n); +} + +static void +etherrtrace(Netfile* f, Etherpkt* pkt, int len) +{ + int i, n; + Block *bp; + + if(qwindow(f->in) <= 0) + return; + if(len > 58) + n = 58; + else + n = len; + bp = iallocb(64); + if(bp == nil) + return; + memmove(bp->wp, pkt->d, n); + i = TK2MS(MACHP(0)->ticks); + bp->wp[58] = len>>8; + bp->wp[59] = len; + bp->wp[60] = i>>24; + bp->wp[61] = i>>16; + bp->wp[62] = i>>8; + bp->wp[63] = i; + bp->wp += 64; + qpass(f->in, bp); +} + +Block* +etheriq(Ether* ether, Block* bp, int fromwire) +{ + Etherpkt *pkt; + ushort type; + int len, multi, tome, fromme; + Netfile **ep, *f, **fp, *fx; + Block *xbp; + + ether->inpackets++; + + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + type = (pkt->type[0]<<8)|pkt->type[1]; + fx = 0; + ep = ðer->f[Ntypes]; + + multi = pkt->d[0] & 1; + /* check for valid multcast addresses */ + if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){ + if(!activemulti(ether, pkt->d, sizeof(pkt->d))){ + if(fromwire){ + freeb(bp); + bp = 0; + } + return bp; + } + } + + /* is it for me? */ + tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0; + + /* + * Multiplex the packet to all the connections which want it. + * If the packet is not to be used subsequently (fromwire != 0), + * attempt to simply pass it into one of the connections, thereby + * saving a copy of the data (usual case hopefully). + */ + for(fp = ether->f; fp < ep; fp++){ + if(f = *fp) + if(f->type == type || f->type < 0) + if(tome || multi || f->prom){ + /* Don't want to hear bridged packets */ + if(f->bridge && !fromwire && !fromme) + continue; + if(!f->headersonly){ + if(fromwire && fx == 0) + fx = f; + else if(xbp = iallocb(len)){ + memmove(xbp->wp, pkt, len); + xbp->wp += len; + if(qpass(f->in, xbp) < 0) + ether->soverflows++; + } + else + ether->soverflows++; + } + else + etherrtrace(f, pkt, len); + } + } + + if(fx){ + if(qpass(fx->in, bp) < 0) + ether->soverflows++; + return 0; + } + if(fromwire){ + freeb(bp); + return 0; + } + + return bp; +} + +static int +etheroq(Ether* ether, Block* bp) +{ + int len, loopback, s; + Etherpkt *pkt; + + ether->outpackets++; + + /* + * Check if the packet has to be placed back onto the input queue, + * i.e. if it's a loopback or broadcast packet or the interface is + * in promiscuous mode. + * If it's a loopback packet indicate to etheriq that the data isn't + * needed and return, etheriq will pass-on or free the block. + * To enable bridging to work, only packets that were originated + * by this interface are fed back. + */ + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){ + s = splhi(); + etheriq(ether, bp, 0); + splx(s); + } + + if(!loopback){ + qbwrite(ether->oq, bp); + if(ether->transmit != nil) + ether->transmit(ether); + } else + freeb(bp); + + return len; +} + +static long +etherwrite(Chan* chan, void* buf, long n, vlong) +{ + Ether *ether; + Block *bp; + int nn, onoff; + Cmdbuf *cb; + + ether = etherxx[chan->dev]; + if(NETTYPE(chan->qid.path) != Ndataqid) { + nn = netifwrite(ether, chan, buf, n); + if(nn >= 0) + return nn; + cb = parsecmd(buf, n); + if(strcmp(cb->f[0], "nonblocking") == 0){ + if(cb->nf <= 1) + onoff = 1; + else + onoff = atoi(cb->f[1]); + qnoblock(ether->oq, onoff); + free(cb); + return n; + } + free(cb); + if(ether->ctl!=nil) + return ether->ctl(ether,buf,n); + + error(Ebadctl); + } + + if(n > ether->maxmtu) + error(Etoobig); + if(n < ether->minmtu) + error(Etoosmall); + + bp = allocb(n); + if(waserror()){ + freeb(bp); + nexterror(); + } + memmove(bp->rp, buf, n); + memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen); + poperror(); + bp->wp += n; + + return etheroq(ether, bp); +} + +static long +etherbwrite(Chan* chan, Block* bp, ulong) +{ + Ether *ether; + long n; + + n = BLEN(bp); + if(NETTYPE(chan->qid.path) != Ndataqid){ + if(waserror()) { + freeb(bp); + nexterror(); + } + n = etherwrite(chan, bp->rp, n, 0); + poperror(); + freeb(bp); + return n; + } + ether = etherxx[chan->dev]; + + if(n > ether->maxmtu){ + freeb(bp); + error(Etoobig); + } + if(n < ether->minmtu){ + freeb(bp); + error(Etoosmall); + } + + return etheroq(ether, bp); +} + +static struct { + char* type; + int (*reset)(Ether*); +} cards[MaxEther+1]; + +void +addethercard(char* t, int (*r)(Ether*)) +{ + static int ncard; + + if(ncard == MaxEther) + panic("too many ether cards"); + cards[ncard].type = t; + cards[ncard].reset = r; + ncard++; +} + +int +parseether(uchar *to, char *from) +{ + char nip[4]; + char *p; + int i; + + p = from; + for(i = 0; i < Eaddrlen; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +static Ether* +etherprobe(int cardno, int ctlrno) +{ + int i; + Ether *ether; + char buf[128], name[32]; + + ether = malloc(sizeof(Ether)); + memset(ether, 0, sizeof(Ether)); + ether->ctlrno = ctlrno; + ether->tbdf = BUSUNKNOWN; + ether->mbps = 10; + ether->minmtu = ETHERMINTU; + ether->maxmtu = ETHERMAXTU; + + if(cardno < 0){ + if(isaconfig("ether", ctlrno, ether) == 0){ + free(ether); + return nil; + } + for(cardno = 0; cards[cardno].type; cardno++){ + if(cistrcmp(cards[cardno].type, ether->type)) + continue; + for(i = 0; i < ether->nopt; i++){ + if(strncmp(ether->opt[i], "ea=", 3)) + continue; + if(parseether(ether->ea, ðer->opt[i][3])) + memset(ether->ea, 0, Eaddrlen); + } + break; + } + } + + if(cardno >= MaxEther || cards[cardno].type == nil){ + free(ether); + return nil; + } + if(cards[cardno].reset(ether) < 0){ + free(ether); + return nil; + } + + /* + * IRQ2 doesn't really exist, it's used to gang the interrupt + * controllers together. A device set to IRQ2 will appear on + * the second interrupt controller as IRQ9. + */ + if(ether->irq == 2) + ether->irq = 9; + snprint(name, sizeof(name), "ether%d", ctlrno); + + /* + * If ether->irq is <0, it is a hack to indicate no interrupt + * used by ethersink. + */ + if(ether->irq >= 0) + intrenable(ether->irq, ether->interrupt, ether, ether->tbdf, name); + + i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %d", + ctlrno, cards[cardno].type, ether->mbps, ether->port, ether->irq); + if(ether->mem) + i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem)); + if(ether->size) + i += sprint(buf+i, " size 0x%luX", ether->size); + i += sprint(buf+i, ": %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux", + ether->ea[0], ether->ea[1], ether->ea[2], + ether->ea[3], ether->ea[4], ether->ea[5]); + sprint(buf+i, "\n"); + print(buf); + + if (ether->mbps >= 1000) { + netifinit(ether, name, Ntypes, 512*1024); + if(ether->oq == 0) + ether->oq = qopen(512*1024, Qmsg, 0, 0); + } else if(ether->mbps >= 100){ + netifinit(ether, name, Ntypes, 256*1024); + if(ether->oq == 0) + ether->oq = qopen(256*1024, Qmsg, 0, 0); + } + else{ + netifinit(ether, name, Ntypes, 128*1024); + if(ether->oq == 0) + ether->oq = qopen(128*1024, Qmsg, 0, 0); + } + if(ether->oq == 0) + panic("etherreset %s", name); + ether->alen = Eaddrlen; + memmove(ether->addr, ether->ea, Eaddrlen); + memset(ether->bcast, 0xFF, Eaddrlen); + + return ether; +} + +static void +etherreset(void) +{ + Ether *ether; + int cardno, ctlrno; + + for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){ + if((ether = etherprobe(-1, ctlrno)) == nil) + continue; + etherxx[ctlrno] = ether; + } + + if(getconf("*noetherprobe")) + return; + + cardno = ctlrno = 0; + while(cards[cardno].type != nil && ctlrno < MaxEther){ + if(etherxx[ctlrno] != nil){ + ctlrno++; + continue; + } + if((ether = etherprobe(cardno, ctlrno)) == nil){ + cardno++; + continue; + } + etherxx[ctlrno] = ether; + ctlrno++; + } +} + +static void +ethershutdown(void) +{ + Ether *ether; + int i; + + for(i = 0; i < MaxEther; i++){ + ether = etherxx[i]; + if(ether == nil) + continue; + if(ether->shutdown == nil) { + print("#l%d: no shutdown fuction\n", i); + continue; + } + (*ether->shutdown)(ether); + } +} + + +#define POLY 0xedb88320 + +/* really slow 32 bit crc for ethers */ +ulong +ethercrc(uchar *p, int len) +{ + int i, j; + ulong crc, b; + + crc = 0xffffffff; + for(i = 0; i < len; i++){ + b = *p++; + for(j = 0; j < 8; j++){ + crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0); + b >>= 1; + } + } + return crc; +} + +Dev etherdevtab = { + 'l', + "ether", + + etherreset, + devinit, + ethershutdown, + etherattach, + etherwalk, + etherstat, + etheropen, + ethercreate, + etherclose, + etherread, + etherbread, + etherwrite, + etherbwrite, + devremove, + etherwstat, +}; diff --git a/os/pc/devfloppy.c b/os/pc/devfloppy.c new file mode 100644 index 00000000..2f29147c --- /dev/null +++ b/os/pc/devfloppy.c @@ -0,0 +1,1082 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "floppy.h" + +/* Intel 82077A (8272A compatible) floppy controller */ + +/* This module expects the following functions to be defined + * elsewhere: + * + * inb() + * outb() + * floppyexec() + * floppyeject() + * floppysetup0() + * floppysetup1() + * dmainit() + * dmasetup() + * dmaend() + * + * On DMA systems, floppyexec() should be an empty function; + * on non-DMA systems, dmaend() should be an empty function; + * dmasetup() may enforce maximum transfer sizes. + */ + +enum { + /* file types */ + Qdir= 0, + Qdata= (1<<2), + Qctl= (2<<2), + Qmask= (3<<2), + + DMAchan= 2, /* floppy dma channel */ +}; + +#define DPRINT if(floppydebug)print +int floppydebug = 0; + +/* + * types of drive (from PC equipment byte) + */ +enum +{ + Tnone= 0, + T360kb= 1, + T1200kb= 2, + T720kb= 3, + T1440kb= 4, +}; + +FType floppytype[] = +{ + { "3½HD", T1440kb, 512, 18, 2, 1, 80, 0x1B, 0x54, 0, }, + { "3½DD", T1440kb, 512, 9, 2, 1, 80, 0x1B, 0x54, 2, }, + { "3½DD", T720kb, 512, 9, 2, 1, 80, 0x1B, 0x54, 2, }, + { "5¼HD", T1200kb, 512, 15, 2, 1, 80, 0x2A, 0x50, 0, }, + { "5¼DD", T1200kb, 512, 9, 2, 2, 40, 0x2A, 0x50, 1, }, + { "ATT3B1", T1200kb, 512, 8, 2, 2, 48, 0x2A, 0x50, 1, }, + { "5¼DD", T360kb, 512, 9, 2, 1, 40, 0x2A, 0x50, 2, }, +}; + +/* + * bytes per sector encoding for the controller. + * - index for b2c is is (bytes per sector/128). + * - index for c2b is code from b2c + */ +static int b2c[] = +{ +[1] 0, +[2] 1, +[4] 2, +[8] 3, +}; +static int c2b[] = +{ + 128, + 256, + 512, + 1024, +}; + +FController fl; + +#define MOTORBIT(i) (1<<((i)+4)) + +/* + * predeclared + */ +static int cmddone(void*); +static void floppyformat(FDrive*, Cmdbuf*); +static void floppykproc(void*); +static void floppypos(FDrive*,long); +static int floppyrecal(FDrive*); +static int floppyresult(void); +static void floppyrevive(void); +static long floppyseek(FDrive*, long); +static int floppysense(void); +static void floppywait(int); +static long floppyxfer(FDrive*, int, void*, long, long); + +Dirtab floppydir[]={ + ".", {Qdir, 0, QTDIR}, 0, 0550, + "fd0disk", {Qdata + 0}, 0, 0660, + "fd0ctl", {Qctl + 0}, 0, 0660, + "fd1disk", {Qdata + 1}, 0, 0660, + "fd1ctl", {Qctl + 1}, 0, 0660, + "fd2disk", {Qdata + 2}, 0, 0660, + "fd2ctl", {Qctl + 2}, 0, 0660, + "fd3disk", {Qdata + 3}, 0, 0660, + "fd3ctl", {Qctl + 3}, 0, 0660, +}; +#define NFDIR 2 /* directory entries/drive */ + +enum +{ + CMdebug, + CMnodebug, + CMeject, + CMformat, + CMreset, +}; + +static Cmdtab floppyctlmsg[] = +{ + CMdebug, "debug", 1, + CMnodebug, "nodebug", 1, + CMeject, "eject", 1, + CMformat, "format", 0, + CMreset, "reset", 1, +}; + +static void +fldump(void) +{ + DPRINT("sra %ux srb %ux dor %ux msr %ux dir %ux\n", inb(Psra), inb(Psrb), + inb(Pdor), inb(Pmsr), inb(Pdir)); +} + +/* + * set floppy drive to its default type + */ +static void +floppysetdef(FDrive *dp) +{ + FType *t; + + for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++) + if(dp->dt == t->dt){ + dp->t = t; + floppydir[1+NFDIR*dp->dev].length = dp->t->cap; + break; + } +} + +static void +floppyreset(void) +{ + FDrive *dp; + FType *t; + ulong maxtsize; + + floppysetup0(&fl); + if(fl.ndrive == 0) + return; + + /* + * init dependent parameters + */ + maxtsize = 0; + for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){ + t->cap = t->bytes * t->heads * t->sectors * t->tracks; + t->bcode = b2c[t->bytes/128]; + t->tsize = t->bytes * t->sectors; + if(maxtsize < t->tsize) + maxtsize = t->tsize; + } + + dmainit(DMAchan, maxtsize); + + /* + * allocate the drive storage + */ + fl.d = xalloc(fl.ndrive*sizeof(FDrive)); + fl.selected = fl.d; + + /* + * stop the motors + */ + fl.motor = 0; + delay(10); + outb(Pdor, fl.motor | Fintena | Fena); + delay(10); + + /* + * init drives + */ + for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){ + dp->dev = dp - fl.d; + dp->dt = T1440kb; + floppysetdef(dp); + dp->cyl = -1; /* because we don't know */ + dp->cache = (uchar*)xspanalloc(maxtsize, BY2PG, 64*1024); + dp->ccyl = -1; + dp->vers = 0; + } + + /* + * first operation will recalibrate + */ + fl.confused = 1; + + floppysetup1(&fl); +} + +static Chan* +floppyattach(char *spec) +{ + static int kstarted; + + if(fl.ndrive == 0) + error(Enodev); + + if(kstarted == 0){ + /* + * watchdog to turn off the motors + */ + kstarted = 1; + kproc("floppy", floppykproc, 0, 0); + } + return devattach('f', spec); +} + +static Walkqid* +floppywalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, floppydir, 1+fl.ndrive*NFDIR, devgen); +} + +static int +floppystat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, floppydir, 1+fl.ndrive*NFDIR, devgen); +} + +static Chan* +floppyopen(Chan *c, int omode) +{ + return devopen(c, omode, floppydir, 1+fl.ndrive*NFDIR, devgen); +} + +static void +floppyclose(Chan *) +{ +} + +static void +islegal(ulong offset, long n, FDrive *dp) +{ + if(offset % dp->t->bytes) + error(Ebadarg); + if(n % dp->t->bytes) + error(Ebadarg); +} + +/* + * check if the floppy has been replaced under foot. cause + * an error if it has. + * + * a seek and a read clears the condition. this was determined + * experimentally, there has to be a better way. + * + * if the read fails, cycle through the possible floppy + * density till one works or we've cycled through all + * possibilities for this drive. + */ +static void +changed(Chan *c, FDrive *dp) +{ + ulong old; + FType *start; + + /* + * if floppy has changed or first time through + */ + if((inb(Pdir)&Fchange) || dp->vers == 0){ + DPRINT("changed\n"); + fldump(); + dp->vers++; + start = dp->t; + dp->maxtries = 3; /* limit it when we're probing */ + + /* floppyon will fail if there's a controller but no drive */ + dp->confused = 1; /* make floppyon recal */ + if(floppyon(dp) < 0) + error(Eio); + + /* seek to the first track */ + floppyseek(dp, dp->t->heads*dp->t->tsize); + while(waserror()){ + /* + * if first attempt doesn't reset changed bit, there's + * no floppy there + */ + if(inb(Pdir)&Fchange) + nexterror(); + + while(++dp->t){ + if(dp->t == &floppytype[nelem(floppytype)]) + dp->t = floppytype; + if(dp->dt == dp->t->dt) + break; + } + floppydir[1+NFDIR*dp->dev].length = dp->t->cap; + + /* floppyon will fail if there's a controller but no drive */ + if(floppyon(dp) < 0) + error(Eio); + + DPRINT("changed: trying %s\n", dp->t->name); + fldump(); + if(dp->t == start) + nexterror(); + } + + /* if the read succeeds, we've got the density right */ + floppyxfer(dp, Fread, dp->cache, 0, dp->t->tsize); + poperror(); + dp->maxtries = 20; + } + + old = c->qid.vers; + c->qid.vers = dp->vers; + if(old && old != dp->vers) + error(Eio); +} + +static int +readtrack(FDrive *dp, int cyl, int head) +{ + int i, nn, sofar; + ulong pos; + + nn = dp->t->tsize; + if(dp->ccyl==cyl && dp->chead==head) + return nn; + pos = (cyl*dp->t->heads+head) * nn; + for(sofar = 0; sofar < nn; sofar += i){ + dp->ccyl = -1; + i = floppyxfer(dp, Fread, dp->cache + sofar, pos + sofar, nn - sofar); + if(i <= 0) + return -1; + } + dp->ccyl = cyl; + dp->chead = head; + return nn; +} + +static long +floppyread(Chan *c, void *a, long n, vlong off) +{ + FDrive *dp; + long rv; + int sec, head, cyl; + long len; + uchar *aa; + ulong offset = off; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, floppydir, 1+fl.ndrive*NFDIR, devgen); + + rv = 0; + dp = &fl.d[c->qid.path & ~Qmask]; + switch ((int)(c->qid.path & Qmask)) { + case Qdata: + islegal(offset, n, dp); + aa = a; + + qlock(&fl); + if(waserror()){ + qunlock(&fl); + nexterror(); + } + floppyon(dp); + changed(c, dp); + for(rv = 0; rv < n; rv += len){ + /* + * all xfers come out of the track cache + */ + dp->len = n - rv; + floppypos(dp, offset+rv); + cyl = dp->tcyl; + head = dp->thead; + len = dp->len; + sec = dp->tsec; + if(readtrack(dp, cyl, head) < 0) + break; + memmove(aa+rv, dp->cache + (sec-1)*dp->t->bytes, len); + } + qunlock(&fl); + poperror(); + + break; + case Qctl: + return readstr(offset, a, n, dp->t->name); + default: + panic("floppyread: bad qid"); + } + + return rv; +} + +static long +floppywrite(Chan *c, void *a, long n, vlong off) +{ + FDrive *dp; + long rv, i; + char *aa = a; + Cmdbuf *cb; + Cmdtab *ct; + ulong offset = off; + + rv = 0; + dp = &fl.d[c->qid.path & ~Qmask]; + switch ((int)(c->qid.path & Qmask)) { + case Qdata: + islegal(offset, n, dp); + qlock(&fl); + if(waserror()){ + qunlock(&fl); + nexterror(); + } + floppyon(dp); + changed(c, dp); + for(rv = 0; rv < n; rv += i){ + floppypos(dp, offset+rv); + if(dp->tcyl == dp->ccyl) + dp->ccyl = -1; + i = floppyxfer(dp, Fwrite, aa+rv, offset+rv, n-rv); + if(i < 0) + break; + if(i == 0) + error(Eio); + } + qunlock(&fl); + poperror(); + break; + case Qctl: + rv = n; + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + qlock(&fl); + if(waserror()){ + qunlock(&fl); + nexterror(); + } + ct = lookupcmd(cb, floppyctlmsg, nelem(floppyctlmsg)); + switch(ct->index){ + case CMeject: + floppyeject(dp); + break; + case CMformat: + floppyformat(dp, cb); + break; + case CMreset: + fl.confused = 1; + floppyon(dp); + break; + case CMdebug: + floppydebug = 1; + break; + case CMnodebug: + floppydebug = 0; + break; + } + poperror(); + qunlock(&fl); + poperror(); + free(cb); + break; + default: + panic("floppywrite: bad qid"); + } + + return rv; +} + +static void +floppykproc(void *) +{ + FDrive *dp; + + while(waserror()) + ; + for(;;){ + for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){ + if((fl.motor&MOTORBIT(dp->dev)) + && TK2SEC(m->ticks - dp->lasttouched) > 5 + && canqlock(&fl)){ + if(TK2SEC(m->ticks - dp->lasttouched) > 5) + floppyoff(dp); + qunlock(&fl); + } + } + tsleep(&up->sleep, return0, 0, 1000); + } +} + +/* + * start a floppy drive's motor. + */ +static int +floppyon(FDrive *dp) +{ + int alreadyon; + int tries; + + if(fl.confused) + floppyrevive(); + + /* start motor and select drive */ + alreadyon = fl.motor & MOTORBIT(dp->dev); + fl.motor |= MOTORBIT(dp->dev); + outb(Pdor, fl.motor | Fintena | Fena | dp->dev); + if(!alreadyon){ + /* wait for drive to spin up */ + tsleep(&up->sleep, return0, 0, 750); + + /* clear any pending interrupts */ + floppysense(); + } + + /* set transfer rate */ + if(fl.rate != dp->t->rate){ + fl.rate = dp->t->rate; + outb(Pdsr, fl.rate); + } + + /* get drive to a known cylinder */ + if(dp->confused) + for(tries = 0; tries < 4; tries++) + if(floppyrecal(dp) >= 0) + break; + dp->lasttouched = m->ticks; + fl.selected = dp; + + /* return -1 if this didn't work */ + if(dp->confused) + return -1; + return 0; +} + +/* + * stop the floppy if it hasn't been used in 5 seconds + */ +static void +floppyoff(FDrive *dp) +{ + fl.motor &= ~MOTORBIT(dp->dev); + outb(Pdor, fl.motor | Fintena | Fena | dp->dev); +} + +/* + * send a command to the floppy + */ +static int +floppycmd(void) +{ + int i; + int tries; + + fl.nstat = 0; + for(i = 0; i < fl.ncmd; i++){ + for(tries = 0; ; tries++){ + if((inb(Pmsr)&(Ffrom|Fready)) == Fready) + break; + if(tries > 1000){ + DPRINT("cmd %ux can't be sent (%d)\n", fl.cmd[0], i); + fldump(); + + /* empty fifo, might have been a bad command */ + floppyresult(); + return -1; + } + microdelay(8); /* for machine independence */ + } + outb(Pfdata, fl.cmd[i]); + } + return 0; +} + +/* + * get a command result from the floppy + * + * when the controller goes ready waiting for a command + * (instead of sending results), we're done + * + */ +static int +floppyresult(void) +{ + int i, s; + int tries; + + /* get the result of the operation */ + for(i = 0; i < sizeof(fl.stat); i++){ + /* wait for status byte */ + for(tries = 0; ; tries++){ + s = inb(Pmsr)&(Ffrom|Fready); + if(s == Fready){ + fl.nstat = i; + return fl.nstat; + } + if(s == (Ffrom|Fready)) + break; + if(tries > 1000){ + DPRINT("floppyresult: %d stats\n", i); + fldump(); + fl.confused = 1; + return -1; + } + microdelay(8); /* for machine independence */ + } + fl.stat[i] = inb(Pfdata); + } + fl.nstat = sizeof(fl.stat); + return fl.nstat; +} + +/* + * calculate physical address of a logical byte offset into the disk + * + * truncate dp->length if it crosses a track boundary + */ +static void +floppypos(FDrive *dp, long off) +{ + int lsec; + int ltrack; + int end; + + lsec = off/dp->t->bytes; + ltrack = lsec/dp->t->sectors; + dp->tcyl = ltrack/dp->t->heads; + dp->tsec = (lsec % dp->t->sectors) + 1; + dp->thead = (lsec/dp->t->sectors) % dp->t->heads; + + /* + * can't read across track boundaries. + * if so, decrement the bytes to be read. + */ + end = (ltrack+1)*dp->t->sectors*dp->t->bytes; + if(off+dp->len > end) + dp->len = end - off; +} + +/* + * get the interrupt cause from the floppy. + */ +static int +floppysense(void) +{ + fl.ncmd = 0; + fl.cmd[fl.ncmd++] = Fsense; + if(floppycmd() < 0) + return -1; + if(floppyresult() < 2){ + DPRINT("can't read sense response\n"); + fldump(); + fl.confused = 1; + return -1; + } + return 0; +} + +static int +cmddone(void *) +{ + return fl.ncmd == 0; +} + +/* + * Wait for a floppy interrupt. If none occurs in 5 seconds, we + * may have missed one. This only happens on some portables which + * do power management behind our backs. Call the interrupt + * routine to try to clear any conditions. + */ +static void +floppywait(int slow) +{ + tsleep(&fl.r, cmddone, 0, slow ? 5000 : 1000); + if(!cmddone(0)){ + floppyintr(0); + fl.confused = 1; + } +} + +/* + * we've lost the floppy position, go to cylinder 0. + */ +static int +floppyrecal(FDrive *dp) +{ + dp->ccyl = -1; + dp->cyl = -1; + + fl.ncmd = 0; + fl.cmd[fl.ncmd++] = Frecal; + fl.cmd[fl.ncmd++] = dp->dev; + if(floppycmd() < 0) + return -1; + floppywait(1); + if(fl.nstat < 2){ + DPRINT("recalibrate: confused %ux\n", inb(Pmsr)); + fl.confused = 1; + return -1; + } + if((fl.stat[0] & (Codemask|Seekend)) != Seekend){ + DPRINT("recalibrate: failed\n"); + dp->confused = 1; + return -1; + } + dp->cyl = fl.stat[1]; + if(dp->cyl != 0){ + DPRINT("recalibrate: wrong cylinder %d\n", dp->cyl); + dp->cyl = -1; + dp->confused = 1; + return -1; + } + + dp->confused = 0; + return 0; +} + +/* + * if the controller or a specific drive is in a confused state, + * reset it and get back to a known state + */ +static void +floppyrevive(void) +{ + FDrive *dp; + + /* + * reset the controller if it's confused + */ + if(fl.confused){ + DPRINT("floppyrevive in\n"); + fldump(); + + /* reset controller and turn all motors off */ + splhi(); + fl.ncmd = 1; + fl.cmd[0] = 0; + outb(Pdor, 0); + delay(10); + outb(Pdor, Fintena|Fena); + delay(10); + spllo(); + fl.motor = 0; + fl.confused = 0; + floppywait(0); + + /* mark all drives in an unknown state */ + for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++) + dp->confused = 1; + + /* set rate to a known value */ + outb(Pdsr, 0); + fl.rate = 0; + + DPRINT("floppyrevive out\n"); + fldump(); + } +} + +/* + * seek to the target cylinder + * + * interrupt, no results + */ +static long +floppyseek(FDrive *dp, long off) +{ + floppypos(dp, off); + if(dp->cyl == dp->tcyl) + return dp->tcyl; + dp->cyl = -1; + + fl.ncmd = 0; + fl.cmd[fl.ncmd++] = Fseek; + fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev; + fl.cmd[fl.ncmd++] = dp->tcyl * dp->t->steps; + if(floppycmd() < 0) + return -1; + floppywait(1); + if(fl.nstat < 2){ + DPRINT("seek: confused\n"); + fl.confused = 1; + return -1; + } + if((fl.stat[0] & (Codemask|Seekend)) != Seekend){ + DPRINT("seek: failed\n"); + dp->confused = 1; + return -1; + } + + dp->cyl = dp->tcyl; + return dp->tcyl; +} + +/* + * read or write to floppy. try up to three times. + */ +static long +floppyxfer(FDrive *dp, int cmd, void *a, long off, long n) +{ + long offset; + int tries; + + if(off >= dp->t->cap) + return 0; + if(off + n > dp->t->cap) + n = dp->t->cap - off; + + /* retry on error (until it gets ridiculous) */ + tries = 0; + while(waserror()){ + if(tries++ >= dp->maxtries) + nexterror(); + DPRINT("floppyxfer: retrying\n"); + } + + dp->len = n; + if(floppyseek(dp, off) < 0){ + DPRINT("xfer: seek failed\n"); + dp->confused = 1; + error(Eio); + } + + /* + * set up the dma (dp->len may be trimmed) + */ + if(waserror()){ + dmaend(DMAchan); + nexterror(); + } + dp->len = dmasetup(DMAchan, a, dp->len, cmd==Fread); + if(dp->len < 0) + error(Eio); + + /* + * start operation + */ + fl.ncmd = 0; + fl.cmd[fl.ncmd++] = cmd | (dp->t->heads > 1 ? Fmulti : 0); + fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev; + fl.cmd[fl.ncmd++] = dp->tcyl; + fl.cmd[fl.ncmd++] = dp->thead; + fl.cmd[fl.ncmd++] = dp->tsec; + fl.cmd[fl.ncmd++] = dp->t->bcode; + fl.cmd[fl.ncmd++] = dp->t->sectors; + fl.cmd[fl.ncmd++] = dp->t->gpl; + fl.cmd[fl.ncmd++] = 0xFF; + if(floppycmd() < 0) + error(Eio); + + /* Poll ready bits and transfer data */ + floppyexec((char*)a, dp->len, cmd==Fread); + + /* + * give bus to DMA, floppyintr() will read result + */ + floppywait(0); + dmaend(DMAchan); + poperror(); + + /* + * check for errors + */ + if(fl.nstat < 7){ + DPRINT("xfer: confused\n"); + fl.confused = 1; + error(Eio); + } + if((fl.stat[0] & Codemask)!=0 || fl.stat[1] || fl.stat[2]){ + DPRINT("xfer: failed %ux %ux %ux\n", fl.stat[0], + fl.stat[1], fl.stat[2]); + DPRINT("offset %lud len %ld\n", off, dp->len); + if((fl.stat[0]&Codemask)==Cmdexec && fl.stat[1]==Overrun){ + DPRINT("DMA overrun: retry\n"); + } else + dp->confused = 1; + error(Eio); + } + + /* + * check for correct cylinder + */ + offset = fl.stat[3] * dp->t->heads + fl.stat[4]; + offset = offset*dp->t->sectors + fl.stat[5] - 1; + offset = offset * c2b[fl.stat[6]]; + if(offset != off+dp->len){ + DPRINT("xfer: ends on wrong cyl\n"); + dp->confused = 1; + error(Eio); + } + poperror(); + + dp->lasttouched = m->ticks; + return dp->len; +} + +/* + * format a track + */ +static void +floppyformat(FDrive *dp, Cmdbuf *cb) +{ + int cyl, h, sec; + ulong track; + uchar *buf, *bp; + FType *t; + + /* + * set the type + */ + if(cb->nf == 2){ + for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){ + if(strcmp(cb->f[1], t->name)==0 && t->dt==dp->dt){ + dp->t = t; + floppydir[1+NFDIR*dp->dev].length = dp->t->cap; + break; + } + } + if(t >= &floppytype[nelem(floppytype)]) + error(Ebadarg); + } else if(cb->nf == 1){ + floppysetdef(dp); + t = dp->t; + } else { + cmderror(cb, "invalid floppy format command"); + SET(t); + } + + /* + * buffer for per track info + */ + buf = smalloc(t->sectors*4); + if(waserror()){ + free(buf); + nexterror(); + } + + /* force a recalibrate to cylinder 0 */ + dp->confused = 1; + if(!waserror()){ + floppyon(dp); + poperror(); + } + + /* + * format a track at time + */ + for(track = 0; track < t->tracks*t->heads; track++){ + cyl = track/t->heads; + h = track % t->heads; + + /* + * seek to track, ignore errors + */ + floppyseek(dp, track*t->tsize); + dp->cyl = cyl; + dp->confused = 0; + + /* + * set up the dma (dp->len may be trimmed) + */ + bp = buf; + for(sec = 1; sec <= t->sectors; sec++){ + *bp++ = cyl; + *bp++ = h; + *bp++ = sec; + *bp++ = t->bcode; + } + if(waserror()){ + dmaend(DMAchan); + nexterror(); + } + if(dmasetup(DMAchan, buf, bp-buf, 0) < 0) + error(Eio); + + /* + * start operation + */ + fl.ncmd = 0; + fl.cmd[fl.ncmd++] = Fformat; + fl.cmd[fl.ncmd++] = (h<<2) | dp->dev; + fl.cmd[fl.ncmd++] = t->bcode; + fl.cmd[fl.ncmd++] = t->sectors; + fl.cmd[fl.ncmd++] = t->fgpl; + fl.cmd[fl.ncmd++] = 0x5a; + if(floppycmd() < 0) + error(Eio); + + /* Poll ready bits and transfer data */ + floppyexec((char *)buf, bp-buf, 0); + + /* + * give bus to DMA, floppyintr() will read result + */ + floppywait(1); + dmaend(DMAchan); + poperror(); + + /* + * check for errors + */ + if(fl.nstat < 7){ + DPRINT("format: confused\n"); + fl.confused = 1; + error(Eio); + } + if((fl.stat[0]&Codemask)!=0 || fl.stat[1]|| fl.stat[2]){ + DPRINT("format: failed %ux %ux %ux\n", + fl.stat[0], fl.stat[1], fl.stat[2]); + dp->confused = 1; + error(Eio); + } + } + free(buf); + dp->confused = 1; + poperror(); +} + +static void +floppyintr(Ureg *) +{ + switch(fl.cmd[0]&~Fmulti){ + case Fread: + case Fwrite: + case Fformat: + case Fdumpreg: + floppyresult(); + break; + case Fseek: + case Frecal: + default: + floppysense(); /* to clear interrupt */ + break; + } + fl.ncmd = 0; + wakeup(&fl.r); +} + +Dev floppydevtab = { + 'f', + "floppy", + + floppyreset, + devinit, + devshutdown, + floppyattach, + floppywalk, + floppystat, + floppyopen, + devcreate, + floppyclose, + floppyread, + devbread, + floppywrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/pc/devi82365.c b/os/pc/devi82365.c new file mode 100644 index 00000000..5c67847f --- /dev/null +++ b/os/pc/devi82365.c @@ -0,0 +1,1044 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +/* + * Intel 82365SL PCIC controller and compatibles. + */ +enum +{ + /* + * registers indices + */ + Rid= 0x0, /* identification and revision */ + Ris= 0x1, /* interface status */ + Rpc= 0x2, /* power control */ + Foutena= (1<<7), /* output enable */ + Fautopower= (1<<5), /* automatic power switching */ + Fcardena= (1<<4), /* PC card enable */ + Rigc= 0x3, /* interrupt and general control */ + Fiocard= (1<<5), /* I/O card (vs memory) */ + Fnotreset= (1<<6), /* reset if not set */ + FSMIena= (1<<4), /* enable change interrupt on SMI */ + Rcsc= 0x4, /* card status change */ + Rcscic= 0x5, /* card status change interrupt config */ + Fchangeena= (1<<3), /* card changed */ + Fbwarnena= (1<<1), /* card battery warning */ + Fbdeadena= (1<<0), /* card battery dead */ + Rwe= 0x6, /* address window enable */ + Fmem16= (1<<5), /* use A23-A12 to decode address */ + Rio= 0x7, /* I/O control */ + Fwidth16= (1<<0), /* 16 bit data width */ + Fiocs16= (1<<1), /* IOCS16 determines data width */ + Fzerows= (1<<2), /* zero wait state */ + Ftiming= (1<<3), /* timing register to use */ + Riobtm0lo= 0x8, /* I/O address 0 start low byte */ + Riobtm0hi= 0x9, /* I/O address 0 start high byte */ + Riotop0lo= 0xa, /* I/O address 0 stop low byte */ + Riotop0hi= 0xb, /* I/O address 0 stop high byte */ + Riobtm1lo= 0xc, /* I/O address 1 start low byte */ + Riobtm1hi= 0xd, /* I/O address 1 start high byte */ + Riotop1lo= 0xe, /* I/O address 1 stop low byte */ + Riotop1hi= 0xf, /* I/O address 1 stop high byte */ + Rmap= 0x10, /* map 0 */ + + /* + * CL-PD67xx extension registers + */ + Rmisc1= 0x16, /* misc control 1 */ + F5Vdetect= (1<<0), + Fvcc3V= (1<<1), + Fpmint= (1<<2), + Fpsirq= (1<<3), + Fspeaker= (1<<4), + Finpack= (1<<7), + Rfifo= 0x17, /* fifo control */ + Fflush= (1<<7), /* flush fifo */ + Rmisc2= 0x1E, /* misc control 2 */ + Flowpow= (1<<1), /* low power mode */ + Rchipinfo= 0x1F, /* chip information */ + Ratactl= 0x26, /* ATA control */ + + /* + * offsets into the system memory address maps + */ + Mbtmlo= 0x0, /* System mem addr mapping start low byte */ + Mbtmhi= 0x1, /* System mem addr mapping start high byte */ + F16bit= (1<<7), /* 16-bit wide data path */ + Mtoplo= 0x2, /* System mem addr mapping stop low byte */ + Mtophi= 0x3, /* System mem addr mapping stop high byte */ + Ftimer1= (1<<6), /* timer set 1 */ + Mofflo= 0x4, /* Card memory offset address low byte */ + Moffhi= 0x5, /* Card memory offset address high byte */ + Fregactive= (1<<6), /* attribute memory */ + + /* + * configuration registers - they start at an offset in attribute + * memory found in the CIS. + */ + Rconfig= 0, + Creset= (1<<7), /* reset device */ + Clevel= (1<<6), /* level sensitive interrupt line */ + Cirq= (1<<2), /* IRQ enable */ + Cdecode= (1<<1), /* address decode */ + Cfunc= (1<<0), /* function enable */ + Riobase0= 5, + Riobase1= 6, + Riosize= 9, +}; + +#define MAP(x,o) (Rmap + (x)*0x8 + o) + +typedef struct I82365 I82365; + +/* a controller */ +enum +{ + Ti82365, + Tpd6710, + Tpd6720, + Tvg46x, +}; +struct I82365 +{ + int type; + int dev; + int nslot; + int xreg; /* index register address */ + int dreg; /* data register address */ + int irq; +}; +static I82365 *controller[4]; +static int ncontroller; +static PCMslot *slot; +static PCMslot *lastslot; +static nslot; + +static void i82365intr(Ureg*, void*); +static int pcmio(int, ISAConf*); +static long pcmread(int, int, void*, long, vlong); +static long pcmwrite(int, int, void*, long, vlong); + +static void i82365dump(PCMslot*); + +/* + * reading and writing card registers + */ +static uchar +rdreg(PCMslot *pp, int index) +{ + outb(((I82365*)pp->cp)->xreg, pp->base + index); + return inb(((I82365*)pp->cp)->dreg); +} +static void +wrreg(PCMslot *pp, int index, uchar val) +{ + outb(((I82365*)pp->cp)->xreg, pp->base + index); + outb(((I82365*)pp->cp)->dreg, val); +} + +/* + * get info about card + */ +static void +slotinfo(PCMslot *pp) +{ + uchar isr; + + isr = rdreg(pp, Ris); + pp->occupied = (isr & (3<<2)) == (3<<2); + pp->powered = isr & (1<<6); + pp->battery = (isr & 3) == 3; + pp->wrprot = isr & (1<<4); + pp->busy = isr & (1<<5); + pp->msec = TK2MS(MACHP(0)->ticks); +} + +static int +vcode(int volt) +{ + switch(volt){ + case 5: + return 1; + case 12: + return 2; + default: + return 0; + } +} + +/* + * enable the slot card + */ +static void +slotena(PCMslot *pp) +{ + if(pp->enabled) + return; + + /* power up and unreset, wait's are empirical (???) */ + wrreg(pp, Rpc, Fautopower|Foutena|Fcardena); + delay(300); + wrreg(pp, Rigc, 0); + delay(100); + wrreg(pp, Rigc, Fnotreset); + delay(5000); + + /* get configuration */ + slotinfo(pp); + if(pp->occupied){ + pcmcisread(pp); + pp->enabled = 1; + } else + wrreg(pp, Rpc, Fautopower); +} + +/* + * disable the slot card + */ +static void +slotdis(PCMslot *pp) +{ + wrreg(pp, Rpc, 0); /* turn off card power */ + wrreg(pp, Rwe, 0); /* no windows */ + pp->enabled = 0; +} + +/* + * status change interrupt + */ +static void +i82365intr(Ureg *, void *) +{ + uchar csc, was; + PCMslot *pp; + + if(slot == 0) + return; + + for(pp = slot; pp < lastslot; pp++){ + csc = rdreg(pp, Rcsc); + was = pp->occupied; + slotinfo(pp); + if(csc & (1<<3) && was != pp->occupied){ + if(!pp->occupied) + slotdis(pp); + } + } +} + +enum +{ + Mshift= 12, + Mgran= (1<mlock); + + /* convert offset to granularity */ + if(len <= 0) + len = 1; + e = ROUND(offset+len, Mgran); + offset &= Mmask; + len = e - offset; + + /* look for a map that covers the right area */ + we = rdreg(pp, Rwe); + bit = 1; + nm = 0; + for(m = pp->mmap; m < &pp->mmap[nelem(pp->mmap)]; m++){ + if((we & bit)) + if(m->attr == attr) + if(offset >= m->ca && e <= m->cea){ + + m->ref++; + unlock(&pp->mlock); + return m; + } + bit <<= 1; + if(nm == 0 && m->ref == 0) + nm = m; + } + m = nm; + if(m == 0){ + unlock(&pp->mlock); + return 0; + } + + /* if isa space isn't big enough, free it and get more */ + if(m->len < len){ + if(m->isa){ + umbfree(m->isa, m->len); + m->len = 0; + } + m->isa = PADDR(umbmalloc(0, len, Mgran)); + if(m->isa == 0){ + print("pcmmap: out of isa space\n"); + unlock(&pp->mlock); + return 0; + } + m->len = len; + } + + /* set up new map */ + m->ca = offset; + m->cea = m->ca + m->len; + m->attr = attr; + i = m-pp->mmap; + bit = 1<isa>>Mshift); + wrreg(pp, MAP(i, Mbtmhi), (m->isa>>(Mshift+8)) | F16bit); + wrreg(pp, MAP(i, Mtoplo), (m->isa+m->len-1)>>Mshift); + wrreg(pp, MAP(i, Mtophi), ((m->isa+m->len-1)>>(Mshift+8))); + offset -= m->isa; + offset &= (1<<25)-1; + offset >>= Mshift; + wrreg(pp, MAP(i, Mofflo), offset); + wrreg(pp, MAP(i, Moffhi), (offset>>8) | (attr ? Fregactive : 0)); + wrreg(pp, Rwe, we | bit); /* enable map */ + m->ref = 1; + + unlock(&pp->mlock); + return m; +} + +void +pcmunmap(int slotno, PCMmap* m) +{ + PCMslot *pp; + + pp = slot + slotno; + lock(&pp->mlock); + m->ref--; + unlock(&pp->mlock); +} + +static void +increfp(PCMslot *pp) +{ + lock(pp); + if(pp->ref++ == 0) + slotena(pp); + unlock(pp); +} + +static void +decrefp(PCMslot *pp) +{ + lock(pp); + if(pp->ref-- == 1) + slotdis(pp); + unlock(pp); +} + +/* + * look for a card whose version contains 'idstr' + */ +static int +pcmcia_pcmspecial(char *idstr, ISAConf *isa) +{ + PCMslot *pp; + extern char *strstr(char*, char*); + int enabled; + + for(pp = slot; pp < lastslot; pp++){ + if(pp->special) + continue; /* already taken */ + + /* + * make sure we don't power on cards when we already know what's + * in them. We'll reread every two minutes if necessary + */ + enabled = 0; + if (pp->msec == ~0 || TK2MS(MACHP(0)->ticks) - pp->msec > 120000){ + increfp(pp); + enabled++; + } + + if(pp->occupied) { + if(strstr(pp->verstr, idstr)){ + if (!enabled){ + enabled = 1; + increfp(pp); + } + if(isa == 0 || pcmio(pp->slotno, isa) == 0){ + pp->special = 1; + return pp->slotno; + } + } + } else + pp->special = 1; + if (enabled) + decrefp(pp); + } + return -1; +} + +static void +pcmcia_pcmspecialclose(int slotno) +{ + PCMslot *pp; + + if(slotno >= nslot) + panic("pcmspecialclose"); + pp = slot + slotno; + pp->special = 0; + decrefp(pp); +} + +enum +{ + Qdir, + Qmem, + Qattr, + Qctl, + + Nents = 3, +}; + +#define SLOTNO(c) ((ulong)((c->qid.path>>8)&0xff)) +#define TYPE(c) ((ulong)(c->qid.path&0xff)) +#define QID(s,t) (((s)<<8)|(t)) + +static int +pcmgen(Chan *c, char*, Dirtab *, int , int i, Dir *dp) +{ + int slotno; + Qid qid; + long len; + PCMslot *pp; + + if(i == DEVDOTDOT){ + mkqid(&qid, Qdir, 0, QTDIR); + devdir(c, qid, "#y", 0, eve, 0555, dp); + return 1; + } + + if(i >= Nents*nslot) + return -1; + slotno = i/Nents; + pp = slot + slotno; + len = 0; + switch(i%Nents){ + case 0: + qid.path = QID(slotno, Qmem); + snprint(up->genbuf, sizeof up->genbuf, "pcm%dmem", slotno); + len = pp->memlen; + break; + case 1: + qid.path = QID(slotno, Qattr); + snprint(up->genbuf, sizeof up->genbuf, "pcm%dattr", slotno); + len = pp->memlen; + break; + case 2: + qid.path = QID(slotno, Qctl); + snprint(up->genbuf, sizeof up->genbuf, "pcm%dctl", slotno); + break; + } + qid.vers = 0; + qid.type = QTFILE; + devdir(c, qid, up->genbuf, len, eve, 0660, dp); + return 1; +} + +static char *chipname[] = +{ +[Ti82365] "Intel 82365SL", +[Tpd6710] "Cirrus Logic CL-PD6710", +[Tpd6720] "Cirrus Logic CL-PD6720", +[Tvg46x] "Vadem VG-46x", +}; + +static I82365* +i82365probe(int x, int d, int dev) +{ + uchar c, id; + I82365 *cp; + ISAConf isa; + int i, nslot; + + outb(x, Rid + (dev<<7)); + id = inb(d); + if((id & 0xf0) != 0x80) + return 0; /* not a memory & I/O card */ + if((id & 0x0f) == 0x00) + return 0; /* no revision number, not possible */ + + cp = xalloc(sizeof(I82365)); + cp->xreg = x; + cp->dreg = d; + cp->dev = dev; + cp->type = Ti82365; + cp->nslot = 2; + + switch(id){ + case 0x82: + case 0x83: + case 0x84: + /* could be a cirrus */ + outb(x, Rchipinfo + (dev<<7)); + outb(d, 0); + c = inb(d); + if((c & 0xc0) != 0xc0) + break; + c = inb(d); + if((c & 0xc0) != 0x00) + break; + if(c & 0x20){ + cp->type = Tpd6720; + } else { + cp->type = Tpd6710; + cp->nslot = 1; + } + + /* low power mode */ + outb(x, Rmisc2 + (dev<<7)); + c = inb(d); + outb(d, c & ~Flowpow); + break; + } + + /* if it's not a Cirrus, it could be a Vadem... */ + if(cp->type == Ti82365){ + /* unlock the Vadem extended regs */ + outb(x, 0x0E + (dev<<7)); + outb(x, 0x37 + (dev<<7)); + + /* make the id register show the Vadem id */ + outb(x, 0x3A + (dev<<7)); + c = inb(d); + outb(d, c|0xC0); + outb(x, Rid + (dev<<7)); + c = inb(d); + if(c & 0x08) + cp->type = Tvg46x; + + /* go back to Intel compatible id */ + outb(x, 0x3A + (dev<<7)); + c = inb(d); + outb(d, c & ~0xC0); + } + + memset(&isa, 0, sizeof(ISAConf)); + if(isaconfig("pcmcia", ncontroller, &isa) && isa.irq) + cp->irq = isa.irq; + else + cp->irq = IrqPCMCIA; + + for(i = 0; i < isa.nopt; i++){ + if(cistrncmp(isa.opt[i], "nslot=", 6)) + continue; + nslot = strtol(&isa.opt[i][6], nil, 0); + if(nslot > 0 && nslot <= 2) + cp->nslot = nslot; + } + + controller[ncontroller++] = cp; + return cp; +} + +static void +i82365dump(PCMslot *pp) +{ + int i; + + for(i = 0; i < 0x40; i++){ + if((i&0x0F) == 0) + print("\n%2.2uX: ", i); + print("%2.2uX ", rdreg(pp, i)); + if(((i+1) & 0x0F) == 0x08) + print(" - "); + } + print("\n"); +} + +/* + * set up for slot cards + */ +void +devi82365link(void) +{ + static int already; + int i, j; + I82365 *cp; + PCMslot *pp; + char buf[32], *p; + + if(already) + return; + already = 1; + + if((p=getconf("pcmcia0")) && strncmp(p, "disabled", 8)==0) + return; + + if(_pcmspecial) + return; + + /* look for controllers if the ports aren't already taken */ + if(ioalloc(0x3E0, 2, 0, "i82365.0") >= 0){ + i82365probe(0x3E0, 0x3E1, 0); + i82365probe(0x3E0, 0x3E1, 1); + if(ncontroller == 0) + iofree(0x3E0); + } + if(ioalloc(0x3E2, 2, 0, "i82365.1") >= 0){ + i = ncontroller; + i82365probe(0x3E2, 0x3E3, 0); + i82365probe(0x3E2, 0x3E3, 1); + if(ncontroller == i) + iofree(0x3E2); + } + + if(ncontroller == 0) + return; + + _pcmspecial = pcmcia_pcmspecial; + _pcmspecialclose = pcmcia_pcmspecialclose; + + for(i = 0; i < ncontroller; i++) + nslot += controller[i]->nslot; + slot = xalloc(nslot * sizeof(PCMslot)); + + lastslot = slot; + for(i = 0; i < ncontroller; i++){ + cp = controller[i]; + print("#y%d: %d slot %s: port 0x%uX irq %d\n", + i, cp->nslot, chipname[cp->type], cp->xreg, cp->irq); + for(j = 0; j < cp->nslot; j++){ + pp = lastslot++; + pp->slotno = pp - slot; + pp->memlen = 64*MB; + pp->base = (cp->dev<<7) | (j<<6); + pp->cp = cp; + pp->msec = ~0; + pp->verstr[0] = 0; + slotdis(pp); + + /* interrupt on status change */ + wrreg(pp, Rcscic, (cp->irq<<4) | Fchangeena); + rdreg(pp, Rcsc); + } + + /* for card management interrupts */ + snprint(buf, sizeof buf, "i82365.%d", i); + intrenable(cp->irq, i82365intr, 0, BUSUNKNOWN, buf); + } +} + +static Chan* +i82365attach(char *spec) +{ + return devattach('y', spec); +} + +static Walkqid* +i82365walk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, pcmgen); +} + +static int +i82365stat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, pcmgen); +} + +static Chan* +i82365open(Chan *c, int omode) +{ + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Eperm); + } else + increfp(slot + SLOTNO(c)); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +i82365close(Chan *c) +{ + if(c->flag & COPEN) + if((c->qid.type & QTDIR) == 0) + decrefp(slot+SLOTNO(c)); +} + +/* a memmove using only bytes */ +static void +memmoveb(uchar *to, uchar *from, int n) +{ + while(n-- > 0) + *to++ = *from++; +} + +/* a memmove using only shorts & bytes */ +static void +memmoves(uchar *to, uchar *from, int n) +{ + ushort *t, *f; + + if((((ulong)to) & 1) || (((ulong)from) & 1) || (n & 1)){ + while(n-- > 0) + *to++ = *from++; + } else { + n = n/2; + t = (ushort*)to; + f = (ushort*)from; + while(n-- > 0) + *t++ = *f++; + } +} + +static long +pcmread(int slotno, int attr, void *a, long n, vlong off) +{ + int i, len; + PCMmap *m; + uchar *ac; + PCMslot *pp; + ulong offset = off; + + pp = slot + slotno; + if(pp->memlen < offset) + return 0; + if(pp->memlen < offset + n) + n = pp->memlen - offset; + + m = 0; + if(waserror()){ + if(m) + pcmunmap(pp->slotno, m); + nexterror(); + } + + ac = a; + for(len = n; len > 0; len -= i){ + m = pcmmap(pp->slotno, offset, 0, attr); + if(m == 0) + error("cannot map PCMCIA card"); + if(offset + len > m->cea) + i = m->cea - offset; + else + i = len; + memmoveb(ac, KADDR(m->isa + offset - m->ca), i); + pcmunmap(pp->slotno, m); + offset += i; + ac += i; + } + + poperror(); + return n; +} + +static long +i82365read(Chan *c, void *a, long n, vlong off) +{ + char *p, *buf, *e; + PCMslot *pp; + ulong offset = off; + + switch(TYPE(c)){ + case Qdir: + return devdirread(c, a, n, 0, 0, pcmgen); + case Qmem: + case Qattr: + return pcmread(SLOTNO(c), TYPE(c) == Qattr, a, n, off); + case Qctl: + buf = p = malloc(READSTR); + e = p + READSTR; + pp = slot + SLOTNO(c); + + buf[0] = 0; + if(pp->occupied){ + p = seprint(p, e, "occupied\n"); + if(pp->verstr[0]) + p = seprint(p, e, "version %s\n", pp->verstr); + } + if(pp->enabled) + p = seprint(p, e, "enabled\n"); + if(pp->powered) + p = seprint(p, e, "powered\n"); + if(pp->configed) + p = seprint(p, e, "configed\n"); + if(pp->wrprot) + p = seprint(p, e, "write protected\n"); + if(pp->busy) + p = seprint(p, e, "busy\n"); + seprint(p, e, "battery lvl %d\n", pp->battery); + + n = readstr(offset, a, n, buf); + free(buf); + + return n; + } + error(Ebadarg); + return -1; /* not reached */ +} + +static long +pcmwrite(int dev, int attr, void *a, long n, vlong off) +{ + int i, len; + PCMmap *m; + uchar *ac; + PCMslot *pp; + ulong offset = off; + + pp = slot + dev; + if(pp->memlen < offset) + return 0; + if(pp->memlen < offset + n) + n = pp->memlen - offset; + + m = 0; + if(waserror()){ + if(m) + pcmunmap(pp->slotno, m); + nexterror(); + } + + ac = a; + for(len = n; len > 0; len -= i){ + m = pcmmap(pp->slotno, offset, 0, attr); + if(m == 0) + error("cannot map PCMCIA card"); + if(offset + len > m->cea) + i = m->cea - offset; + else + i = len; + memmoveb(KADDR(m->isa + offset - m->ca), ac, i); + pcmunmap(pp->slotno, m); + offset += i; + ac += i; + } + + poperror(); + return n; +} + +static long +i82365write(Chan *c, void *a, long n, vlong off) +{ + PCMslot *pp; + char buf[32]; + + switch(TYPE(c)){ + case Qctl: + if(n >= sizeof(buf)) + n = sizeof(buf) - 1; + strncpy(buf, a, n); + buf[n] = 0; + pp = slot + SLOTNO(c); + if(!pp->occupied) + error(Eio); + + /* set vpp on card */ + if(strncmp(buf, "vpp", 3) == 0) + wrreg(pp, Rpc, vcode(atoi(buf+3))|Fautopower|Foutena|Fcardena); + return n; + case Qmem: + case Qattr: + pp = slot + SLOTNO(c); + if(pp->occupied == 0 || pp->enabled == 0) + error(Eio); + n = pcmwrite(pp->slotno, TYPE(c) == Qattr, a, n, off); + if(n < 0) + error(Eio); + return n; + } + error(Ebadarg); + return -1; /* not reached */ +} + +Dev i82365devtab = { + 'y', + "i82365", + + devreset, + devinit, + devshutdown, + i82365attach, + i82365walk, + i82365stat, + i82365open, + devcreate, + i82365close, + i82365read, + devbread, + i82365write, + devbwrite, + devremove, + devwstat, +}; + +/* + * configure the PCMslot for IO. We assume very heavily that we can read + * configuration info from the CIS. If not, we won't set up correctly. + */ +static int +pcmio(int slotno, ISAConf *isa) +{ + uchar we, x, *p; + PCMslot *pp; + PCMconftab *ct, *et, *t; + PCMmap *m; + int i, index, irq; + char *cp; + + irq = isa->irq; + if(irq == 2) + irq = 9; + + if(slotno > nslot) + return -1; + pp = slot + slotno; + + if(!pp->occupied) + return -1; + + et = &pp->ctab[pp->nctab]; + + ct = 0; + for(i = 0; i < isa->nopt; i++){ + if(strncmp(isa->opt[i], "index=", 6)) + continue; + index = strtol(&isa->opt[i][6], &cp, 0); + if(cp == &isa->opt[i][6] || index >= pp->nctab) + return -1; + ct = &pp->ctab[index]; + } + + if(ct == 0){ + /* assume default is right */ + if(pp->def) + ct = pp->def; + else + ct = pp->ctab; + + /* try for best match */ + if(ct->nio == 0 + || ct->io[0].start != isa->port || ((1<irqs) == 0){ + for(t = pp->ctab; t < et; t++) + if(t->nio + && t->io[0].start == isa->port + && ((1<irqs)){ + ct = t; + break; + } + } + if(ct->nio == 0 || ((1<irqs) == 0){ + for(t = pp->ctab; t < et; t++) + if(t->nio && ((1<irqs)){ + ct = t; + break; + } + } + if(ct->nio == 0){ + for(t = pp->ctab; t < et; t++) + if(t->nio){ + ct = t; + break; + } + } + } + + if(ct == et || ct->nio == 0) + return -1; + if(isa->port == 0 && ct->io[0].start == 0) + return -1; + + /* route interrupts */ + isa->irq = irq; + wrreg(pp, Rigc, irq | Fnotreset | Fiocard); + + /* set power and enable device */ + x = vcode(ct->vpp1); + wrreg(pp, Rpc, x|Fautopower|Foutena|Fcardena); + + /* 16-bit data path */ + if(ct->bit16) + x = Ftiming|Fiocs16|Fwidth16; + else + x = Ftiming; + if(ct->nio == 2 && ct->io[1].start) + x |= x<<4; + wrreg(pp, Rio, x); + + /* + * enable io port map 0 + * the 'top' register value includes the last valid address + */ + if(isa->port == 0) + isa->port = ct->io[0].start; + we = rdreg(pp, Rwe); + wrreg(pp, Riobtm0lo, isa->port); + wrreg(pp, Riobtm0hi, isa->port>>8); + i = isa->port+ct->io[0].len-1; + wrreg(pp, Riotop0lo, i); + wrreg(pp, Riotop0hi, i>>8); + we |= 1<<6; + if(ct->nio >= 2 && ct->io[1].start){ + wrreg(pp, Riobtm1lo, ct->io[1].start); + wrreg(pp, Riobtm1hi, ct->io[1].start>>8); + i = ct->io[1].start+ct->io[1].len-1; + wrreg(pp, Riotop1lo, i); + wrreg(pp, Riotop1hi, i>>8); + we |= 1<<7; + } + wrreg(pp, Rwe, we); + + /* only touch Rconfig if it is present */ + m = pcmmap(slotno, pp->cfg[0].caddr + Rconfig, 0x20, 1); + p = KADDR(m->isa + pp->cfg[0].caddr - m->ca); + if(pp->cfg[0].cpresent & (1<index; + if(ct->irqtype & 0x20) + x |= Clevel; + + /* enable the device, enable address decode and + * irq enable. + */ + x |= Cfunc|Cdecode|Cirq; + + p[0] = x; + //delay(5); + microdelay(40); + } + + if(pp->cfg[0].cpresent & (1<port; + p[Riobase1 << 1] = isa->port >> 8; + } + + if(pp->cfg[0].cpresent & (1<io[0].len; + pcmunmap(slotno, m); + return 0; +} diff --git a/os/pc/devlm78.c b/os/pc/devlm78.c new file mode 100644 index 00000000..617d5cb9 --- /dev/null +++ b/os/pc/devlm78.c @@ -0,0 +1,346 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +/* this driver doesn't implement the management interrupts. we + * leave the LM78 interrupts set to whatever the BIOS did. we do + * allow reading and writing the the readouts and alarm values. + * Read(2)ing or write(2)ing at offset 0x0-0x1f, is + * equivalent to reading or writing lm78 registers 0x20-0x3f. + */ +enum +{ + /* address of chip on serial interface */ + Serialaddr= 0x2d, + + /* parallel access registers */ + Rpaddr= 0x5, + Bbusy= (1<<7), + Rpdata= 0x6, + + /* internal register addresses */ + Rconfig= 0x40, + Bstart= (1<<0), + Bsmiena= (1<<1), + Birqena= (1<<2), + Bintclr= (1<<3), + Breset= (1<<4), + Bnmi= (1<<5), /* if set, use nmi, else irq */ + Bpowbypass= (1<<6), + Binit= (1<<7), + Ristat1= 0x41, + Ristat2= 0x42, + Rsmimask1= 0x43, + Rsmimask2= 0x44, + Rnmimask1= 0x45, + Rnmimask2= 0x46, + Rvidfan= 0x47, /* set fan counter, and read voltage level */ + Mvid= 0x0f, + Mfan= 0xf0, + Raddr= 0x48, /* address used on serial bus */ + Rresetid= 0x49, /* chip reset and ID register */ + Rpost= 0x00, /* start of post ram */ + Rvalue= 0x20, /* start of value ram */ + + VRsize= 0x20, /* size of value ram */ +}; + +enum +{ + Qdir, + Qlm78vram, +}; + +static Dirtab lm78dir[] = { + ".", { Qdir, 0, QTDIR}, 0, 0555, + "lm78vram", { Qlm78vram, 0 }, 0, 0444, +}; + +/* interface type */ +enum +{ + None= 0, + Smbus, + Parallel, +}; + +static struct { + QLock; + int probed; + int ifc; /* which interface is connected */ + SMBus *smbus; /* serial interface */ + int port; /* parallel interface */ +} lm78; + +extern SMBus* piix4smbus(void); + +/* wait for device to become quiescent and then set the */ +/* register address */ +static void +setreg(int reg) +{ + int tries; + + for(tries = 0; tries < 1000000; tries++) + if((inb(lm78.port+Rpaddr) & Bbusy) == 0){ + outb(lm78.port+Rpaddr, reg); + return; + } + error("lm78 broken"); +} + +/* routines that actually touch the device */ +static void +lm78wrreg(int reg, uchar val) +{ + if(waserror()){ + qunlock(&lm78); + nexterror(); + } + qlock(&lm78); + + switch(lm78.ifc){ + case Smbus: + lm78.smbus->transact(lm78.smbus, SMBbytewrite, Serialaddr, reg, &val); + break; + case Parallel: + setreg(reg); + outb(lm78.port+Rpdata, val); + break; + default: + error(Enodev); + break; + } + + qunlock(&lm78); + poperror(); +} + +static int +lm78rdreg(int reg) +{ + uchar val; + + if(waserror()){ + qunlock(&lm78); + nexterror(); + } + qlock(&lm78); + + switch(lm78.ifc){ + case Smbus: + lm78.smbus->transact(lm78.smbus, SMBsend, Serialaddr, reg, nil); + lm78.smbus->transact(lm78.smbus, SMBrecv, Serialaddr, 0, &val); + break; + case Parallel: + setreg(reg); + val = inb(lm78.port+Rpdata); + break; + default: + error(Enodev); + break; + } + + qunlock(&lm78); + poperror(); + return val; +} + +/* start the chip monitoring but don't change any smi + * interrupts and/or alarms that the BIOS may have set up. + * this isn't locked because it's thought to be idempotent + */ +static void +lm78enable(void) +{ + uchar config; + + if(lm78.ifc == None) + error(Enodev); + + if(lm78.probed == 0){ + /* make sure its really there */ + if(lm78rdreg(Raddr) != Serialaddr){ + lm78.ifc = None; + error(Enodev); + } else { + /* start the sampling */ + config = lm78rdreg(Rconfig); + config = (config | Bstart) & ~(Bintclr|Binit); + lm78wrreg(Rconfig, config); +pprint("Rvidfan %2.2ux\n", lm78rdreg(Rconfig), lm78rdreg(Rvidfan)); + } + lm78.probed = 1; + } +} + +enum +{ + IntelVendID= 0x8086, + PiixID= 0x122E, + Piix3ID= 0x7000, + + Piix4PMID= 0x7113, /* PIIX4 power management function */ + + PCSC= 0x78, /* programmable chip select control register */ + PCSC8bytes= 0x01, +}; + +/* figure out what kind of interface we could have */ +void +lm78reset(void) +{ + int pcs; + Pcidev *p; + + lm78.ifc = None; + p = nil; + while((p = pcimatch(p, IntelVendID, 0)) != nil){ + switch(p->did){ + /* these bridges use the PCSC to map the lm78 into port space. */ + /* for this case the lm78's CS# select is connected to the PIIX's */ + /* PCS# output and the bottom 3 bits of address are passed to the */ + /* LM78's A0-A2 inputs. */ + case PiixID: + case Piix3ID: + pcs = pcicfgr16(p, PCSC); + if(pcs & 3) { + /* already enabled */ + lm78.port = pcs & ~3; + lm78.ifc = Parallel; + return; + } + + /* enable the chip, use default address 0x50 */ + pcicfgw16(p, PCSC, 0x50|PCSC8bytes); + pcs = pcicfgr16(p, PCSC); + lm78.port = pcs & ~3; + lm78.ifc = Parallel; + return; + + /* this bridge puts the lm78's serial interface on the smbus */ + case Piix4PMID: + lm78.smbus = piix4smbus(); + if(lm78.smbus == nil) + continue; + print("found piix4 smbus, base %lud\n", lm78.smbus->base); + lm78.ifc = Smbus; + return; + } + } +} + +Walkqid * +lm78walk(Chan* c, Chan *nc, char** name, int nname) +{ + return devwalk(c, nc, name, nname, lm78dir, nelem(lm78dir), devgen); +} + +static int +lm78stat(Chan* c, uchar* dp, int n) +{ + return devstat(c, dp, n, lm78dir, nelem(lm78dir), devgen); +} + +static Chan* +lm78open(Chan* c, int omode) +{ + return devopen(c, omode, lm78dir, nelem(lm78dir), devgen); +} + +static void +lm78close(Chan*) +{ +} + +enum +{ + Linelen= 25, +}; + +static long +lm78read(Chan *c, void *a, long n, vlong offset) +{ + uchar *va = a; + int off, e; + + off = offset; + + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, lm78dir, nelem(lm78dir), devgen); + + case Qlm78vram: + if(off >= VRsize) + return 0; + e = off + n; + if(e > VRsize) + e = VRsize; + for(; off < e; off++) + *va++ = lm78rdreg(Rvalue+off); + return (int)(va - (uchar*)a); + } + return 0; +} + +static long +lm78write(Chan *c, void *a, long n, vlong offset) +{ + uchar *va = a; + int off, e; + + off = offset; + + switch((ulong)c->qid.path){ + default: + error(Eperm); + + case Qlm78vram: + if(off >= VRsize) + return 0; + e = off + n; + if(e > VRsize) + e = VRsize; + for(; off < e; off++) + lm78wrreg(Rvalue+off, *va++); + return va - (uchar*)a; + } + return 0; +} + +extern Dev lm78devtab; + +static Chan* +lm78attach(char* spec) +{ + lm78enable(); + + return devattach(lm78devtab.dc, spec); +} + +Dev lm78devtab = { + 'T', + "lm78", + + lm78reset, + devinit, + devshutdown, + lm78attach, + lm78walk, + lm78stat, + lm78open, + devcreate, + lm78close, + lm78read, + devbread, + lm78write, + devbwrite, + devremove, + devwstat, +}; + diff --git a/os/pc/devlpt.c b/os/pc/devlpt.c new file mode 100644 index 00000000..2fbc2aba --- /dev/null +++ b/os/pc/devlpt.c @@ -0,0 +1,245 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* Centronix parallel (printer) port */ + +/* base addresses */ +static int lptbase[] = { + 0x378, /* lpt1 */ + 0x3bc, /* lpt2 */ + 0x278 /* lpt3 (sic) */ +}; +#define NDEV nelem(lptbase) +static int lptallocd[NDEV]; + +/* offsets, and bits in the registers */ +enum +{ + Qdir= 0x8000, + /* data latch register */ + Qdlr= 0x0, + /* printer status register */ + Qpsr= 0x1, + Fnotbusy= 0x80, + Fack= 0x40, + Fpe= 0x20, + Fselect= 0x10, + Fnoerror= 0x08, + /* printer control register */ + Qpcr= 0x2, + Fie= 0x10, + Fselectin= 0x08, + Finitbar= 0x04, + Faf= 0x02, + Fstrobe= 0x01, + /* fake `data register' */ + Qdata= 0x3, +}; + +static int lptready(void*); +static void outch(int, int); +static void lptintr(Ureg*, void*); + +static Rendez lptrendez; + +Dirtab lptdir[]={ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "dlr", {Qdlr}, 1, 0666, + "psr", {Qpsr}, 5, 0444, + "pcr", {Qpcr}, 0, 0222, + "data", {Qdata}, 0, 0222, +}; + +static int +lptgen(Chan *c, char*, Dirtab *tab, int ntab, int i, Dir *dp) +{ + Qid qid; + + if(i == DEVDOTDOT){ + mkqid(&qid, Qdir, 0, QTDIR); + devdir(c, qid, ".", 0, eve, 0555, dp); + return 1; + } + i++; /* skip first element for . itself */ + if(tab==0 || i>=ntab) + return -1; + tab += i; + qid = tab->qid; + qid.path &= ~Qdir; + if(qid.path < Qdata) + qid.path += lptbase[c->dev]; + qid.vers = c->dev; + sprint(up->genbuf, "lpt%lud%s", c->dev+1, tab->name); + devdir(c, qid, up->genbuf, tab->length, eve, tab->perm, dp); + return 1; +} + +static Chan* +lptattach(char *spec) +{ + Chan *c; + int i = (spec && *spec) ? strtol(spec, 0, 0) : 1; + char name[5]; + static int set; + + if(!set){ + outb(lptbase[i-1]+Qpcr, 0); /* turn off interrupts */ + set = 1; + intrenable(IrqLPT, lptintr, 0, BUSUNKNOWN, "lpt"); + } + if(i < 1 || i > NDEV) + error(Ebadarg); + if(lptallocd[i-1] == 0){ + int ecr; + sprint(name, "lpt%d", i-1); + if(ioalloc(lptbase[i-1], 3, 0, name) < 0) + error("lpt port space in use"); + lptallocd[i-1] = 1; + // Detect ECP - if found, put into PS/2 mode to suit style of driver + ecr = lptbase[i-1] + 0x402; + if ((inb(ecr) & 3) == 1) { + outb(ecr, 0x34); + if (inb(ecr) == 0x35) { + outb(ecr, (inb(ecr) & 0x1f) | (1 << 5)); + if(ioalloc(ecr, 1, 0, name) < 0) + error("lpt ecr port space in use"); + } + } + } + c = devattach('L', spec); + c->qid.path = Qdir; + c->dev = i-1; + return c; +} + +static Walkqid* +lptwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, lptdir, nelem(lptdir), lptgen); +} + +static int +lptstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, lptdir, nelem(lptdir), lptgen); +} + +static Chan* +lptopen(Chan *c, int omode) +{ + return devopen(c, omode, lptdir, nelem(lptdir), lptgen); +} + +static void +lptclose(Chan *) +{ +} + +static long +lptread(Chan *c, void *a, long n, vlong) +{ + char str[16]; + int size; + ulong o; + + if(c->qid.path == Qdir) + return devdirread(c, a, n, lptdir, nelem(lptdir), lptgen); + size = sprint(str, "0x%2.2ux\n", inb(c->qid.path)); + o = c->offset; + if(o >= size) + return 0; + if(o+n > size) + n = size-c->offset; + memmove(a, str+o, n); + return n; +} + +static long +lptwrite(Chan *c, void *a, long n, vlong) +{ + char str[16], *p; + long base, k; + + if(n <= 0) + return 0; + if(c->qid.path != Qdata){ + if(n > sizeof str-1) + n = sizeof str-1; + memmove(str, a, n); + str[n] = 0; + outb(c->qid.path, strtoul(str, 0, 0)); + return n; + } + p = a; + k = n; + base = lptbase[c->dev]; + if(waserror()){ + outb(base+Qpcr, Finitbar); + nexterror(); + } + while(--k >= 0) + outch(base, *p++); + poperror(); + return n; +} + +static void +outch(int base, int c) +{ + int status, tries; + + for(tries=0;; tries++) { + status = inb(base+Qpsr); + if(status&Fnotbusy) + break; + if((status&Fpe)==0 && (status&(Fselect|Fnoerror)) != (Fselect|Fnoerror)) + error(Eio); + if(tries < 10) + tsleep(&lptrendez, return0, nil, 1); + else { + outb(base+Qpcr, Finitbar|Fie); + tsleep(&lptrendez, lptready, (void *)base, 100); + } + } + outb(base+Qdlr, c); + outb(base+Qpcr, Finitbar|Fstrobe); + outb(base+Qpcr, Finitbar); +} + +static int +lptready(void *base) +{ + return inb((int)base+Qpsr)&Fnotbusy; +} + +static void +lptintr(Ureg *, void *) +{ + wakeup(&lptrendez); +} + +Dev lptdevtab = { + 'L', + "lpt", + + devreset, + devinit, + devshutdown, + lptattach, + lptwalk, + lptstat, + lptopen, + devcreate, + lptclose, + lptread, + devbread, + lptwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/pc/devmouse.c b/os/pc/devmouse.c new file mode 100644 index 00000000..1b3c55c6 --- /dev/null +++ b/os/pc/devmouse.c @@ -0,0 +1,672 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* + * TODO + * - shift key should modify right button with non-serial mice + * + intellimouse implementation + * - acceleration for all mouse types + * + spurious interrupt 7 after probing for ps2 mouse for the first time...? + * - test with ms busmouse + * - test with logitech serial mouse + */ + +/* + * mouse types + */ +enum +{ + Mouseother, + Mouseserial, + MousePS2, + Mousebus, + Mouseintelli, + Mousemsbus, +}; + +static int mousetype; +static int mouseswap; +static int mouseport; /* port for serial mice, irq for bus mice */ +static int mousesubtype; +static int accelerated; +static QLock mouselock; + +static int msbusmousedetect(void); +static int busmousedetect(void); +static void mousectl(char *buf); +static void mouseprobe(char *buf, int len); +static void mousestatus(char *buf, int len); + +enum{ + Qdir, + Qmousectl, + Qmouseprobe, +}; + +static +Dirtab mousetab[]={ + "mousectl", {Qmousectl, 0}, 0, 0600, + "mouseprobe", {Qmouseprobe, 0}, 0, 0400, +}; + +static Chan* +mouseattach(char* spec) +{ + return devattach('m', spec); +} + +static int +mousewalk(Chan* c, char* name) +{ + return devwalk(c, name, mousetab, nelem(mousetab), devgen); +} + +static void +mousestat(Chan* c, char* db) +{ + devstat(c, db, mousetab, nelem(mousetab), devgen); +} + +static Chan* +mouseopen(Chan* c, int omode) +{ + return devopen(c, omode, mousetab, nelem(mousetab), devgen); +} + +static void +mouseclose(Chan* c) +{ + USED(c); +} + +static long +mouseread(Chan* c, void* a, long n, vlong offset) +{ + char buf[64]; + USED(offset); + + switch(c->qid.path & ~CHDIR){ + case Qdir: + return devdirread(c, a, n, mousetab, nelem(mousetab), devgen); + case Qmousectl: + qlock(&mouselock); + mousestatus(buf, sizeof(buf)); + qunlock(&mouselock); + n = readstr(offset, a, n, buf); + break; + case Qmouseprobe: + if (mousetype) + error(Emouseset); + mouseprobe(buf, sizeof(buf)); + n = readstr(offset, a, n, buf); + break; + default: + n=0; + break; + } + return n; +} + +static long +mousewrite(Chan* c, void *a, long n, vlong) +{ + char buf[64]; + if ((c->qid.path & ~CHDIR) != Qmousectl) + error(Ebadusefd); + if (n >= sizeof(buf)) + n = sizeof(buf) - 1; + strncpy(buf, a, n); + buf[n] = 0; + + qlock(&mouselock); + if (waserror()) { + qunlock(&mouselock); + nexterror(); + } + mousectl(buf); + poperror(); + qunlock(&mouselock); + return n; +} + +static void +track(int b, int dx, int dy) +{ + static uchar map[8] = {0,4,2,6,1,5,3,7}; + if (mouseswap) + b = map[b&7]; + mousetrack(b, dx, dy); +} + +static void +setintellimouse(void) +{ + i8042auxcmd(0xF3); /* set sample */ + i8042auxcmd(0xC8); + i8042auxcmd(0xF3); /* set sample */ + i8042auxcmd(0x64); + i8042auxcmd(0xF3); /* set sample */ + i8042auxcmd(0x50); +} + +/* + * check for an Intellimouse. + * this is only used when we know there's an 8042 aux device + */ +static int +intellimousedetect(void) +{ + int id; + setintellimouse(); + /* check whether the mouse is now in extended mode */ + id = i8042auxcmdval(0xf2); /* identify device */ + if (id != 3) { + /* + * set back to standard sample rate (100 per sec) + */ + i8042auxcmd(0xf3); + i8042auxcmd(0x64); + return 0; + } + return 1; +} + +static void +mouseprobe(char *buf, int len) +{ + USED(len); + /* + * bus mice are easiest, so probe them first + */ + if (busmousedetect()) + sprint(buf, "bus\n"); + else if (msbusmousedetect()) + sprint(buf, "msbus\n"); + else if (i8042auxdetect()) { + if (intellimousedetect()) + sprint(buf, "ps2intellimouse\n"); + else + sprint(buf, "ps2\n"); + } + else + *buf = 0; +} + + +static void +mousestatus(char *buf, int len) +{ + char *s; + USED(len); + s = buf; + switch (mousetype) { + case Mouseserial: + if (mousesubtype) + s += sprint(s, "serial %d %c\n", mouseport, mousesubtype); + else + s += sprint(s, "serial %d\n", mouseport); + break; + case MousePS2: + s += sprint(s, "ps2\n"); + break; + case Mousebus: + s += sprint(s, "bus %d\n", mouseport); + break; + case Mouseintelli: + s += sprint(s, "intelli\n"); + break; + case Mousemsbus: + s += sprint(s, "msbus %d\n", mouseport); + break; + default: + case Mouseother: + s += sprint(s, "unknown\n"); + break; + } + if (accelerated) + s += sprint(s, "accelerated\n"); + if (mouseswap) + sprint(s, "swap\n"); +} + +/* + * Logitech 5 byte packed binary mouse format, 8 bit bytes + * + * shift & right button is the same as middle button (for 2 button mice) + */ +static int +logitechmouseputc(Queue *q, int c) +{ + static short msg[5]; + static int nb; + static uchar b[] = {0, 4, 2, 6, 1, 5, 3, 7, 0, 2, 2, 6, 1, 5, 3, 7}; + int dx, dy, newbuttons; + int mouseshifted; + + USED(q); + if((c&0xF0) == 0x80) + nb=0; + msg[nb] = c; + if(c & 0x80) + msg[nb] |= ~0xFF; /* sign extend */ + if(++nb == 5){ + mouseshifted = 0; /* XXX should be from keyboard shift key */ + newbuttons = b[((msg[0]&7)^7) | (mouseshifted ? 8 : 0)]; + dx = msg[1]+msg[3]; + dy = -(msg[2]+msg[4]); + track(newbuttons, dx, dy); + nb = 0; + } + return 0; +} + +/* + * microsoft 3 button, 7 bit bytes + * + * byte 0 - 1 L R Y7 Y6 X7 X6 + * byte 1 - 0 X5 X4 X3 X2 X1 X0 + * byte 2 - 0 Y5 Y4 Y3 Y2 Y1 Y0 + * byte 3 - 0 M x x x x x (optional) + * + * shift & right button is the same as middle button (for 2 button mice) + */ +static int +m3mouseputc(Queue*, int c) +{ + static uchar msg[3]; + static int nb; + static int middle; + static uchar b[] = { 0, 4, 1, 5, 0, 2, 1, 5 }; + short x; + int dx, dy, buttons; + + /* + * check bit 6 for consistency + */ + if(nb==0){ + if((c&0x40) == 0){ + /* an extra byte gets sent for the middle button */ + if(c & 0x1c) + return 0; + middle = (c&0x20) ? 2 : 0; + buttons = (mouse.b & ~2) | middle; + track(buttons, 0, 0); + return 0; + } + } + msg[nb] = c&0x3f; + if(++nb == 3){ + nb = 0; + buttons = middle | b[(msg[0]>>4)&3]; + x = (msg[0]&0x3)<<14; + dx = (x>>8) | msg[1]; + x = (msg[0]&0xc)<<12; + dy = (x>>8) | msg[2]; + track(buttons, dx, dy); + } + return 0; +} + +static void +serialmouse(int port, char *type, int setspeed) +{ + int (*putc)(Queue *, int) = 0; + char pn[KNAMELEN]; + + if(mousetype) + error(Emouseset); + + if(port >= 2 || port < 0) + error(Ebadarg); + + if (type == 0) + putc = logitechmouseputc; + else if (*type == 'M') + putc = m3mouseputc; + else + error(Ebadarg); + snprint(pn, sizeof(pn), "%d", port); + i8250mouse(pn, putc, setspeed); + mousetype = Mouseserial; + mouseport = port; + mousesubtype = (type && *type == 'M') ? 'M' : 0; +} + +/* + * ps/2 mouse message is three bytes + * + * byte 0 - 0 0 SDY SDX 1 M R L + * byte 1 - DX + * byte 2 - DY + * + * shift & left button is the same as middle button + */ +static void +ps2mouseputc(int c, int shift) +{ + static short msg[3]; + static int nb; + static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 5, 2, 3, 6, 7 }; + int buttons, dx, dy; + + /* + * check byte 0 for consistency + */ + if(nb==0 && (c&0xc8)!=0x08) + return; + + msg[nb] = c; + if(++nb == 3){ + nb = 0; + if(msg[0] & 0x10) + msg[1] |= 0xFF00; + if(msg[0] & 0x20) + msg[2] |= 0xFF00; + + buttons = b[(msg[0]&7) | (shift ? 8 : 0)]; + dx = msg[1]; + dy = -msg[2]; + track(buttons, dx, dy); + } + return; +} + +/* + * set up a ps2 mouse + */ +static void +ps2mouse(void) +{ + if(mousetype) + error(Emouseset); + + i8042auxenable(ps2mouseputc); + /* make mouse streaming, enabled */ + i8042auxcmd(0xEA); + i8042auxcmd(0xF4); + + mousetype = MousePS2; +} + +/* logitech bus mouse ports and commands */ +enum { + /* ports */ + BMdatap = 0x23c, + BMsigp = 0x23d, + BMctlp = 0x23e, + BMintrp = 0x23e, + BMconfigp = 0x23f, + + /* commands */ + BMintron = 0x0, + BMintroff = 0x10, + BMrxlo = 0x80, + BMrxhi = 0xa0, + BMrylo = 0xc0, + BMryhi = 0xe0, + + BMconfig = 0x91, + BMdefault = 0x90, + + BMsigval = 0xa5 +}; + +static void +busmouseintr(Ureg *, void *) +{ + char dx, dy; + uchar b; + static uchar oldb; + static Lock intrlock; + ilock(&intrlock); + outb(BMintrp, BMintroff); + outb(BMctlp, BMrxlo); + dx = inb(BMdatap) & 0xf; + outb(BMctlp, BMrxhi); + dx |= (inb(BMdatap) & 0xf) << 4; + outb(BMctlp, BMrylo); + dy = inb(BMdatap) & 0xf; + outb(BMctlp, BMryhi); + b = inb(BMdatap); + dy |= (b & 0xf) << 4; + b = ~(b >> 5) & 7; + if (dx || dy || b != oldb) { + oldb = b; + track((b>>2)|(b&0x02)|((b&0x01)<<2), dx, dy); + } + iunlock(&intrlock); + outb(BMintrp, BMintron); +} + +static int +busmousedetect(void) +{ + outb(BMconfigp, BMconfig); + outb(BMsigp, BMsigval); + delay(2); + if (inb(BMsigp) != BMsigval) + return 0; + outb(BMconfigp, BMdefault); + return 1; +} + +/* + * set up a logitech bus mouse + */ +static void +busmouse(int irq) +{ + if (mousetype) + error(Emouseset); + if (!busmousedetect()) + error(Enodev); + + intrenable(irq >= 0 ? irq+VectorPIC : VectorBUSMOUSE, busmouseintr, 0, BUSUNKNOWN); + outb(BMintrp, BMintron); + mousetype = Mousebus; + mouseport = irq >= 0 ? irq : VectorBUSMOUSE-VectorPIC; +} + +/* microsoft bus mouse ports and commands */ +enum { + MBMdatap= 0x23d, + MBMsigp= 0x23e, + MBMctlp= 0x23c, + MBMconfigp= 0x23f, + + MBMintron= 0x11, + MBMintroff= 0x10, + MBMrbuttons= 0x00, + MBMrx= 0x01, + MBMry= 0x02, + MBMstart= 0x80, + MBMcmd= 0x07, +}; + +static void +msbusmouseintr(Ureg *, void *) +{ + char dx, dy; + uchar b; + static uchar oldb; + static Lock intrlock; + ilock(&intrlock); + outb(MBMctlp, MBMcmd); + outb(MBMdatap, inb(MBMdatap)|0x20); + + outb(MBMctlp, MBMrx); + dx = inb(MBMdatap); + + outb(MBMctlp, MBMry); + dy = inb(MBMdatap); + + outb(MBMctlp, MBMrbuttons); + b = inb(MBMdatap) & 0x7; + + outb(MBMctlp, MBMcmd); + outb(MBMdatap, inb(MBMdatap)&0xdf); + + if (dx != 0 || dy != 0 || b != oldb) { + oldb = b; + /* XXX this is almost certainly wrong */ + track((b>>2)|(b&0x02)|((b&0x01)<<2), dx, dy); + } + iunlock(&intrlock); +} + +static int +msbusmousedetect(void) +{ + if (inb(MBMsigp) == 0xde) { + int v, i; + delay(1); + v = inb(MBMsigp); + delay(1); + for (i = 0; i < 4; i++) { + if (inb(MBMsigp) != 0xde) + break; + delay(1); + if (inb(MBMsigp) != v) + break; + delay(1); + } + if (i == 4) { + outb(MBMctlp, MBMcmd); + return 1; + } + } + return 0; +} + +static void +msbusmouse(int irq) +{ + if (mousetype) + error(Emouseset); + if (!msbusmousedetect()) + error(Enodev); + mousetype = Mousemsbus; + mouseport = irq >= 0 ? irq : VectorBUSMOUSE-VectorPIC; + intrenable(irq >= 0 ? irq+VectorPIC : VectorBUSMOUSE, msbusmouseintr, 0, BUSUNKNOWN); + outb(MBMdatap, MBMintron); +} + +static void +mousectl(char *buf) +{ + int nf, x; + char *field[10]; + nf = getfields(buf, field, 10, 1, " \t\n"); + if (nf < 1) + return; + if(strncmp(field[0], "serial", 6) == 0){ + switch(nf){ + /* the difference between these two cases is intriguing - wrtp */ + case 1: + serialmouse(atoi(field[0]+6), 0, 1); + break; + case 2: + serialmouse(atoi(field[1]), 0, 0); + break; + case 3: + default: + serialmouse(atoi(field[1]), field[2], 0); + break; + } + } else if(strcmp(field[0], "ps2") == 0){ + ps2mouse(); + } else if (strcmp(field[0], "ps2intellimouse") == 0) { + ps2mouse(); + setintellimouse(); + } else if (strncmp(field[0], "bus", 3) == 0 || strncmp(field[0], "msbus", 5) == 0) { + int irq, isms; + + isms = (field[0][0] == 'm'); + if (nf == 1) + irq = atoi(field[0] + (isms ? 5 : 3)); + else + irq = atoi(field[1]); + if (irq < 1) + irq = -1; + if (isms) + msbusmouse(irq); + else + busmouse(irq); + } else if(strcmp(field[0], "accelerated") == 0){ + switch(mousetype){ + case MousePS2: + x = splhi(); + i8042auxcmd(0xE7); + splx(x); + accelerated = 1; + break; + } + } else if(strcmp(field[0], "linear") == 0){ + switch(mousetype){ + case MousePS2: + x = splhi(); + i8042auxcmd(0xE6); + splx(x); + accelerated = 0; + break; + } + } else if(strcmp(field[0], "res") == 0){ + int n,m; + switch(nf){ + default: + n = 0x02; + m = 0x23; + break; + case 2: + n = atoi(field[1])&0x3; + m = 0x7; + break; + case 3: + n = atoi(field[1])&0x3; + m = atoi(field[2])&0x7; + break; + } + + switch(mousetype){ + case MousePS2: + x = splhi(); + i8042auxcmd(0xE8); + i8042auxcmd(n); + i8042auxcmd(0x5A); + i8042auxcmd(0x30|m); + i8042auxcmd(0x5A); + i8042auxcmd(0x20|(m>>1)); + splx(x); + break; + } + } else if(strcmp(field[0], "swap") == 0) + mouseswap ^= 1; +} + +Dev mousedevtab = { /* defaults in dev.c */ + 'm', + "mouse", + + devreset, /* devreset */ + devinit, /* devinit */ + mouseattach, + devdetach, + devclone, /* devclone */ + mousewalk, + mousestat, + mouseopen, + devcreate, /* devcreate */ + mouseclose, + mouseread, + devbread, /* devbread */ + mousewrite, + devbwrite, /* devbwrite */ + devremove, /* devremove */ + devwstat, /* devwstat */ +}; + diff --git a/os/pc/devmpeg.c b/os/pc/devmpeg.c new file mode 100644 index 00000000..038cfa6c --- /dev/null +++ b/os/pc/devmpeg.c @@ -0,0 +1,1063 @@ +/* + * Boffin MPEG decoder + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "zoran.h" +#include "crystal.h" +#include "io.h" + +enum +{ + + CPUACCCTRL = 0x20, /* Trident Window Chip control registers */ + CPUACCMD = 0x21, + BNKADR = 0x22, + SYSCONFIG = 0x23, + VGACOMP = 0x24, + VGAMASK = 0x25, + VIDCOMPL = 0x26, + VIDCOMPH = 0x27, + MOS = 0x28, + DISPCTRL = 0x29, + CAPCTRL = 0x2a, + OVLKT = 0x2b, + OVLWINHSTRT = 0x2c, + OVLWINVSTRT = 0x2d, + OVLWINHEND = 0x2e, + OVLWINVEND = 0x2f, + RESERVED1 = 0x30, + RESERVED2 = 0x31, + DISPWINVSTRT1 = 0x32, + DISPWINVSTRT2 = 0x33, + DISPWINVEND = 0x34, + DISPWINHSTRT1 = 0x35, + DISPWINHSTRT2 = 0x36, + DISPWINHEND = 0x37, + CAPWINVSTRT = 0x38, + CAPWINHSTRT = 0x39, + CAPWINVMF = 0x3a, + CAPWINHMF = 0x3b, + RESERVED3 = 0x3c, + CAPMASK = 0x3d, + BNKPOLATION = 0x3e, + SYNCPOL = 0x3f, + DISPVTOTAL = 0x40, + DISPHTOTAL = 0x41, + DISPVSTRT = 0x42, + DISPVEND = 0x43, + DISPHSTRT = 0x44, + DISPHEND = 0x45, + DISPSYNCW = 0x46, + DISPCRTCCTRL = 0x47, + CAPVTOTAL = 0x48, + CAPHTOTAL = 0x49, + CAPVSTRT = 0x4a, + CAPVEND = 0x4b, + CAPHSTRT = 0x4c, + CAPHEND = 0x4d, + CAPSYNCW = 0x4e, + CAPCRTCCTRL = 0x4f, + VIDLUTDACRW = 0x50, + VIDLUTDACRW0 = (VIDLUTDACRW), + VIDLUTDACRW1 = (VIDLUTDACRW+1), + VIDLUTDACRW2 = (VIDLUTDACRW+2), + VIDLUTDACRW3 = (VIDLUTDACRW+3), + VIDLUTDACRW4 = (VIDLUTDACRW+4), + VIDLUTDACRW5 = (VIDLUTDACRW+5), + VIDLUTDACRW6 = (VIDLUTDACRW+6), + VIDLUTDACRW7 = (VIDLUTDACRW+7), + VGALUTDACRW = 0x58, + VGALUTDACRW0 = (VGALUTDACRW), + VGALUTDACRW1 = (VGALUTDACRW+1), + VGALUTDACRW2 = (VGALUTDACRW+2), + VGALUTDACRW3 = (VGALUTDACRW+3), + VGALUTDACRW4 = (VGALUTDACRW+4), + VGALUTDACRW5 = (VGALUTDACRW+5), + VGALUTDACRW6 = (VGALUTDACRW+6), + VGALUTDACRW7 = (VGALUTDACRW+7), + HZOOMF = 0x60, + VZOOMF = 0x61, + DELAY1 = 0x62, + DELAY2 = 0x63, + + TRILO = 0, + TRIHI = 1, + TRIINDEX = 2, + + SCL = 0x02, + SDA = 0x01, + I2CR = 0x2B, + SAA7110 = 0x9c, + WRITE_C = 0x00, + I2DLY = 5, +}; + +enum +{ + ZR36100 = 0x1e0, + ZRIRQ = 15, + ZRDMA = 6, + + ZRIDREG = 4, /* offset */ + ZRMACH210 = 6, /* offset */ + ZRREG0 = 8, /* offset */ + ZRREG1 = 10, /* offset */ + ZRSR = ZRREG1, /* offset */ + ZRRDY = (1<<3), + ZRIDLE = (1<<2), + ZRREG2 = 12, /* offset */ + ZRREG3 = 14, /* offset */ + + SIFwidth = 320, + SIFheight = 240, + + IDPCOUNT = 3064, + PMDPCOUNT = 2048, + SVMDPCOUNT = 2048, + + HIWAT = 2*128*1024, + DMABLK = 16384, +}; + +static struct { + int zrport; + int irq; + int dma; + int trport; +} mpegconf; + +static char Evmode[] = "video format not supported"; +static char Eaudio[] = "invalid audio layer"; +static char Earate[] = "bad audio sample rate"; + +/* Status bits depend on board revision */ +static short STDBY; +static short VIDSEL; +static short VSNIRQn; +static short INTENAn; +static short DSPBOOT; +static short DSPRST; +static short MPGRST; +static int machsr; +static int dopen; +static int started; +static int stop; +static int pause; +static int sp2br; +static int sp2cd; +static char properties[] = "video mpeg1,sif\naudio musicam,I musicam,II\n"; +static void inittrident(void); +static int initzoran(void); +static void initcrystal(void); +static void mpegintr(Ureg*, void*); +static void setwindow(int, char**); +static void freebufs(void); +static int mkbuf(char*, int); + +typedef struct Buf Buf; +struct Buf +{ + int nchar; + uchar* ptr; + Buf* link; + uchar data[1]; +}; + +static struct +{ + Lock; + int qlen; + Buf* head; + Buf* tail; + Rendez flow; +} bqueue; + +static int +zrstatus(void) +{ + return ins(mpegconf.zrport+ZRSR) & 0xf; +} + +static int +zrwaitrdy(int timo, char *msg) +{ + int i; + + for(i = 0; i < timo; i++) + if(ins(mpegconf.zrport+ZRSR) & ZRRDY) + return 0; + + print("devmpeg: device not ready %s\n", msg); + return 1; +} + +static void +zrdma(Buf *b) +{ + int n; + + n = dmasetup(mpegconf.dma, b->ptr, b->nchar, 0); + b->ptr += n; + b->nchar -= n; + bqueue.qlen -= n; +} + +static void +triwr(int reg, int val) +{ + outb(mpegconf.trport+TRIINDEX, reg); + outb(mpegconf.trport+TRILO, val); + outb(mpegconf.trport+TRIHI, val>>8); +} + +static int +trird(int reg) +{ + int v; + + outb(mpegconf.trport+TRIINDEX, reg); + v = inb(mpegconf.trport+TRILO); + v |= inb(mpegconf.trport+TRIHI)<<8; + + return v; +} + +enum +{ + Qdir, + Qdata, + Qctl, +}; +static Dirtab mpegtab[]= +{ + "mpeg", {Qdata, 0}, 0, 0666, + "mpegctl", {Qctl, 0}, 0, 0666, +}; + +static void +mpegreset(void) +{ + ISAConf isa; + + mpegconf.zrport = ZR36100; + mpegconf.irq = ZRIRQ; + mpegconf.dma = ZRDMA; + + memset(&isa, 0, sizeof(isa)); + if(isaconfig("mpeg", 0, &isa) == 0) + return; + if(isa.port) + mpegconf.zrport = isa.port; + if(isa.irq) + mpegconf.irq = isa.irq; + if(isa.dma) + mpegconf.dma = isa.dma; + dmainit(mpegconf.dma, 64*1024); + print("mpeg0: port 0x%uX, irq %d, dma %d\n", + mpegconf.zrport, mpegconf.irq, mpegconf.dma); + mpegconf.trport = mpegconf.zrport+0x100; + intrenable(VectorPIC+mpegconf.irq, mpegintr, 0, BUSUNKNOWN); +} + +static void +mpeginit(void) +{ + if(mpegconf.trport == 0) + return; + + inittrident(); + setwindow(0, 0); +} + +static Chan* +mpegattach(char *spec) +{ + if(mpegconf.trport == 0) + error(Enodev); + + return devattach('E', spec); +} + +static int +mpegwalk(Chan *c, char *name) +{ + return devwalk(c, name, mpegtab, nelem(mpegtab), devgen); +} + +static void +mpegstat(Chan *c, char *db) +{ + devstat(c, db, mpegtab, nelem(mpegtab), devgen); +} + +static Chan* +mpegopen(Chan *c, int omode) +{ + switch(c->qid.path) { + default: + break; + case Qdata: + if(dopen) + error(Einuse); + dopen = 1; + break; + } + return devopen(c, omode, mpegtab, nelem(mpegtab), devgen); +} + +static void +mpegclose(Chan *c) +{ + int i; + + switch(c->qid.path) { + default: + break; + case Qdata: + if((c->flag & COPEN) == 0) + break; + if(started) { + for(i = 0; i < 50; i++) { + if(ins(mpegconf.zrport+ZRSR) & ZRIDLE) + break; + tsleep(&up->sleep, return0, 0, 100); + } + } + if(stop != 0) + outs(mpegconf.zrport+ZRREG1, 0x1000); + microdelay(15); + outs(mpegconf.zrport+ZRREG1, 0x8000); + freebufs(); + dopen = 0; + } +} + +static long +mpegread(Chan *c, void *a, long n, ulong off) +{ + switch(c->qid.path & ~CHDIR){ + default: + error(Eperm); + case Qdir: + return devdirread(c, a, n, mpegtab, nelem(mpegtab), devgen); + case Qctl: + return readstr(off, a, n, properties); + } + return 0; +} + +#define SCALE(a, b) ((((a)<<10)/(b))-1024) +enum +{ + CWINVF = 0x3ff, + CWINHF = 0x1da, +}; + +static void +setwindow(int nf, char **field) +{ + int minx, miny, maxx, maxy, width, height; + + if(field == 0) { + minx = 0; + miny = 0; + maxx = 0; + maxy = 0; + } + else { + if(nf != 5) + error(Ebadarg); + + minx = strtoul(field[1], 0, 0); + miny = strtoul(field[2], 0, 0); + maxx = strtoul(field[3], 0, 0) + 8; + maxy = strtoul(field[4], 0, 0); + } + + triwr(OVLWINHSTRT, minx); + triwr(OVLWINVSTRT, miny); + triwr(OVLWINHEND, maxx+12); + triwr(OVLWINVEND, maxy); + + width = maxx - minx; + height = maxy - miny; + if(width >= SIFwidth) { + triwr(HZOOMF, SCALE(width, SIFwidth)); + triwr(CAPWINHMF, CWINHF); + } + else { + triwr(HZOOMF, SCALE(SIFwidth, SIFwidth)); + triwr(CAPWINHMF, width*CWINHF/SIFwidth); + } + if(height >= SIFheight) { + triwr(VZOOMF, SCALE(height, SIFheight)); + triwr(CAPWINVMF, CWINVF); + } + else { + triwr(VZOOMF, SCALE(SIFheight, SIFheight)); + triwr(CAPWINVMF, height*CWINVF/SIFheight); + } +} + +static int +mpegflow(void*) +{ + return bqueue.qlen < HIWAT || stop; +} + +static int +mkbuf(char *d, int n) +{ + Buf *b; + + b = malloc(sizeof(Buf)+n); + if(b == 0) + return 0; + + memmove(b->data, d, n); + b->ptr = b->data; + b->nchar = n; + b->link = 0; + + ilock(&bqueue); + bqueue.qlen += n; + if(bqueue.head) + bqueue.tail->link = b; + else + bqueue.head = b; + bqueue.tail = b; + iunlock(&bqueue); + + return 1; +} + +static void +freebufs(void) +{ + Buf *next; + + ilock(&bqueue); + bqueue.qlen = 0; + while(bqueue.head) { + next = bqueue.head->link; + free(bqueue.head); + bqueue.head = next; + } + iunlock(&bqueue); +} + +typedef struct Audio Audio; +struct Audio { + int rate; + int cd; + int br; +}; + +static Audio AudioclkI[] = +{ + 64000, 0x000000bb, 0x00071797, + 96000, 0x0000007d, 0x00071c71, + 128000, 0x0000005d, 0x00070de1, + 160000, 0x0000004b, 0x00071c71, + 192000, 0x0000003e, 0x00070de1, + 224000, 0x00000035, 0x00070906, + 256000, 0x0000002e, 0x0006fa76, + 288000, 0x00000029, 0x0006ff51, + 320000, 0x00000025, 0x0007042b, + 352000, 0x00000022, 0x00071797, + 384000, 0x0000001f, 0x00070de1, + 416000, 0x0000001c, 0x0006e70b, + 448000, 0x0000001a, 0x0006e70b, +}; + +static Audio AudioclkII[] = +{ + 48000, 0x000000fa, 0x00071c71, + 56000, 0x000000d6, 0x00071a04, + 64000, 0x000000bb, 0x00071797, + 80000, 0x00000096, 0x00071c71, + 96000, 0x0000007d, 0x00071c71, + 112000, 0x0000006b, 0x00071a04, + 128000, 0x0000005d, 0x00070de1, + 160000, 0x0000004b, 0x00071c71, + 192000, 0x0000003e, 0x00070de1, + 224000, 0x00000035, 0x00070906, + 256000, 0x0000002e, 0x0006fa76, + 320000, 0x00000025, 0x0007042b, + 384000, 0x0000001f, 0x00070de1, +}; + +static long +mpegwrite(Chan *c, char *a, long n, vlong) +{ + Audio *t; + int i, nf, l, x; + char buf[128], *field[10]; + + switch(c->qid.path & ~CHDIR) { + case Qctl: + if(n > sizeof(buf)-1) + n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = '\0'; + + nf = getfields(buf, field, nelem(field), 1, " \t\n"); + if(nf < 1) + error(Ebadarg); + + if(strcmp(field[0], "stop") == 0) { + if(started == 0) + error("not started"); + if(pause) { + pause = 0; + outs(mpegconf.zrport+ZRREG1, 0x9000); + } + stop = 1; + outs(mpegconf.zrport+ZRREG1, 0x1000); + microdelay(15); + outs(mpegconf.zrport+ZRREG1, 0x8000); + wakeup(&bqueue.flow); + return n; + } + if(strcmp(field[0], "pause") == 0) { + if(started == 0) + error("not started"); + if(pause == 0) { + pause = 1; + outs(mpegconf.zrport+ZRREG1, 0x1000); + } + else { + pause = 0; + outs(mpegconf.zrport+ZRREG1, 0x9000); + } + return n; + } + if(strcmp(field[0], "window") == 0) { + setwindow(nf, field); + return n; + } + if(strcmp(field[0], "audio") == 0) { + if(nf < 3) + error(Ebadarg); + t = 0; + if(strcmp(field[1], "musicam,I") == 0) + t = AudioclkI; + else + if(strcmp(field[1], "musicam,II") == 0) + t = AudioclkII; + else + error(Eaudio); + x = strtoul(field[2], 0, 0); + for(i = 0; t[i].rate != 0; i++) { + if(t[i].rate == x) { + sp2cd = t[i].cd; + sp2br = t[i].br; + return n; + } + } + error(Earate); + } + if(strcmp(field[0], "video") == 0) { + if(nf != 3) + error(Ebadarg); + if(strcmp(field[1], "iso11172") != 0) + error(Evmode); + if(strcmp(field[2], "mpeg1,sif") != 0) + error(Evmode); + return n; + } + if(strcmp(field[0], "init") == 0) { + inittrident(); + for(i = 0; i < 3; i++) + if(initzoran() != -1) + break; + initcrystal(); + started = 0; + stop = 0; + pause = 0; + return n; + } + error(Ebadarg); + case Qdata: + if(n & 1) + error("odd write"); + + while(!mpegflow(0)) + sleep(&bqueue.flow, mpegflow, 0); + + if(stop) + error("stopped"); + + x = n; + while(x) { + l = x; + if(l > DMABLK) + l = DMABLK; + if(mkbuf(a, l) == 0) + error(Enomem); + x -= l; + a += l; + } + if(started || bqueue.qlen < (HIWAT*3)/4) + break; + + zrdma(bqueue.head); + outs(mpegconf.zrport+ZRREG1, 0x0000); + outs(mpegconf.zrport+ZRREG1, 0x0000); + started = 1; + break; + default: + error(Ebadusefd); + } + return n; +} + +Dev mpegdevtab = { + 'E', + "mpeg", + + mpegreset, + mpeginit, + mpegattach, + devdetach, + devclone, + mpegwalk, + mpegstat, + mpegopen, + devcreate, + mpegclose, + mpegread, + devbread, + mpegwrite, + devbwrite, + devremove, + devwstat, +}; + +static void +initctl(void) +{ + int boardid; + static int done; + + if(done) + return; + + boardid = ins(mpegconf.zrport+ZRIDREG); + if(boardid == 0xE3E3) { /* REV c/d */ + STDBY = 0x0000; + VIDSEL = 0x2020; + VSNIRQn = 0x1010; + INTENAn = 0x0808; + DSPBOOT = 0x0404; + DSPRST = 0x0202; + MPGRST = 0x0101; + } + else { /* REV b */ + STDBY = 0x0404; + VIDSEL = 0x1010; + VSNIRQn = 0x8080; + INTENAn = 0x4040; + DSPBOOT = 0x0202; + DSPRST = 0x0101; + MPGRST = 0x2020; + } + done = 1; + +} + +/* + * nbl (reg 0x1[ab]) was 0x0022, nblf (reg 1[cd]) was 0x0006 + */ +static uchar +zrparam[] = +{ +/* 00 */ 0xEF, 0x01, 0x01, 0x01, 0x80, 0x0E, 0x31, 0x00, +/* 08 */ 0x01, 0x60, 0x00, 0x00, 0x03, 0x5A, 0x00, 0x7A, +/* 10 */ 0x00, 0x10, 0x00, 0x08, 0x00, 0xF0, 0x00, 0x00, +/* 18 */ 0x02, 0x0D, 0x00, 0x1e, 0x00, 0x0a, 0x00, 0x02, +/* 20 */ 0x40, 0x06, 0x80, 0x00, 0x80, 0x00, 0x05, 0x9B, +/* 28 */ 0x07, 0x16, 0xFD, 0x25, 0xFE, 0xA0, 0x00, 0x00, +/* 30 */ 0x00, 0x07, 0x0d, 0xe1, 0x00, 0x00, 0x00, 0x3E, +/* 38 */ 0x00, 0x00, 0x09, 0x51, 0x00, 0x00, 0xCD, 0xFE, +/* 40 */ 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 50 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 58 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 60 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 68 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 70 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 78 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static int +initzoran(void) +{ + int i, nbytes, zrs; + + initctl(); + freebufs(); + + machsr = DSPRST|VSNIRQn; + outs(mpegconf.zrport+ZRMACH210, machsr); + microdelay(4000); + + machsr |= STDBY; + outs(mpegconf.zrport+ZRMACH210, machsr); + microdelay(4000); + + machsr |= MPGRST; + outs(mpegconf.zrport+ZRMACH210, machsr); + microdelay(4000); + machsr &= ~MPGRST; + outs(mpegconf.zrport+ZRMACH210, machsr); + microdelay(4000); + machsr |= MPGRST; + outs(mpegconf.zrport+ZRMACH210, machsr); + microdelay(4000); + + if(zrwaitrdy(2000, "load IDP")) + return -1; + + for(i = 0; i < IDPCOUNT; i++) + outb(mpegconf.zrport+ZRREG2, zrmpeg1[i]); + + if(((zrs = zrstatus()) & 3) != 3) { +/* print("devmpeg: error loading IDP sr=%2.2ux\n", zrs); */ + USED(zrs); + return -1; + } + + if(zrwaitrdy(2000, "load PMDP")) + return -1; + + for(i = 0; i < PMDPCOUNT; i++) + outb(mpegconf.zrport+ZRREG3, zrmpeg2[i]); + + if(((zrs = zrstatus()) & 3) != 3) { +/* print("devmpeg: error loading PMDP sr=%2.2ux\n", zrs); */ + USED(zrs); + return -1; + } + + zrparam[0x36] = sp2cd>>8; + zrparam[0x37] = sp2cd>>0; + zrparam[0x31] = sp2br>>16; + zrparam[0x32] = sp2br>>8; + zrparam[0x33] = sp2br>>0; + + nbytes = 16; + for(i = 0; i < 128; i++) { + if(nbytes >= 16) { + if(zrwaitrdy(2000, "load parameters")) + return -1; + nbytes = 0; + } + outb(mpegconf.zrport+ZRREG0, zrparam[i]); + nbytes++; + } + + if(zrwaitrdy(2000, "load SVMDP")) + return -1; + + for(i = 0; i < SVMDPCOUNT; i++) + outb(mpegconf.zrport+ZRREG3, zrmpeg3s[i]); + + if(((zrs = zrstatus()) & 3) != 3) { +/* print("devmpeg: error loading SVMDP sr=%2.2ux\n", zrs); */ + USED(zrs); + return -1; + } + return 0; +} + +static struct +{ + short reg; + ushort val; +} trireg[] = +{ + 0x20, 0x0400, + 0x21, 0x00e9, + 0x22, 0x0000, + 0x23, 0x07ee, + 0x24, 0x0005, + 0x25, 0xff00, + 0x26, 0x0000, + 0x27, 0x7fff, + 0x28, 0x0004, + 0x29, 0x88a0, + 0x2a, 0x0011, + 0x2b, 0x8540, + 0x2c, 0x00c4, + 0x2d, 0x00ac, + 0x2e, 0x020f, + 0x2f, 0x019d, + 0x30, 0x00bd, + 0x31, 0x00ff, + 0x32, 0x0000, + 0x33, 0x0000, + 0x34, 0x03ff, + 0x35, 0x0000, + 0x36, 0x0000, + 0x37, 0x03ff, + 0x38, 0x0000, + 0x39, 0x0000, + 0x3a, 0x03ff, + 0x3b, 0x01da, + 0x3c, 0xe8ce, + 0x3d, 0x2ac0, + 0x3e, 0x891f, + 0x3f, 0x3e25, + 0x40, 0x03ff, + 0x41, 0x01ff, + 0x42, 0x001f, + 0x43, 0x01ff, + 0x44, 0x003b, + 0x45, 0x0186, + 0x46, 0x1d06, + 0x47, 0x1a4f, + 0x48, 0x020d, + 0x49, 0x01ad, + 0x4a, 0x001b, + 0x4b, 0x01fd, + 0x4c, 0x003a, + 0x4d, 0x034b, + 0x4e, 0x2006, + 0x4f, 0x0083, + 0x50, 0xef08, + 0x51, 0xef3a, + 0x52, 0xefff, + 0x53, 0xef08, + 0x54, 0xef08, + 0x55, 0xef15, + 0x56, 0xefc0, + 0x57, 0xef08, + 0x58, 0xefef, + 0x59, 0xefef, + 0x5a, 0xefef, + 0x5b, 0xefef, + 0x5c, 0xefef, + 0x5d, 0xefef, + 0x5e, 0xefef, + 0x5f, 0xefef, + 0x60, 0x0000, + 0x61, 0x0004, + 0x62, 0x0020, + 0x63, 0x8080, + 0x64, 0x0300, + -1 +}; + +static void +clrI2C(uchar b) +{ + uchar t; + + outb(mpegconf.trport+TRIINDEX, I2CR); + t = inb(mpegconf.trport+TRIHI); + t &= ~b; + outb(mpegconf.trport+TRIHI, t); +} + +static void +setI2C(uchar b) +{ + uchar t; + + outb(mpegconf.trport+TRIINDEX, I2CR); + t = inb(mpegconf.trport+TRIHI); + t |= b; + outb(mpegconf.trport+TRIHI, t); +} + +static void +startI2C(void) +{ + setI2C(SDA); + setI2C(SCL); + microdelay(I2DLY); + clrI2C(SDA); + microdelay(I2DLY); + clrI2C(SCL); + microdelay(I2DLY); +} + +static void +endI2C(void) +{ + clrI2C(SDA); + clrI2C(SCL); + microdelay(I2DLY); + setI2C(SCL); + microdelay(I2DLY); + setI2C(SDA); + microdelay(I2DLY); +} + +static void +wrI2Cbit(uchar b) +{ + clrI2C(SDA); + clrI2C(SCL); + microdelay(I2DLY); + if(b & 1) { + setI2C(SDA); + microdelay(I2DLY); + setI2C(SCL); + microdelay(I2DLY); + clrI2C(SCL); + microdelay(I2DLY); + clrI2C(SDA); + microdelay(I2DLY); + } + else { + setI2C(SCL); + microdelay(I2DLY); + clrI2C(SCL); + microdelay(I2DLY); + } +} + +static void +wrI2CB(unsigned char data) +{ + int i; + + for(i = 0; i < 8; i++) + wrI2Cbit(data >>(7-i)); +} + +static int +rdI2CBit(void) +{ + int bit = 1; + + setI2C(SDA); + clrI2C(SCL); + setI2C(SCL); + outb(mpegconf.trport+TRIINDEX, I2CR); + if(inb(mpegconf.trport+TRIHI) & SDA) + bit = 0; + clrI2C(SDA); + clrI2C(SCL); + + return bit; +} + +static int +wrI2CD(uchar data) +{ + int r; + ulong s; + + s = splhi(); + wrI2CB(data); + r = rdI2CBit(); + splx(s); + return r; +} + +static uchar +setupSAA7110[] = +{ + /* Digital */ + 0x4c, 0x3c, 0x0d, 0xef, 0xbd, 0xf0, 0x40, 0x03, + 0xf8, 0xf8, 0x90, 0x90, 0x00, 0x02, 0x10, 0x77, + 0x00, 0x2c, 0x40, 0x40, 0x3b, 0x10, 0xfc, 0xd2, + 0xf0, 0x80, + + /* Analog */ + 0xd9, 0x16, 0x40, 0x40, 0x80, 0x40, 0x80, 0x4f, + 0xfe, 0x01, 0xcf, 0x0f, 0x03, 0x01, 0x81, 0x0a, + 0x40, 0x35, 0x02, 0x8c, 0x03 +}; + +static void +addrI2CB(int addr, int val) +{ + ulong s; + + s = splhi(); + startI2C(); + wrI2CD(SAA7110|WRITE_C); + wrI2CD(addr); + wrI2CD(val); + endI2C(); + splx(s); +} + +static void +inittrident(void) +{ + int i; + + for(i = 0; trireg[i].reg != -1; i++) + triwr(trireg[i].reg, trireg[i].val); + + for(i = 0; i < 47; i++) + addrI2CB(i, setupSAA7110[i]); +} + +static void +initcrystal(void) +{ + int i; + static int done; + + if(done) + return; + + done = 1; + + initctl(); + + /* Reboot the Musicam decoder */ + clrI2C(SCL); + clrI2C(SDA); + machsr |= DSPRST; + outs(mpegconf.zrport+ZRMACH210, machsr); + machsr |= DSPBOOT; + outs(mpegconf.zrport+ZRMACH210, machsr); + machsr &= ~DSPRST; + outs(mpegconf.zrport+ZRMACH210, machsr); + machsr |= DSPRST; + outs(mpegconf.zrport+ZRMACH210, machsr); + machsr &= ~DSPBOOT; + outs(mpegconf.zrport+ZRMACH210, machsr); + + startI2C(); + wrI2CD(0); + for(i = 0; i < sizeof(crystal); i++ ) + wrI2CD(crystal[i]); + endI2C(); +} + +static void +mpegintr(Ureg*, void*) +{ + Buf *b; + + b = bqueue.head; + if(b == 0 || dmadone(mpegconf.dma) == 0) + return; + + dmaend(mpegconf.dma); + if(b->nchar == 0) { + bqueue.head = b->link; + free(b); + + b = bqueue.head; + if(b == 0) { + started = 0; + return; + } + } + zrdma(b); + wakeup(&bqueue.flow); +} diff --git a/os/pc/devpccard.c b/os/pc/devpccard.c new file mode 100644 index 00000000..3f6a09ca --- /dev/null +++ b/os/pc/devpccard.c @@ -0,0 +1,1949 @@ +/* + cardbus and pcmcia (grmph) support. +*/ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +#define MAP(x,o) (Rmap + (x)*0x8 + o) + +enum { + TI_vid = 0x104c, + TI_1131_did = 0xAC15, + TI_1250_did = 0xAC16, + TI_1450_did = 0xAC1B, + TI_1251A_did = 0xAC1D, + TI_1420_did = 0xAC51, + + Ricoh_vid = 0x1180, + Ricoh_475_did = 0x0475, + Ricoh_476_did = 0x0476, + Ricoh_478_did = 0x0478, + + Nslots = 4, /* Maximum number of CardBus slots to use */ + + K = 1024, + M = K * K, + + LegacyAddr = 0x3e0, + NUMEVENTS = 10, + + TI1131xSC = 0x80, // system control + TI122X_SC_INTRTIE = 1 << 29, + TI12xxIM = 0x8c, // + TI1131xCC = 0x91, // card control + TI113X_CC_RIENB = 1 << 7, + TI113X_CC_ZVENABLE = 1 << 6, + TI113X_CC_PCI_IRQ_ENA = 1 << 5, + TI113X_CC_PCI_IREQ = 1 << 4, + TI113X_CC_PCI_CSC = 1 << 3, + TI113X_CC_SPKROUTEN = 1 << 1, + TI113X_CC_IFG = 1 << 0, + TI1131xDC = 0x92, // device control +}; + +typedef struct Variant Variant; +struct Variant { + ushort vid; + ushort did; + char *name; +}; + +static Variant variant[] = { +{ Ricoh_vid, Ricoh_475_did, "Ricoh 475 PCI/Cardbus bridge", }, +{ Ricoh_vid, Ricoh_476_did, "Ricoh 476 PCI/Cardbus bridge", }, +{ Ricoh_vid, Ricoh_478_did, "Ricoh 478 PCI/Cardbus bridge", }, +{ TI_vid, TI_1131_did, "TI PCI-1131 Cardbus Controller", }, +{ TI_vid, TI_1250_did, "TI PCI-1250 Cardbus Controller", }, +{ TI_vid, TI_1450_did, "TI PCI-1450 Cardbus Controller", }, +{ TI_vid, TI_1251A_did, "TI PCI-1251A Cardbus Controller", }, +{ TI_vid, TI_1420_did, "TI PCI-1420 Cardbus Controller", }, +}; + +/* Cardbus registers */ +enum { + SocketEvent = 0, + SE_CCD = 3 << 1, + SE_POWER = 1 << 3, + SocketMask = 1, + SocketState = 2, + SS_CCD = 3 << 1, + SS_POWER = 1 << 3, + SS_PC16 = 1 << 4, + SS_CBC = 1 << 5, + SS_NOTCARD = 1 << 7, + SS_BADVCC = 1 << 9, + SS_5V = 1 << 10, + SS_3V = 1 << 11, + SocketForce = 3, + SocketControl = 4, + SC_5V = 0x22, + SC_3V = 0x33, +}; + +enum { + PciPCR_IO = 1 << 0, + PciPCR_MEM = 1 << 1, + PciPCR_Master = 1 << 2, + + PciPMC = 0xa4, + + Nbars = 6, + Ncmd = 10, + CBIRQ = 9, + + PC16, + PC32, +}; + +enum { + Ti82365, + Tpd6710, + Tpd6720, + Tvg46x, +}; + +static char *chipname[] = { +[Ti82365] "Intel 82365SL", +[Tpd6710] "Cirrus Logic PD6710", +[Tpd6720] "Cirrus Logic PD6720", +[Tvg46x] "Vadem VG-46x", +}; + +/* + * Intel 82365SL PCIC controller for the PCMCIA or + * Cirrus Logic PD6710/PD6720 which is mostly register compatible + */ +enum +{ + /* + * registers indices + */ + Rid= 0x0, /* identification and revision */ + Ris= 0x1, /* interface status */ + Rpc= 0x2, /* power control */ + Foutena= (1<<7), /* output enable */ + Fautopower= (1<<5), /* automatic power switching */ + Fcardena= (1<<4), /* PC card enable */ + Rigc= 0x3, /* interrupt and general control */ + Fiocard= (1<<5), /* I/O card (vs memory) */ + Fnotreset= (1<<6), /* reset if not set */ + FSMIena= (1<<4), /* enable change interrupt on SMI */ + Rcsc= 0x4, /* card status change */ + Rcscic= 0x5, /* card status change interrupt config */ + Fchangeena= (1<<3), /* card changed */ + Fbwarnena= (1<<1), /* card battery warning */ + Fbdeadena= (1<<0), /* card battery dead */ + Rwe= 0x6, /* address window enable */ + Fmem16= (1<<5), /* use A23-A12 to decode address */ + Rio= 0x7, /* I/O control */ + Fwidth16= (1<<0), /* 16 bit data width */ + Fiocs16= (1<<1), /* IOCS16 determines data width */ + Fzerows= (1<<2), /* zero wait state */ + Ftiming= (1<<3), /* timing register to use */ + Riobtm0lo= 0x8, /* I/O address 0 start low byte */ + Riobtm0hi= 0x9, /* I/O address 0 start high byte */ + Riotop0lo= 0xa, /* I/O address 0 stop low byte */ + Riotop0hi= 0xb, /* I/O address 0 stop high byte */ + Riobtm1lo= 0xc, /* I/O address 1 start low byte */ + Riobtm1hi= 0xd, /* I/O address 1 start high byte */ + Riotop1lo= 0xe, /* I/O address 1 stop low byte */ + Riotop1hi= 0xf, /* I/O address 1 stop high byte */ + Rmap= 0x10, /* map 0 */ + + /* + * CL-PD67xx extension registers + */ + Rmisc1= 0x16, /* misc control 1 */ + F5Vdetect= (1<<0), + Fvcc3V= (1<<1), + Fpmint= (1<<2), + Fpsirq= (1<<3), + Fspeaker= (1<<4), + Finpack= (1<<7), + Rfifo= 0x17, /* fifo control */ + Fflush= (1<<7), /* flush fifo */ + Rmisc2= 0x1E, /* misc control 2 */ + Flowpow= (1<<1), /* low power mode */ + Rchipinfo= 0x1F, /* chip information */ + Ratactl= 0x26, /* ATA control */ + + /* + * offsets into the system memory address maps + */ + Mbtmlo= 0x0, /* System mem addr mapping start low byte */ + Mbtmhi= 0x1, /* System mem addr mapping start high byte */ + F16bit= (1<<7), /* 16-bit wide data path */ + Mtoplo= 0x2, /* System mem addr mapping stop low byte */ + Mtophi= 0x3, /* System mem addr mapping stop high byte */ + Ftimer1= (1<<6), /* timer set 1 */ + Mofflo= 0x4, /* Card memory offset address low byte */ + Moffhi= 0x5, /* Card memory offset address high byte */ + Fregactive= (1<<6), /* attribute memory */ + + /* + * configuration registers - they start at an offset in attribute + * memory found in the CIS. + */ + Rconfig= 0, + Creset= (1<<7), /* reset device */ + Clevel= (1<<6), /* level sensitive interrupt line */ +}; + +/* + * read and crack the card information structure enough to set + * important parameters like power + */ +/* cis memory walking */ +typedef struct Cisdat Cisdat; +struct Cisdat { + uchar *cisbase; + int cispos; + int cisskip; + int cislen; +}; + +typedef struct Pcminfo Pcminfo; +struct Pcminfo { + char verstr[512]; /* Version string */ + PCMmap mmap[4]; /* maps, last is always for the kernel */ + ulong conf_addr; /* Config address */ + uchar conf_present; /* Config register present */ + int nctab; /* In use configuration tables */ + PCMconftab ctab[8]; /* Configuration tables */ + PCMconftab *defctab; /* Default conftab */ + + int port; /* Actual port usage */ + int irq; /* Actual IRQ usage */ +}; + +typedef struct Cardbus Cardbus; +struct Cardbus { + Lock; + Variant *variant; /* Which CardBus chipset */ + Pcidev *pci; /* The bridge itself */ + ulong *regs; /* Cardbus registers */ + int ltype; /* Legacy type */ + int lindex; /* Legacy port index address */ + int ldata; /* Legacy port data address */ + int lbase; /* Base register for this socket */ + + int state; /* Current state of card */ + int type; /* Type of card */ + Pcminfo linfo; /* PCMCIA slot info */ + + int special; /* card is allocated to a driver */ + + int refs; /* Number of refs to slot */ + Lock refslock; /* inc/dev ref lock */ +}; + +static int managerstarted; + +enum { + Mshift= 12, + Mgran= (1<state], messages[message]); + switch (cb->state) { + case SlotEmpty: + + switch (message) { + case CardDetected: + cb->state = SlotFull; + powerup(cb); + break; + case CardEjected: + break; + default: + //print("#Y%d: Invalid message %s in SlotEmpty state\n", + // (int)(cb - cbslots), messages[message]); + break; + } + break; + + case SlotFull: + + switch (message) { + case CardPowered: + cb->state = SlotPowered; + configure(cb); + break; + case CardEjected: + cb->state = SlotEmpty; + powerdown(cb); + break; + default: + //print("#Y%d: Invalid message %s in SlotFull state\n", + // (int)(cb - cbslots), messages[message]); + break; + } + break; + + case SlotPowered: + + switch (message) { + case CardConfigured: + cb->state = SlotConfigured; + break; + case CardEjected: + cb->state = SlotEmpty; + unconfigure(cb); + powerdown(cb); + break; + default: + //print("#Y%d: Invalid message %s in SlotPowered state\n", + // (int)(cb - cbslots), messages[message]); + break; + } + break; + + case SlotConfigured: + + switch (message) { + case CardEjected: + cb->state = SlotEmpty; + unconfigure(cb); + powerdown(cb); + break; + default: + //print("#Y%d: Invalid message %s in SlotConfigured state\n", + // (int)(cb - cbslots), messages[message]); + break; + } + break; + } +} + +static void +qengine(Cardbus *cb, int message) +{ + lock(cb); + engine(cb, message); + unlock(cb); +} + +typedef struct Events Events; +struct Events { + Cardbus *cb; + int message; +}; + +static Lock levents; +static Events events[NUMEVENTS]; +static Rendez revents; +static int nevents; + +static void +iengine(Cardbus *cb, int message) +{ + if (nevents >= NUMEVENTS) { + print("#Y: Too many events queued, discarding request\n"); + return; + } + ilock(&levents); + events[nevents].cb = cb; + events[nevents].message = message; + nevents++; + iunlock(&levents); + wakeup(&revents); +} + +static int +eventoccured(void) +{ + return nevents > 0; +} + +static void +processevents(void *) +{ + while (1) { + int message; + Cardbus *cb; + + sleep(&revents, (int (*)(void *))eventoccured, nil); + + cb = nil; + message = 0; + ilock(&levents); + if (nevents > 0) { + cb = events[0].cb; + message = events[0].message; + nevents--; + if (nevents > 0) + memmove(events, &events[1], nevents * sizeof(Events)); + } + iunlock(&levents); + + if (cb) + qengine(cb, message); + } +} + +static void +cbinterrupt(Ureg *, void *) +{ + int i; + + for (i = 0; i != nslots; i++) { + Cardbus *cb = &cbslots[i]; + ulong event, state; + + event= cb->regs[SocketEvent]; + state = cb->regs[SocketState]; + rdreg(cb, Rcsc); /* Ack the interrupt */ + + //print("interrupt: slot %d, event %.8lX, state %.8lX, (%s)\n", + // (int)(cb - cbslots), event, state, states[cb->state]); + + if (event & SE_CCD) { + cb->regs[SocketEvent] |= SE_CCD; /* Ack interrupt */ + if (state & SE_CCD) { + if (cb->state != SlotEmpty) { + print("#Y: take cardejected interrupt\n"); + iengine(cb, CardEjected); + } + } + else + iengine(cb, CardDetected); + } + + if (event & SE_POWER) { + cb->regs[SocketEvent] |= SE_POWER; /* Ack interrupt */ + iengine(cb, CardPowered); + } + } +} + +void +devpccardlink(void) +{ + static int initialized; + Pcidev *pci; + int i; + uchar intl; + char *p; + + if (initialized) + return; + initialized = 1; + + if((p=getconf("pccard0")) && strncmp(p, "disabled", 8)==0) + return; + + if(_pcmspecial) + return; + + /* Allocate legacy space */ + if (ioalloc(LegacyAddr, 2, 0, "i82365.0") < 0) + print("#Y: WARNING: Cannot allocate legacy ports\n"); + + /* Find all CardBus controllers */ + pci = nil; + intl = (uchar)-1; + while ((pci = pcimatch(pci, 0, 0)) != nil) { + ulong baddr; + Cardbus *cb; + int slot; + uchar pin; + + for (i = 0; i != nelem(variant); i++) + if (pci->vid == variant[i].vid && pci->did == variant[i].did) + break; + if (i == nelem(variant)) + continue; + + /* initialize this slot */ + slot = nslots++; + cb = &cbslots[slot]; + + cb->pci = pci; + cb->variant = &variant[i]; + + if (pci->vid != TI_vid) { + // Gross hack, needs a fix. Inherit the mappings from 9load + // for the TIs (pb) + pcicfgw32(pci, PciCBMBR0, 0xffffffff); + pcicfgw32(pci, PciCBMLR0, 0); + pcicfgw32(pci, PciCBMBR1, 0xffffffff); + pcicfgw32(pci, PciCBMLR1, 0); + pcicfgw32(pci, PciCBIBR0, 0xffffffff); + pcicfgw32(pci, PciCBILR0, 0); + pcicfgw32(pci, PciCBIBR1, 0xffffffff); + pcicfgw32(pci, PciCBILR1, 0); + } + + // Set up PCI bus numbers if needed. + if (pcicfgr8(pci, PciSBN) == 0) { + static int busbase = 0x20; + + pcicfgw8(pci, PciSBN, busbase); + pcicfgw8(pci, PciUBN, busbase + 2); + busbase += 3; + } + + // Patch up intl if needed. + if ((pin = pcicfgr8(pci, PciINTP)) != 0 && + (pci->intl == 0xff || pci->intl == 0)) { + pci->intl = pciipin(nil, pin); + pcicfgw8(pci, PciINTL, pci->intl); + + if (pci->intl == 0xff || pci->intl == 0) + print("#Y%d: No interrupt?\n", (int)(cb - cbslots)); + } + + // Don't you love standards! + if (pci->vid == TI_vid) { + if (pci->did <= TI_1131_did) { + uchar cc; + + cc = pcicfgr8(pci, TI1131xCC); + cc &= ~(TI113X_CC_PCI_IRQ_ENA | + TI113X_CC_PCI_IREQ | + TI113X_CC_PCI_CSC | + TI113X_CC_ZVENABLE); + cc |= TI113X_CC_PCI_IRQ_ENA | + TI113X_CC_PCI_IREQ | + TI113X_CC_SPKROUTEN; + pcicfgw8(pci, TI1131xCC, cc); + + // PCI interrupts only + pcicfgw8(pci, TI1131xDC, + pcicfgr8(pci, TI1131xDC) & ~6); + + // CSC ints to PCI bus. + wrreg(cb, Rigc, rdreg(cb, Rigc) | 0x10); + } + else if (pci->did == TI_1250_did) { + print("No support yet for the TI_1250_did, prod pb\n"); + } + else if (pci->did == TI_1420_did) { + // Disable Vcc protection + pcicfgw32(cb->pci, 0x80, + pcicfgr32(cb->pci, 0x80) | (1 << 21)); + } + + pcicfgw16(cb->pci, PciPMC, pcicfgr16(cb->pci, PciPMC) & ~3); + } + + if (intl != -1 && intl != pci->intl) + intrenable(pci->intl, cbinterrupt, cb, pci->tbdf, "cardbus"); + intl = pci->intl; + + if ((baddr = pcicfgr32(cb->pci, PciBAR0)) == 0) { + int align = (pci->did == Ricoh_478_did)? 0x10000: 0x1000; + + baddr = upamalloc(baddr, align, align); + pcicfgw32(cb->pci, PciBAR0, baddr); + cb->regs = (ulong *)KADDR(baddr); + } + else + cb->regs = (ulong *)KADDR(upamalloc(baddr, 4096, 0)); + cb->state = SlotEmpty; + + /* Don't really know what to do with this... */ + i82365probe(cb, LegacyAddr, LegacyAddr + 1); + + print("#Y%ld: %s, %.8ulX intl %d\n", cb - cbslots, + variant[i].name, baddr, pci->intl); + } + + if (nslots == 0){ + iofree(LegacyAddr); + return; + } + + _pcmspecial = pccard_pcmspecial; + _pcmspecialclose = pccard_pcmspecialclose; + + for (i = 0; i != nslots; i++) { + Cardbus *cb = &cbslots[i]; + + if ((cb->regs[SocketState] & SE_CCD) == 0) + engine(cb, CardDetected); + } + + delay(500); /* Allow time for power up */ + + for (i = 0; i != nslots; i++) { + Cardbus *cb = &cbslots[i]; + + if (cb->regs[SocketState] & SE_POWER) + engine(cb, CardPowered); + + /* Ack and enable interrupts on all events */ + // cb->regs[SocketEvent] = cb->regs[SocketEvent]; + cb->regs[SocketMask] |= 0xF; + wrreg(cb, Rcscic, 0xC); + } +} + +static int +powerup(Cardbus *cb) +{ + ulong state; + ushort bcr; + + state = cb->regs[SocketState]; + if (state & SS_PC16) { + + // print("#Y%ld: Probed a PC16 card, powering up card\n", cb - cbslots); + cb->type = PC16; + memset(&cb->linfo, 0, sizeof(Pcminfo)); + + /* power up and unreset, wait's are empirical (???) */ + wrreg(cb, Rpc, Fautopower|Foutena|Fcardena); + delay(300); + wrreg(cb, Rigc, 0); + delay(100); + wrreg(cb, Rigc, Fnotreset); + delay(500); + + return 1; + } + + if (state & SS_CCD) + return 0; + + if (state & SS_NOTCARD) { + print("#Y%ld: Not a card inserted\n", cb - cbslots); + return 0; + } + + if ((state & SS_3V) == 0 && (state & SS_5V) == 0) { + print("#Y%ld: Unsupported voltage, powering down card!\n", + cb - cbslots); + cb->regs[SocketControl] = 0; + return 0; + } + + //print("#Y%ld: card %spowered at %d volt\n", cb - cbslots, + // (state & SS_POWER)? "": "not ", + // (state & SS_3V)? 3: (state & SS_5V)? 5: -1); + + /* Power up the card + * and make sure the secondary bus is not in reset. + */ + cb->regs[SocketControl] = (state & SS_5V)? SC_5V: SC_3V; + delay(50); + bcr = pcicfgr16(cb->pci, PciBCR); + bcr &= ~0x40; + pcicfgw16(cb->pci, PciBCR, bcr); + delay(100); + + cb->type = (state & SS_PC16)? PC16: PC32; + return 1; +} + +static void +powerdown(Cardbus *cb) +{ + ushort bcr; + + if (cb->type == PC16) { + + wrreg(cb, Rpc, 0); /* turn off card power */ + wrreg(cb, Rwe, 0); /* no windows */ + + cb->type = -1; + return; + } + + bcr = pcicfgr16(cb->pci, PciBCR); + bcr |= 0x40; + pcicfgw16(cb->pci, PciBCR, bcr); + cb->regs[SocketControl] = 0; + cb->type = -1; +} + +static void +configure(Cardbus *cb) +{ + int i; + Pcidev *pci; + + //print("configuring slot %d (%s)\n", (int)(cb - cbslots), states[cb->state]); + if (cb->state == SlotConfigured) + return; + engine(cb, CardConfigured); + + delay(50); /* Emperically established */ + + if (cb->type == PC16) { + i82365configure(cb); + return; + } + + /* Scan the CardBus for new PCI devices */ + pciscan(pcicfgr8(cb->pci, PciSBN), &cb->pci->bridge); + pci = cb->pci->bridge; + while (pci) { + ulong size, bar; + int memindex, ioindex; + + pcicfgw16(pci, PciPCR, + pcicfgr16(pci, PciPCR) & ~(PciPCR_IO|PciPCR_MEM)); + + /* Treat the found device as an ordinary PCI card. It seems that the + CIS is not always present in CardBus cards. XXX, need to support + multifunction cards */ + memindex = ioindex = 0; + for (i = 0; i != Nbars; i++) { + + if (pci->mem[i].size == 0) continue; + if (pci->mem[i].bar & 1) { + + // Allocate I/O space + if (ioindex > 1) { + print("#Y%ld: WARNING: Can only configure 2 I/O slots\n", cb - cbslots); + continue; + } + bar = ioreserve(-1, pci->mem[i].size, 0, "cardbus"); + + pci->mem[i].bar = bar | 1; + pcicfgw32(pci, PciBAR0 + i * sizeof(ulong), + pci->mem[i].bar); + pcicfgw16(cb->pci, PciCBIBR0 + ioindex * 8, bar); + pcicfgw16(cb->pci, PciCBILR0 + ioindex * 8, + bar + pci->mem[i].size - 1); + //print("ioindex[%d] %.8uX (%d)\n", + // ioindex, bar, pci->mem[i].size); + ioindex++; + continue; + } + + // Allocating memory space + if (memindex > 1) { + print("#Y%ld: WARNING: Can only configure 2 memory slots\n", cb - cbslots); + continue; + } + + bar = upamalloc(0, pci->mem[i].size, BY2PG); + pci->mem[i].bar = bar | (pci->mem[i].bar & 0x80); + pcicfgw32(pci, PciBAR0 + i * sizeof(ulong), pci->mem[i].bar); + pcicfgw32(cb->pci, PciCBMBR0 + memindex * 8, bar); + pcicfgw32(cb->pci, PciCBMLR0 + memindex * 8, + bar + pci->mem[i].size - 1); + + if (pci->mem[i].bar & 0x80) + /* Enable prefetch */ + pcicfgw16(cb->pci, PciBCR, + pcicfgr16(cb->pci, PciBCR) | + (1 << (8 + memindex))); + + //print("memindex[%d] %.8uX (%d)\n", + // memindex, bar, pci->mem[i].size); + memindex++; + } + + if ((size = pcibarsize(pci, PciEBAR0)) > 0) { + + if (memindex > 1) + print("#Y%ld: WARNING: Too many memory spaces, not mapping ROM space\n", + cb - cbslots); + else { + pci->rom.bar = upamalloc(0, size, BY2PG); + pci->rom.size = size; + + pcicfgw32(pci, PciEBAR0, pci->rom.bar); + pcicfgw32(cb->pci, PciCBMBR0 + memindex * 8, + pci->rom.bar); + pcicfgw32(cb->pci, PciCBMLR0 + memindex * 8, + pci->rom.bar + pci->rom.size - 1); + } + } + + /* Set the basic PCI registers for the device */ + pci->pcr = pcicfgr16(pci, PciPCR) | PciPCR_IO|PciPCR_MEM|PciPCR_Master; + pci->cls = 8; + pci->ltr = 64; + pcicfgw16(pci, PciPCR, pci->pcr); + pcicfgw8(pci, PciCLS, pci->cls); + pcicfgw8(pci, PciLTR, pci->ltr); + + if (pcicfgr8(pci, PciINTP)) { + pci->intl = pcicfgr8(cb->pci, PciINTL); + pcicfgw8(pci, PciINTL, pci->intl); + + /* Route interrupts to INTA#/B# */ + pcicfgw16(cb->pci, PciBCR, + pcicfgr16(cb->pci, PciBCR) & ~(1 << 7)); + } + + pci = pci->list; + } +} + +static void +unconfigure(Cardbus *cb) +{ + Pcidev *pci; + int i, ioindex, memindex; + + if (cb->type == PC16) { + print("#Y%d: Don't know how to unconfigure a PC16 card\n", + (int)(cb - cbslots)); + + memset(&cb->linfo, 0, sizeof(Pcminfo)); + return; + } + + pci = cb->pci->bridge; + if (pci == nil) + return; /* Not configured */ + cb->pci->bridge = nil; + + memindex = ioindex = 0; + while (pci) { + Pcidev *_pci; + + for (i = 0; i != Nbars; i++) { + if (pci->mem[i].size == 0) continue; + if (pci->mem[i].bar & 1) { + iofree(pci->mem[i].bar & ~1); + pcicfgw16(cb->pci, PciCBIBR0 + ioindex * 8, + (ushort)-1); + pcicfgw16(cb->pci, PciCBILR0 + ioindex * 8, 0); + ioindex++; + continue; + } + + upafree(pci->mem[i].bar & ~0xF, pci->mem[i].size); + pcicfgw32(cb->pci, PciCBMBR0 + memindex * 8, + (ulong)-1); + pcicfgw32(cb->pci, PciCBMLR0 + memindex * 8, 0); + pcicfgw16(cb->pci, PciBCR, + pcicfgr16(cb->pci, PciBCR) & + ~(1 << (8 + memindex))); + memindex++; + } + + if (pci->rom.bar && memindex < 2) { + upafree(pci->rom.bar & ~0xF, pci->rom.size); + pcicfgw32(cb->pci, PciCBMBR0 + memindex * 8, + (ulong)-1); + pcicfgw32(cb->pci, PciCBMLR0 + memindex * 8, 0); + memindex++; + } + + _pci = pci->list; + free(_pci); + pci = _pci; + } +} + +static void +i82365configure(Cardbus *cb) +{ + int this; + Cisdat cis; + PCMmap *m; + uchar type, link; + + /* + * Read all tuples in attribute space. + */ + m = isamap(cb, 0, 0, 1); + if(m == 0) + return; + + cis.cisbase = KADDR(m->isa); + cis.cispos = 0; + cis.cisskip = 2; + cis.cislen = m->len; + + /* loop through all the tuples */ + for(;;){ + this = cis.cispos; + if(readc(&cis, &type) != 1) + break; + if(type == 0xFF) + break; + if(readc(&cis, &link) != 1) + break; + + switch(type){ + default: + break; + case 0x15: + tvers1(cb, &cis, type); + break; + case 0x1A: + tcfig(cb, &cis, type); + break; + case 0x1B: + tentry(cb, &cis, type); + break; + } + + if(link == 0xFF) + break; + cis.cispos = this + (2+link); + } + isaunmap(m); +} + +/* + * look for a card whose version contains 'idstr' + */ +static int +pccard_pcmspecial(char *idstr, ISAConf *isa) +{ + int i, irq; + PCMconftab *ct, *et; + Pcminfo *pi; + Cardbus *cb; + uchar x, we, *p; + + cb = nil; + for (i = 0; i != nslots; i++) { + cb = &cbslots[i]; + + lock(cb); + if (cb->state == SlotConfigured && + cb->type == PC16 && + !cb->special && + strstr(cb->linfo.verstr, idstr)) + break; + unlock(cb); + } + + if (i == nslots) { + // print("#Y: %s not found\n", idstr); + return -1; + } + + pi = &cb->linfo; + + /* + * configure the PCMslot for IO. We assume very heavily that we can read + * configuration info from the CIS. If not, we won't set up correctly. + */ + irq = isa->irq; + if(irq == 2) + irq = 9; + + et = &pi->ctab[pi->nctab]; + ct = nil; + for(i = 0; i < isa->nopt; i++){ + int index; + char *cp; + + if(strncmp(isa->opt[i], "index=", 6)) + continue; + index = strtol(&isa->opt[i][6], &cp, 0); + if(cp == &isa->opt[i][6] || index >= pi->nctab) { + unlock(cb); + print("#Y%d: Cannot find index %d in conf table\n", + (int)(cb - cbslots), index); + return -1; + } + ct = &pi->ctab[index]; + } + + if(ct == nil){ + PCMconftab *t; + + /* assume default is right */ + if(pi->defctab) + ct = pi->defctab; + else + ct = pi->ctab; + + /* try for best match */ + if(ct->nio == 0 + || ct->io[0].start != isa->port || ((1<irqs) == 0){ + for(t = pi->ctab; t < et; t++) + if(t->nio + && t->io[0].start == isa->port + && ((1<irqs)){ + ct = t; + break; + } + } + if(ct->nio == 0 || ((1<irqs) == 0){ + for(t = pi->ctab; t < et; t++) + if(t->nio && ((1<irqs)){ + ct = t; + break; + } + } + if(ct->nio == 0){ + for(t = pi->ctab; t < et; t++) + if(t->nio){ + ct = t; + break; + } + } + } + + if(ct == et || ct->nio == 0) { + unlock(cb); + print("#Y%d: No configuration?\n", (int)(cb - cbslots)); + return -1; + } + if(isa->port == 0 && ct->io[0].start == 0) { + unlock(cb); + print("#Y%d: No part or start address\n", (int)(cb - cbslots)); + return -1; + } + + cb->special = 1; /* taken */ + + /* route interrupts */ + isa->irq = irq; + wrreg(cb, Rigc, irq | Fnotreset | Fiocard); + + /* set power and enable device */ + x = vcode(ct->vpp1); + wrreg(cb, Rpc, x|Fautopower|Foutena|Fcardena); + + /* 16-bit data path */ + if(ct->bit16) + x = Ftiming|Fiocs16|Fwidth16; + else + x = Ftiming; + if(ct->nio == 2 && ct->io[1].start) + x |= x<<4; + wrreg(cb, Rio, x); + + /* + * enable io port map 0 + * the 'top' register value includes the last valid address + */ + if(isa->port == 0) + isa->port = ct->io[0].start; + we = rdreg(cb, Rwe); + wrreg(cb, Riobtm0lo, isa->port); + wrreg(cb, Riobtm0hi, isa->port>>8); + i = isa->port+ct->io[0].len-1; + wrreg(cb, Riotop0lo, i); + wrreg(cb, Riotop0hi, i>>8); + we |= 1<<6; + if(ct->nio == 2 && ct->io[1].start){ + wrreg(cb, Riobtm1lo, ct->io[1].start); + wrreg(cb, Riobtm1hi, ct->io[1].start>>8); + i = ct->io[1].start+ct->io[1].len-1; + wrreg(cb, Riotop1lo, i); + wrreg(cb, Riotop1hi, i>>8); + we |= 1<<7; + } + wrreg(cb, Rwe, we); + + /* only touch Rconfig if it is present */ + if(pi->conf_present & (1<conf_addr + Rconfig, 1, 1); + p = KADDR(m->isa + pi->conf_addr + Rconfig - m->ca); + + /* set configuration and interrupt type */ + x = ct->index; + if(ct->irqtype & 0x20) + x |= Clevel; + *p = x; + delay(5); + + isaunmap(m); + } + + pi->port = isa->port; + pi->irq = isa->irq; + unlock(cb); + + print("#Y%d: %s irq %d, port %lX\n", (int)(cb - cbslots), pi->verstr, isa->irq, isa->port); + return (int)(cb - cbslots); +} + +static void +pccard_pcmspecialclose(int slotno) +{ + Cardbus *cb = &cbslots[slotno]; + + wrreg(cb, Rwe, 0); /* no windows */ + cb->special = 0; +} + +static int +xcistuple(int slotno, int tuple, int subtuple, void *v, int nv, int attr) +{ + PCMmap *m; + Cisdat cis; + int i, l; + uchar *p; + uchar type, link, n, c; + int this, subtype; + Cardbus *cb = &cbslots[slotno]; + + m = isamap(cb, 0, 0, attr); + if(m == 0) + return -1; + + cis.cisbase = KADDR(m->isa); + cis.cispos = 0; + cis.cisskip = attr ? 2 : 1; + cis.cislen = m->len; + + /* loop through all the tuples */ + for(i = 0; i < 1000; i++){ + this = cis.cispos; + if(readc(&cis, &type) != 1) + break; + if(type == 0xFF) + break; + if(readc(&cis, &link) != 1) + break; + if(link == 0xFF) + break; + + n = link; + if (link > 1 && subtuple != -1) { + if (readc(&cis, &c) != 1) + break; + subtype = c; + n--; + } else + subtype = -1; + + if(type == tuple && subtype == subtuple) { + p = v; + for(l=0; lqid.path>>8)&0xff)) +#define TYPE(c) ((ulong)(c->qid.path&0xff)) +#define QID(s,t) (((s)<<8)|(t)) + +static int +pccardgen(Chan *c, char*, Dirtab *, int , int i, Dir *dp) +{ + int slotno; + Qid qid; + long len; + int entry; + + if(i == DEVDOTDOT){ + mkqid(&qid, Qdir, 0, QTDIR); + devdir(c, qid, "#Y", 0, eve, 0555, dp); + return 1; + } + + len = 0; + if(i >= Nents * nslots) return -1; + slotno = i / Nents; + entry = i % Nents; + if (entry == 0) { + qid.path = QID(slotno, Qctl); + snprint(up->genbuf, sizeof up->genbuf, "cb%dctl", slotno); + } + else { + /* Entries for memory regions. I'll implement them when + needed. (pb) */ + } + qid.vers = 0; + qid.type = QTFILE; + devdir(c, qid, up->genbuf, len, eve, 0660, dp); + return 1; +} + +static Walkqid* +pccardwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, pccardgen); +} + +static int +pccardstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, pccardgen); +} + +static void +increfp(Cardbus *cb) +{ + lock(&cb->refslock); + cb->refs++; + unlock(&cb->refslock); +} + +static void +decrefp(Cardbus *cb) +{ + lock(&cb->refslock); + cb->refs--; + unlock(&cb->refslock); +} + +static Chan* +pccardopen(Chan *c, int omode) +{ + if (c->qid.type & QTDIR){ + if(omode != OREAD) + error(Eperm); + } else + increfp(&cbslots[SLOTNO(c)]); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +pccardclose(Chan *c) +{ + if(c->flag & COPEN) + if((c->qid.type & QTDIR) == 0) + decrefp(&cbslots[SLOTNO(c)]); +} + +static long +pccardread(Chan *c, void *a, long n, vlong offset) +{ + Cardbus *cb; + char *buf, *p, *e; + int i; + + switch(TYPE(c)){ + case Qdir: + return devdirread(c, a, n, 0, 0, pccardgen); + + case Qctl: + buf = p = malloc(READSTR); + buf[0] = 0; + e = p + READSTR; + + cb = &cbslots[SLOTNO(c)]; + lock(cb); + p = seprint(p, e, "slot %ld: %s; ", cb - cbslots, states[cb->state]); + + switch (cb->type) { + case -1: + seprint(p, e, "\n"); + break; + + case PC32: + if (cb->pci->bridge) { + Pcidev *pci = cb->pci->bridge; + int i; + + while (pci) { + p = seprint(p, e, "%.4uX %.4uX; irq %d\n", + pci->vid, pci->did, pci->intl); + for (i = 0; i != Nbars; i++) + if (pci->mem[i].size) + p = seprint(p, e, + "\tmem[%d] %.8ulX (%.8uX)\n", + i, pci->mem[i].bar, + pci->mem[i].size); + if (pci->rom.size) + p = seprint(p, e, "\tROM %.8ulX (%.8uX)\n", + pci->rom.bar, pci->rom.size); + pci = pci->list; + } + } + break; + + case PC16: + if (cb->state == SlotConfigured) { + Pcminfo *pi = &cb->linfo; + + p = seprint(p, e, "%s port %X; irq %d;\n", + pi->verstr, pi->port, + pi->irq); + for (i = 0; i != pi->nctab; i++) { + PCMconftab *ct; + int j; + + ct = &pi->ctab[i]; + p = seprint(p, e, + "\tconfiguration[%d] irqs %.4uX; vpp %d, %d; %s\n", + i, ct->irqs, ct->vpp1, ct->vpp2, + (ct == pi->defctab)? "(default);": ""); + for (j = 0; j != ct->nio; j++) + if (ct->io[j].len > 0) + p = seprint(p, e, "\t\tio[%d] %.8ulX %uld\n", + j, ct->io[j].start, ct->io[j].len); + } + } + break; + } + unlock(cb); + + n = readstr(offset, a, n, buf); + free(buf); + return n; + } + return 0; +} + +static long +pccardwrite(Chan *c, void *v, long n, vlong) +{ + Rune r; + ulong n0; + char *device; + Cmdbuf *cbf; + Cmdtab *ct; + Cardbus *cb; + + n0 = n; + switch(TYPE(c)){ + case Qctl: + cb = &cbslots[SLOTNO(c)]; + + cbf = parsecmd(v, n); + if(waserror()){ + free(cbf); + nexterror(); + } + ct = lookupcmd(cbf, pccardctlmsg, nelem(pccardctlmsg)); + switch(ct->index){ + case CMdown: + device = cbf->f[1]; + device += chartorune(&r, device); + if ((n = devno(r, 1)) >= 0 && devtab[n]->config) + devtab[n]->config(0, device, nil); + qengine(cb, CardEjected); + break; + case CMpower: + if ((cb->regs[SocketState] & SS_CCD) == 0) + qengine(cb, CardDetected); + break; + } + poperror(); + free(cbf); + break; + } + return n0 - n; +} + +Dev pccarddevtab = { + 'Y', + "cardbus", + + devreset, + devinit, + devshutdown, + pccardattach, + pccardwalk, + pccardstat, + pccardopen, + devcreate, + pccardclose, + pccardread, + devbread, + pccardwrite, + devbwrite, + devremove, + devwstat, +}; + +static PCMmap * +isamap(Cardbus *cb, ulong offset, int len, int attr) +{ + uchar we, bit; + PCMmap *m, *nm; + Pcminfo *pi; + int i; + ulong e; + + pi = &cb->linfo; + + /* convert offset to granularity */ + if(len <= 0) + len = 1; + e = ROUND(offset+len, Mgran); + offset &= Mmask; + len = e - offset; + + /* look for a map that covers the right area */ + we = rdreg(cb, Rwe); + bit = 1; + nm = 0; + for(m = pi->mmap; m < &pi->mmap[nelem(pi->mmap)]; m++){ + if((we & bit)) + if(m->attr == attr) + if(offset >= m->ca && e <= m->cea){ + + m->ref++; + return m; + } + bit <<= 1; + if(nm == 0 && m->ref == 0) + nm = m; + } + m = nm; + if(m == 0) + return 0; + + /* if isa space isn't big enough, free it and get more */ + if(m->len < len){ + if(m->isa){ + umbfree(m->isa, m->len); + m->len = 0; + } + m->isa = PADDR(umbmalloc(0, len, Mgran)); + if(m->isa == 0){ + print("isamap: out of isa space\n"); + return 0; + } + m->len = len; + } + + /* set up new map */ + m->ca = offset; + m->cea = m->ca + m->len; + m->attr = attr; + i = m - pi->mmap; + bit = 1<isa>>Mshift); + wrreg(cb, MAP(i, Mbtmhi), (m->isa>>(Mshift+8)) | F16bit); + wrreg(cb, MAP(i, Mtoplo), (m->isa+m->len-1)>>Mshift); + wrreg(cb, MAP(i, Mtophi), ((m->isa+m->len-1)>>(Mshift+8))); + offset -= m->isa; + offset &= (1<<25)-1; + offset >>= Mshift; + wrreg(cb, MAP(i, Mofflo), offset); + wrreg(cb, MAP(i, Moffhi), (offset>>8) | (attr ? Fregactive : 0)); + wrreg(cb, Rwe, we | bit); /* enable map */ + m->ref = 1; + + return m; +} + +static void +isaunmap(PCMmap* m) +{ + m->ref--; +} + +/* + * reading and writing card registers + */ +static uchar +rdreg(Cardbus *cb, int index) +{ + outb(cb->lindex, cb->lbase + index); + return inb(cb->ldata); +} + +static void +wrreg(Cardbus *cb, int index, uchar val) +{ + outb(cb->lindex, cb->lbase + index); + outb(cb->ldata, val); +} + +static int +readc(Cisdat *cis, uchar *x) +{ + if(cis->cispos >= cis->cislen) + return 0; + *x = cis->cisbase[cis->cisskip*cis->cispos]; + cis->cispos++; + return 1; +} + +static ulong +getlong(Cisdat *cis, int size) +{ + uchar c; + int i; + ulong x; + + x = 0; + for(i = 0; i < size; i++){ + if(readc(cis, &c) != 1) + break; + x |= c<<(i*8); + } + return x; +} + +static void +tcfig(Cardbus *cb, Cisdat *cis, int ) +{ + uchar size, rasize, rmsize; + uchar last; + Pcminfo *pi; + + if(readc(cis, &size) != 1) + return; + rasize = (size&0x3) + 1; + rmsize = ((size>>2)&0xf) + 1; + if(readc(cis, &last) != 1) + return; + + pi = &cb->linfo; + pi->conf_addr = getlong(cis, rasize); + pi->conf_present = getlong(cis, rmsize); +} + +static void +tvers1(Cardbus *cb, Cisdat *cis, int ) +{ + uchar c, major, minor, last; + int i; + Pcminfo *pi; + + pi = &cb->linfo; + if(readc(cis, &major) != 1) + return; + if(readc(cis, &minor) != 1) + return; + last = 0; + for(i = 0; i < sizeof(pi->verstr) - 1; i++){ + if(readc(cis, &c) != 1) + return; + if(c == 0) + c = ';'; + if(c == '\n') + c = ';'; + if(c == 0xff) + break; + if(c == ';' && last == ';') + continue; + pi->verstr[i] = c; + last = c; + } + pi->verstr[i] = 0; +} + +static ulong +microvolt(Cisdat *cis) +{ + uchar c; + ulong microvolts; + ulong exp; + + if(readc(cis, &c) != 1) + return 0; + exp = exponent[c&0x7]; + microvolts = vmant[(c>>3)&0xf]*exp; + while(c & 0x80){ + if(readc(cis, &c) != 1) + return 0; + switch(c){ + case 0x7d: + break; /* high impedence when sleeping */ + case 0x7e: + case 0x7f: + microvolts = 0; /* no connection */ + break; + default: + exp /= 10; + microvolts += exp*(c&0x7f); + } + } + return microvolts; +} + +static ulong +nanoamps(Cisdat *cis) +{ + uchar c; + ulong nanoamps; + + if(readc(cis, &c) != 1) + return 0; + nanoamps = exponent[c&0x7]*vmant[(c>>3)&0xf]; + while(c & 0x80){ + if(readc(cis, &c) != 1) + return 0; + if(c == 0x7d || c == 0x7e || c == 0x7f) + nanoamps = 0; + } + return nanoamps; +} + +/* + * only nominal voltage (feature 1) is important for config, + * other features must read card to stay in sync. + */ +static ulong +power(Cisdat *cis) +{ + uchar feature; + ulong mv; + + mv = 0; + if(readc(cis, &feature) != 1) + return 0; + if(feature & 1) + mv = microvolt(cis); + if(feature & 2) + microvolt(cis); + if(feature & 4) + microvolt(cis); + if(feature & 8) + nanoamps(cis); + if(feature & 0x10) + nanoamps(cis); + if(feature & 0x20) + nanoamps(cis); + if(feature & 0x40) + nanoamps(cis); + return mv/1000000; +} + +static ulong +ttiming(Cisdat *cis, int scale) +{ + uchar unscaled; + ulong nanosecs; + + if(readc(cis, &unscaled) != 1) + return 0; + nanosecs = (mantissa[(unscaled>>3)&0xf]*exponent[unscaled&7])/10; + nanosecs = nanosecs * exponent[scale]; + return nanosecs; +} + +static void +timing(Cisdat *cis, PCMconftab *ct) +{ + uchar c, i; + + if(readc(cis, &c) != 1) + return; + i = c&0x3; + if(i != 3) + ct->maxwait = ttiming(cis, i); /* max wait */ + i = (c>>2)&0x7; + if(i != 7) + ct->readywait = ttiming(cis, i); /* max ready/busy wait */ + i = (c>>5)&0x7; + if(i != 7) + ct->otherwait = ttiming(cis, i); /* reserved wait */ +} + +static void +iospaces(Cisdat *cis, PCMconftab *ct) +{ + uchar c; + int i, nio; + + ct->nio = 0; + if(readc(cis, &c) != 1) + return; + + ct->bit16 = ((c>>5)&3) >= 2; + if(!(c & 0x80)){ + ct->io[0].start = 0; + ct->io[0].len = 1<<(c&0x1f); + ct->nio = 1; + return; + } + + if(readc(cis, &c) != 1) + return; + + /* + * For each of the range descriptions read the + * start address and the length (value is length-1). + */ + nio = (c&0xf)+1; + for(i = 0; i < nio; i++){ + ct->io[i].start = getlong(cis, (c>>4)&0x3); + ct->io[i].len = getlong(cis, (c>>6)&0x3)+1; + } + ct->nio = nio; +} + +static void +irq(Cisdat *cis, PCMconftab *ct) +{ + uchar c; + + if(readc(cis, &c) != 1) + return; + ct->irqtype = c & 0xe0; + if(c & 0x10) + ct->irqs = getlong(cis, 2); + else + ct->irqs = 1<<(c&0xf); + ct->irqs &= 0xDEB8; /* levels available to card */ +} + +static void +memspace(Cisdat *cis, int asize, int lsize, int host) +{ + ulong haddress, address, len; + + len = getlong(cis, lsize)*256; + address = getlong(cis, asize)*256; + USED(len, address); + if(host){ + haddress = getlong(cis, asize)*256; + USED(haddress); + } +} + +static void +tentry(Cardbus *cb, Cisdat *cis, int ) +{ + uchar c, i, feature; + PCMconftab *ct; + Pcminfo *pi; + + pi = &cb->linfo; + if(pi->nctab >= nelem(pi->ctab)) + return; + if(readc(cis, &c) != 1) + return; + ct = &pi->ctab[pi->nctab++]; + + /* copy from last default config */ + if(pi->defctab) + *ct = *pi->defctab; + + ct->index = c & 0x3f; + + /* is this the new default? */ + if(c & 0x40) + pi->defctab = ct; + + /* memory wait specified? */ + if(c & 0x80){ + if(readc(cis, &i) != 1) + return; + if(i&0x80) + ct->memwait = 1; + } + + if(readc(cis, &feature) != 1) + return; + switch(feature&0x3){ + case 1: + ct->vpp1 = ct->vpp2 = power(cis); + break; + case 2: + power(cis); + ct->vpp1 = ct->vpp2 = power(cis); + break; + case 3: + power(cis); + ct->vpp1 = power(cis); + ct->vpp2 = power(cis); + break; + default: + break; + } + if(feature&0x4) + timing(cis, ct); + if(feature&0x8) + iospaces(cis, ct); + if(feature&0x10) + irq(cis, ct); + switch((feature>>5)&0x3){ + case 1: + memspace(cis, 0, 2, 0); + break; + case 2: + memspace(cis, 2, 2, 0); + break; + case 3: + if(readc(cis, &c) != 1) + return; + for(i = 0; i <= (c&0x7); i++) + memspace(cis, (c>>5)&0x3, (c>>3)&0x3, c&0x80); + break; + } +} + +static void +i82365probe(Cardbus *cb, int lindex, int ldata) +{ + uchar c, id; + int dev = 0; /* According to the Ricoh spec 00->3F _and_ 80->BF seem + to be the same socket A (ditto for B). */ + + outb(lindex, Rid + (dev<<7)); + id = inb(ldata); + if((id & 0xf0) != 0x80) + return; /* not a memory & I/O card */ + if((id & 0x0f) == 0x00) + return; /* no revision number, not possible */ + + cb->lindex = lindex; + cb->ldata = ldata; + cb->ltype = Ti82365; + cb->lbase = (int)(cb - cbslots) * 0x40; + + switch(id){ + case 0x82: + case 0x83: + case 0x84: + /* could be a cirrus */ + outb(cb->lindex, Rchipinfo + (dev<<7)); + outb(cb->ldata, 0); + c = inb(cb->ldata); + if((c & 0xc0) != 0xc0) + break; + c = inb(cb->ldata); + if((c & 0xc0) != 0x00) + break; + if(c & 0x20){ + cb->ltype = Tpd6720; + } else { + cb->ltype = Tpd6710; + } + break; + } + + /* if it's not a Cirrus, it could be a Vadem... */ + if(cb->ltype == Ti82365){ + /* unlock the Vadem extended regs */ + outb(cb->lindex, 0x0E + (dev<<7)); + outb(cb->lindex, 0x37 + (dev<<7)); + + /* make the id register show the Vadem id */ + outb(cb->lindex, 0x3A + (dev<<7)); + c = inb(cb->ldata); + outb(cb->ldata, c|0xC0); + outb(cb->lindex, Rid + (dev<<7)); + c = inb(cb->ldata); + if(c & 0x08) + cb->ltype = Tvg46x; + + /* go back to Intel compatible id */ + outb(cb->lindex, 0x3A + (dev<<7)); + c = inb(cb->ldata); + outb(cb->ldata, c & ~0xC0); + } +} + +static int +vcode(int volt) +{ + switch(volt){ + case 5: + return 1; + case 12: + return 2; + default: + return 0; + } +} + diff --git a/os/pc/devpnp.c b/os/pc/devpnp.c new file mode 100644 index 00000000..fe4c05dd --- /dev/null +++ b/os/pc/devpnp.c @@ -0,0 +1,652 @@ +/* + * ISA PNP 1.0 support + access to PCI configuration space + * + * TODO + * - implement PNP card configuration (setting io bases etc) + * - write user program to drive PNP configuration... + * - extend PCI raw access to configuration space (writes, byte/short access?) + * - implement PCI access to memory/io space/BIOS ROM + * - use c->aux instead of performing lookup on each read/write? + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +typedef struct Pnp Pnp; +typedef struct Card Card; + +struct Pnp +{ + QLock; + int rddata; + int debug; + Card *cards; +}; + +struct Card +{ + int csn; + ulong id1; + ulong id2; + char *cfgstr; + int ncfg; + Card* next; +}; + +static Pnp pnp; + +#define DPRINT if(pnp.debug) print +#define XPRINT if(1) print + +enum { + Address = 0x279, + WriteData = 0xa79, + + Qtopdir = 0, + + Qpnpdir, + Qpnpctl, + Qcsnctl, + Qcsnraw, + + Qpcidir, + Qpcictl, + Qpciraw, +}; + +#define TYPE(q) ((ulong)(q).path & 0x0F) +#define CSN(q) (((ulong)(q).path>>4) & 0xFF) +#define QID(c, t) (((c)<<4)|(t)) + +static Dirtab topdir[] = { + ".", { Qtopdir, 0, QTDIR }, 0, 0555, + "pnp", { Qpnpdir, 0, QTDIR }, 0, 0555, + "pci", { Qpcidir, 0, QTDIR }, 0, 0555, +}; + +static Dirtab pnpdir[] = { + ".", { Qpnpdir, 0, QTDIR }, 0, 0555, + "ctl", { Qpnpctl, 0, 0 }, 0, 0666, +}; + +extern Dev pnpdevtab; +static int wrconfig(Card*, char*); + +static char key[32] = +{ + 0x6A, 0xB5, 0xDA, 0xED, 0xF6, 0xFB, 0x7D, 0xBE, + 0xDF, 0x6F, 0x37, 0x1B, 0x0D, 0x86, 0xC3, 0x61, + 0xB0, 0x58, 0x2C, 0x16, 0x8B, 0x45, 0xA2, 0xD1, + 0xE8, 0x74, 0x3A, 0x9D, 0xCE, 0xE7, 0x73, 0x39, +}; + +static void +cmd(int reg, int val) +{ + outb(Address, reg); + outb(WriteData, val); +} + +/* Send initiation key, putting each card in Sleep state */ +static void +initiation(void) +{ + int i; + + /* ensure each card's LFSR is reset */ + outb(Address, 0x00); + outb(Address, 0x00); + + /* send initiation key */ + for (i = 0; i < 32; i++) + outb(Address, key[i]); +} + +/* isolation protocol... */ +static int +readbit(int rddata) +{ + int r1, r2; + + r1 = inb(rddata); + r2 = inb(rddata); + microdelay(250); + return (r1 == 0x55) && (r2 == 0xaa); +} + +static int +isolate(int rddata, ulong *id1, ulong *id2) +{ + int i, csum, bit; + uchar *p, id[9]; + + outb(Address, 0x01); /* point to serial isolation register */ + delay(1); + csum = 0x6a; + for(i = 0; i < 64; i++){ + bit = readbit(rddata); + csum = (csum>>1) | (((csum&1) ^ ((csum>>1)&1) ^ bit)<<7); + p = &id[i>>3]; + *p = (*p>>1) | (bit<<7); + } + for(; i < 72; i++){ + p = &id[i>>3]; + *p = (*p>>1) | (readbit(rddata)<<7); + } + *id1 = (id[3]<<24)|(id[2]<<16)|(id[1]<<8)|id[0]; + *id2 = (id[7]<<24)|(id[6]<<16)|(id[5]<<8)|id[4]; + if(*id1 == 0) + return 0; + if(id[8] != csum) + DPRINT("pnp: bad checksum id1 %lux id2 %lux csum %x != %x\n", *id1, *id2, csum, id[8]); /**/ + return id[8] == csum; +} + +static int +getresbyte(int rddata) +{ + int tries = 0; + + outb(Address, 0x05); + while ((inb(rddata) & 1) == 0) + if (tries++ > 1000000) + error("pnp: timeout waiting for resource data\n"); + outb(Address, 0x04); + return inb(rddata); +} + +static char * +serial(ulong id1, ulong id2) +{ + int i1, i2, i3; + ulong x; + static char buf[20]; + + i1 = (id1>>2)&31; + i2 = ((id1<<3)&24)+((id1>>13)&7); + i3 = (id1>>8)&31; + x = (id1>>8)&0xff00|(id1>>24)&0x00ff; + if (i1 > 0 && i1 < 27 && i2 > 0 && i2 < 27 && i3 > 0 && i3 < 27 && (id1 & (1<<7)) == 0) + snprint(buf, sizeof(buf), "%c%c%c%.4lux.%lux", 'A'+i1-1, 'A'+i2-1, 'A'+i3-1, x, id2); + else + snprint(buf, sizeof(buf), "%.4lux%.4lux.%lux", (id1<<8)&0xff00|(id1>>8)&0x00ff, x, id2); + return buf; +} + +static Card * +findcsn(int csn, int create, int dolock) +{ + Card *c, *nc, **l; + + if(dolock) + qlock(&pnp); + l = &pnp.cards; + for(c = *l; c != nil; c = *l) { + if(c->csn == csn) + goto done; + if(c->csn > csn) + break; + l = &c->next; + } + if(create) { + *l = nc = malloc(sizeof(Card)); + nc->next = c; + nc->csn = csn; + c = nc; + } +done: + if(dolock) + qunlock(&pnp); + return c; +} + +static int +newcsn(void) +{ + int csn; + Card *c; + + csn = 1; + for(c = pnp.cards; c != nil; c = c->next) { + if(c->csn > csn) + break; + csn = c->csn+1; + } + return csn; +} + +static int +pnpncfg(int rddata) +{ + int i, n, x, ncfg, n1, n2; + + ncfg = 0; + for (;;) { + x = getresbyte(rddata); + if((x & 0x80) == 0) { + n = (x&7)+1; + for(i = 1; i < n; i++) + getresbyte(rddata); + } + else { + n1 = getresbyte(rddata); + n2 = getresbyte(rddata); + n = (n2<<8)|n1 + 3; + for (i = 3; i < n; i++) + getresbyte(rddata); + } + ncfg += n; + if((x>>3) == 0x0f) + break; + } + return ncfg; +} + +/* look for cards, and assign them CSNs */ +static int +pnpscan(int rddata, int dawn) +{ + Card *c; + int csn; + ulong id1, id2; + + initiation(); /* upsilon sigma */ + cmd(0x02, 0x04+0x01); /* reset CSN on all cards and reset logical devices */ + delay(1); /* delay after resetting cards */ + + cmd(0x03, 0); /* Wake all cards with a CSN of 0 */ + cmd(0x00, rddata>>2); /* Set the READ_DATA port on all cards */ + while(isolate(rddata, &id1, &id2)) { + for(c = pnp.cards; c != nil; c = c->next) + if(c->id1 == id1 && c->id2 == id2) + break; + if(c == nil) { + csn = newcsn(); + c = findcsn(csn, 1, 0); + c->id1 = id1; + c->id2 = id2; + } + else if(c->cfgstr != nil) { + if(!wrconfig(c, c->cfgstr)) + print("pnp%d: bad cfg: %s\n", c->csn, c->cfgstr); + c->cfgstr = nil; + } + cmd(0x06, c->csn); /* set the card's csn */ + if(dawn) + print("pnp%d: %s\n", c->csn, serial(id1, id2)); + c->ncfg = pnpncfg(rddata); + cmd(0x03, 0); /* Wake all cards with a CSN of 0, putting this card to sleep */ + } + cmd(0x02, 0x02); /* return cards to Wait for Key state */ + if(pnp.cards != 0) { + pnp.rddata = rddata; + return 1; + } + return 0; +} + +static void +pnpreset(void) +{ + Card *c; + ulong id1, id2; + int csn, i1, i2, i3, x; + char *s, *p, buf[20]; + ISAConf isa; + + memset(&isa, 0, sizeof(ISAConf)); + pnp.rddata = -1; + if (isaconfig("pnp", 0, &isa) == 0) + return; + if(isa.port < 0x203 || isa.port > 0x3ff) + return; + for(csn = 1; csn < 256; csn++) { + sprint(buf, "pnp%d", csn); + s = getconf(buf); + if(s == 0) + continue; + if(strlen(s) < 8 || s[7] != '.' || s[0] < 'A' || s[0] > 'Z' || s[1] < 'A' || s[1] > 'Z' || s[2] < 'A' || s[2] > 'Z') { +bad: + print("pnp%d: bad conf string %s\n", csn, s); + continue; + } + i1 = s[0]-'A'+1; + i2 = s[1]-'A'+1; + i3 = s[2]-'A'+1; + x = strtoul(&s[3], 0, 16); + id1 = (i1<<2)|((i2>>3)&3)|((i2&7)<<13)|(i3<<8)|((x&0xff)<<24)|((x&0xff00)<<8); + id2 = strtoul(&s[8], &p, 16); + if(*p == ' ') + p++; + else if(*p == '\0') + p = nil; + else + goto bad; + c = findcsn(csn, 1, 0); + c->id1 = id1; + c->id2 = id2; + c->cfgstr = p; + } + pnpscan(isa.port, 1); +} + +static int +csngen(Chan *c, int t, int csn, Card *cp, Dir *dp) +{ + Qid q; + + switch(t) { + case Qcsnctl: + q = (Qid){QID(csn, Qcsnctl), 0, 0}; + sprint(up->genbuf, "csn%dctl", csn); + devdir(c, q, up->genbuf, 0, eve, 0664, dp); + return 1; + case Qcsnraw: + q = (Qid){QID(csn, Qcsnraw), 0, 0}; + sprint(up->genbuf, "csn%draw", csn); + devdir(c, q, up->genbuf, cp->ncfg, eve, 0444, dp); + return 1; + } + return -1; +} + +static int +pcigen(Chan *c, int t, int tbdf, Dir *dp) +{ + Qid q; + + q = (Qid){BUSBDF(tbdf)|t, 0, 0}; + switch(t) { + case Qpcictl: + sprint(up->genbuf, "%d.%d.%dctl", BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf)); + devdir(c, q, up->genbuf, 0, eve, 0444, dp); + return 1; + case Qpciraw: + sprint(up->genbuf, "%d.%d.%draw", BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf)); + devdir(c, q, up->genbuf, 128, eve, 0444, dp); + return 1; + } + return -1; +} + +static int +pnpgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp) +{ + Qid q; + Card *cp; + Pcidev *p; + int csn, tbdf; + + switch(TYPE(c->qid)){ + case Qtopdir: + if(s == DEVDOTDOT){ + q = (Qid){QID(0, Qtopdir), 0, QTDIR}; + sprint(up->genbuf, "#%C", pnpdevtab.dc); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + return devgen(c, nil, topdir, nelem(topdir), s, dp); + case Qpnpdir: + if(s == DEVDOTDOT){ + q = (Qid){QID(0, Qtopdir), 0, QTDIR}; + sprint(up->genbuf, "#%C", pnpdevtab.dc); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + if(s < nelem(pnpdir)-1) + return devgen(c, nil, pnpdir, nelem(pnpdir), s, dp); + s -= nelem(pnpdir)-1; + qlock(&pnp); + cp = pnp.cards; + while(s >= 2 && cp != nil) { + s -= 2; + cp = cp->next; + } + qunlock(&pnp); + if(cp == nil) + return -1; + return csngen(c, s+Qcsnctl, cp->csn, cp, dp); + case Qpnpctl: + return devgen(c, nil, pnpdir, nelem(pnpdir), s, dp); + case Qcsnctl: + case Qcsnraw: + csn = CSN(c->qid); + cp = findcsn(csn, 0, 1); + if(cp == nil) + return -1; + return csngen(c, TYPE(c->qid), csn, cp, dp); + case Qpcidir: + if(s == DEVDOTDOT){ + q = (Qid){QID(0, Qtopdir), 0, QTDIR}; + sprint(up->genbuf, "#%C", pnpdevtab.dc); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + p = pcimatch(nil, 0, 0); + while(s >= 2 && p != nil) { + p = pcimatch(p, 0, 0); + s -= 2; + } + if(p == nil) + return -1; + return pcigen(c, s+Qpcictl, p->tbdf, dp); + case Qpcictl: + case Qpciraw: + tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path); + p = pcimatchtbdf(tbdf); + if(p == nil) + return -1; + return pcigen(c, TYPE(c->qid), tbdf, dp); + default: + break; + } + return -1; +} + +static Chan* +pnpattach(char *spec) +{ + return devattach(pnpdevtab.dc, spec); +} + +Walkqid* +pnpwalk(Chan* c, Chan *nc, char** name, int nname) +{ + return devwalk(c, nc, name, nname, (Dirtab *)0, 0, pnpgen); +} + +static int +pnpstat(Chan* c, uchar* dp, int n) +{ + return devstat(c, dp, n, (Dirtab *)0, 0L, pnpgen); +} + +static Chan* +pnpopen(Chan *c, int omode) +{ + c = devopen(c, omode, (Dirtab*)0, 0, pnpgen); + switch(TYPE(c->qid)){ + default: + break; + } + return c; +} + +static void +pnpclose(Chan*) +{ +} + +static long +pnpread(Chan *c, void *va, long n, vlong offset) +{ + ulong x; + Card *cp; + Pcidev *p; + char buf[256], *ebuf, *w; + char *a = va; + int csn, i, tbdf, r; + + switch(TYPE(c->qid)){ + case Qtopdir: + case Qpnpdir: + case Qpcidir: + return devdirread(c, a, n, (Dirtab *)0, 0L, pnpgen); + case Qpnpctl: + if(pnp.rddata > 0) + sprint(up->genbuf, "enabled 0x%x\n", pnp.rddata); + else + sprint(up->genbuf, "disabled\n"); + return readstr(offset, a, n, up->genbuf); + case Qcsnraw: + csn = CSN(c->qid); + cp = findcsn(csn, 0, 1); + if(cp == nil) + error(Egreg); + if(offset+n > cp->ncfg) + n = cp->ncfg - offset; + qlock(&pnp); + initiation(); + cmd(0x03, csn); /* Wake up the card */ + for(i = 0; i < offset+9; i++) /* 9 == skip serial + csum */ + getresbyte(pnp.rddata); + for(i = 0; i < n; i++) + a[i] = getresbyte(pnp.rddata); + cmd(0x03, 0); /* Wake all cards with a CSN of 0, putting this card to sleep */ + cmd(0x02, 0x02); /* return cards to Wait for Key state */ + qunlock(&pnp); + break; + case Qcsnctl: + csn = CSN(c->qid); + cp = findcsn(csn, 0, 1); + if(cp == nil) + error(Egreg); + sprint(up->genbuf, "%s\n", serial(cp->id1, cp->id2)); + return readstr(offset, a, n, up->genbuf); + case Qpcictl: + tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path); + p = pcimatchtbdf(tbdf); + if(p == nil) + error(Egreg); + ebuf = buf+sizeof buf-1; /* -1 for newline */ + w = seprint(buf, ebuf, "%.2x.%.2x.%.2x %.4x/%.4x %3d", + p->ccrb, p->ccru, p->ccrp, p->vid, p->did, p->intl); + for(i=0; imem); i++){ + if(p->mem[i].size == 0) + continue; + w = seprint(w, ebuf, " %d:%.8lux %d", i, p->mem[i].bar, p->mem[i].size); + } + *w++ = '\n'; + *w = '\0'; + return readstr(offset, a, n, buf); + case Qpciraw: + tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path); + p = pcimatchtbdf(tbdf); + if(p == nil) + error(Egreg); + if(offset > 256) + return 0; + if(n+offset > 256) + n = 256-offset; + if(offset%4) + error(Ebadarg); + r = offset; + for(i = 0; i+4 <= n; i+=4) { + x = pcicfgr32(p, r); + a[0] = x; + a[1] = (x>>8); + a[2] = (x>>16); + a[3] = (x>>24); + a += 4; + r += 4; + } + return i; + default: + error(Egreg); + } + return n; +} + +static long +pnpwrite(Chan *c, void *a, long n, vlong) +{ + int csn; + Card *cp; + ulong port; + char buf[256]; + + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + strncpy(buf, a, n); + buf[n] = 0; + + switch(TYPE(c->qid)){ + case Qpnpctl: + if(strncmp(buf, "port ", 5) == 0) { + port = strtoul(buf+5, 0, 0); + if(port < 0x203 || port > 0x3ff) + error("bad value for rddata port"); + qlock(&pnp); + if(waserror()) { + qunlock(&pnp); + nexterror(); + } + if(pnp.rddata > 0) + error("pnp port already set"); + if(!pnpscan(port, 0)) + error("no cards found"); + qunlock(&pnp); + poperror(); + } + else if(strncmp(buf, "debug ", 6) == 0) + pnp.debug = strtoul(buf+6, 0, 0); + else + error(Ebadctl); + break; + case Qcsnctl: + csn = CSN(c->qid); + cp = findcsn(csn, 0, 1); + if(cp == nil) + error(Egreg); + if(!wrconfig(cp, buf)) + error(Ebadctl); + break; + default: + error(Egreg); + } + return n; +} + +static int +wrconfig(Card *c, char *cmd) +{ + /* This should implement setting of I/O bases, etc */ + USED(c, cmd); + return 1; +} + + +Dev pnpdevtab = { + '$', + "pnp", + + pnpreset, + devinit, + devshutdown, + pnpattach, + pnpwalk, + pnpstat, + pnpopen, + devcreate, + pnpclose, + pnpread, + devbread, + pnpwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/pc/devrtc.c b/os/pc/devrtc.c new file mode 100644 index 00000000..f6e1d911 --- /dev/null +++ b/os/pc/devrtc.c @@ -0,0 +1,461 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +/* + * real time clock and non-volatile ram + */ + +enum { + Paddr= 0x70, /* address port */ + Pdata= 0x71, /* data port */ + + Seconds= 0x00, + Minutes= 0x02, + Hours= 0x04, + Mday= 0x07, + Month= 0x08, + Year= 0x09, + Status= 0x0A, + + Nvoff= 128, /* where usable nvram lives */ + Nvsize= 256, + + Nbcd= 6, +}; + +typedef struct Rtc Rtc; +struct Rtc +{ + int sec; + int min; + int hour; + int mday; + int mon; + int year; +}; + + +enum{ + Qdir = 0, + Qrtc, + Qnvram, +}; + +Dirtab rtcdir[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "nvram", {Qnvram, 0}, Nvsize, 0664, + "rtc", {Qrtc, 0}, 0, 0664, +}; + +static ulong rtc2sec(Rtc*); +static void sec2rtc(ulong, Rtc*); + +void +rtcinit(void) +{ + if(ioalloc(Paddr, 2, 0, "rtc/nvr") < 0) + panic("rtcinit: ioalloc failed"); +} + +static Chan* +rtcattach(char* spec) +{ + return devattach('r', spec); +} + +static Walkqid* +rtcwalk(Chan* c, Chan *nc, char** name, int nname) +{ + return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen); +} + +static int +rtcstat(Chan* c, uchar* dp, int n) +{ + return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen); +} + +static Chan* +rtcopen(Chan* c, int omode) +{ + omode = openmode(omode); + switch((ulong)c->qid.path){ + case Qrtc: + if(strcmp(up->env->user, eve)!=0 && omode!=OREAD) + error(Eperm); + break; + case Qnvram: + if(strcmp(up->env->user, eve)!=0) + error(Eperm); + } + return devopen(c, omode, rtcdir, nelem(rtcdir), devgen); +} + +static void +rtcclose(Chan*) +{ +} + +#define GETBCD(o) ((bcdclock[o]&0xf) + 10*(bcdclock[o]>>4)) + +static long +_rtctime(void) +{ + uchar bcdclock[Nbcd]; + Rtc rtc; + int i; + + /* don't do the read until the clock is no longer busy */ + for(i = 0; i < 10000; i++){ + outb(Paddr, Status); + if(inb(Pdata) & 0x80) + continue; + + /* read clock values */ + outb(Paddr, Seconds); bcdclock[0] = inb(Pdata); + outb(Paddr, Minutes); bcdclock[1] = inb(Pdata); + outb(Paddr, Hours); bcdclock[2] = inb(Pdata); + outb(Paddr, Mday); bcdclock[3] = inb(Pdata); + outb(Paddr, Month); bcdclock[4] = inb(Pdata); + outb(Paddr, Year); bcdclock[5] = inb(Pdata); + + outb(Paddr, Status); + if((inb(Pdata) & 0x80) == 0) + break; + } + + /* + * convert from BCD + */ + rtc.sec = GETBCD(0); + rtc.min = GETBCD(1); + rtc.hour = GETBCD(2); + rtc.mday = GETBCD(3); + rtc.mon = GETBCD(4); + rtc.year = GETBCD(5); + + /* + * the world starts jan 1 1970 + */ + if(rtc.year < 70) + rtc.year += 2000; + else + rtc.year += 1900; + return rtc2sec(&rtc); +} + +static Lock nvrtlock; + +long +rtctime(void) +{ + int i; + long t, ot; + + ilock(&nvrtlock); + + /* loop till we get two reads in a row the same */ + t = _rtctime(); + for(i = 0; i < 100; i++){ + ot = t; + t = _rtctime(); + if(ot == t) + break; + } + if(i == 100) print("we are boofheads\n"); + + iunlock(&nvrtlock); + + return t; +} + +static long +rtcread(Chan* c, void* buf, long n, vlong off) +{ + ulong t; + char *a, *start; + ulong offset = off; + + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen); + + switch((ulong)c->qid.path){ + case Qrtc: + t = rtctime(); + n = readnum(offset, buf, n, t, 12); + return n; + case Qnvram: + if(n == 0) + return 0; + if(n > Nvsize) + n = Nvsize; + a = start = smalloc(n); + + ilock(&nvrtlock); + for(t = offset; t < offset + n; t++){ + if(t >= Nvsize) + break; + outb(Paddr, Nvoff+t); + *a++ = inb(Pdata); + } + iunlock(&nvrtlock); + + if(waserror()){ + free(start); + nexterror(); + } + memmove(buf, start, t - offset); + poperror(); + + free(start); + return t - offset; + } + error(Ebadarg); + return 0; +} + +#define PUTBCD(n,o) bcdclock[o] = (n % 10) | (((n / 10) % 10)<<4) + +static long +rtcwrite(Chan* c, void* buf, long n, vlong off) +{ + int t; + char *a, *start; + Rtc rtc; + ulong secs; + uchar bcdclock[Nbcd]; + char *cp, *ep; + ulong offset = off; + + if(offset!=0) + error(Ebadarg); + + + switch((ulong)c->qid.path){ + case Qrtc: + /* + * read the time + */ + cp = ep = buf; + ep += n; + while(cp < ep){ + if(*cp>='0' && *cp<='9') + break; + cp++; + } + secs = strtoul(cp, 0, 0); + + /* + * convert to bcd + */ + sec2rtc(secs, &rtc); + PUTBCD(rtc.sec, 0); + PUTBCD(rtc.min, 1); + PUTBCD(rtc.hour, 2); + PUTBCD(rtc.mday, 3); + PUTBCD(rtc.mon, 4); + PUTBCD(rtc.year, 5); + + /* + * write the clock + */ + ilock(&nvrtlock); + outb(Paddr, Seconds); outb(Pdata, bcdclock[0]); + outb(Paddr, Minutes); outb(Pdata, bcdclock[1]); + outb(Paddr, Hours); outb(Pdata, bcdclock[2]); + outb(Paddr, Mday); outb(Pdata, bcdclock[3]); + outb(Paddr, Month); outb(Pdata, bcdclock[4]); + outb(Paddr, Year); outb(Pdata, bcdclock[5]); + iunlock(&nvrtlock); + return n; + case Qnvram: + if(n == 0) + return 0; + if(n > Nvsize) + n = Nvsize; + + start = a = smalloc(n); + if(waserror()){ + free(start); + nexterror(); + } + memmove(a, buf, n); + poperror(); + + ilock(&nvrtlock); + for(t = offset; t < offset + n; t++){ + if(t >= Nvsize) + break; + outb(Paddr, Nvoff+t); + outb(Pdata, *a++); + } + iunlock(&nvrtlock); + + free(start); + return t - offset; + } + error(Ebadarg); + return 0; +} + +Dev rtcdevtab = { + 'r', + "rtc", + + devreset, + rtcinit, + devshutdown, + rtcattach, + rtcwalk, + rtcstat, + rtcopen, + devcreate, + rtcclose, + rtcread, + devbread, + rtcwrite, + devbwrite, + devremove, + devwstat, +}; + +#define SEC2MIN 60L +#define SEC2HOUR (60L*SEC2MIN) +#define SEC2DAY (24L*SEC2HOUR) + +/* + * days per month plus days/year + */ +static int dmsize[] = +{ + 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; +static int ldmsize[] = +{ + 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +/* + * return the days/month for the given year + */ +static int* +yrsize(int y) +{ + if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0)) + return ldmsize; + else + return dmsize; +} + +/* + * compute seconds since Jan 1 1970 + */ +static ulong +rtc2sec(Rtc *rtc) +{ + ulong secs; + int i; + int *d2m; + + secs = 0; + + /* + * seconds per year + */ + for(i = 1970; i < rtc->year; i++){ + d2m = yrsize(i); + secs += d2m[0] * SEC2DAY; + } + + /* + * seconds per month + */ + d2m = yrsize(rtc->year); + for(i = 1; i < rtc->mon; i++) + secs += d2m[i] * SEC2DAY; + + secs += (rtc->mday-1) * SEC2DAY; + secs += rtc->hour * SEC2HOUR; + secs += rtc->min * SEC2MIN; + secs += rtc->sec; + + return secs; +} + +/* + * compute rtc from seconds since Jan 1 1970 + */ +static void +sec2rtc(ulong secs, Rtc *rtc) +{ + int d; + long hms, day; + int *d2m; + + /* + * break initial number into days + */ + hms = secs % SEC2DAY; + day = secs / SEC2DAY; + if(hms < 0) { + hms += SEC2DAY; + day -= 1; + } + + /* + * generate hours:minutes:seconds + */ + rtc->sec = hms % 60; + d = hms / 60; + rtc->min = d % 60; + d /= 60; + rtc->hour = d; + + /* + * year number + */ + if(day >= 0) + for(d = 1970; day >= *yrsize(d); d++) + day -= *yrsize(d); + else + for (d = 1970; day < 0; d--) + day += *yrsize(d-1); + rtc->year = d; + + /* + * generate month + */ + d2m = yrsize(rtc->year); + for(d = 1; day >= d2m[d]; d++) + day -= d2m[d]; + rtc->mday = day + 1; + rtc->mon = d; + + return; +} + +uchar +nvramread(int addr) +{ + uchar data; + + ilock(&nvrtlock); + outb(Paddr, addr); + data = inb(Pdata); + iunlock(&nvrtlock); + + return data; +} + +void +nvramwrite(int addr, uchar data) +{ + ilock(&nvrtlock); + outb(Paddr, addr); + outb(Pdata, data); + iunlock(&nvrtlock); +} diff --git a/os/pc/devtv.c b/os/pc/devtv.c new file mode 100644 index 00000000..5e45fa37 --- /dev/null +++ b/os/pc/devtv.c @@ -0,0 +1,1826 @@ +/* + * Driver for Hauppage TV board + * + * Control commands: + * + * init + * window %d %d %d %d + * colorkey %d %d %d %d %d %d + * capture %d %d %d %d + * capbrightness %d + * capcontrast %d + * capsaturation %d + * caphue %d + * capbw %d + * brightness %d + * contrast %d + * saturation %d + * source %d + * svideo %d + * format %d + * channel %d %d + * signal + * volume %d [ %d ] + * bass %d + * treble %d + * freeze %d + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "tv.h" + +#include + +enum { + MemSize= 1, + MemAddr= 0xB8000, + + CompressReg= -14, + + /* smart lock registers */ + SLReg1= -2, + SLReg2= -1, + + /* the Bt812 registers */ + Bt812Index= -5, + Bt812Data= -6, + + Bt2VideoPresent= 0x40, + Bt4ColorBars= 0x40, + Bt5YCFormat= 0x80, + Bt7TriState= 0x0C, + + /* VxP 500 registers */ + Vxp500Index= 0, + Vxp500Data= 1, + + /* video controller registers */ + MemoryWindowBaseAddrA= 0x14, + MemoryWindowBaseAddrB= 0x15, + MemoryPageReg= 0x16, + MemoryConfReg= 0x18, + ISAControl= 0x30, + I2CControl= 0x34, + InputVideoConfA= 0x38, + InputVideoConfB= 0x39, + ISASourceWindowWidthA= 0x3A, + ISASourceWindowWidthB= 0x3B, + ISASourceWindowHeightA= 0x3C, + ISASourceWindowHeightB= 0x3D, + InputHorzCropLeftA= 0x40, + InputHorzCropLeftB= 0x41, + InputHorzCropRightA= 0x44, + InputHorzCropRightB= 0x45, + InputHorzCropTopA= 0x48, + InputHorzCropTopB= 0x49, + InputHorzCropBottomA= 0x4C, + InputHorzCropBottomB= 0x4D, + InputHorzFilter= 0x50, + InputHorzScaleControlA= 0x54, + InputHorzScaleControlB= 0x55, + InputVertInterpolControl= 0x58, + InputVertScaleControlA= 0x5C, + InputVertScaleControlB= 0x5D, + InputFieldPixelBufStatus= 0x64, + VideoInputFrameBufDepthA= 0x68, + VideoInputFrameBufDepthB= 0x69, + AcquisitionControl= 0x6C, + AcquisitionAddrA= 0x70, + AcquisitionAddrB= 0x71, + AcquisitionAddrC= 0x72, + VideoBufferLayoutControl= 0x73, + CaptureControl= 0x80, + CaptureViewPortAddrA= 0x81, + CaptureViewPortAddrB= 0x82, + CaptureViewPortAddrC= 0x83, + CaptureViewPortWidthA= 0x84, + CaptureViewPortWidthB= 0x85, + CaptureViewPortHeightA= 0x86, + CaptureViewPortHeightB= 0x87, + CapturePixelBufLow= 0x88, + CapturePixelBufHigh= 0x89, + CaptureMultiBufDepthA= 0x8A, + CaptureMultiBufDepthB= 0x8B, + DisplayControl= 0x92, + VGAControl= 0x94, + OutputProcControlA= 0x96, + OutputProcControlB= 0x97, + DisplayViewPortStartAddrA= 0xA0, + DisplayViewPortStartAddrB= 0xA1, + DisplayViewPortStartAddrC= 0xA2, + DisplayViewPortWidthA= 0xA4, + DisplayViewPortWidthB= 0xA5, + DisplayViewPortHeightA= 0xA6, + DisplayViewPortHeightB= 0xA7, + DisplayViewPortOrigTopA= 0xA8, + DisplayViewPortOrigTopB= 0xA9, + DisplayViewPortOrigLeftA= 0xAA, + DisplayViewPortOrigLeftB= 0xAB, + DisplayWindowLeftA= 0xB0, + DisplayWindowLeftB= 0xB1, + DisplayWindowRightA= 0xB4, + DisplayWindowRightB= 0xB5, + DisplayWindowTopA= 0xB8, + DisplayWindowTopB= 0xB9, + DisplayWindowBottomA= 0xBC, + DisplayWindowBottomB= 0xBD, + OutputVertZoomControlA= 0xC0, + OutputVertZoomControlB= 0xC1, + OutputHorzZoomControlA= 0xC4, + OutputHorzZoomControlB= 0xC5, + BrightnessControl= 0xC8, + ContrastControl= 0xC9, + SaturationControl= 0xCA, + VideoOutIntrStatus= 0xD3, + + /* smart lock bits */ + PixelClk= 0x03, + SmartLock= 0x00, + FeatureConnector= 0x01, + Divider= 0x02, + Window= 0x08, + KeyWindow= 0x0C, + HSyncLow= 0x20, + VSyncLow= 0x40, + + ClkBit= 0x01, + DataBit= 0x02, + HoldBit= 0x04, + SelBit= 0x08, + DivControl= 0x40, + + /* i2c bus control bits */ + I2C_Clock= 0x02, + I2C_Data= 0x08, + I2C_RdClock= 0x10, + I2C_RdData= 0x20, + I2C_RdData_D= 0x40, + + /* I2C bus addresses */ + Adr5249= 0x22, /* teletext decoder */ + Adr8444= 0x48, /* 6-bit DAC (TDA 8444) */ + Adr6300= 0x80, /* sound fader (TEA 6300) */ + Adr6320= 0x80, /* sound fader (TEA 6320T) */ + AdrTuner= 0xC0, + + /* Philips audio chips */ + TEA6300= 0, + TEA6320T= 1, + + /* input formats */ + NTSC_M = 0, + NTSC_443 = 1, + External = 2, + + NTSCCropLeft= 36, /* NTSC 3.6 usec */ + NTSCCropRight= 558, /* NTSC 55.8 usec */ + + /* color control indices */ + Vxp500Brightness= 1, + Vxp500Contrast= 2, + Vxp500Saturation= 3, + Bt812Brightness= 4, + Bt812Contrast= 5, + Bt812Saturation= 6, + Bt812Hue= 7, + Bt812BW= 8, + + /* board revision numbers */ + RevisionPP= 0, + RevisionA= 1, + HighQ= 2, + + /* VGA controller registers */ + VGAMiscOut= 0x3CC, + VGAIndex= 0x3D4, + VGAData= 0x3D5, + VGAHorzTotal= 0x00, +}; + +enum { + Qdir, + Qdata, + Qctl, +}; + +static +Dirtab tvtab[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "tv", {Qdata, 0}, 0, 0666, + "tvctl", {Qctl, 0}, 0, 0666, +}; + +static +int ports[] = { /* board addresses */ + 0x51C, 0x53C, 0x55C, 0x57C, + 0x59C, 0x5BC, 0x5DC, 0x5FC +}; + +/* + * Default settings, settings between 0..100 + */ +static +int defaults[] = { + Vxp500Brightness, 0, + Vxp500Contrast, 54, + Vxp500Saturation, 54, + Bt812Brightness, 13, + Bt812Contrast, 57, + Bt812Saturation, 51, + Bt812Hue, 0, + Bt812BW, 0, +}; + +static int port; +static int soundchip; +static int boardrev; +static int left, right; +static int vsync, hsync; +static ulong xtalfreq; +static ushort cropleft, cropright; +static ushort cropbottom, croptop; +static Rectangle window, capwindow; + +static void setreg(int, int); +static void setbt812reg(int, int); +static void videoinit(void); +static void createwindow(Rectangle); +static void setcontrols(int, uchar); +static void setcolorkey(int, int, int, int, int, int); +static void soundinit(void); +static void setvolume(int, int); +static void setbass(int); +static void settreble(int); +static void setsoundsource(int); +static void tunerinit(void); +static void settuner(int, int); +static void setvideosource(int); +static int waitvideosignal(void); +static void freeze(int); +static void setsvideo(int); +static void setinputformat(int); +static void enablevideo(void); +static void *saveframe(int *); + +static int +min(int a, int b) +{ + return a < b ? a : b; +} + +static int +max(int a, int b) +{ + return a < b ? b : a; +} + +static int +present(int port) +{ + outb(port+Vxp500Index, 0xAA); + if (inb(port+Vxp500Index) != 0xAA) + return 0; + outb(port+Vxp500Index, 0x55); + outb(port+Vxp500Data, 0xAA); + if (inb(port+Vxp500Index) != 0x55) + return 0; + if (inb(port+Vxp500Data) != 0xAA) + return 0; + outb(port+Vxp500Data, 0x55); + if (inb(port+Vxp500Index) != 0x55) + return 0; + if (inb(port+Vxp500Data) != 0x55) + return 0; + return 1; +} + +static int +getvsync(void) +{ + int vslow, vshigh, s; + ushort timo; + + s = splhi(); + + outb(port+Vxp500Index, VideoOutIntrStatus); + + /* wait for VSync to go high then low */ + for (timo = ~0; timo; timo--) + if (inb(port+Vxp500Data) & 2) break; + for (timo = ~0; timo; timo--) + if ((inb(port+Vxp500Data) & 2) == 0) break; + + /* count how long it stays low and how long it stays high */ + for (vslow = 0, timo = ~0; timo; timo--, vslow++) + if (inb(port+Vxp500Data) & 2) break; + for (vshigh = 0, timo = ~0; timo; timo--, vshigh++) + if ((inb(port+Vxp500Data) & 2) == 0) break; + splx(s); + + return vslow < vshigh; +} + +static int +gethsync(void) +{ + int hslow, hshigh, s; + ushort timo; + + s = splhi(); + + outb(port+Vxp500Index, VideoOutIntrStatus); + + /* wait for HSync to go high then low */ + for (timo = ~0; timo; timo--) + if (inb(port+Vxp500Data) & 1) break; + for (timo = ~0; timo; timo--) + if ((inb(port+Vxp500Data) & 1) == 0) break; + + /* count how long it stays low and how long it stays high */ + for (hslow = 0, timo = ~0; timo; timo--, hslow++) + if (inb(port+Vxp500Data) & 1) break; + for (hshigh = 0, timo = ~0; timo; timo--, hshigh++) + if ((inb(port+Vxp500Data) & 1) == 0) break; + splx(s); + + return hslow < hshigh; +} + +static void +tvinit(void) +{ + int i; + + for (i = 0, port = 0; i < nelem(ports); i++) { + if (present(ports[i])) { + port = ports[i]; + break; + } + } + if (i == nelem(ports)) + return; + + /* + * the following routines are the prefered way to + * find out the sync polarities. Unfortunately, it + * doesn't always work. + */ +#ifndef VSync + vsync = getvsync(); + hsync = gethsync(); +#else + vsync = VSync; + hsync = HSync; +#endif + left = right = 80; + soundinit(); + tunerinit(); + videoinit(); +} + +static Chan* +tvattach(char *spec) +{ + if (port == 0) + error(Enonexist); + return devattach('V', spec); +} + +static Walkqid* +tvwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, tvtab, nelem(tvtab), devgen); +} + +static int +tvstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, tvtab, nelem(tvtab), devgen); +} + +static Chan* +tvopen(Chan *c, int omode) +{ + return devopen(c, omode, tvtab, nelem(tvtab), devgen); +} + +static void +tvclose(Chan *) +{ +} + +static long +tvread(Chan *c, void *a, long n, vlong offset) +{ + static void *frame; + static int size; + + USED(offset); + + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, tvtab, nelem(tvtab), devgen); + case Qdata: + if (eqrect(capwindow, Rect(0, 0, 0, 0))) + error(Ebadarg); + if (offset == 0) + frame = saveframe(&size); + if (frame) { + if (n > size - offset) + n = size - offset; + memmove(a, (char *)frame + offset, n); + } else + error(Enovmem); + break; + default: + n=0; + break; + } + return n; +} + +static long +tvwrite(Chan *c, void *vp, long n, vlong offset) +{ + char buf[128], *field[10], *a; + int i, nf, source; + static Rectangle win; + static int hsize, size = 0; + static void *frame; + + USED(offset); + + a = vp; + switch((ulong)c->qid.path){ + case Qctl: + if (n > sizeof(buf)-1) + n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = '\0'; + + nf = getfields(buf, field, nelem(field), 1, " \t"); + if (nf < 1) error(Ebadarg); + + if (strcmp(field[0], "init") == 0) { + window = Rect(0, 0, 0, 0); + capwindow = Rect(0, 0, 0, 0); + source = 0; /* video 0 input */ + setvideosource(source); + left = right = 80; + setsoundsource(source); + for (i = 0; i < nelem(defaults); i += 2) + setcontrols(defaults[i], defaults[i+1]); + } else if (strcmp(field[0], "colorkey") == 0) { + if (nf < 7) error(Ebadarg); + setcolorkey(strtoul(field[1], 0, 0), strtoul(field[2], 0, 0), + strtoul(field[3], 0, 0), strtoul(field[4], 0, 0), + strtoul(field[5], 0, 0), strtoul(field[6], 0, 0)); + } else if (strcmp(field[0], "window") == 0) { + if (nf < 5) error(Ebadarg); + createwindow(Rect(strtoul(field[1], 0, 0), strtoul(field[2], 0, 0), + strtoul(field[3], 0, 0), strtoul(field[4], 0, 0))); + setvolume(left, right); + } else if (strcmp(field[0], "capture") == 0) { + if (nf < 5) error(Ebadarg); + capwindow = Rect(strtoul(field[1], 0, 0), strtoul(field[2], 0, 0), + strtoul(field[3], 0, 0), strtoul(field[4], 0, 0)); + } else if (strcmp(field[0], "freeze") == 0) { + if (nf < 2) error(Ebadarg); + freeze(strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "capbrightness") == 0) { + if (nf < 2) error(Ebadarg); + setcontrols(Bt812Brightness, strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "capcontrast") == 0) { + if (nf < 2) error(Ebadarg); + setcontrols(Bt812Contrast, strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "capsaturation") == 0) { + if (nf < 2) error(Ebadarg); + setcontrols(Bt812Saturation, strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "caphue") == 0) { + if (nf < 2) error(Ebadarg); + setcontrols(Bt812Hue, strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "capbw") == 0) { + if (nf < 2) error(Ebadarg); + setcontrols(Bt812BW, strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "brightness") == 0) { + if (nf < 2) error(Ebadarg); + setcontrols(Vxp500Brightness, strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "contrast") == 0) { + if (nf < 2) error(Ebadarg); + setcontrols(Vxp500Contrast, strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "saturation") == 0) { + if (nf < 2) error(Ebadarg); + setcontrols(Vxp500Saturation, strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "source") == 0) { + if (nf < 2) error(Ebadarg); + source = strtoul(field[1], 0, 0); + setvideosource(source); + setsoundsource(source); + } else if (strcmp(field[0], "svideo") == 0) { + if (nf < 2) error(Ebadarg); + setsvideo(strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "format") == 0) { + if (nf < 2) error(Ebadarg); + setinputformat(strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "channel") == 0) { + if (nf < 3) error(Ebadarg); + setvolume(0, 0); + settuner(strtoul(field[1], 0, 0), strtoul(field[2], 0, 0)); + tsleep(&up->sleep, return0, 0, 300); + setvolume(left, right); + } else if (strcmp(field[0], "signal") == 0) { + if (!waitvideosignal()) + error(Etimedout); + } else if (strcmp(field[0], "volume") == 0) { + if (nf < 2) error(Ebadarg); + left = strtoul(field[1], 0, 0); + if (nf < 3) + right = left; + else + right = strtoul(field[2], 0, 0); + setvolume(left, right); + } else if (strcmp(field[0], "bass") == 0) { + if (nf < 2) error(Ebadarg); + setbass(strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "treble") == 0) { + if (nf < 2) error(Ebadarg); + settreble(strtoul(field[1], 0, 0)); + } else + error(Ebadctl); + break; + default: + error(Ebadusefd); + } + return n; +} + + +Dev tvdevtab = { + 'V', + "tv", + + devreset, + tvinit, + devshutdown, + tvattach, + tvwalk, + tvstat, + tvopen, + devcreate, + tvclose, + tvread, + devbread, + tvwrite, + devbwrite, + devremove, + devwstat, +}; + +static void +setreg(int index, int data) +{ + outb(port+Vxp500Index, index); + outb(port+Vxp500Data, data); +} + +static unsigned int +getreg(int index) +{ + outb(port+Vxp500Index, index); + return inb(port+Vxp500Data); +} + +/* + * I2C routines + */ +static void +delayi2c(void) +{ + int i, val; + + /* delay for 4.5 usec to guarantee clock time */ + for (i = 0; i < 75; i++) { /* was 50 */ + val = inb(port+Vxp500Data); + USED(val); + } +} + +static int +waitSDA(void) +{ + ushort timo; + + /* wait for i2c clock to float high */ + for (timo = ~0; timo; timo--) + if (inb(port+Vxp500Data) & I2C_RdData) + break; + if (!timo) print("devtv: waitSDA fell out of loop\n"); + return !timo; +} + +static int +waitSCL(void) +{ + ushort timo; + + /* wait for i2c clock to float high */ + for (timo = ~0; timo; timo--) + if (inb(port+Vxp500Data) & I2C_RdClock) + break; + delayi2c(); + if (!timo) print("devtv: waitSCL fell out of loop\n"); + return !timo; +} + +static int +seti2cdata(int data) +{ + int b, reg, val; + int error; + + error = 0; + reg = inb(port+Vxp500Data); + for (b = 0x80; b; b >>= 1) { + if (data & b) + reg |= I2C_Data; + else + reg &= ~I2C_Data; + outb(port+Vxp500Data, reg); + reg |= I2C_Clock; + outb(port+Vxp500Data, reg); + error |= waitSCL(); + reg &= ~I2C_Clock; + outb(port+Vxp500Data, reg); + delayi2c(); + } + reg |= I2C_Data; + outb(port+Vxp500Data, reg); + reg |= I2C_Clock; + outb(port+Vxp500Data, reg); + error |= waitSCL(); + val = inb(port+Vxp500Data); + USED(val); + reg &= ~I2C_Clock; + outb(port+Vxp500Data, reg); + delayi2c(); + return error; +} + +static int +seti2creg(int id, int index, int data) +{ + int reg, error; + + error = 0; + /* set i2c control register to enable i2c clock and data lines */ + setreg(I2CControl, I2C_Data|I2C_Clock); + error |= waitSDA(); + error |= waitSCL(); + outb(port+Vxp500Data, I2C_Clock); + delayi2c(); + outb(port+Vxp500Data, 0); + delayi2c(); + + error |= seti2cdata(id); + error |= seti2cdata(index); + error |= seti2cdata(data); + + reg = inb(port+Vxp500Data); + reg &= ~I2C_Data; + outb(port+Vxp500Data, reg); + reg |= I2C_Clock; + outb(port+Vxp500Data, reg); + error |= waitSCL(); + reg |= I2C_Data; + outb(port+Vxp500Data, reg); + error |= waitSDA(); + return error; +} + +static int +seti2cregs(int id, int index, int n, uchar *data) +{ + int reg, error; + + error = 0; + /* set i2c control register to enable i2c clock and data lines */ + setreg(I2CControl, I2C_Data|I2C_Clock); + error |= waitSDA(); + error |= waitSCL(); + outb(port+Vxp500Data, I2C_Clock); + delayi2c(); + outb(port+Vxp500Data, 0); + delayi2c(); + + /* send data */ + error |= seti2cdata(id); + error |= seti2cdata(index); + while (n--) + error |= seti2cdata(*data++); + + /* send stop */ + reg = inb(port+Vxp500Data); + reg &= ~I2C_Data; + outb(port+Vxp500Data, reg); + reg |= I2C_Clock; + outb(port+Vxp500Data, reg); + error |= waitSCL(); + reg |= I2C_Data; + outb(port+Vxp500Data, reg); + error |= waitSDA(); + return error; +} + +/* + * Audio routines + */ +static void +setvolume(int left, int right) +{ + int vol, loudness = 0; + + if (soundchip == TEA6300) { + seti2creg(Adr6300, 0, (63L * left) / 100); + seti2creg(Adr6300, 1, (63L * right) / 100); + vol = (15L * max(left, right)) / 100; + seti2creg(Adr6300, 4, 0x30 | vol); + } else { + vol = (63L * max(left, right)) / 100; + seti2creg(Adr6320, 0, vol | (loudness << 6)); + seti2creg(Adr6320, 1, (63L * right) / 100); + seti2creg(Adr6320, 2, (63L * left) / 100); + } +} + +static void +setbass(int bass) +{ + if (soundchip == TEA6300) + seti2creg(Adr6300, 2, (15L * bass) / 100); + else + seti2creg(Adr6320, 5, max((31L * bass) / 100, 4)); +} + +static void +settreble(int treble) +{ + if (soundchip == TEA6300) + seti2creg(Adr6300, 3, (15L * treble) / 100); + else + seti2creg(Adr6320, 6, max((31L * treble) / 100, 7)); + +} + +static void +setsoundsource(int source) +{ + if (soundchip == TEA6300) + seti2creg(Adr6300, 5, 1 << source); + else + seti2creg(Adr6320, 7, source); + setbass(50); + settreble(50); + setvolume(left, right); +} + +static void +soundinit(void) +{ + if (seti2creg(Adr6320, 7, 0) && seti2creg(Adr6300, 4, 0)) + print("devtv: Audio init failed\n"); + + soundchip = AudioChip; + setvolume(0, 0); +} + +/* + * Tuner routines + */ +static +long hrcfreq[] = { /* HRC CATV frequencies */ + 0, 7200, 5400, 6000, 6600, 7800, 8400, 17400, + 18000, 18600, 19200, 19800, 20400, 21000, 12000, 12600, + 13200, 13800, 14400, 15000, 15600, 16200, 16800, 21600, + 22200, 22800, 23400, 24000, 24600, 25200, 25800, 26400, + 27000, 27600, 28200, 28800, 29400, 30000, 30600, 31200, + 31800, 32400, 33000, 33600, 34200, 34800, 35400, 36000, + 36600, 37200, 37800, 38400, 39000, 39600, 40200, 40800, + 41400, 42000, 42600, 43200, 43800, 44400, 45000, 45600, + 46200, 46800, 47400, 48000, 48600, 49200, 49800, 50400, + 51000, 51600, 52200, 52800, 53400, 54000, 54600, 55200, + 55800, 56400, 57000, 57600, 58200, 58800, 59400, 60000, + 60600, 61200, 61800, 62400, 63000, 63600, 64200, 9000, + 9600, 10200, 10800, 11400, 64800, 65400, 66000, 66600, + 67200, 67800, 68400, 69000, 69600, 70200, 70800, 71400, + 72000, 72600, 73200, 73800, 74400, 75000, 75600, 76200, + 76800, 77400, 78000, 78600, 79200, 79800, +}; + +static void +settuner(int channel, int finetune) +{ + static long lastfreq; + uchar data[3]; + long freq; + int cw2, n, sa; + + if (channel < 0 || channel > nelem(hrcfreq)) + error(Ebadarg); + + freq = hrcfreq[channel]; + + /* these settings are all for (FS936E) USA Tuners */ + if (freq < 16025) /* low band */ + cw2 = 0xA4; + else if (freq < 45425) /* mid band */ + cw2 = 0x94; + else + cw2 = 0x34; + + /* + * Channels are stored are 1/100 MHz resolutions, but + * the tuner wants stuff in MHZ, so divide by 100, we + * then have to shift by 4 to get the prog. div. value + */ + n = ((freq + 4575L) * 16) / 100L + finetune; + + if (freq > lastfreq) { + sa = (n >> 8) & 0xFF; + data[0] = n & 0xFF; + data[1] = 0x8E; + data[2] = cw2; + } else { + sa = 0x8E; + data[0] = cw2; + data[1] = (n >> 8) & 0xFF; + data[2] = n & 0xFF; + } + lastfreq = freq; + seti2cregs(AdrTuner, sa, 3, data); +} + +static void +tunerinit(void) +{ + if (seti2creg(AdrTuner, 0, 0)) + print("devtv: Tuner init failed\n"); +} + +/* + * Video routines + */ +static int slreg1 = 0; +static int slreg2 = 0; +static int vcogain = 0; +static int phdetgain = 2; +static int plln1 = 2; +static int pllp2 = 1; + +static void +waitforretrace(void) +{ + ushort timo; + + for (timo = ~0; (getreg(VideoOutIntrStatus) & 2) == 0 && timo; timo--) + /* wait for VSync inactive */; + for (timo = ~0; (getreg(VideoOutIntrStatus) & 2) && timo; timo--) + /* wait for VSync active */; +} + +static void +updateshadowregs(void) +{ + int val; + + setreg(InputVideoConfA, getreg(InputVideoConfA) | 0x40); + val = getreg(OutputProcControlB); + setreg(OutputProcControlB, val & 0x7F); + setreg(OutputProcControlB, val | 0x80); +} + +static void +setvgareg(int data) +{ + /* set HSync & VSync first, to make sure VSync works properly */ + setreg(VGAControl, (getreg(VGAControl) & ~0x06) | (data & 0x06)); + + /* wait for VSync and set the whole register */ + waitforretrace(); + setreg(VGAControl, data); +} + +static void +setbt812reg(int index, int data) +{ + outb(port+Bt812Index, index); + outb(port+Bt812Data, data); +} + +static int +getbt812reg(int index) +{ + outb(port+Bt812Index, index); + return inb(port+Bt812Data); +} + +static void +setbt812regpair(int index, ushort data) +{ + outb(port+Bt812Index, index); + outb(port+Bt812Data, data); + outb(port+Bt812Data, data >> 8); +} + +static void +setvideosource(int source) +{ + int s; + + source &= 7; + s = source & 3; + setbt812reg(0, ((s << 2) | s) << 3); + s = (source & 4) << 4; + setbt812reg(4, (getbt812reg(4) & ~Bt4ColorBars) | s); +} + +static void +setsvideo(int enable) +{ + if (enable) + setbt812reg(5, getbt812reg(5) | Bt5YCFormat); + else + setbt812reg(5, getbt812reg(5) & ~Bt5YCFormat); +} + +static int +waitvideosignal(void) +{ + ushort timo; + + for (timo = ~0; timo; timo--) + if (getbt812reg(2) & Bt2VideoPresent) + return 1; + return 0; +} + +/* + * ICS1572 Programming Configuration + * + * R = 1 + * M = x + * A = x + * N1 = 4 + * N2 = internal divide ratio + */ +static +uchar ICSbits[7] = { + 0x01, /* bits 8 - 1 00000001 */ + 0x05, /* bits 16 - 9 00000101 */ + 0xFF, /* bits 24 - 17 11111111 */ + 0x8C, /* bits 32 - 25 10001100 */ + 0xBF, /* bits 40 - 33 10111111 */ + 0x00, /* bits 48 - 41 00000000 */ + 0x00, /* bits 56 - 49 00000000 */ +}; + +static void +sendbit(int val, int hold) +{ + slreg2 &= ~(HoldBit|DataBit|ClkBit); + if (val) slreg2 |= DataBit; + if (hold) slreg2 |= HoldBit; + outb(port+SLReg2, slreg2); + outb(port+SLReg2, slreg2|ClkBit); + outb(port+SLReg2, slreg2); +} + +static void +load1572(int select) +{ + int reg; + uchar mask; + + if (select) + slreg2 |= SelBit; + else + slreg2 &= ~SelBit; + outb(port+SLReg2, slreg2); + + for (reg = 0; reg < sizeof(ICSbits); reg++) { + for (mask = 1; mask != 0; mask <<= 1) { + if (reg == sizeof(ICSbits)-1 && mask == 0x80) { + sendbit(ICSbits[reg] & mask, 1); + } else + sendbit(ICSbits[reg] & mask, 0); + } + } +} + +static void +smartlockdiv(int count, int vcogain, int phdetgain, int n1, int p2) +{ + int extdiv, intdiv; + int nslreg2, external; + + nslreg2 = slreg2; + extdiv = ((count - 1) / 512) + 1; + intdiv = (count / extdiv); + nslreg2 &= ~0xC0; + switch (extdiv) { + case 1: external = 0; break; + case 2: external = 1; break; + case 3: external = 1; nslreg2 |= 0x40; break; + case 4: external = 1; nslreg2 |= 0x80; break; + default: return; + } + if ((slreg1 & PixelClk) == 0) { + slreg2 = nslreg2; + outb(port+SLReg2, slreg2); + } + + /* set PLL divider */ + ICSbits[0] &= ~0x07; + ICSbits[0] |= n1 & 0x07; + ICSbits[3] &= ~0xB7; + ICSbits[3] |= vcogain & 0x07; + ICSbits[3] |= (phdetgain & 0x03) << 4; + ICSbits[3] |= p2 << 7; + if (external) + ICSbits[1] |= 0x04; /* set EXTFBKEN */ + else + ICSbits[1] &= ~0x04; /* clear EXTFBKEN */ + intdiv--; + ICSbits[2] = intdiv; /* set N2 */ + ICSbits[3] &= ~ 0x08; + ICSbits[3] |= (intdiv >> 5) & 0x08; + load1572(1); +} + +static void +disablecolorkey(void) +{ + setreg(DisplayControl, getreg(DisplayControl) & 0xFE); + updateshadowregs(); +} + +static +uchar colorkeylimit[6] = { + 15, /* upper limit green */ + 255, /* lower limit green */ + 63, /* upper limit red */ + 63, /* upper limit blue */ + 15, /* lower limit red */ + 15, /* lower limit blue */ +}; + +static void +enablecolorkey(int enable) +{ + int i; + + if (enable) { + for (i = 0; i < 6; i++) + seti2creg(Adr8444, 0xF0 | i, colorkeylimit[i]); + slreg1 &= ~0x1C; + if (colorkeylimit[4] == 255) + slreg1 |= 0x04; /* disable red lower limit */ + if (colorkeylimit[1] == 255) + slreg1 |= 0x08; /* disable green lower limit */ + if (colorkeylimit[5] == 255) + slreg1 |= 0x10; /* disable blue lower limit */ + } else { + for (i = 0; i < 6; i++) + seti2creg(Adr8444, 0xF0 | i, 63); + slreg1 |= 0x1C; + } + outb(port+SLReg1, slreg1); + disablecolorkey(); +} + +static void +setcolorkey(int rl, int rh, int gl, int gh, int bl, int bh) +{ + colorkeylimit[0] = gh; + colorkeylimit[1] = gl; + colorkeylimit[2] = rh; + colorkeylimit[3] = bh; + colorkeylimit[4] = rl; + colorkeylimit[5] = bl; + enablecolorkey(1); +} + +static void +waitvideoframe(void) +{ + ushort timo; + int val; + + /* clear status bits and wait for start of an even field */ + val = getreg(InputFieldPixelBufStatus); + USED(val); + for (timo = ~0; timo; timo--) + if ((getreg(InputFieldPixelBufStatus) & 2) == 0) + break; + if (!timo) print("devtv: Wait for video frame failed\n"); +} + +static void +freeze(int enable) +{ + ushort timo; + int reg; + + if (enable) { + waitvideoframe(); + waitvideoframe(); + + setreg(InputVideoConfB, getreg(InputVideoConfB) | 0x08); + updateshadowregs(); + + for (timo = ~0; timo; timo--) + if (getreg(InputVideoConfB) & 0x80) break; + waitvideoframe(); + + reg = getreg(OutputProcControlB); + if ((reg & 0x20) == 0) { + setreg(ISAControl, 0x80); + setreg(OutputProcControlB, getreg(OutputProcControlB) | 0x20); + setreg(ISAControl, 0x42); + + reg = getreg(OutputProcControlB); + setreg(OutputProcControlB, reg & 0x7F); + setreg(OutputProcControlB, reg | 0x80); + } + } else { + setreg(InputVideoConfB, getreg(InputVideoConfB) & ~0x08); + updateshadowregs(); + + for (timo = ~0; timo; timo--) + if (getreg(InputVideoConfB) & 0x40) break; + waitvideoframe(); + reg = getreg(InputFieldPixelBufStatus); + USED(reg); + } +} + +static void +enablevideo(void) +{ + setreg(DisplayControl, 0x04); + updateshadowregs(); +} + +static void +disablevideo(void) +{ + setreg(DisplayControl, 0x18); + updateshadowregs(); +} + +static +uchar vxp500init[] = { /* video register initialization in (index,data) hex pairs */ + 0x30, 0x82, 0x39, 0x40, 0x58, 0x0C, 0x73, 0x02, 0x80, 0x00, 0x25, 0x0F, + 0x26, 0x0F, 0x38, 0x46, 0x30, 0x03, 0x12, 0x3B, 0x97, 0x20, 0x13, 0x00, + 0x14, 0x34, 0x15, 0x04, 0x16, 0x00, 0x17, 0x53, 0x18, 0x04, 0x19, 0x62, + 0x1C, 0x00, 0x1D, 0x00, 0x34, 0x3A, 0x38, 0x06, 0x3A, 0x00, 0x3B, 0x00, + 0x3C, 0x00, 0x3D, 0x00, 0x40, 0x40, 0x41, 0x40, 0x44, 0xFF, 0x45, 0xFF, + 0x48, 0x40, 0x49, 0x40, 0x4C, 0xFF, 0x4D, 0xFF, 0x50, 0xF0, 0x54, 0x30, + 0x55, 0x00, 0x5C, 0x04, 0x5D, 0x00, 0x60, 0x00, 0x68, 0x00, 0x69, 0x00, + 0x6C, 0x06, 0x6D, 0x00, 0x70, 0x00, 0x71, 0x00, 0x72, 0x00, 0x78, 0x01, + 0x79, 0x0C, 0x80, 0x10, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, + 0x85, 0x00, 0x86, 0x00, 0x87, 0x00, 0x88, 0x04, 0x89, 0x10, 0x8A, 0x00, + 0x8B, 0x00, 0x90, 0x05, 0x91, 0x0C, 0x92, 0x18, 0x93, 0x00, 0x96, 0x18, + 0x9A, 0x30, 0x9C, 0x2D, 0x9D, 0x00, 0xA0, 0x00, 0xA1, 0x00, 0xA2, 0x00, + 0xA4, 0x50, 0xA5, 0x50, 0xA6, 0xF0, 0xA7, 0xF0, 0xA8, 0x19, 0xA9, 0x18, + 0xAA, 0x64, 0xAB, 0x64, 0xB0, 0x64, 0xB1, 0x64, 0xB4, 0xA4, 0xB5, 0xA5, + 0xB8, 0x19, 0xB9, 0x18, 0xBC, 0x09, 0xBD, 0x09, 0xC0, 0x00, 0xC1, 0x02, + 0xC4, 0x00, 0xC5, 0x00, 0xC8, 0x00, 0xC9, 0x08, 0xCA, 0x08, 0xCE, 0x00, + 0xCF, 0x00, 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD8, 0x00, 0xD9, 0x00, + 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0x38, 0x46, 0x97, 0xA0, + 0x97, 0x20, 0x97, 0xA0, +}; + +static +uchar bt812init[] = { /* bt812 initializations */ + 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0xC0, + 0x04, 0x08, 0x05, 0x00, 0x06, 0x40, 0x07, 0x00, 0x08, 0x10, + 0x09, 0x90, 0x0A, 0x80, 0x0B, 0x00, 0x0C, 0x0C, 0x0D, 0x03, + 0x0E, 0x66, 0x0F, 0x00, 0x10, 0x80, 0x11, 0x02, 0x12, 0x16, + 0x13, 0x00, 0x14, 0xE5, 0x15, 0x01, 0x16, 0xAB, 0x17, 0xAA, + 0x18, 0x12, 0x19, 0x51, 0x1A, 0x46, 0x1B, 0x00, 0x1C, 0x00, + 0x1D, 0x37, +}; + +static ushort actpixs = 720; +static ulong Hdesired = 13500000L; + +/* NTSC-M NTSC-443 EXTERNAL */ +static ushort horzfreq[] = { 15734, 15625, 0 }; +static ushort Vdelay[] = { 22, 25, 25 }; +static ushort s2b[] = { 90, 90, 0 }; +static ushort actlines[] = { 485, 485, 575 }; +static ulong subcarfreq[] = { 3579545, 4433619, 4433619 }; + +static +unsigned int framewidth[5][4] = { + 1024, 512, 512, 512, /* mode 0 - single, double, single, quad */ + 1536, 768, 768, 384, /* mode 1 - single, double, single, quad */ + 2048, 1024, 1024, 512, /* mode 2 - single, double, single, quad */ + 1024, 512, 512, 512, /* mode 3 - single, double, single, quad */ + 1536, 768, 768, 384 /* mode 4 - single, double, single, quad */ +}; + +static +unsigned int frameheight[5][4] = { + 512, 512, 1024, 512, /* mode 0 - single, double, single, quad */ + 512, 512, 1024, 512, /* mode 1 - single, double, single, quad */ + 512, 512, 1024, 512, /* mode 2 - single, double, single, quad */ + 512, 512, 1024, 512, /* mode 3 - single, double, single, quad */ + 512, 512, 1024, 256 /* mode 4 - single, double, single, quad */ +}; + +static +uchar horzfilter[] = { 3, 3, 2, 2, 1, 1, 0, 0 }; + +static +uchar interleave[] = { 2, 3, 4, 2, 3 }; + +#define ADJUST(n) (((n) * hrsmult + hrsdiv - 1) / hrsdiv) + +static int q = 100; +static int ilv = 2; +static int hrsmult = 1; +static int hrsdiv = 2; + +static ushort panmask[] = { 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE }; + + +static void +cropwindow(int left, int right, int top, int bottom) +{ + top &= 0x3FE; + bottom &= 0x3FE; + setreg(InputHorzCropLeftA, left); + setreg(InputHorzCropLeftB, left >> 8); + setreg(InputHorzCropRightA, right); + setreg(InputHorzCropRightB, right >> 8); + setreg(InputHorzCropTopA, top); + setreg(InputHorzCropTopB, top >> 8); + setreg(InputHorzCropBottomA, bottom); + setreg(InputHorzCropBottomB, bottom >> 8); +} + +static void +setinputformat(int format) +{ + ushort hclock, hclockdesired; + ulong subcarrier; + int cr7; + + cr7 = getbt812reg(7) & ~Bt7TriState; + if (format == External) + cr7 |= Bt7TriState; + setbt812reg(7, cr7); + setbt812reg(5, getbt812reg(5) & 2); + + hclock = (xtalfreq >> 1) / horzfreq[format]; + setbt812regpair(0x0C, hclock); + setbt812regpair(0x0E, + (ushort)(s2b[format] * (Hdesired / 10) / 1000000L) | 1); + setbt812regpair(0x10, actpixs); + setbt812regpair(0x12, Vdelay[format]); + setbt812regpair(0x14, actlines[format]); + + subcarrier = (ulong) + ((((long long)subcarfreq[format] * 0x1000000) / xtalfreq + 1) / 2); + setbt812regpair(0x16, (int)(subcarrier & 0xFFFF)); /* subcarrier */ + setbt812reg(0x18, (int)(subcarrier >> 16)); + + setbt812reg(0x19, (uchar)(((xtalfreq / 200) * 675) / 1000000L + 8)); + setbt812reg(0x1A, (uchar)((xtalfreq * 65) / 20000000L - 10)); + hclockdesired = (ushort) (Hdesired / horzfreq[format]); + setbt812regpair(0x1B, + (ushort)(((hclock - hclockdesired) * 65536L) / hclockdesired)); +} + +static ushort +vgadivider(void) +{ + ushort horztotal; + + outb(VGAIndex, VGAHorzTotal); + horztotal = (inb(VGAData) << 3) + 40; + if (horztotal > ScreenWidth && horztotal < ((ScreenWidth * 3 ) / 2)) + return horztotal; + else + return (ScreenWidth * 5) / 4; +} + +static void +videoinit(void) +{ + int i, reg, width, tuner; + + /* early PLL smart lock initialization */ + if (ScreenWidth == 640) { + slreg1 = Window|HSyncLow|VSyncLow; + slreg2 = 0x0D; + } else { + slreg1 = Window; + slreg2 = 0x0C; + } + outb(port+CompressReg, 2); + outb(port+SLReg1, slreg1); + outb(port+SLReg2, slreg2); + smartlockdiv((vgadivider() * hrsmult)/hrsdiv, vcogain, phdetgain, 2, 1); + + /* program the VxP-500 chip (disables video) */ + waitforretrace(); + for (i = 0; i < sizeof(vxp500init); i += 2) + setreg(vxp500init[i], vxp500init[i+1]); + + /* set memory base for frame capture */ + setreg(MemoryWindowBaseAddrA, MemAddr >> 14); + setreg(MemoryWindowBaseAddrB, ((MemAddr >> 22) & 3) | (MemSize << 2)); + setreg(MemoryPageReg, 0); + + /* generic 422 decoder, mode 3 and 4 */ + setreg(MemoryConfReg, ilv+1); + + setreg(AcquisitionAddrA, 0); + setreg(AcquisitionAddrB, 0); + setreg(AcquisitionAddrC, 0); + + /* program VxP-500 for correct sync polarity */ + reg = ScreenWidth > 1023 ? 0x01 : 0x00; + reg |= (vsync << 1) | (hsync << 2); + setvgareg(reg); + setreg(VGAControl, reg); + + setreg(VideoBufferLayoutControl, 0); /* for ilv = 2 */ + + /* set sync polarities to get proper blanking */ + if (vsync) + slreg1 |= VSyncLow; + if (!hsync) { + slreg1 ^= HSyncLow; + setreg(VGAControl, reg | 4); + } + outb(port+SLReg1, slreg1); + + if ((slreg1 & PixelClk) == 0) { /* smart lock active */ + enablecolorkey(1); + setreg(VGAControl, getreg(VGAControl) & 6); + } else + enablecolorkey(0); + + /* color key initializations */ + if ((slreg1 & PixelClk) == 0) + setreg(VGAControl, getreg(VGAControl) & 7); + + /* initialize Bt812 */ + for (i = 0; i < sizeof(bt812init); i += 2) + setbt812reg(bt812init[i], bt812init[i+1]); + + /* figure out clock source (Xtal or Oscillator) and revision */ + setbt812reg(6, 0x40); + reg = getreg(InputFieldPixelBufStatus) & 3; + if ((getreg(InputFieldPixelBufStatus) & 3) == reg) { + /* crystal - could be revision PP if R34 is installed */ + setbt812reg(6, 0x00); + reg = inb(port+SLReg1); + if (reg & 0x20) { + if ((reg & 0xE0) == 0xE0) + boardrev = HighQ; + else + boardrev = RevisionA; + } else + boardrev = RevisionPP; + } else /* revision A or newer with 27 MHz oscillator */ + boardrev = RevisionA; + + /* figure out xtal frequency */ + if (xtalfreq == 0) { + if (boardrev == RevisionPP) { + tuner = (inb(port+SLReg1) >> 6) & 3; + if (tuner == 0) /* NTSC */ + xtalfreq = 24545400L; + else + xtalfreq = 29500000L; + } else if (boardrev == HighQ) + xtalfreq = 29500000L; + else + xtalfreq = 27000000L; + } + +// print("Hauppage revision %d (xtalfreq %ld)\n", boardrev, xtalfreq); + + /* on RevPP boards set early sync, on rev A and newer clear it */ + if (boardrev == RevisionPP) + setreg(InputVideoConfA, getreg(InputVideoConfA) | 4); + else + setreg(InputVideoConfA, getreg(InputVideoConfA) & ~4); + + switch (xtalfreq) { + case 24545400L: + actpixs = 640; + break; + case 29500000L: + actpixs = 768; + break; + default: + actpixs = 720; + break; + } + + /* set crop window (these values are for NTSC!) */ + if (boardrev == RevisionPP) { + Hdesired = xtalfreq / 2; + cropleft = (NTSCCropLeft * ((Hdesired / 10))) / 1000000L; + cropright = (NTSCCropRight * ((Hdesired / 10))) / 1000000L; + } else { + cropleft = actpixs / 100; + cropright = actpixs - cropleft; + } + width = ((cropright - cropleft + ilv) / ilv) * ilv; + cropright = cropleft + width + 1; + croptop = 26; + cropbottom = 505; + cropwindow(cropleft, cropright, croptop, cropbottom); + + /* set input format */ + setinputformat(NTSC_M); + setsvideo(0); +} + +static void +panwindow(Point p) +{ + int memmode, ilv, frw; + ulong pos; + + memmode = getreg(MemoryConfReg) & 7; + ilv = interleave[memmode]; + frw = framewidth[memmode][getreg(VideoBufferLayoutControl) & 3]; + + pos = (p.y * (frw/ilv)) + ((p.x/ilv) & panmask[memmode]); + setreg(DisplayViewPortStartAddrA, (uchar) pos); + setreg(DisplayViewPortStartAddrB, (uchar) (pos >> 8)); + setreg(DisplayViewPortStartAddrC, (uchar) (pos >> 16) & 0x03); + updateshadowregs(); +} + +static int +testqfactor(void) +{ + ulong timo; + int reg; + + waitvideoframe(); + for (reg = 0, timo = ~0; timo; timo--) { + reg |= getreg(InputFieldPixelBufStatus); + if (reg & 0xE) break; + } + if (reg & 0xC) return 0; + + waitvideoframe(); + for (reg = 0, timo = ~0; timo; timo--) { + reg |= getreg(InputFieldPixelBufStatus); + if (reg & 0xE) break; + } + return (reg & 0xC) == 0; +} + +static void +newwindow(Rectangle r) +{ + unsigned ww, wh, dx, dy, xs, ys, xe, ye; + unsigned scalex, scaley; + int frwidth, frheight, vidwidth, vidheight; + int memmode, layout; + int width, height; + int filter, changed, val; + + changed = r.min.x != window.min.x || r.min.y != window.min.y || + r.max.x != window.max.x || r.max.y != window.max.y; + if (changed) window = r; + + if (r.min.x < 0) r.min.x = 0; + if (r.max.x > ScreenWidth) r.max.x = ScreenWidth; + if (r.min.y < 0) r.min.y = 0; + if (r.max.y > ScreenHeight) r.max.y = ScreenHeight; + + if ((dx = r.max.x - r.min.x) <= 0) dx = 1; + if ((dy = r.max.y - r.min.y) <= 0) dy = 1; + + wh = dy; + ww = dx = ADJUST(dx); + r.min.x = (r.min.x * hrsmult) / hrsdiv; + + memmode = getreg(MemoryConfReg) & 7; + layout = getreg(VideoBufferLayoutControl) & 3; + vidwidth = cropright - cropleft + 1; + vidheight = (cropbottom & 0x3FE) - (croptop & 0x3FE) + 1; + frwidth = min(framewidth[memmode][layout], vidwidth); + frheight = min(frameheight[memmode][layout], vidheight); + + /* round up scale width to nearest multiple of interleave factor */ + dx = ((ulong)dx * q) / 100; + dx = ilv * ((dx + ilv - 1) / ilv); + + scalex = (((ulong)dx * 1024L) + vidwidth - 2) / (vidwidth - 1); + if (dy > frheight) dy = frheight - 1; + scaley = (((ulong)dy * 1024L) + vidheight - 2) / (vidheight - 1); + + setreg(InputHorzScaleControlA, (scalex << 6) & 0xC0); + setreg(InputHorzScaleControlB, (scalex >> 2) & 0xFF); + setreg(InputVertScaleControlA, (scaley << 6) & 0xC0); + setreg(InputVertScaleControlB, (scaley >> 2) & 0xFF); + + /* turn on horizontal filtering if we are scaling down */ + setreg(InputHorzFilter, horzfilter[((scalex - 1) >> 7) & 7]); + + /* set vertical interpolation */ + filter = scaley > 512 ? (ScreenWidth == 640 ? 0x44 : 0xC5) : 0x46; /* magic */ + if ((getreg(InputVertInterpolControl) & 0x1F) != (filter & 0x1F)) { + setreg(ISAControl, 0x80); + setreg(InputVertInterpolControl, filter & 0x1F); + setreg(ISAControl, 0x42); + } + setreg(AcquisitionControl, ((filter >> 6) ^ 3) | 0x04); + + /* set viewport position and size */ + width = ((ulong)ww * q) / 100; + if (width >= frwidth - ilv) + width = frwidth - ilv; + width = ((width + ilv - 1) / ilv) + 2; + + height = ((ulong)wh * dy + wh - 1) / wh; + if (height >= frheight) + height = frheight - 3; + height += 2; + + xs = r.min.x + XCorrection; + if (xs < 0) xs = 2; + ys = r.min.y + YCorrection; + if (ys < 0) ys = 2; + if (ScreenWidth > 1023) ys |= 1; + + setreg(DisplayViewPortWidthA, width); + setreg(DisplayViewPortWidthB, width >> 8); + setreg(DisplayViewPortHeightA, height); + setreg(DisplayViewPortHeightB, height >> 8); + setreg(DisplayViewPortOrigTopA, ys); + setreg(DisplayViewPortOrigTopB, ys >> 8); + setreg(DisplayViewPortOrigLeftA, xs); + setreg(DisplayViewPortOrigLeftB, xs >> 8); + + xe = r.min.x + ww - 1 + XCorrection; + if (xe < 0) xe = 2; + ye = r.min.y + wh - 1 + YCorrection; + if (ye < 0) ye = 2; + + setreg(DisplayWindowLeftA, xs); + setreg(DisplayWindowLeftB, xs >> 8); + setreg(DisplayWindowRightA, xe); + setreg(DisplayWindowRightB, xe >> 8); + setreg(DisplayWindowTopA, ys); + setreg(DisplayWindowTopB, ys >> 8); + setreg(DisplayWindowBottomA, ye); + setreg(DisplayWindowBottomB, ye >> 8); + + if (dx < ww) { /* horizontal zoom */ + int zoom = ((ulong) (dx - 1) * 2048) / ww; + setreg(OutputProcControlA, getreg(OutputProcControlA) | 6); + setreg(OutputHorzZoomControlA, zoom); + setreg(OutputHorzZoomControlB, zoom >> 8); + } else + setreg(OutputProcControlA, getreg(OutputProcControlA) & 0xF9); + + if (dy < wh) { /* vertical zoom */ + int zoom = ((ulong) (dy - 1) * 2048) / wh; + setreg(OutputProcControlB, getreg(OutputProcControlB) | 1); + setreg(OutputVertZoomControlA, zoom); + setreg(OutputVertZoomControlB, zoom >> 8); + } else + setreg(OutputProcControlB, getreg(OutputProcControlB) & 0xFE); + + setreg(OutputProcControlB, getreg(OutputProcControlB) | 0x20); + updateshadowregs(); + + if (changed) { + setreg(OutputProcControlA, getreg(OutputProcControlA) & 0xDF); + } else { + val = getreg(InputFieldPixelBufStatus); + USED(val); + } + + panwindow(Pt(0, 0)); +} + +static void +createwindow(Rectangle r) +{ + for (q = 100; q >= 30; q -= 10) { + newwindow(r); + if (testqfactor()) + break; + } + enablevideo(); +} + +static void +setcontrols(int index, uchar val) +{ + switch (index) { + case Vxp500Brightness: + setreg(BrightnessControl, (127L * val) / 100); + updateshadowregs(); + break; + case Vxp500Contrast: + setreg(ContrastControl, (15L * val) / 100); + updateshadowregs(); + break; + case Vxp500Saturation: + setreg(SaturationControl, (15L * val) / 100); + updateshadowregs(); + break; + case Bt812Brightness: + setbt812reg(0x08, ((126L * val) / 100) & 0xFE); + break; + case Bt812Contrast: + setbt812reg(0x09, ((254L * val) / 100) & 0xFE); + break; + case Bt812Saturation: + setbt812reg(0x0A, ((254L * val) / 100) & 0xFE); + break; + case Bt812Hue: + setbt812reg(0x0B, ((254L * val) / 100) & 0xFE); + break; + case Bt812BW: + setbt812reg(0x05, (getbt812reg(0x5) & ~2) | ((val << 1) & 2)); + break; + } +} + +static void +enablememwindow(void) +{ + setreg(MemoryWindowBaseAddrB, getreg(MemoryWindowBaseAddrB) | 0x20); +} + +static void +disablememwindow(void) +{ + setreg(MemoryWindowBaseAddrB, getreg(MemoryWindowBaseAddrB) & ~0x20); +} + +volatile static ushort *fb = (ushort *)EISA(MemAddr); +static uchar yuvpadbound[] = { 4, 12, 4, 2, 6 }; + +/* + * Capture a frame in UY0, VY1 format + */ +static void * +saveframe(int *nb) +{ + int memmode, layout, ilv; + int frwidth, frheight; + int bound, n, val, toggle; + unsigned save58, save6C; + int x, y, w, h, width, height; + ulong pos, size; + char *p; + static void *frame = 0; + static ulong framesize = 0; + + width = capwindow.max.x - capwindow.min.x; + height = capwindow.max.y - capwindow.min.y; + + memmode = getreg(MemoryConfReg) & 7; + if (memmode <= 2) { + print("devtv: cannot handle YUV411\n"); + error(Egreg); /* actually, Eleendert */ + } + layout = getreg(VideoBufferLayoutControl) & 3; + ilv = interleave[memmode]; + frwidth = framewidth[memmode][layout]; + frheight = frameheight[memmode][layout]; + + pos = getreg(AcquisitionAddrA) + + (getreg(AcquisitionAddrB) << 8) + (getreg(AcquisitionAddrC) & 3) << 16; + + x = capwindow.min.x + (pos % frwidth); + y = capwindow.min.y + (pos / frwidth); + if (x > frwidth || y > frheight) + return 0; + if (x + width > frwidth) + width = frwidth - x; + if (y + height > frheight) + height = frheight - y; + + pos = y * (frwidth / ilv) + (x / ilv); + + /* compute padding for each scan line */ + bound = yuvpadbound[memmode]; + switch (bound) { + case 2: + width = (width + 1) & ~1; + break; + case 4: + width = (width + 3) & ~3; + break; + default: + width = (width + (bound - 1)) / bound; + break; + } + + size = width * height * sizeof(ushort); + if (size != framesize) { + framesize = 0; + if (frame) + free(frame); + frame = malloc(size + 256); + } + if (frame == 0) + return 0; + + memset(frame, 0, size + 256); + + framesize = size; + p = (char *) frame + snprint(frame, 256, + "TYPE=ccir601\nWINDOW=%d %d %d %d\n\n", + capwindow.min.x, capwindow.min.y, + capwindow.min.x+width, capwindow.min.y+height); + + freeze(1); + + save58 = getreg(InputVertInterpolControl); + save6C = getreg(AcquisitionControl); + + waitforretrace(); + setreg(ISAControl, 0xC0); /* global reset */ + setreg(InputVertInterpolControl, 0x0D); + setreg(AcquisitionControl, 0x04); + setreg(CaptureControl, 0x80); /* set capture mode */ + setreg(VideoInputFrameBufDepthA, 0xFF); + setreg(VideoInputFrameBufDepthB, 0x03); + setreg(InputVideoConfA, getreg(InputVideoConfA) | 0x40); + setreg(ISAControl, 0x44); /* tight decode, global reset off */ + + setreg(CaptureViewPortAddrA, (int) pos & 0xFF); + setreg(CaptureViewPortAddrB, (int) (pos >> 8) & 0xFF); + setreg(CaptureViewPortAddrC, (int) (pos >> 16) & 0x03); + n = (width / ilv) - 1; + setreg(CaptureViewPortWidthA, n & 0xFF); + setreg(CaptureViewPortWidthB, n >> 8); + setreg(CaptureViewPortHeightA, (height-1) & 0xFF); + setreg(CaptureViewPortHeightB, (height-1) >> 8); + setreg(CapturePixelBufLow, 0x04); /* pix buffer low */ + setreg(CapturePixelBufHigh, 0x0E); /* pix buffer high */ + setreg(CaptureMultiBufDepthA, 0xFF); /* multi buffer depth maximum */ + setreg(CaptureMultiBufDepthB, 0x03); + updateshadowregs(); + + setreg(CaptureControl, 0x90); /* capture reset */ + val = getreg(InputFieldPixelBufStatus); /* clear read status */ + USED(val); + + toggle = !(getreg(OutputProcControlA) & 0x01) ? 0x8000 : 0x0000; + setreg(CaptureControl, 0xC0); /* capture enable, active */ + + while ((getreg(InputFieldPixelBufStatus) & 0x10) == 0) + /* wait for capture FIFO to become ready */; + + enablememwindow(); + for (h = height; h > 0; h--) { + for (w = width; w > 0; w -= 2) { + ushort uy0 = swab16(fb[0]) ^ toggle; + ushort vy1 = swab16(fb[1]) ^ toggle; + /* unfortunately p may not be properly aligned */ + *p++ = uy0 >> 8; + *p++ = uy0; + *p++ = vy1 >> 8; + *p++ = vy1; + } + } + disablememwindow(); + + waitforretrace(); + setreg(ISAControl, 0xC0); /* global reset */ + setreg(CaptureControl, 0); /* clear capture mode */ + setreg(InputVertInterpolControl, save58); + setreg(AcquisitionControl, save6C); + setreg(InputVideoConfA, getreg(InputVideoConfA) | 0x40); + setreg(ISAControl, 0x40); /* clear global reset */ + updateshadowregs(); + + freeze(0); + + *nb = p - (char *) frame; + return frame; +} diff --git a/os/pc/devusb.c b/os/pc/devusb.c new file mode 100644 index 00000000..56a3442a --- /dev/null +++ b/os/pc/devusb.c @@ -0,0 +1,951 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "usb.h" + +static int debug = 0; + +#define Chatty 1 +#define DPRINT if(Chatty)print +#define XPRINT if(debug)iprint + +Usbhost* usbhost[MaxUsb]; + +static char *devstates[] = { + [Disabled] "Disabled", + [Attached] "Attached", + [Enabled] "Enabled", + [Assigned] "Assigned", + [Configured] "Configured", +}; + +static char Ebadusbmsg[] = "invalid parameters to USB ctl message"; + +enum +{ + Qtopdir = 0, + Q2nd, + Qnew, + Qport, + Q3rd, + Qctl, + Qstatus, + Qep0, + /* other endpoint files */ +}; + +/* + * Qid path is: + * 8 bits of file type (qids above) + * 8 bits of slot number; default address 0 used for per-controller files + * 4 bits of controller number + */ +enum { + TYPEBITS = 8, + SLOTBITS = 8, + CTLRBITS = 4, + + SLOTSHIFT = TYPEBITS, + CTLRSHIFT = SLOTSHIFT+SLOTBITS, + + TYPEMASK = (1<>SLOTSHIFT)&SLOTMASK) +#define CTLR(q) ((((ulong)(q).path)>>CTLRSHIFT)&CTLRMASK) +#define PATH(t, s, c) ((t)|((s)< nelem(uh->dev)) + return nil; + return uh->dev[s]; +} + +static Udev* +usbdevice(Chan *c) +{ + int bus; + Udev *d; + Usbhost *uh; + + bus = CTLR(c->qid); + if(bus > nelem(usbhost) || (uh = usbhost[bus]) == nil) { + error(Egreg); + return nil; /* for compiler */ + } + d = usbdeviceofslot(uh, SLOT(c->qid)); + if(d == nil || d->id != c->qid.vers || d->state == Disabled) + error(Ehungup); + return d; +} + +static Endpt * +devendpt(Udev *d, int id, int add) +{ + Usbhost *uh; + Endpt *e, **p; + + p = &d->ep[id&0xF]; + lock(d); + e = *p; + if(e != nil){ + incref(e); + XPRINT("incref(0x%p) in devendpt, new value %ld\n", e, e->ref); + unlock(d); + return e; + } + unlock(d); + if(!add) + return nil; + + e = mallocz(sizeof(*e), 1); + e->ref = 1; + e->x = id&0xF; + e->id = id; + e->sched = -1; + e->maxpkt = 8; + e->nbuf = 1; + e->dev = d; + e->active = 0; + + uh = d->uh; + uh->epalloc(uh, e); + + lock(d); + if(*p != nil){ + incref(*p); + XPRINT("incref(0x%p) in devendpt, new value %ld\n", *p, (*p)->ref); + unlock(d); + uh->epfree(uh, e); + free(e); + return *p; + } + *p = e; + unlock(d); + e->rq = qopen(8*1024, 0, nil, e); + e->wq = qopen(8*1024, 0, nil, e); + return e; +} + +static void +freept(Endpt *e) +{ + Usbhost *uh; + + if(e != nil && decref(e) == 0){ + XPRINT("freept(%d,%d)\n", e->dev->x, e->x); + uh = e->dev->uh; + uh->epclose(uh, e); + e->dev->ep[e->x] = nil; + uh->epfree(uh, e); + free(e); + } +} + +static Udev* +usbnewdevice(Usbhost *uh) +{ + int i; + Udev *d; + Endpt *e; + + d = nil; + qlock(uh); + if(waserror()){ + qunlock(uh); + nexterror(); + } + for(i=0; idev); i++) + if(uh->dev[i] == nil){ + uh->idgen++; + d = mallocz(sizeof(*d), 1); + d->uh = uh; + d->ref = 1; + d->x = i; + d->id = (uh->idgen << 8) | i; + d->state = Enabled; + XPRINT("calling devendpt in usbnewdevice\n"); + e = devendpt(d, 0, 1); /* always provide control endpoint 0 */ + e->mode = ORDWR; + e->iso = 0; + e->sched = -1; + uh->dev[i] = d; + break; + } + poperror(); + qunlock(uh); + return d; +} + +static void +freedev(Udev *d, int ept) +{ + int i; + Endpt *e; + Usbhost *uh; + + uh = d->uh; + if(decref(d) == 0){ + XPRINT("freedev 0x%p, 0\n", d); + for(i=0; iep); i++) + freept(d->ep[i]); + if(d->x >= 0) + uh->dev[d->x] = nil; + free(d); + } else { + if(ept >= 0 && ept < nelem(d->ep)){ + e = d->ep[ept]; + XPRINT("freedev, freept 0x%p\n", e); + if(e != nil) + uh->epclose(uh, e); + } + } +} + +static int +usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp) +{ + Qid q; + Udev *d; + Endpt *e; + Dirtab *tab; + Usbhost *uh; + int t, bus, slot, perm; + + /* + * Top level directory contains the controller names. + */ + if(c->qid.path == Qtopdir){ + if(s == DEVDOTDOT){ + mkqid(&q, Qtopdir, 0, QTDIR); + devdir(c, q, "#U", 0, eve, 0555, dp); + return 1; + } + if(s >= nelem(usbhost) || usbhost[s] == nil) + return -1; + mkqid(&q, PATH(Q2nd, 0, s), 0, QTDIR); + snprint(up->genbuf, sizeof up->genbuf, "usb%d", s); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + bus = CTLR(c->qid); + if(bus >= nelem(usbhost) || (uh = usbhost[bus]) == nil) + return -1; + + /* + * Second level contains "new", "port", and a numbered + * directory for each enumerated device on the bus. + */ + t = TYPE(c->qid); + if(t < Q3rd){ + if(s == DEVDOTDOT){ + mkqid(&q, Qtopdir, 0, QTDIR); + devdir(c, q, "#U", 0, eve, 0555, dp); + return 1; + } + if(s < nelem(usbdir2)){ + d = uh->dev[0]; + if(d == nil) + return -1; + tab = &usbdir2[s]; + mkqid(&q, PATH(tab->qid.path, 0, bus), d->id, QTFILE); + devdir(c, q, tab->name, tab->length, eve, tab->perm, dp); + return 1; + } + s -= nelem(usbdir2); + if(s >= 0 && s < nelem(uh->dev)) { + d = uh->dev[s]; + if(d == nil) + return 0; + sprint(up->genbuf, "%d", s); + mkqid(&q, PATH(Q3rd, s, bus), d->id, QTDIR); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + return -1; + } + + /* + * Third level. + */ + slot = SLOT(c->qid); + if(s == DEVDOTDOT) { + mkqid(&q, PATH(Q2nd, 0, bus), c->qid.vers, QTDIR); + snprint(up->genbuf, sizeof up->genbuf, "usb%d", bus); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + if(s < nelem(usbdir3)) { + tab = &usbdir3[s]; + mkqid(&q, PATH(tab->qid.path, slot, bus), c->qid.vers, QTFILE); + devdir(c, q, tab->name, tab->length, eve, tab->perm, dp); + return 1; + } + s -= nelem(usbdir3); + + /* active endpoints */ + d = usbdeviceofslot(uh, slot); + if(d == nil || s >= nelem(d->ep)) + return -1; + if(s == 0 || (e = d->ep[s]) == nil) /* ep0data is called "setup" */ + return 0; + sprint(up->genbuf, "ep%ddata", s); + mkqid(&q, PATH(Qep0+s, slot, bus), c->qid.vers, QTFILE); + switch(e->mode) { + case OREAD: + perm = 0444; + break; + case OWRITE: + perm = 0222; + break; + default: + perm = 0666; + break; + } + devdir(c, q, up->genbuf, e->buffered, eve, perm, dp); + return 1; +} + +static Usbhost* +usbprobe(int cardno, int ctlrno) +{ + Usbhost *uh; + char buf[128], *ebuf, name[64], *p, *type; + + uh = malloc(sizeof(Usbhost)); + memset(uh, 0, sizeof(Usbhost)); + uh->tbdf = BUSUNKNOWN; + + if(cardno < 0){ + if(isaconfig("usb", ctlrno, uh) == 0){ + free(uh); + return nil; + } + for(cardno = 0; usbtypes[cardno].type; cardno++){ + type = uh->type; + if(type==nil || *type==0) + type = "uhci"; + if(cistrcmp(usbtypes[cardno].type, type)) + continue; + break; + } + } + + if(cardno >= MaxUsb || usbtypes[cardno].type == nil){ + free(uh); + return nil; + } + if(usbtypes[cardno].reset(uh) < 0){ + free(uh); + return nil; + } + + /* + * IRQ2 doesn't really exist, it's used to gang the interrupt + * controllers together. A device set to IRQ2 will appear on + * the second interrupt controller as IRQ9. + */ + if(uh->irq == 2) + uh->irq = 9; + snprint(name, sizeof(name), "usb%d", ctlrno); + intrenable(uh->irq, uh->interrupt, uh, uh->tbdf, name); + + ebuf = buf + sizeof buf; + p = seprint(buf, ebuf, "#U/usb%d: %s: port 0x%luX irq %d", ctlrno, usbtypes[cardno].type, uh->port, uh->irq); + if(uh->mem) + p = seprint(p, ebuf, " addr 0x%luX", PADDR(uh->mem)); + if(uh->size) + seprint(p, ebuf, " size 0x%luX", uh->size); + print("%s\n", buf); + + return uh; +} + +static void +usbreset(void) +{ + int cardno, ctlrno; + Usbhost *uh; + + for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){ + if((uh = usbprobe(-1, ctlrno)) == nil) + continue; + usbhost[ctlrno] = uh; + } + + if(getconf("*nousbprobe")) + return; + + cardno = ctlrno = 0; + while(usbtypes[cardno].type != nil && ctlrno < MaxUsb){ + if(usbhost[ctlrno] != nil){ + ctlrno++; + continue; + } + if((uh = usbprobe(cardno, ctlrno)) == nil){ + cardno++; + continue; + } + usbhost[ctlrno] = uh; + ctlrno++; + } +} + +void +usbinit(void) +{ + Udev *d; + int ctlrno; + Usbhost *uh; + + for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){ + uh = usbhost[ctlrno]; + if(uh == nil) + continue; + if(uh->init != 0) + uh->init(uh); + + /* reserve device for configuration */ + d = usbnewdevice(uh); + incref(d); + d->state = Attached; + } +} + +Chan * +usbattach(char *spec) +{ + return devattach('U', spec); +} + +static Walkqid* +usbwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, usbgen); +} + +static int +usbstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, nil, 0, usbgen); +} + +Chan* +usbopen(Chan *c, int omode) +{ + Udev *d; + Endpt *e; + int f, s, type; + Usbhost *uh; + + if(c->qid.type == QTDIR) + return devopen(c, omode, nil, 0, usbgen); + + f = 0; + type = TYPE(c->qid); + if(type == Qnew){ + d = usbdevice(c); + d = usbnewdevice(d->uh); + XPRINT("usbopen, new dev 0x%p\n", d); + if(d == nil) { + XPRINT("usbopen failed (usbnewdevice)\n"); + error(Enodev); + } + type = Qctl; + mkqid(&c->qid, PATH(type, d->x, CTLR(c->qid)), d->id, QTFILE); + f = 1; + } + + if(type < Q3rd){ + XPRINT("usbopen, devopen < Q3rd\n"); + return devopen(c, omode, nil, 0, usbgen); + } + + d = usbdevice(c); + uh = d->uh; + qlock(uh); + if(waserror()){ + qunlock(uh); + nexterror(); + } + + switch(type){ + case Qctl: + if(0&&d->busy) + error(Einuse); + d->busy = 1; + if(!f) + incref(d); + XPRINT("usbopen, Qctl 0x%p\n", d); + break; + + default: + s = type - Qep0; + XPRINT("usbopen, default 0x%p, %d\n", d, s); + if(s >= 0 && s < nelem(d->ep)){ + if((e = d->ep[s]) == nil) { + XPRINT("usbopen failed (endpoint)\n"); + error(Enodev); + } + XPRINT("usbopen: dev 0x%p, ept 0x%p\n", d, e); + uh->epopen(uh, e); + e->foffset = 0; + e->toffset = 0; + e->poffset = 0; + e->buffered = 0; + } + incref(d); + break; + } + poperror(); + qunlock(uh); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +void +usbclose(Chan *c) +{ + Udev *d; + int ept, type; + Usbhost *uh; + + type = TYPE(c->qid); + if(c->qid.type == QTDIR || type < Q3rd) + return; + d = usbdevice(c); + uh = d->uh; + qlock(uh); + if(waserror()){ + qunlock(uh); + nexterror(); + } + if(type == Qctl) + d->busy = 0; + XPRINT("usbclose: dev 0x%p\n", d); + if(c->flag & COPEN){ + ept = (type != Qctl) ? type - Qep0 : -1; + XPRINT("usbclose: freedev 0x%p\n", d); + freedev(d, ept); + } + poperror(); + qunlock(uh); +} + +static char * +epstatus(char *s, char *se, Endpt *e, int i) +{ + char *p; + + p = seprint(s, se, "%2d %#6.6lux %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks); + if(e->iso){ + p = seprint(p, se, "bufsize %6d buffered %6d", e->maxpkt, e->buffered); + if(e->toffset) + p = seprint(p, se, " offset %10lud time %19lld\n", e->toffset, e->time); + p = seprint(p, se, "\n"); + } + return p; +} + +long +usbread(Chan *c, void *a, long n, vlong offset) +{ + int t, i; + Udev *d; + Endpt *e; + Usbhost *uh; + char *s, *se, *p; + + if(c->qid.type == QTDIR) + return devdirread(c, a, n, nil, 0, usbgen); + + d = usbdevice(c); + uh = d->uh; + t = TYPE(c->qid); + + if(t >= Qep0) { + t -= Qep0; + if(t >= nelem(d->ep)) + error(Eio); + e = d->ep[t]; + if(e == nil || e->mode == OWRITE) + error(Egreg); + if(t == 0) { + if(e->iso) + error(Egreg); + e->data01 = 1; + n = uh->read(uh, e, a, n, 0LL); + if(e->setin){ + e->setin = 0; + e->data01 = 1; + uh->write(uh, e, "", 0, 0LL, TokOUT); + } + return n; + } + return uh->read(uh, e, a, n, offset); + } + + s = smalloc(READSTR); + se = s+READSTR; + if(waserror()){ + free(s); + nexterror(); + } + switch(t){ + case Qport: + uh->portinfo(uh, s, se); + break; + + case Qctl: + seprint(s, se, "%11d %11d\n", d->x, d->id); + break; + + case Qstatus: + if (d->did || d->vid) + p = seprint(s, se, "%s %#6.6lux %#4.4ux %#4.4ux\n", devstates[d->state], d->csp, d->vid, d->did); + else + p = seprint(s, se, "%s %#6.6lux\n", devstates[d->state], d->csp); + for(i=0; iep); i++) { + e = d->ep[i]; + if(e == nil) + continue; + /* TO DO: freeze e */ + p = epstatus(p, se, e, i); + } + } + n = readstr(offset, a, n, s); + poperror(); + free(s); + return n; +} + +long +usbwrite(Chan *c, void *a, long n, vlong offset) +{ + Udev *d; + Endpt *e; + Cmdtab *ct; + Cmdbuf *cb; + Usbhost *uh; + int id, nw, t, i; + char cmd[50]; + + if(c->qid.type == QTDIR) + error(Egreg); + d = usbdevice(c); + uh = d->uh; + t = TYPE(c->qid); + switch(t){ + case Qport: + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + + ct = lookupcmd(cb, usbportmsg, nelem(usbportmsg)); + id = strtol(cb->f[1], nil, 0); + switch(ct->index){ + case PMdisable: + uh->portenable(uh, id, 0); + break; + case PMenable: + uh->portenable(uh, id, 1); + break; + case PMreset: + uh->portreset(uh, id); + break; + } + + poperror(); + free(cb); + return n; + case Qctl: + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + + ct = lookupcmd(cb, usbctlmsg, nelem(usbctlmsg)); + switch(ct->index){ + case CMspeed: + d->ls = strtoul(cb->f[1], nil, 0) == 0; + break; + case CMclass: + if (cb->nf != 4 && cb->nf != 6) + cmderror(cb, Ebadusbmsg); + /* class #ifc ept csp ( == class subclass proto) [vendor product] */ + d->npt = strtoul(cb->f[1], nil, 0); /* # of interfaces */ + i = strtoul(cb->f[2], nil, 0); /* endpoint */ + if (i < 0 || i >= nelem(d->ep) + || d->npt > nelem(d->ep) || i >= d->npt) + cmderror(cb, Ebadusbmsg); + if (cb->nf == 6) { + d->vid = strtoul(cb->f[4], nil, 0); + d->did = strtoul(cb->f[5], nil, 0); + } + if (i == 0) + d->csp = strtoul(cb->f[3], nil, 0); + if(d->ep[i] == nil){ + XPRINT("calling devendpt in usbwrite (CMclass)\n"); + d->ep[i] = devendpt(d, i, 1); + } + d->ep[i]->csp = strtoul(cb->f[3], nil, 0); + break; + case CMdata: + i = strtoul(cb->f[1], nil, 0); + if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) + error(Ebadusbmsg); + e = d->ep[i]; + e->data01 = strtoul(cb->f[2], nil, 0) != 0; + break; + case CMmaxpkt: + i = strtoul(cb->f[1], nil, 0); + if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) + error(Ebadusbmsg); + e = d->ep[i]; + e->maxpkt = strtoul(cb->f[2], nil, 0); + if(e->maxpkt > 1500) + e->maxpkt = 1500; + break; + case CMadjust: + i = strtoul(cb->f[1], nil, 0); + if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) + error(Ebadusbmsg); + e = d->ep[i]; + if (e->iso == 0) + error(Eperm); + i = strtoul(cb->f[2], nil, 0); + /* speed may not result in change of maxpkt */ + if (i < (e->maxpkt-1)/e->samplesz * 1000/e->pollms + || i > e->maxpkt/e->samplesz * 1000/e->pollms){ + snprint(cmd, sizeof(cmd), "%d < %d < %d?", + (e->maxpkt-1)/e->samplesz * 1000/e->pollms, + i, + e->maxpkt/e->samplesz * 1000/e->pollms); + error(cmd); + } + e->hz = i; + break; + case CMdebug: + i = strtoul(cb->f[1], nil, 0); + if(i < -1 || i >= nelem(d->ep) || d->ep[i] == nil) + error(Ebadusbmsg); + if (i == -1) + debug = 0; + else { + debug = 1; + e = d->ep[i]; + e->debug = strtoul(cb->f[2], nil, 0); + } + break; + case CMunstall: + i = strtoul(cb->f[1], nil, 0); + if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) + error(Ebadusbmsg); + e = d->ep[i]; + e->err = nil; + break; + case CMep: + /* ep n `bulk' mode maxpkt nbuf OR + * ep n period mode samplesize Hz + */ + i = strtoul(cb->f[1], nil, 0); + if(i < 0 || i >= nelem(d->ep)) { + XPRINT("field 1: 0 <= %d < %d\n", i, nelem(d->ep)); + error(Ebadarg); + } + if((e = d->ep[i]) == nil){ + XPRINT("calling devendpt in usbwrite (CMep)\n"); + e = devendpt(d, i, 1); + } + qlock(uh); + if(waserror()){ + freept(e); + qunlock(uh); + nexterror(); + } + if(e->active) + error(Eperm); + if(strcmp(cb->f[2], "bulk") == 0){ + /* ep n `bulk' mode maxpkt nbuf */ + e->iso = 0; + i = strtoul(cb->f[4], nil, 0); + if(i < 8 || i > 1023) + i = 8; + e->maxpkt = i; + i = strtoul(cb->f[5], nil, 0); + if(i >= 1 && i <= 32) + e->nbuf = i; + } else { + /* ep n period mode samplesize Hz */ + i = strtoul(cb->f[2], nil, 0); + if(i > 0 && i <= 1000){ + e->pollms = i; + }else { + XPRINT("field 4: 0 <= %d <= 1000\n", i); + error(Ebadarg); + } + i = strtoul(cb->f[4], nil, 0); + if(i >= 1 && i <= 8){ + e->samplesz = i; + }else { + XPRINT("field 4: 0 < %d <= 8\n", i); + error(Ebadarg); + } + i = strtoul(cb->f[5], nil, 0); + if(i >= 1 && i*e->samplesz <= 12*1000*1000){ + /* Hz */ + e->hz = i; + e->remain = 0; + }else { + XPRINT("field 5: 1 < %d <= 100000 Hz\n", i); + error(Ebadarg); + } + e->maxpkt = (e->hz * e->pollms + 999)/1000 * e->samplesz; + e->iso = 1; + } + e->mode = strcmp(cb->f[3],"r") == 0? OREAD : + strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR; + uh->epmode(uh, e); + poperror(); + qunlock(uh); + } + + poperror(); + free(cb); + return n; + + case Qep0: /* SETUP endpoint 0 */ + /* should canqlock etc */ + e = d->ep[0]; + if(e == nil || e->iso) + error(Egreg); + if(n < 8) + error(Eio); + nw = *(uchar*)a & RD2H; + e->data01 = 0; + n = uh->write(uh, e, a, n, 0LL, TokSETUP); + if(nw == 0) { /* host to device: use IN[DATA1] to ack */ + e->data01 = 1; + nw = uh->read(uh, e, cmd, 0LL, 8); + if(nw != 0) + error(Eio); /* could provide more status */ + }else + e->setin = 1; /* two-phase */ + break; + + default: /* sends DATA[01] */ + t -= Qep0; + if(t < 0 || t >= nelem(d->ep)) + error(Egreg); + e = d->ep[t]; + if(e == nil || e->mode == OREAD) + error(Egreg); + n = uh->write(uh, e, a, n, offset, TokOUT); + break; + } + return n; +} + +Dev usbdevtab = { + 'U', + "usb", + + usbreset, + usbinit, + devshutdown, + usbattach, + usbwalk, + usbstat, + usbopen, + devcreate, + usbclose, + usbread, + devbread, + usbwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/pc/devvga.c b/os/pc/devvga.c new file mode 100644 index 00000000..0d7a9f0d --- /dev/null +++ b/os/pc/devvga.c @@ -0,0 +1,620 @@ +/* + * VGA controller + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +typedef struct Vgaseg Vgaseg; +struct Vgaseg { + QLock; + ulong pa; + ulong len; + void* va; +}; + +enum { + Nvgaseg = 4, + + Qdir = 0, + Qvgactl, + Qvgaovl, + Qvgaovlctl, + + Qsegs, + Qmax = Qsegs+Nvgaseg +}; + +static Dirtab vgadir[Qmax] = { + ".", { Qdir, 0, QTDIR }, 0, 0550, + "vgactl", { Qvgactl, 0 }, 0, 0660, + "vgaovl", { Qvgaovl, 0 }, 0, 0660, + "vgaovlctl", { Qvgaovlctl, 0 }, 0, 0660, + /* dynamically-created memory segments are added here */ +}; + +static Vgaseg vgasegs[Nvgaseg]; +static Lock vgadirlock; +static int nvgadir = Qsegs; + +enum { + CMactualsize, + CMblank, + CMblanktime, + CMdrawinit, + CMhwaccel, + CMhwblank, + CMhwgc, + CMlinear, + CMpalettedepth, + CMpanning, + CMsize, + CMtype, + CMunblank, +}; + +static Cmdtab vgactlmsg[] = { + CMactualsize, "actualsize", 2, + CMblank, "blank", 1, + CMblanktime, "blanktime", 2, + CMdrawinit, "drawinit", 1, + CMhwaccel, "hwaccel", 2, + CMhwblank, "hwblank", 2, + CMhwgc, "hwgc", 2, + CMlinear, "linear", 0, + CMpalettedepth, "palettedepth", 2, + CMpanning, "panning", 2, + CMsize, "size", 3, + CMtype, "type", 2, + CMunblank, "unblank", 1, +}; + +static void +vgareset(void) +{ + /* reserve the 'standard' vga registers */ + if(ioalloc(0x2b0, 0x2df-0x2b0+1, 0, "vga") < 0) + panic("vga ports already allocated"); + if(ioalloc(0x3c0, 0x3da-0x3c0+1, 0, "vga") < 0) + panic("vga ports already allocated"); + conf.monitor = 1; +} + +void +addvgaseg(char *name, ulong pa, ulong size) +{ + int i; + Dirtab d; + Vgaseg *s; + ulong va; + + va = mmukmap(pa, 0, size); + if(va == 0) + return; + memset(&d, 0, sizeof(d)); + strecpy(d.name, d.name+sizeof(name), name); + lock(&vgadirlock); + for(i=0; i= nelem(vgadir)){ + unlock(&vgadirlock); + print("devvga: segment %s: too many segments\n", name); + return; + } + d.qid.path = nvgadir; + d.perm = 0660; + d.length = size; + s = &vgasegs[nvgadir-Qsegs]; + s->pa = pa; + s->len = size; + s->va = (void*)va; + vgadir[nvgadir] = d; + nvgadir++; + unlock(&vgadirlock); +} + +static long +vgasegrd(Vgaseg *s, uchar *buf, long n, ulong offset) +{ + int i; + uchar *a, *d; + ulong v; + + if(offset >= s->len) + return 0; + if(offset+n > s->len) + n = s->len - offset; + d = (uchar*)s->va + offset; + qlock(s); + if(waserror()){ + qunlock(s); + nexterror(); + } + a = buf; + while(n > 0){ + i = 4 - ((ulong)d & 3); + if(i > n) + i = n; + if(i == 3) + i = 2; + switch(i){ + case 4: + v = (a[3]<<24) | (a[2]<<16) | (a[1]<<8) | a[0]; + *(ulong*)d = v; + break; + case 2: + v = (a[1]<<8) | a[0]; + *(ushort*)d = v; + break; + case 1: + *d = *a; + break; + } + d += i; + a += i; + n -= i; + } + poperror(); + qunlock(s); + return a-buf; +} + +static long +vgasegwr(Vgaseg *s, uchar *buf, long n, ulong offset) +{ + int i; + uchar *a, *r; + ulong v; + + if(offset >= s->len) + return 0; + if(offset+n > s->len) + n = s->len - offset; + r = (uchar*)s->va + offset; + qlock(s); + if(waserror()){ + qunlock(s); + nexterror(); + } + a = buf; + while(n > 0){ + i = 4 - ((ulong)r & 3); + if(i > n) + i = n; + if(i == 3) + i = 2; + switch(i){ + case 4: + v = *(ulong*)r; + a[0] = v; + a[1] = v>>8; + a[2] = v>>16; + a[3] = v>>24; + break; + case 2: + v = *(ushort*)r; + a[0] = v; + a[1] = v>>8; + break; + case 1: + *a = *r; + break; + } + r += i; + a += i; + n -= i; + } + poperror(); + qunlock(s); + return a-buf; +} + +static Chan* +vgaattach(char* spec) +{ + if(*spec && strcmp(spec, "0")) + error(Eio); + return devattach('v', spec); +} + +Walkqid* +vgawalk(Chan* c, Chan *nc, char** name, int nname) +{ + return devwalk(c, nc, name, nname, vgadir, nvgadir, devgen); +} + +static int +vgastat(Chan* c, uchar* dp, int n) +{ + return devstat(c, dp, n, vgadir, nvgadir, devgen); +} + +static Chan* +vgaopen(Chan* c, int omode) +{ + VGAscr *scr; + static char *openctl = "openctl\n"; + + scr = &vgascreen[0]; + if ((ulong)c->qid.path == Qvgaovlctl) { + if (scr->dev && scr->dev->ovlctl) + scr->dev->ovlctl(scr, c, openctl, strlen(openctl)); + else + error(Enonexist); + } + return devopen(c, omode, vgadir, nvgadir, devgen); +} + +static void +vgaclose(Chan* c) +{ + VGAscr *scr; + static char *closectl = "closectl\n"; + + scr = &vgascreen[0]; + if((ulong)c->qid.path == Qvgaovlctl) + if(scr->dev && scr->dev->ovlctl){ + if(waserror()){ + print("ovlctl error: %s\n", up->env->errstr); + return; + } + scr->dev->ovlctl(scr, c, closectl, strlen(closectl)); + poperror(); + } +} + +static void +checkport(int start, int end) +{ + /* standard vga regs are OK */ + if(start >= 0x2b0 && end <= 0x2df+1) + return; + if(start >= 0x3c0 && end <= 0x3da+1) + return; + + if(iounused(start, end)) + return; + error(Eperm); +} + +static long +vgaread(Chan* c, void* a, long n, vlong off) +{ + int len; + char *p, *s; + VGAscr *scr; + ulong offset = off; + char chbuf[30]; + + switch((ulong)c->qid.path){ + + case Qdir: + return devdirread(c, a, n, vgadir, nvgadir, devgen); + + case Qvgactl: + scr = &vgascreen[0]; + + p = malloc(READSTR); + if(waserror()){ + free(p); + nexterror(); + } + + len = 0; + + if(scr->dev) + s = scr->dev->name; + else + s = "cga"; + len += snprint(p+len, READSTR-len, "type %s\n", s); + + if(scr->gscreen) { + len += snprint(p+len, READSTR-len, "size %dx%dx%d %s\n", + scr->gscreen->r.max.x, scr->gscreen->r.max.y, + scr->gscreen->depth, chantostr(chbuf, scr->gscreen->chan)); + + if(Dx(scr->gscreen->r) != Dx(physgscreenr) + || Dy(scr->gscreen->r) != Dy(physgscreenr)) + len += snprint(p+len, READSTR-len, "actualsize %dx%d\n", + physgscreenr.max.x, physgscreenr.max.y); + } + + len += snprint(p+len, READSTR-len, "blank time %lud idle %d state %s\n", + blanktime, drawidletime(), scr->isblank ? "off" : "on"); + len += snprint(p+len, READSTR-len, "hwaccel %s\n", hwaccel ? "on" : "off"); + len += snprint(p+len, READSTR-len, "hwblank %s\n", hwblank ? "on" : "off"); + len += snprint(p+len, READSTR-len, "panning %s\n", panning ? "on" : "off"); + snprint(p+len, READSTR-len, "addr 0x%lux\n", scr->aperture); + n = readstr(offset, a, n, p); + poperror(); + free(p); + + return n; + + case Qvgaovl: + case Qvgaovlctl: + error(Ebadusefd); + break; + + default: + if(c->qid.path < nvgadir) + return vgasegrd(&vgasegs[c->qid.path], a, n, offset); + error(Egreg); + break; + } + + return 0; +} + +static char Ebusy[] = "vga already configured"; + +static void +vgactl(Cmdbuf *cb) +{ + int align, i, size, x, y, z; + char *chanstr, *p; + ulong chan; + Cmdtab *ct; + VGAscr *scr; + extern VGAdev *vgadev[]; + extern VGAcur *vgacur[]; + + scr = &vgascreen[0]; + ct = lookupcmd(cb, vgactlmsg, nelem(vgactlmsg)); + switch(ct->index){ + case CMhwgc: + if(strcmp(cb->f[1], "off") == 0){ + lock(&cursor); + if(scr->cur){ + if(scr->cur->disable) + scr->cur->disable(scr); + scr->cur = nil; + } + unlock(&cursor); + return; + } + + for(i = 0; vgacur[i]; i++){ + if(strcmp(cb->f[1], vgacur[i]->name)) + continue; + lock(&cursor); + if(scr->cur && scr->cur->disable) + scr->cur->disable(scr); + scr->cur = vgacur[i]; + if(scr->cur->enable) + scr->cur->enable(scr); + unlock(&cursor); + return; + } + break; + + case CMtype: + for(i = 0; vgadev[i]; i++){ + if(strcmp(cb->f[1], vgadev[i]->name)) + continue; + if(scr->dev && scr->dev->disable) + scr->dev->disable(scr); + scr->dev = vgadev[i]; + if(scr->dev->enable) + scr->dev->enable(scr); + return; + } + break; + + case CMsize: + if(drawhasclients()) + error(Ebusy); + + x = strtoul(cb->f[1], &p, 0); + if(x == 0 || x > 2048) + error(Ebadarg); + if(*p) + p++; + + y = strtoul(p, &p, 0); + if(y == 0 || y > 2048) + error(Ebadarg); + if(*p) + p++; + + z = strtoul(p, &p, 0); + + chanstr = cb->f[2]; + if((chan = strtochan(chanstr)) == 0) + error("bad channel"); + + if(chantodepth(chan) != z) + error("depth, channel do not match"); + + cursoroff(1); + deletescreenimage(); + if(screensize(x, y, z, chan)) + error(Egreg); + vgascreenwin(scr); + cursoron(1); + return; + + case CMactualsize: + if(scr->gscreen == nil) + error("set the screen size first"); + + x = strtoul(cb->f[1], &p, 0); + if(x == 0 || x > 2048) + error(Ebadarg); + if(*p) + p++; + + y = strtoul(p, nil, 0); + if(y == 0 || y > 2048) + error(Ebadarg); + + if(x > scr->gscreen->r.max.x || y > scr->gscreen->r.max.y) + error("physical screen bigger than virtual"); + + physgscreenr = Rect(0,0,x,y); + scr->gscreen->clipr = physgscreenr; + return; + + case CMpalettedepth: + x = strtoul(cb->f[1], &p, 0); + if(x != 8 && x != 6) + error(Ebadarg); + + scr->palettedepth = x; + return; + + case CMdrawinit: + memimagedraw(scr->gscreen, scr->gscreen->r, memblack, ZP, nil, ZP, S); + if(scr && scr->dev && scr->dev->drawinit) + scr->dev->drawinit(scr); + return; + + case CMlinear: + if(cb->nf!=2 && cb->nf!=3) + error(Ebadarg); + size = strtoul(cb->f[1], 0, 0); + if(cb->nf == 2) + align = 0; + else + align = strtoul(cb->f[2], 0, 0); + if(screenaperture(size, align)) + error("not enough free address space"); + return; + + case CMblank: + drawblankscreen(1); + return; + + case CMunblank: + drawblankscreen(0); + return; + + case CMblanktime: + blanktime = strtoul(cb->f[1], 0, 0); + return; + + case CMpanning: + if(strcmp(cb->f[1], "on") == 0){ + if(scr == nil || scr->cur == nil) + error("set screen first"); + if(!scr->cur->doespanning) + error("panning not supported"); + scr->gscreen->clipr = scr->gscreen->r; + panning = 1; + } + else if(strcmp(cb->f[1], "off") == 0){ + scr->gscreen->clipr = physgscreenr; + panning = 0; + }else + break; + return; + + case CMhwaccel: + if(strcmp(cb->f[1], "on") == 0) + hwaccel = 1; + else if(strcmp(cb->f[1], "off") == 0) + hwaccel = 0; + else + break; + return; + + case CMhwblank: + if(strcmp(cb->f[1], "on") == 0) + hwblank = 1; + else if(strcmp(cb->f[1], "off") == 0) + hwblank = 0; + else + break; + return; + } + + cmderror(cb, "bad VGA control message"); +} + +char Enooverlay[] = "No overlay support"; + +static long +vgawrite(Chan* c, void* a, long n, vlong off) +{ + ulong offset = off; + Cmdbuf *cb; + VGAscr *scr; + + switch((ulong)c->qid.path){ + + case Qdir: + error(Eperm); + + case Qvgactl: + if(offset || n >= READSTR) + error(Ebadarg); + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + vgactl(cb); + poperror(); + free(cb); + return n; + + case Qvgaovl: + scr = &vgascreen[0]; + if (scr->dev == nil || scr->dev->ovlwrite == nil) { + error(Enooverlay); + break; + } + return scr->dev->ovlwrite(scr, a, n, off); + + case Qvgaovlctl: + scr = &vgascreen[0]; + if (scr->dev == nil || scr->dev->ovlctl == nil) { + error(Enooverlay); + break; + } + scr->dev->ovlctl(scr, c, a, n); + return n; + + default: + if(c->qid.path < nvgadir) + return vgasegwr(&vgasegs[c->qid.path], a, n, offset); + error(Egreg); + break; + } + + return 0; +} + +Dev vgadevtab = { + 'v', + "vga", + + vgareset, + devinit, + devshutdown, + vgaattach, + vgawalk, + vgastat, + vgaopen, + devcreate, + vgaclose, + vgaread, + devbread, + vgawrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/pc/devzt5512.c b/os/pc/devzt5512.c new file mode 100644 index 00000000..af189fff --- /dev/null +++ b/os/pc/devzt5512.c @@ -0,0 +1,308 @@ +/* + * Namespace Interface for Ziatech 5512 System Registers + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + + +enum{ + Qdir, + Qsysid, + Qwatchdog, + Qledctl, + Qpower, + Qswitch, + Qstat, +}; + +static +Dirtab zttab[]={ + ".", {Qdir,0,QTDIR}, 0, 0555, + "id", {Qsysid, 0}, 0, 0444, + "watchdog", {Qwatchdog, 0}, 0, 0600, + "ledctl", {Qledctl, 0}, 0, 0666, + "powerstat", {Qpower, 0}, 0, 0444, + "switch", {Qswitch, 0}, 0, 0444, + "stat", {Qstat, 0}, 0, 0444, +}; + +extern int watchdog; +void +watchdog_strobe(void) +{ + uchar sysreg; + + sysreg = inb(0x78); + sysreg &= (~1); + outb(0x78, sysreg); /* disable/strobe watchdog */ + sysreg |= 1; + outb(0x78, sysreg); /* enable watchdog */ +} + +static void +ztreset(void) /* default in dev.c */ +{ + uchar sysreg; + + if(watchdog) + addclock0link(watchdog_strobe); + /* clear status LEDs */ + sysreg = inb(0xe2); + sysreg &= ~3; /* clear usr1 */ + sysreg &= ~(3 << 2); /* clear usr2 */ + outb(0xe2, sysreg); +} + +static Chan* +ztattach(char* spec) +{ + return devattach('Z', spec); +} + +static int +ztwalk(Chan* c, char* name) +{ + return devwalk(c, name, zttab, nelem(zttab), devgen); +} + +static void +ztstat(Chan* c, char* db) +{ + devstat(c, db, zttab, nelem(zttab), devgen); +} + +static Chan* +ztopen(Chan* c, int omode) +{ + return devopen(c, omode, zttab, nelem(zttab), devgen); +} + +static void +ztclose(Chan* c) +{ + USED(c); +} + +static long +ztread(Chan* c, void* a, long n, vlong offset) +{ + uchar sysreg; + char buf[256]; + + USED(offset); + + switch(c->qid.path & ~CHDIR) { + case Qdir: + return devdirread(c, a, n, zttab, nelem(zttab), devgen); + case Qsysid: { + ulong rev; + sysreg = inb(0xe3); + rev = (ulong) (sysreg & 0x7f); + sysreg = inb(0xe2); + sprint(buf, "Board Rev: %lud\nSerial #: %lud\n", rev, (ulong)(sysreg >> 4)); + return readstr(offset, a, n, buf); + }; + case Qwatchdog: + sysreg = inb(0x78); + if((sysreg & 1) == 1) { + n = readstr(offset, a, n, "enabled"); + } else { + n = readstr(offset, a, n, "disabled"); + } + return n; + case Qledctl: + { + char usr1[6], usr2[6]; + sysreg = inb(0xe2); + switch( sysreg & 3 ) { + case 0: + case 3: + sprint(usr1, "off"); + break; + case 1: + sprint(usr1, "red"); + break; + case 2: + sprint(usr1, "green"); + }; + switch( (sysreg >> 2) & 3) { + case 0: + case 3: + sprint(usr2, "off"); + break; + case 1: + sprint(usr2, "red"); + break; + case 2: + sprint(usr2, "green"); + }; + sprint(buf, "usr1: %s\nusr2: %s\n",usr1, usr2); + return readstr(offset, a, n, buf); + }; + case Qpower: + sysreg = inb(0xe4); + sprint(buf, "DEG#: %d\nFAL#: %d\n", (sysreg & 2), (sysreg & 1)); + return readstr(offset, a, n, buf); + case Qswitch: + sysreg = inb(0xe4); + sprint(buf, "%d %d %d %d", (sysreg & (1<<6)), (sysreg & (1<<5)), (sysreg & (1<<4)), (sysreg & (1<<3))); + return readstr(offset, a, n, buf); + case Qstat: { + char bus[10],cpu[20], mode[20], boot[20]; + + sysreg = inb(0xe5); + switch (sysreg & 0x7) { + case 1: + sprint(bus, "66 MHz"); + break; + case 2: + sprint(bus, "60 MHz"); + break; + case 3: + sprint(bus, "50 MHz"); + break; + default: + sprint(bus, "unknown"); + }; + switch ((sysreg>>3)&0x7) { + case 0: + sprint(cpu, "75, 90, 100 MHz"); + break; + case 1: + sprint(cpu, "120, 133 MHz"); + break; + case 2: + sprint(cpu, "180, 200 MHz"); + break; + case 3: + sprint(cpu, "150, 166 MHz"); + default: + sprint(cpu, "unknown"); + }; + if(sysreg & (1<<6)) + sprint(mode, "Port 80 test mode"); + else + sprint(mode, "Normal decode"); + if(sysreg & (1<<7)) + sprint(boot,"EEPROM"); + else + sprint(boot,"Flash"); + sprint(buf,"Bus Frequency: %s\nPentium: %s\nTest Mode Status: %s\nBIOS Boot ROM: %s\n", + bus, cpu, mode, boot); + return readstr(offset, a, n, buf); + }; + default: + n=0; + break; + } + return n; +} + + + +static long +ztwrite(Chan* c, void *vp, long n, vlong offset) +{ + uchar sysreg; + char buf[256]; + char *a; + int nf; + char *fields[3]; + + a = vp; + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + strncpy(buf, a, n); + buf[n] = 0; + + USED(a, offset); + + switch(c->qid.path & ~CHDIR){ + case Qwatchdog: + sysreg = inb(0x78); + + if(strncmp(buf, "enable", 6) == 0) { + if((sysreg & 1) != 1) + addclock0link(watchdog_strobe); + break; + } + n = 0; + error(Ebadarg); + case Qledctl: + nf = getfields(buf, fields, 3, 1, " \t\n"); + if(nf < 2) { + error(Ebadarg); + n = 0; + break; + } + sysreg = inb(0xe2); + USED(sysreg); + if(strncmp(fields[0],"usr1", 4)==0) { + sysreg &= ~3; + if(strncmp(fields[1], "off", 3)==0) { + outb(0xe2, sysreg); + break; + } + if(strncmp(fields[1], "red", 3)==0) { + sysreg |= 1; + outb(0xe2, sysreg); + break; + } + if(strncmp(fields[1], "green", 5)==0) { + sysreg |= 2; + outb(0xe2, sysreg); + break; + } + } + if(strncmp(fields[0],"usr2", 4)==0) { + sysreg &= ~(3 << 2); + if(strncmp(fields[1], "off", 3)==0) { + outb(0xe2, sysreg); + break; + } + if(strncmp(fields[1], "red", 3)==0) { + sysreg |= (1 << 2); + outb(0xe2, sysreg); + break; + } + if(strncmp(fields[1], "green", 5)==0) { + sysreg |= (2 << 2); + outb(0xe2, sysreg); + break; + } + } + n = 0; + error(Ebadarg); + default: + error(Ebadusefd); + } + return n; +} + + + +Dev zt5512devtab = { /* defaults in dev.c */ + 'Z', + "Ziatech5512", + + ztreset, /* devreset */ + devinit, /* devinit */ + ztattach, + devdetach, + devclone, /* devclone */ + ztwalk, + ztstat, + ztopen, + devcreate, /* devcreate */ + ztclose, + ztread, + devbread, /* devbread */ + ztwrite, + devbwrite, /* devbwrite */ + devremove, /* devremove */ + devwstat, /* devwstat */ +}; diff --git a/os/pc/dma.c b/os/pc/dma.c new file mode 100644 index 00000000..9da7bddd --- /dev/null +++ b/os/pc/dma.c @@ -0,0 +1,237 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +typedef struct DMAport DMAport; +typedef struct DMA DMA; +typedef struct DMAxfer DMAxfer; + +/* + * state of a dma transfer + */ +struct DMAxfer +{ + ulong bpa; /* bounce buffer physical address */ + void* bva; /* bounce buffer virtual address */ + int blen; /* bounce buffer length */ + void* va; /* virtual address destination/src */ + long len; /* bytes to be transferred */ + int isread; +}; + +/* + * the dma controllers. the first half of this structure specifies + * the I/O ports used by the DMA controllers. + */ +struct DMAport +{ + uchar addr[4]; /* current address (4 channels) */ + uchar count[4]; /* current count (4 channels) */ + uchar page[4]; /* page registers (4 channels) */ + uchar cmd; /* command status register */ + uchar req; /* request registers */ + uchar sbm; /* single bit mask register */ + uchar mode; /* mode register */ + uchar cbp; /* clear byte pointer */ + uchar mc; /* master clear */ + uchar cmask; /* clear mask register */ + uchar wam; /* write all mask register bit */ +}; + +struct DMA +{ + DMAport; + int shift; + Lock; + DMAxfer x[4]; +}; + +DMA dma[2] = { + { 0x00, 0x02, 0x04, 0x06, + 0x01, 0x03, 0x05, 0x07, + 0x87, 0x83, 0x81, 0x82, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0 }, + + { 0xc0, 0xc4, 0xc8, 0xcc, + 0xc2, 0xc6, 0xca, 0xce, + 0x8f, 0x8b, 0x89, 0x8a, + 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde, + 1 }, +}; + +/* + * DMA must be in the first 16MB. This gets called early by the + * initialisation routines of any devices which require DMA to ensure + * the allocated bounce buffers are below the 16MB limit. + */ +int +dmainit(int chan, int maxtransfer) +{ + DMA *dp; + DMAxfer *xp; + static int once; + + if(once == 0){ + if(ioalloc(0x00, 0x10, 0, "dma") < 0 + || ioalloc(0x80, 0x10, 0, "dma") < 0 + || ioalloc(0xd0, 0x10, 0, "dma") < 0) + panic("dmainit"); + once = 1; + } + + if(maxtransfer > 64*1024) + maxtransfer = 64*1024; + + dp = &dma[(chan>>2)&1]; + chan = chan & 3; + xp = &dp->x[chan]; + if(xp->bva != nil){ + if(xp->blen < maxtransfer) + return 1; + return 0; + } + + xp->bva = xspanalloc(maxtransfer, BY2PG, 64*1024); + if(xp->bva == nil) + return 1; + xp->bpa = PADDR(xp->bva); + if(xp->bpa >= 16*MB){ + /* + * This will panic with the current + * implementation of xspanalloc(). + xfree(xp->bva); + */ + xp->bva = nil; + return 1; + } + xp->blen = maxtransfer; + xp->len = 0; + xp->isread = 0; + + return 0; +} + +/* + * setup a dma transfer. if the destination is not in kernel + * memory, allocate a page for the transfer. + * + * we assume BIOS has set up the command register before we + * are booted. + * + * return the updated transfer length (we can't transfer across 64k + * boundaries) + */ +long +dmasetup(int chan, void *va, long len, int isread) +{ + DMA *dp; + ulong pa; + uchar mode; + DMAxfer *xp; + + dp = &dma[(chan>>2)&1]; + chan = chan & 3; + xp = &dp->x[chan]; + + /* + * if this isn't kernel memory or crossing 64k boundary or above 16 meg + * use the bounce buffer. + */ + pa = PADDR(va); + if((((ulong)va)&0xF0000000) != KZERO + || (pa&0xFFFF0000) != ((pa+len)&0xFFFF0000) + || pa >= 16*MB) { + if(xp->bva == nil) + return -1; + if(len > xp->blen) + len = xp->blen; + if(!isread) + memmove(xp->bva, va, len); + xp->va = va; + xp->len = len; + xp->isread = isread; + pa = xp->bpa; + } + else + xp->len = 0; + + /* + * this setup must be atomic + */ + ilock(dp); + mode = (isread ? 0x44 : 0x48) | chan; + outb(dp->mode, mode); /* single mode dma (give CPU a chance at mem) */ + outb(dp->page[chan], pa>>16); + outb(dp->cbp, 0); /* set count & address to their first byte */ + outb(dp->addr[chan], pa>>dp->shift); /* set address */ + outb(dp->addr[chan], pa>>(8+dp->shift)); + outb(dp->count[chan], (len>>dp->shift)-1); /* set count */ + outb(dp->count[chan], ((len>>dp->shift)-1)>>8); + outb(dp->sbm, chan); /* enable the channel */ + iunlock(dp); + + return len; +} + +int +dmadone(int chan) +{ + DMA *dp; + + dp = &dma[(chan>>2)&1]; + chan = chan & 3; + + return inb(dp->cmd) & (1<>2)&1]; + chan = chan & 3; + + /* + * disable the channel + */ + ilock(dp); + outb(dp->sbm, 4|chan); + iunlock(dp); + + xp = &dp->x[chan]; + if(xp->len == 0 || !xp->isread) + return; + + /* + * copy out of temporary page + */ + memmove(xp->va, xp->bva, xp->len); + xp->len = 0; +} + +/* +int +dmacount(int chan) +{ + int retval; + DMA *dp; + + dp = &dma[(chan>>2)&1]; + outb(dp->cbp, 0); + retval = inb(dp->count[chan]); + retval |= inb(dp->count[chan]) << 8; + return((retval<shift)+1); +} + */ diff --git a/os/pc/ether2000.c b/os/pc/ether2000.c new file mode 100644 index 00000000..4ea8fd4f --- /dev/null +++ b/os/pc/ether2000.c @@ -0,0 +1,230 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" +#include "ether8390.h" + +/* + * Driver written for the 'Notebook Computer Ethernet LAN Adapter', + * a plug-in to the bus-slot on the rear of the Gateway NOMAD 425DXL + * laptop. The manual says NE2000 compatible. + * The interface appears to be pretty well described in the National + * Semiconductor Local Area Network Databook (1992) as one of the + * AT evaluation cards. + * + * The NE2000 is really just a DP8390[12] plus a data port + * and a reset port. + */ +enum { + Data = 0x10, /* offset from I/O base of data port */ + Reset = 0x1F, /* offset from I/O base of reset port */ +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + Pcidev* pcidev; + Ctlr* next; + int active; +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +static struct { + char* name; + int id; +} ne2000pci[] = { + { "Realtek 8029", (0x8029<<16)|0x10EC, }, + { "Winbond 89C940", (0x0940<<16)|0x1050, }, + { nil }, +}; + +static Ctlr* +ne2000match(Ether* edev, int id) +{ + int port; + Pcidev *p; + Ctlr *ctlr; + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + p = ctlr->pcidev; + if(((p->did<<16)|p->vid) != id) + continue; + port = p->mem[0].bar & ~0x01; + if(edev->port != 0 && edev->port != port) + continue; + + /* + * It suffices to fill these in, + * the rest is gleaned from the card. + */ + edev->port = port; + edev->irq = p->intl; + + ctlr->active = 1; + + return ctlr; + } + + return nil; +} + +static void +ne2000pnp(Ether* edev) +{ + int i, id; + Pcidev *p; + Ctlr *ctlr; + + /* + * Make a list of all ethernet controllers + * if not already done. + */ + if(ctlrhead == nil){ + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + ctlr = malloc(sizeof(Ctlr)); + ctlr->pcidev = p; + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } + } + + /* + * Is it a card with an unrecognised vid+did? + * Normally a search is made through all the found controllers + * for one which matches any of the known vid+did pairs. + * If a vid+did pair is specified a search is made for that + * specific controller only. + */ + id = 0; + for(i = 0; i < edev->nopt; i++){ + if(cistrncmp(edev->opt[i], "id=", 3) == 0) + id = strtol(&edev->opt[i][3], nil, 0); + } + + if(id != 0) + ne2000match(edev, id); + else for(i = 0; ne2000pci[i].name; i++){ + if(ne2000match(edev, ne2000pci[i].id) != nil) + break; + } +} + +static int +ne2000reset(Ether* edev) +{ + static int first; + ushort buf[16]; + ulong port; + Dp8390 *dp8390; + int i; + uchar ea[Eaddrlen]; + + if(edev->port == 0) + ne2000pnp(edev); + + /* + * Set up the software configuration. + * Use defaults for irq, mem and size + * if not specified. + * Must have a port, no more default. + */ + if(edev->port == 0) + return -1; + if(edev->irq == 0) + edev->irq = 2; + if(edev->mem == 0) + edev->mem = 0x4000; + if(edev->size == 0) + edev->size = 16*1024; + port = edev->port; + + if(ioalloc(edev->port, 0x20, 0, "ne2000") < 0) + return -1; + + edev->ctlr = malloc(sizeof(Dp8390)); + dp8390 = edev->ctlr; + dp8390->width = 2; + dp8390->ram = 0; + + dp8390->port = port; + dp8390->data = port+Data; + + dp8390->tstart = HOWMANY(edev->mem, Dp8390BufSz); + dp8390->pstart = dp8390->tstart + HOWMANY(sizeof(Etherpkt), Dp8390BufSz); + dp8390->pstop = dp8390->tstart + HOWMANY(edev->size, Dp8390BufSz); + + dp8390->dummyrr = 1; + for(i = 0; i < edev->nopt; i++){ + if(strcmp(edev->opt[i], "nodummyrr")) + continue; + dp8390->dummyrr = 0; + break; + } + + /* + * Reset the board. This is done by doing a read + * followed by a write to the Reset address. + */ + buf[0] = inb(port+Reset); + delay(2); + outb(port+Reset, buf[0]); + delay(2); + + /* + * Init the (possible) chip, then use the (possible) + * chip to read the (possible) PROM for ethernet address + * and a marker byte. + * Could just look at the DP8390 command register after + * initialisation has been tried, but that wouldn't be + * enough, there are other ethernet boards which could + * match. + */ + dp8390reset(edev); + memset(buf, 0, sizeof(buf)); + dp8390read(dp8390, buf, 0, sizeof(buf)); + if((buf[0x0E] & 0xFF) != 0x57 || (buf[0x0F] & 0xFF) != 0x57){ + iofree(edev->port); + free(edev->ctlr); + return -1; + } + + /* + * Stupid machine. Shorts were asked for, + * shorts were delivered, although the PROM is a byte array. + * Set the ethernet address. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, edev->ea, Eaddrlen) == 0){ + for(i = 0; i < sizeof(edev->ea); i++) + edev->ea[i] = buf[i]; + } + dp8390setea(edev); + + return 0; +} + +void +ether2000link(void) +{ + addethercard("NE2000", ne2000reset); +} diff --git a/os/pc/ether2114x.c b/os/pc/ether2114x.c new file mode 100644 index 00000000..43bae880 --- /dev/null +++ b/os/pc/ether2114x.c @@ -0,0 +1,1828 @@ +/* + * Digital Semiconductor DECchip 2114x PCI Fast Ethernet LAN Controller. + * To do: + * thresholds; + * ring sizing; + * handle more error conditions; + * tidy setup packet mess; + * push initialisation back to attach; + * full SROM decoding. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +#define DEBUG (0) +#define debug if(DEBUG)print + +enum { + Nrde = 64, + Ntde = 64, +}; + +#define Rbsz ROUNDUP(sizeof(Etherpkt)+4, 4) + +enum { /* CRS0 - Bus Mode */ + Swr = 0x00000001, /* Software Reset */ + Bar = 0x00000002, /* Bus Arbitration */ + Dsl = 0x0000007C, /* Descriptor Skip Length (field) */ + Ble = 0x00000080, /* Big/Little Endian */ + Pbl = 0x00003F00, /* Programmable Burst Length (field) */ + Cal = 0x0000C000, /* Cache Alignment (field) */ + Cal8 = 0x00004000, /* 8 longword boundary alignment */ + Cal16 = 0x00008000, /* 16 longword boundary alignment */ + Cal32 = 0x0000C000, /* 32 longword boundary alignment */ + Tap = 0x000E0000, /* Transmit Automatic Polling (field) */ + Dbo = 0x00100000, /* Descriptor Byte Ordering Mode */ + Rml = 0x00200000, /* Read Multiple */ +}; + +enum { /* CSR[57] - Status and Interrupt Enable */ + Ti = 0x00000001, /* Transmit Interrupt */ + Tps = 0x00000002, /* Transmit Process Stopped */ + Tu = 0x00000004, /* Transmit buffer Unavailable */ + Tjt = 0x00000008, /* Transmit Jabber Timeout */ + Unf = 0x00000020, /* transmit UNderFlow */ + Ri = 0x00000040, /* Receive Interrupt */ + Ru = 0x00000080, /* Receive buffer Unavailable */ + Rps = 0x00000100, /* Receive Process Stopped */ + Rwt = 0x00000200, /* Receive Watchdog Timeout */ + Eti = 0x00000400, /* Early Transmit Interrupt */ + Gte = 0x00000800, /* General purpose Timer Expired */ + Fbe = 0x00002000, /* Fatal Bit Error */ + Ais = 0x00008000, /* Abnormal Interrupt Summary */ + Nis = 0x00010000, /* Normal Interrupt Summary */ + Rs = 0x000E0000, /* Receive process State (field) */ + Ts = 0x00700000, /* Transmit process State (field) */ + Eb = 0x03800000, /* Error bits */ +}; + +enum { /* CSR6 - Operating Mode */ + Hp = 0x00000001, /* Hash/Perfect receive filtering mode */ + Sr = 0x00000002, /* Start/stop Receive */ + Ho = 0x00000004, /* Hash-Only filtering mode */ + Pb = 0x00000008, /* Pass Bad frames */ + If = 0x00000010, /* Inverse Filtering */ + Sb = 0x00000020, /* Start/stop Backoff counter */ + Pr = 0x00000040, /* Promiscuous Mode */ + Pm = 0x00000080, /* Pass all Multicast */ + Fd = 0x00000200, /* Full Duplex mode */ + Om = 0x00000C00, /* Operating Mode (field) */ + Fc = 0x00001000, /* Force Collision */ + St = 0x00002000, /* Start/stop Transmission Command */ + Tr = 0x0000C000, /* ThReshold control bits (field) */ + Tr128 = 0x00000000, + Tr256 = 0x00004000, + Tr512 = 0x00008000, + Tr1024 = 0x0000C000, + Ca = 0x00020000, /* CApture effect enable */ + Ps = 0x00040000, /* Port Select */ + Hbd = 0x00080000, /* HeartBeat Disable */ + Imm = 0x00100000, /* IMMediate mode */ + Sf = 0x00200000, /* Store and Forward */ + Ttm = 0x00400000, /* Transmit Threshold Mode */ + Pcs = 0x00800000, /* PCS function */ + Scr = 0x01000000, /* SCRambler mode */ + Mbo = 0x02000000, /* Must Be One */ + Ra = 0x40000000, /* Receive All */ + Sc = 0x80000000, /* Special Capture effect enable */ + + TrMODE = Tr512, /* default transmission threshold */ +}; + +enum { /* CSR9 - ROM and MII Management */ + Scs = 0x00000001, /* serial ROM chip select */ + Sclk = 0x00000002, /* serial ROM clock */ + Sdi = 0x00000004, /* serial ROM data in */ + Sdo = 0x00000008, /* serial ROM data out */ + Ss = 0x00000800, /* serial ROM select */ + Wr = 0x00002000, /* write */ + Rd = 0x00004000, /* read */ + + Mdc = 0x00010000, /* MII management clock */ + Mdo = 0x00020000, /* MII management write data */ + Mii = 0x00040000, /* MII management operation mode (W) */ + Mdi = 0x00080000, /* MII management data in */ +}; + +enum { /* CSR12 - General-Purpose Port */ + Gpc = 0x00000100, /* General Purpose Control */ +}; + +typedef struct Des { + int status; + int control; + ulong addr; + Block* bp; +} Des; + +enum { /* status */ + Of = 0x00000001, /* Rx: OverFlow */ + Ce = 0x00000002, /* Rx: CRC Error */ + Db = 0x00000004, /* Rx: Dribbling Bit */ + Re = 0x00000008, /* Rx: Report on MII Error */ + Rw = 0x00000010, /* Rx: Receive Watchdog */ + Ft = 0x00000020, /* Rx: Frame Type */ + Cs = 0x00000040, /* Rx: Collision Seen */ + Tl = 0x00000080, /* Rx: Frame too Long */ + Ls = 0x00000100, /* Rx: Last deScriptor */ + Fs = 0x00000200, /* Rx: First deScriptor */ + Mf = 0x00000400, /* Rx: Multicast Frame */ + Rf = 0x00000800, /* Rx: Runt Frame */ + Dt = 0x00003000, /* Rx: Data Type (field) */ + De = 0x00004000, /* Rx: Descriptor Error */ + Fl = 0x3FFF0000, /* Rx: Frame Length (field) */ + Ff = 0x40000000, /* Rx: Filtering Fail */ + + Def = 0x00000001, /* Tx: DEFerred */ + Uf = 0x00000002, /* Tx: UnderFlow error */ + Lf = 0x00000004, /* Tx: Link Fail report */ + Cc = 0x00000078, /* Tx: Collision Count (field) */ + Hf = 0x00000080, /* Tx: Heartbeat Fail */ + Ec = 0x00000100, /* Tx: Excessive Collisions */ + Lc = 0x00000200, /* Tx: Late Collision */ + Nc = 0x00000400, /* Tx: No Carrier */ + Lo = 0x00000800, /* Tx: LOss of carrier */ + To = 0x00004000, /* Tx: Transmission jabber timeOut */ + + Es = 0x00008000, /* [RT]x: Error Summary */ + Own = 0x80000000, /* [RT]x: OWN bit */ +}; + +enum { /* control */ + Bs1 = 0x000007FF, /* [RT]x: Buffer 1 Size */ + Bs2 = 0x003FF800, /* [RT]x: Buffer 2 Size */ + + Ch = 0x01000000, /* [RT]x: second address CHained */ + Er = 0x02000000, /* [RT]x: End of Ring */ + + Ft0 = 0x00400000, /* Tx: Filtering Type 0 */ + Dpd = 0x00800000, /* Tx: Disabled PaDding */ + Ac = 0x04000000, /* Tx: Add CRC disable */ + Set = 0x08000000, /* Tx: SETup packet */ + Ft1 = 0x10000000, /* Tx: Filtering Type 1 */ + Fseg = 0x20000000, /* Tx: First SEGment */ + Lseg = 0x40000000, /* Tx: Last SEGment */ + Ic = 0x80000000, /* Tx: Interrupt on Completion */ +}; + +enum { /* PHY registers */ + Bmcr = 0, /* Basic Mode Control */ + Bmsr = 1, /* Basic Mode Status */ + Phyidr1 = 2, /* PHY Identifier #1 */ + Phyidr2 = 3, /* PHY Identifier #2 */ + Anar = 4, /* Auto-Negotiation Advertisment */ + Anlpar = 5, /* Auto-Negotiation Link Partner Ability */ + Aner = 6, /* Auto-Negotiation Expansion */ +}; + +enum { /* Variants */ + Tulip0 = (0x0009<<16)|0x1011, + Tulip1 = (0x0014<<16)|0x1011, + Tulip3 = (0x0019<<16)|0x1011, + Pnic = (0x0002<<16)|0x11AD, + Pnic2 = (0xC115<<16)|0x11AD, + CentaurP = (0x0985<<16)|0x1317, +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; /* (pcidev->did<<16)|pcidev->vid */ + + uchar* srom; + int sromsz; /* address size in bits */ + uchar* sromea; /* MAC address */ + uchar* leaf; + int sct; /* selected connection type */ + int k; /* info block count */ + uchar* infoblock[16]; + int sctk; /* sct block index */ + int curk; /* current block index */ + uchar* type5block; + + int phy[32]; /* logical to physical map */ + int phyreset; /* reset bitmap */ + int curphyad; + int fdx; + int ttm; + + uchar fd; /* option */ + int medium; /* option */ + + int csr6; /* CSR6 - operating mode */ + int mask; /* CSR[57] - interrupt mask */ + int mbps; + + Lock lock; + + Des* rdr; /* receive descriptor ring */ + int nrdr; /* size of rdr */ + int rdrx; /* index into rdr */ + + Lock tlock; + Des* tdr; /* transmit descriptor ring */ + int ntdr; /* size of tdr */ + int tdrh; /* host index into tdr */ + int tdri; /* interface index into tdr */ + int ntq; /* descriptors active */ + int ntqmax; + Block* setupbp; + + ulong of; /* receive statistics */ + ulong ce; + ulong cs; + ulong tl; + ulong rf; + ulong de; + + ulong ru; + ulong rps; + ulong rwt; + + ulong uf; /* transmit statistics */ + ulong ec; + ulong lc; + ulong nc; + ulong lo; + ulong to; + + ulong tps; + ulong tu; + ulong tjt; + ulong unf; +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +#define csr32r(c, r) (inl((c)->port+((r)*8))) +#define csr32w(c, r, l) (outl((c)->port+((r)*8), (ulong)(l))) + +static void +promiscuous(void* arg, int on) +{ + Ctlr *ctlr; + + ctlr = ((Ether*)arg)->ctlr; + ilock(&ctlr->lock); + if(on) + ctlr->csr6 |= Pr; + else + ctlr->csr6 &= ~Pr; + csr32w(ctlr, 6, ctlr->csr6); + iunlock(&ctlr->lock); +} + +/* multicast already on, don't need to do anything */ +static void +multicast(void*, uchar*, int) +{ +} + +static void +attach(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->lock); + if(!(ctlr->csr6 & Sr)){ + ctlr->csr6 |= Sr; + csr32w(ctlr, 6, ctlr->csr6); + } + iunlock(&ctlr->lock); +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + Ctlr *ctlr; + char *buf, *p; + int i, l, len; + + ctlr = ether->ctlr; + + ether->crcs = ctlr->ce; + ether->frames = ctlr->rf+ctlr->cs; + ether->buffs = ctlr->de+ctlr->tl; + ether->overflows = ctlr->of; + + if(n == 0) + return 0; + + p = malloc(READSTR); + l = snprint(p, READSTR, "Overflow: %lud\n", ctlr->of); + l += snprint(p+l, READSTR-l, "Ru: %lud\n", ctlr->ru); + l += snprint(p+l, READSTR-l, "Rps: %lud\n", ctlr->rps); + l += snprint(p+l, READSTR-l, "Rwt: %lud\n", ctlr->rwt); + l += snprint(p+l, READSTR-l, "Tps: %lud\n", ctlr->tps); + l += snprint(p+l, READSTR-l, "Tu: %lud\n", ctlr->tu); + l += snprint(p+l, READSTR-l, "Tjt: %lud\n", ctlr->tjt); + l += snprint(p+l, READSTR-l, "Unf: %lud\n", ctlr->unf); + l += snprint(p+l, READSTR-l, "CRC Error: %lud\n", ctlr->ce); + l += snprint(p+l, READSTR-l, "Collision Seen: %lud\n", ctlr->cs); + l += snprint(p+l, READSTR-l, "Frame Too Long: %lud\n", ctlr->tl); + l += snprint(p+l, READSTR-l, "Runt Frame: %lud\n", ctlr->rf); + l += snprint(p+l, READSTR-l, "Descriptor Error: %lud\n", ctlr->de); + l += snprint(p+l, READSTR-l, "Underflow Error: %lud\n", ctlr->uf); + l += snprint(p+l, READSTR-l, "Excessive Collisions: %lud\n", ctlr->ec); + l += snprint(p+l, READSTR-l, "Late Collision: %lud\n", ctlr->lc); + l += snprint(p+l, READSTR-l, "No Carrier: %lud\n", ctlr->nc); + l += snprint(p+l, READSTR-l, "Loss of Carrier: %lud\n", ctlr->lo); + l += snprint(p+l, READSTR-l, "Transmit Jabber Timeout: %lud\n", + ctlr->to); + l += snprint(p+l, READSTR-l, "csr6: %luX %uX\n", csr32r(ctlr, 6), + ctlr->csr6); + snprint(p+l, READSTR-l, "ntqmax: %d\n", ctlr->ntqmax); + ctlr->ntqmax = 0; + buf = a; + len = readstr(offset, buf, n, p); + if(offset > l) + offset -= l; + else + offset = 0; + buf += len; + n -= len; + + l = snprint(p, READSTR, "srom:"); + for(i = 0; i < (1<<(ctlr->sromsz)*sizeof(ushort)); i++){ + if(i && ((i & 0x0F) == 0)) + l += snprint(p+l, READSTR-l, "\n "); + l += snprint(p+l, READSTR-l, " %2.2uX", ctlr->srom[i]); + } + + snprint(p+l, READSTR-l, "\n"); + len += readstr(offset, buf, n, p); + free(p); + + return len; +} + +static void +txstart(Ether* ether) +{ + Ctlr *ctlr; + Block *bp; + Des *des; + int control; + + ctlr = ether->ctlr; + while(ctlr->ntq < (ctlr->ntdr-1)){ + if(ctlr->setupbp){ + bp = ctlr->setupbp; + ctlr->setupbp = 0; + control = Ic|Set|BLEN(bp); + } + else{ + bp = qget(ether->oq); + if(bp == nil) + break; + control = Ic|Lseg|Fseg|BLEN(bp); + } + + ctlr->tdr[PREV(ctlr->tdrh, ctlr->ntdr)].control &= ~Ic; + des = &ctlr->tdr[ctlr->tdrh]; + des->bp = bp; + des->addr = PCIWADDR(bp->rp); + des->control |= control; + ctlr->ntq++; + coherence(); + des->status = Own; + csr32w(ctlr, 1, 0); + ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr); + } + + if(ctlr->ntq > ctlr->ntqmax) + ctlr->ntqmax = ctlr->ntq; +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->tlock); + txstart(ether); + iunlock(&ctlr->tlock); +} + +static void +interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *ether; + int len, status; + Des *des; + Block *bp; + + ether = arg; + ctlr = ether->ctlr; + + while((status = csr32r(ctlr, 5)) & (Nis|Ais)){ + /* + * Acknowledge the interrupts and mask-out + * the ones that are implicitly handled. + */ + csr32w(ctlr, 5, status); + status &= (ctlr->mask & ~(Nis|Ti)); + + if(status & Ais){ + if(status & Tps) + ctlr->tps++; + if(status & Tu) + ctlr->tu++; + if(status & Tjt) + ctlr->tjt++; + if(status & Ru) + ctlr->ru++; + if(status & Rps) + ctlr->rps++; + if(status & Rwt) + ctlr->rwt++; + status &= ~(Ais|Rwt|Rps|Ru|Tjt|Tu|Tps); + } + + /* + * Received packets. + */ + if(status & Ri){ + des = &ctlr->rdr[ctlr->rdrx]; + while(!(des->status & Own)){ + if(des->status & Es){ + if(des->status & Of) + ctlr->of++; + if(des->status & Ce) + ctlr->ce++; + if(des->status & Cs) + ctlr->cs++; + if(des->status & Tl) + ctlr->tl++; + if(des->status & Rf) + ctlr->rf++; + if(des->status & De) + ctlr->de++; + } + else if(bp = iallocb(Rbsz)){ + len = ((des->status & Fl)>>16)-4; + des->bp->wp = des->bp->rp+len; + etheriq(ether, des->bp, 1); + des->bp = bp; + des->addr = PCIWADDR(bp->rp); + } + + des->control &= Er; + des->control |= Rbsz; + coherence(); + des->status = Own; + + ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr); + des = &ctlr->rdr[ctlr->rdrx]; + } + status &= ~Ri; + } + + /* + * Check the transmit side: + * check for Transmit Underflow and Adjust + * the threshold upwards; + * free any transmitted buffers and try to + * top-up the ring. + */ + if(status & Unf){ + ctlr->unf++; + ilock(&ctlr->lock); + csr32w(ctlr, 6, ctlr->csr6 & ~St); + switch(ctlr->csr6 & Tr){ + case Tr128: + len = Tr256; + break; + case Tr256: + len = Tr512; + break; + case Tr512: + len = Tr1024; + break; + default: + case Tr1024: + len = Sf; + break; + } + ctlr->csr6 = (ctlr->csr6 & ~Tr)|len; + csr32w(ctlr, 6, ctlr->csr6); + iunlock(&ctlr->lock); + csr32w(ctlr, 5, Tps); + status &= ~(Unf|Tps); + } + + ilock(&ctlr->tlock); + while(ctlr->ntq){ + des = &ctlr->tdr[ctlr->tdri]; + if(des->status & Own) + break; + + if(des->status & Es){ + if(des->status & Uf) + ctlr->uf++; + if(des->status & Ec) + ctlr->ec++; + if(des->status & Lc) + ctlr->lc++; + if(des->status & Nc) + ctlr->nc++; + if(des->status & Lo) + ctlr->lo++; + if(des->status & To) + ctlr->to++; + ether->oerrs++; + } + + freeb(des->bp); + des->control &= Er; + + ctlr->ntq--; + ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr); + } + txstart(ether); + iunlock(&ctlr->tlock); + + /* + * Anything left not catered for? + */ + if(status) + panic("#l%d: status %8.8uX\n", ether->ctlrno, status); + } +} + +static void +ctlrinit(Ether* ether) +{ + Ctlr *ctlr; + Des *des; + Block *bp; + int i; + uchar bi[Eaddrlen*2]; + + ctlr = ether->ctlr; + + /* + * Allocate and initialise the receive ring; + * allocate and initialise the transmit ring; + * unmask interrupts and start the transmit side; + * create and post a setup packet to initialise + * the physical ethernet address. + */ + ctlr->rdr = xspanalloc(ctlr->nrdr*sizeof(Des), 8*sizeof(ulong), 0); + for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){ + des->bp = iallocb(Rbsz); + if(des->bp == nil) + panic("can't allocate ethernet receive ring\n"); + des->status = Own; + des->control = Rbsz; + des->addr = PCIWADDR(des->bp->rp); + } + ctlr->rdr[ctlr->nrdr-1].control |= Er; + ctlr->rdrx = 0; + csr32w(ctlr, 3, PCIWADDR(ctlr->rdr)); + + ctlr->tdr = xspanalloc(ctlr->ntdr*sizeof(Des), 8*sizeof(ulong), 0); + ctlr->tdr[ctlr->ntdr-1].control |= Er; + ctlr->tdrh = 0; + ctlr->tdri = 0; + csr32w(ctlr, 4, PCIWADDR(ctlr->tdr)); + + /* + * Clear any bits in the Status Register (CSR5) as + * the PNIC has a different reset value from a true 2114x. + */ + ctlr->mask = Nis|Ais|Fbe|Rwt|Rps|Ru|Ri|Unf|Tjt|Tps|Ti; + csr32w(ctlr, 5, ctlr->mask); + csr32w(ctlr, 7, ctlr->mask); + ctlr->csr6 |= St|Pm; + csr32w(ctlr, 6, ctlr->csr6); + + for(i = 0; i < Eaddrlen/2; i++){ + bi[i*4] = ether->ea[i*2]; + bi[i*4+1] = ether->ea[i*2+1]; + bi[i*4+2] = ether->ea[i*2+1]; + bi[i*4+3] = ether->ea[i*2]; + } + bp = iallocb(Eaddrlen*2*16); + if(bp == nil) + panic("can't allocate ethernet setup buffer\n"); + memset(bp->rp, 0xFF, sizeof(bi)); + for(i = sizeof(bi); i < sizeof(bi)*16; i += sizeof(bi)) + memmove(bp->rp+i, bi, sizeof(bi)); + bp->wp += sizeof(bi)*16; + + ctlr->setupbp = bp; + ether->oq = qopen(256*1024, Qmsg, 0, 0); + transmit(ether); +} + +static void +csr9w(Ctlr* ctlr, int data) +{ + csr32w(ctlr, 9, data); + microdelay(1); +} + +static int +miimdi(Ctlr* ctlr, int n) +{ + int data, i; + + /* + * Read n bits from the MII Management Register. + */ + data = 0; + for(i = n-1; i >= 0; i--){ + if(csr32r(ctlr, 9) & Mdi) + data |= (1<= 0; i--){ + if(bits & (1<id == Pnic){ + i = 1000; + csr32w(ctlr, 20, 0x60020000|(phyad<<23)|(regad<<18)); + do{ + microdelay(1); + data = csr32r(ctlr, 20); + }while((data & 0x80000000) && --i); + + if(i == 0) + return -1; + return data & 0xFFFF; + } + + /* + * Preamble; + * ST+OP+PHYAD+REGAD; + * TA + 16 data bits. + */ + miimdo(ctlr, 0xFFFFFFFF, 32); + miimdo(ctlr, 0x1800|(phyad<<5)|regad, 14); + data = miimdi(ctlr, 18); + + if(data & 0x10000) + return -1; + + return data & 0xFFFF; +} + +static void +miiw(Ctlr* ctlr, int phyad, int regad, int data) +{ + /* + * Preamble; + * ST+OP+PHYAD+REGAD+TA + 16 data bits; + * Z. + */ + miimdo(ctlr, 0xFFFFFFFF, 32); + data &= 0xFFFF; + data |= (0x05<<(5+5+2+16))|(phyad<<(5+2+16))|(regad<<(2+16))|(0x02<<16); + miimdo(ctlr, data, 32); + csr9w(ctlr, Mdc); + csr9w(ctlr, 0); +} + +static int +sromr(Ctlr* ctlr, int r) +{ + int i, op, data, size; + + if(ctlr->id == Pnic){ + i = 1000; + csr32w(ctlr, 19, 0x600|r); + do{ + microdelay(1); + data = csr32r(ctlr, 19); + }while((data & 0x80000000) && --i); + + if(ctlr->sromsz == 0) + ctlr->sromsz = 6; + + return csr32r(ctlr, 9) & 0xFFFF; + } + + /* + * This sequence for reading a 16-bit register 'r' + * in the EEPROM is taken (pretty much) straight from Section + * 7.4 of the 21140 Hardware Reference Manual. + */ +reread: + csr9w(ctlr, Rd|Ss); + csr9w(ctlr, Rd|Ss|Scs); + csr9w(ctlr, Rd|Ss|Sclk|Scs); + csr9w(ctlr, Rd|Ss); + + op = 0x06; + for(i = 3-1; i >= 0; i--){ + data = Rd|Ss|(((op>>i) & 0x01)<<2)|Scs; + csr9w(ctlr, data); + csr9w(ctlr, data|Sclk); + csr9w(ctlr, data); + } + + /* + * First time through must work out the EEPROM size. + * This doesn't seem to work on the 21041 as implemented + * in Virtual PC for the Mac, so wire any 21041 to 6, + * it's the only 21041 this code will ever likely see. + */ + if((size = ctlr->sromsz) == 0){ + if(ctlr->id == Tulip1) + ctlr->sromsz = size = 6; + else + size = 8; + } + + for(size = size-1; size >= 0; size--){ + data = Rd|Ss|(((r>>size) & 0x01)<<2)|Scs; + csr9w(ctlr, data); + csr9w(ctlr, data|Sclk); + csr9w(ctlr, data); + microdelay(1); + if(ctlr->sromsz == 0 && !(csr32r(ctlr, 9) & Sdo)) + break; + } + + data = 0; + for(i = 16-1; i >= 0; i--){ + csr9w(ctlr, Rd|Ss|Sclk|Scs); + if(csr32r(ctlr, 9) & Sdo) + data |= (1<sromsz == 0){ + ctlr->sromsz = 8-size; + goto reread; + } + + return data & 0xFFFF; +} + +static void +shutdown(Ether* ether) +{ + Ctlr *ctlr = ether->ctlr; + +print("ether2114x shutting down\n"); + csr32w(ctlr, 0, Swr); +} + +static void +softreset(Ctlr* ctlr) +{ + /* + * Soft-reset the controller and initialise bus mode. + * Delay should be >= 50 PCI cycles (2×S @ 25MHz). + */ + csr32w(ctlr, 0, Swr); + microdelay(10); + csr32w(ctlr, 0, Rml|Cal16); + delay(1); +} + +static int +type5block(Ctlr* ctlr, uchar* block) +{ + int csr15, i, len; + + /* + * Reset or GPR sequence. Reset should be once only, + * before the GPR sequence. + * Note 'block' is not a pointer to the block head but + * a pointer to the data in the block starting at the + * reset length value so type5block can be used for the + * sequences contained in type 1 and type 3 blocks. + * The SROM docs state the 21140 type 5 block is the + * same as that for the 21143, but the two controllers + * use different registers and sequence-element lengths + * so the 21140 code here is a guess for a real type 5 + * sequence. + */ + len = *block++; + if(ctlr->id != Tulip3){ + for(i = 0; i < len; i++){ + csr32w(ctlr, 12, *block); + block++; + } + return len; + } + + for(i = 0; i < len; i++){ + csr15 = *block++<<16; + csr15 |= *block++<<24; + csr32w(ctlr, 15, csr15); + debug("%8.8uX ", csr15); + } + return 2*len; +} + +static int +typephylink(Ctlr* ctlr, uchar*) +{ + int an, bmcr, bmsr, csr6, x; + + /* + * Fail if + * auto-negotiataion enabled but not complete; + * no valid link established. + */ + bmcr = miir(ctlr, ctlr->curphyad, Bmcr); + miir(ctlr, ctlr->curphyad, Bmsr); + bmsr = miir(ctlr, ctlr->curphyad, Bmsr); + debug("bmcr 0x%2.2uX bmsr 0x%2.2uX\n", bmcr, bmsr); + if(((bmcr & 0x1000) && !(bmsr & 0x0020)) || !(bmsr & 0x0004)) + return 0; + + if(bmcr & 0x1000){ + an = miir(ctlr, ctlr->curphyad, Anar); + an &= miir(ctlr, ctlr->curphyad, Anlpar) & 0x3E0; + debug("an 0x%2.uX 0x%2.2uX 0x%2.2uX\n", + miir(ctlr, ctlr->curphyad, Anar), + miir(ctlr, ctlr->curphyad, Anlpar), + an); + + if(an & 0x0100) + x = 0x4000; + else if(an & 0x0080) + x = 0x2000; + else if(an & 0x0040) + x = 0x1000; + else if(an & 0x0020) + x = 0x0800; + else + x = 0; + } + else if((bmcr & 0x2100) == 0x2100) + x = 0x4000; + else if(bmcr & 0x2000){ + /* + * If FD capable, force it if necessary. + */ + if((bmsr & 0x4000) && ctlr->fd){ + miiw(ctlr, ctlr->curphyad, Bmcr, 0x2100); + x = 0x4000; + } + else + x = 0x2000; + } + else if(bmcr & 0x0100) + x = 0x1000; + else + x = 0x0800; + + csr6 = Sc|Mbo|Hbd|Ps|Ca|TrMODE|Sb; + if(ctlr->fdx & x) + csr6 |= Fd; + if(ctlr->ttm & x) + csr6 |= Ttm; + debug("csr6 0x%8.8uX 0x%8.8uX 0x%8.8luX\n", + csr6, ctlr->csr6, csr32r(ctlr, 6)); + if(csr6 != ctlr->csr6){ + ctlr->csr6 = csr6; + csr32w(ctlr, 6, csr6); + } + + return 1; +} + +static int +typephymode(Ctlr* ctlr, uchar* block, int wait) +{ + uchar *p; + int len, mc, nway, phyx, timeo; + + if(DEBUG){ + int i; + + len = (block[0] & ~0x80)+1; + for(i = 0; i < len; i++) + debug("%2.2uX ", block[i]); + debug("\n"); + } + + if(block[1] == 1) + len = 1; + else if(block[1] == 3) + len = 2; + else + return -1; + + /* + * Snarf the media capabilities, nway advertisment, + * FDX and TTM bitmaps. + */ + p = &block[5+len*block[3]+len*block[4+len*block[3]]]; + mc = *p++; + mc |= *p++<<8; + nway = *p++; + nway |= *p++<<8; + ctlr->fdx = *p++; + ctlr->fdx |= *p++<<8; + ctlr->ttm = *p++; + ctlr->ttm |= *p<<8; + debug("mc %4.4uX nway %4.4uX fdx %4.4uX ttm %4.4uX\n", + mc, nway, ctlr->fdx, ctlr->ttm); + USED(mc); + + phyx = block[2]; + ctlr->curphyad = ctlr->phy[phyx]; + + ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|TrMODE|Sb; + //csr32w(ctlr, 6, ctlr->csr6); + if(typephylink(ctlr, block)) + return 0; + + if(!(ctlr->phyreset & (1<type5block) + type5block(ctlr, &ctlr->type5block[2]); + else + type5block(ctlr, &block[4+len*block[3]]); + debug("\n"); + ctlr->phyreset |= (1<csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|TrMODE|Sb; + //csr32w(ctlr, 6, ctlr->csr6); + if(typephylink(ctlr, block)) + return 0; + + /* + * Turn off auto-negotiation, set the auto-negotiation + * advertisment register then start the auto-negotiation + * process again. + */ + miiw(ctlr, ctlr->curphyad, Bmcr, 0); + miiw(ctlr, ctlr->curphyad, Anar, nway|1); + miiw(ctlr, ctlr->curphyad, Bmcr, 0x1000); + + if(!wait) + return 0; + + for(timeo = 0; timeo < 45; timeo++){ + if(typephylink(ctlr, block)) + return 0; + delay(100); + } + + return -1; +} + +static int +typesymmode(Ctlr *ctlr, uchar *block, int wait) +{ + uint gpmode, gpdata, command; + + USED(wait); + gpmode = block[3] | ((uint) block[4] << 8); + gpdata = block[5] | ((uint) block[6] << 8); + command = (block[7] | ((uint) block[8] << 8)) & 0x71; + if (command & 0x8000) { + print("ether2114x.c: FIXME: handle type 4 mode blocks where cmd.active_invalid != 0\n"); + return -1; + } + csr32w(ctlr, 15, gpmode); + csr32w(ctlr, 15, gpdata); + ctlr->csr6 = (command & 0x71) << 18; + csr32w(ctlr, 6, ctlr->csr6); + return 0; +} + +static int +type2mode(Ctlr* ctlr, uchar* block, int) +{ + uchar *p; + int csr6, csr13, csr14, csr15, gpc, gpd; + + csr6 = Sc|Mbo|Ca|TrMODE|Sb; + debug("type2mode: medium 0x%2.2uX\n", block[2]); + + /* + * Don't attempt full-duplex + * unless explicitly requested. + */ + if((block[2] & 0x3F) == 0x04){ /* 10BASE-TFD */ + if(!ctlr->fd) + return -1; + csr6 |= Fd; + } + + /* + * Operating mode programming values from the datasheet + * unless media specific data is explicitly given. + */ + p = &block[3]; + if(block[2] & 0x40){ + csr13 = (block[4]<<8)|block[3]; + csr14 = (block[6]<<8)|block[5]; + csr15 = (block[8]<<8)|block[7]; + p += 6; + } + else switch(block[2] & 0x3F){ + default: + return -1; + case 0x00: /* 10BASE-T */ + csr13 = 0x00000001; + csr14 = 0x00007F3F; + csr15 = 0x00000008; + break; + case 0x01: /* 10BASE-2 */ + csr13 = 0x00000009; + csr14 = 0x00000705; + csr15 = 0x00000006; + break; + case 0x02: /* 10BASE-5 (AUI) */ + csr13 = 0x00000009; + csr14 = 0x00000705; + csr15 = 0x0000000E; + break; + case 0x04: /* 10BASE-TFD */ + csr13 = 0x00000001; + csr14 = 0x00007F3D; + csr15 = 0x00000008; + break; + } + gpc = *p++<<16; + gpc |= *p++<<24; + gpd = *p++<<16; + gpd |= *p<<24; + + csr32w(ctlr, 13, 0); + csr32w(ctlr, 14, csr14); + csr32w(ctlr, 15, gpc|csr15); + delay(10); + csr32w(ctlr, 15, gpd|csr15); + csr32w(ctlr, 13, csr13); + + ctlr->csr6 = csr6; + csr32w(ctlr, 6, ctlr->csr6); + + debug("type2mode: csr13 %8.8uX csr14 %8.8uX csr15 %8.8uX\n", + csr13, csr14, csr15); + debug("type2mode: gpc %8.8uX gpd %8.8uX csr6 %8.8uX\n", + gpc, gpd, csr6); + + return 0; +} + +static int +type0link(Ctlr* ctlr, uchar* block) +{ + int m, polarity, sense; + + m = (block[3]<<8)|block[2]; + sense = 1<<((m & 0x000E)>>1); + if(m & 0x0080) + polarity = sense; + else + polarity = 0; + + return (csr32r(ctlr, 12) & sense)^polarity; +} + +static int +type0mode(Ctlr* ctlr, uchar* block, int wait) +{ + int csr6, m, timeo; + + csr6 = Sc|Mbo|Hbd|Ca|TrMODE|Sb; +debug("type0: medium 0x%uX, fd %d: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n", + ctlr->medium, ctlr->fd, block[0], block[1], block[2], block[3]); + switch(block[0]){ + default: + break; + + case 0x04: /* 10BASE-TFD */ + case 0x05: /* 100BASE-TXFD */ + case 0x08: /* 100BASE-FXFD */ + /* + * Don't attempt full-duplex + * unless explicitly requested. + */ + if(!ctlr->fd) + return -1; + csr6 |= Fd; + break; + } + + m = (block[3]<<8)|block[2]; + if(m & 0x0001) + csr6 |= Ps; + if(m & 0x0010) + csr6 |= Ttm; + if(m & 0x0020) + csr6 |= Pcs; + if(m & 0x0040) + csr6 |= Scr; + + csr32w(ctlr, 12, block[1]); + microdelay(10); + csr32w(ctlr, 6, csr6); + ctlr->csr6 = csr6; + + if(!wait) + return 0; + + for(timeo = 0; timeo < 30; timeo++){ + if(type0link(ctlr, block)) + return 0; + delay(100); + } + + return -1; +} + +static int +media21041(Ether* ether, int wait) +{ + Ctlr* ctlr; + uchar *block; + int csr6, csr13, csr14, csr15, medium, timeo; + + ctlr = ether->ctlr; + block = ctlr->infoblock[ctlr->curk]; + debug("media21041: block[0] %2.2uX, medium %4.4uX sct %4.4uX\n", + block[0], ctlr->medium, ctlr->sct); + + medium = block[0] & 0x3F; + if(ctlr->medium >= 0 && medium != ctlr->medium) + return 0; + if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != medium) + return 0; + + csr6 = Sc|Mbo|Ca|TrMODE|Sb; + if(block[0] & 0x40){ + csr13 = (block[2]<<8)|block[1]; + csr14 = (block[4]<<8)|block[3]; + csr15 = (block[6]<<8)|block[5]; + } + else switch(medium){ + default: + return -1; + case 0x00: /* 10BASE-T */ + csr13 = 0xEF01; + csr14 = 0xFF3F; + csr15 = 0x0008; + break; + case 0x01: /* 10BASE-2 */ + csr13 = 0xEF09; + csr14 = 0xF73D; + csr15 = 0x0006; + break; + case 0x02: /* 10BASE-5 */ + csr13 = 0xEF09; + csr14 = 0xF73D; + csr15 = 0x000E; + break; + case 0x04: /* 10BASE-TFD */ + csr13 = 0xEF01; + csr14 = 0xFF3D; + csr15 = 0x0008; + break; + } + + csr32w(ctlr, 13, 0); + csr32w(ctlr, 14, csr14); + csr32w(ctlr, 15, csr15); + csr32w(ctlr, 13, csr13); + delay(10); + + if(medium == 0x04) + csr6 |= Fd; + ctlr->csr6 = csr6; + csr32w(ctlr, 6, ctlr->csr6); + + debug("media21041: csr6 %8.8uX csr13 %4.4uX csr14 %4.4uX csr15 %4.4uX\n", + csr6, csr13, csr14, csr15); + + if(!wait) + return 0; + + for(timeo = 0; timeo < 30; timeo++){ + if(!(csr32r(ctlr, 12) & 0x0002)){ + debug("media21041: ok: csr12 %4.4luX timeo %d\n", + csr32r(ctlr, 12), timeo); + return 10; + } + delay(100); + } + debug("media21041: !ok: csr12 %4.4luX\n", csr32r(ctlr, 12)); + + return -1; +} + +static int +mediaxx(Ether* ether, int wait) +{ + Ctlr* ctlr; + uchar *block; + + ctlr = ether->ctlr; + block = ctlr->infoblock[ctlr->curk]; + if(block[0] & 0x80){ + switch(block[1]){ + default: + return -1; + case 0: + if(ctlr->medium >= 0 && block[2] != ctlr->medium) + return 0; +/* need this test? */ if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[2]) + return 0; + if(type0mode(ctlr, block+2, wait)) + return 0; + break; + case 1: + if(typephymode(ctlr, block, wait)) + return 0; + break; + case 2: + debug("type2: medium %d block[2] %d\n", + ctlr->medium, block[2]); + if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium)) + return 0; + if(type2mode(ctlr, block, wait)) + return 0; + break; + case 3: + if(typephymode(ctlr, block, wait)) + return 0; + break; + case 4: + debug("type4: medium %d block[2] %d\n", + ctlr->medium, block[2]); + if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium)) + return 0; + if(typesymmode(ctlr, block, wait)) + return 0; + break; + } + } + else{ + if(ctlr->medium >= 0 && block[0] != ctlr->medium) + return 0; +/* need this test? */if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[0]) + return 0; + if(type0mode(ctlr, block, wait)) + return 0; + } + + if(ctlr->csr6){ + if(!(ctlr->csr6 & Ps) || (ctlr->csr6 & Ttm)) + return 10; + return 100; + } + + return 0; +} + +static int +media(Ether* ether, int wait) +{ + Ctlr* ctlr; + int k, mbps; + + ctlr = ether->ctlr; + for(k = 0; k < ctlr->k; k++){ + switch(ctlr->id){ + default: + mbps = mediaxx(ether, wait); + break; + case Tulip1: /* 21041 */ + mbps = media21041(ether, wait); + break; + } + if(mbps > 0) + return mbps; + if(ctlr->curk == 0) + ctlr->curk = ctlr->k-1; + else + ctlr->curk--; + } + + return 0; +} + +static char* mediatable[9] = { + "10BASE-T", /* TP */ + "10BASE-2", /* BNC */ + "10BASE-5", /* AUI */ + "100BASE-TX", + "10BASE-TFD", + "100BASE-TXFD", + "100BASE-T4", + "100BASE-FX", + "100BASE-FXFD", +}; + +static uchar en1207[] = { /* Accton EN1207-COMBO */ + 0x00, 0x00, 0xE8, /* [0] vendor ethernet code */ + 0x00, /* [3] spare */ + + 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */ + 0x1F, /* [6] general purpose control */ + 2, /* [7] block count */ + + 0x00, /* [8] media code (10BASE-TX) */ + 0x0B, /* [9] general purpose port data */ + 0x9E, 0x00, /* [10] command (LSB+MSB = 0x009E) */ + + 0x03, /* [8] media code (100BASE-TX) */ + 0x1B, /* [9] general purpose port data */ + 0x6D, 0x00, /* [10] command (LSB+MSB = 0x006D) */ + + /* There is 10BASE-2 as well, but... */ +}; + +static uchar ana6910fx[] = { /* Adaptec (Cogent) ANA-6910FX */ + 0x00, 0x00, 0x92, /* [0] vendor ethernet code */ + 0x00, /* [3] spare */ + + 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */ + 0x3F, /* [6] general purpose control */ + 1, /* [7] block count */ + + 0x07, /* [8] media code (100BASE-FX) */ + 0x03, /* [9] general purpose port data */ + 0x2D, 0x00 /* [10] command (LSB+MSB = 0x000D) */ +}; + +static uchar smc9332[] = { /* SMC 9332 */ + 0x00, 0x00, 0xC0, /* [0] vendor ethernet code */ + 0x00, /* [3] spare */ + + 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */ + 0x1F, /* [6] general purpose control */ + 2, /* [7] block count */ + + 0x00, /* [8] media code (10BASE-TX) */ + 0x00, /* [9] general purpose port data */ + 0x9E, 0x00, /* [10] command (LSB+MSB = 0x009E) */ + + 0x03, /* [8] media code (100BASE-TX) */ + 0x09, /* [9] general purpose port data */ + 0x6D, 0x00, /* [10] command (LSB+MSB = 0x006D) */ +}; + +static uchar* leaf21140[] = { + en1207, /* Accton EN1207-COMBO */ + ana6910fx, /* Adaptec (Cogent) ANA-6910FX */ + smc9332, /* SMC 9332 */ + nil, +}; + +/* + * Copied to ctlr->srom at offset 20. + */ +static uchar leafpnic[] = { + 0x00, 0x00, 0x00, 0x00, /* MAC address */ + 0x00, 0x00, + 0x00, /* controller 0 device number */ + 0x1E, 0x00, /* controller 0 info leaf offset */ + 0x00, /* reserved */ + 0x00, 0x08, /* selected connection type */ + 0x00, /* general purpose control */ + 0x01, /* block count */ + + 0x8C, /* format indicator and count */ + 0x01, /* block type */ + 0x00, /* PHY number */ + 0x00, /* GPR sequence length */ + 0x00, /* reset sequence length */ + 0x00, 0x78, /* media capabilities */ + 0xE0, 0x01, /* Nway advertisment */ + 0x00, 0x50, /* FDX bitmap */ + 0x00, 0x18, /* TTM bitmap */ +}; + +static int +srom(Ctlr* ctlr) +{ + int i, k, oui, phy, x; + uchar *p; + + /* + * This is a partial decoding of the SROM format described in + * 'Digital Semiconductor 21X4 Serial ROM Format, Version 4.05, + * 2-Mar-98'. Only the 2114[03] are handled, support for other + * controllers can be added as needed. + * Do a dummy read first to get the size and allocate ctlr->srom. + */ + sromr(ctlr, 0); + if(ctlr->srom == nil) + ctlr->srom = malloc((1<sromsz)*sizeof(ushort)); + for(i = 0; i < (1<sromsz); i++){ + x = sromr(ctlr, i); + ctlr->srom[2*i] = x; + ctlr->srom[2*i+1] = x>>8; + } + + if(DEBUG){ + print("srom:"); + for(i = 0; i < ((1<sromsz)*sizeof(ushort)); i++){ + if(i && ((i & 0x0F) == 0)) + print("\n "); + print(" %2.2uX", ctlr->srom[i]); + } + print("\n"); + } + + /* + * There are at least 2 SROM layouts: + * e.g. Digital EtherWORKS station address at offset 20; + * this complies with the 21140A SROM + * application note from Digital; + * e.g. SMC9332 station address at offset 0 followed by + * 2 additional bytes, repeated at offset + * 6; the 8 bytes are also repeated in + * reverse order at offset 8. + * To check which it is, read the SROM and check for the repeating + * patterns of the non-compliant cards; if that fails use the one at + * offset 20. + */ + ctlr->sromea = ctlr->srom; + for(i = 0; i < 8; i++){ + x = ctlr->srom[i]; + if(x != ctlr->srom[15-i] || x != ctlr->srom[16+i]){ + ctlr->sromea = &ctlr->srom[20]; + break; + } + } + + /* + * Fake up the SROM for the PNIC and AMDtek. + * They look like a 21140 with a PHY. + * The MAC address is byte-swapped in the orginal + * PNIC SROM data. + */ + if(ctlr->id == Pnic){ + memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic)); + for(i = 0; i < Eaddrlen; i += 2){ + ctlr->srom[20+i] = ctlr->srom[i+1]; + ctlr->srom[20+i+1] = ctlr->srom[i]; + } + } + if(ctlr->id == CentaurP){ + memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic)); + for(i = 0; i < Eaddrlen; i += 2){ + ctlr->srom[20+i] = ctlr->srom[8+i]; + ctlr->srom[20+i+1] = ctlr->srom[8+i+1]; + } + } + + /* + * Next, try to find the info leaf in the SROM for media detection. + * If it's a non-conforming card try to match the vendor ethernet code + * and point p at a fake info leaf with compact 21140 entries. + */ + if(ctlr->sromea == ctlr->srom){ + p = nil; + for(i = 0; leaf21140[i] != nil; i++){ + if(memcmp(leaf21140[i], ctlr->sromea, 3) == 0){ + p = &leaf21140[i][4]; + break; + } + } + if(p == nil) + return -1; + } + else + p = &ctlr->srom[(ctlr->srom[28]<<8)|ctlr->srom[27]]; + + /* + * Set up the info needed for later media detection. + * For the 21140, set the general-purpose mask in CSR12. + * The info block entries are stored in order of increasing + * precedence, so detection will work backwards through the + * stored indexes into ctlr->srom. + * If an entry is found which matches the selected connection + * type, save the index. Otherwise, start at the last entry. + * If any MII entries are found (type 1 and 3 blocks), scan + * for PHYs. + */ + ctlr->leaf = p; + ctlr->sct = *p++; + ctlr->sct |= *p++<<8; + if(ctlr->id != Tulip3 && ctlr->id != Tulip1){ + csr32w(ctlr, 12, Gpc|*p++); + delay(200); + } + ctlr->k = *p++; + if(ctlr->k >= nelem(ctlr->infoblock)) + ctlr->k = nelem(ctlr->infoblock)-1; + ctlr->sctk = ctlr->k-1; + phy = 0; + for(k = 0; k < ctlr->k; k++){ + ctlr->infoblock[k] = p; + if(ctlr->id == Tulip1){ + debug("type21041: 0x%2.2uX\n", p[0]); + if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF)) + ctlr->sctk = k; + if(*p & 0x40) + p += 7; + else + p += 1; + } + /* + * The RAMIX PMC665 has a badly-coded SROM, + * hence the test for 21143 and type 3. + */ + else if((*p & 0x80) || (ctlr->id == Tulip3 && *(p+1) == 3)){ + *p |= 0x80; + if(*(p+1) == 1 || *(p+1) == 3) + phy = 1; + if(*(p+1) == 5) + ctlr->type5block = p; + p += (*p & ~0x80)+1; + } + else{ + debug("type0: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n", + p[0], p[1], p[2], p[3]); + if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF)) + ctlr->sctk = k; + p += 4; + } + } + ctlr->curk = ctlr->sctk; + debug("sct 0x%uX medium 0x%uX k %d curk %d phy %d\n", + ctlr->sct, ctlr->medium, ctlr->k, ctlr->curk, phy); + + if(phy){ + x = 0; + for(k = 0; k < nelem(ctlr->phy); k++){ + if(ctlr->id == CentaurP && k != 1) + continue; + if((oui = miir(ctlr, k, 2)) == -1 || oui == 0) + continue; + debug("phy reg 2 %4.4uX\n", oui); + if(DEBUG){ + oui = (oui & 0x3FF)<<6; + oui |= miir(ctlr, k, 3)>>10; + miir(ctlr, k, 1); + debug("phy%d: index %d oui %uX reg1 %uX\n", + x, k, oui, miir(ctlr, k, 1)); + USED(oui); + } + ctlr->phy[x] = k; + } + } + + ctlr->fd = 0; + ctlr->medium = -1; + + return 0; +} + +static void +dec2114xpci(void) +{ + Ctlr *ctlr; + Pcidev *p; + int x; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + switch((p->did<<16)|p->vid){ + default: + continue; + + case Tulip3: /* 21143 */ + /* + * Exit sleep mode. + */ + x = pcicfgr32(p, 0x40); + x &= ~0xC0000000; + pcicfgw32(p, 0x40, x); + /*FALLTHROUGH*/ + + case Tulip0: /* 21140 */ + case Tulip1: /* 21041 */ + case Pnic: /* PNIC */ + case Pnic2: /* PNIC-II */ + case CentaurP: /* ADMtek */ + break; + } + + /* + * bar[0] is the I/O port register address and + * bar[1] is the memory-mapped register address. + */ + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = p->mem[0].bar & ~0x01; + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + + if(ioalloc(ctlr->port, p->mem[0].size, 0, "dec2114x") < 0){ + print("dec2114x: port 0x%uX in use\n", ctlr->port); + free(ctlr); + continue; + } + + /* + * Some cards (e.g. ANA-6910FX) seem to need the Ps bit + * set or they don't always work right after a hardware + * reset. + */ + csr32w(ctlr, 6, Mbo|Ps); + softreset(ctlr); + + if(srom(ctlr)){ + iofree(ctlr->port); + free(ctlr); + continue; + } + + switch(ctlr->id){ + default: + break; + + case Pnic: /* PNIC */ + /* + * Turn off the jabber timer. + */ + csr32w(ctlr, 15, 0x00000001); + break; + case CentaurP: + /* + * Nice - the register offsets change from *8 to *4 + * for CSR16 and up... + * CSR25/26 give the MAC address read from the SROM. + * Don't really need to use this other than as a check, + * the SROM will be read in anyway so the value there + * can be used directly. + */ + debug("csr25 %8.8luX csr26 %8.8luX\n", + inl(ctlr->port+0xA4), inl(ctlr->port+0xA8)); + debug("phyidr1 %4.4luX phyidr2 %4.4luX\n", + inl(ctlr->port+0xBC), inl(ctlr->port+0xC0)); + break; + } + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + +static int +reset(Ether* ether) +{ + Ctlr *ctlr; + int i, x; + uchar ea[Eaddrlen]; + static int scandone; + + if(scandone == 0){ + dec2114xpci(); + scandone = 1; + } + + /* + * Any adapter matches if no ether->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(ether->port == 0 || ether->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + ether->ctlr = ctlr; + ether->port = ctlr->port; + ether->irq = ctlr->pcidev->intl; + ether->tbdf = ctlr->pcidev->tbdf; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to + * loading the station address in the hardware. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0) + memmove(ether->ea, ctlr->sromea, Eaddrlen); + + /* + * Look for a medium override in case there's no autonegotiation + * (no MII) or the autonegotiation fails. + */ + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "FD") == 0){ + ctlr->fd = 1; + continue; + } + for(x = 0; x < nelem(mediatable); x++){ + debug("compare <%s> <%s>\n", mediatable[x], + ether->opt[i]); + if(cistrcmp(mediatable[x], ether->opt[i])) + continue; + ctlr->medium = x; + + switch(ctlr->medium){ + default: + ctlr->fd = 0; + break; + + case 0x04: /* 10BASE-TFD */ + case 0x05: /* 100BASE-TXFD */ + case 0x08: /* 100BASE-FXFD */ + ctlr->fd = 1; + break; + } + break; + } + } + + ether->mbps = media(ether, 1); + + /* + * Initialise descriptor rings, ethernet address. + */ + ctlr->nrdr = Nrde; + ctlr->ntdr = Ntde; + pcisetbme(ctlr->pcidev); + ctlrinit(ether); + + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = ifstat; + + ether->arg = ether; + ether->shutdown = shutdown; + ether->multicast = multicast; + ether->promiscuous = promiscuous; + + return 0; +} + +void +ether2114xlink(void) +{ + addethercard("21140", reset); + addethercard("2114x", reset); +} diff --git a/os/pc/ether589.c b/os/pc/ether589.c new file mode 100644 index 00000000..ef19093f --- /dev/null +++ b/os/pc/ether589.c @@ -0,0 +1,214 @@ +/* + * 3C589 and 3C562. + * To do: + * check xcvr10Base2 still works (is GlobalReset necessary?). + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +enum { /* all windows */ + CommandR = 0x000E, + IntStatusR = 0x000E, +}; + +enum { /* Commands */ + GlobalReset = 0x0000, + SelectRegisterWindow = 0x0001, + RxReset = 0x0005, + TxReset = 0x000B, + AcknowledgeInterrupt = 0x000D, +}; + +enum { /* IntStatus bits */ + commandInProgress = 0x1000, +}; + +#define COMMAND(port, cmd, a) outs((port)+CommandR, ((cmd)<<11)|(a)) +#define STATUS(port) ins((port)+IntStatusR) + +enum { /* Window 0 - setup */ + Wsetup = 0x0000, + /* registers */ + ManufacturerID = 0x0000, /* 3C5[08]*, 3C59[27] */ + ProductID = 0x0002, /* 3C5[08]*, 3C59[27] */ + ConfigControl = 0x0004, /* 3C5[08]*, 3C59[27] */ + AddressConfig = 0x0006, /* 3C5[08]*, 3C59[27] */ + ResourceConfig = 0x0008, /* 3C5[08]*, 3C59[27] */ + EepromCommand = 0x000A, + EepromData = 0x000C, + /* AddressConfig Bits */ + autoSelect9 = 0x0080, + xcvrMask9 = 0xC000, + /* ConfigControl bits */ + Ena = 0x0001, + base10TAvailable9 = 0x0200, + coaxAvailable9 = 0x1000, + auiAvailable9 = 0x2000, + /* EepromCommand bits */ + EepromReadRegister = 0x0080, + EepromBusy = 0x8000, +}; + +enum { /* Window 1 - operating set */ + Wop = 0x0001, +}; + +enum { /* Window 3 - FIFO management */ + Wfifo = 0x0003, + /* registers */ + InternalConfig = 0x0000, /* 3C509B, 3C589, 3C59[0257] */ + /* InternalConfig bits */ + xcvr10BaseT = 0x00000000, + xcvr10Base2 = 0x00300000, +}; + +enum { /* Window 4 - diagnostic */ + Wdiagnostic = 0x0004, + /* registers */ + MediaStatus = 0x000A, + /* MediaStatus bits */ + linkBeatDetect = 0x0800, +}; + +extern int etherelnk3reset(Ether*); + +static char *tcmpcmcia[] = { + "3C589", /* 3COM 589[ABCD] */ + "3C562", /* 3COM 562 */ + "589E", /* 3COM Megahertz 589E */ + nil, +}; + +static int +configASIC(Ether* ether, int port, int xcvr) +{ + int x; + + /* set Window 0 configuration registers */ + COMMAND(port, SelectRegisterWindow, Wsetup); + outs(port+ConfigControl, Ena); + + /* IRQ must be 3 on 3C589/3C562 */ + outs(port + ResourceConfig, 0x3F00); + + x = ins(port+AddressConfig) & ~xcvrMask9; + x |= (xcvr>>20)<<14; + outs(port+AddressConfig, x); + + COMMAND(port, TxReset, 0); + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, RxReset, 0); + while(STATUS(port) & commandInProgress) + ; + + return etherelnk3reset(ether); +} + +static int +reset(Ether* ether) +{ + int i, t, slot; + char *type; + int port; + enum { WantAny, Want10BT, Want10B2 }; + int want; + uchar ea[6]; + char *p; + + if(ether->irq == 0) + ether->irq = 10; + if(ether->port == 0) + ether->port = 0x240; + port = ether->port; + + if(ioalloc(port, 0x10, 0, "3C589") < 0) + return -1; + + type = nil; + slot = -1; + for(i = 0; tcmpcmcia[i] != nil; i++){ + type = tcmpcmcia[i]; + if((slot = pcmspecial(type, ether)) >= 0) + break; + } + if(slot < 0){ + iofree(port); + return -1; + } + + /* + * Read Ethernet address from card memory + * on 3C562, but only if the user has not + * overridden it. + */ + memset(ea, 0, sizeof ea); + if(memcmp(ea, ether->ea, 6) == 0 && strcmp(type, "3C562") == 0) { + if(pcmcistuple(slot, 0x88, -1, ea, 6) == 6) { + for(i = 0; i < 6; i += 2){ + t = ea[i]; + ea[i] = ea[i+1]; + ea[i+1] = t; + } + memmove(ether->ea, ea, 6); + } + } + /* + * Allow user to specify desired media in plan9.ini + */ + want = WantAny; + for(i = 0; i < ether->nopt; i++){ + if(cistrncmp(ether->opt[i], "media=", 6) != 0) + continue; + p = ether->opt[i]+6; + if(cistrcmp(p, "10base2") == 0) + want = Want10B2; + else if(cistrcmp(p, "10baseT") == 0) + want = Want10BT; + } + + /* try configuring as a 10BaseT */ + if(want==WantAny || want==Want10BT){ + if(configASIC(ether, port, xcvr10BaseT) < 0){ + pcmspecialclose(slot); + iofree(port); + return -1; + } + delay(100); + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + if((ins(port+MediaStatus)&linkBeatDetect) || want==Want10BT){ + COMMAND(port, SelectRegisterWindow, Wop); + print("#l%d: xcvr10BaseT %s\n", ether->ctlrno, type); + return 0; + } + } + + /* try configuring as a 10base2 */ + if(want==WantAny || want==Want10B2){ + COMMAND(port, GlobalReset, 0); + if(configASIC(ether, port, xcvr10Base2) < 0){ + pcmspecialclose(slot); + iofree(port); + return -1; + } + print("#l%d: xcvr10Base2 %s\n", ether->ctlrno, type); + return 0; + } + return -1; /* not reached */ +} + +void +ether589link(void) +{ + addethercard("3C589", reset); + addethercard("3C562", reset); + addethercard("589E", reset); +} diff --git a/os/pc/ether79c960.c b/os/pc/ether79c960.c new file mode 100644 index 00000000..f74574cd --- /dev/null +++ b/os/pc/ether79c960.c @@ -0,0 +1,523 @@ +/* + * AM79C960 + * PCnet Single-Chip Ethernet Controller for ISA Bus + * To do: + * only issue transmit interrupt if necessary? + * dynamically increase rings as necessary? + * use Blocks as receive buffers? + * currently hardwires 10Base-T + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +#define chatty 1 +#define DPRINT if(chatty)print + +enum { + Lognrdre = 6, + Nrdre = (1<ctlr; + ilock(ctlr); + if(ctlr->init){ + iunlock(ctlr); + return; + } + port = ether->port; + outs(port+Rdp, Iena|Strt); + iunlock(ctlr); +} + +static void +ringinit(Ctlr* ctlr) +{ + int i, x; + + /* + * Initialise the receive and transmit buffer rings. The ring + * entries must be aligned on 16-byte boundaries. + * + * This routine is protected by ctlr->init. + */ + if(ctlr->rdr == 0) + ctlr->rdr = xspanalloc(Nrdre*sizeof(Rdre), 0x10, 0); + if(ctlr->rrb == 0) + ctlr->rrb = xalloc(Nrdre*Rbsize); + + x = PADDR(ctlr->rrb); + if ((x >> 24)&0xFF) + panic("ether79c960: address>24bit"); + for(i = 0; i < Nrdre; i++){ + ctlr->rdr[i].rbadr = x&0xFFFF; + ctlr->rdr[i].rmd1 = Own|(x>>16)&0xFF; + x += Rbsize; + ctlr->rdr[i].rmd2 = 0xF000|-Rbsize&0x0FFF; + ctlr->rdr[i].rmd3 = 0; + } + ctlr->rdrx = 0; + + if(ctlr->tdr == 0) + ctlr->tdr = xspanalloc(Ntdre*sizeof(Tdre), 0x10, 0); + if(ctlr->trb == 0) + ctlr->trb = xalloc(Ntdre*Rbsize); + + x = PADDR(ctlr->trb); + if ((x >> 24)&0xFF) + panic("ether79c960: address>24bit"); + for(i = 0; i < Ntdre; i++){ + ctlr->tdr[i].tbadr = x&0xFFFF; + ctlr->tdr[i].tmd1 = (x>>16)&0xFF; + x += Rbsize; + ctlr->tdr[i].tmd2 = 0xF000|-Rbsize&0x0FFF; + } + ctlr->tdrx = 0; +} + +static void +promiscuous(void* arg, int on) +{ + Ether *ether; + int port, x; + Ctlr *ctlr; + + ether = arg; + port = ether->port; + ctlr = ether->ctlr; + + /* + * Put the chip into promiscuous mode. First we must wait until + * anyone transmitting is done, then we can stop the chip and put + * it in promiscuous mode. Restarting is made harder by the chip + * reloading the transmit and receive descriptor pointers with their + * base addresses when Strt is set (unlike the older Lance chip), + * so the rings must be re-initialised. + */ + ilock(ctlr); + if(ctlr->init){ + iunlock(ctlr); + return; + } + ctlr->init = 1; + iunlock(ctlr); + + outs(port+Rdp, Stop); + + outs(port+Rap, 15); + x = ins(port+Rdp) & ~Prom; + if(on) + x |= Prom; /* BUG: multicast ... */ + outs(port+Rdp, x); + outs(port+Rap, 0); + + ringinit(ctlr); + + ilock(ctlr); + ctlr->init = 0; + outs(port+Rdp, Iena|Strt); + iunlock(ctlr); +} + +static int +owntdre(void* arg) +{ + return (((Tdre*)arg)->tmd1 & Own) == 0; +} + +static void +txstart(Ether *ether) +{ + int port; + Ctlr *ctlr; + Tdre *tdre; + Etherpkt *pkt; + Block *bp; + int n; + + port = ether->port; + ctlr = ether->ctlr; + + if(ctlr->init) + return; + + /* + * Take the next transmit buffer, if it is free. + */ + tdre = &ctlr->tdr[ctlr->tdrx]; + if(owntdre(tdre) == 0) + return; + bp = qget(ether->oq); + if(bp == nil) + return; + + /* + * Copy the packet to the transmit buffer and fill in our + * source ethernet address. There's no need to pad to ETHERMINTU + * here as we set ApadXmit in CSR4. + */ + n = BLEN(bp); + pkt = KADDR(tdre->tbadr|(tdre->tmd1&0xFF)<<16); + memmove(pkt->d, bp->rp, n); + memmove(pkt->s, ether->ea, sizeof(pkt->s)); + freeb(bp); + + /* + * Give ownership of the descriptor to the chip, increment the + * software ring descriptor pointer and tell the chip to poll. + */ + tdre->tmd3 = 0; + tdre->tmd2 = 0xF000|(-n)&0x0FFF; + tdre->tmd1 |= Own|Stp|Enp; + ctlr->tdrx = NEXT(ctlr->tdrx, Ntdre); + outs(port+Rdp, Iena|Tdmd); + + ether->outpackets++; +} + +static void +transmit(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + + ilock(ctlr); + txstart(ether); + iunlock(ctlr); +} + +static void +interrupt(Ureg*, void* arg) +{ + Ether *ether; + int port, csr0, status; + Ctlr *ctlr; + Rdre *rdre; + Etherpkt *pkt; + Block *bp; + int len; + + ether = arg; + port = ether->port; + ctlr = ether->ctlr; + + /* + * Acknowledge all interrupts and whine about those that shouldn't + * happen. + */ + csr0 = ins(port+Rdp); + outs(port+Rdp, Babl|Cerr|Miss|Merr|Rint|Tint|Iena); + if(csr0 & (Babl|Miss|Merr)) + print("AMD70C960#%d: csr0 = 0x%uX\n", ether->ctlrno, csr0); + + /* + * Receiver interrupt: run round the descriptor ring logging + * errors and passing valid receive data up to the higher levels + * until we encounter a descriptor still owned by the chip. + */ + if(csr0 & Rint){ + rdre = &ctlr->rdr[ctlr->rdrx]; + while(((status = rdre->rmd1) & Own) == 0){ + if(status & RxErr){ + if(status & RxBuff) + ether->buffs++; + if(status & RxCrc) + ether->crcs++; + if(status & RxOflo) + ether->overflows++; + } + else { + len = (rdre->rmd3 & 0x0FFF)-4; + if((bp = iallocb(len)) != nil){ + ether->inpackets++; + pkt = KADDR(rdre->rbadr|(rdre->rmd1&0xFF)<<16); + memmove(bp->wp, pkt, len); + bp->wp += len; + etheriq(ether, bp, 1); + } + } + + /* + * Finished with this descriptor, reinitialise it, + * give it back to the chip, then on to the next... + */ + rdre->rmd3 = 0; + rdre->rmd2 = 0xF000|-Rbsize&0x0FFF; + rdre->rmd1 |= Own; + + ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre); + rdre = &ctlr->rdr[ctlr->rdrx]; + } + } + + /* + * Transmitter interrupt: start next block if waiting for free descriptor. + */ + if(csr0 & Tint){ + lock(ctlr); + txstart(ether); + unlock(ctlr); + } +} + +static int +reset(Ether* ether) +{ + int port, x, i; + uchar ea[Eaddrlen]; + Ctlr *ctlr; + + if(ether->port == 0) + ether->port = 0x300; + if(ether->irq == 0) + ether->irq = 10; + if(ether->irq == 2) + ether->irq = 9; + if(ether->dma == 0) + ether->dma = 5; + port = ether->port; + + if(port == 0 || ether->dma == 0) + return -1; + + /* + * Allocate a controller structure and start to fill in the + * initialisation block (must be DWORD aligned). + */ + ether->ctlr = malloc(sizeof(Ctlr)); + ctlr = ether->ctlr; + + ilock(ctlr); + ctlr->init = 1; + + /* + * Set the auto pad transmit in CSR4. + */ + /*outs(port+Rdp, 0x00);/**/ + ins(port+Sreset); /**/ + delay(1); + outs(port+Rap, 0); + outs(port+Rdp, Stop); + + outs(port+Rap, 4); + x = ins(port+Rdp) & 0xFFFF; + outs(port+Rdp, ApadXmt|x); + + outs(port+Rap, 0); + + /* + * Check if we are going to override the adapter's station address. + * If not, read it from the I/O-space and set in ether->ea prior to loading the + * station address in the initialisation block. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + for(i=0; i<6; i++) + ether->ea[i] = inb(port + Aprom + i); + } + + ctlr->iblock.rlen = Lognrdre<<5; + ctlr->iblock.tlen = Logntdre<<5; + memmove(ctlr->iblock.padr, ether->ea, sizeof(ctlr->iblock.padr)); + + ringinit(ctlr); + + x = PADDR(ctlr->rdr); + ctlr->iblock.rdra0 = x&0xFFFF; + ctlr->iblock.rdra16 = (x >> 16)&0xFF; + x = PADDR(ctlr->tdr); + ctlr->iblock.tdra0 = x&0xFFFF; + ctlr->iblock.tdra16 = (x >> 16)&0xFF; + + /* + * set the DMA controller to cascade mode for bus master + */ + switch(ether->dma){ + case 5: + outb(0xd6, 0xc1); outb(0xd4, 1); break; + case 6: + outb(0xd6, 0xc2); outb(0xd4, 2); break; + case 7: + outb(0xd6, 0xc3); outb(0xd4, 3); break; + } + + /* + * Ensure 10Base-T (for now) + */ + ctlr->iblock.mode = TenBaseT; + outs(port+Rap, 2); + x = ins(port+Idp); + x &= ~Isamedia; + x |= Isa10; + x |= Isaawake; + outs(port+Idp, x); + + /* + * Point the chip at the initialisation block and tell it to go. + * Mask the Idon interrupt and poll for completion. Strt and interrupt + * enables will be set later when we're ready to attach to the network. + */ + x = PADDR(&ctlr->iblock); + if((x>>24)&0xFF) + panic("ether79c960: address>24bit"); + outs(port+Rap, 1); + outs(port+Rdp, x & 0xFFFF); + outs(port+Rap, 2); + outs(port+Rdp, (x>>16) & 0xFF); + outs(port+Rap, 3); + outs(port+Rdp, Idonm); + outs(port+Rap, 0); + outs(port+Rdp, Init); + + while((ins(port+Rdp) & Idon) == 0) + ; + outs(port+Rdp, Idon|Stop); + ctlr->init = 0; + iunlock(ctlr); + + ether->port = port; + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = 0; + + ether->promiscuous = promiscuous; + ether->arg = ether; + + return 0; +} + +void +ether79c960link(void) +{ + addethercard("AMD79C960", reset); +} diff --git a/os/pc/ether79c970.c b/os/pc/ether79c970.c new file mode 100644 index 00000000..25bf4185 --- /dev/null +++ b/os/pc/ether79c970.c @@ -0,0 +1,640 @@ +/* + * AMD79C970 + * PCnet-PCI Single-Chip Ethernet Controller for PCI Local Bus + * To do: + * finish this rewrite + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +enum { + Lognrdre = 6, + Nrdre = (1<= Rdp) + r = (r-Rdp)/2+Rdp; + return ins(c->port+r); +} + +static void +io16w(Ctlr *c, int r, int v) +{ + if(r >= Rdp) + r = (r-Rdp)/2+Rdp; + outs(c->port+r, v); +} + +static int +io32r(Ctlr *c, int r) +{ + return inl(c->port+r); +} + +static void +io32w(Ctlr *c, int r, int v) +{ + outl(c->port+r, v); +} + +static void +attach(Ether*) +{ +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + char *p; + int len; + Ctlr *ctlr; + + ctlr = ether->ctlr; + + ether->crcs = ctlr->crc; + ether->frames = ctlr->fram; + ether->buffs = ctlr->rxbuff+ctlr->txbuff; + ether->overflows = ctlr->oflo; + + if(n == 0) + return 0; + + p = malloc(READSTR); + len = snprint(p, READSTR, "Rxbuff: %ld\n", ctlr->rxbuff); + len += snprint(p+len, READSTR-len, "Crc: %ld\n", ctlr->crc); + len += snprint(p+len, READSTR-len, "Oflo: %ld\n", ctlr->oflo); + len += snprint(p+len, READSTR-len, "Fram: %ld\n", ctlr->fram); + len += snprint(p+len, READSTR-len, "Rtry: %ld\n", ctlr->rtry); + len += snprint(p+len, READSTR-len, "Lcar: %ld\n", ctlr->lcar); + len += snprint(p+len, READSTR-len, "Lcol: %ld\n", ctlr->lcol); + len += snprint(p+len, READSTR-len, "Uflo: %ld\n", ctlr->uflo); + len += snprint(p+len, READSTR-len, "Txbuff: %ld\n", ctlr->txbuff); + len += snprint(p+len, READSTR-len, "Merr: %ld\n", ctlr->merr); + len += snprint(p+len, READSTR-len, "Miss: %ld\n", ctlr->miss); + snprint(p+len, READSTR-len, "Babl: %ld\n", ctlr->babl); + + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static void +ringinit(Ctlr* ctlr) +{ + Dre *dre; + + /* + * Initialise the receive and transmit buffer rings. + * The ring entries must be aligned on 16-byte boundaries. + * + * This routine is protected by ctlr->init. + */ + if(ctlr->rdr == 0){ + ctlr->rdr = xspanalloc(Nrdre*sizeof(Dre), 0x10, 0); + for(dre = ctlr->rdr; dre < &ctlr->rdr[Nrdre]; dre++){ + dre->bp = iallocb(Rbsize); + if(dre->bp == nil) + panic("can't allocate ethernet receive ring\n"); + dre->addr = PADDR(dre->bp->rp); + dre->md2 = 0; + dre->md1 = Own|(-Rbsize & 0xFFFF); + } + } + ctlr->rdrx = 0; + + if(ctlr->tdr == 0) + ctlr->tdr = xspanalloc(Ntdre*sizeof(Dre), 0x10, 0); + memset(ctlr->tdr, 0, Ntdre*sizeof(Dre)); + ctlr->tdrh = ctlr->tdri = 0; +} + +static void +promiscuous(void* arg, int on) +{ + Ether *ether; + int x; + Ctlr *ctlr; + + ether = arg; + ctlr = ether->ctlr; + + /* + * Put the chip into promiscuous mode. First must wait until + * anyone transmitting is done, then stop the chip and put + * it in promiscuous mode. Restarting is made harder by the chip + * reloading the transmit and receive descriptor pointers with their + * base addresses when Strt is set (unlike the older Lance chip), + * so the rings must be re-initialised. + */ + ilock(ctlr); + if(ctlr->init){ + iunlock(ctlr); + return; + } + ctlr->init = 1; + iunlock(ctlr); + + while(ctlr->ntq) + ; + + ctlr->iow(ctlr, Rdp, Stop); + + ctlr->iow(ctlr, Rap, 15); + x = ctlr->ior(ctlr, Rdp) & ~Prom; + if(on) + x |= Prom; + ctlr->iow(ctlr, Rdp, x); + ctlr->iow(ctlr, Rap, 0); + + ringinit(ctlr); + + ilock(ctlr); + ctlr->init = 0; + ctlr->iow(ctlr, Rdp, Iena|Strt); + iunlock(ctlr); +} + +static void +txstart(Ether* ether) +{ + Ctlr *ctlr; + Block *bp; + Dre *dre; + + ctlr = ether->ctlr; + + if(ctlr->init) + return; + + while(ctlr->ntq < (Ntdre-1)){ + bp = qget(ether->oq); + if(bp == nil) + break; + + /* + * Give ownership of the descriptor to the chip, + * increment the software ring descriptor pointer + * and tell the chip to poll. + * There's no need to pad to ETHERMINTU + * here as ApadXmt is set in CSR4. + */ + dre = &ctlr->tdr[ctlr->tdrh]; + dre->bp = bp; + dre->addr = PADDR(bp->rp); + dre->md2 = 0; + dre->md1 = Own|Stp|Enp|(-BLEN(bp) & 0xFFFF); + ctlr->ntq++; + ctlr->iow(ctlr, Rdp, Iena|Tdmd); + ctlr->tdrh = NEXT(ctlr->tdrh, Ntdre); + } +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + txstart(ether); + iunlock(ctlr); +} + +static void +interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *ether; + int csr0, len; + Dre *dre; + Block *bp; + + ether = arg; + ctlr = ether->ctlr; + + /* + * Acknowledge all interrupts and whine about those that shouldn't + * happen. + */ +intrloop: + csr0 = ctlr->ior(ctlr, Rdp) & 0xFFFF; + ctlr->iow(ctlr, Rdp, Babl|Cerr|Miss|Merr|Rint|Tint|Iena); + if(csr0 & Merr) + ctlr->merr++; + if(csr0 & Miss) + ctlr->miss++; + if(csr0 & Babl) + ctlr->babl++; + //if(csr0 & (Babl|Miss|Merr)) + // print("#l%d: csr0 = 0x%uX\n", ether->ctlrno, csr0); + if(!(csr0 & (Rint|Tint))) + return; + + /* + * Receiver interrupt: run round the descriptor ring logging + * errors and passing valid receive data up to the higher levels + * until a descriptor is encountered still owned by the chip. + */ + if(csr0 & Rint){ + dre = &ctlr->rdr[ctlr->rdrx]; + while(!(dre->md1 & Own)){ + if(dre->md1 & RxErr){ + if(dre->md1 & RxBuff) + ctlr->rxbuff++; + if(dre->md1 & Crc) + ctlr->crc++; + if(dre->md1 & Oflo) + ctlr->oflo++; + if(dre->md1 & Fram) + ctlr->fram++; + } + else if(bp = iallocb(Rbsize)){ + len = (dre->md2 & 0x0FFF)-4; + dre->bp->wp = dre->bp->rp+len; + etheriq(ether, dre->bp, 1); + dre->bp = bp; + dre->addr = PADDR(bp->rp); + } + + /* + * Finished with this descriptor, reinitialise it, + * give it back to the chip, then on to the next... + */ + dre->md2 = 0; + dre->md1 = Own|(-Rbsize & 0xFFFF); + + ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre); + dre = &ctlr->rdr[ctlr->rdrx]; + } + } + + /* + * Transmitter interrupt: wakeup anyone waiting for a free descriptor. + */ + if(csr0 & Tint){ + lock(ctlr); + while(ctlr->ntq){ + dre = &ctlr->tdr[ctlr->tdri]; + if(dre->md1 & Own) + break; + + if(dre->md1 & TxErr){ + if(dre->md2 & Rtry) + ctlr->rtry++; + if(dre->md2 & Lcar) + ctlr->lcar++; + if(dre->md2 & Lcol) + ctlr->lcol++; + if(dre->md2 & Uflo) + ctlr->uflo++; + if(dre->md2 & TxBuff) + ctlr->txbuff++; + ether->oerrs++; + } + + freeb(dre->bp); + + ctlr->ntq--; + ctlr->tdri = NEXT(ctlr->tdri, Ntdre); + } + txstart(ether); + unlock(ctlr); + } + goto intrloop; +} + +static void +amd79c970pci(void) +{ + int port; + Ctlr *ctlr; + Pcidev *p; + + p = nil; + while(p = pcimatch(p, 0x1022, 0x2000)){ + port = p->mem[0].bar & ~0x01; + if(ioalloc(port, p->mem[0].size, 0, "amd79c970") < 0){ + print("amd79c970: port 0x%uX in use\n", port); + continue; + } + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = p->mem[0].bar & ~0x01; + ctlr->pcidev = p; + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + +static int +reset(Ether* ether) +{ + int x; + uchar ea[Eaddrlen]; + Ctlr *ctlr; + + if(ctlrhead == nil) + amd79c970pci(); + + /* + * Any adapter matches if no port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(ether->port == 0 || ether->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + /* + * Allocate a controller structure and start to initialise it. + */ + ether->ctlr = ctlr; + ether->port = ctlr->port; + ether->irq = ctlr->pcidev->intl; + ether->tbdf = ctlr->pcidev->tbdf; + pcisetbme(ctlr->pcidev); + ilock(ctlr); + ctlr->init = 1; + + io32r(ctlr, Sreset); + io16r(ctlr, Sreset); + + if(io16w(ctlr, Rap, 0), io16r(ctlr, Rdp) == 4){ + ctlr->ior = io16r; + ctlr->iow = io16w; + }else if(io32w(ctlr, Rap, 0), io32r(ctlr, Rdp) == 4){ + ctlr->ior = io32r; + ctlr->iow = io32w; + }else{ + print("#l%d: card doesn't talk right\n", ether->ctlrno); +iprint("#l%d: card doesn't talk right\n", ether->ctlrno); + iunlock(ctlr); + return -1; + } + + ctlr->iow(ctlr, Rap, 88); + x = ctlr->ior(ctlr, Rdp); + ctlr->iow(ctlr, Rap, 89); + x |= ctlr->ior(ctlr, Rdp)<<16; + + switch(x&0xFFFFFFF){ + case 0x2420003: /* PCnet/PCI 79C970 */ + case 0x2621003: /* PCnet/PCI II 79C970A */ + break; + default: + print("#l%d: unknown PCnet card version %.7ux\n", + ether->ctlrno, x&0xFFFFFFF); +iprint("#l%d: unknown PCnet card version %.7ux\n", + ether->ctlrno, x&0xFFFFFFF); + iunlock(ctlr); + return -1; + } + + /* + * Set the software style in BCR20 to be PCnet-PCI to ensure 32-bit access. + * Set the auto pad transmit in CSR4. + */ + ctlr->iow(ctlr, Rap, 20); + ctlr->iow(ctlr, Bdp, 0x0002); + + ctlr->iow(ctlr, Rap, 4); + x = ctlr->ior(ctlr, Rdp) & 0xFFFF; + ctlr->iow(ctlr, Rdp, ApadXmt|x); + + ctlr->iow(ctlr, Rap, 0); + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the I/O-space and set in ether->ea prior to + * loading the station address in the initialisation block. + */ + memset(ea, 0, Eaddrlen); + if(!memcmp(ea, ether->ea, Eaddrlen)){ + x = ctlr->ior(ctlr, Aprom); + ether->ea[0] = x; + ether->ea[1] = x>>8; + if(ctlr->ior == io16r) + x = ctlr->ior(ctlr, Aprom+2); + else + x >>= 16; + ether->ea[2] = x; + ether->ea[3] = x>>8; + x = ctlr->ior(ctlr, Aprom+4); + ether->ea[4] = x; + ether->ea[5] = x>>8; + } + + /* + * Start to fill in the initialisation block + * (must be DWORD aligned). + */ + ctlr->iblock.rlen = Lognrdre<<4; + ctlr->iblock.tlen = Logntdre<<4; + memmove(ctlr->iblock.padr, ether->ea, sizeof(ctlr->iblock.padr)); + + ringinit(ctlr); + ctlr->iblock.rdra = PADDR(ctlr->rdr); + ctlr->iblock.tdra = PADDR(ctlr->tdr); + + /* + * Point the chip at the initialisation block and tell it to go. + * Mask the Idon interrupt and poll for completion. Strt and interrupt + * enables will be set later when attaching to the network. + */ + x = PADDR(&ctlr->iblock); + ctlr->iow(ctlr, Rap, 1); + ctlr->iow(ctlr, Rdp, x & 0xFFFF); + ctlr->iow(ctlr, Rap, 2); + ctlr->iow(ctlr, Rdp, (x>>16) & 0xFFFF); + ctlr->iow(ctlr, Rap, 3); + ctlr->iow(ctlr, Rdp, Idon); + ctlr->iow(ctlr, Rap, 0); + ctlr->iow(ctlr, Rdp, Init); + + while(!(ctlr->ior(ctlr, Rdp) & Idon)) + ; + + /* + * We used to set CSR0 to Idon|Stop here, and then + * in attach change it to Iena|Strt. Apparently the simulated + * 79C970 in VMware never enables after a write of Idon|Stop, + * so we enable the device here now. + */ + ctlr->iow(ctlr, Rdp, Iena|Strt); + ctlr->init = 0; + iunlock(ctlr); + + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = ifstat; + + ether->arg = ether; + ether->promiscuous = promiscuous; + + return 0; +} + +void +ether79c970link(void) +{ + addethercard("AMD79C970", reset); +} diff --git a/os/pc/ether8003.c b/os/pc/ether8003.c new file mode 100644 index 00000000..41244c09 --- /dev/null +++ b/os/pc/ether8003.c @@ -0,0 +1,271 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" +#include "ether8390.h" + +/* + * Western Digital/Standard Microsystems Corporation cards (WD80[01]3). + * Also handles 8216 cards (Elite Ultra). + * Configuration code based on that provided by SMC a long time ago. + */ +enum { /* 83C584 Bus Interface Controller */ + Msr = 0x00, /* Memory Select Register */ + Icr = 0x01, /* Interface Configuration Register */ + Iar = 0x02, /* I/O Address Register */ + Bio = 0x03, /* BIOS ROM Address Register */ + Ear = 0x03, /* EEROM Address Register (shared with Bio) */ + Irr = 0x04, /* Interrupt Request Register */ + Hcr = 0x04, /* 8216 hardware control */ + Laar = 0x05, /* LA Address Register */ + Ijr = 0x06, /* Initialisation Jumpers */ + Gp2 = 0x07, /* General Purpose Data Register */ + Lar = 0x08, /* LAN Address Registers */ + Id = 0x0E, /* Card ID byte */ + Cksum = 0x0F, /* Checksum */ +}; + +enum { /* Msr */ + Rst = 0x80, /* software reset */ + Menb = 0x40, /* memory enable */ +}; + +enum { /* Icr */ + Bit16 = 0x01, /* 16-bit bus */ + Other = 0x02, /* other register access */ + Ir2 = 0x04, /* IR2 */ + Msz = 0x08, /* SRAM size */ + Rla = 0x10, /* recall LAN address */ + Rx7 = 0x20, /* recall all but I/O and LAN address */ + Rio = 0x40, /* recall I/O address from EEROM */ + Sto = 0x80, /* non-volatile EEROM store */ +}; + +enum { /* Laar */ + ZeroWS16 = 0x20, /* zero wait states for 16-bit ops */ + L16en = 0x40, /* enable 16-bit LAN operation */ + M16en = 0x80, /* enable 16-bit memory access */ +}; + +enum { /* Ijr */ + Ienable = 0x01, /* 8216 interrupt enable */ +}; + +/* + * Mapping from configuration bits to interrupt level. + */ +static int irq8003[8] = { + 9, 3, 5, 7, 10, 11, 15, 4, +}; + +static int irq8216[8] = { + 0, 9, 3, 5, 7, 10, 11, 15, +}; + +static void +reset8003(Ether* ether, uchar ea[Eaddrlen], uchar ic[8]) +{ + Dp8390 *ctlr; + ulong port; + + ctlr = ether->ctlr; + port = ether->port; + + /* + * Check for old, dumb 8003E, which doesn't have an interface + * chip. Only Msr exists out of the 1st eight registers, reads + * of the others just alias the 2nd eight registers, the LAN + * address ROM. Can check Icr, Irr and Laar against the ethernet + * address read above and if they match it's an 8003E (or an + * 8003EBT, 8003S, 8003SH or 8003WT, doesn't matter), in which + * case the default irq gets used. + */ + if(memcmp(&ea[1], &ic[1], 5) == 0){ + memset(ic, 0, sizeof(ic)); + ic[Msr] = (((ulong)ether->mem)>>13) & 0x3F; + } + else{ + /* + * As a final sanity check for the 8013EBT, which doesn't have + * the 83C584 interface chip, but has 2 real registers, write Gp2 + * and if it reads back the same, it's not an 8013EBT. + */ + outb(port+Gp2, 0xAA); + inb(port+Msr); /* wiggle bus */ + if(inb(port+Gp2) != 0xAA){ + memset(ic, 0, sizeof(ic)); + ic[Msr] = (((ulong)ether->mem)>>13) & 0x3F; + } + else + ether->irq = irq8003[((ic[Irr]>>5) & 0x3)|(ic[Icr] & 0x4)]; + + /* + * Check if 16-bit card. + * If Bit16 is read/write, then it's an 8-bit card. + * If Bit16 is set, it's in a 16-bit slot. + */ + outb(port+Icr, ic[Icr]^Bit16); + inb(port+Msr); /* wiggle bus */ + if((inb(port+Icr) & Bit16) == (ic[Icr] & Bit16)){ + ctlr->width = 2; + ic[Icr] &= ~Bit16; + } + outb(port+Icr, ic[Icr]); + + if(ctlr->width == 2 && (inb(port+Icr) & Bit16) == 0) + ctlr->width = 1; + } + + ether->mem = (ulong)KADDR((ic[Msr] & 0x3F)<<13); + if(ctlr->width == 2) + ether->mem |= (ic[Laar] & 0x1F)<<19; + else + ether->mem |= 0x80000; + + if(ic[Icr] & (1<<3)) + ether->size = 32*1024; + if(ctlr->width == 2) + ether->size <<= 1; + + /* + * Enable interface RAM, set interface width. + */ + outb(port+Msr, ic[Msr]|Menb); + if(ctlr->width == 2) + outb(port+Laar, ic[Laar]|L16en|M16en|ZeroWS16); +} + +static void +reset8216(Ether* ether, uchar[8]) +{ + uchar hcr, irq, x; + ulong addr, port; + Dp8390 *ctlr; + + ctlr = ether->ctlr; + port = ether->port; + + ctlr->width = 2; + + /* + * Switch to the alternate register set and retrieve the memory + * and irq information. + */ + hcr = inb(port+Hcr); + outb(port+Hcr, 0x80|hcr); + addr = inb(port+0x0B) & 0xFF; + irq = inb(port+0x0D); + outb(port+Hcr, hcr); + + ether->mem = (ulong)KADDR(0xC0000+((((addr>>2) & 0x30)|(addr & 0x0F))<<13)); + ether->size = 8192*(1<<((addr>>4) & 0x03)); + ether->irq = irq8216[((irq>>4) & 0x04)|((irq>>2) & 0x03)]; + + /* + * Enable interface RAM, set interface width, and enable interrupts. + */ + x = inb(port+Msr) & ~Rst; + outb(port+Msr, Menb|x); + x = inb(port+Laar); + outb(port+Laar, M16en|x); + outb(port+Ijr, Ienable); +} + +/* + * Get configuration parameters, enable memory. + * There are opportunities here for buckets of code, try to resist. + */ +static int +reset(Ether* ether) +{ + int i; + uchar ea[Eaddrlen], ic[8], id, nullea[Eaddrlen], sum; + ulong port; + Dp8390 *ctlr; + + /* + * Set up the software configuration. + * Use defaults for port, irq, mem and size if not specified. + * Defaults are set for the dumb 8003E which can't be + * autoconfigured. + */ + if(ether->port == 0) + ether->port = 0x280; + if(ether->irq == 0) + ether->irq = 3; + if(ether->mem == 0) + ether->mem = 0xD0000; + if(ether->size == 0) + ether->size = 8*1024; + if(ioalloc(ether->port, 0x20, 0, "wd8003") < 0) + return -1; + + /* + * Look for the interface. Read the LAN address ROM + * and validate the checksum - the sum of all 8 bytes + * should be 0xFF. + * At the same time, get the (possible) interface chip + * registers, they'll be used later to check for aliasing. + */ + port = ether->port; + sum = 0; + for(i = 0; i < sizeof(ea); i++){ + ea[i] = inb(port+Lar+i); + sum += ea[i]; + ic[i] = inb(port+i); + } + id = inb(port+Id); + sum += id; + sum += inb(port+Cksum); + if(sum != 0xFF){ + iofree(ether->port); + return -1; + } + + ether->ctlr = malloc(sizeof(Dp8390)); + ctlr = ether->ctlr; + ctlr->ram = 1; + + if((id & 0xFE) == 0x2A) + reset8216(ether, ic); + else + reset8003(ether, ea, ic); + + /* + * Set the DP8390 ring addresses. + */ + ctlr->port = port+0x10; + ctlr->tstart = 0; + ctlr->pstart = HOWMANY(sizeof(Etherpkt), Dp8390BufSz); + ctlr->pstop = HOWMANY(ether->size, Dp8390BufSz); + + /* + * Finally, init the 8390, set the ethernet address + * and claim the memory used. + */ + dp8390reset(ether); + memset(nullea, 0, Eaddrlen); + if(memcmp(nullea, ether->ea, Eaddrlen) == 0){ + for(i = 0; i < sizeof(ether->ea); i++) + ether->ea[i] = ea[i]; + } + dp8390setea(ether); + + if(umbrwmalloc(PADDR(ether->mem), ether->size, 0) == 0) + print("ether8003: warning - 0x%luX unavailable\n", + PADDR(ether->mem)); + + return 0; +} + +void +ether8003link(void) +{ + addethercard("WD8003", reset); +} diff --git a/os/pc/ether8139.c b/os/pc/ether8139.c new file mode 100644 index 00000000..12deba1b --- /dev/null +++ b/os/pc/ether8139.c @@ -0,0 +1,755 @@ +/* + * Realtek 8139 (but not the 8129). + * Error recovery for the various over/under -flow conditions + * may need work. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +enum { /* registers */ + Idr0 = 0x0000, /* MAC address */ + Mar0 = 0x0008, /* Multicast address */ + Tsd0 = 0x0010, /* Transmit Status Descriptor0 */ + Tsad0 = 0x0020, /* Transmit Start Address Descriptor0 */ + Rbstart = 0x0030, /* Receive Buffer Start Address */ + Erbcr = 0x0034, /* Early Receive Byte Count */ + Ersr = 0x0036, /* Early Receive Status */ + Cr = 0x0037, /* Command Register */ + Capr = 0x0038, /* Current Address of Packet Read */ + Cbr = 0x003A, /* Current Buffer Address */ + Imr = 0x003C, /* Interrupt Mask */ + Isr = 0x003E, /* Interrupt Status */ + Tcr = 0x0040, /* Transmit Configuration */ + Rcr = 0x0044, /* Receive Configuration */ + Tctr = 0x0048, /* Timer Count */ + Mpc = 0x004C, /* Missed Packet Counter */ + Cr9346 = 0x0050, /* 9346 Command Register */ + Config0 = 0x0051, /* Configuration Register 0 */ + Config1 = 0x0052, /* Configuration Register 1 */ + TimerInt = 0x0054, /* Timer Interrupt */ + Msr = 0x0058, /* Media Status */ + Config3 = 0x0059, /* Configuration Register 3 */ + Config4 = 0x005A, /* Configuration Register 4 */ + Mulint = 0x005C, /* Multiple Interrupt Select */ + RerID = 0x005E, /* PCI Revision ID */ + Tsad = 0x0060, /* Transmit Status of all Descriptors */ + + Bmcr = 0x0062, /* Basic Mode Control */ + Bmsr = 0x0064, /* Basic Mode Status */ + Anar = 0x0066, /* Auto-Negotiation Advertisment */ + Anlpar = 0x0068, /* Auto-Negotiation Link Partner */ + Aner = 0x006A, /* Auto-Negotiation Expansion */ + Dis = 0x006C, /* Disconnect Counter */ + Fcsc = 0x006E, /* False Carrier Sense Counter */ + Nwaytr = 0x0070, /* N-way Test */ + Rec = 0x0072, /* RX_ER Counter */ + Cscr = 0x0074, /* CS Configuration */ + Phy1parm = 0x0078, /* PHY Parameter 1 */ + Twparm = 0x007C, /* Twister Parameter */ + Phy2parm = 0x0080, /* PHY Parameter 2 */ +}; + +enum { /* Cr */ + Bufe = 0x01, /* Rx Buffer Empty */ + Te = 0x04, /* Transmitter Enable */ + Re = 0x08, /* Receiver Enable */ + Rst = 0x10, /* Software Reset */ +}; + +enum { /* Imr/Isr */ + Rok = 0x0001, /* Receive OK */ + Rer = 0x0002, /* Receive Error */ + Tok = 0x0004, /* Transmit OK */ + Ter = 0x0008, /* Transmit Error */ + Rxovw = 0x0010, /* Receive Buffer Overflow */ + PunLc = 0x0020, /* Packet Underrun or Link Change */ + Fovw = 0x0040, /* Receive FIFO Overflow */ + Clc = 0x2000, /* Cable Length Change */ + Timerbit = 0x4000, /* Timer */ + Serr = 0x8000, /* System Error */ +}; + +enum { /* Tcr */ + Clrabt = 0x00000001, /* Clear Abort */ + TxrrSHIFT = 4, /* Transmit Retry Count */ + TxrrMASK = 0x000000F0, + MtxdmaSHIFT = 8, /* Max. DMA Burst Size */ + MtxdmaMASK = 0x00000700, + Mtxdma2048 = 0x00000700, + Acrc = 0x00010000, /* Append CRC (not) */ + LbkSHIFT = 17, /* Loopback Test */ + LbkMASK = 0x00060000, + Rtl8139ArevG = 0x00800000, /* RTL8139A Rev. G ID */ + IfgSHIFT = 24, /* Interframe Gap */ + IfgMASK = 0x03000000, + HwveridSHIFT = 26, /* Hardware Version ID */ + HwveridMASK = 0x7C000000, +}; + +enum { /* Rcr */ + Aap = 0x00000001, /* Accept All Packets */ + Apm = 0x00000002, /* Accept Physical Match */ + Am = 0x00000004, /* Accept Multicast */ + Ab = 0x00000008, /* Accept Broadcast */ + Ar = 0x00000010, /* Accept Runt */ + Aer = 0x00000020, /* Accept Error */ + Sel9356 = 0x00000040, /* 9356 EEPROM used */ + Wrap = 0x00000080, /* Rx Buffer Wrap Control */ + MrxdmaSHIFT = 8, /* Max. DMA Burst Size */ + MrxdmaMASK = 0x00000700, + Mrxdmaunlimited = 0x00000700, + RblenSHIFT = 11, /* Receive Buffer Length */ + RblenMASK = 0x00001800, + Rblen8K = 0x00000000, /* 8KB+16 */ + Rblen16K = 0x00000800, /* 16KB+16 */ + Rblen32K = 0x00001000, /* 32KB+16 */ + Rblen64K = 0x00001800, /* 64KB+16 */ + RxfthSHIFT = 13, /* Receive Buffer Length */ + RxfthMASK = 0x0000E000, + Rxfth256 = 0x00008000, + Rxfthnone = 0x0000E000, + Rer8 = 0x00010000, /* Accept Error Packets > 8 bytes */ + MulERINT = 0x00020000, /* Multiple Early Interrupt Select */ + ErxthSHIFT = 24, /* Early Rx Threshold */ + ErxthMASK = 0x0F000000, + Erxthnone = 0x00000000, +}; + +enum { /* Received Packet Status */ + Rcok = 0x0001, /* Receive Completed OK */ + Fae = 0x0002, /* Frame Alignment Error */ + Crc = 0x0004, /* CRC Error */ + Long = 0x0008, /* Long Packet */ + Runt = 0x0010, /* Runt Packet Received */ + Ise = 0x0020, /* Invalid Symbol Error */ + Bar = 0x2000, /* Broadcast Address Received */ + Pam = 0x4000, /* Physical Address Matched */ + Mar = 0x8000, /* Multicast Address Received */ +}; + +enum { /* Media Status Register */ + Rxpf = 0x01, /* Pause Flag */ + Txpf = 0x02, /* Pause Flag */ + Linkb = 0x04, /* Inverse of Link Status */ + Speed10 = 0x08, /* 10Mbps */ + Auxstatus = 0x10, /* Aux. Power Present Status */ + Rxfce = 0x40, /* Receive Flow Control Enable */ + Txfce = 0x80, /* Transmit Flow Control Enable */ +}; + +typedef struct Td Td; +struct Td { /* Soft Transmit Descriptor */ + int tsd; + int tsad; + uchar* data; + Block* bp; +}; + +enum { /* Tsd0 */ + SizeSHIFT = 0, /* Descriptor Size */ + SizeMASK = 0x00001FFF, + Own = 0x00002000, + Tun = 0x00004000, /* Transmit FIFO Underrun */ + Tcok = 0x00008000, /* Transmit COmpleted OK */ + EtxthSHIFT = 16, /* Early Tx Threshold */ + EtxthMASK = 0x001F0000, + NccSHIFT = 24, /* Number of Collisions Count */ + NccMASK = 0x0F000000, + Cdh = 0x10000000, /* CD Heartbeat */ + Owc = 0x20000000, /* Out of Window Collision */ + Tabt = 0x40000000, /* Transmit Abort */ + Crs = 0x80000000, /* Carrier Sense Lost */ +}; + +enum { + Rblen = Rblen64K, /* Receive Buffer Length */ + Ntd = 4, /* Number of Transmit Descriptors */ + Tdbsz = ROUNDUP(sizeof(Etherpkt), 4), +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; + + QLock alock; /* attach */ + Lock ilock; /* init */ + void* alloc; /* base of per-Ctlr allocated data */ + + int rcr; /* receive configuration register */ + uchar* rbstart; /* receive buffer */ + int rblen; /* receive buffer length */ + int ierrs; /* receive errors */ + + Lock tlock; /* transmit */ + Td td[Ntd]; + int ntd; /* descriptors active */ + int tdh; /* host index into td */ + int tdi; /* interface index into td */ + int etxth; /* early transmit threshold */ + int taligned; /* packet required no alignment */ + int tunaligned; /* packet required alignment */ + + int dis; /* disconnect counter */ + int fcsc; /* false carrier sense counter */ + int rec; /* RX_ER counter */ +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +#define csr8r(c, r) (inb((c)->port+(r))) +#define csr16r(c, r) (ins((c)->port+(r))) +#define csr32r(c, r) (inl((c)->port+(r))) +#define csr8w(c, r, b) (outb((c)->port+(r), (int)(b))) +#define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w))) +#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l))) + +static void +rtl8139promiscuous(void* arg, int on) +{ + Ether *edev; + Ctlr * ctlr; + + edev = arg; + ctlr = edev->ctlr; + ilock(&ctlr->ilock); + + if(on) + ctlr->rcr |= Aap; + else + ctlr->rcr &= ~Aap; + csr32w(ctlr, Rcr, ctlr->rcr); + iunlock(&ctlr->ilock); +} + +static long +rtl8139ifstat(Ether* edev, void* a, long n, ulong offset) +{ + int l; + char *p; + Ctlr *ctlr; + + ctlr = edev->ctlr; + p = malloc(READSTR); + l = snprint(p, READSTR, "rcr %#8.8ux\n", ctlr->rcr); + l += snprint(p+l, READSTR-l, "ierrs %d\n", ctlr->ierrs); + l += snprint(p+l, READSTR-l, "etxth %d\n", ctlr->etxth); + l += snprint(p+l, READSTR-l, "taligned %d\n", ctlr->taligned); + l += snprint(p+l, READSTR-l, "tunaligned %d\n", ctlr->tunaligned); + ctlr->dis += csr16r(ctlr, Dis); + l += snprint(p+l, READSTR-l, "dis %d\n", ctlr->dis); + ctlr->fcsc += csr16r(ctlr, Fcsc); + l += snprint(p+l, READSTR-l, "fcscnt %d\n", ctlr->fcsc); + ctlr->rec += csr16r(ctlr, Rec); + l += snprint(p+l, READSTR-l, "rec %d\n", ctlr->rec); + + l += snprint(p+l, READSTR-l, "Tcr %#8.8lux\n", csr32r(ctlr, Tcr)); + l += snprint(p+l, READSTR-l, "Config0 %#2.2ux\n", csr8r(ctlr, Config0)); + l += snprint(p+l, READSTR-l, "Config1 %#2.2ux\n", csr8r(ctlr, Config1)); + l += snprint(p+l, READSTR-l, "Msr %#2.2ux\n", csr8r(ctlr, Msr)); + l += snprint(p+l, READSTR-l, "Config3 %#2.2ux\n", csr8r(ctlr, Config3)); + l += snprint(p+l, READSTR-l, "Config4 %#2.2ux\n", csr8r(ctlr, Config4)); + + l += snprint(p+l, READSTR-l, "Bmcr %#4.4ux\n", csr16r(ctlr, Bmcr)); + l += snprint(p+l, READSTR-l, "Bmsr %#4.4ux\n", csr16r(ctlr, Bmsr)); + l += snprint(p+l, READSTR-l, "Anar %#4.4ux\n", csr16r(ctlr, Anar)); + l += snprint(p+l, READSTR-l, "Anlpar %#4.4ux\n", csr16r(ctlr, Anlpar)); + l += snprint(p+l, READSTR-l, "Aner %#4.4ux\n", csr16r(ctlr, Aner)); + l += snprint(p+l, READSTR-l, "Nwaytr %#4.4ux\n", csr16r(ctlr, Nwaytr)); + snprint(p+l, READSTR-l, "Cscr %#4.4ux\n", csr16r(ctlr, Cscr)); + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static int +rtl8139reset(Ctlr* ctlr) +{ + int timeo; + + /* + * Soft reset the controller. + */ + csr8w(ctlr, Cr, Rst); + for(timeo = 0; timeo < 1000; timeo++){ + if(!(csr8r(ctlr, Cr) & Rst)) + return 0; + delay(1); + } + + return -1; +} + +static void +rtl8139halt(Ctlr* ctlr) +{ + int i; + + csr8w(ctlr, Cr, 0); + csr16w(ctlr, Imr, 0); + csr16w(ctlr, Isr, ~0); + + for(i = 0; i < Ntd; i++){ + if(ctlr->td[i].bp == nil) + continue; + freeb(ctlr->td[i].bp); + ctlr->td[i].bp = nil; + } +} + +static void +rtl8139init(Ether* edev) +{ + int i; + ulong r; + Ctlr *ctlr; + uchar *alloc; + + ctlr = edev->ctlr; + ilock(&ctlr->ilock); + + rtl8139halt(ctlr); + + /* + * MAC Address. + */ + r = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0]; + csr32w(ctlr, Idr0, r); + r = (edev->ea[5]<<8)|edev->ea[4]; + csr32w(ctlr, Idr0+4, r); + + /* + * Receiver + */ + alloc = (uchar*)ROUNDUP((ulong)ctlr->alloc, 32); + ctlr->rbstart = alloc; + alloc += ctlr->rblen+16; + memset(ctlr->rbstart, 0, ctlr->rblen+16); + csr32w(ctlr, Rbstart, PCIWADDR(ctlr->rbstart)); + ctlr->rcr = Rxfth256|Rblen|Mrxdmaunlimited|Ab|Apm; + + /* + * Transmitter. + */ + for(i = 0; i < Ntd; i++){ + ctlr->td[i].tsd = Tsd0+i*4; + ctlr->td[i].tsad = Tsad0+i*4; + ctlr->td[i].data = alloc; + alloc += Tdbsz; + ctlr->td[i].bp = nil; + } + ctlr->ntd = ctlr->tdh = ctlr->tdi = 0; + ctlr->etxth = 128/32; + + /* + * Interrupts. + */ + csr32w(ctlr, TimerInt, 0); + csr16w(ctlr, Imr, Serr|Timerbit|Fovw|PunLc|Rxovw|Ter|Tok|Rer|Rok); + csr32w(ctlr, Mpc, 0); + + /* + * Enable receiver/transmitter. + * Need to enable before writing the Rcr or it won't take. + */ + csr8w(ctlr, Cr, Te|Re); + csr32w(ctlr, Tcr, Mtxdma2048); + csr32w(ctlr, Rcr, ctlr->rcr); + + iunlock(&ctlr->ilock); +} + +static void +rtl8139attach(Ether* edev) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + qlock(&ctlr->alock); + if(ctlr->alloc == nil){ + ctlr->rblen = 1<<((Rblen>>RblenSHIFT)+13); + ctlr->alloc = mallocz(ctlr->rblen+16 + Ntd*Tdbsz + 32, 0); + rtl8139init(edev); + } + qunlock(&ctlr->alock); +} + +static void +rtl8139txstart(Ether* edev) +{ + Td *td; + int size; + Block *bp; + Ctlr *ctlr; + + ctlr = edev->ctlr; + while(ctlr->ntd < Ntd){ + bp = qget(edev->oq); + if(bp == nil) + break; + size = BLEN(bp); + + td = &ctlr->td[ctlr->tdh]; + if(((int)bp->rp) & 0x03){ + memmove(td->data, bp->rp, size); + freeb(bp); + csr32w(ctlr, td->tsad, PCIWADDR(td->data)); + ctlr->tunaligned++; + } + else{ + td->bp = bp; + csr32w(ctlr, td->tsad, PCIWADDR(bp->rp)); + ctlr->taligned++; + } + csr32w(ctlr, td->tsd, (ctlr->etxth<ntd++; + ctlr->tdh = NEXT(ctlr->tdh, Ntd); + } +} + +static void +rtl8139transmit(Ether* edev) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + ilock(&ctlr->tlock); + rtl8139txstart(edev); + iunlock(&ctlr->tlock); +} + +static void +rtl8139receive(Ether* edev) +{ + Block *bp; + Ctlr *ctlr; + ushort capr; + uchar cr, *p; + int l, length, status; + + ctlr = edev->ctlr; + + /* + * Capr is where the host is reading from, + * Cbr is where the NIC is currently writing. + */ + capr = (csr16r(ctlr, Capr)+16) % ctlr->rblen; + while(!(csr8r(ctlr, Cr) & Bufe)){ + p = ctlr->rbstart+capr; + + /* + * Apparently the packet length may be 0xFFF0 if + * the NIC is still copying the packet into memory. + */ + length = (*(p+3)<<8)|*(p+2); + if(length == 0xFFF0) + break; + status = (*(p+1)<<8)|*p; + + if(!(status & Rcok)){ + if(status & (Ise|Fae)) + edev->frames++; + if(status & Crc) + edev->crcs++; + if(status & (Runt|Long)) + edev->buffs++; + + /* + * Reset the receiver. + * Also may have to restore the multicast list + * here too if it ever gets used. + */ + cr = csr8r(ctlr, Cr); + csr8w(ctlr, Cr, cr & ~Re); + csr32w(ctlr, Rbstart, PCIWADDR(ctlr->rbstart)); + csr8w(ctlr, Cr, cr); + csr32w(ctlr, Rcr, ctlr->rcr); + + continue; + } + + /* + * Receive Completed OK. + * Very simplistic; there are ways this could be done + * without copying, but the juice probably isn't worth + * the squeeze. + * The packet length includes a 4 byte CRC on the end. + */ + capr = (capr+4) % ctlr->rblen; + p = ctlr->rbstart+capr; + capr = (capr+length) % ctlr->rblen; + + if((bp = iallocb(length)) != nil){ + if(p+length >= ctlr->rbstart+ctlr->rblen){ + l = ctlr->rbstart+ctlr->rblen - p; + memmove(bp->wp, p, l); + bp->wp += l; + length -= l; + p = ctlr->rbstart; + } + if(length > 0){ + memmove(bp->wp, p, length); + bp->wp += length; + } + bp->wp -= 4; + etheriq(edev, bp, 1); + } + + capr = ROUNDUP(capr, 4); + csr16w(ctlr, Capr, capr-16); + } +} + +static void +rtl8139interrupt(Ureg*, void* arg) +{ + Td *td; + Ctlr *ctlr; + Ether *edev; + int isr, msr, tsd; + + edev = arg; + ctlr = edev->ctlr; + + while((isr = csr16r(ctlr, Isr)) != 0){ + csr16w(ctlr, Isr, isr); + if(isr & (Fovw|PunLc|Rxovw|Rer|Rok)){ + rtl8139receive(edev); + if(!(isr & Rok)) + ctlr->ierrs++; + isr &= ~(Fovw|Rxovw|Rer|Rok); + } + + if(isr & (Ter|Tok)){ + ilock(&ctlr->tlock); + while(ctlr->ntd){ + td = &ctlr->td[ctlr->tdi]; + tsd = csr32r(ctlr, td->tsd); + if(!(tsd & (Tabt|Tun|Tcok))) + break; + + if(!(tsd & Tcok)){ + if(tsd & Tun){ + if(ctlr->etxth < ETHERMAXTU/32) + ctlr->etxth++; + } + edev->oerrs++; + } + + if(td->bp != nil){ + freeb(td->bp); + td->bp = nil; + } + + ctlr->ntd--; + ctlr->tdi = NEXT(ctlr->tdi, Ntd); + } + rtl8139txstart(edev); + iunlock(&ctlr->tlock); + isr &= ~(Ter|Tok); + } + + if(isr & PunLc){ + /* + * Maybe the link changed - do we care very much? + */ + msr = csr8r(ctlr, Msr); + if(!(msr & Linkb)){ + if(!(msr & Speed10) && edev->mbps != 100){ + edev->mbps = 100; + qsetlimit(edev->oq, 256*1024); + } + else if((msr & Speed10) && edev->mbps != 10){ + edev->mbps = 10; + qsetlimit(edev->oq, 65*1024); + } + } + isr &= ~(Clc|PunLc); + } + + /* + * Only Serr|Timerbit should be left by now. + * Should anything be done to tidy up? TimerInt isn't + * used so that can be cleared. A PCI bus error is indicated + * by Serr, that's pretty serious; is there anyhing to do + * other than try to reinitialise the chip? + */ + if((isr & (Serr|Timerbit)) != 0){ + iprint("rtl8139interrupt: imr %#4.4ux isr %#4.4ux\n", + csr16r(ctlr, Imr), isr); + if(isr & Timerbit) + csr32w(ctlr, TimerInt, 0); + if(isr & Serr) + rtl8139init(edev); + } + } +} + +static Ctlr* +rtl8139match(Ether* edev, int id) +{ + Pcidev *p; + Ctlr *ctlr; + int i, port; + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + p = ctlr->pcidev; + if(((p->did<<16)|p->vid) != id) + continue; + port = p->mem[0].bar & ~0x01; + if(edev->port != 0 && edev->port != port) + continue; + + if(ioalloc(port, p->mem[0].size, 0, "rtl8139") < 0){ + print("rtl8139: port %#ux in use\n", port); + continue; + } + + if(pcigetpms(p) > 0){ + pcisetpms(p, 0); + + for(i = 0; i < 6; i++) + pcicfgw32(p, PciBAR0+i*4, p->mem[i].bar); + pcicfgw8(p, PciINTL, p->intl); + pcicfgw8(p, PciLTR, p->ltr); + pcicfgw8(p, PciCLS, p->cls); + pcicfgw16(p, PciPCR, p->pcr); + } + + ctlr->port = port; + if(rtl8139reset(ctlr)) + continue; + pcisetbme(p); + + ctlr->active = 1; + return ctlr; + } + return nil; +} + +static struct { + char* name; + int id; +} rtl8139pci[] = { + { "rtl8139", (0x8139<<16)|0x10EC, }, /* generic */ + { "smc1211", (0x1211<<16)|0x1113, }, /* SMC EZ-Card */ + { "dfe-538tx", (0x1300<<16)|0x1186, }, /* D-Link DFE-538TX */ + { "dfe-560txd", (0x1340<<16)|0x1186, }, /* D-Link DFE-560TXD */ + { nil }, +}; + +static int +rtl8139pnp(Ether* edev) +{ + int i, id; + Pcidev *p; + Ctlr *ctlr; + uchar ea[Eaddrlen]; + + /* + * Make a list of all ethernet controllers + * if not already done. + */ + if(ctlrhead == nil){ + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + ctlr = malloc(sizeof(Ctlr)); + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } + } + + /* + * Is it an RTL8139 under a different name? + * Normally a search is made through all the found controllers + * for one which matches any of the known vid+did pairs. + * If a vid+did pair is specified a search is made for that + * specific controller only. + */ + id = 0; + for(i = 0; i < edev->nopt; i++){ + if(cistrncmp(edev->opt[i], "id=", 3) == 0) + id = strtol(&edev->opt[i][3], nil, 0); + } + + ctlr = nil; + if(id != 0) + ctlr = rtl8139match(edev, id); + else for(i = 0; rtl8139pci[i].name; i++){ + if((ctlr = rtl8139match(edev, rtl8139pci[i].id)) != nil) + break; + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pcidev->intl; + edev->tbdf = ctlr->pcidev->tbdf; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the device and set in edev->ea. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, edev->ea, Eaddrlen) == 0){ + i = csr32r(ctlr, Idr0); + edev->ea[0] = i; + edev->ea[1] = i>>8; + edev->ea[2] = i>>16; + edev->ea[3] = i>>24; + i = csr32r(ctlr, Idr0+4); + edev->ea[4] = i; + edev->ea[5] = i>>8; + } + + edev->attach = rtl8139attach; + edev->transmit = rtl8139transmit; + edev->interrupt = rtl8139interrupt; + edev->ifstat = rtl8139ifstat; + + edev->arg = edev; + edev->promiscuous = rtl8139promiscuous; + + /* + * This should be much more dynamic but will do for now. + */ + if((csr8r(ctlr, Msr) & (Speed10|Linkb)) == 0) + edev->mbps = 100; + + return 0; +} + +void +ether8139link(void) +{ + addethercard("rtl8139", rtl8139pnp); +} diff --git a/os/pc/ether82543gc.c b/os/pc/ether82543gc.c new file mode 100644 index 00000000..6277b10b --- /dev/null +++ b/os/pc/ether82543gc.c @@ -0,0 +1,1367 @@ +/* + * Intel RS-82543GC Gigabit Ethernet Controller + * as found on the Intel PRO/1000[FT] Server Adapter. + * The older non-[FT] cards use the 82542 (LSI L2A1157) chip; no attempt + * is made to handle the older chip although it should be possible. + * The datasheet is not very clear about running on a big-endian system + * and this driver assumes little-endian throughout. + * To do: + * GMII/MII + * receive tuning + * transmit tuning + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +enum { + Ctrl = 0x00000000, /* Device Control */ + Status = 0x00000008, /* Device Status */ + Eecd = 0x00000010, /* EEPROM/Flash Control/Data */ + Ctrlext = 0x00000018, /* Extended Device Control */ + Mdic = 0x00000020, /* MDI Control */ + Fcal = 0x00000028, /* Flow Control Address Low */ + Fcah = 0x0000002C, /* Flow Control Address High */ + Fct = 0x00000030, /* Flow Control Type */ + Icr = 0x000000C0, /* Interrupt Cause Read */ + Ics = 0x000000C8, /* Interrupt Cause Set */ + Ims = 0x000000D0, /* Interrupt Mask Set/Read */ + Imc = 0x000000D8, /* Interrupt mask Clear */ + Rctl = 0x00000100, /* Receive Control */ + Fcttv = 0x00000170, /* Flow Control Transmit Timer Value */ + Txcw = 0x00000178, /* Transmit configuration word reg. */ + Rxcw = 0x00000180, /* Receive configuration word reg. */ + Tctl = 0x00000400, /* Transmit Control */ + Tipg = 0x00000410, /* Transmit IPG */ + Tbt = 0x00000448, /* Transmit Burst Timer */ + Ait = 0x00000458, /* Adaptive IFS Throttle */ + Fcrtl = 0x00002160, /* Flow Control RX Threshold Low */ + Fcrth = 0x00002168, /* Flow Control Rx Threshold High */ + Rdfh = 0x00002410, /* Receive data fifo head */ + Rdft = 0x00002418, /* Receive data fifo tail */ + Rdfhs = 0x00002420, /* Receive data fifo head saved */ + Rdfts = 0x00002428, /* Receive data fifo tail saved */ + Rdfpc = 0x00002430, /* Receive data fifo packet count */ + Rdbal = 0x00002800, /* Rdesc Base Address Low */ + Rdbah = 0x00002804, /* Rdesc Base Address High */ + Rdlen = 0x00002808, /* Receive Descriptor Length */ + Rdh = 0x00002810, /* Receive Descriptor Head */ + Rdt = 0x00002818, /* Receive Descriptor Tail */ + Rdtr = 0x00002820, /* Receive Descriptor Timer Ring */ + Rxdctl = 0x00002828, /* Receive Descriptor Control */ + Txdmac = 0x00003000, /* Transfer DMA Control */ + Ett = 0x00003008, /* Early Transmit Control */ + Tdfh = 0x00003410, /* Transmit data fifo head */ + Tdft = 0x00003418, /* Transmit data fifo tail */ + Tdfhs = 0x00003420, /* Transmit data Fifo Head saved */ + Tdfts = 0x00003428, /* Transmit data fifo tail saved */ + Tdfpc = 0x00003430, /* Trasnmit data Fifo packet count */ + Tdbal = 0x00003800, /* Tdesc Base Address Low */ + Tdbah = 0x00003804, /* Tdesc Base Address High */ + Tdlen = 0x00003808, /* Transmit Descriptor Length */ + Tdh = 0x00003810, /* Transmit Descriptor Head */ + Tdt = 0x00003818, /* Transmit Descriptor Tail */ + Tidv = 0x00003820, /* Transmit Interrupt Delay Value */ + Txdctl = 0x00003828, /* Transmit Descriptor Control */ + + Statistics = 0x00004000, /* Start of Statistics Area */ + Gorcl = 0x88/4, /* Good Octets Received Count */ + Gotcl = 0x90/4, /* Good Octets Transmitted Count */ + Torl = 0xC0/4, /* Total Octets Received */ + Totl = 0xC8/4, /* Total Octets Transmitted */ + Nstatistics = 64, + + Rxcsum = 0x00005000, /* Receive Checksum Control */ + Mta = 0x00005200, /* Multicast Table Array */ + Ral = 0x00005400, /* Receive Address Low */ + Rah = 0x00005404, /* Receive Address High */ +}; + +enum { /* Ctrl */ + Bem = 0x00000002, /* Big Endian Mode */ + Prior = 0x00000004, /* Priority on the PCI bus */ + Lrst = 0x00000008, /* Link Reset */ + Asde = 0x00000020, /* Auto-Speed Detection Enable */ + Slu = 0x00000040, /* Set Link Up */ + Ilos = 0x00000080, /* Invert Loss of Signal (LOS) */ + Frcspd = 0x00000800, /* Force Speed */ + Frcdplx = 0x00001000, /* Force Duplex */ + Swdpinslo = 0x003C0000, /* Software Defined Pins - lo nibble */ + Swdpin0 = 0x00040000, + Swdpin1 = 0x00080000, + Swdpin2 = 0x00100000, + Swdpin3 = 0x00200000, + Swdpiolo = 0x03C00000, /* Software Defined I/O Pins */ + Swdpio0 = 0x00400000, + Swdpio1 = 0x00800000, + Swdpio2 = 0x01000000, + Swdpio3 = 0x02000000, + Devrst = 0x04000000, /* Device Reset */ + Rfce = 0x08000000, /* Receive Flow Control Enable */ + Tfce = 0x10000000, /* Transmit Flow Control Enable */ + Vme = 0x40000000, /* VLAN Mode Enable */ +}; + +enum { /* Status */ + Lu = 0x00000002, /* Link Up */ + Tckok = 0x00000004, /* Transmit clock is running */ + Rbcok = 0x00000008, /* Receive clock is running */ + Txoff = 0x00000010, /* Transmission Paused */ + Tbimode = 0x00000020, /* TBI Mode Indication */ + SpeedMASK = 0x000000C0, + Speed10 = 0x00000000, /* 10Mb/s */ + Speed100 = 0x00000040, /* 100Mb/s */ + Speed1000 = 0x00000080, /* 1000Mb/s */ + Mtxckok = 0x00000400, /* MTX clock is running */ + Pci66 = 0x00000800, /* PCI Bus speed indication */ + Bus64 = 0x00001000, /* PCI Bus width indication */ +}; + +enum { /* Ctrl and Status */ + Fd = 0x00000001, /* Full-Duplex */ + AsdvMASK = 0x00000300, + Asdv10 = 0x00000000, /* 10Mb/s */ + Asdv100 = 0x00000100, /* 100Mb/s */ + Asdv1000 = 0x00000200, /* 1000Mb/s */ +}; + +enum { /* Eecd */ + Sk = 0x00000001, /* Clock input to the EEPROM */ + Cs = 0x00000002, /* Chip Select */ + Di = 0x00000004, /* Data Input to the EEPROM */ + Do = 0x00000008, /* Data Output from the EEPROM */ +}; + +enum { /* Ctrlext */ + Gpien = 0x0000000F, /* General Purpose Interrupt Enables */ + Swdpinshi = 0x000000F0, /* Software Defined Pins - hi nibble */ + Swdpiohi = 0x00000F00, /* Software Defined Pins - I or O */ + Asdchk = 0x00001000, /* ASD Check */ + Eerst = 0x00002000, /* EEPROM Reset */ + Ips = 0x00004000, /* Invert Power State */ + Spdbyps = 0x00008000, /* Speed Select Bypass */ +}; + +enum { /* EEPROM content offsets */ + Ea = 0x00, /* Ethernet Address */ + Cf = 0x03, /* Compatibility Field */ + Pba = 0x08, /* Printed Board Assembly number */ + Icw1 = 0x0A, /* Initialization Control Word 1 */ + Sid = 0x0B, /* Subsystem ID */ + Svid = 0x0C, /* Subsystem Vendor ID */ + Did = 0x0D, /* Device ID */ + Vid = 0x0E, /* Vendor ID */ + Icw2 = 0x0F, /* Initialization Control Word 2 */ +}; + +enum { /* Mdic */ + MDIdMASK = 0x0000FFFF, /* Data */ + MDIdSHIFT = 0, + MDIrMASK = 0x001F0000, /* PHY Register Address */ + MDIrSHIFT = 16, + MDIpMASK = 0x03E00000, /* PHY Address */ + MDIpSHIFT = 21, + MDIwop = 0x04000000, /* Write Operation */ + MDIrop = 0x08000000, /* Read Operation */ + MDIready = 0x10000000, /* End of Transaction */ + MDIie = 0x20000000, /* Interrupt Enable */ + MDIe = 0x40000000, /* Error */ +}; + +enum { /* Icr, Ics, Ims, Imc */ + Txdw = 0x00000001, /* Transmit Descriptor Written Back */ + Txqe = 0x00000002, /* Transmit Queue Empty */ + Lsc = 0x00000004, /* Link Status Change */ + Rxseq = 0x00000008, /* Receive Sequence Error */ + Rxdmt0 = 0x00000010, /* Rdesc Minimum Threshold Reached */ + Rxo = 0x00000040, /* Receiver Overrun */ + Rxt0 = 0x00000080, /* Receiver Timer Interrupt */ + Mdac = 0x00000200, /* MDIO Access Completed */ + Rxcfg = 0x00000400, /* Receiving /C/ ordered sets */ + Gpi0 = 0x00000800, /* General Purpose Interrupts */ + Gpi1 = 0x00001000, + Gpi2 = 0x00002000, + Gpi3 = 0x00004000, +}; + +enum { /* Txcw */ + Ane = 0x80000000, /* Autonegotiate enable */ + Np = 0x00008000, /* Next Page */ + As = 0x00000100, /* Asymmetric Flow control desired */ + Ps = 0x00000080, /* Pause supported */ + Hd = 0x00000040, /* Half duplex supported */ + TxcwFd = 0x00000020, /* Full Duplex supported */ +}; + +enum { /* Rxcw */ + Rxword = 0x0000FFFF, /* Data from auto-negotiation process */ + Rxnocarrier = 0x04000000, /* Carrier Sense indication */ + Rxinvalid = 0x08000000, /* Invalid Symbol during configuration */ + Rxchange = 0x10000000, /* Change to the Rxword indication */ + Rxconfig = 0x20000000, /* /C/ order set reception indication */ + Rxsync = 0x40000000, /* Lost bit synchronization indication */ + Anc = 0x80000000, /* Auto Negotiation Complete */ +}; + +enum { /* Rctl */ + Rrst = 0x00000001, /* Receiver Software Reset */ + Ren = 0x00000002, /* Receiver Enable */ + Sbp = 0x00000004, /* Store Bad Packets */ + Upe = 0x00000008, /* Unicast Promiscuous Enable */ + Mpe = 0x00000010, /* Multicast Promiscuous Enable */ + Lpe = 0x00000020, /* Long Packet Reception Enable */ + LbmMASK = 0x000000C0, /* Loopback Mode */ + LbmOFF = 0x00000000, /* No Loopback */ + LbmTBI = 0x00000040, /* TBI Loopback */ + LbmMII = 0x00000080, /* GMII/MII Loopback */ + LbmXCVR = 0x000000C0, /* Transceiver Loopback */ + RdtmsMASK = 0x00000300, /* Rdesc Minimum Threshold Size */ + RdtmsHALF = 0x00000000, /* Threshold is 1/2 Rdlen */ + RdtmsQUARTER = 0x00000100, /* Threshold is 1/4 Rdlen */ + RdtmsEIGHTH = 0x00000200, /* Threshold is 1/8 Rdlen */ + MoMASK = 0x00003000, /* Multicast Offset */ + Bam = 0x00008000, /* Broadcast Accept Mode */ + BsizeMASK = 0x00030000, /* Receive Buffer Size */ + Bsize2048 = 0x00000000, /* Bsex = 0 */ + Bsize1024 = 0x00010000, /* Bsex = 0 */ + Bsize512 = 0x00020000, /* Bsex = 0 */ + Bsize256 = 0x00030000, /* Bsex = 0 */ + Bsize16384 = 0x00010000, /* Bsex = 1 */ + Vfe = 0x00040000, /* VLAN Filter Enable */ + Cfien = 0x00080000, /* Canonical Form Indicator Enable */ + Cfi = 0x00100000, /* Canonical Form Indicator value */ + Dpf = 0x00400000, /* Discard Pause Frames */ + Pmcf = 0x00800000, /* Pass MAC Control Frames */ + Bsex = 0x02000000, /* Buffer Size Extension */ + Secrc = 0x04000000, /* Strip CRC from incoming packet */ +}; + +enum { /* Tctl */ + Trst = 0x00000001, /* Transmitter Software Reset */ + Ten = 0x00000002, /* Transmit Enable */ + Psp = 0x00000008, /* Pad Short Packets */ + CtMASK = 0x00000FF0, /* Collision Threshold */ + CtSHIFT = 4, + ColdMASK = 0x003FF000, /* Collision Distance */ + ColdSHIFT = 12, + Swxoff = 0x00400000, /* Sofware XOFF Transmission */ + Pbe = 0x00800000, /* Packet Burst Enable */ + Rtlc = 0x01000000, /* Re-transmit on Late Collision */ + Nrtu = 0x02000000, /* No Re-transmit on Underrrun */ +}; + +enum { /* [RT]xdctl */ + PthreshMASK = 0x0000003F, /* Prefetch Threshold */ + PthreshSHIFT = 0, + HthreshMASK = 0x00003F00, /* Host Threshold */ + HthreshSHIFT = 8, + WthreshMASK = 0x003F0000, /* Writeback Threshold */ + WthreshSHIFT = 16, + Gran = 0x00000000, /* Granularity */ + RxGran = 0x01000000, /* Granularity */ +}; + +enum { /* Rxcsum */ + PcssMASK = 0x000000FF, /* Packet Checksum Start */ + PcssSHIFT = 0, + Ipofl = 0x00000100, /* IP Checksum Off-load Enable */ + Tuofl = 0x00000200, /* TCP/UDP Checksum Off-load Enable */ +}; + +enum { /* Receive Delay Timer Ring */ + Fpd = 0x80000000, /* Flush partial Descriptor Block */ +}; + +typedef struct Rdesc { /* Receive Descriptor */ + uint addr[2]; + ushort length; + ushort checksum; + uchar status; + uchar errors; + ushort special; +} Rdesc; + +enum { /* Rdesc status */ + Rdd = 0x01, /* Descriptor Done */ + Reop = 0x02, /* End of Packet */ + Ixsm = 0x04, /* Ignore Checksum Indication */ + Vp = 0x08, /* Packet is 802.1Q (matched VET) */ + Tcpcs = 0x20, /* TCP Checksum Calculated on Packet */ + Ipcs = 0x40, /* IP Checksum Calculated on Packet */ + Pif = 0x80, /* Passed in-exact filter */ +}; + +enum { /* Rdesc errors */ + Ce = 0x01, /* CRC Error or Alignment Error */ + Se = 0x02, /* Symbol Error */ + Seq = 0x04, /* Sequence Error */ + Cxe = 0x10, /* Carrier Extension Error */ + Tcpe = 0x20, /* TCP/UDP Checksum Error */ + Ipe = 0x40, /* IP Checksum Error */ + Rxe = 0x80, /* RX Data Error */ +}; + +typedef struct Tdesc { /* Legacy+Normal Transmit Descriptor */ + uint addr[2]; + uint control; /* varies with descriptor type */ + uint status; /* varies with descriptor type */ +} Tdesc; + +enum { /* Tdesc control */ + CsoMASK = 0x00000F00, /* Checksum Offset */ + CsoSHIFT = 16, + Teop = 0x01000000, /* End of Packet */ + Ifcs = 0x02000000, /* Insert FCS */ + Ic = 0x04000000, /* Insert Checksum (Dext == 0) */ + Tse = 0x04000000, /* TCP Segmentaion Enable (Dext == 1) */ + Rs = 0x08000000, /* Report Status */ + Rps = 0x10000000, /* Report Status Sent */ + Dext = 0x20000000, /* Extension (!legacy) */ + Vle = 0x40000000, /* VLAN Packet Enable */ + Ide = 0x80000000, /* Interrupt Delay Enable */ +}; + +enum { /* Tdesc status */ + Tdd = 0x00000001, /* Descriptor Done */ + Ec = 0x00000002, /* Excess Collisions */ + Lc = 0x00000004, /* Late Collision */ + Tu = 0x00000008, /* Transmit Underrun */ + CssMASK = 0x0000FF00, /* Checksum Start Field */ + CssSHIFT = 8, +}; + +enum { + Nrdesc = 256, /* multiple of 8 */ + Ntdesc = 256, /* multiple of 8 */ + Nblocks = 4098, /* total number of blocks to use */ + + SBLOCKSIZE = 2048, + JBLOCKSIZE = 16384, + + NORMAL = 1, + JUMBO = 2, +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int started; + int id; + ushort eeprom[0x40]; + + int* nic; + int im; /* interrupt mask */ + + Lock slock; + uint statistics[Nstatistics]; + + Lock rdlock; + Rdesc* rdba; /* receive descriptor base address */ + Block* rb[Nrdesc]; /* receive buffers */ + int rdh; /* receive descriptor head */ + int rdt; /* receive descriptor tail */ + Block** freehead; /* points to long or short head */ + + Lock tdlock; + Tdesc* tdba; /* transmit descriptor base address */ + Block* tb[Ntdesc]; /* transmit buffers */ + int tdh; /* transmit descriptor head */ + int tdt; /* transmit descriptor tail */ + int txstalled; /* count of times unable to send */ + + int txcw; + int fcrtl; + int fcrth; + + ulong multimask[128]; /* bit mask for multicast addresses */ +} Ctlr; + +static Ctlr* gc82543ctlrhead; +static Ctlr* gc82543ctlrtail; + +static Lock freelistlock; +static Block* freeShortHead; +static Block* freeJumboHead; + +#define csr32r(c, r) (*((c)->nic+((r)/4))) +#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v)) + +static void gc82543watchdog(void* arg); + +static void +gc82543attach(Ether* edev) +{ + int ctl; + Ctlr *ctlr; + char name[KNAMELEN]; + + /* + * To do here: + * one-time stuff; + * adjust queue length depending on speed; + * flow control. + * more needed here... + */ + ctlr = edev->ctlr; + lock(&ctlr->slock); + if(ctlr->started == 0){ + ctlr->started = 1; + snprint(name, KNAMELEN, "#l%d82543", edev->ctlrno); + kproc(name, gc82543watchdog, edev, 0); + } + unlock(&ctlr->slock); + + ctl = csr32r(ctlr, Rctl)|Ren; + csr32w(ctlr, Rctl, ctl); + ctl = csr32r(ctlr, Tctl)|Ten; + csr32w(ctlr, Tctl, ctl); + + csr32w(ctlr, Ims, ctlr->im); +} + +static char* statistics[Nstatistics] = { + "CRC Error", + "Alignment Error", + "Symbol Error", + "RX Error", + "Missed Packets", + "Single Collision", + "Excessive Collisions", + "Multiple Collision", + "Late Collisions", + nil, + "Collision", + "Transmit Underrun", + "Defer", + "Transmit - No CRS", + "Sequence Error", + "Carrier Extension Error", + "Receive Error Length", + nil, + "XON Received", + "XON Transmitted", + "XOFF Received", + "XOFF Transmitted", + "FC Received Unsupported", + "Packets Received (64 Bytes)", + "Packets Received (65-127 Bytes)", + "Packets Received (128-255 Bytes)", + "Packets Received (256-511 Bytes)", + "Packets Received (512-1023 Bytes)", + "Packets Received (1024-1522 Bytes)", + "Good Packets Received", + "Broadcast Packets Received", + "Multicast Packets Received", + "Good Packets Transmitted", + nil, + "Good Octets Received", + nil, + "Good Octets Transmitted", + nil, + nil, + nil, + "Receive No Buffers", + "Receive Undersize", + "Receive Fragment", + "Receive Oversize", + "Receive Jabber", + nil, + nil, + nil, + "Total Octets Received", + nil, + "Total Octets Transmitted", + nil, + "Total Packets Received", + "Total Packets Transmitted", + "Packets Transmitted (64 Bytes)", + "Packets Transmitted (65-127 Bytes)", + "Packets Transmitted (128-255 Bytes)", + "Packets Transmitted (256-511 Bytes)", + "Packets Transmitted (512-1023 Bytes)", + "Packets Transmitted (1024-1522 Bytes)", + "Multicast Packets Transmitted", + "Broadcast Packets Transmitted", + "TCP Segmentation Context Transmitted", + "TCP Segmentation Context Fail", +}; + +static long +gc82543ifstat(Ether* edev, void* a, long n, ulong offset) +{ + Ctlr *ctlr; + char *p, *s; + int i, l, r; + uvlong tuvl, ruvl; + + ctlr = edev->ctlr; + lock(&ctlr->slock); + p = malloc(2*READSTR); + l = 0; + for(i = 0; i < Nstatistics; i++){ + r = csr32r(ctlr, Statistics+i*4); + if((s = statistics[i]) == nil) + continue; + switch(i){ + case Gorcl: + case Gotcl: + case Torl: + case Totl: + ruvl = r; + ruvl += ((uvlong)csr32r(ctlr, Statistics+(i+1)*4))<<32; + tuvl = ruvl; + tuvl += ctlr->statistics[i]; + tuvl += ((uvlong)ctlr->statistics[i+1])<<32; + if(tuvl == 0) + continue; + ctlr->statistics[i] = tuvl; + ctlr->statistics[i+1] = tuvl>>32; + l += snprint(p+l, 2*READSTR-l, "%s: %llud %llud\n", + s, tuvl, ruvl); + i++; + break; + + default: + ctlr->statistics[i] += r; + if(ctlr->statistics[i] == 0) + continue; + l += snprint(p+l, 2*READSTR-l, "%s: %ud %ud\n", + s, ctlr->statistics[i], r); + break; + } + } + + l += snprint(p+l, 2*READSTR-l, "eeprom:"); + for(i = 0; i < 0x40; i++){ + if(i && ((i & 0x07) == 0)) + l += snprint(p+l, 2*READSTR-l, "\n "); + l += snprint(p+l, 2*READSTR-l, " %4.4uX", ctlr->eeprom[i]); + } + + snprint(p+l, 2*READSTR-l, "\ntxstalled %d\n", ctlr->txstalled); + n = readstr(offset, a, n, p); + free(p); + unlock(&ctlr->slock); + + return n; +} + +static void +gc82543promiscuous(void* arg, int on) +{ + int rctl; + Ctlr *ctlr; + Ether *edev; + + edev = arg; + ctlr = edev->ctlr; + + rctl = csr32r(ctlr, Rctl); + rctl &= ~MoMASK; /* make sure we're using bits 47:36 */ + if(on) + rctl |= Upe|Mpe; + else + rctl &= ~(Upe|Mpe); + csr32w(ctlr, Rctl, rctl); +} + +static void +gc82543multicast(void* arg, uchar* addr, int on) +{ + int bit, x; + Ctlr *ctlr; + Ether *edev; + + edev = arg; + ctlr = edev->ctlr; + x = addr[5]>>1; + bit = ((addr[5] & 1)<<4)|(addr[4]>>4); + if(on) + ctlr->multimask[x] |= 1<multimask[x] &= ~(1<multimask[x]); +} + +static long +gc82543ctl(Ether* edev, void* buf, long n) +{ + Cmdbuf *cb; + Ctlr *ctlr; + int ctrl, i, r; + + ctlr = edev->ctlr; + if(ctlr == nil) + error(Enonexist); + + lock(&ctlr->slock); + r = 0; + cb = parsecmd(buf, n); + if(cb->nf < 2) + r = -1; + else if(cistrcmp(cb->f[0], "auto") == 0){ + ctrl = csr32r(ctlr, Ctrl); + if(cistrcmp(cb->f[1], "off") == 0){ + csr32w(ctlr, Txcw, ctlr->txcw & ~Ane); + ctrl |= (Slu|Fd); + if(ctlr->txcw & As) + ctrl |= Rfce; + if(ctlr->txcw & Ps) + ctrl |= Tfce; + csr32w(ctlr, Ctrl, ctrl); + } + else if(cistrcmp(cb->f[1], "on") == 0){ + csr32w(ctlr, Txcw, ctlr->txcw); + ctrl &= ~(Slu|Fd); + csr32w(ctlr, Ctrl, ctrl); + } + else + r = -1; + } + else if(cistrcmp(cb->f[0], "clear") == 0){ + if(cistrcmp(cb->f[1], "stats") == 0){ + for(i = 0; i < Nstatistics; i++) + ctlr->statistics[i] = 0; + } + else + r = -1; + } + else + r = -1; + unlock(&ctlr->slock); + + free(cb); + return (r == 0) ? n : r; +} + +static void +gc82543txinit(Ctlr* ctlr) +{ + int i; + int tdsize; + Block *bp, **bpp; + + tdsize = ROUND(Ntdesc*sizeof(Tdesc), 4096); + + if(ctlr->tdba == nil) + ctlr->tdba = xspanalloc(tdsize, 32, 0); + + for(i = 0; i < Ntdesc; i++){ + bpp = &ctlr->tb[i]; + bp = *bpp; + if(bp != nil){ + *bpp = nil; + freeb(bp); + } + memset(&ctlr->tdba[i], 0, sizeof(Tdesc)); + } + + csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba)); + csr32w(ctlr, Tdbah, 0); + csr32w(ctlr, Tdlen, Ntdesc*sizeof(Tdesc)); + + /* + * set the ring head and tail pointers. + */ + ctlr->tdh = 0; + csr32w(ctlr, Tdh, ctlr->tdh); + ctlr->tdt = 0; + csr32w(ctlr, Tdt, ctlr->tdt); + + csr32w(ctlr, Tipg, (6<<20)|(8<<10)|6); + csr32w(ctlr, Tidv, 128); + csr32w(ctlr, Ait, 0); + csr32w(ctlr, Txdmac, 0); + csr32w(ctlr, Txdctl, Gran|(4<im |= Txdw; +} + +static void +gc82543transmit(Ether* edev) +{ + Block *bp, **bpp; + Ctlr *ctlr; + Tdesc *tdesc; + int tdh, tdt, s; + + ctlr = edev->ctlr; + + ilock(&ctlr->tdlock); + tdh = ctlr->tdh; + for(;;){ + /* + * Free any completed packets + */ + tdesc = &ctlr->tdba[tdh]; + if(!(tdesc->status & Tdd)) + break; + memset(tdesc, 0, sizeof(Tdesc)); + bpp = &ctlr->tb[tdh]; + bp = *bpp; + if(bp != nil){ + *bpp = nil; + freeb(bp); + } + tdh = NEXT(tdh, Ntdesc); + } + ctlr->tdh = tdh; + s = csr32r(ctlr, Status); + + /* + * Try to fill the ring back up + * but only if link is up and transmission isn't paused. + */ + if((s & (Txoff|Lu)) == Lu){ + tdt = ctlr->tdt; + while(NEXT(tdt, Ntdesc) != tdh){ + if((bp = qget(edev->oq)) == nil) + break; + + tdesc = &ctlr->tdba[tdt]; + tdesc->addr[0] = PCIWADDR(bp->rp); + tdesc->control = Ide|Rs|Ifcs|Teop|BLEN(bp); + ctlr->tb[tdt] = bp; + tdt = NEXT(tdt, Ntdesc); + } + + if(tdt != ctlr->tdt){ + ctlr->tdt = tdt; + csr32w(ctlr, Tdt, tdt); + } + } + else + ctlr->txstalled++; + + iunlock(&ctlr->tdlock); +} + +static Block * +gc82543allocb(Ctlr* ctlr) +{ + Block *bp; + + ilock(&freelistlock); + if((bp = *(ctlr->freehead)) != nil){ + *(ctlr->freehead) = bp->next; + bp->next = nil; + } + iunlock(&freelistlock); + return bp; +} + +static void +gc82543replenish(Ctlr* ctlr) +{ + int rdt; + Block *bp; + Rdesc *rdesc; + + ilock(&ctlr->rdlock); + rdt = ctlr->rdt; + while(NEXT(rdt, Nrdesc) != ctlr->rdh){ + rdesc = &ctlr->rdba[rdt]; + if(ctlr->rb[rdt] == nil){ + bp = gc82543allocb(ctlr); + if(bp == nil){ + iprint("no available buffers\n"); + break; + } + ctlr->rb[rdt] = bp; + rdesc->addr[0] = PCIWADDR(bp->rp); + rdesc->addr[1] = 0; + } + coherence(); + rdesc->status = 0; + rdt = NEXT(rdt, Nrdesc); + } + ctlr->rdt = rdt; + csr32w(ctlr, Rdt, rdt); + iunlock(&ctlr->rdlock); +} + +static void +gc82543rxinit(Ctlr* ctlr) +{ + int rdsize, i; + + csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF); + + /* + * Allocate the descriptor ring and load its + * address and length into the NIC. + */ + rdsize = ROUND(Nrdesc*sizeof(Rdesc), 4096); + if(ctlr->rdba == nil) + ctlr->rdba = xspanalloc(rdsize, 32, 0); + memset(ctlr->rdba, 0, rdsize); + + ctlr->rdh = 0; + ctlr->rdt = 0; + + csr32w(ctlr, Rdtr, Fpd|64); + csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba)); + csr32w(ctlr, Rdbah, 0); + csr32w(ctlr, Rdlen, Nrdesc*sizeof(Rdesc)); + csr32w(ctlr, Rdh, 0); + csr32w(ctlr, Rdt, 0); + for(i = 0; i < Nrdesc; i++){ + if(ctlr->rb[i] != nil){ + freeb(ctlr->rb[i]); + ctlr->rb[i] = nil; + } + } + gc82543replenish(ctlr); + + csr32w(ctlr, Rxdctl, RxGran|(8<im |= Rxt0|Rxo|Rxdmt0|Rxseq; +} + +static void +gc82543recv(Ether* edev, int icr) +{ + Block *bp; + Ctlr *ctlr; + Rdesc *rdesc; + int rdh; + + ctlr = edev->ctlr; + + rdh = ctlr->rdh; + for(;;){ + rdesc = &ctlr->rdba[rdh]; + + if(!(rdesc->status & Rdd)) + break; + + if((rdesc->status & Reop) && rdesc->errors == 0){ + bp = ctlr->rb[rdh]; + ctlr->rb[rdh] = nil; + bp->wp += rdesc->length; + bp->next = nil; + etheriq(edev, bp, 1); + } + + if(ctlr->rb[rdh] != nil){ + /* either non eop packet, or error */ + freeb(ctlr->rb[rdh]); + ctlr->rb[rdh] = nil; + } + memset(rdesc, 0, sizeof(Rdesc)); + coherence(); + rdh = NEXT(rdh, Nrdesc); + } + ctlr->rdh = rdh; + + if(icr & Rxdmt0) + gc82543replenish(ctlr); +} + +static void +freegc82543short(Block *bp) +{ + ilock(&freelistlock); + /* reset read/write pointer to proper positions */ + bp->rp = bp->lim - ROUND(SBLOCKSIZE, BLOCKALIGN); + bp->wp = bp->rp; + bp->next = freeShortHead; + freeShortHead = bp; + iunlock(&freelistlock); +} + +static void +freegc82532jumbo(Block *bp) +{ + ilock(&freelistlock); + /* reset read/write pointer to proper positions */ + bp->rp = bp->lim - ROUND(JBLOCKSIZE, BLOCKALIGN); + bp->wp = bp->rp; + bp->next = freeJumboHead; + freeJumboHead = bp; + iunlock(&freelistlock); +} + +static void +linkintr(Ctlr* ctlr) +{ + int ctrl; + + ctrl = csr32r(ctlr, Ctrl); + + if((ctrl & Swdpin1) || + ((csr32r(ctlr, Rxcw) & Rxconfig) && !(csr32r(ctlr, Txcw) & Ane))){ + csr32w(ctlr, Txcw, ctlr->txcw); + ctrl &= ~(Slu|Fd|Frcdplx); + csr32w(ctlr, Ctrl, ctrl); + } +} + +static void +gc82543interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *edev; + int icr; + + edev = arg; + ctlr = edev->ctlr; + + while((icr = csr32r(ctlr, Icr) & ctlr->im) != 0){ + /* + * Link status changed. + */ + if(icr & (Lsc|Rxseq)) + linkintr(ctlr); + + /* + * Process recv buffers. + */ + gc82543recv(edev, icr); + + /* + * Refill transmit ring and free packets. + */ + gc82543transmit(edev); + } +} + +static int +gc82543init(Ether* edev) +{ + int csr, i; + Block *bp; + Ctlr *ctlr; + + ctlr = edev->ctlr; + + /* + * Allocate private buffer pool to use for receiving packets. + */ + ilock(&freelistlock); + if (ctlr->freehead == nil){ + for(i = 0; i < Nblocks; i++){ + bp = iallocb(SBLOCKSIZE); + if(bp != nil){ + bp->next = freeShortHead; + bp->free = freegc82543short; + freeShortHead = bp; + } + else{ + print("82543gc: no memory\n"); + break; + } + } + ctlr->freehead = &freeShortHead; + } + iunlock(&freelistlock); + + /* + * Set up the receive addresses. + * There are 16 addresses. The first should be the MAC address. + * The others are cleared and not marked valid (MS bit of Rah). + */ + csr = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0]; + csr32w(ctlr, Ral, csr); + csr = 0x80000000|(edev->ea[5]<<8)|edev->ea[4]; + csr32w(ctlr, Rah, csr); + for(i = 1; i < 16; i++){ + csr32w(ctlr, Ral+i*8, 0); + csr32w(ctlr, Rah+i*8, 0); + } + + /* + * Clear the Multicast Table Array. + * It's a 4096 bit vector accessed as 128 32-bit registers. + */ + for(i = 0; i < 128; i++) + csr32w(ctlr, Mta+i*4, 0); + + gc82543txinit(ctlr); + gc82543rxinit(ctlr); + + return 0; +} + +static int +at93c46io(Ctlr* ctlr, char* op, int data) +{ + char *lp, *p; + int i, loop, eecd, r; + + eecd = csr32r(ctlr, Eecd); + + r = 0; + loop = -1; + lp = nil; + for(p = op; *p != '\0'; p++){ + switch(*p){ + default: + return -1; + case ' ': + continue; + case ':': /* start of loop */ + if(lp != nil){ + if(p != (lp+1) || loop != 7) + return -1; + lp = p; + loop = 15; + continue; + } + lp = p; + loop = 7; + continue; + case ';': /* end of loop */ + if(lp == nil) + return -1; + loop--; + if(loop >= 0) + p = lp; + else + lp = nil; + continue; + case 'C': /* assert clock */ + eecd |= Sk; + break; + case 'c': /* deassert clock */ + eecd &= ~Sk; + break; + case 'D': /* next bit in 'data' byte */ + if(loop < 0) + return -1; + if(data & (1<= 0) + r |= (i<= 0) + return -1; + return r; +} + +static int +at93c46r(Ctlr* ctlr) +{ + ushort sum; + int addr, data; + + sum = 0; + for(addr = 0; addr < 0x40; addr++){ + /* + * Read a word at address 'addr' from the Atmel AT93C46 + * 3-Wire Serial EEPROM or compatible. The EEPROM access is + * controlled by 4 bits in Eecd. See the AT93C46 datasheet + * for protocol details. + */ + if(at93c46io(ctlr, "S ICc :DCc;", (0x02<<6)|addr) != 0) + break; + data = at93c46io(ctlr, "::COc;", 0); + at93c46io(ctlr, "sic", 0); + ctlr->eeprom[addr] = data; + sum += data; + } + + return sum; +} + +static void +gc82543detach(Ctlr* ctlr) +{ + /* + * Perform a device reset to get the chip back to the + * power-on state, followed by an EEPROM reset to read + * the defaults for some internal registers. + */ + csr32w(ctlr, Imc, ~0); + csr32w(ctlr, Rctl, 0); + csr32w(ctlr, Tctl, 0); + + delay(10); + + csr32w(ctlr, Ctrl, Devrst); + while(csr32r(ctlr, Ctrl) & Devrst) + ; + + csr32w(ctlr, Ctrlext, Eerst); + while(csr32r(ctlr, Ctrlext) & Eerst) + ; + + csr32w(ctlr, Imc, ~0); + while(csr32r(ctlr, Icr)) + ; +} + +static void +gc82543checklink(Ctlr* ctlr) +{ + int ctrl, status, rxcw; + + ctrl = csr32r(ctlr, Ctrl); + status = csr32r(ctlr, Status); + rxcw = csr32r(ctlr, Rxcw); + + if(!(status & Lu)){ + if(!(ctrl & (Swdpin1|Slu)) && !(rxcw & Rxconfig)){ + csr32w(ctlr, Txcw, ctlr->txcw & ~Ane); + ctrl |= (Slu|Fd); + if(ctlr->txcw & As) + ctrl |= Rfce; + if(ctlr->txcw & Ps) + ctrl |= Tfce; + csr32w(ctlr, Ctrl, ctrl); + } + } + else if((ctrl & Slu) && (rxcw & Rxconfig)){ + csr32w(ctlr, Txcw, ctlr->txcw); + ctrl &= ~(Slu|Fd); + csr32w(ctlr, Ctrl, ctrl); + } +} + +static void +gc82543shutdown(Ether* ether) +{ + gc82543detach(ether->ctlr); +} + +static int +gc82543reset(Ctlr* ctlr) +{ + int ctl; + int te; + + /* + * Read the EEPROM, validate the checksum + * then get the device back to a power-on state. + */ + if(at93c46r(ctlr) != 0xBABA) + return -1; + + gc82543detach(ctlr); + + te = ctlr->eeprom[Icw2]; + if((te & 0x3000) == 0){ + ctlr->fcrtl = 0x00002000; + ctlr->fcrth = 0x00004000; + ctlr->txcw = Ane|TxcwFd; + } + else if((te & 0x3000) == 0x2000){ + ctlr->fcrtl = 0; + ctlr->fcrth = 0; + ctlr->txcw = Ane|TxcwFd|As; + } + else{ + ctlr->fcrtl = 0x00002000; + ctlr->fcrth = 0x00004000; + ctlr->txcw = Ane|TxcwFd|As|Ps; + } + + csr32w(ctlr, Txcw, ctlr->txcw); + + csr32w(ctlr, Ctrlext, (te & 0x00f0)<<4); + + csr32w(ctlr, Tctl, csr32r(ctlr, Tctl)|(64<eeprom[Icw1]; + ctl = ((te & 0x01E0)<<17)|(te & 0x0010)<<3; + csr32w(ctlr, Ctrl, ctl); + + delay(10); + + /* + * Flow control - values from the datasheet. + */ + csr32w(ctlr, Fcal, 0x00C28001); + csr32w(ctlr, Fcah, 0x00000100); + csr32w(ctlr, Fct, 0x00008808); + csr32w(ctlr, Fcttv, 0x00000100); + + csr32w(ctlr, Fcrtl, ctlr->fcrtl); + csr32w(ctlr, Fcrth, ctlr->fcrth); + + ctlr->im = Lsc; + gc82543checklink(ctlr); + + return 0; +} + +static void +gc82543watchdog(void* arg) +{ + Ether *edev; + Ctlr *ctlr; + + edev = arg; + for(;;){ + tsleep(&up->sleep, return0, 0, 1000); + + ctlr = edev->ctlr; + if(ctlr == nil){ + print("%s: exiting\n", up->text); + pexit("disabled", 0); + } + + gc82543checklink(ctlr); + gc82543replenish(ctlr); + } +} + +static void +gc82543pci(void) +{ + int port, cls; + Pcidev *p; + Ctlr *ctlr; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + + switch((p->did<<16)|p->vid){ + case (0x1000<<16)|0x8086: /* LSI L2A1157 (82542) */ + case (0x1004<<16)|0x8086: /* Intel PRO/1000 T */ + case (0x1008<<16)|0x8086: /* Intel PRO/1000 XT */ + default: + continue; + case (0x1001<<16)|0x8086: /* Intel PRO/1000 F */ + break; + } + + port = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0); + if(port == 0){ + print("gc82543: can't map %8.8luX\n", p->mem[0].bar); + continue; + } + cls = pcicfgr8(p, PciCLS); + switch(cls){ + case 0x00: + case 0xFF: + print("82543gc: unusable cache line size\n"); + continue; + case 0x08: + break; + default: + print("82543gc: cache line size %d, expected 32\n", + cls*4); + } + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = port; + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + + ctlr->nic = KADDR(ctlr->port); + + if(gc82543reset(ctlr)){ + free(ctlr); + continue; + } + + if(gc82543ctlrhead != nil) + gc82543ctlrtail->next = ctlr; + else + gc82543ctlrhead = ctlr; + gc82543ctlrtail = ctlr; + } +} + +static int +gc82543pnp(Ether* edev) +{ + int i; + Ctlr *ctlr; + uchar ea[Eaddrlen]; + + if(gc82543ctlrhead == nil) + gc82543pci(); + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = gc82543ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(edev->port == 0 || edev->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pcidev->intl; + edev->tbdf = ctlr->pcidev->tbdf; + edev->mbps = 1000; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to + * loading the station address in the hardware. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, edev->ea, Eaddrlen) == 0){ + for(i = Ea; i < Eaddrlen/2; i++){ + edev->ea[2*i] = ctlr->eeprom[i]; + edev->ea[2*i+1] = ctlr->eeprom[i]>>8; + } + } + gc82543init(edev); + + /* + * Linkage to the generic ethernet driver. + */ + edev->attach = gc82543attach; + edev->transmit = gc82543transmit; + edev->interrupt = gc82543interrupt; + edev->ifstat = gc82543ifstat; + edev->shutdown = gc82543shutdown; + edev->ctl = gc82543ctl; + edev->arg = edev; + edev->promiscuous = gc82543promiscuous; + edev->multicast = gc82543multicast; + + return 0; +} + +void +ether82543gclink(void) +{ + addethercard("82543GC", gc82543pnp); +} diff --git a/os/pc/ether82557.c b/os/pc/ether82557.c new file mode 100644 index 00000000..b61e3e1c --- /dev/null +++ b/os/pc/ether82557.c @@ -0,0 +1,1325 @@ +/* + * Intel 82557 Fast Ethernet PCI Bus LAN Controller + * as found on the Intel EtherExpress PRO/100B. This chip is full + * of smarts, unfortunately they're not all in the right place. + * To do: + * the PCI scanning code could be made common to other adapters; + * auto-negotiation, full-duplex; + * optionally use memory-mapped registers; + * detach for PCI reset problems (also towards loadable drivers). + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +enum { + Nrfd = 64, /* receive frame area */ + Ncb = 64, /* maximum control blocks queued */ + + NullPointer = 0xFFFFFFFF, /* 82557 NULL pointer */ +}; + +enum { /* CSR */ + Status = 0x00, /* byte or word (word includes Ack) */ + Ack = 0x01, /* byte */ + CommandR = 0x02, /* byte or word (word includes Interrupt) */ + Interrupt = 0x03, /* byte */ + General = 0x04, /* dword */ + Port = 0x08, /* dword */ + Fcr = 0x0C, /* Flash control register */ + Ecr = 0x0E, /* EEPROM control register */ + Mcr = 0x10, /* MDI control register */ + Gstatus = 0x1D, /* General status register */ +}; + +enum { /* Status */ + RUidle = 0x0000, + RUsuspended = 0x0004, + RUnoresources = 0x0008, + RUready = 0x0010, + RUrbd = 0x0020, /* bit */ + RUstatus = 0x003F, /* mask */ + + CUidle = 0x0000, + CUsuspended = 0x0040, + CUactive = 0x0080, + CUstatus = 0x00C0, /* mask */ + + StatSWI = 0x0400, /* SoftWare generated Interrupt */ + StatMDI = 0x0800, /* MDI r/w done */ + StatRNR = 0x1000, /* Receive unit Not Ready */ + StatCNA = 0x2000, /* Command unit Not Active (Active->Idle) */ + StatFR = 0x4000, /* Finished Receiving */ + StatCX = 0x8000, /* Command eXecuted */ + StatTNO = 0x8000, /* Transmit NOT OK */ +}; + +enum { /* Command (byte) */ + CUnop = 0x00, + CUstart = 0x10, + CUresume = 0x20, + LoadDCA = 0x40, /* Load Dump Counters Address */ + DumpSC = 0x50, /* Dump Statistical Counters */ + LoadCUB = 0x60, /* Load CU Base */ + ResetSA = 0x70, /* Dump and Reset Statistical Counters */ + + RUstart = 0x01, + RUresume = 0x02, + RUabort = 0x04, + LoadHDS = 0x05, /* Load Header Data Size */ + LoadRUB = 0x06, /* Load RU Base */ + RBDresume = 0x07, /* Resume frame reception */ +}; + +enum { /* Interrupt (byte) */ + InterruptM = 0x01, /* interrupt Mask */ + InterruptSI = 0x02, /* Software generated Interrupt */ +}; + +enum { /* Ecr */ + EEsk = 0x01, /* serial clock */ + EEcs = 0x02, /* chip select */ + EEdi = 0x04, /* serial data in */ + EEdo = 0x08, /* serial data out */ + + EEstart = 0x04, /* start bit */ + EEread = 0x02, /* read opcode */ +}; + +enum { /* Mcr */ + MDIread = 0x08000000, /* read opcode */ + MDIwrite = 0x04000000, /* write opcode */ + MDIready = 0x10000000, /* ready bit */ + MDIie = 0x20000000, /* interrupt enable */ +}; + +typedef struct Rfd { + int field; + ulong link; + ulong rbd; + ushort count; + ushort size; + + uchar data[1700]; +} Rfd; + +enum { /* field */ + RfdCollision = 0x00000001, + RfdIA = 0x00000002, /* IA match */ + RfdRxerr = 0x00000010, /* PHY character error */ + RfdType = 0x00000020, /* Type frame */ + RfdRunt = 0x00000080, + RfdOverrun = 0x00000100, + RfdBuffer = 0x00000200, + RfdAlignment = 0x00000400, + RfdCRC = 0x00000800, + + RfdOK = 0x00002000, /* frame received OK */ + RfdC = 0x00008000, /* reception Complete */ + RfdSF = 0x00080000, /* Simplified or Flexible (1) Rfd */ + RfdH = 0x00100000, /* Header RFD */ + + RfdI = 0x20000000, /* Interrupt after completion */ + RfdS = 0x40000000, /* Suspend after completion */ + RfdEL = 0x80000000, /* End of List */ +}; + +enum { /* count */ + RfdF = 0x4000, + RfdEOF = 0x8000, +}; + +typedef struct Cb Cb; +typedef struct Cb { + ushort status; + ushort command; + ulong link; + union { + uchar data[24]; /* CbIAS + CbConfigure */ + struct { + ulong tbd; + ushort count; + uchar threshold; + uchar number; + + ulong tba; + ushort tbasz; + ushort pad; + }; + }; + + Block* bp; + Cb* next; +} Cb; + +enum { /* action command */ + CbU = 0x1000, /* transmit underrun */ + CbOK = 0x2000, /* DMA completed OK */ + CbC = 0x8000, /* execution Complete */ + + CbNOP = 0x0000, + CbIAS = 0x0001, /* Individual Address Setup */ + CbConfigure = 0x0002, + CbMAS = 0x0003, /* Multicast Address Setup */ + CbTransmit = 0x0004, + CbDump = 0x0006, + CbDiagnose = 0x0007, + CbCommand = 0x0007, /* mask */ + + CbSF = 0x0008, /* Flexible-mode CbTransmit */ + + CbI = 0x2000, /* Interrupt after completion */ + CbS = 0x4000, /* Suspend after completion */ + CbEL = 0x8000, /* End of List */ +}; + +enum { /* CbTransmit count */ + CbEOF = 0x8000, +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + Lock slock; /* attach */ + int state; + + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + + int eepromsz; /* address size in bits */ + ushort* eeprom; + + Lock miilock; + + int tick; + + Lock rlock; /* registers */ + int command; /* last command issued */ + + Block* rfdhead; /* receive side */ + Block* rfdtail; + int nrfd; + + Lock cblock; /* transmit side */ + int action; + int nop; + uchar configdata[24]; + int threshold; + int ncb; + Cb* cbr; + Cb* cbhead; + Cb* cbtail; + int cbq; + int cbqmax; + int cbqmaxhw; + + Lock dlock; /* dump statistical counters */ + ulong dump[17]; +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +static uchar configdata[24] = { + 0x16, /* byte count */ + 0x08, /* Rx/Tx FIFO limit */ + 0x00, /* adaptive IFS */ + 0x00, + 0x00, /* Rx DMA maximum byte count */ +// 0x80, /* Tx DMA maximum byte count */ + 0x00, /* Tx DMA maximum byte count */ + 0x32, /* !late SCB, CNA interrupts */ + 0x03, /* discard short Rx frames */ + 0x00, /* 503/MII */ + + 0x00, + 0x2E, /* normal operation, NSAI */ + 0x00, /* linear priority */ + 0x60, /* inter-frame spacing */ + 0x00, + 0xF2, + 0xC8, /* 503, promiscuous mode off */ + 0x00, + 0x40, + 0xF3, /* transmit padding enable */ + 0x80, /* full duplex pin enable */ + 0x3F, /* no Multi IA */ + 0x05, /* no Multi Cast ALL */ +}; + +#define csr8r(c, r) (inb((c)->port+(r))) +#define csr16r(c, r) (ins((c)->port+(r))) +#define csr32r(c, r) (inl((c)->port+(r))) +#define csr8w(c, r, b) (outb((c)->port+(r), (int)(b))) +#define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w))) +#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l))) + +static void +command(Ctlr* ctlr, int c, int v) +{ + int timeo; + + ilock(&ctlr->rlock); + + /* + * Only back-to-back CUresume can be done + * without waiting for any previous command to complete. + * This should be the common case. + * Unfortunately there's a chip errata where back-to-back + * CUresumes can be lost, the fix is to always wait. + if(c == CUresume && ctlr->command == CUresume){ + csr8w(ctlr, CommandR, c); + iunlock(&ctlr->rlock); + return; + } + */ + + for(timeo = 0; timeo < 100; timeo++){ + if(!csr8r(ctlr, CommandR)) + break; + microdelay(1); + } + if(timeo >= 100){ + ctlr->command = -1; + iunlock(&ctlr->rlock); + iprint("i82557: command 0x%uX %uX timeout\n", c, v); + return; + } + + switch(c){ + + case CUstart: + case LoadDCA: + case LoadCUB: + case RUstart: + case LoadHDS: + case LoadRUB: + csr32w(ctlr, General, v); + break; + + /* + case CUnop: + case CUresume: + case DumpSC: + case ResetSA: + case RUresume: + case RUabort: + */ + default: + break; + } + csr8w(ctlr, CommandR, c); + ctlr->command = c; + + iunlock(&ctlr->rlock); +} + +static Block* +rfdalloc(ulong link) +{ + Block *bp; + Rfd *rfd; + + if(bp = iallocb(sizeof(Rfd))){ + rfd = (Rfd*)bp->rp; + rfd->field = 0; + rfd->link = link; + rfd->rbd = NullPointer; + rfd->count = 0; + rfd->size = sizeof(Etherpkt); + } + + return bp; +} + +static void +watchdog(void* arg) +{ + Ether *ether; + Ctlr *ctlr; + static void txstart(Ether*); + + ether = arg; + for(;;){ + tsleep(&up->sleep, return0, 0, 4000); + + /* + * Hmmm. This doesn't seem right. Currently + * the device can't be disabled but it may be in + * the future. + */ + ctlr = ether->ctlr; + if(ctlr == nil || ctlr->state == 0){ + print("%s: exiting\n", up->text); + pexit("disabled", 0); + } + + ilock(&ctlr->cblock); + if(ctlr->tick++){ + ctlr->action = CbMAS; + txstart(ether); + } + iunlock(&ctlr->cblock); + } +} + +static void +attach(Ether* ether) +{ + Ctlr *ctlr; + char name[KNAMELEN]; + + ctlr = ether->ctlr; + lock(&ctlr->slock); + if(ctlr->state == 0){ + ilock(&ctlr->rlock); + csr8w(ctlr, Interrupt, 0); + iunlock(&ctlr->rlock); + command(ctlr, RUstart, PADDR(ctlr->rfdhead->rp)); + ctlr->state = 1; + + /* + * Start the watchdog timer for the receive lockup errata + * unless the EEPROM compatibility word indicates it may be + * omitted. + */ + if((ctlr->eeprom[0x03] & 0x0003) != 0x0003){ + snprint(name, KNAMELEN, "#l%dwatchdog", ether->ctlrno); + kproc(name, watchdog, ether, 0); + } + } + unlock(&ctlr->slock); +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + char *p; + int i, len, phyaddr; + Ctlr *ctlr; + ulong dump[17]; + + ctlr = ether->ctlr; + lock(&ctlr->dlock); + + /* + * Start the command then + * wait for completion status, + * should be 0xA005. + */ + ctlr->dump[16] = 0; + command(ctlr, DumpSC, 0); + while(ctlr->dump[16] == 0) + ; + + ether->oerrs = ctlr->dump[1]+ctlr->dump[2]+ctlr->dump[3]; + ether->crcs = ctlr->dump[10]; + ether->frames = ctlr->dump[11]; + ether->buffs = ctlr->dump[12]+ctlr->dump[15]; + ether->overflows = ctlr->dump[13]; + + if(n == 0){ + unlock(&ctlr->dlock); + return 0; + } + + memmove(dump, ctlr->dump, sizeof(dump)); + unlock(&ctlr->dlock); + + p = malloc(READSTR); + len = snprint(p, READSTR, "transmit good frames: %lud\n", dump[0]); + len += snprint(p+len, READSTR-len, "transmit maximum collisions errors: %lud\n", dump[1]); + len += snprint(p+len, READSTR-len, "transmit late collisions errors: %lud\n", dump[2]); + len += snprint(p+len, READSTR-len, "transmit underrun errors: %lud\n", dump[3]); + len += snprint(p+len, READSTR-len, "transmit lost carrier sense: %lud\n", dump[4]); + len += snprint(p+len, READSTR-len, "transmit deferred: %lud\n", dump[5]); + len += snprint(p+len, READSTR-len, "transmit single collisions: %lud\n", dump[6]); + len += snprint(p+len, READSTR-len, "transmit multiple collisions: %lud\n", dump[7]); + len += snprint(p+len, READSTR-len, "transmit total collisions: %lud\n", dump[8]); + len += snprint(p+len, READSTR-len, "receive good frames: %lud\n", dump[9]); + len += snprint(p+len, READSTR-len, "receive CRC errors: %lud\n", dump[10]); + len += snprint(p+len, READSTR-len, "receive alignment errors: %lud\n", dump[11]); + len += snprint(p+len, READSTR-len, "receive resource errors: %lud\n", dump[12]); + len += snprint(p+len, READSTR-len, "receive overrun errors: %lud\n", dump[13]); + len += snprint(p+len, READSTR-len, "receive collision detect errors: %lud\n", dump[14]); + len += snprint(p+len, READSTR-len, "receive short frame errors: %lud\n", dump[15]); + len += snprint(p+len, READSTR-len, "nop: %d\n", ctlr->nop); + if(ctlr->cbqmax > ctlr->cbqmaxhw) + ctlr->cbqmaxhw = ctlr->cbqmax; + len += snprint(p+len, READSTR-len, "cbqmax: %d\n", ctlr->cbqmax); + ctlr->cbqmax = 0; + len += snprint(p+len, READSTR-len, "threshold: %d\n", ctlr->threshold); + + len += snprint(p+len, READSTR-len, "eeprom:"); + for(i = 0; i < (1<eepromsz); i++){ + if(i && ((i & 0x07) == 0)) + len += snprint(p+len, READSTR-len, "\n "); + len += snprint(p+len, READSTR-len, " %4.4uX", ctlr->eeprom[i]); + } + + if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000)){ + phyaddr = ctlr->eeprom[6] & 0x00FF; + len += snprint(p+len, READSTR-len, "\nphy %2d:", phyaddr); + for(i = 0; i < 6; i++){ + static int miir(Ctlr*, int, int); + + len += snprint(p+len, READSTR-len, " %4.4uX", + miir(ctlr, phyaddr, i)); + } + } + + snprint(p+len, READSTR-len, "\n"); + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static void +txstart(Ether* ether) +{ + Ctlr *ctlr; + Block *bp; + Cb *cb; + + ctlr = ether->ctlr; + while(ctlr->cbq < (ctlr->ncb-1)){ + cb = ctlr->cbhead->next; + if(ctlr->action == 0){ + bp = qget(ether->oq); + if(bp == nil) + break; + + cb->command = CbS|CbSF|CbTransmit; + cb->tbd = PADDR(&cb->tba); + cb->count = 0; + cb->threshold = ctlr->threshold; + cb->number = 1; + cb->tba = PADDR(bp->rp); + cb->bp = bp; + cb->tbasz = BLEN(bp); + } + else if(ctlr->action == CbConfigure){ + cb->command = CbS|CbConfigure; + memmove(cb->data, ctlr->configdata, sizeof(ctlr->configdata)); + ctlr->action = 0; + } + else if(ctlr->action == CbIAS){ + cb->command = CbS|CbIAS; + memmove(cb->data, ether->ea, Eaddrlen); + ctlr->action = 0; + } + else if(ctlr->action == CbMAS){ + cb->command = CbS|CbMAS; + memset(cb->data, 0, sizeof(cb->data)); + ctlr->action = 0; + } + else{ + print("#l%d: action 0x%uX\n", ether->ctlrno, ctlr->action); + ctlr->action = 0; + break; + } + cb->status = 0; + + coherence(); + ctlr->cbhead->command &= ~CbS; + ctlr->cbhead = cb; + ctlr->cbq++; + } + + /* + * Workaround for some broken HUB chips + * when connected at 10Mb/s half-duplex. + */ + if(ctlr->nop){ + command(ctlr, CUnop, 0); + microdelay(1); + } + command(ctlr, CUresume, 0); + + if(ctlr->cbq > ctlr->cbqmax) + ctlr->cbqmax = ctlr->cbq; +} + +static void +configure(Ether* ether, int promiscuous) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->cblock); + if(promiscuous){ + ctlr->configdata[6] |= 0x80; /* Save Bad Frames */ + //ctlr->configdata[6] &= ~0x40; /* !Discard Overrun Rx Frames */ + ctlr->configdata[7] &= ~0x01; /* !Discard Short Rx Frames */ + ctlr->configdata[15] |= 0x01; /* Promiscuous mode */ + ctlr->configdata[18] &= ~0x01; /* (!Padding enable?), !stripping enable */ + ctlr->configdata[21] |= 0x08; /* Multi Cast ALL */ + } + else{ + ctlr->configdata[6] &= ~0x80; + //ctlr->configdata[6] |= 0x40; + ctlr->configdata[7] |= 0x01; + ctlr->configdata[15] &= ~0x01; + ctlr->configdata[18] |= 0x01; /* 0x03? */ + ctlr->configdata[21] &= ~0x08; + } + ctlr->action = CbConfigure; + txstart(ether); + iunlock(&ctlr->cblock); +} + +static void +promiscuous(void* arg, int on) +{ + configure(arg, on); +} + +static void +multicast(void* arg, uchar *addr, int on) +{ + USED(addr, on); + configure(arg, 1); +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->cblock); + txstart(ether); + iunlock(&ctlr->cblock); +} + +static void +receive(Ether* ether) +{ + Rfd *rfd; + Ctlr *ctlr; + int count; + Block *bp, *pbp, *xbp; + + ctlr = ether->ctlr; + bp = ctlr->rfdhead; + for(rfd = (Rfd*)bp->rp; rfd->field & RfdC; rfd = (Rfd*)bp->rp){ + /* + * If it's an OK receive frame + * 1) save the count + * 2) if it's small, try to allocate a block and copy + * the data, then adjust the necessary fields for reuse; + * 3) if it's big, try to allocate a new Rfd and if + * successful + * adjust the received buffer pointers for the + * actual data received; + * initialise the replacement buffer to point to + * the next in the ring; + * initialise bp to point to the replacement; + * 4) if there's a good packet, pass it on for disposal. + */ + if(rfd->field & RfdOK){ + pbp = nil; + count = rfd->count & 0x3FFF; + if((count < ETHERMAXTU/4) && (pbp = iallocb(count))){ + memmove(pbp->rp, bp->rp+sizeof(Rfd)-sizeof(rfd->data), count); + pbp->wp = pbp->rp + count; + + rfd->count = 0; + rfd->field = 0; + } + else if(xbp = rfdalloc(rfd->link)){ + bp->rp += sizeof(Rfd)-sizeof(rfd->data); + bp->wp = bp->rp + count; + + xbp->next = bp->next; + bp->next = 0; + + pbp = bp; + bp = xbp; + } + if(pbp != nil) + etheriq(ether, pbp, 1); + } + else{ + rfd->count = 0; + rfd->field = 0; + } + + /* + * The ring tail pointer follows the head with with one + * unused buffer in between to defeat hardware prefetch; + * once the tail pointer has been bumped on to the next + * and the new tail has the Suspend bit set, it can be + * removed from the old tail buffer. + * As a replacement for the current head buffer may have + * been allocated above, ensure that the new tail points + * to it (next and link). + */ + rfd = (Rfd*)ctlr->rfdtail->rp; + ctlr->rfdtail = ctlr->rfdtail->next; + ctlr->rfdtail->next = bp; + ((Rfd*)ctlr->rfdtail->rp)->link = PADDR(bp->rp); + ((Rfd*)ctlr->rfdtail->rp)->field |= RfdS; + coherence(); + rfd->field &= ~RfdS; + + /* + * Finally done with the current (possibly replaced) + * head, move on to the next and maintain the sentinel + * between tail and head. + */ + ctlr->rfdhead = bp->next; + bp = ctlr->rfdhead; + } +} + +static void +interrupt(Ureg*, void* arg) +{ + Cb* cb; + Ctlr *ctlr; + Ether *ether; + int status; + + ether = arg; + ctlr = ether->ctlr; + + for(;;){ + ilock(&ctlr->rlock); + status = csr16r(ctlr, Status); + csr8w(ctlr, Ack, (status>>8) & 0xFF); + iunlock(&ctlr->rlock); + + if(!(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI))) + break; + + /* + * If the watchdog timer for the receiver lockup errata is running, + * let it know the receiver is active. + */ + if(status & (StatFR|StatRNR)){ + ilock(&ctlr->cblock); + ctlr->tick = 0; + iunlock(&ctlr->cblock); + } + + if(status & StatFR){ + receive(ether); + status &= ~StatFR; + } + + if(status & StatRNR){ + command(ctlr, RUresume, 0); + status &= ~StatRNR; + } + + if(status & StatCNA){ + ilock(&ctlr->cblock); + + cb = ctlr->cbtail; + while(ctlr->cbq){ + if(!(cb->status & CbC)) + break; + if(cb->bp){ + freeb(cb->bp); + cb->bp = nil; + } + if((cb->status & CbU) && ctlr->threshold < 0xE0) + ctlr->threshold++; + + ctlr->cbq--; + cb = cb->next; + } + ctlr->cbtail = cb; + + txstart(ether); + iunlock(&ctlr->cblock); + + status &= ~StatCNA; + } + + if(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI)) + panic("#l%d: status %uX\n", ether->ctlrno, status); + } +} + +static void +ctlrinit(Ctlr* ctlr) +{ + int i; + Block *bp; + Rfd *rfd; + ulong link; + + /* + * Create the Receive Frame Area (RFA) as a ring of allocated + * buffers. + * A sentinel buffer is maintained between the last buffer in + * the ring (marked with RfdS) and the head buffer to defeat the + * hardware prefetch of the next RFD and allow dynamic buffer + * allocation. + */ + link = NullPointer; + for(i = 0; i < Nrfd; i++){ + bp = rfdalloc(link); + if(ctlr->rfdhead == nil) + ctlr->rfdtail = bp; + bp->next = ctlr->rfdhead; + ctlr->rfdhead = bp; + link = PADDR(bp->rp); + } + ctlr->rfdtail->next = ctlr->rfdhead; + rfd = (Rfd*)ctlr->rfdtail->rp; + rfd->link = PADDR(ctlr->rfdhead->rp); + rfd->field |= RfdS; + ctlr->rfdhead = ctlr->rfdhead->next; + + /* + * Create a ring of control blocks for the + * transmit side. + */ + ilock(&ctlr->cblock); + ctlr->cbr = malloc(ctlr->ncb*sizeof(Cb)); + for(i = 0; i < ctlr->ncb; i++){ + ctlr->cbr[i].status = CbC|CbOK; + ctlr->cbr[i].command = CbS|CbNOP; + ctlr->cbr[i].link = PADDR(&ctlr->cbr[NEXT(i, ctlr->ncb)].status); + ctlr->cbr[i].next = &ctlr->cbr[NEXT(i, ctlr->ncb)]; + } + ctlr->cbhead = ctlr->cbr; + ctlr->cbtail = ctlr->cbr; + ctlr->cbq = 0; + + memmove(ctlr->configdata, configdata, sizeof(configdata)); + ctlr->threshold = 80; + ctlr->tick = 0; + + iunlock(&ctlr->cblock); +} + +static int +miir(Ctlr* ctlr, int phyadd, int regadd) +{ + int mcr, timo; + + lock(&ctlr->miilock); + csr32w(ctlr, Mcr, MDIread|(phyadd<<21)|(regadd<<16)); + mcr = 0; + for(timo = 64; timo; timo--){ + mcr = csr32r(ctlr, Mcr); + if(mcr & MDIready) + break; + microdelay(1); + } + unlock(&ctlr->miilock); + + if(mcr & MDIready) + return mcr & 0xFFFF; + + return -1; +} + +static int +miiw(Ctlr* ctlr, int phyadd, int regadd, int data) +{ + int mcr, timo; + + lock(&ctlr->miilock); + csr32w(ctlr, Mcr, MDIwrite|(phyadd<<21)|(regadd<<16)|(data & 0xFFFF)); + mcr = 0; + for(timo = 64; timo; timo--){ + mcr = csr32r(ctlr, Mcr); + if(mcr & MDIready) + break; + microdelay(1); + } + unlock(&ctlr->miilock); + + if(mcr & MDIready) + return 0; + + return -1; +} + +static int +hy93c46r(Ctlr* ctlr, int r) +{ + int data, i, op, size; + + /* + * Hyundai HY93C46 or equivalent serial EEPROM. + * This sequence for reading a 16-bit register 'r' + * in the EEPROM is taken straight from Section + * 3.3.4.2 of the Intel 82557 User's Guide. + */ +reread: + csr16w(ctlr, Ecr, EEcs); + op = EEstart|EEread; + for(i = 2; i >= 0; i--){ + data = (((op>>i) & 0x01)<<2)|EEcs; + csr16w(ctlr, Ecr, data); + csr16w(ctlr, Ecr, data|EEsk); + microdelay(1); + csr16w(ctlr, Ecr, data); + microdelay(1); + } + + /* + * First time through must work out the EEPROM size. + */ + if((size = ctlr->eepromsz) == 0) + size = 8; + + for(size = size-1; size >= 0; size--){ + data = (((r>>size) & 0x01)<<2)|EEcs; + csr16w(ctlr, Ecr, data); + csr16w(ctlr, Ecr, data|EEsk); + delay(1); + csr16w(ctlr, Ecr, data); + microdelay(1); + if(!(csr16r(ctlr, Ecr) & EEdo)) + break; + } + + data = 0; + for(i = 15; i >= 0; i--){ + csr16w(ctlr, Ecr, EEcs|EEsk); + microdelay(1); + if(csr16r(ctlr, Ecr) & EEdo) + data |= (1<eepromsz == 0){ + ctlr->eepromsz = 8-size; + ctlr->eeprom = malloc((1<eepromsz)*sizeof(ushort)); + goto reread; + } + + return data; +} + +static void +i82557pci(void) +{ + Pcidev *p; + Ctlr *ctlr; + int i, nop, port; + + p = nil; + nop = 0; + while(p = pcimatch(p, 0x8086, 0)){ + switch(p->did){ + default: + continue; + case 0x1031: /* Intel 82562EM */ + case 0x1050: /* Intel 82562EZ */ + case 0x2449: /* Intel 82562ET */ + nop = 1; + /*FALLTHROUGH*/ + case 0x1209: /* Intel 82559ER */ + case 0x1229: /* Intel 8255[789] */ + case 0x1030: /* Intel 82559 InBusiness 10/100 */ + case 0x1039: /* Intel 82801BD PRO/100 VE */ + case 0x103A: /* Intel 82562 PRO/100 VE */ + break; + } + + if(pcigetpms(p) > 0){ + pcisetpms(p, 0); + + for(i = 0; i < 6; i++) + pcicfgw32(p, PciBAR0+i*4, p->mem[i].bar); + pcicfgw8(p, PciINTL, p->intl); + pcicfgw8(p, PciLTR, p->ltr); + pcicfgw8(p, PciCLS, p->cls); + pcicfgw16(p, PciPCR, p->pcr); + } + + /* + * bar[0] is the memory-mapped register address (4KB), + * bar[1] is the I/O port register address (32 bytes) and + * bar[2] is for the flash ROM (1MB). + */ + port = p->mem[1].bar & ~0x01; + if(ioalloc(port, p->mem[1].size, 0, "i82557") < 0){ + print("i82557: port 0x%uX in use\n", port); + continue; + } + + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = port; + ctlr->pcidev = p; + ctlr->nop = nop; + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + + pcisetbme(p); + } +} + +static char* mediatable[9] = { + "10BASE-T", /* TP */ + "10BASE-2", /* BNC */ + "10BASE-5", /* AUI */ + "100BASE-TX", + "10BASE-TFD", + "100BASE-TXFD", + "100BASE-T4", + "100BASE-FX", + "100BASE-FXFD", +}; + +static int +scanphy(Ctlr* ctlr) +{ + int i, oui, x; + + for(i = 0; i < 32; i++){ + if((oui = miir(ctlr, i, 2)) == -1 || oui == 0 || oui == 0xFFFF) + continue; + oui <<= 6; + x = miir(ctlr, i, 3); + oui |= x>>10; + //print("phy%d: oui %uX reg1 %uX\n", i, oui, miir(ctlr, i, 1)); + + ctlr->eeprom[6] = i; + if(oui == 0xAA00) + ctlr->eeprom[6] |= 0x07<<8; + else if(oui == 0x80017){ + if(x & 0x01) + ctlr->eeprom[6] |= 0x0A<<8; + else + ctlr->eeprom[6] |= 0x04<<8; + } + return i; + } + return -1; +} + +static void +shutdown(Ether* ether) +{ + Ctlr *ctlr = ether->ctlr; + +print("ether82557 shutting down\n"); + csr32w(ctlr, Port, 0); + delay(1); + csr8w(ctlr, Interrupt, InterruptM); +} + + +static int +reset(Ether* ether) +{ + int anar, anlpar, bmcr, bmsr, i, k, medium, phyaddr, x; + unsigned short sum; + uchar ea[Eaddrlen]; + Ctlr *ctlr; + + if(ctlrhead == nil) + i82557pci(); + + /* + * Any adapter matches if no ether->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(ether->port == 0 || ether->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + /* + * Initialise the Ctlr structure. + * Perform a software reset after which should ensure busmastering + * is still enabled. The EtherExpress PRO/100B appears to leave + * the PCI configuration alone (see the 'To do' list above) so punt + * for now. + * Load the RUB and CUB registers for linear addressing (0). + */ + ether->ctlr = ctlr; + ether->port = ctlr->port; + ether->irq = ctlr->pcidev->intl; + ether->tbdf = ctlr->pcidev->tbdf; + + ilock(&ctlr->rlock); + csr32w(ctlr, Port, 0); + delay(1); + csr8w(ctlr, Interrupt, InterruptM); + iunlock(&ctlr->rlock); + + command(ctlr, LoadRUB, 0); + command(ctlr, LoadCUB, 0); + command(ctlr, LoadDCA, PADDR(ctlr->dump)); + + /* + * Initialise the receive frame, transmit ring and configuration areas. + */ + ctlr->ncb = Ncb; + ctlrinit(ctlr); + + /* + * Read the EEPROM. + * Do a dummy read first to get the size + * and allocate ctlr->eeprom. + */ + hy93c46r(ctlr, 0); + sum = 0; + for(i = 0; i < (1<eepromsz); i++){ + x = hy93c46r(ctlr, i); + ctlr->eeprom[i] = x; + sum += x; + } + if(sum != 0xBABA) + print("#l%d: EEPROM checksum - 0x%4.4uX\n", ether->ctlrno, sum); + + /* + * Eeprom[6] indicates whether there is a PHY and whether + * it's not 10Mb-only, in which case use the given PHY address + * to set any PHY specific options and determine the speed. + * Unfortunately, sometimes the EEPROM is blank except for + * the ether address and checksum; in this case look at the + * controller type and if it's am 82558 or 82559 it has an + * embedded PHY so scan for that. + * If no PHY, assume 82503 (serial) operation. + */ + if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000)) + phyaddr = ctlr->eeprom[6] & 0x00FF; + else + switch(ctlr->pcidev->rid){ + case 0x01: /* 82557 A-step */ + case 0x02: /* 82557 B-step */ + case 0x03: /* 82557 C-step */ + default: + phyaddr = -1; + break; + case 0x04: /* 82558 A-step */ + case 0x05: /* 82558 B-step */ + case 0x06: /* 82559 A-step */ + case 0x07: /* 82559 B-step */ + case 0x08: /* 82559 C-step */ + case 0x09: /* 82559ER A-step */ + phyaddr = scanphy(ctlr); + break; + } + if(phyaddr >= 0){ + /* + * Resolve the highest common ability of the two + * link partners. In descending order: + * 0x0100 100BASE-TX Full Duplex + * 0x0200 100BASE-T4 + * 0x0080 100BASE-TX + * 0x0040 10BASE-T Full Duplex + * 0x0020 10BASE-T + */ + anar = miir(ctlr, phyaddr, 0x04); + anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0; + anar &= anlpar; + bmcr = 0; + if(anar & 0x380) + bmcr = 0x2000; + if(anar & 0x0140) + bmcr |= 0x0100; + + switch((ctlr->eeprom[6]>>8) & 0x001F){ + + case 0x04: /* DP83840 */ + case 0x0A: /* DP83840A */ + /* + * The DP83840[A] requires some tweaking for + * reliable operation. + * The manual says bit 10 should be unconditionally + * set although it supposedly only affects full-duplex + * operation (an & 0x0140). + */ + x = miir(ctlr, phyaddr, 0x17) & ~0x0520; + x |= 0x0420; + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "congestioncontrol")) + continue; + x |= 0x0100; + break; + } + miiw(ctlr, phyaddr, 0x17, x); + + /* + * If the link partner can't autonegotiate, determine + * the speed from elsewhere. + */ + if(anlpar == 0){ + miir(ctlr, phyaddr, 0x01); + bmsr = miir(ctlr, phyaddr, 0x01); + x = miir(ctlr, phyaddr, 0x19); + if((bmsr & 0x0004) && !(x & 0x0040)) + bmcr = 0x2000; + } + break; + + case 0x07: /* Intel 82555 */ + /* + * Auto-negotiation may fail if the other end is + * a DP83840A and the cable is short. + */ + miir(ctlr, phyaddr, 0x01); + bmsr = miir(ctlr, phyaddr, 0x01); + if((miir(ctlr, phyaddr, 0) & 0x1000) && !(bmsr & 0x0020)){ + miiw(ctlr, phyaddr, 0x1A, 0x2010); + x = miir(ctlr, phyaddr, 0); + miiw(ctlr, phyaddr, 0, 0x0200|x); + for(i = 0; i < 3000; i++){ + delay(1); + if(miir(ctlr, phyaddr, 0x01) & 0x0020) + break; + } + miiw(ctlr, phyaddr, 0x1A, 0x2000); + + anar = miir(ctlr, phyaddr, 0x04); + anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0; + anar &= anlpar; + bmcr = 0; + if(anar & 0x380) + bmcr = 0x2000; + if(anar & 0x0140) + bmcr |= 0x0100; + } + break; + } + + /* + * Force speed and duplex if no auto-negotiation. + */ + if(anlpar == 0){ + medium = -1; + for(i = 0; i < ether->nopt; i++){ + for(k = 0; k < nelem(mediatable); k++){ + if(cistrcmp(mediatable[k], ether->opt[i])) + continue; + medium = k; + break; + } + + switch(medium){ + default: + break; + + case 0x00: /* 10BASE-T */ + case 0x01: /* 10BASE-2 */ + case 0x02: /* 10BASE-5 */ + bmcr &= ~(0x2000|0x0100); + ctlr->configdata[19] &= ~0x40; + break; + + case 0x03: /* 100BASE-TX */ + case 0x06: /* 100BASE-T4 */ + case 0x07: /* 100BASE-FX */ + ctlr->configdata[19] &= ~0x40; + bmcr |= 0x2000; + break; + + case 0x04: /* 10BASE-TFD */ + bmcr = (bmcr & ~0x2000)|0x0100; + ctlr->configdata[19] |= 0x40; + break; + + case 0x05: /* 100BASE-TXFD */ + case 0x08: /* 100BASE-FXFD */ + bmcr |= 0x2000|0x0100; + ctlr->configdata[19] |= 0x40; + break; + } + } + if(medium != -1) + miiw(ctlr, phyaddr, 0x00, bmcr); + } + + if(bmcr & 0x2000) + ether->mbps = 100; + + ctlr->configdata[8] = 1; + ctlr->configdata[15] &= ~0x80; + } + else{ + ctlr->configdata[8] = 0; + ctlr->configdata[15] |= 0x80; + } + + /* + * Workaround for some broken HUB chips when connected at 10Mb/s + * half-duplex. + * This is a band-aid, but as there's no dynamic auto-negotiation + * code at the moment, only deactivate the workaround code in txstart + * if the link is 100Mb/s. + */ + if(ether->mbps != 10) + ctlr->nop = 0; + + /* + * Load the chip configuration and start it off. + */ + if(ether->oq == 0) + ether->oq = qopen(256*1024, Qmsg, 0, 0); + configure(ether, 0); + command(ctlr, CUstart, PADDR(&ctlr->cbr->status)); + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to loading + * the station address with the Individual Address Setup command. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + for(i = 0; i < Eaddrlen/2; i++){ + x = ctlr->eeprom[i]; + ether->ea[2*i] = x; + ether->ea[2*i+1] = x>>8; + } + } + + ilock(&ctlr->cblock); + ctlr->action = CbIAS; + txstart(ether); + iunlock(&ctlr->cblock); + + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = ifstat; + ether->shutdown = shutdown; + + ether->promiscuous = promiscuous; + ether->multicast = multicast; + ether->arg = ether; + + return 0; +} + +void +ether82557link(void) +{ + addethercard("i82557", reset); +} diff --git a/os/pc/ether83815.c b/os/pc/ether83815.c new file mode 100644 index 00000000..2059bad4 --- /dev/null +++ b/os/pc/ether83815.c @@ -0,0 +1,994 @@ +/* + * National Semiconductor DP83815 + * + * Supports only internal PHY and has been tested on: + * Netgear FA311TX (using Netgear DS108 10/100 hub) + * To do: + * check Ethernet address; + * test autonegotiation on 10 Mbit, and 100 Mbit full duplex; + * external PHY via MII (should be common code for MII); + * thresholds; + * ring sizing; + * physical link changes/disconnect; + * push initialisation back to attach. + * + * C H Forsyth, forsyth@vitanuova.com, 18th June 2001. + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +#define DEBUG (0) +#define debug if(DEBUG)print + +enum { + Nrde = 64, + Ntde = 64, +}; + +#define Rbsz ROUNDUP(sizeof(Etherpkt)+4, 4) + +typedef struct Des { + ulong next; + int cmdsts; + ulong addr; + Block* bp; +} Des; + +enum { /* cmdsts */ + Own = 1<<31, /* set by data producer to hand to consumer */ + More = 1<<30, /* more of packet in next descriptor */ + Intr = 1<<29, /* interrupt when device is done with it */ + Supcrc = 1<<28, /* suppress crc on transmit */ + Inccrc = 1<<28, /* crc included on receive (always) */ + Ok = 1<<27, /* packet ok */ + Size = 0xFFF, /* packet size in bytes */ + + /* transmit */ + Txa = 1<<26, /* transmission aborted */ + Tfu = 1<<25, /* transmit fifo underrun */ + Crs = 1<<24, /* carrier sense lost */ + Td = 1<<23, /* transmission deferred */ + Ed = 1<<22, /* excessive deferral */ + Owc = 1<<21, /* out of window collision */ + Ec = 1<<20, /* excessive collisions */ + /* 19-16 collision count */ + + /* receive */ + Rxa = 1<<26, /* receive aborted (same as Rxo) */ + Rxo = 1<<25, /* receive overrun */ + Dest = 3<<23, /* destination class */ + Drej= 0<<23, /* packet was rejected */ + Duni= 1<<23, /* unicast */ + Dmulti= 2<<23, /* multicast */ + Dbroad= 3<<23, /* broadcast */ + Long = 1<<22, /* too long packet received */ + Runt = 1<<21, /* packet less than 64 bytes */ + Ise = 1<<20, /* invalid symbol */ + Crce = 1<<19, /* invalid crc */ + Fae = 1<<18, /* frame alignment error */ + Lbp = 1<<17, /* loopback packet */ + Col = 1<<16, /* collision during receive */ +}; + +enum { /* Variants */ + Nat83815 = (0x0020<<16)|0x100B, + Sis900 = (0x0630<<16)|0x1039, /* untested */ +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; /* (pcidev->did<<16)|pcidev->vid */ + + ushort srom[0xB+1]; + uchar sromea[Eaddrlen]; /* MAC address */ + + uchar fd; /* option or auto negotiation */ + + int mbps; + + Lock lock; + + Des* rdr; /* receive descriptor ring */ + int nrdr; /* size of rdr */ + int rdrx; /* index into rdr */ + + Lock tlock; + Des* tdr; /* transmit descriptor ring */ + int ntdr; /* size of tdr */ + int tdrh; /* host index into tdr */ + int tdri; /* interface index into tdr */ + int ntq; /* descriptors active */ + int ntqmax; + + ulong rxa; /* receive statistics */ + ulong rxo; + ulong rlong; + ulong runt; + ulong ise; + ulong crce; + ulong fae; + ulong lbp; + ulong col; + ulong rxsovr; + ulong rxorn; + + ulong txa; /* transmit statistics */ + ulong tfu; + ulong crs; + ulong td; + ulong ed; + ulong owc; + ulong ec; + ulong txurn; + + ulong dperr; /* system errors */ + ulong rmabt; + ulong rtabt; + ulong sserr; + ulong rxsover; +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +enum { + /* registers (could memory map) */ + Rcr= 0x00, /* command register */ + Rst= 1<<8, + Rxr= 1<<5, /* receiver reset */ + Txr= 1<<4, /* transmitter reset */ + Rxd= 1<<3, /* receiver disable */ + Rxe= 1<<2, /* receiver enable */ + Txd= 1<<1, /* transmitter disable */ + Txe= 1<<0, /* transmitter enable */ + Rcfg= 0x04, /* configuration */ + Lnksts= 1<<31, /* link good */ + Speed100= 1<<30, /* 100 Mb/s link */ + Fdup= 1<<29, /* full duplex */ + Pol= 1<<28, /* polarity reversal (10baseT) */ + Aneg_dn= 1<<27, /* autonegotiation done */ + Pint_acen= 1<<17, /* PHY interrupt auto clear enable */ + Pause_adv= 1<<16, /* advertise pause during auto neg */ + Paneg_ena= 1<<13, /* auto negotiation enable */ + Paneg_all= 7<<13, /* auto negotiation enable 10/100 half & full */ + Ext_phy= 1<<12, /* enable MII for external PHY */ + Phy_rst= 1<<10, /* reset internal PHY */ + Phy_dis= 1<<9, /* disable internal PHY (eg, low power) */ + Req_alg= 1<<7, /* PCI bus request: set means less aggressive */ + Sb= 1<<6, /* single slot back-off not random */ + Pow= 1<<5, /* out of window timer selection */ + Exd= 1<<4, /* disable excessive deferral timer */ + Pesel= 1<<3, /* parity error algorithm selection */ + Brom_dis= 1<<2, /* disable boot rom interface */ + Bem= 1<<0, /* big-endian mode */ + Rmear= 0x08, /* eeprom access */ + Mdc= 1<<6, /* MII mangement check */ + Mddir= 1<<5, /* MII management direction */ + Mdio= 1<<4, /* MII mangement data */ + Eesel= 1<<3, /* EEPROM chip select */ + Eeclk= 1<<2, /* EEPROM clock */ + Eedo= 1<<1, /* EEPROM data out (from chip) */ + Eedi= 1<<0, /* EEPROM data in (to chip) */ + Rptscr= 0x0C, /* pci test control */ + Risr= 0x10, /* interrupt status */ + Txrcmp= 1<<25, /* transmit reset complete */ + Rxrcmp= 1<<24, /* receiver reset complete */ + Dperr= 1<<23, /* detected parity error */ + Sserr= 1<<22, /* signalled system error */ + Rmabt= 1<<21, /* received master abort */ + Rtabt= 1<<20, /* received target abort */ + Rxsovr= 1<<16, /* RX status FIFO overrun */ + Hiberr= 1<<15, /* high bits error set (OR of 25-16) */ + Phy= 1<<14, /* PHY interrupt */ + Pme= 1<<13, /* power management event (wake online) */ + Swi= 1<<12, /* software interrupt */ + Mib= 1<<11, /* MIB service */ + Txurn= 1<<10, /* TX underrun */ + Txidle= 1<<9, /* TX idle */ + Txerr= 1<<8, /* TX packet error */ + Txdesc= 1<<7, /* TX descriptor (with Intr bit done) */ + Txok= 1<<6, /* TX ok */ + Rxorn= 1<<5, /* RX overrun */ + Rxidle= 1<<4, /* RX idle */ + Rxearly= 1<<3, /* RX early threshold */ + Rxerr= 1<<2, /* RX packet error */ + Rxdesc= 1<<1, /* RX descriptor (with Intr bit done) */ + Rxok= 1<<0, /* RX ok */ + Rimr= 0x14, /* interrupt mask */ + Rier= 0x18, /* interrupt enable */ + Ie= 1<<0, /* interrupt enable */ + Rtxdp= 0x20, /* transmit descriptor pointer */ + Rtxcfg= 0x24, /* transmit configuration */ + Csi= 1<<31, /* carrier sense ignore (needed for full duplex) */ + Hbi= 1<<30, /* heartbeat ignore (needed for full duplex) */ + Atp= 1<<28, /* automatic padding of runt packets */ + Mxdma= 7<<20, /* maximum dma transfer field */ + Mxdma32= 4<<20, /* 4x32-bit words (32 bytes) */ + Mxdma64= 5<<20, /* 8x32-bit words (64 bytes) */ + Flth= 0x3F<<8,/* Tx fill threshold, units of 32 bytes (must be > Mxdma) */ + Drth= 0x3F<<0,/* Tx drain threshold (units of 32 bytes) */ + Flth128= 4<<8, /* fill at 128 bytes */ + Drth512= 16<<0, /* drain at 512 bytes */ + Rrxdp= 0x30, /* receive descriptor pointer */ + Rrxcfg= 0x34, /* receive configuration */ + Atx= 1<<28, /* accept transmit packets (needed for full duplex) */ + Rdrth= 0x1F<<1,/* Rx drain threshold (units of 32 bytes) */ + Rdrth64= 2<<1, /* drain at 64 bytes */ + Rccsr= 0x3C, /* CLKRUN control/status */ + Pmests= 1<<15, /* PME status */ + Rwcsr= 0x40, /* wake on lan control/status */ + Rpcr= 0x44, /* pause control/status */ + Rrfcr= 0x48, /* receive filter/match control */ + Rfen= 1<<31, /* receive filter enable */ + Aab= 1<<30, /* accept all broadcast */ + Aam= 1<<29, /* accept all multicast */ + Aau= 1<<28, /* accept all unicast */ + Apm= 1<<27, /* accept on perfect match */ + Apat= 0xF<<23,/* accept on pattern match */ + Aarp= 1<<22, /* accept ARP */ + Mhen= 1<<21, /* multicast hash enable */ + Uhen= 1<<20, /* unicast hash enable */ + Ulm= 1<<19, /* U/L bit mask */ + /* bits 0-9 are rfaddr */ + Rrfdr= 0x4C, /* receive filter/match data */ + Rbrar= 0x50, /* boot rom address */ + Rbrdr= 0x54, /* boot rom data */ + Rsrr= 0x58, /* silicon revision */ + Rmibc= 0x5C, /* MIB control */ + /* 60-78 MIB data */ + + /* PHY registers */ + Rbmcr= 0x80, /* basic mode configuration */ + Reset= 1<<15, + Sel100= 1<<13, /* select 100Mb/sec if no auto neg */ + Anena= 1<<12, /* auto negotiation enable */ + Anrestart= 1<<9, /* restart auto negotiation */ + Selfdx= 1<<8, /* select full duplex if no auto neg */ + Rbmsr= 0x84, /* basic mode status */ + Ancomp= 1<<5, /* autonegotiation complete */ + Rphyidr1= 0x88, + Rphyidr2= 0x8C, + Ranar= 0x90, /* autonegotiation advertisement */ + Ranlpar= 0x94, /* autonegotiation link partner ability */ + Raner= 0x98, /* autonegotiation expansion */ + Rannptr= 0x9C, /* autonegotiation next page TX */ + Rphysts= 0xC0, /* PHY status */ + Rmicr= 0xC4, /* MII control */ + Inten= 1<<1, /* PHY interrupt enable */ + Rmisr= 0xC8, /* MII status */ + Rfcscr= 0xD0, /* false carrier sense counter */ + Rrecr= 0xD4, /* receive error counter */ + Rpcsr= 0xD8, /* 100Mb config/status */ + Rphycr= 0xE4, /* PHY control */ + Rtbscr= 0xE8, /* 10BaseT status/control */ +}; + +/* + * eeprom addresses + * 7 to 9 (16 bit words): mac address, shifted and reversed + */ + +#define csr32r(c, r) (inl((c)->port+(r))) +#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l))) +#define csr16r(c, r) (ins((c)->port+(r))) +#define csr16w(c, r, l) (outs((c)->port+(r), (ulong)(l))) + +static void +dumpcregs(Ctlr *ctlr) +{ + int i; + + for(i=0; i<=0x5C; i+=4) + print("%2.2ux %8.8lux\n", i, csr32r(ctlr, i)); +} + +static void +promiscuous(void* arg, int on) +{ + Ctlr *ctlr; + ulong w; + + ctlr = ((Ether*)arg)->ctlr; + ilock(&ctlr->lock); + w = csr32r(ctlr, Rrfcr); + if(on != ((w&Aau)!=0)){ + csr32w(ctlr, Rrfcr, w & ~Rfen); + csr32w(ctlr, Rrfcr, Rfen | (w ^ Aau)); + } + iunlock(&ctlr->lock); +} + +static void +attach(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->lock); + if(0) + dumpcregs(ctlr); + csr32w(ctlr, Rcr, Rxe); + iunlock(&ctlr->lock); +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + Ctlr *ctlr; + char *buf, *p; + int i, l, len; + + ctlr = ether->ctlr; + + ether->crcs = ctlr->crce; + ether->frames = ctlr->runt+ctlr->ise+ctlr->rlong+ctlr->fae; + ether->buffs = ctlr->rxorn+ctlr->tfu; + ether->overflows = ctlr->rxsovr; + + if(n == 0) + return 0; + + p = malloc(READSTR); + l = snprint(p, READSTR, "Rxa: %lud\n", ctlr->rxa); + l += snprint(p+l, READSTR-l, "Rxo: %lud\n", ctlr->rxo); + l += snprint(p+l, READSTR-l, "Rlong: %lud\n", ctlr->rlong); + l += snprint(p+l, READSTR-l, "Runt: %lud\n", ctlr->runt); + l += snprint(p+l, READSTR-l, "Ise: %lud\n", ctlr->ise); + l += snprint(p+l, READSTR-l, "Fae: %lud\n", ctlr->fae); + l += snprint(p+l, READSTR-l, "Lbp: %lud\n", ctlr->lbp); + l += snprint(p+l, READSTR-l, "Tfu: %lud\n", ctlr->tfu); + l += snprint(p+l, READSTR-l, "Txa: %lud\n", ctlr->txa); + l += snprint(p+l, READSTR-l, "CRC Error: %lud\n", ctlr->crce); + l += snprint(p+l, READSTR-l, "Collision Seen: %lud\n", ctlr->col); + l += snprint(p+l, READSTR-l, "Frame Too Long: %lud\n", ctlr->rlong); + l += snprint(p+l, READSTR-l, "Runt Frame: %lud\n", ctlr->runt); + l += snprint(p+l, READSTR-l, "Rx Underflow Error: %lud\n", ctlr->rxorn); + l += snprint(p+l, READSTR-l, "Tx Underrun: %lud\n", ctlr->txurn); + l += snprint(p+l, READSTR-l, "Excessive Collisions: %lud\n", ctlr->ec); + l += snprint(p+l, READSTR-l, "Late Collision: %lud\n", ctlr->owc); + l += snprint(p+l, READSTR-l, "Loss of Carrier: %lud\n", ctlr->crs); + l += snprint(p+l, READSTR-l, "Parity: %lud\n", ctlr->dperr); + l += snprint(p+l, READSTR-l, "Aborts: %lud\n", ctlr->rmabt+ctlr->rtabt); + l += snprint(p+l, READSTR-l, "RX Status overrun: %lud\n", ctlr->rxsover); + snprint(p+l, READSTR-l, "ntqmax: %d\n", ctlr->ntqmax); + ctlr->ntqmax = 0; + buf = a; + len = readstr(offset, buf, n, p); + if(offset > l) + offset -= l; + else + offset = 0; + buf += len; + n -= len; + + l = snprint(p, READSTR, "srom:"); + for(i = 0; i < nelem(ctlr->srom); i++){ + if(i && ((i & 0x0F) == 0)) + l += snprint(p+l, READSTR-l, "\n "); + l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->srom[i]); + } + + snprint(p+l, READSTR-l, "\n"); + len += readstr(offset, buf, n, p); + free(p); + + return len; +} + +static void +txstart(Ether* ether) +{ + Ctlr *ctlr; + Block *bp; + Des *des; + int started; + + ctlr = ether->ctlr; + started = 0; + while(ctlr->ntq < ctlr->ntdr-1){ + bp = qget(ether->oq); + if(bp == nil) + break; + des = &ctlr->tdr[ctlr->tdrh]; + des->bp = bp; + des->addr = PADDR(bp->rp); + ctlr->ntq++; + coherence(); + des->cmdsts = Own | BLEN(bp); + ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr); + started = 1; + } + if(started){ + coherence(); + csr32w(ctlr, Rcr, Txe); /* prompt */ + } + + if(ctlr->ntq > ctlr->ntqmax) + ctlr->ntqmax = ctlr->ntq; +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->tlock); + txstart(ether); + iunlock(&ctlr->tlock); +} + +static void +txrxcfg(Ctlr *ctlr, int txdrth) +{ + ulong rx, tx; + + rx = csr32r(ctlr, Rrxcfg); + tx = csr32r(ctlr, Rtxcfg); + if(ctlr->fd){ + rx |= Atx; + tx |= Csi | Hbi; + }else{ + rx &= ~Atx; + tx &= ~(Csi | Hbi); + } + tx &= ~(Mxdma|Drth|Flth); + tx |= Mxdma64 | Flth128 | txdrth; + csr32w(ctlr, Rtxcfg, tx); + rx &= ~(Mxdma|Rdrth); + rx |= Mxdma64 | Rdrth64; + csr32w(ctlr, Rrxcfg, rx); +} + +static void +interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *ether; + int len, status, cmdsts; + Des *des; + Block *bp; + + ether = arg; + ctlr = ether->ctlr; + + while((status = csr32r(ctlr, Risr)) != 0){ + + status &= ~(Pme|Mib); + + if(status & Hiberr){ + if(status & Rxsovr) + ctlr->rxsover++; + if(status & Sserr) + ctlr->sserr++; + if(status & Dperr) + ctlr->dperr++; + if(status & Rmabt) + ctlr->rmabt++; + if(status & Rtabt) + ctlr->rtabt++; + status &= ~(Hiberr|Txrcmp|Rxrcmp|Rxsovr|Dperr|Sserr|Rmabt|Rtabt); + } + + /* + * Received packets. + */ + if(status & (Rxdesc|Rxok|Rxerr|Rxearly|Rxorn)){ + des = &ctlr->rdr[ctlr->rdrx]; + while((cmdsts = des->cmdsts) & Own){ + if((cmdsts&Ok) == 0){ + if(cmdsts & Rxa) + ctlr->rxa++; + if(cmdsts & Rxo) + ctlr->rxo++; + if(cmdsts & Long) + ctlr->rlong++; + if(cmdsts & Runt) + ctlr->runt++; + if(cmdsts & Ise) + ctlr->ise++; + if(cmdsts & Crce) + ctlr->crce++; + if(cmdsts & Fae) + ctlr->fae++; + if(cmdsts & Lbp) + ctlr->lbp++; + if(cmdsts & Col) + ctlr->col++; + } + else if(bp = iallocb(Rbsz)){ + len = (cmdsts&Size)-4; + des->bp->wp = des->bp->rp+len; + etheriq(ether, des->bp, 1); + des->bp = bp; + des->addr = PADDR(bp->rp); + coherence(); + } + + des->cmdsts = Rbsz; + coherence(); + + ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr); + des = &ctlr->rdr[ctlr->rdrx]; + } + status &= ~(Rxdesc|Rxok|Rxerr|Rxearly|Rxorn); + } + + /* + * Check the transmit side: + * check for Transmit Underflow and Adjust + * the threshold upwards; + * free any transmitted buffers and try to + * top-up the ring. + */ + if(status & Txurn){ + ctlr->txurn++; + ilock(&ctlr->lock); + /* change threshold */ + iunlock(&ctlr->lock); + status &= ~(Txurn); + } + + ilock(&ctlr->tlock); + while(ctlr->ntq){ + des = &ctlr->tdr[ctlr->tdri]; + cmdsts = des->cmdsts; + if(cmdsts & Own) + break; + + if((cmdsts & Ok) == 0){ + if(cmdsts & Txa) + ctlr->txa++; + if(cmdsts & Tfu) + ctlr->tfu++; + if(cmdsts & Td) + ctlr->td++; + if(cmdsts & Ed) + ctlr->ed++; + if(cmdsts & Owc) + ctlr->owc++; + if(cmdsts & Ec) + ctlr->ec++; + ether->oerrs++; + } + + freeb(des->bp); + des->bp = nil; + des->cmdsts = 0; + + ctlr->ntq--; + ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr); + } + txstart(ether); + iunlock(&ctlr->tlock); + + status &= ~(Txurn|Txidle|Txerr|Txdesc|Txok); + + /* + * Anything left not catered for? + */ + if(status) + print("#l%d: status %8.8uX\n", ether->ctlrno, status); + } +} + +static void +ctlrinit(Ether* ether) +{ + Ctlr *ctlr; + Des *des, *last; + + ctlr = ether->ctlr; + + /* + * Allocate suitable aligned descriptors + * for the transmit and receive rings; + * initialise the receive ring; + * initialise the transmit ring; + * unmask interrupts and start the transmit side. + */ + des = xspanalloc((ctlr->nrdr+ctlr->ntdr)*sizeof(Des), 32, 0); + ctlr->tdr = des; + ctlr->rdr = des+ctlr->ntdr; + + last = nil; + for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){ + des->bp = iallocb(Rbsz); + if (des->bp == nil) + error(Enomem); + des->cmdsts = Rbsz; + des->addr = PADDR(des->bp->rp); + if(last != nil) + last->next = PADDR(des); + last = des; + } + ctlr->rdr[ctlr->nrdr-1].next = PADDR(ctlr->rdr); + ctlr->rdrx = 0; + csr32w(ctlr, Rrxdp, PADDR(ctlr->rdr)); + + last = nil; + for(des = ctlr->tdr; des < &ctlr->tdr[ctlr->ntdr]; des++){ + des->cmdsts = 0; + des->bp = nil; + des->addr = ~0; + if(last != nil) + last->next = PADDR(des); + last = des; + } + ctlr->tdr[ctlr->ntdr-1].next = PADDR(ctlr->tdr); + ctlr->tdrh = 0; + ctlr->tdri = 0; + csr32w(ctlr, Rtxdp, PADDR(ctlr->tdr)); + + txrxcfg(ctlr, Drth512); + + csr32w(ctlr, Rimr, Dperr|Sserr|Rmabt|Rtabt|Rxsovr|Hiberr|Txurn|Txerr|Txdesc|Txok|Rxorn|Rxerr|Rxdesc|Rxok); /* Phy|Pme|Mib */ + csr32r(ctlr, Risr); /* clear status */ + csr32w(ctlr, Rier, Ie); +} + +static void +eeclk(Ctlr *ctlr, int clk) +{ + csr32w(ctlr, Rmear, Eesel | clk); + microdelay(2); +} + +static void +eeidle(Ctlr *ctlr) +{ + int i; + + eeclk(ctlr, 0); + eeclk(ctlr, Eeclk); + for(i=0; i<25; i++){ + eeclk(ctlr, 0); + eeclk(ctlr, Eeclk); + } + eeclk(ctlr, 0); + csr32w(ctlr, Rmear, 0); + microdelay(2); +} + +static int +eegetw(Ctlr *ctlr, int a) +{ + int d, i, w, v; + + eeidle(ctlr); + eeclk(ctlr, 0); + eeclk(ctlr, Eeclk); + d = 0x180 | a; + for(i=0x400; i; i>>=1){ + v = (d & i) ? Eedi : 0; + eeclk(ctlr, v); + eeclk(ctlr, Eeclk|v); + } + eeclk(ctlr, 0); + + w = 0; + for(i=0x8000; i; i >>= 1){ + eeclk(ctlr, Eeclk); + if(csr32r(ctlr, Rmear) & Eedo) + w |= i; + microdelay(2); + eeclk(ctlr, 0); + } + eeidle(ctlr); + return w; +} + +static void +resetctlr(Ctlr *ctlr) +{ + int i; + + csr32w(ctlr, Rcr, Rst); + for(i=0;; i++){ + if(i > 100) + panic("ns83815: soft reset did not complete"); + microdelay(250); + if((csr32r(ctlr, Rcr) & Rst) == 0) + break; + delay(1); + } +} + +static void +shutdown(Ether* ether) +{ + Ctlr *ctlr = ether->ctlr; + +print("ether83815 shutting down\n"); + csr32w(ctlr, Rcr, Rxd|Txd); /* disable transceiver */ + resetctlr(ctlr); +} + +static void +softreset(Ctlr* ctlr, int resetphys) +{ + int i, w; + + /* + * Soft-reset the controller + */ + resetctlr(ctlr); + csr32w(ctlr, Rccsr, Pmests); + csr32w(ctlr, Rccsr, 0); + csr32w(ctlr, Rcfg, csr32r(ctlr, Rcfg) | Pint_acen); + + if(resetphys){ + /* + * Soft-reset the PHY + */ + csr32w(ctlr, Rbmcr, Reset); + for(i=0;; i++){ + if(i > 100) + panic("ns83815: PHY soft reset time out"); + if((csr32r(ctlr, Rbmcr) & Reset) == 0) + break; + delay(1); + } + } + + /* + * Initialisation values, in sequence (see 4.4 Recommended Registers Configuration) + */ + csr16w(ctlr, 0xCC, 0x0001); /* PGSEL */ + csr16w(ctlr, 0xE4, 0x189C); /* PMCCSR */ + csr16w(ctlr, 0xFC, 0x0000); /* TSTDAT */ + csr16w(ctlr, 0xF4, 0x5040); /* DSPCFG */ + csr16w(ctlr, 0xF8, 0x008C); /* SDCFG */ + + /* + * Auto negotiate + */ + w = csr16r(ctlr, Rbmsr); /* clear latched bits */ + debug("anar: %4.4ux\n", csr16r(ctlr, Ranar)); + csr16w(ctlr, Rbmcr, Anena); + if(csr16r(ctlr, Ranar) == 0 || (csr32r(ctlr, Rcfg) & Aneg_dn) == 0){ + csr16w(ctlr, Rbmcr, Anena|Anrestart); + for(i=0;; i++){ + if(i > 6000){ + print("ns83815: auto neg timed out\n"); + break; + } + if((w = csr16r(ctlr, Rbmsr)) & Ancomp) + break; + delay(1); + } + debug("%d ms\n", i); + w &= 0xFFFF; + debug("bmsr: %4.4ux\n", w); + } + USED(w); + debug("anar: %4.4ux\n", csr16r(ctlr, Ranar)); + debug("anlpar: %4.4ux\n", csr16r(ctlr, Ranlpar)); + debug("aner: %4.4ux\n", csr16r(ctlr, Raner)); + debug("physts: %4.4ux\n", csr16r(ctlr, Rphysts)); + debug("tbscr: %4.4ux\n", csr16r(ctlr, Rtbscr)); +} + +static int +media(Ether* ether) +{ + Ctlr* ctlr; + ulong cfg; + + ctlr = ether->ctlr; + cfg = csr32r(ctlr, Rcfg); + ctlr->fd = (cfg & Fdup) != 0; + if(cfg & Speed100) + return 100; + if((cfg & Lnksts) == 0) + return 100; /* no link: use 100 to ensure larger queues */ + return 10; +} + +static char* mediatable[9] = { + "10BASE-T", /* TP */ + "10BASE-2", /* BNC */ + "10BASE-5", /* AUI */ + "100BASE-TX", + "10BASE-TFD", + "100BASE-TXFD", + "100BASE-T4", + "100BASE-FX", + "100BASE-FXFD", +}; + +static void +srom(Ctlr* ctlr) +{ + int i, j; + + for(i = 0; i < nelem(ctlr->srom); i++) + ctlr->srom[i] = eegetw(ctlr, i); + + /* + * the MAC address is reversed, straddling word boundaries + */ + memset(ctlr->sromea, 0, sizeof(ctlr->sromea)); + j = 6*16 + 15; + for(i=0; i<48; i++){ + ctlr->sromea[i>>3] |= ((ctlr->srom[j>>4] >> (15-(j&0xF))) & 1) << (i&7); + j++; + } +} + +static void +scanpci83815(void) +{ + Ctlr *ctlr; + Pcidev *p; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + switch((p->did<<16)|p->vid){ + default: + continue; + + case Nat83815: + case Sis900: + break; + } + + /* + * bar[0] is the I/O port register address and + * bar[1] is the memory-mapped register address. + */ + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = p->mem[0].bar & ~0x01; + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + + if(ioalloc(ctlr->port, p->mem[0].size, 0, "ns83815") < 0){ + print("ns83815: port 0x%uX in use\n", ctlr->port); + free(ctlr); + continue; + } + + softreset(ctlr, 0); + srom(ctlr); + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + +/* multicast already on, don't need to do anything */ +static void +multicast(void*, uchar*, int) +{ +} + +static int +reset(Ether* ether) +{ + Ctlr *ctlr; + int i, x; + uchar ea[Eaddrlen]; + static int scandone; + + if(scandone == 0){ + scanpci83815(); + scandone = 1; + } + + /* + * Any adapter matches if no ether->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(ether->port == 0 || ether->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + ether->ctlr = ctlr; + ether->port = ctlr->port; + ether->irq = ctlr->pcidev->intl; + ether->tbdf = ctlr->pcidev->tbdf; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to + * loading the station address in the hardware. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0) + memmove(ether->ea, ctlr->sromea, Eaddrlen); + for(i=0; iea[i] | (ether->ea[i+1]<<8); + csr32w(ctlr, Rrfcr, i); + csr32w(ctlr, Rrfdr, x); + } + csr32w(ctlr, Rrfcr, Rfen|Apm|Aab|Aam); + + ether->mbps = media(ether); + + /* + * Look for a medium override in case there's no autonegotiation + * the autonegotiation fails. + */ + + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "FD") == 0){ + ctlr->fd = 1; + continue; + } + for(x = 0; x < nelem(mediatable); x++){ + debug("compare <%s> <%s>\n", mediatable[x], + ether->opt[i]); + if(cistrcmp(mediatable[x], ether->opt[i]) == 0){ + if(x != 4 && x >= 3) + ether->mbps = 100; + else + ether->mbps = 10; + switch(x){ + default: + ctlr->fd = 0; + break; + + case 0x04: /* 10BASE-TFD */ + case 0x05: /* 100BASE-TXFD */ + case 0x08: /* 100BASE-FXFD */ + ctlr->fd = 1; + break; + } + break; + } + } + } + + /* + * Initialise descriptor rings, ethernet address. + */ + ctlr->nrdr = Nrde; + ctlr->ntdr = Ntde; + pcisetbme(ctlr->pcidev); + ctlrinit(ether); + + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = ifstat; + + ether->arg = ether; + ether->promiscuous = promiscuous; + ether->multicast = multicast; + ether->shutdown = shutdown; + return 0; +} + +void +ether83815link(void) +{ + addethercard("83815", reset); +} diff --git a/os/pc/ether8390.c b/os/pc/ether8390.c new file mode 100644 index 00000000..42bde3cf --- /dev/null +++ b/os/pc/ether8390.c @@ -0,0 +1,812 @@ +/* + * National Semiconductor DP8390 and clone + * Network Interface Controller. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" +#include "ether8390.h" + +enum { /* NIC core registers */ + Cr = 0x00, /* command register, all pages */ + + /* Page 0, read */ + Clda0 = 0x01, /* current local DMA address 0 */ + Clda1 = 0x02, /* current local DMA address 1 */ + Bnry = 0x03, /* boundary pointer (R/W) */ + Tsr = 0x04, /* transmit status register */ + Ncr = 0x05, /* number of collisions register */ + Fifo = 0x06, /* FIFO */ + Isr = 0x07, /* interrupt status register (R/W) */ + Crda0 = 0x08, /* current remote DMA address 0 */ + Crda1 = 0x09, /* current remote DMA address 1 */ + Rsr = 0x0C, /* receive status register */ + Ref0 = 0x0D, /* frame alignment errors */ + Ref1 = 0x0E, /* CRC errors */ + Ref2 = 0x0F, /* missed packet errors */ + + /* Page 0, write */ + Pstart = 0x01, /* page start register */ + Pstop = 0x02, /* page stop register */ + Tpsr = 0x04, /* transmit page start address */ + Tbcr0 = 0x05, /* transmit byte count register 0 */ + Tbcr1 = 0x06, /* transmit byte count register 1 */ + Rsar0 = 0x08, /* remote start address register 0 */ + Rsar1 = 0x09, /* remote start address register 1 */ + Rbcr0 = 0x0A, /* remote byte count register 0 */ + Rbcr1 = 0x0B, /* remote byte count register 1 */ + Rcr = 0x0C, /* receive configuration register */ + Tcr = 0x0D, /* transmit configuration register */ + Dcr = 0x0E, /* data configuration register */ + Imr = 0x0F, /* interrupt mask */ + + /* Page 1, read/write */ + Par0 = 0x01, /* physical address register 0 */ + Curr = 0x07, /* current page register */ + Mar0 = 0x08, /* multicast address register 0 */ +}; + +enum { /* Cr */ + Stp = 0x01, /* stop */ + Sta = 0x02, /* start */ + Txp = 0x04, /* transmit packet */ + Rd0 = 0x08, /* remote DMA command */ + Rd1 = 0x10, + Rd2 = 0x20, + RdREAD = Rd0, /* remote read */ + RdWRITE = Rd1, /* remote write */ + RdSEND = Rd1|Rd0, /* send packet */ + RdABORT = Rd2, /* abort/complete remote DMA */ + Ps0 = 0x40, /* page select */ + Ps1 = 0x80, + Page0 = 0x00, + Page1 = Ps0, + Page2 = Ps1, +}; + +enum { /* Isr/Imr */ + Prx = 0x01, /* packet received */ + Ptx = 0x02, /* packet transmitted */ + Rxe = 0x04, /* receive error */ + Txe = 0x08, /* transmit error */ + Ovw = 0x10, /* overwrite warning */ + Cnt = 0x20, /* counter overflow */ + Rdc = 0x40, /* remote DMA complete */ + Rst = 0x80, /* reset status */ +}; + +enum { /* Dcr */ + Wts = 0x01, /* word transfer select */ + Bos = 0x02, /* byte order select */ + Las = 0x04, /* long address select */ + Ls = 0x08, /* loopback select */ + Arm = 0x10, /* auto-initialise remote */ + Ft0 = 0x20, /* FIFO threshold select */ + Ft1 = 0x40, + Ft1WORD = 0x00, + Ft2WORD = Ft0, + Ft4WORD = Ft1, + Ft6WORD = Ft1|Ft0, +}; + +enum { /* Tcr */ + Crc = 0x01, /* inhibit CRC */ + Lb0 = 0x02, /* encoded loopback control */ + Lb1 = 0x04, + LpbkNORMAL = 0x00, /* normal operation */ + LpbkNIC = Lb0, /* internal NIC module loopback */ + LpbkENDEC = Lb1, /* internal ENDEC module loopback */ + LpbkEXTERNAL = Lb1|Lb0, /* external loopback */ + Atd = 0x08, /* auto transmit disable */ + Ofst = 0x10, /* collision offset enable */ +}; + +enum { /* Tsr */ + Ptxok = 0x01, /* packet transmitted */ + Col = 0x04, /* transmit collided */ + Abt = 0x08, /* tranmit aborted */ + Crs = 0x10, /* carrier sense lost */ + Fu = 0x20, /* FIFO underrun */ + Cdh = 0x40, /* CD heartbeat */ + Owc = 0x80, /* out of window collision */ +}; + +enum { /* Rcr */ + Sep = 0x01, /* save errored packets */ + Ar = 0x02, /* accept runt packets */ + Ab = 0x04, /* accept broadcast */ + Am = 0x08, /* accept multicast */ + Pro = 0x10, /* promiscuous physical */ + Mon = 0x20, /* monitor mode */ +}; + +enum { /* Rsr */ + Prxok = 0x01, /* packet received intact */ + Crce = 0x02, /* CRC error */ + Fae = 0x04, /* frame alignment error */ + Fo = 0x08, /* FIFO overrun */ + Mpa = 0x10, /* missed packet */ + Phy = 0x20, /* physical/multicast address */ + Dis = 0x40, /* receiver disabled */ + Dfr = 0x80, /* deferring */ +}; + +typedef struct Hdr Hdr; +struct Hdr { + uchar status; + uchar next; + uchar len0; + uchar len1; +}; + +void +dp8390getea(Ether* ether, uchar* ea) +{ + Dp8390 *ctlr; + uchar cr; + int i; + + ctlr = ether->ctlr; + + /* + * Get the ethernet address from the chip. + * Take care to restore the command register + * afterwards. + */ + ilock(ctlr); + cr = regr(ctlr, Cr) & ~Txp; + regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr)); + for(i = 0; i < Eaddrlen; i++) + ea[i] = regr(ctlr, Par0+i); + regw(ctlr, Cr, cr); + iunlock(ctlr); +} + +void +dp8390setea(Ether* ether) +{ + int i; + uchar cr; + Dp8390 *ctlr; + + ctlr = ether->ctlr; + + /* + * Set the ethernet address into the chip. + * Take care to restore the command register + * afterwards. Don't care about multicast + * addresses as multicast is never enabled + * (currently). + */ + ilock(ctlr); + cr = regr(ctlr, Cr) & ~Txp; + regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr)); + for(i = 0; i < Eaddrlen; i++) + regw(ctlr, Par0+i, ether->ea[i]); + regw(ctlr, Cr, cr); + iunlock(ctlr); +} + +static void* +_dp8390read(Dp8390* ctlr, void* to, ulong from, ulong len) +{ + uchar cr; + int timo; + + /* + * Read some data at offset 'from' in the card's memory + * using the DP8390 remote DMA facility, and place it at + * 'to' in main memory, via the I/O data port. + */ + cr = regr(ctlr, Cr) & ~Txp; + regw(ctlr, Cr, Page0|RdABORT|Sta); + regw(ctlr, Isr, Rdc); + + /* + * Set up the remote DMA address and count. + */ + len = ROUNDUP(len, ctlr->width); + regw(ctlr, Rbcr0, len & 0xFF); + regw(ctlr, Rbcr1, (len>>8) & 0xFF); + regw(ctlr, Rsar0, from & 0xFF); + regw(ctlr, Rsar1, (from>>8) & 0xFF); + + /* + * Start the remote DMA read and suck the data + * out of the I/O port. + */ + regw(ctlr, Cr, Page0|RdREAD|Sta); + rdread(ctlr, to, len); + + /* + * Wait for the remote DMA to complete. The timeout + * is necessary because this routine may be called on + * a non-existent chip during initialisation and, due + * to the miracles of the bus, it's possible to get this + * far and still be talking to a slot full of nothing. + */ + for(timo = 10000; (regr(ctlr, Isr) & Rdc) == 0 && timo; timo--) + ; + + regw(ctlr, Isr, Rdc); + regw(ctlr, Cr, cr); + + return to; +} + +void* +dp8390read(Dp8390* ctlr, void* to, ulong from, ulong len) +{ + void *v; + + ilock(ctlr); + v = _dp8390read(ctlr, to, from, len); + iunlock(ctlr); + + return v; +} + +static void* +dp8390write(Dp8390* ctlr, ulong to, void* from, ulong len) +{ + ulong crda; + uchar cr; + int timo, width; + +top: + /* + * Write some data to offset 'to' in the card's memory + * using the DP8390 remote DMA facility, reading it at + * 'from' in main memory, via the I/O data port. + */ + cr = regr(ctlr, Cr) & ~Txp; + regw(ctlr, Cr, Page0|RdABORT|Sta); + regw(ctlr, Isr, Rdc); + + len = ROUNDUP(len, ctlr->width); + + /* + * Set up the remote DMA address and count. + * This is straight from the DP8390[12D] datasheet, + * hence the initial set up for read. + * Assumption here that the A7000 EtherV card will + * never need a dummyrr. + */ + if(ctlr->dummyrr && (ctlr->width == 1 || ctlr->width == 2)){ + if(ctlr->width == 2) + width = 1; + else + width = 0; + crda = to-1-width; + regw(ctlr, Rbcr0, (len+1+width) & 0xFF); + regw(ctlr, Rbcr1, ((len+1+width)>>8) & 0xFF); + regw(ctlr, Rsar0, crda & 0xFF); + regw(ctlr, Rsar1, (crda>>8) & 0xFF); + regw(ctlr, Cr, Page0|RdREAD|Sta); + + for(timo=0;; timo++){ + if(timo > 10000){ + print("ether8390: dummyrr timeout; assuming nodummyrr\n"); + ctlr->dummyrr = 0; + goto top; + } + crda = regr(ctlr, Crda0); + crda |= regr(ctlr, Crda1)<<8; + if(crda == to){ + /* + * Start the remote DMA write and make sure + * the registers are correct. + */ + regw(ctlr, Cr, Page0|RdWRITE|Sta); + + crda = regr(ctlr, Crda0); + crda |= regr(ctlr, Crda1)<<8; + if(crda != to) + panic("crda write %lud to %lud\n", crda, to); + + break; + } + } + } + else{ + regw(ctlr, Rsar0, to & 0xFF); + regw(ctlr, Rsar1, (to>>8) & 0xFF); + regw(ctlr, Rbcr0, len & 0xFF); + regw(ctlr, Rbcr1, (len>>8) & 0xFF); + regw(ctlr, Cr, Page0|RdWRITE|Sta); + } + + /* + * Pump the data into the I/O port + * then wait for the remote DMA to finish. + */ + rdwrite(ctlr, from, len); + for(timo = 10000; (regr(ctlr, Isr) & Rdc) == 0 && timo; timo--) + ; + + regw(ctlr, Isr, Rdc); + regw(ctlr, Cr, cr); + + return (void*)to; +} + +static void +ringinit(Dp8390* ctlr) +{ + regw(ctlr, Pstart, ctlr->pstart); + regw(ctlr, Pstop, ctlr->pstop); + regw(ctlr, Bnry, ctlr->pstop-1); + + regw(ctlr, Cr, Page1|RdABORT|Stp); + regw(ctlr, Curr, ctlr->pstart); + regw(ctlr, Cr, Page0|RdABORT|Stp); + + ctlr->nxtpkt = ctlr->pstart; +} + +static uchar +getcurr(Dp8390* ctlr) +{ + uchar cr, curr; + + cr = regr(ctlr, Cr) & ~Txp; + regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr)); + curr = regr(ctlr, Curr); + regw(ctlr, Cr, cr); + + return curr; +} + +static void +receive(Ether* ether) +{ + Dp8390 *ctlr; + uchar curr, *p; + Hdr hdr; + ulong count, data, len; + Block *bp; + + ctlr = ether->ctlr; + for(curr = getcurr(ctlr); ctlr->nxtpkt != curr; curr = getcurr(ctlr)){ + data = ctlr->nxtpkt*Dp8390BufSz; + if(ctlr->ram) + memmove(&hdr, (void*)(ether->mem+data), sizeof(Hdr)); + else + _dp8390read(ctlr, &hdr, data, sizeof(Hdr)); + + /* + * Don't believe the upper byte count, work it + * out from the software next-page pointer and + * the current next-page pointer. + */ + if(hdr.next > ctlr->nxtpkt) + len = hdr.next - ctlr->nxtpkt - 1; + else + len = (ctlr->pstop-ctlr->nxtpkt) + (hdr.next-ctlr->pstart) - 1; + if(hdr.len0 > (Dp8390BufSz-sizeof(Hdr))) + len--; + + len = ((len<<8)|hdr.len0)-4; + + /* + * Chip is badly scrogged, reinitialise the ring. + */ + if(hdr.next < ctlr->pstart || hdr.next >= ctlr->pstop + || len < 60 || len > sizeof(Etherpkt)){ + print("dp8390: H#%2.2ux#%2.2ux#%2.2ux#%2.2ux,%lud\n", + hdr.status, hdr.next, hdr.len0, hdr.len1, len); + regw(ctlr, Cr, Page0|RdABORT|Stp); + ringinit(ctlr); + regw(ctlr, Cr, Page0|RdABORT|Sta); + + return; + } + + /* + * If it's a good packet read it in to the software buffer. + * If the packet wraps round the hardware ring, read it in + * two pieces. + */ + if((hdr.status & (Fo|Fae|Crce|Prxok)) == Prxok && (bp = iallocb(len))){ + p = bp->rp; + bp->wp = p+len; + data += sizeof(Hdr); + + if((data+len) >= ctlr->pstop*Dp8390BufSz){ + count = ctlr->pstop*Dp8390BufSz - data; + if(ctlr->ram) + memmove(p, (void*)(ether->mem+data), count); + else + _dp8390read(ctlr, p, data, count); + p += count; + data = ctlr->pstart*Dp8390BufSz; + len -= count; + } + if(len){ + if(ctlr->ram) + memmove(p, (void*)(ether->mem+data), len); + else + _dp8390read(ctlr, p, data, len); + } + + /* + * Copy the packet to whoever wants it. + */ + etheriq(ether, bp, 1); + } + + /* + * Finished with this packet, update the + * hardware and software ring pointers. + */ + ctlr->nxtpkt = hdr.next; + + hdr.next--; + if(hdr.next < ctlr->pstart) + hdr.next = ctlr->pstop-1; + regw(ctlr, Bnry, hdr.next); + } +} + +static void +txstart(Ether* ether) +{ + int len; + Dp8390 *ctlr; + Block *bp; + uchar minpkt[ETHERMINTU], *rp; + + ctlr = ether->ctlr; + + /* + * This routine is called both from the top level and from interrupt + * level and expects to be called with ctlr already locked. + */ + if(ctlr->txbusy) + return; + bp = qget(ether->oq); + if(bp == nil) + return; + + /* + * Make sure the packet is of minimum length; + * copy it to the card's memory by the appropriate means; + * start the transmission. + */ + len = BLEN(bp); + rp = bp->rp; + if(len < ETHERMINTU){ + rp = minpkt; + memmove(rp, bp->rp, len); + memset(rp+len, 0, ETHERMINTU-len); + len = ETHERMINTU; + } + + if(ctlr->ram) + memmove((void*)(ether->mem+ctlr->tstart*Dp8390BufSz), rp, len); + else + dp8390write(ctlr, ctlr->tstart*Dp8390BufSz, rp, len); + freeb(bp); + + regw(ctlr, Tbcr0, len & 0xFF); + regw(ctlr, Tbcr1, (len>>8) & 0xFF); + regw(ctlr, Cr, Page0|RdABORT|Txp|Sta); + + ether->outpackets++; + ctlr->txbusy = 1; +} + +static void +transmit(Ether* ether) +{ + Dp8390 *ctlr; + + ctlr = ether->ctlr; + + ilock(ctlr); + txstart(ether); + iunlock(ctlr); +} + +static void +overflow(Ether *ether) +{ + Dp8390 *ctlr; + uchar txp; + int resend; + + ctlr = ether->ctlr; + + /* + * The following procedure is taken from the DP8390[12D] datasheet, + * it seems pretty adamant that this is what has to be done. + */ + txp = regr(ctlr, Cr) & Txp; + regw(ctlr, Cr, Page0|RdABORT|Stp); + delay(2); + regw(ctlr, Rbcr0, 0); + regw(ctlr, Rbcr1, 0); + + resend = 0; + if(txp && (regr(ctlr, Isr) & (Txe|Ptx)) == 0) + resend = 1; + + regw(ctlr, Tcr, LpbkNIC); + regw(ctlr, Cr, Page0|RdABORT|Sta); + receive(ether); + regw(ctlr, Isr, Ovw); + regw(ctlr, Tcr, LpbkNORMAL); + + if(resend) + regw(ctlr, Cr, Page0|RdABORT|Txp|Sta); +} + +static void +interrupt(Ureg*, void* arg) +{ + Ether *ether; + Dp8390 *ctlr; + uchar isr, r; + + ether = arg; + ctlr = ether->ctlr; + + /* + * While there is something of interest, + * clear all the interrupts and process. + */ + ilock(ctlr); + regw(ctlr, Imr, 0x00); + while(isr = (regr(ctlr, Isr) & (Cnt|Ovw|Txe|Rxe|Ptx|Prx))){ + if(isr & Ovw){ + overflow(ether); + regw(ctlr, Isr, Ovw); + ether->overflows++; + } + + /* + * Packets have been received. + * Take a spin round the ring. + */ + if(isr & (Rxe|Prx)){ + receive(ether); + regw(ctlr, Isr, Rxe|Prx); + } + + /* + * A packet completed transmission, successfully or + * not. Start transmission on the next buffered packet, + * and wake the output routine. + */ + if(isr & (Txe|Ptx)){ + r = regr(ctlr, Tsr); + if((isr & Txe) && (r & (Cdh|Fu|Crs|Abt))){ + print("dp8390: Tsr#%2.2ux|", r); + ether->oerrs++; + } + + regw(ctlr, Isr, Txe|Ptx); + + if(isr & Ptx) + ether->outpackets++; + ctlr->txbusy = 0; + txstart(ether); + } + + if(isr & Cnt){ + ether->frames += regr(ctlr, Ref0); + ether->crcs += regr(ctlr, Ref1); + ether->buffs += regr(ctlr, Ref2); + regw(ctlr, Isr, Cnt); + } + } + regw(ctlr, Imr, Cnt|Ovw|Txe|Rxe|Ptx|Prx); + iunlock(ctlr); +} + +static uchar allmar[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +static void +setfilter(Ether *ether, Dp8390 *ctlr) +{ + uchar r, cr; + int i; + uchar *mar; + + r = Ab; + mar = 0; + if(ether->prom){ + r |= Pro|Am; + mar = allmar; + } else if(ether->nmaddr){ + r |= Am; + mar = ctlr->mar; + } + if(mar){ + cr = regr(ctlr, Cr) & ~Txp; + regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr)); + for(i = 0; i < 8; i++) + regw(ctlr, Mar0+i, *(mar++)); + regw(ctlr, Cr, cr); + } + regw(ctlr, Rcr, r); +} + +static void +promiscuous(void *arg, int ) +{ + Ether *ether; + Dp8390 *ctlr; + + ether = arg; + ctlr = ether->ctlr; + + ilock(ctlr); + setfilter(ether, ctlr); + iunlock(ctlr); +} + +static void +setbit(Dp8390 *ctlr, int bit, int on) +{ + int i, h; + + i = bit/8; + h = bit%8; + if(on){ + if(++(ctlr->mref[bit]) == 1) + ctlr->mar[i] |= 1<mref[bit]) <= 0){ + ctlr->mref[bit] = 0; + ctlr->mar[i] &= ~(1<ctlr; + if(reverse[1] == 0){ + for(i = 0; i < 64; i++) + reverse[i] = ((i&1)<<5) | ((i&2)<<3) | ((i&4)<<1) + | ((i&8)>>1) | ((i&16)>>3) | ((i&32)>>5); + } + + /* + * change filter bits + */ + h = ethercrc(addr, 6); + ilock(ctlr); + setbit(ctlr, reverse[h&0x3f], on); + setfilter(ether, ctlr); + iunlock(ctlr); +} + +static void +attach(Ether* ether) +{ + Dp8390 *ctlr; + uchar r; + + ctlr = ether->ctlr; + + /* + * Enable the chip for transmit/receive. + * The init routine leaves the chip in monitor + * mode. Clear the missed-packet counter, it + * increments while in monitor mode. + * Sometimes there's an interrupt pending at this + * point but there's nothing in the Isr, so + * any pending interrupts are cleared and the + * mask of acceptable interrupts is enabled here. + */ + r = Ab; + if(ether->prom) + r |= Pro; + if(ether->nmaddr) + r |= Am; + ilock(ctlr); + regw(ctlr, Isr, 0xFF); + regw(ctlr, Imr, Cnt|Ovw|Txe|Rxe|Ptx|Prx); + regw(ctlr, Rcr, r); + r = regr(ctlr, Ref2); + regw(ctlr, Tcr, LpbkNORMAL); + iunlock(ctlr); + USED(r); +} + +static void +disable(Dp8390* ctlr) +{ + int timo; + + /* + * Stop the chip. Set the Stp bit and wait for the chip + * to finish whatever was on its tiny mind before it sets + * the Rst bit. + * The timeout is needed because there may not be a real + * chip there if this is called when probing for a device + * at boot. + */ + regw(ctlr, Cr, Page0|RdABORT|Stp); + regw(ctlr, Rbcr0, 0); + regw(ctlr, Rbcr1, 0); + for(timo = 10000; (regr(ctlr, Isr) & Rst) == 0 && timo; timo--) + ; +} + +int +dp8390reset(Ether* ether) +{ + Dp8390 *ctlr; + + ctlr = ether->ctlr; + + /* + * This is the initialisation procedure described + * as 'mandatory' in the datasheet, with references + * to the 3C503 technical reference manual. + */ + disable(ctlr); + if(ctlr->width != 1) + regw(ctlr, Dcr, Ft4WORD|Ls|Wts); + else + regw(ctlr, Dcr, Ft4WORD|Ls); + + regw(ctlr, Rbcr0, 0); + regw(ctlr, Rbcr1, 0); + + regw(ctlr, Tcr, LpbkNIC); + regw(ctlr, Rcr, Mon); + + /* + * Init the ring hardware and software ring pointers. + * Can't initialise ethernet address as it may not be + * known yet. + */ + ringinit(ctlr); + regw(ctlr, Tpsr, ctlr->tstart); + + /* + * Clear any pending interrupts and mask then all off. + */ + regw(ctlr, Isr, 0xFF); + regw(ctlr, Imr, 0); + + /* + * Leave the chip initialised, + * but in monitor mode. + */ + regw(ctlr, Cr, Page0|RdABORT|Sta); + + /* + * Set up the software configuration. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = 0; + + ether->promiscuous = promiscuous; + ether->multicast = multicast; + ether->arg = ether; + + return 0; +} diff --git a/os/pc/ether8390.h b/os/pc/ether8390.h new file mode 100644 index 00000000..b5bf6e55 --- /dev/null +++ b/os/pc/ether8390.h @@ -0,0 +1,74 @@ +/* + * Ctlr for the boards using the National Semiconductor DP8390 + * and SMC 83C90 Network Interface Controller. + * Common code is in ether8390.c. + */ +typedef struct { + Lock; + + ulong port; /* I/O address of 8390 */ + ulong data; /* I/O data port if no shared memory */ + + uchar width; /* data transfer width in bytes */ + uchar ram; /* true if card has shared memory */ + uchar dummyrr; /* do dummy remote read */ + + uchar nxtpkt; /* receive: software bndry */ + uchar pstart; + uchar pstop; + + int txbusy; /* transmit */ + uchar tstart; /* 8390 ring addresses */ + + uchar mar[8]; /* shadow multicast address registers */ + int mref[64]; /* reference counts for multicast groups */ +} Dp8390; + +#define Dp8390BufSz 256 + +extern int dp8390reset(Ether*); +extern void *dp8390read(Dp8390*, void*, ulong, ulong); +extern void dp8390getea(Ether*, uchar*); +extern void dp8390setea(Ether*); + +/* + * x86-specific code. + */ +#define regr(c, r) inb((c)->port+(r)) +#define regw(c, r, v) outb((c)->port+(r), (v)) + +static void +rdread(Dp8390* ctlr, void* to, int len) +{ + switch(ctlr->width){ + default: + panic("dp8390 rdread: width %d\n", ctlr->width); + break; + + case 2: + inss(ctlr->data, to, len/2); + break; + + case 1: + insb(ctlr->data, to, len); + break; + } +} + +static void +rdwrite(Dp8390* ctlr, void* from, int len) +{ + switch(ctlr->width){ + default: + panic("dp8390 rdwrite: width %d\n", ctlr->width); + break; + + case 2: + outss(ctlr->data, from, len/2); + break; + + case 1: + outsb(ctlr->data, from, len); + break; + } +} diff --git a/os/pc/etherec2t.c b/os/pc/etherec2t.c new file mode 100644 index 00000000..ffbec852 --- /dev/null +++ b/os/pc/etherec2t.c @@ -0,0 +1,173 @@ +/* + * Supposed NE2000 PCMCIA clones, see the comments in ether2000.c + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" +#include "ether8390.h" + +enum { + Data = 0x10, /* offset from I/O base of data port */ + Reset = 0x1F, /* offset from I/O base of reset port */ +}; + +typedef struct Ec2t { + char* name; + int iochecksum; +} Ec2t; + +static Ec2t ec2tpcmcia[] = { + { "EC2T", 0, }, /* Linksys Combo PCMCIA EthernetCard */ + { "PCMPC100", 1, }, /* EtherFast 10/100 PC Card */ + { "PCM100", 1, }, /* EtherFast PCM100 Card */ + { "EN2216", 0, }, /* Accton EtherPair-PCMCIA */ + { "FA410TX", 1, }, /* Netgear FA410TX */ + { "Network Everywhere", 0, }, /* Linksys NP10T 10BaseT Card */ + { "10/100 Port Attached", 1, }, /* SMC 8040TX */ + { "FA411", 0 }, /* Netgear FA411 PCMCIA */ + { nil, 0, }, +}; + +static int +reset(Ether* ether) +{ + ushort buf[16]; + ulong port; + Dp8390 *ctlr; + int i, slot; + uchar ea[Eaddrlen], sum, x; + Ec2t *ec2t, tmpec2t; + + /* + * Set up the software configuration. + * Use defaults for port, irq, mem and size + * if not specified. + * The manual says 16KB memory, the box + * says 32KB. The manual seems to be correct. + */ + if(ether->port == 0) + ether->port = 0x300; + if(ether->irq == 0) + ether->irq = 9; + if(ether->mem == 0) + ether->mem = 0x4000; + if(ether->size == 0) + ether->size = 16*1024; + port = ether->port; + + if(ioalloc(ether->port, 0x20, 0, "ec2t") < 0) + return -1; + slot = -1; + for(ec2t = ec2tpcmcia; ec2t->name != nil; ec2t++){ + if((slot = pcmspecial(ec2t->name, ether)) >= 0) + break; + } + if(ec2t->name == nil){ + ec2t = &tmpec2t; + ec2t->name = nil; + ec2t->iochecksum = 0; + for(i = 0; i < ether->nopt; i++){ + if(cistrncmp(ether->opt[i], "id=", 3) == 0){ + ec2t->name = ðer->opt[i][3]; + slot = pcmspecial(ec2t->name, ether); + } + else if(cistrncmp(ether->opt[i], "iochecksum", 10) == 0) + ec2t->iochecksum = 1; + } + } + if(slot < 0){ + iofree(port); + return -1; + } + + ether->ctlr = malloc(sizeof(Dp8390)); + ctlr = ether->ctlr; + ctlr->width = 2; + ctlr->ram = 0; + + ctlr->port = port; + ctlr->data = port+Data; + + ctlr->tstart = HOWMANY(ether->mem, Dp8390BufSz); + ctlr->pstart = ctlr->tstart + HOWMANY(sizeof(Etherpkt), Dp8390BufSz); + ctlr->pstop = ctlr->tstart + HOWMANY(ether->size, Dp8390BufSz); + + ctlr->dummyrr = 0; + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "nodummyrr") == 0) + ctlr->dummyrr = 0; + else if(cistrncmp(ether->opt[i], "dummyrr=", 8) == 0) + ctlr->dummyrr = strtol(ðer->opt[i][8], nil, 0); + } + + /* + * Reset the board. This is done by doing a read + * followed by a write to the Reset address. + */ + buf[0] = inb(port+Reset); + delay(2); + outb(port+Reset, buf[0]); + delay(2); + + /* + * Init the (possible) chip, then use the (possible) + * chip to read the (possible) PROM for ethernet address + * and a marker byte. + * Could just look at the DP8390 command register after + * initialisation has been tried, but that wouldn't be + * enough, there are other ethernet boards which could + * match. + */ + dp8390reset(ether); + sum = 0; + if(ec2t->iochecksum){ + /* + * These cards have the ethernet address in I/O space. + * There's a checksum over 8 bytes which sums to 0xFF. + */ + for(i = 0; i < 8; i++){ + x = inb(port+0x14+i); + sum += x; + buf[i] = (x<<8)|x; + } + } + else{ + memset(buf, 0, sizeof(buf)); + dp8390read(ctlr, buf, 0, sizeof(buf)); + if((buf[0x0E] & 0xFF) == 0x57 && (buf[0x0F] & 0xFF) == 0x57) + sum = 0xFF; + } + if(sum != 0xFF){ + pcmspecialclose(slot); + iofree(ether->port); + free(ether->ctlr); + return -1; + } + + /* + * Stupid machine. Shorts were asked for, + * shorts were delivered, although the PROM is a byte array. + * Set the ethernet address. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + for(i = 0; i < sizeof(ether->ea); i++) + ether->ea[i] = buf[i]; + } + dp8390setea(ether); + + return 0; +} + +void +etherec2tlink(void) +{ + addethercard("EC2T", reset); +} diff --git a/os/pc/etherelnk3.c b/os/pc/etherelnk3.c new file mode 100644 index 00000000..e0be41f1 --- /dev/null +++ b/os/pc/etherelnk3.c @@ -0,0 +1,2132 @@ +/* + * Etherlink III, Fast EtherLink and Fast EtherLink XL adapters. + * To do: + * check robustness in the face of errors (e.g. busmaster & rxUnderrun); + * RxEarly and busmaster; + * autoSelect; + * PCI latency timer and master enable; + * errata list; + * rewrite all initialisation. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +#define XCVRDEBUG if(0)print + +enum { + IDport = 0x0110, /* anywhere between 0x0100 and 0x01F0 */ +}; + +enum { /* all windows */ + CommandR = 0x000E, + IntStatusR = 0x000E, +}; + +enum { /* Commands */ + GlobalReset = 0x0000, + SelectRegisterWindow = 0x0001, + EnableDcConverter = 0x0002, + RxDisable = 0x0003, + RxEnable = 0x0004, + RxReset = 0x0005, + Stall = 0x0006, /* 3C90x */ + TxDone = 0x0007, + RxDiscard = 0x0008, + TxEnable = 0x0009, + TxDisable = 0x000A, + TxReset = 0x000B, + RequestInterrupt = 0x000C, + AcknowledgeInterrupt = 0x000D, + SetInterruptEnable = 0x000E, + SetIndicationEnable = 0x000F, /* SetReadZeroMask */ + SetRxFilter = 0x0010, + SetRxEarlyThresh = 0x0011, + SetTxAvailableThresh = 0x0012, + SetTxStartThresh = 0x0013, + StartDma = 0x0014, /* initiate busmaster operation */ + StatisticsEnable = 0x0015, + StatisticsDisable = 0x0016, + DisableDcConverter = 0x0017, + SetTxReclaimThresh = 0x0018, /* PIO-only adapters */ + PowerUp = 0x001B, /* not all adapters */ + PowerDownFull = 0x001C, /* not all adapters */ + PowerAuto = 0x001D, /* not all adapters */ +}; + +enum { /* (Global|Rx|Tx)Reset command bits */ + tpAuiReset = 0x0001, /* 10BaseT and AUI transceivers */ + endecReset = 0x0002, /* internal Ethernet encoder/decoder */ + networkReset = 0x0004, /* network interface logic */ + fifoReset = 0x0008, /* FIFO control logic */ + aismReset = 0x0010, /* autoinitialise state-machine logic */ + hostReset = 0x0020, /* bus interface logic */ + dmaReset = 0x0040, /* bus master logic */ + vcoReset = 0x0080, /* on-board 10Mbps VCO */ + updnReset = 0x0100, /* upload/download (Rx/TX) logic */ + + resetMask = 0x01FF, +}; + +enum { /* Stall command bits */ + upStall = 0x0000, + upUnStall = 0x0001, + dnStall = 0x0002, + dnUnStall = 0x0003, +}; + +enum { /* SetRxFilter command bits */ + receiveIndividual = 0x0001, /* match station address */ + receiveMulticast = 0x0002, + receiveBroadcast = 0x0004, + receiveAllFrames = 0x0008, /* promiscuous */ +}; + +enum { /* StartDma command bits */ + Upload = 0x0000, /* transfer data from adapter to memory */ + Download = 0x0001, /* transfer data from memory to adapter */ +}; + +enum { /* IntStatus bits */ + interruptLatch = 0x0001, + hostError = 0x0002, /* Adapter Failure */ + txComplete = 0x0004, + txAvailable = 0x0008, + rxComplete = 0x0010, + rxEarly = 0x0020, + intRequested = 0x0040, + updateStats = 0x0080, + transferInt = 0x0100, /* Bus Master Transfer Complete */ + dnComplete = 0x0200, + upComplete = 0x0400, + busMasterInProgress = 0x0800, + commandInProgress = 0x1000, + + interruptMask = 0x07FE, +}; + +#define COMMAND(port, cmd, a) outs((port)+CommandR, ((cmd)<<11)|(a)) +#define STATUS(port) ins((port)+IntStatusR) + +enum { /* Window 0 - setup */ + Wsetup = 0x0000, + /* registers */ + ManufacturerID = 0x0000, /* 3C5[08]*, 3C59[27] */ + ProductID = 0x0002, /* 3C5[08]*, 3C59[27] */ + ConfigControl = 0x0004, /* 3C5[08]*, 3C59[27] */ + AddressConfig = 0x0006, /* 3C5[08]*, 3C59[27] */ + ResourceConfig = 0x0008, /* 3C5[08]*, 3C59[27] */ + EepromCommand = 0x000A, + EepromData = 0x000C, + /* AddressConfig Bits */ + autoSelect9 = 0x0080, + xcvrMask9 = 0xC000, + /* ConfigControl bits */ + Ena = 0x0001, + base10TAvailable9 = 0x0200, + coaxAvailable9 = 0x1000, + auiAvailable9 = 0x2000, + /* EepromCommand bits */ + EepromReadRegister = 0x0080, + EepromReadOffRegister = 0x00B0, + EepromRead8bRegister = 0x0230, + EepromBusy = 0x8000, +}; + +#define EEPROMCMD(port, cmd, a) outs((port)+EepromCommand, (cmd)|(a)) +#define EEPROMBUSY(port) (ins((port)+EepromCommand) & EepromBusy) +#define EEPROMDATA(port) ins((port)+EepromData) + +enum { /* Window 1 - operating set */ + Wop = 0x0001, + /* registers */ + Fifo = 0x0000, + RxError = 0x0004, /* 3C59[0257] only */ + RxStatus = 0x0008, + TIMER = 0x000A, + TxStatus = 0x000B, + TxFree = 0x000C, + /* RxError bits */ + rxOverrun = 0x0001, + runtFrame = 0x0002, + alignmentError = 0x0004, /* Framing */ + crcError = 0x0008, + oversizedFrame = 0x0010, + dribbleBits = 0x0080, + /* RxStatus bits */ + rxBytes = 0x1FFF, /* 3C59[0257] mask */ + rxBytes9 = 0x07FF, /* 3C5[078]9 mask */ + rxError9 = 0x3800, /* 3C5[078]9 error mask */ + rxOverrun9 = 0x0000, + oversizedFrame9 = 0x0800, + dribbleBits9 = 0x1000, + runtFrame9 = 0x1800, + alignmentError9 = 0x2000, /* Framing */ + crcError9 = 0x2800, + rxError = 0x4000, + rxIncomplete = 0x8000, + /* TxStatus Bits */ + txStatusOverflow = 0x0004, + maxCollisions = 0x0008, + txUnderrun = 0x0010, + txJabber = 0x0020, + interruptRequested = 0x0040, + txStatusComplete = 0x0080, +}; + +enum { /* Window 2 - station address */ + Wstation = 0x0002, + + ResetOp905B = 0x000C, +}; + +enum { /* Window 3 - FIFO management */ + Wfifo = 0x0003, + /* registers */ + InternalConfig = 0x0000, /* 3C509B, 3C589, 3C59[0257] */ + OtherInt = 0x0004, /* 3C59[0257] */ + RomControl = 0x0006, /* 3C509B, 3C59[27] */ + MacControl = 0x0006, /* 3C59[0257] */ + ResetOptions = 0x0008, /* 3C59[0257] */ + MediaOptions = 0x0008, /* 3C905B */ + RxFree = 0x000A, + /* InternalConfig bits */ + disableBadSsdDetect = 0x00000100, + ramLocation = 0x00000200, /* 0 external, 1 internal */ + ramPartition5to3 = 0x00000000, + ramPartition3to1 = 0x00010000, + ramPartition1to1 = 0x00020000, + ramPartition3to5 = 0x00030000, + ramPartitionMask = 0x00030000, + xcvr10BaseT = 0x00000000, + xcvrAui = 0x00100000, /* 10BASE5 */ + xcvr10Base2 = 0x00300000, + xcvr100BaseTX = 0x00400000, + xcvr100BaseFX = 0x00500000, + xcvrMii = 0x00600000, + xcvrMask = 0x00700000, + autoSelect = 0x01000000, + /* MacControl bits */ + deferExtendEnable = 0x0001, + deferTIMERSelect = 0x001E, /* mask */ + fullDuplexEnable = 0x0020, + allowLargePackets = 0x0040, + extendAfterCollision = 0x0080, /* 3C90xB */ + flowControlEnable = 0x0100, /* 3C90xB */ + vltEnable = 0x0200, /* 3C90xB */ + /* ResetOptions bits */ + baseT4Available = 0x0001, + baseTXAvailable = 0x0002, + baseFXAvailable = 0x0004, + base10TAvailable = 0x0008, + coaxAvailable = 0x0010, + auiAvailable = 0x0020, + miiConnector = 0x0040, +}; + +enum { /* Window 4 - diagnostic */ + Wdiagnostic = 0x0004, + /* registers */ + VcoDiagnostic = 0x0002, + FifoDiagnostic = 0x0004, + NetworkDiagnostic = 0x0006, + PhysicalMgmt = 0x0008, + MediaStatus = 0x000A, + BadSSD = 0x000C, + UpperBytesOk = 0x000D, + /* FifoDiagnostic bits */ + txOverrun = 0x0400, + rxUnderrun = 0x2000, + receiving = 0x8000, + /* PhysicalMgmt bits */ + mgmtClk = 0x0001, + mgmtData = 0x0002, + mgmtDir = 0x0004, + cat5LinkTestDefeat = 0x8000, + /* MediaStatus bits */ + dataRate100 = 0x0002, + crcStripDisable = 0x0004, + enableSqeStats = 0x0008, + collisionDetect = 0x0010, + carrierSense = 0x0020, + jabberGuardEnable = 0x0040, + linkBeatEnable = 0x0080, + jabberDetect = 0x0200, + polarityReversed = 0x0400, + linkBeatDetect = 0x0800, + txInProg = 0x1000, + dcConverterEnabled = 0x4000, + auiDisable = 0x8000, /* 10BaseT transceiver selected */ +}; + +enum { /* Window 5 - internal state */ + Wstate = 0x0005, + /* registers */ + TxStartThresh = 0x0000, + TxAvailableThresh = 0x0002, + RxEarlyThresh = 0x0006, + RxFilter = 0x0008, + InterruptEnable = 0x000A, + IndicationEnable = 0x000C, +}; + +enum { /* Window 6 - statistics */ + Wstatistics = 0x0006, + /* registers */ + CarrierLost = 0x0000, + SqeErrors = 0x0001, + MultipleColls = 0x0002, + SingleCollFrames = 0x0003, + LateCollisions = 0x0004, + RxOverruns = 0x0005, + FramesXmittedOk = 0x0006, + FramesRcvdOk = 0x0007, + FramesDeferred = 0x0008, + UpperFramesOk = 0x0009, + BytesRcvdOk = 0x000A, + BytesXmittedOk = 0x000C, +}; + +enum { /* Window 7 - bus master operations */ + Wmaster = 0x0007, + /* registers */ + MasterAddress = 0x0000, + MasterLen = 0x0006, + MasterStatus = 0x000C, + /* MasterStatus bits */ + masterAbort = 0x0001, + targetAbort = 0x0002, + targetRetry = 0x0004, + targetDisc = 0x0008, + masterDownload = 0x1000, + masterUpload = 0x4000, + masterInProgress = 0x8000, + + masterMask = 0xD00F, +}; + +enum { /* 3C90x extended register set */ + TIMER905 = 0x001A, /* 8-bits */ + TxStatus905 = 0x001B, /* 8-bits */ + PktStatus = 0x0020, /* 32-bits */ + DnListPtr = 0x0024, /* 32-bits, 8-byte aligned */ + FragAddr = 0x0028, /* 32-bits */ + FragLen = 0x002C, /* 16-bits */ + ListOffset = 0x002E, /* 8-bits */ + TxFreeThresh = 0x002F, /* 8-bits */ + UpPktStatus = 0x0030, /* 32-bits */ + FreeTIMER = 0x0034, /* 16-bits */ + UpListPtr = 0x0038, /* 32-bits, 8-byte aligned */ + + /* PktStatus bits */ + fragLast = 0x00000001, + dnCmplReq = 0x00000002, + dnStalled = 0x00000004, + upCompleteX = 0x00000008, + dnCompleteX = 0x00000010, + upRxEarlyEnable = 0x00000020, + armCountdown = 0x00000040, + dnInProg = 0x00000080, + counterSpeed = 0x00000010, /* 0 3.2uS, 1 320nS */ + countdownMode = 0x00000020, + /* UpPktStatus bits (dpd->control) */ + upPktLenMask = 0x00001FFF, + upStalled = 0x00002000, + upError = 0x00004000, + upPktComplete = 0x00008000, + upOverrun = 0x00010000, /* RxError<<16 */ + upRuntFrame = 0x00020000, + upAlignmentError = 0x00040000, + upCRCError = 0x00080000, + upOversizedFrame = 0x00100000, + upDribbleBits = 0x00800000, + upOverflow = 0x01000000, + + dnIndicate = 0x80000000, /* FrameStartHeader (dpd->control) */ + + updnLastFrag = 0x80000000, /* (dpd->len) */ + + Nup = 32, + Ndn = 64, +}; + +/* + * Up/Dn Packet Descriptors. + * The hardware info (np, control, addr, len) must be 8-byte aligned + * and this structure size must be a multiple of 8. + */ +typedef struct Pd Pd; +typedef struct Pd { + ulong np; /* next pointer */ + ulong control; /* FSH or UpPktStatus */ + ulong addr; + ulong len; + + Pd* next; + Block* bp; +} Pd; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + int irq; + Ctlr* next; + int active; + int did; + + Lock wlock; /* window access */ + + int attached; + int busmaster; + Block* rbp; /* receive buffer */ + + Block* txbp; /* FIFO -based transmission */ + int txthreshold; + int txbusy; + + int nup; /* full-busmaster -based reception */ + void* upbase; + Pd* upr; + Pd* uphead; + + int ndn; /* full-busmaster -based transmission */ + void* dnbase; + Pd* dnr; + Pd* dnhead; + Pd* dntail; + int dnq; + + long interrupts; /* statistics */ + long bogusinterrupts; + long timer[2]; + long stats[BytesRcvdOk+3]; + + int upqmax; + int upqmaxhw; + ulong upinterrupts; + ulong upqueued; + ulong upstalls; + int dnqmax; + int dnqmaxhw; + ulong dninterrupts; + ulong dnqueued; + + int xcvr; /* transceiver type */ + int eepromcmd; /* EEPROM read command */ + int rxstatus9; /* old-style RxStatus register */ + int rxearly; /* RxEarlyThreshold */ + int ts; /* threshold shift */ + int upenabled; + int dnenabled; + ulong cbfnpa; /* CardBus functions */ + ulong* cbfn; +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +static void +init905(Ctlr* ctlr) +{ + Block *bp; + Pd *pd, *prev; + + /* + * Create rings for the receive and transmit sides. + * Take care with alignment: + * make sure ring base is 8-byte aligned; + * make sure each entry is 8-byte aligned. + */ + ctlr->upbase = malloc((ctlr->nup+1)*sizeof(Pd)); + ctlr->upr = (Pd*)ROUNDUP((ulong)ctlr->upbase, 8); + + prev = ctlr->upr; + for(pd = &ctlr->upr[ctlr->nup-1]; pd >= ctlr->upr; pd--){ + pd->np = PADDR(&prev->np); + pd->control = 0; + bp = iallocb(sizeof(Etherpkt)); + if(bp == nil) + panic("can't allocate ethernet receive ring"); + pd->addr = PADDR(bp->rp); + pd->len = updnLastFrag|sizeof(Etherpkt); + + pd->next = prev; + prev = pd; + pd->bp = bp; + } + ctlr->uphead = ctlr->upr; + + ctlr->dnbase = malloc((ctlr->ndn+1)*sizeof(Pd)); + ctlr->dnr = (Pd*)ROUNDUP((ulong)ctlr->dnbase, 8); + + prev = ctlr->dnr; + for(pd = &ctlr->dnr[ctlr->ndn-1]; pd >= ctlr->dnr; pd--){ + pd->next = prev; + prev = pd; + } + ctlr->dnhead = ctlr->dnr; + ctlr->dntail = ctlr->dnr; + ctlr->dnq = 0; +} + +static Block* +rbpalloc(Block* (*f)(int)) +{ + Block *bp; + ulong addr; + + /* + * The receive buffers must be on a 32-byte + * boundary for EISA busmastering. + */ + if(bp = f(ROUNDUP(sizeof(Etherpkt), 4) + 31)){ + addr = (ulong)bp->base; + addr = ROUNDUP(addr, 32); + bp->rp = (uchar*)addr; + } + + return bp; +} + +static uchar* +startdma(Ether* ether, ulong address) +{ + int port, status, w; + uchar *wp; + + port = ether->port; + + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wmaster); + + wp = KADDR(inl(port+MasterAddress)); + status = ins(port+MasterStatus); + if(status & (masterInProgress|targetAbort|masterAbort)) + print("#l%d: BM status 0x%uX\n", ether->ctlrno, status); + outs(port+MasterStatus, masterMask); + outl(port+MasterAddress, address); + outs(port+MasterLen, sizeof(Etherpkt)); + COMMAND(port, StartDma, Upload); + + COMMAND(port, SelectRegisterWindow, w); + return wp; +} + +static void +promiscuous(void* arg, int on) +{ + int filter, port; + Ether *ether; + + ether = (Ether*)arg; + port = ether->port; + + filter = receiveBroadcast|receiveIndividual; + if(ether->nmaddr) + filter |= receiveMulticast; + if(on) + filter |= receiveAllFrames; + COMMAND(port, SetRxFilter, filter); +} + +static void +multicast(void* arg, uchar *addr, int on) +{ + int filter, port; + Ether *ether; + + USED(addr, on); + + ether = (Ether*)arg; + port = ether->port; + + filter = receiveBroadcast|receiveIndividual; + if(ether->nmaddr) + filter |= receiveMulticast; + if(ether->prom) + filter |= receiveAllFrames; + COMMAND(port, SetRxFilter, filter); +} + +/* On the 575B and C, interrupts need to be acknowledged in CardBus memory space */ +static void +intrackcb(ulong *cbfn) +{ + cbfn[1] = 0x8000; +} + +static void +attach(Ether* ether) +{ + int port, x; + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->wlock); + if(ctlr->attached){ + iunlock(&ctlr->wlock); + return; + } + + port = ether->port; + + /* + * Set the receiver packet filter for this and broadcast addresses, + * set the interrupt masks for all interrupts, enable the receiver + * and transmitter. + */ + promiscuous(ether, ether->prom); + + x = interruptMask; + if(ctlr->busmaster == 1) + x &= ~(rxEarly|rxComplete); + else{ + if(ctlr->dnenabled) + x &= ~transferInt; + if(ctlr->upenabled) + x &= ~(rxEarly|rxComplete); + } + COMMAND(port, SetIndicationEnable, x); + COMMAND(port, SetInterruptEnable, x); + COMMAND(port, RxEnable, 0); + COMMAND(port, TxEnable, 0); + + /* + * If this is a CardBus card, acknowledge any interrupts. + */ + if(ctlr->cbfn != nil) + intrackcb(ctlr->cbfn); + + /* + * Prime the busmaster channel for receiving directly into a + * receive packet buffer if necessary. + */ + if(ctlr->busmaster == 1) + startdma(ether, PADDR(ctlr->rbp->rp)); + else{ + if(ctlr->upenabled) + outl(port+UpListPtr, PADDR(&ctlr->uphead->np)); + } + + ctlr->attached = 1; + iunlock(&ctlr->wlock); +} + +static void +statistics(Ether* ether) +{ + int port, i, u, w; + Ctlr *ctlr; + + port = ether->port; + ctlr = ether->ctlr; + + /* + * 3C59[27] require a read between a PIO write and + * reading a statistics register. + */ + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wstatistics); + STATUS(port); + + for(i = 0; i < UpperFramesOk; i++) + ctlr->stats[i] += inb(port+i) & 0xFF; + u = inb(port+UpperFramesOk) & 0xFF; + ctlr->stats[FramesXmittedOk] += (u & 0x30)<<4; + ctlr->stats[FramesRcvdOk] += (u & 0x03)<<8; + ctlr->stats[BytesRcvdOk] += ins(port+BytesRcvdOk) & 0xFFFF; + ctlr->stats[BytesRcvdOk+1] += ins(port+BytesXmittedOk) & 0xFFFF; + + switch(ctlr->xcvr){ + + case xcvrMii: + case xcvr100BaseTX: + case xcvr100BaseFX: + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + STATUS(port); + ctlr->stats[BytesRcvdOk+2] += inb(port+BadSSD); + break; + } + + COMMAND(port, SelectRegisterWindow, w); +} + +static void +txstart(Ether* ether) +{ + int port, len; + Ctlr *ctlr; + Block *bp; + + port = ether->port; + ctlr = ether->ctlr; + + /* + * Attempt to top-up the transmit FIFO. If there's room simply + * stuff in the packet length (unpadded to a dword boundary), the + * packet data (padded) and remove the packet from the queue. + * If there's no room post an interrupt for when there is. + * This routine is called both from the top level and from interrupt + * level and expects to be called with ctlr->wlock already locked + * and the correct register window (Wop) in place. + */ + for(;;){ + if(ctlr->txbp){ + bp = ctlr->txbp; + ctlr->txbp = 0; + } + else{ + bp = qget(ether->oq); + if(bp == nil) + break; + } + + len = ROUNDUP(BLEN(bp), 4); + if(len+4 <= ins(port+TxFree)){ + outl(port+Fifo, BLEN(bp)); + outsl(port+Fifo, bp->rp, len/4); + + freeb(bp); + + ether->outpackets++; + } + else{ + ctlr->txbp = bp; + if(ctlr->txbusy == 0){ + ctlr->txbusy = 1; + COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts); + } + break; + } + } +} + +static void +txstart905(Ether* ether) +{ + Ctlr *ctlr; + int port, stalled, timeo; + Block *bp; + Pd *pd; + + ctlr = ether->ctlr; + port = ether->port; + + /* + * Free any completed packets. + */ + pd = ctlr->dntail; + while(ctlr->dnq){ + if(PADDR(&pd->np) == inl(port+DnListPtr)) + break; + if(pd->bp){ + freeb(pd->bp); + pd->bp = nil; + } + ctlr->dnq--; + pd = pd->next; + } + ctlr->dntail = pd; + + stalled = 0; + while(ctlr->dnq < (ctlr->ndn-1)){ + bp = qget(ether->oq); + if(bp == nil) + break; + + pd = ctlr->dnhead->next; + pd->np = 0; + pd->control = dnIndicate|BLEN(bp); + pd->addr = PADDR(bp->rp); + pd->len = updnLastFrag|BLEN(bp); + pd->bp = bp; + + if(stalled == 0 && ctlr->dnq && inl(port+DnListPtr)){ + COMMAND(port, Stall, dnStall); + for(timeo = 100; (STATUS(port) & commandInProgress) && timeo; timeo--) + ; + if(timeo == 0) + print("#l%d: dnstall %d\n", ether->ctlrno, timeo); + stalled = 1; + } + + coherence(); + ctlr->dnhead->np = PADDR(&pd->np); + ctlr->dnhead->control &= ~dnIndicate; + ctlr->dnhead = pd; + if(ctlr->dnq == 0) + ctlr->dntail = pd; + ctlr->dnq++; + + ctlr->dnqueued++; + } + + if(ctlr->dnq > ctlr->dnqmax) + ctlr->dnqmax = ctlr->dnq; + + /* + * If the adapter is not currently processing anything + * and there is something on the queue, start it processing. + */ + if(inl(port+DnListPtr) == 0 && ctlr->dnq) + outl(port+DnListPtr, PADDR(&ctlr->dnhead->np)); + if(stalled) + COMMAND(port, Stall, dnUnStall); +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + int port, w; + + port = ether->port; + ctlr = ether->ctlr; + + ilock(&ctlr->wlock); + if(ctlr->dnenabled) + txstart905(ether); + else{ + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wop); + txstart(ether); + COMMAND(port, SelectRegisterWindow, w); + } + iunlock(&ctlr->wlock); +} + +static void +receive905(Ether* ether) +{ + Ctlr *ctlr; + int len, port, q; + Pd *pd; + Block *bp; + + ctlr = ether->ctlr; + port = ether->port; + + if(inl(port+UpPktStatus) & upStalled) + ctlr->upstalls++; + q = 0; + for(pd = ctlr->uphead; pd->control & upPktComplete; pd = pd->next){ + if(pd->control & upError){ + if(pd->control & upOverrun) + ether->overflows++; + if(pd->control & (upOversizedFrame|upRuntFrame)) + ether->buffs++; + if(pd->control & upAlignmentError) + ether->frames++; + if(pd->control & upCRCError) + ether->crcs++; + } + else if(bp = iallocb(sizeof(Etherpkt)+4)){ + len = pd->control & rxBytes; + pd->bp->wp = pd->bp->rp+len; + etheriq(ether, pd->bp, 1); + pd->bp = bp; + pd->addr = PADDR(bp->rp); + coherence(); + } + + pd->control = 0; + COMMAND(port, Stall, upUnStall); + + q++; + } + ctlr->uphead = pd; + + ctlr->upqueued += q; + if(q > ctlr->upqmax) + ctlr->upqmax = q; +} + +static void +receive(Ether* ether) +{ + int len, port, rxerror, rxstatus; + Ctlr *ctlr; + Block *bp; + + port = ether->port; + ctlr = ether->ctlr; + + while(((rxstatus = ins(port+RxStatus)) & rxIncomplete) == 0){ + if(ctlr->busmaster == 1 && (STATUS(port) & busMasterInProgress)) + break; + + /* + * If there was an error, log it and continue. + * Unfortunately the 3C5[078]9 has the error info in the status register + * and the 3C59[0257] implement a separate RxError register. + */ + if(rxstatus & rxError){ + if(ctlr->rxstatus9){ + switch(rxstatus & rxError9){ + + case rxOverrun9: + ether->overflows++; + break; + + case oversizedFrame9: + case runtFrame9: + ether->buffs++; + break; + + case alignmentError9: + ether->frames++; + break; + + case crcError9: + ether->crcs++; + break; + + } + } + else{ + rxerror = inb(port+RxError); + if(rxerror & rxOverrun) + ether->overflows++; + if(rxerror & (oversizedFrame|runtFrame)) + ether->buffs++; + if(rxerror & alignmentError) + ether->frames++; + if(rxerror & crcError) + ether->crcs++; + } + } + + /* + * If there was an error or a new receive buffer can't be + * allocated, discard the packet and go on to the next. + */ + if((rxstatus & rxError) || (bp = rbpalloc(iallocb)) == 0){ + COMMAND(port, RxDiscard, 0); + while(STATUS(port) & commandInProgress) + ; + + if(ctlr->busmaster == 1) + startdma(ether, PADDR(ctlr->rbp->rp)); + + continue; + } + + /* + * A valid receive packet awaits: + * if using PIO, read it into the buffer; + * discard the packet from the FIFO; + * if using busmastering, start a new transfer for + * the next packet and as a side-effect get the + * end-pointer of the one just received; + * pass the packet on to whoever wants it. + */ + if(ctlr->busmaster == 0 || ctlr->busmaster == 2){ + len = (rxstatus & rxBytes9); + ctlr->rbp->wp = ctlr->rbp->rp + len; + insl(port+Fifo, ctlr->rbp->rp, HOWMANY(len, 4)); + } + + COMMAND(port, RxDiscard, 0); + while(STATUS(port) & commandInProgress) + ; + + if(ctlr->busmaster == 1) + ctlr->rbp->wp = startdma(ether, PADDR(bp->rp)); + + etheriq(ether, ctlr->rbp, 1); + ctlr->rbp = bp; + } +} + +static int +ejectable(int did) +{ + switch (did) { + case 0x5157: + return 1; + + default: + return 0; + } +} + +static void +interrupt(Ureg*, void* arg) +{ + Ether *ether; + int port, status, s, txstatus, w, x; + Ctlr *ctlr; + + ether = arg; + port = ether->port; + ctlr = ether->ctlr; + + ilock(&ctlr->wlock); + status = STATUS(port); + if(!(status & (interruptMask|interruptLatch))){ + ctlr->bogusinterrupts++; + iunlock(&ctlr->wlock); + return; + } + w = (status>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wop); + + ctlr->interrupts++; + if(ctlr->busmaster == 2) + ctlr->timer[0] += inb(port+TIMER905) & 0xFF; + else + ctlr->timer[0] += inb(port+TIMER) & 0xFF; + + do{ + if(status & hostError){ + /* + * Adapter failure, try to find out why, reset if + * necessary. What happens if Tx is active and a reset + * occurs, need to retransmit? This probably isn't right. + */ + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+FifoDiagnostic); + COMMAND(port, SelectRegisterWindow, Wop); + + if (status == 0xFFFF && x == 0xFFFF && ejectable(ctlr->did)) { + print("#l%d: Card ejected?\n", ether->ctlrno); + iunlock(&ctlr->wlock); + return; + } + + print("#l%d: status 0x%uX, diag 0x%uX\n", + ether->ctlrno, status, x); + + if(x & txOverrun){ + if(ctlr->busmaster == 0) + COMMAND(port, TxReset, 0); + else + COMMAND(port, TxReset, (updnReset|dmaReset)); + COMMAND(port, TxEnable, 0); + } + + if(x & rxUnderrun){ + /* + * This shouldn't happen... + * Reset the receiver and restore the filter and RxEarly + * threshold before re-enabling. + * Need to restart any busmastering? + */ + COMMAND(port, SelectRegisterWindow, Wstate); + s = (port+RxFilter) & 0x000F; + COMMAND(port, SelectRegisterWindow, Wop); + COMMAND(port, RxReset, 0); + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, SetRxFilter, s); + COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts); + COMMAND(port, RxEnable, 0); + } + + status &= ~hostError; + } + + if(status & (transferInt|rxComplete)){ + receive(ether); + status &= ~(transferInt|rxComplete); + } + + if(status & (upComplete)){ + COMMAND(port, AcknowledgeInterrupt, upComplete); + receive905(ether); + status &= ~upComplete; + ctlr->upinterrupts++; + } + + if(status & txComplete){ + /* + * Pop the TxStatus stack, accumulating errors. + * Adjust the TX start threshold if there was an underrun. + * If there was a Jabber or Underrun error, reset + * the transmitter, taking care not to reset the dma logic + * as a busmaster receive may be in progress. + * For all conditions enable the transmitter. + */ + if(ctlr->busmaster == 2) + txstatus = port+TxStatus905; + else + txstatus = port+TxStatus; + s = 0; + do{ + if(x = inb(txstatus)) + outb(txstatus, 0); + s |= x; + }while(STATUS(port) & txComplete); + + if(s & txUnderrun){ + if(ctlr->dnenabled){ + while(inl(port+PktStatus) & dnInProg) + ; + } + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + while(ins(port+MediaStatus) & txInProg) + ; + COMMAND(port, SelectRegisterWindow, Wop); + if(ctlr->txthreshold < ETHERMAXTU) + ctlr->txthreshold += ETHERMINTU; + } + + /* + * According to the manual, maxCollisions does not require + * a TxReset, merely a TxEnable. However, evidence points to + * it being necessary on the 3C905. The jury is still out. + * On busy or badly configured networks maxCollisions can + * happen frequently enough for messages to be annoying so + * keep quiet about them by popular request. + */ + if(s & (txJabber|txUnderrun|maxCollisions)){ + if(ctlr->busmaster == 0) + COMMAND(port, TxReset, 0); + else + COMMAND(port, TxReset, (updnReset|dmaReset)); + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts); + if(ctlr->busmaster == 2) + outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256)); + if(ctlr->dnenabled) + status |= dnComplete; + } + + if(s & ~(txStatusComplete|maxCollisions)) + print("#l%d: txstatus 0x%uX, threshold %d\n", + ether->ctlrno, s, ctlr->txthreshold); + COMMAND(port, TxEnable, 0); + ether->oerrs++; + status &= ~txComplete; + status |= txAvailable; + } + + if(status & txAvailable){ + COMMAND(port, AcknowledgeInterrupt, txAvailable); + ctlr->txbusy = 0; + txstart(ether); + status &= ~txAvailable; + } + + if(status & dnComplete){ + COMMAND(port, AcknowledgeInterrupt, dnComplete); + txstart905(ether); + status &= ~dnComplete; + ctlr->dninterrupts++; + } + + if(status & updateStats){ + statistics(ether); + status &= ~updateStats; + } + + /* + * Currently, this shouldn't happen. + */ + if(status & rxEarly){ + COMMAND(port, AcknowledgeInterrupt, rxEarly); + status &= ~rxEarly; + } + + /* + * Panic if there are any interrupts not dealt with. + */ + if(status & interruptMask) + panic("#l%d: interrupt mask 0x%uX\n", ether->ctlrno, status); + + COMMAND(port, AcknowledgeInterrupt, interruptLatch); + if(ctlr->cbfn != nil) + intrackcb(ctlr->cbfn); + + }while((status = STATUS(port)) & (interruptMask|interruptLatch)); + + if(ctlr->busmaster == 2) + ctlr->timer[1] += inb(port+TIMER905) & 0xFF; + else + ctlr->timer[1] += inb(port+TIMER) & 0xFF; + + COMMAND(port, SelectRegisterWindow, w); + iunlock(&ctlr->wlock); +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + char *p; + int len; + Ctlr *ctlr; + + if(n == 0) + return 0; + + ctlr = ether->ctlr; + + ilock(&ctlr->wlock); + statistics(ether); + iunlock(&ctlr->wlock); + + p = malloc(READSTR); + len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts); + len += snprint(p+len, READSTR-len, "bogusinterrupts: %lud\n", ctlr->bogusinterrupts); + len += snprint(p+len, READSTR-len, "timer: %lud %lud\n", + ctlr->timer[0], ctlr->timer[1]); + len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", + ctlr->stats[CarrierLost]); + len += snprint(p+len, READSTR-len, "sqeerrors: %lud\n", + ctlr->stats[SqeErrors]); + len += snprint(p+len, READSTR-len, "multiplecolls: %lud\n", + ctlr->stats[MultipleColls]); + len += snprint(p+len, READSTR-len, "singlecollframes: %lud\n", + ctlr->stats[SingleCollFrames]); + len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", + ctlr->stats[LateCollisions]); + len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", + ctlr->stats[RxOverruns]); + len += snprint(p+len, READSTR-len, "framesxmittedok: %lud\n", + ctlr->stats[FramesXmittedOk]); + len += snprint(p+len, READSTR-len, "framesrcvdok: %lud\n", + ctlr->stats[FramesRcvdOk]); + len += snprint(p+len, READSTR-len, "framesdeferred: %lud\n", + ctlr->stats[FramesDeferred]); + len += snprint(p+len, READSTR-len, "bytesrcvdok: %lud\n", + ctlr->stats[BytesRcvdOk]); + len += snprint(p+len, READSTR-len, "bytesxmittedok: %lud\n", + ctlr->stats[BytesRcvdOk+1]); + + if(ctlr->upenabled){ + if(ctlr->upqmax > ctlr->upqmaxhw) + ctlr->upqmaxhw = ctlr->upqmax; + len += snprint(p+len, READSTR-len, "up: q %lud i %lud m %d h %d s %lud\n", + ctlr->upqueued, ctlr->upinterrupts, + ctlr->upqmax, ctlr->upqmaxhw, ctlr->upstalls); + ctlr->upqmax = 0; + } + if(ctlr->dnenabled){ + if(ctlr->dnqmax > ctlr->dnqmaxhw) + ctlr->dnqmaxhw = ctlr->dnqmax; + len += snprint(p+len, READSTR-len, "dn: q %lud i %lud m %d h %d\n", + ctlr->dnqueued, ctlr->dninterrupts, ctlr->dnqmax, ctlr->dnqmaxhw); + ctlr->dnqmax = 0; + } + + snprint(p+len, READSTR-len, "badssd: %lud\n", ctlr->stats[BytesRcvdOk+2]); + + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static void +txrxreset(int port) +{ + COMMAND(port, TxReset, 0); + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, RxReset, 0); + while(STATUS(port) & commandInProgress) + ; +} + +static Ctlr* +tcmadapter(int port, int irq, Pcidev* pcidev) +{ + Ctlr *ctlr; + + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = port; + ctlr->irq = irq; + ctlr->pcidev = pcidev; + ctlr->eepromcmd = EepromReadRegister; + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + + return ctlr; +} + +/* + * Write two 0 bytes to identify the IDport and then reset the + * ID sequence. Then send the ID sequence to the card to get + * the card into command state. + */ +static void +idseq(void) +{ + int i; + uchar al; + static int reset, untag; + + /* + * One time only: + * reset any adapters listening + */ + if(reset == 0){ + outb(IDport, 0); + outb(IDport, 0); + outb(IDport, 0xC0); + delay(20); + reset = 1; + } + + outb(IDport, 0); + outb(IDport, 0); + for(al = 0xFF, i = 0; i < 255; i++){ + outb(IDport, al); + if(al & 0x80){ + al <<= 1; + al ^= 0xCF; + } + else + al <<= 1; + } + + /* + * One time only: + * write ID sequence to get the attention of all adapters; + * untag all adapters. + * If a global reset is done here on all adapters it will confuse + * any ISA cards configured for EISA mode. + */ + if(untag == 0){ + outb(IDport, 0xD0); + untag = 1; + } +} + +static ulong +activate(void) +{ + int i; + ushort x, acr; + + /* + * Do the little configuration dance: + * + * 2. write the ID sequence to get to command state. + */ + idseq(); + + /* + * 3. Read the Manufacturer ID from the EEPROM. + * This is done by writing the IDPort with 0x87 (0x80 + * is the 'read EEPROM' command, 0x07 is the offset of + * the Manufacturer ID field in the EEPROM). + * The data comes back 1 bit at a time. + * A delay seems necessary between reading the bits. + * + * If the ID doesn't match, there are no more adapters. + */ + outb(IDport, 0x87); + delay(20); + for(x = 0, i = 0; i < 16; i++){ + delay(20); + x <<= 1; + x |= inb(IDport) & 0x01; + } + if(x != 0x6D50) + return 0; + + /* + * 3. Read the Address Configuration from the EEPROM. + * The Address Configuration field is at offset 0x08 in the EEPROM). + */ + outb(IDport, 0x88); + for(acr = 0, i = 0; i < 16; i++){ + delay(20); + acr <<= 1; + acr |= inb(IDport) & 0x01; + } + + return (acr & 0x1F)*0x10 + 0x200; +} + +static void +tcm509isa(void) +{ + int irq, port; + + /* + * Attempt to activate all adapters. If adapter is set for + * EISA mode (0x3F0), tag it and ignore. Otherwise, activate + * it fully. + */ + while(port = activate()){ + if(ioalloc(port, 0x10, 0, "tcm509isa") < 0){ + print("tcm509isa: port 0x%uX in use\n", port); + continue; + } + + /* + * 6. Tag the adapter so it won't respond in future. + */ + outb(IDport, 0xD1); + if(port == 0x3F0){ + iofree(port); + continue; + } + + /* + * 6. Activate the adapter by writing the Activate command + * (0xFF). + */ + outb(IDport, 0xFF); + delay(20); + + /* + * 8. Can now talk to the adapter's I/O base addresses. + * Use the I/O base address from the acr just read. + * + * Enable the adapter and clear out any lingering status + * and interrupts. + */ + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, SelectRegisterWindow, Wsetup); + outs(port+ConfigControl, Ena); + + txrxreset(port); + COMMAND(port, AcknowledgeInterrupt, 0xFF); + + irq = (ins(port+ResourceConfig)>>12) & 0x0F; + tcmadapter(port, irq, nil); + } +} + +static void +tcm5XXeisa(void) +{ + ushort x; + int irq, port, slot; + + /* + * Check if this is an EISA machine. + * If not, nothing to do. + */ + if(strncmp((char*)KADDR(0xFFFD9), "EISA", 4)) + return; + + /* + * Continue through the EISA slots looking for a match on both + * 3COM as the manufacturer and 3C579-* or 3C59[27]-* as the product. + * If an adapter is found, select window 0, enable it and clear + * out any lingering status and interrupts. + */ + for(slot = 1; slot < MaxEISA; slot++){ + port = slot*0x1000; + if(ioalloc(port, 0x1000, 0, "tcm5XXeisa") < 0){ + print("tcm5XXeisa: port 0x%uX in use\n", port); + continue; + } + if(ins(port+0xC80+ManufacturerID) != 0x6D50){ + iofree(port); + continue; + } + x = ins(port+0xC80+ProductID); + if((x & 0xF0FF) != 0x9050 && (x & 0xFF00) != 0x5900){ + iofree(port); + continue; + } + + COMMAND(port, SelectRegisterWindow, Wsetup); + outs(port+ConfigControl, Ena); + + txrxreset(port); + COMMAND(port, AcknowledgeInterrupt, 0xFF); + + irq = (ins(port+ResourceConfig)>>12) & 0x0F; + tcmadapter(port, irq, nil); + } +} + +static void +tcm59Xpci(void) +{ + Pcidev *p; + Ctlr *ctlr; + int irq, port; + + p = nil; + while(p = pcimatch(p, 0x10B7, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + /* + * Not prepared to deal with memory-mapped + * devices yet. + */ + if(!(p->mem[0].bar & 0x01)) + continue; + port = p->mem[0].bar & ~0x01; + if((port = ioalloc((port == 0)? -1: port, p->mem[0].size, + 0, "tcm59Xpci")) < 0){ + print("tcm59Xpci: port 0x%uX in use\n", port); + continue; + } + irq = p->intl; + + txrxreset(port); + COMMAND(port, AcknowledgeInterrupt, 0xFF); + + ctlr = tcmadapter(port, irq, p); + switch(p->did){ + default: + break; + case 0x5157: + ctlr->eepromcmd = EepromRead8bRegister; + ctlr->cbfnpa = upamalloc(p->mem[2].bar, p->mem[2].size, 0); + break; + case 0x6056: + ctlr->eepromcmd = EepromReadOffRegister; + ctlr->cbfnpa = upamalloc(p->mem[2].bar, p->mem[2].size, 0); + break; + } + if(ctlr->cbfnpa != 0) + ctlr->cbfn = KADDR(ctlr->cbfnpa); + pcisetbme(p); + } +} + +static char* tcmpcmcia[] = { + "3C589", /* 3COM 589[ABCD] */ + "3C562", /* 3COM 562 */ + "589E", /* 3COM Megahertz 589E */ + nil, +}; + +static Ctlr* +tcm5XXpcmcia(Ether* ether) +{ + int i; + Ctlr *ctlr; + + if(ether->type == nil) + return nil; + + for(i = 0; tcmpcmcia[i] != nil; i++){ + if(cistrcmp(ether->type, tcmpcmcia[i])) + continue; + ctlr = tcmadapter(ether->port, ether->irq, nil); + ctlr->active = 1; + return ctlr; + } + + return nil; +} + +static void +setxcvr(Ctlr* ctlr, int xcvr) +{ + int port, x; + + port = ctlr->port; + if(ctlr->rxstatus9){ + COMMAND(port, SelectRegisterWindow, Wsetup); + x = ins(port+AddressConfig) & ~xcvrMask9; + x |= (xcvr>>20)<<14; + outs(port+AddressConfig, x); + } + else{ + COMMAND(port, SelectRegisterWindow, Wfifo); + x = inl(port+InternalConfig) & ~xcvrMask; + x |= xcvr; + outl(port+InternalConfig, x); + } + + txrxreset(port); +} + +static void +setfullduplex(int port) +{ + int x; + + COMMAND(port, SelectRegisterWindow, Wfifo); + x = ins(port+MacControl); + outs(port+MacControl, fullDuplexEnable|x); + + txrxreset(port); +} + +static int +miimdi(int port, int n) +{ + int data, i; + + /* + * Read n bits from the MII Management Register. + */ + data = 0; + for(i = n-1; i >= 0; i--){ + if(ins(port) & mgmtData) + data |= (1<= 0; i--){ + if(bits & (1<>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + port += PhysicalMgmt; + + /* + * Preamble; + * ST+OP+PHYAD+REGAD; + * TA + 16 data bits. + */ + miimdo(port, 0xFFFFFFFF, 32); + miimdo(port, 0x1800|(phyad<<5)|regad, 14); + data = miimdi(port, 18); + + port -= PhysicalMgmt; + COMMAND(port, SelectRegisterWindow, w); + + if(data & 0x10000) + return -1; + + return data & 0xFFFF; +} + +static int +scanphy(int port) +{ + int i, x; + + for(i = 0; i < 32; i++){ + if((x = miir(port, i, 2)) == -1 || x == 0) + continue; + x <<= 6; + x |= miir(port, i, 3)>>10; + XCVRDEBUG("phy%d: oui %uX reg1 %uX\n", i, x, miir(port, i, 1)); + USED(x); + + return i; + } + return 24; +} + +static struct { + char *name; + int avail; + int xcvr; +} media[] = { + "10BaseT", base10TAvailable, xcvr10BaseT, + "10Base2", coaxAvailable, xcvr10Base2, + "100BaseTX", baseTXAvailable, xcvr100BaseTX, + "100BaseFX", baseFXAvailable, xcvr100BaseFX, + "aui", auiAvailable, xcvrAui, + "mii", miiConnector, xcvrMii +}; + +static int +autoselect(Ctlr* ctlr) +{ + int media, port, x; + + /* + * Pathetic attempt at automatic media selection. + * Really just to get the Fast Etherlink 10BASE-T/100BASE-TX + * cards operational. + * It's a bonus if it works for anything else. + */ + port = ctlr->port; + if(ctlr->rxstatus9){ + COMMAND(port, SelectRegisterWindow, Wsetup); + x = ins(port+ConfigControl); + media = 0; + if(x & base10TAvailable9) + media |= base10TAvailable; + if(x & coaxAvailable9) + media |= coaxAvailable; + if(x & auiAvailable9) + media |= auiAvailable; + } + else{ + COMMAND(port, SelectRegisterWindow, Wfifo); + media = ins(port+ResetOptions); + } + XCVRDEBUG("autoselect: media %uX\n", media); + + if(media & miiConnector) + return xcvrMii; + + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + XCVRDEBUG("autoselect: media status %uX\n", ins(port+MediaStatus)); + + if(media & baseTXAvailable){ + /* + * Must have InternalConfig register. + */ + setxcvr(ctlr, xcvr100BaseTX); + + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable); + outs(port+MediaStatus, linkBeatEnable|x); + delay(10); + + if(ins(port+MediaStatus) & linkBeatDetect) + return xcvr100BaseTX; + outs(port+MediaStatus, x); + } + + if(media & base10TAvailable){ + setxcvr(ctlr, xcvr10BaseT); + + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~dcConverterEnabled; + outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x); + delay(100); + + XCVRDEBUG("autoselect: 10BaseT media status %uX\n", ins(port+MediaStatus)); + if(ins(port+MediaStatus) & linkBeatDetect) + return xcvr10BaseT; + outs(port+MediaStatus, x); + } + + /* + * Botch. + */ + return autoSelect; +} + +static int +eepromdata(Ctlr* ctlr, int offset) +{ + int port; + + port = ctlr->port; + + COMMAND(port, SelectRegisterWindow, Wsetup); + while(EEPROMBUSY(port)) + ; + EEPROMCMD(port, ctlr->eepromcmd, offset); + while(EEPROMBUSY(port)) + ; + return EEPROMDATA(port); +} + +static void +resetctlr(Ctlr *ctlr) +{ + int x, port = ctlr->port; + + txrxreset(port); + x = ins(port+ResetOp905B); + XCVRDEBUG("905[BC] reset ops 0x%uX\n", x); + x &= ~0x4010; + if(ctlr->did == 0x5157){ + x |= 0x0010; /* Invert LED */ + outs(port+ResetOp905B, x); + } + if(ctlr->did == 0x6056){ + x |= 0x4000; + outs(port+ResetOp905B, x); + + COMMAND(port, SelectRegisterWindow, Wsetup); + outs(port, 0x0800); + } +} + +static void +shutdown(Ether *ether) +{ +print("etherelnk3 shutting down\n"); + resetctlr(ether->ctlr); +} + +int +etherelnk3reset(Ether* ether) +{ + char *p; + Ctlr *ctlr; + uchar ea[Eaddrlen]; + static int scandone; + int anar, anlpar, i, j, phyaddr, phystat, port, timeo, x; + + /* + * Scan for adapter on PCI, EISA and finally + * using the little ISA configuration dance. + */ + if(scandone == 0){ + tcm59Xpci(); + tcm5XXeisa(); + tcm509isa(); + scandone = 1; + } + + /* + * Any adapter matches if no ether->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(ether->port == 0 || ether->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil && (ctlr = tcm5XXpcmcia(ether)) == 0) + return -1; + + ether->ctlr = ctlr; + port = ctlr->port; + ether->port = port; + ether->irq = ctlr->irq; + if(ctlr->pcidev != nil) + ether->tbdf = ctlr->pcidev->tbdf; + else + ether->tbdf = BUSUNKNOWN; + + /* + * Read the DeviceID from the EEPROM, it's at offset 0x03, + * and do something depending on capabilities. + */ + switch(ctlr->did = eepromdata(ctlr, 0x03)){ + case 0x5157: /* 3C575 Cyclone */ + case 0x6056: + /*FALLTHROUGH*/ + case 0x4500: /* 3C450 HomePNA Tornado */ + case 0x7646: /* 3CSOHO100-TX */ + case 0x9055: /* 3C905B-TX */ + case 0x9200: /* 3C905C-TX */ + case 0x9201: /* 3C920 */ + /*FALLTHROUGH*/ + case 0x9000: /* 3C900-TPO */ + case 0x9001: /* 3C900-COMBO */ + case 0x9005: /* 3C900B-COMBO */ + case 0x9050: /* 3C905-TX */ + case 0x9051: /* 3C905-T4 */ + if(BUSTYPE(ether->tbdf) != BusPCI) + goto buggery; + ctlr->busmaster = 2; + goto vortex; + case 0x5900: /* 3C590-[TP|COMBO|TPO] */ + case 0x5920: /* 3C592-[TP|COMBO|TPO] */ + case 0x5950: /* 3C595-TX */ + case 0x5951: /* 3C595-T4 */ + case 0x5952: /* 3C595-MII */ + case 0x5970: /* 3C597-TX */ + case 0x5971: /* 3C597-T4 */ + case 0x5972: /* 3C597-MII */ + ctlr->busmaster = 1; + vortex: + COMMAND(port, SelectRegisterWindow, Wfifo); + ctlr->xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask); + ctlr->rxearly = 8188; + ctlr->rxstatus9 = 0; + break; + buggery: + default: + ctlr->busmaster = 0; + COMMAND(port, SelectRegisterWindow, Wsetup); + x = ins(port+AddressConfig); + ctlr->xcvr = ((x & xcvrMask9)>>14)<<20; + if(x & autoSelect9) + ctlr->xcvr |= autoSelect; + ctlr->rxearly = 2044; + ctlr->rxstatus9 = 1; + break; + } + if(ctlr->rxearly >= 2048) + ctlr->ts = 2; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to + * loading the station address in Wstation. + * The EEPROM returns 16-bits at a time. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + for(i = 0; i < Eaddrlen/2; i++){ + x = eepromdata(ctlr, i); + ether->ea[2*i] = x>>8; + ether->ea[2*i+1] = x; + } + } + + COMMAND(port, SelectRegisterWindow, Wstation); + for(i = 0; i < Eaddrlen; i++) + outb(port+i, ether->ea[i]); + + /* + * Enable the transceiver if necessary and determine whether + * busmastering can be used. Due to bugs in the first revision + * of the 3C59[05], don't use busmastering at 10Mbps. + */ + XCVRDEBUG("reset: xcvr %uX\n", ctlr->xcvr); + + /* + * Allow user to specify desired media in plan9.ini + */ + for(i = 0; i < ether->nopt; i++){ + if(cistrncmp(ether->opt[i], "media=", 6) != 0) + continue; + p = ether->opt[i]+6; + for(j = 0; j < nelem(media); j++) + if(cistrcmp(p, media[j].name) == 0) + ctlr->xcvr = media[j].xcvr; + } + + /* + * forgive me, but i am weak + */ + switch(ctlr->did){ + default: + if(ctlr->xcvr & autoSelect) + ctlr->xcvr = autoselect(ctlr); + break; + case 0x5157: + case 0x6056: + case 0x4500: + case 0x7646: + case 0x9055: + case 0x9200: + case 0x9201: + ctlr->xcvr = xcvrMii; + resetctlr(ctlr); + break; + } + XCVRDEBUG("xcvr selected: %uX, did 0x%uX\n", ctlr->xcvr, ctlr->did); + + switch(ctlr->xcvr){ + case xcvrMii: + /* + * Quick hack. + */ + if(ctlr->did == 0x5157) + phyaddr = 0; + else if(ctlr->did == 0x6056) + phyaddr = scanphy(port); + else + phyaddr = 24; + for(i = 0; i < 7; i++) + XCVRDEBUG(" %2.2uX", miir(port, phyaddr, i)); + XCVRDEBUG("\n"); + + for(timeo = 0; timeo < 30; timeo++){ + phystat = miir(port, phyaddr, 0x01); + if(phystat & 0x20) + break; + XCVRDEBUG(" %2.2uX", phystat); + delay(100); + } + XCVRDEBUG(" %2.2uX", miir(port, phyaddr, 0x01)); + XCVRDEBUG("\n"); + + anar = miir(port, phyaddr, 0x04); + anlpar = miir(port, phyaddr, 0x05) & 0x03E0; + anar &= anlpar; + miir(port, phyaddr, 0x00); + XCVRDEBUG("mii an: %uX anlp: %uX r0:%uX r1:%uX\n", + anar, anlpar, miir(port, phyaddr, 0x00), + miir(port, phyaddr, 0x01)); + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "fullduplex") == 0) + anar |= 0x0100; + else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0) + anar |= 0x0100; + else if(cistrcmp(ether->opt[i], "force100") == 0) + anar |= 0x0080; + } + XCVRDEBUG("mii anar: %uX\n", anar); + if(anar & 0x0100){ /* 100BASE-TXFD */ + ether->mbps = 100; + setfullduplex(port); + } + else if(anar & 0x0200){ /* 100BASE-T4 */ + /* nothing to do */ + } + else if(anar & 0x0080) /* 100BASE-TX */ + ether->mbps = 100; + else if(anar & 0x0040) /* 10BASE-TFD */ + setfullduplex(port); + else{ /* 10BASE-T */ + /* nothing to do */ + } + break; + case xcvr100BaseTX: + case xcvr100BaseFX: + COMMAND(port, SelectRegisterWindow, Wfifo); + x = inl(port+InternalConfig) & ~ramPartitionMask; + outl(port+InternalConfig, x|ramPartition1to1); + + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable); + x |= linkBeatEnable; + outs(port+MediaStatus, x); + + if(x & dataRate100) + ether->mbps = 100; + break; + case xcvr10BaseT: + /* + * Enable Link Beat and Jabber to start the + * transceiver. + */ + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~dcConverterEnabled; + x |= linkBeatEnable|jabberGuardEnable; + outs(port+MediaStatus, x); + + if((ctlr->did & 0xFF00) == 0x5900) + ctlr->busmaster = 0; + break; + case xcvr10Base2: + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable); + outs(port+MediaStatus, x); + + /* + * Start the DC-DC converter. + * Wait > 800 microseconds. + */ + COMMAND(port, EnableDcConverter, 0); + delay(1); + break; + } + + /* + * Wop is the normal operating register set. + * The 3C59[0257] adapters allow access to more than one register window + * at a time, but there are situations where switching still needs to be + * done, so just do it. + * Clear out any lingering Tx status. + */ + COMMAND(port, SelectRegisterWindow, Wop); + if(ctlr->busmaster == 2) + x = port+TxStatus905; + else + x = port+TxStatus; + while(inb(x)) + outb(x, 0); + + /* + * Clear out the + * adapter statistics, clear the statistics logged into ctlr + * and enable statistics collection. + */ + ilock(&ctlr->wlock); + statistics(ether); + memset(ctlr->stats, 0, sizeof(ctlr->stats)); + + COMMAND(port, StatisticsEnable, 0); + + /* + * Allocate any receive buffers. + */ + switch(ctlr->busmaster){ + case 2: + ctlr->dnenabled = 1; + + /* + * 10MUpldBug. + * Disabling is too severe, can use receive busmastering at + * 100Mbps OK, but how to tell which rate is actually being used - + * the 3c905 always seems to have dataRate100 set? + * Believe the bug doesn't apply if upRxEarlyEnable is set + * and the threshold is set such that uploads won't start + * until the whole packet has been received. + */ + ctlr->upenabled = 1; + x = eepromdata(ctlr, 0x0F); + if(!(x & 0x01)) + outl(port+PktStatus, upRxEarlyEnable); + + if(ctlr->upenabled || ctlr->dnenabled){ + ctlr->nup = Nup; + ctlr->ndn = Ndn; + init905(ctlr); + } + else { + ctlr->rbp = rbpalloc(iallocb); + if(ctlr->rbp == nil) + panic("can't reset ethernet: out of memory"); + } + outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256)); + break; + default: + ctlr->rbp = rbpalloc(iallocb); + if(ctlr->rbp == nil) + panic("can't reset ethernet: out of memory"); + break; + } + + /* + * Set a base TxStartThresh which will be incremented + * if any txUnderrun errors occur and ensure no RxEarly + * interrupts happen. + */ + ctlr->txthreshold = ETHERMAXTU/2; + COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts); + COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts); + + iunlock(&ctlr->wlock); + + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = ifstat; + + ether->promiscuous = promiscuous; + ether->multicast = multicast; + ether->shutdown = shutdown; + ether->arg = ether; + + return 0; +} + +void +etherelnk3link(void) +{ + addethercard("elnk3", etherelnk3reset); + addethercard("3C509", etherelnk3reset); + addethercard("3C575", etherelnk3reset); +} diff --git a/os/pc/etherga620.c b/os/pc/etherga620.c new file mode 100644 index 00000000..45e34faf --- /dev/null +++ b/os/pc/etherga620.c @@ -0,0 +1,1224 @@ +/* + * Netgear GA620 Gigabit Ethernet Card. + * Specific for the Alteon Tigon 2 and Intel Pentium or later. + * To Do: + * cache alignment for PCI Write-and-Invalidate + * mini ring (what size)? + * tune coalescing values + * statistics formatting + * don't update Spi if nothing to send + * receive ring alignment + * watchdog for link management? + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#define malign(n) xspanalloc((n), 32, 0) + +#include "etherif.h" +#include "etherga620fw.h" + +enum { + Mhc = 0x0040, /* Miscellaneous Host Control */ + Mlc = 0x0044, /* Miscellaneous Local Control */ + Mc = 0x0050, /* Miscellaneous Configuration */ + Ps = 0x005C, /* PCI State */ + Wba = 0x0068, /* Window Base Address */ + Wd = 0x006C, /* Window Data */ + + DMAas = 0x011C, /* DMA Assist State */ + + CPUAstate = 0x0140, /* CPU A State */ + CPUApc = 0x0144, /* CPU A Programme Counter */ + + CPUBstate = 0x0240, /* CPU B State */ + + Hi = 0x0504, /* Host In Interrupt Handler */ + Cpi = 0x050C, /* Command Producer Index */ + Spi = 0x0514, /* Send Producer Index */ + Rspi = 0x051C, /* Receive Standard Producer Index */ + Rjpi = 0x0524, /* Receive Jumbo Producer Index */ + Rmpi = 0x052C, /* Receive Mini Producer Index */ + + Mac = 0x0600, /* MAC Address */ + Gip = 0x0608, /* General Information Pointer */ + Om = 0x0618, /* Operating Mode */ + DMArc = 0x061C, /* DMA Read Configuration */ + DMAwc = 0x0620, /* DMA Write Configuration */ + Tbr = 0x0624, /* Transmit Buffer Ratio */ + Eci = 0x0628, /* Event Consumer Index */ + Cci = 0x062C, /* Command Consumer Index */ + + Rct = 0x0630, /* Receive Coalesced Ticks */ + Sct = 0x0634, /* Send Coalesced Ticks */ + St = 0x0638, /* Stat Ticks */ + SmcBD = 0x063C, /* Send Max. Coalesced BDs */ + RmcBD = 0x0640, /* Receive Max. Coalesced BDs */ + Nt = 0x0644, /* NIC Tracing */ + Gln = 0x0648, /* Gigabit Link Negotiation */ + Fln = 0x064C, /* 10/100 Link Negotiation */ + Ifx = 0x065C, /* Interface Index */ + IfMTU = 0x0660, /* Interface MTU */ + Mi = 0x0664, /* Mask Interrupts */ + Gls = 0x0668, /* Gigabit Link State */ + Fls = 0x066C, /* 10/100 Link State */ + + Cr = 0x0700, /* Command Ring */ + + Lmw = 0x0800, /* Local Memory Window */ +}; + +enum { /* Mhc */ + Is = 0x00000001, /* Interrupt State */ + Ci = 0x00000002, /* Clear Interrupt */ + Hr = 0x00000008, /* Hard Reset */ + Eebs = 0x00000010, /* Enable Endian Byte Swap */ + Eews = 0x00000020, /* Enable Endian Word (64-bit) swap */ + Mpio = 0x00000040, /* Mask PCI Interrupt Output */ +}; + +enum { /* Mlc */ + SRAM512 = 0x00000200, /* SRAM Bank Size of 512KB */ + SRAMmask = 0x00000300, + EEclk = 0x00100000, /* Serial EEPROM Clock Output */ + EEdoe = 0x00200000, /* Serial EEPROM Data Out Enable */ + EEdo = 0x00400000, /* Serial EEPROM Data Out Value */ + EEdi = 0x00800000, /* Serial EEPROM Data Input */ +}; + +enum { /* Mc */ + SyncSRAM = 0x00100000, /* Set Synchronous SRAM Timing */ +}; + +enum { /* Ps */ + PCIwm32 = 0x000000C0, /* Write Max DMA 32 */ + PCImrm = 0x00020000, /* Use Memory Read Multiple Command */ + PCI66 = 0x00080000, + PCI32 = 0x00100000, + PCIrcmd = 0x06000000, /* PCI Read Command */ + PCIwcmd = 0x70000000, /* PCI Write Command */ +}; + +enum { /* CPUAstate */ + CPUrf = 0x00000010, /* ROM Fail */ + CPUhalt = 0x00010000, /* Halt the internal CPU */ + CPUhie = 0x00040000, /* HALT instruction executed */ +}; + +enum { /* Om */ + BswapBD = 0x00000002, /* Byte Swap Buffer Descriptors */ + WswapBD = 0x00000004, /* Word Swap Buffer Descriptors */ + Warn = 0x00000008, + BswapDMA = 0x00000010, /* Byte Swap DMA Data */ + Only1DMA = 0x00000040, /* Only One DMA Active at a time */ + NoJFrag = 0x00000200, /* Don't Fragment Jumbo Frames */ + Fatal = 0x40000000, +}; + +enum { /* Lmw */ + Lmwsz = 2*1024, /* Local Memory Window Size */ + + Sr = 0x3800, /* Send Ring (accessed via Lmw) */ +}; + +enum { /* Link */ + Lpref = 0x00008000, /* Preferred Link */ + L10MB = 0x00010000, + L100MB = 0x00020000, + L1000MB = 0x00040000, + Lfd = 0x00080000, /* Full Duplex */ + Lhd = 0x00100000, /* Half Duplex */ + Lefc = 0x00200000, /* Emit Flow Control Packets */ + Lofc = 0x00800000, /* Obey Flow Control Packets */ + Lean = 0x20000000, /* Enable Autonegotiation/Sensing */ + Le = 0x40000000, /* Link Enable */ +}; + +typedef struct Host64 { + uint hi; + uint lo; +} Host64; + +typedef struct Ere { /* Event Ring Element */ + int event; /* (event<<24)|(code<<12)|index */ + int unused; +} Ere; + +typedef int Cmd; /* (cmd<<24)|(flags<<12)|index */ + +typedef struct Rbd { /* Receive Buffer Descriptor */ + Host64 addr; + int indexlen; /* (ring-index<<16)|buffer-length */ + int flags; /* only lower 16-bits */ + int checksum; /* (ip<<16)|tcp/udp */ + int error; /* only upper 16-bits */ + int reserved; + void* opaque; /* passed to receive return ring */ +} Rbd; + +typedef struct Sbd { /* Send Buffer Descriptor */ + Host64 addr; + int lenflags; /* (len<<16)|flags */ + int reserved; +} Sbd; + +enum { /* Buffer Descriptor Flags */ + Fend = 0x00000004, /* Frame Ends in this Buffer */ + Frjr = 0x00000010, /* Receive Jumbo Ring Buffer */ + Funicast = 0x00000020, /* Unicast packet (2-bit field) */ + Fmulticast = 0x00000040, /* Multicast packet */ + Fbroadcast = 0x00000060, /* Broadcast packet */ + Ferror = 0x00000400, /* Frame Has Error */ + Frmr = 0x00001000, /* Receive Mini Ring Buffer */ +}; + +enum { /* Buffer Error Flags */ + Ecrc = 0x00010000, /* bad CRC */ + Ecollision = 0x00020000, /* collision */ + Elink = 0x00040000, /* link lost */ + Ephy = 0x00080000, /* unspecified PHY frame decode error */ + Eodd = 0x00100000, /* odd number of nibbles */ + Emac = 0x00200000, /* unspecified MAC abort */ + Elen64 = 0x00400000, /* short packet */ + Eresources = 0x00800000, /* MAC out of internal resources */ + Egiant = 0x01000000, /* packet too big */ +}; + +typedef struct Rcb { /* Ring Control Block */ + Host64 addr; /* points to the Rbd ring */ + int control; /* (max_len<<16)|flags */ + int unused; +} Rcb; + +enum { + TcpUdpCksum = 0x0001, /* Perform TCP or UDP checksum */ + IpCksum = 0x0002, /* Perform IP checksum */ + NoPseudoHdrCksum= 0x0008, /* Don't include the pseudo header */ + VlanAssist = 0x0010, /* Enable VLAN tagging */ + CoalUpdateOnly = 0x0020, /* Coalesce transmit interrupts */ + HostRing = 0x0040, /* Sr in host memory */ + SnapCksum = 0x0080, /* Parse + offload 802.3 SNAP frames */ + UseExtRxBd = 0x0100, /* Extended Rbd for Jumbo frames */ + RingDisabled = 0x0200, /* Jumbo or Mini RCB only */ +}; + +typedef struct Gib { /* General Information Block */ + int statistics[256]; /* Statistics */ + Rcb ercb; /* Event Ring */ + Rcb crcb; /* Command Ring */ + Rcb srcb; /* Send Ring */ + Rcb rsrcb; /* Receive Standard Ring */ + Rcb rjrcb; /* Receive Jumbo Ring */ + Rcb rmrcb; /* Receive Mini Ring */ + Rcb rrrcb; /* Receive Return Ring */ + Host64 epp; /* Event Producer */ + Host64 rrrpp; /* Receive Return Ring Producer */ + Host64 scp; /* Send Consumer */ + Host64 rsp; /* Refresh Stats */ +} Gib; + +enum { /* Host/NIC Interface ring sizes */ + Ner = 256, /* event ring */ + Ncr = 64, /* command ring */ + Nsr = 512, /* send ring */ + Nrsr = 512, /* receive standard ring */ + Nrjr = 256, /* receive jumbo ring */ + Nrmr = 1024, /* receive mini ring */ + Nrrr = 2048, /* receive return ring */ +}; + +enum { + NrsrHI = 72, /* Fill-level of Rsr (m.b. < Nrsr) */ + NrsrLO = 54, /* Level at which to top-up ring */ + NrjrHI = 0, /* Fill-level of Rjr (m.b. < Nrjr) */ + NrjrLO = 0, /* Level at which to top-up ring */ + NrmrHI = 0, /* Fill-level of Rmr (m.b. < Nrmr) */ + NrmrLO = 0, /* Level at which to top-up ring */ +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; + + uchar ea[Eaddrlen]; + + int* nic; + Gib* gib; + + Ere* er; + + Lock srlock; + Sbd* sr; + Block** srb; + int nsr; /* currently in send ring */ + + Rbd* rsr; + int nrsr; /* currently in Receive Standard Ring */ + Rbd* rjr; + int nrjr; /* currently in Receive Jumbo Ring */ + Rbd* rmr; + int nrmr; /* currently in Receive Mini Ring */ + Rbd* rrr; + int rrrci; /* Receive Return Ring Consumer Index */ + + int epi[2]; /* Event Producer Index */ + int rrrpi[2]; /* Receive Return Ring Producer Index */ + int sci[3]; /* Send Consumer Index ([2] is host) */ + + int interrupts; /* statistics */ + int mi; + uvlong ticks; + + int coalupdateonly; /* tuning */ + int hardwarecksum; + int rct; /* Receive Coalesce Ticks */ + int sct; /* Send Coalesce Ticks */ + int st; /* Stat Ticks */ + int smcbd; /* Send Max. Coalesced BDs */ + int rmcbd; /* Receive Max. Coalesced BDs */ +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +#define csr32r(c, r) (*((c)->nic+((r)/4))) +#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v)) + +static void +sethost64(Host64* host64, void* addr) +{ + uvlong uvl; + + uvl = PCIWADDR(addr); + host64->hi = uvl>>32; + host64->lo = uvl & 0xFFFFFFFFL; +} + +static void +ga620command(Ctlr* ctlr, int cmd, int flags, int index) +{ + int cpi; + + cpi = csr32r(ctlr, Cpi); + csr32w(ctlr, Cr+(cpi*4), (cmd<<24)|(flags<<12)|index); + cpi = NEXT(cpi, Ncr); + csr32w(ctlr, Cpi, cpi); +} + +static void +ga620attach(Ether* edev) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + USED(ctlr); +} + +static long +ga620ifstat(Ether* edev, void* a, long n, ulong offset) +{ + char *p; + Ctlr *ctlr; + int i, l, r; + + ctlr = edev->ctlr; + + if(n == 0) + return 0; + p = malloc(READSTR); + l = 0; + for(i = 0; i < 256; i++){ + if((r = ctlr->gib->statistics[i]) == 0) + continue; + l += snprint(p+l, READSTR-l, "%d: %ud\n", i, r); + } + + l += snprint(p+l, READSTR-l, "interrupts: %ud\n", ctlr->interrupts); + l += snprint(p+l, READSTR-l, "mi: %ud\n", ctlr->mi); + l += snprint(p+l, READSTR-l, "ticks: %llud\n", ctlr->ticks); + l += snprint(p+l, READSTR-l, "coalupdateonly: %d\n", ctlr->coalupdateonly); + l += snprint(p+l, READSTR-l, "hardwarecksum: %d\n", ctlr->hardwarecksum); + l += snprint(p+l, READSTR-l, "rct: %d\n", ctlr->rct); + l += snprint(p+l, READSTR-l, "sct: %d\n", ctlr->sct); + l += snprint(p+l, READSTR-l, "smcbd: %d\n", ctlr->smcbd); + snprint(p+l, READSTR-l, "rmcbd: %d\n", ctlr->rmcbd); + + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static long +ga620ctl(Ether* edev, void* buf, long n) +{ + char *p; + Cmdbuf *cb; + Ctlr *ctlr; + int control, i, r; + + ctlr = edev->ctlr; + if(ctlr == nil) + error(Enonexist); + r = 0; + cb = parsecmd(buf, n); + if(cb->nf < 2) + r = -1; + else if(cistrcmp(cb->f[0], "coalupdateonly") == 0){ + if(cistrcmp(cb->f[1], "off") == 0){ + control = ctlr->gib->srcb.control; + control &= ~CoalUpdateOnly; + ctlr->gib->srcb.control = control; + ctlr->coalupdateonly = 0; + } + else if(cistrcmp(cb->f[1], "on") == 0){ + control = ctlr->gib->srcb.control; + control |= CoalUpdateOnly; + ctlr->gib->srcb.control = control; + ctlr->coalupdateonly = 1; + } + else + r = -1; + } + else if(cistrcmp(cb->f[0], "hardwarecksum") == 0){ + if(cistrcmp(cb->f[1], "off") == 0){ + control = ctlr->gib->srcb.control; + control &= ~(TcpUdpCksum|NoPseudoHdrCksum); + ctlr->gib->srcb.control = control; + + control = ctlr->gib->rsrcb.control; + control &= ~(TcpUdpCksum|NoPseudoHdrCksum); + ctlr->gib->rsrcb.control = control; + + ctlr->hardwarecksum = 0; + } + else if(cistrcmp(cb->f[1], "on") == 0){ + control = ctlr->gib->srcb.control; + control |= (TcpUdpCksum|NoPseudoHdrCksum); + ctlr->gib->srcb.control = control; + + control = ctlr->gib->rsrcb.control; + control |= (TcpUdpCksum|NoPseudoHdrCksum); + ctlr->gib->rsrcb.control = control; + + ctlr->hardwarecksum = 1; + } + else + r = -1; + } + else if(cistrcmp(cb->f[0], "rct") == 0){ + i = strtol(cb->f[1], &p, 0); + if(i < 0 || p == cb->f[1]) + r = -1; + else{ + ctlr->rct = i; + csr32w(ctlr, Rct, ctlr->rct); + } + } + else if(cistrcmp(cb->f[0], "sct") == 0){ + i = strtol(cb->f[1], &p, 0); + if(i < 0 || p == cb->f[1]) + r = -1; + else{ + ctlr->sct = i; + csr32w(ctlr, Sct, ctlr->sct); + } + } + else if(cistrcmp(cb->f[0], "st") == 0){ + i = strtol(cb->f[1], &p, 0); + if(i < 0 || p == cb->f[1]) + r = -1; + else{ + ctlr->st = i; + csr32w(ctlr, St, ctlr->st); + } + } + else if(cistrcmp(cb->f[0], "smcbd") == 0){ + i = strtol(cb->f[1], &p, 0); + if(i < 0 || p == cb->f[1]) + r = -1; + else{ + ctlr->smcbd = i; + csr32w(ctlr, SmcBD, ctlr->smcbd); + } + } + else if(cistrcmp(cb->f[0], "rmcbd") == 0){ + i = strtol(cb->f[1], &p, 0); + if(i < 0 || p == cb->f[1]) + r = -1; + else{ + ctlr->rmcbd = i; + csr32w(ctlr, RmcBD, ctlr->rmcbd); + } + } + else + r = -1; + + free(cb); + if(r == 0) + return n; + return r; +} + +static int +_ga620transmit(Ether* edev) +{ + Sbd *sbd; + Block *bp; + Ctlr *ctlr; + int sci, spi, work; + + /* + * For now there are no smarts here, just empty the + * ring and try to fill it back up. Tuning comes later. + */ + ctlr = edev->ctlr; + ilock(&ctlr->srlock); + + /* + * Free any completed packets. + * Ctlr->sci[0] is where the NIC has got to consuming the ring. + * Ctlr->sci[2] is where the host has got to tidying up after the + * NIC has done with the packets. + */ + work = 0; + for(sci = ctlr->sci[2]; sci != ctlr->sci[0]; sci = NEXT(sci, Nsr)){ + if(ctlr->srb[sci] == nil) + continue; + freeb(ctlr->srb[sci]); + ctlr->srb[sci] = nil; + work++; + } + ctlr->sci[2] = sci; + + sci = PREV(sci, Nsr); + for(spi = csr32r(ctlr, Spi); spi != sci; spi = NEXT(spi, Nsr)){ + if((bp = qget(edev->oq)) == nil) + break; + + sbd = &ctlr->sr[spi]; + sethost64(&sbd->addr, bp->rp); + sbd->lenflags = (BLEN(bp)<<16)|Fend; + + ctlr->srb[spi] = bp; + work++; + } + csr32w(ctlr, Spi, spi); + + iunlock(&ctlr->srlock); + + return work; +} + +static void +ga620transmit(Ether* edev) +{ + _ga620transmit(edev); +} + +static void +ga620replenish(Ctlr* ctlr) +{ + Rbd *rbd; + int rspi; + Block *bp; + + rspi = csr32r(ctlr, Rspi); + while(ctlr->nrsr < NrsrHI){ + if((bp = iallocb(ETHERMAXTU+4)) == nil) + break; + rbd = &ctlr->rsr[rspi]; + sethost64(&rbd->addr, bp->rp); + rbd->indexlen = (rspi<<16)|(ETHERMAXTU+4); + rbd->flags = 0; + rbd->opaque = bp; + + rspi = NEXT(rspi, Nrsr); + ctlr->nrsr++; + } + csr32w(ctlr, Rspi, rspi); +} + +static void +ga620event(Ctlr* ctlr, int eci, int epi) +{ + int event; + + while(eci != epi){ + event = ctlr->er[eci].event; + switch(event>>24){ + case 0x01: /* firmware operational */ + ga620command(ctlr, 0x01, 0x01, 0x00); + ga620command(ctlr, 0x0B, 0x00, 0x00); +print("%8.8uX: %8.8uX\n", ctlr->port, event); + break; + case 0x04: /* statistics updated */ + break; + case 0x06: /* link state changed */ +print("%8.8uX: %8.8uX %8.8uX %8.8uX\n", + ctlr->port, event, csr32r(ctlr, Gls), csr32r(ctlr, Fls)); + break; + case 0x07: /* event error */ + default: + print("er[%d] = %8.8uX\n", eci, event); + break; + } + eci = NEXT(eci, Ner); + } + csr32w(ctlr, Eci, eci); +} + +static void +ga620receive(Ether* edev) +{ + int len; + Rbd *rbd; + Block *bp; + Ctlr* ctlr; + + ctlr = edev->ctlr; + while(ctlr->rrrci != ctlr->rrrpi[0]){ + rbd = &ctlr->rrr[ctlr->rrrci]; + /* + * Errors are collected in the statistics block so + * no need to tally them here, let ifstat do the work. + */ + len = rbd->indexlen & 0xFFFF; + if(!(rbd->flags & Ferror) && len != 0){ + bp = rbd->opaque; + bp->wp = bp->rp+len; + etheriq(edev, bp, 1); + } + else + freeb(rbd->opaque); + rbd->opaque = nil; + + if(rbd->flags & Frjr) + ctlr->nrjr--; + else if(rbd->flags & Frmr) + ctlr->nrmr--; + else + ctlr->nrsr--; + + ctlr->rrrci = NEXT(ctlr->rrrci, Nrrr); + } +} + +static void +ga620interrupt(Ureg*, void* arg) +{ + int csr, ie, work; + Ctlr *ctlr; + Ether *edev; + uvlong tsc0, tsc1; + + edev = arg; + ctlr = edev->ctlr; + + if(!(csr32r(ctlr, Mhc) & Is)) + return; + cycles(&tsc0); + + ctlr->interrupts++; + csr32w(ctlr, Hi, 1); + + ie = 0; + work = 0; + while(ie < 2){ + if(ctlr->rrrci != ctlr->rrrpi[0]){ + ga620receive(edev); + work = 1; + } + + if(_ga620transmit(edev) != 0) + work = 1; + + csr = csr32r(ctlr, Eci); + if(csr != ctlr->epi[0]){ + ga620event(ctlr, csr, ctlr->epi[0]); + work = 1; + } + + if(ctlr->nrsr <= NrsrLO) + ga620replenish(ctlr); + if(work == 0){ + if(ie == 0) + csr32w(ctlr, Hi, 0); + ie++; + } + work = 0; + } + + cycles(&tsc1); + ctlr->ticks += tsc1-tsc0; +} + +static void +ga620lmw(Ctlr* ctlr, int addr, int* data, int len) +{ + int i, l, lmw, v; + + /* + * Write to or clear ('data' == nil) 'len' bytes of the NIC + * local memory at address 'addr'. + * The destination address and count should be 32-bit aligned. + */ + v = 0; + while(len > 0){ + /* + * 1) Set the window. The (Lmwsz-1) bits are ignored + * in Wba when accessing through the local memory window; + * 2) Find the minimum of how many bytes still to + * transfer and how many left in this window; + * 3) Create the offset into the local memory window in the + * shared memory space then copy (or zero) the data; + * 4) Bump the counts. + */ + csr32w(ctlr, Wba, addr); + + l = ROUNDUP(addr+1, Lmwsz) - addr; + if(l > len) + l = len; + + lmw = Lmw + (addr & (Lmwsz-1)); + for(i = 0; i < l; i += 4){ + if(data != nil) + v = *data++; + csr32w(ctlr, lmw+i, v); + } + + len -= l; + addr += l; + } +} + +static int +ga620init(Ether* edev) +{ + Ctlr *ctlr; + Host64 host64; + int csr, ea, i, flags; + + ctlr = edev->ctlr; + + /* + * Load the MAC address. + */ + ea = (edev->ea[0]<<8)|edev->ea[1]; + csr32w(ctlr, Mac, ea); + ea = (edev->ea[2]<<24)|(edev->ea[3]<<16)|(edev->ea[4]<<8)|edev->ea[5]; + csr32w(ctlr, Mac+4, ea); + + /* + * General Information Block. + */ + ctlr->gib = malloc(sizeof(Gib)); + sethost64(&host64, ctlr->gib); + csr32w(ctlr, Gip, host64.hi); + csr32w(ctlr, Gip+4, host64.lo); + + /* + * Event Ring. + * This is located in host memory. Allocate the ring, + * tell the NIC where it is and initialise the indices. + */ + ctlr->er = malign(sizeof(Ere)*Ner); + sethost64(&ctlr->gib->ercb.addr, ctlr->er); + sethost64(&ctlr->gib->epp, ctlr->epi); + csr32w(ctlr, Eci, 0); + + /* + * Command Ring. + * This is located in the General Communications Region + * and so the value placed in the Rcb is unused, the NIC + * knows where it is. Stick in the value according to + * the datasheet anyway. + * Initialise the ring and indices. + */ + ctlr->gib->crcb.addr.lo = Cr-0x400; + for(i = 0; i < Ncr*4; i += 4) + csr32w(ctlr, Cr+i, 0); + csr32w(ctlr, Cpi, 0); + csr32w(ctlr, Cci, 0); + + /* + * Send Ring. + * This ring is either in NIC memory at a fixed location depending + * on how big the ring is or it is in host memory. If in NIC + * memory it is accessed via the Local Memory Window; with a send + * ring size of 128 the window covers the whole ring and then need + * only be set once: + * ctlr->sr = KADDR(ctlr->port+Lmw); + * ga620lmw(ctlr, Sr, nil, sizeof(Sbd)*Nsr); + * ctlr->gib->srcb.addr.lo = Sr; + * There is nowhere in the Sbd to hold the Block* associated + * with this entry so an external array must be kept. + */ + ctlr->sr = malign(sizeof(Sbd)*Nsr); + sethost64(&ctlr->gib->srcb.addr, ctlr->sr); + if(ctlr->hardwarecksum) + flags = TcpUdpCksum|NoPseudoHdrCksum|HostRing; + else + flags = HostRing; + if(ctlr->coalupdateonly) + flags |= CoalUpdateOnly; + ctlr->gib->srcb.control = (Nsr<<16)|flags; + sethost64(&ctlr->gib->scp, ctlr->sci); + csr32w(ctlr, Spi, 0); + ctlr->srb = malloc(sizeof(Block*)*Nsr); + + /* + * Receive Standard Ring. + */ + ctlr->rsr = malign(sizeof(Rbd)*Nrsr); + sethost64(&ctlr->gib->rsrcb.addr, ctlr->rsr); + if(ctlr->hardwarecksum) + flags = TcpUdpCksum|NoPseudoHdrCksum; + else + flags = 0; + ctlr->gib->rsrcb.control = ((ETHERMAXTU+4)<<16)|flags; + csr32w(ctlr, Rspi, 0); + + /* + * Jumbo and Mini Rings. Unused for now. + */ + ctlr->gib->rjrcb.control = RingDisabled; + ctlr->gib->rmrcb.control = RingDisabled; + + /* + * Receive Return Ring. + * This is located in host memory. Allocate the ring, + * tell the NIC where it is and initialise the indices. + */ + ctlr->rrr = malign(sizeof(Rbd)*Nrrr); + sethost64(&ctlr->gib->rrrcb.addr, ctlr->rrr); + ctlr->gib->rrrcb.control = (Nrrr<<16)|0; + sethost64(&ctlr->gib->rrrpp, ctlr->rrrpi); + ctlr->rrrci = 0; + + /* + * Refresh Stats Pointer. + * For now just point it at the existing statistics block. + */ + sethost64(&ctlr->gib->rsp, ctlr->gib->statistics); + + /* + * DMA configuration. + * Use the recommended values. + */ + csr32w(ctlr, DMArc, 0x80); + csr32w(ctlr, DMAwc, 0x80); + + /* + * Transmit Buffer Ratio. + * Set to 1/3 of available buffer space (units are 1/64ths) + * if using Jumbo packets, ~64KB otherwise (assume 1MB on NIC). + */ + if(NrjrHI > 0 || Nsr > 128) + csr32w(ctlr, Tbr, 64/3); + else + csr32w(ctlr, Tbr, 4); + + /* + * Tuneable parameters. + * These defaults are based on the tuning hints in the Alteon + * Host/NIC Software Interface Definition and example software. + */ + ctlr->rct = 1/*100*/; + csr32w(ctlr, Rct, ctlr->rct); + ctlr->sct = 0; + csr32w(ctlr, Sct, ctlr->sct); + ctlr->st = 1000000; + csr32w(ctlr, St, ctlr->st); + ctlr->smcbd = Nsr/4; + csr32w(ctlr, SmcBD, ctlr->smcbd); + ctlr->rmcbd = 4/*6*/; + csr32w(ctlr, RmcBD, ctlr->rmcbd); + + /* + * Enable DMA Assist Logic. + */ + csr = csr32r(ctlr, DMAas) & ~0x03; + csr32w(ctlr, DMAas, csr|0x01); + + /* + * Link negotiation. + * The bits are set here but the NIC must be given a command + * once it is running to set negotiation in motion. + */ + csr32w(ctlr, Gln, Le|Lean|Lofc|Lfd|L1000MB|Lpref); + csr32w(ctlr, Fln, Le|Lean|Lhd|Lfd|L100MB|L10MB); + + /* + * A unique index for this controller and the maximum packet + * length expected. + * For now only standard packets are expected. + */ + csr32w(ctlr, Ifx, 1); + csr32w(ctlr, IfMTU, ETHERMAXTU+4); + + /* + * Enable Interrupts. + * There are 3 ways to mask interrupts - a bit in the Mhc (which + * is already cleared), the Mi register and the Hi mailbox. + * Writing to the Hi mailbox has the side-effect of clearing the + * PCI interrupt. + */ + csr32w(ctlr, Mi, 0); + csr32w(ctlr, Hi, 0); + + /* + * Start the firmware. + */ + csr32w(ctlr, CPUApc, tigon2FwStartAddr); + csr = csr32r(ctlr, CPUAstate) & ~CPUhalt; + csr32w(ctlr, CPUAstate, csr); + + return 0; +} + +static int +at24c32io(Ctlr* ctlr, char* op, int data) +{ + char *lp, *p; + int i, loop, mlc, r; + + mlc = csr32r(ctlr, Mlc); + + r = 0; + loop = -1; + lp = nil; + for(p = op; *p != '\0'; p++){ + switch(*p){ + default: + return -1; + case ' ': + continue; + case ':': /* start of 8-bit loop */ + if(lp != nil) + return -1; + lp = p; + loop = 7; + continue; + case ';': /* end of 8-bit loop */ + if(lp == nil) + return -1; + loop--; + if(loop >= 0) + p = lp; + else + lp = nil; + continue; + case 'C': /* assert clock */ + mlc |= EEclk; + break; + case 'c': /* deassert clock */ + mlc &= ~EEclk; + break; + case 'D': /* next bit in 'data' byte */ + if(loop < 0) + return -1; + if(data & (1<= 0) + r |= (i<= 0) + return -1; + return r; +} + +static int +at24c32r(Ctlr* ctlr, int addr) +{ + int data; + + /* + * Read a byte at address 'addr' from the Atmel AT24C32 + * Serial EEPROM. The 2-wire EEPROM access is controlled + * by 4 bits in Mlc. See the AT24C32 datasheet for + * protocol details. + */ + /* + * Start condition - a high to low transition of data + * with the clock high must precede any other command. + */ + at24c32io(ctlr, "OECoc", 0); + + /* + * Perform a random read at 'addr'. A dummy byte + * write sequence is performed to clock in the device + * and data word addresses (0 and 'addr' respectively). + */ + data = -1; + if(at24c32io(ctlr, "oE :DCc; oeCIc", 0xA0) != 0) + goto stop; + if(at24c32io(ctlr, "oE :DCc; oeCIc", addr>>8) != 0) + goto stop; + if(at24c32io(ctlr, "oE :DCc; oeCIc", addr) != 0) + goto stop; + + /* + * Now send another start condition followed by a + * request to read the device. The EEPROM responds + * by clocking out the data. + */ + at24c32io(ctlr, "OECoc", 0); + if(at24c32io(ctlr, "oE :DCc; oeCIc", 0xA1) != 0) + goto stop; + data = at24c32io(ctlr, ":CIc;", 0xA1); + +stop: + /* + * Stop condition - a low to high transition of data + * with the clock high is a stop condition. After a read + * sequence, the stop command will place the EEPROM in + * a standby power mode. + */ + at24c32io(ctlr, "oECOc", 0); + + return data; +} + +static int +ga620detach(Ctlr* ctlr) +{ + int timeo; + + /* + * Hard reset (don't know which endian so catch both); + * enable for little-endian mode; + * wait for code to be loaded from serial EEPROM or flash; + * make sure CPU A is halted. + */ + csr32w(ctlr, Mhc, (Hr<<24)|Hr); + csr32w(ctlr, Mhc, ((Eews|Ci)<<24)|(Eews|Ci)); + + microdelay(1); + for(timeo = 0; timeo < 500000; timeo++){ + if((csr32r(ctlr, CPUAstate) & (CPUhie|CPUrf)) == CPUhie) + break; + microdelay(1); + } + if((csr32r(ctlr, CPUAstate) & (CPUhie|CPUrf)) != CPUhie) + return -1; + csr32w(ctlr, CPUAstate, CPUhalt); + + /* + * After reset, CPU B seems to be stuck in 'CPUrf'. + * Worry about it later. + */ + csr32w(ctlr, CPUBstate, CPUhalt); + + return 0; +} + +static void +ga620shutdown(Ether* ether) +{ +print("ga620shutdown\n"); + ga620detach(ether->ctlr); +} + +static int +ga620reset(Ctlr* ctlr) +{ + int cls, csr, i; + + if(ga620detach(ctlr) < 0) + return -1; + + /* + * Tigon 2 PCI NICs have 512KB SRAM per bank. + * Clear out any lingering serial EEPROM state + * bits. + */ + csr = csr32r(ctlr, Mlc) & ~(EEdi|EEdo|EEdoe|EEclk|SRAMmask); + csr32w(ctlr, Mlc, SRAM512|csr); + csr = csr32r(ctlr, Mc); + csr32w(ctlr, Mc, SyncSRAM|csr); + + /* + * Initialise PCI State register. + * If PCI Write-and-Invalidate is enabled set the max write DMA + * value to the host cache-line size (32 on Pentium or later). + */ + csr = csr32r(ctlr, Ps) & (PCI32|PCI66); + csr |= PCIwcmd|PCIrcmd|PCImrm; + if(ctlr->pcidev->pcr & 0x0010){ + cls = pcicfgr8(ctlr->pcidev, PciCLS) * 4; + if(cls != 32) + pcicfgw8(ctlr->pcidev, PciCLS, 32/4); + csr |= PCIwm32; + } + csr32w(ctlr, Ps, csr); + + /* + * Operating Mode. + */ + csr32w(ctlr, Om, Fatal|NoJFrag|BswapDMA|WswapBD); + + /* + * Snarf the MAC address from the serial EEPROM. + */ + for(i = 0; i < Eaddrlen; i++){ + if((ctlr->ea[i] = at24c32r(ctlr, 0x8E+i)) == -1) + return -1; + } + + /* + * Load the firmware. + */ + ga620lmw(ctlr, tigon2FwTextAddr, tigon2FwText, tigon2FwTextLen); + ga620lmw(ctlr, tigon2FwRodataAddr, tigon2FwRodata, tigon2FwRodataLen); + ga620lmw(ctlr, tigon2FwDataAddr, tigon2FwData, tigon2FwDataLen); + ga620lmw(ctlr, tigon2FwSbssAddr, nil, tigon2FwSbssLen); + ga620lmw(ctlr, tigon2FwBssAddr, nil, tigon2FwBssLen); + + return 0; +} + +static void +ga620pci(void) +{ + int port; + Pcidev *p; + Ctlr *ctlr; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + + switch((p->did<<16)|p->vid){ + default: + continue; + case (0x620A<<16)|0x1385: /* Netgear GA620 */ + case (0x630A<<16)|0x1385: /* Netgear GA620T */ + case (0x0001<<16)|0x12AE: /* Alteon Acenic fiber + * and DEC DEGPA-SA */ + case (0x0002<<16)|0x12AE: /* Alteon Acenic copper */ + case (0x0009<<16)|0x10A9: /* SGI Acenic */ + break; + } + + port = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0); + if(port == 0){ + print("ga620: can't map %8.8luX\n", p->mem[0].bar); + continue; + } + + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = port; + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + + ctlr->nic = KADDR(ctlr->port); + if(ga620reset(ctlr)){ + free(ctlr); + continue; + } + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + +static int +ga620pnp(Ether* edev) +{ + Ctlr *ctlr; + uchar ea[Eaddrlen]; + + if(ctlrhead == nil) + ga620pci(); + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(edev->port == 0 || edev->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pcidev->intl; + edev->tbdf = ctlr->pcidev->tbdf; + edev->mbps = 1000; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to + * loading the station address in the hardware. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, edev->ea, Eaddrlen) == 0) + memmove(edev->ea, ctlr->ea, Eaddrlen); + + ga620init(edev); + + /* + * Linkage to the generic ethernet driver. + */ + edev->attach = ga620attach; + edev->transmit = ga620transmit; + edev->interrupt = ga620interrupt; + edev->ifstat = ga620ifstat; + edev->ctl = ga620ctl; + edev->shutdown = ga620shutdown; + + edev->arg = edev; + edev->promiscuous = nil; + + return 0; +} + +void +etherga620link(void) +{ + addethercard("GA620", ga620pnp); +} diff --git a/os/pc/etherga620fw.h b/os/pc/etherga620fw.h new file mode 100644 index 00000000..c30859e7 --- /dev/null +++ b/os/pc/etherga620fw.h @@ -0,0 +1,4858 @@ +/* Generated by genfw.c */ +#define tigon2FwReleaseMajor 0xc +#define tigon2FwReleaseMinor 0x4 +#define tigon2FwReleaseFix 0xb +#define tigon2FwStartAddr 0x00004000 +#define tigon2FwTextAddr 0x00004000 +#define tigon2FwTextLen 0x11bc0 +#define tigon2FwRodataAddr 0x00015bc0 +#define tigon2FwRodataLen 0x10d0 +#define tigon2FwDataAddr 0x00016cc0 +#define tigon2FwDataLen 0x1c0 +#define tigon2FwSbssAddr 0x00016e80 +#define tigon2FwSbssLen 0xcc +#define tigon2FwBssAddr 0x00016f50 +#define tigon2FwBssLen 0x20c0 +static int tigon2FwText[/*(MAX_TEXT_LEN/4) + 1*/] = { +0x0, +0x10000003, 0x0, 0xd, 0xd, +0x3c1d0001, 0x8fbd6d20, 0x3a0f021, 0x3c100000, +0x26104000, 0xc0010c0, 0x0, 0xd, +0x3c1d0001, 0x8fbd6d24, 0x3a0f021, 0x3c100000, +0x26104000, 0xc0017e0, 0x0, 0xd, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x2000008, +0x0, 0x800172f, 0x3c0a0001, 0x800172f, +0x3c0a0002, 0x800172f, 0x0, 0x8002cac, +0x0, 0x8002c4f, 0x0, 0x800172f, +0x3c0a0004, 0x800328a, 0x0, 0x8001a52, +0x0, 0x800394d, 0x0, 0x80038f4, +0x0, 0x800172f, 0x3c0a0006, 0x80039bb, +0x3c0a0007, 0x800172f, 0x3c0a0008, 0x800172f, +0x3c0a0009, 0x8003a13, 0x0, 0x8002ea6, +0x0, 0x800172f, 0x3c0a000b, 0x800172f, +0x3c0a000c, 0x800172f, 0x3c0a000d, 0x80028fb, +0x0, 0x8002890, 0x0, 0x800172f, +0x3c0a000e, 0x800208c, 0x0, 0x8001964, +0x0, 0x8001a04, 0x0, 0x8003ca6, +0x0, 0x8003c94, 0x0, 0x800172f, +0x0, 0x800191a, 0x0, 0x800172f, +0x0, 0x800172f, 0x3c0a0013, 0x800172f, +0x3c0a0014, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x27bdffe0, +0x3c1cc000, 0xafbf001c, 0xafb00018, 0x8f820140, +0x24030003, 0xaf8300ec, 0x34420004, 0xc002b20, +0xaf820140, 0x3c0100c0, 0xc001763, 0xac203ffc, +0x401821, 0x3c020010, 0x3c010001, 0xac236e9c, +0x10620011, 0x43102b, 0x14400002, 0x3c020020, +0x3c020008, 0x1062000c, 0x24050100, 0x3c060001, +0x8cc66e9c, 0x3c040001, 0x24845c74, 0x3821, +0xafa00010, 0xc002b3b, 0xafa00014, 0x3c020020, +0x3c010001, 0xac226e9c, 0x24020008, 0x3c010001, +0xac226eb4, 0x2402001f, 0x3c010001, 0xac226ec4, +0x24020016, 0x3c010001, 0xac226e98, 0x3c05fffe, +0x34a56f08, 0x3c020001, 0x8c426e9c, 0x3c030002, +0x24639010, 0x3c040001, 0x8c846cc4, 0x431023, +0x14800002, 0x458021, 0x2610fa38, 0x2402f000, +0x2028024, 0xc001785, 0x2002021, 0x2022823, +0x3c040020, 0x821823, 0x651823, 0x247bb000, +0x3c03fffe, 0x3463bf08, 0x363b821, 0x3c0600bf, +0x34c6f000, 0x3c070001, 0x8ce76cc0, 0x3c0300bf, +0x3463e000, 0x852023, 0x3c010001, 0xac246ea8, +0x822023, 0x3c010001, 0xac256e90, 0x52842, +0x3c010001, 0xac226e84, 0x27620ffc, 0x3c010001, +0xac226d20, 0x27621ffc, 0xdb3023, 0x7b1823, +0x3c010001, 0xac246e88, 0x3c010001, 0xac256eac, +0x3c010001, 0xac226d24, 0xaf860150, 0x10e00011, +0xaf830250, 0x3c1d0001, 0x8fbd6ccc, 0x3a0f021, +0xc001749, 0x0, 0x3c020001, 0x8c426cd0, +0x3c030001, 0x8c636cd4, 0x2442fe00, 0x24630200, +0x3c010001, 0xac226cd0, 0x3c010001, 0x10000004, +0xac236cd4, 0x3c1d0001, 0x8fbd6d20, 0x3a0f021, +0x3c020001, 0x8c426cc4, 0x1040000d, 0x26fafa38, +0x3c020001, 0x8c426cd0, 0x3c030001, 0x8c636cd4, +0x3c1a0001, 0x8f5a6cd4, 0x2442fa38, 0x246305c8, +0x3c010001, 0xac226cd0, 0x3c010001, 0xac236cd4, +0x3c020001, 0x8c426cc8, 0x14400003, 0x0, +0x3c010001, 0xac206cd0, 0xc001151, 0x0, +0x8fbf001c, 0x8fb00018, 0x3e00008, 0x27bd0020, +0x3c020001, 0x8c426cd0, 0x3c030001, 0x8c636cd4, +0x27bdff98, 0xafb00048, 0x3c100001, 0x8e1066b8, +0xafb20050, 0x3c120000, 0x26524100, 0xafbf0060, +0xafbe005c, 0xafb50058, 0xafb30054, 0xafb1004c, +0xafa20034, 0xafa30030, 0xafa00010, 0xafa00014, +0x8f860040, 0x3c040001, 0x24845c80, 0x24050200, +0x3c010001, 0xac326e80, 0xc002b3b, 0x2003821, +0x8f830040, 0x3c02f000, 0x621824, 0x3c026000, +0x1062000b, 0xa3a0003f, 0x240e0001, 0x3c040001, +0x24845c88, 0xa3ae003f, 0xafa00010, 0xafa00014, +0x8f860040, 0x24050300, 0xc002b3b, 0x2003821, +0x8f820240, 0x3c030001, 0x431025, 0xaf820240, +0xaf800048, 0x8f820048, 0x14400005, 0x0, +0xaf800048, 0x8f820048, 0x10400004, 0x0, +0xaf800048, 0x10000003, 0x2e02021, 0xaf80004c, +0x2e02021, 0x3c050001, 0xc002ba8, 0x34a540f8, +0x3402021, 0xc002ba8, 0x240505c8, 0x3c020001, +0x8c426ea8, 0x3c0d0001, 0x8dad6e88, 0x3c030001, +0x8c636e84, 0x3c080001, 0x8d086e90, 0x3c090001, +0x8d296eac, 0x3c0a0001, 0x8d4a6eb4, 0x3c0b0001, +0x8d6b6ec4, 0x3c0c0001, 0x8d8c6e98, 0x3c040001, +0x24845c94, 0x24050400, 0xaf42013c, 0x8f42013c, +0x24060001, 0x24070001, 0xaf400000, 0xaf4d0138, +0xaf430144, 0xaf480148, 0xaf49014c, 0xaf4a0150, +0xaf4b0154, 0xaf4c0158, 0x2442ff80, 0xaf420140, +0x24020001, 0xafa20010, 0xc002b3b, 0xafa00014, +0x8f420138, 0xafa20010, 0x8f42013c, 0xafa20014, +0x8f460144, 0x8f470148, 0x3c040001, 0x24845ca0, +0xc002b3b, 0x24050500, 0xafb70010, 0xafba0014, +0x8f46014c, 0x8f470150, 0x3c040001, 0x24845cac, +0xc002b3b, 0x24050600, 0x3c020001, 0x8c426e9c, +0x3603821, 0x3c060002, 0x24c69010, 0x2448ffff, +0x1061824, 0xe81024, 0x43102b, 0x10400006, +0x24050900, 0x3c040001, 0x24845cb8, 0xafa80010, +0xc002b3b, 0xafa00014, 0x8f82000c, 0xafa20010, +0x8f82003c, 0xafa20014, 0x8f860000, 0x8f870004, +0x3c040001, 0x24845cc4, 0xc002b3b, 0x24051000, +0x8c020220, 0x8c030224, 0x8c060218, 0x8c07021c, +0x3c040001, 0x24845ccc, 0x24051100, 0xafa20010, +0xc002b3b, 0xafa30014, 0xaf800054, 0xaf80011c, +0x8c020218, 0x30420002, 0x10400009, 0x0, +0x8c020220, 0x3c030002, 0x34630004, 0x431025, +0xaf42000c, 0x8c02021c, 0x10000008, 0x34420004, +0x8c020220, 0x3c030002, 0x34630006, 0x431025, +0xaf42000c, 0x8c02021c, 0x34420006, 0xaf420014, +0x8c020218, 0x30420010, 0x1040000a, 0x0, +0x8c02021c, 0x34420004, 0xaf420010, 0x8c020220, +0x3c03000a, 0x34630004, 0x431025, 0x10000009, +0xaf420008, 0x8c020220, 0x3c03000a, 0x34630006, +0x431025, 0xaf420008, 0x8c02021c, 0x34420006, +0xaf420010, 0x24020001, 0xaf8200a0, 0xaf8200b0, +0x8f830054, 0x8f820054, 0xaf8000d0, 0xaf8000c0, +0x10000002, 0x24630064, 0x8f820054, 0x621023, +0x2c420065, 0x1440fffc, 0x0, 0x8c040208, +0x8c05020c, 0x26e20028, 0xaee20020, 0x24020490, +0xaee20010, 0xaee40008, 0xaee5000c, 0x26e40008, +0x8c820000, 0x8c830004, 0xaf820090, 0xaf830094, +0x8c820018, 0xaf8200b4, 0x9482000a, 0xaf82009c, +0x8f420014, 0xaf8200b0, 0x8f8200b0, 0x30420004, +0x1440fffd, 0x0, 0x8f8200b0, 0x3c03ef00, +0x431024, 0x10400021, 0x0, 0x8f8200b4, +0xafa20010, 0x8f820090, 0x8f830094, 0x3c040001, +0x24845cd4, 0xafa30014, 0x8f8600b0, 0x8f87009c, +0x3c050001, 0xc002b3b, 0x34a5200d, 0x3c040001, +0x24845ce0, 0x240203c0, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e75ce8, 0xc002b3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x96e20472, +0x96e60452, 0x96e70462, 0xafa20010, 0x96e20482, +0x3c040001, 0x24845d14, 0x24051200, 0xc002b3b, +0xafa20014, 0x96f00452, 0x32020001, 0x10400002, +0xb021, 0x24160001, 0x32020002, 0x54400001, +0x36d60002, 0x32020008, 0x54400001, 0x36d60004, +0x32020010, 0x54400001, 0x36d60008, 0x32020020, +0x54400001, 0x36d60010, 0x32020040, 0x54400001, +0x36d60020, 0x32020080, 0x54400001, 0x36d60040, +0x96e60482, 0x30c20200, 0x54400001, 0x36d64000, +0x96e30472, 0x30620200, 0x10400003, 0x30620100, +0x10000003, 0x36d62000, 0x54400001, 0x36d61000, +0x96f00462, 0x32c24000, 0x14400004, 0x3207009b, +0x30c2009b, 0x14e20007, 0x240e0001, 0x32c22000, +0x1440000d, 0x32020001, 0x3062009b, 0x10e20009, +0x240e0001, 0x3c040001, 0x24845d20, 0x24051300, +0x2003821, 0xa3ae003f, 0xafa30010, 0xc002b3b, +0xafa00014, 0x32020001, 0x54400001, 0x36d60080, +0x32020002, 0x54400001, 0x36d60100, 0x32020008, +0x54400001, 0x36d60200, 0x32020010, 0x54400001, +0x36d60400, 0x32020080, 0x54400001, 0x36d60800, +0x8c020218, 0x30420200, 0x10400002, 0x3c020008, +0x2c2b025, 0x8c020218, 0x30420800, 0x10400002, +0x3c020080, 0x2c2b025, 0x8c020218, 0x30420400, +0x10400002, 0x3c020100, 0x2c2b025, 0x8c020218, +0x30420100, 0x10400002, 0x3c020200, 0x2c2b025, +0x8c020218, 0x30420080, 0x10400002, 0x3c020400, +0x2c2b025, 0x8c020218, 0x30422000, 0x10400002, +0x3c020010, 0x2c2b025, 0x8c020218, 0x30424000, +0x10400002, 0x3c020020, 0x2c2b025, 0x8c020218, +0x30421000, 0x10400002, 0x3c020040, 0x2c2b025, +0x8ee20498, 0x8ee3049c, 0xaf420160, 0xaf430164, +0x8ee204a0, 0x8ee304a4, 0xaf420168, 0xaf43016c, +0x8ee204a8, 0x8ee304ac, 0xaf420170, 0xaf430174, +0x8ee20428, 0x8ee3042c, 0xaf420178, 0xaf43017c, +0x8ee20448, 0x8ee3044c, 0xaf420180, 0xaf430184, +0x8ee20458, 0x8ee3045c, 0xaf420188, 0xaf43018c, +0x8ee20468, 0x8ee3046c, 0xaf420190, 0xaf430194, +0x8ee20478, 0x8ee3047c, 0xaf420198, 0xaf43019c, +0x8ee20488, 0x8ee3048c, 0xaf4201a0, 0xaf4301a4, +0x8ee204b0, 0x8ee304b4, 0x24040080, 0xaf4201a8, +0xaf4301ac, 0xc002ba8, 0x24050080, 0x8c02025c, +0x27440224, 0xaf4201f0, 0x8c020260, 0x24050200, +0x24060008, 0xc002bbf, 0xaf4201f8, 0x3c043b9a, +0x3484ca00, 0x3821, 0x24020006, 0x24030002, +0xaf4201f4, 0x240203e8, 0xaf430204, 0xaf430200, +0xaf4401fc, 0xaf420294, 0x24020001, 0xaf430290, +0xaf42029c, 0x3c030001, 0x671821, 0x90636cd8, +0x3471021, 0x24e70001, 0xa043022c, 0x2ce2000f, +0x1440fff8, 0x3471821, 0x24e70001, 0x3c080001, +0x350840f8, 0x8f820040, 0x3c040001, 0x24845d2c, +0x24051400, 0x21702, 0x24420030, 0xa062022c, +0x3471021, 0xa040022c, 0x8c070218, 0x2c03021, +0x240205c8, 0xafa20010, 0xc002b3b, 0xafa80014, +0x3c040001, 0x24845d38, 0x3c050000, 0x24a55c80, +0x24060010, 0x27b10030, 0x2203821, 0x27b30034, +0xc0017a3, 0xafb30010, 0x3c030001, 0x8c636cc8, +0x1060000a, 0x408021, 0x8fa30030, 0x2405ff00, +0x8fa20034, 0x246400ff, 0x852024, 0x831823, +0x431023, 0xafa20034, 0xafa40030, 0x3c040001, +0x24845d44, 0x3c050000, 0x24a54100, 0x24060108, +0x2203821, 0xc0017a3, 0xafb30010, 0x409021, +0x32c20003, 0x3c010001, 0xac326e80, 0x10400045, +0x2203821, 0x8f820050, 0x3c030010, 0x431024, +0x10400016, 0x0, 0x8c020218, 0x30420040, +0x1040000f, 0x24020001, 0x8f820050, 0x8c030218, +0x240e0001, 0x3c040001, 0x24845d50, 0xa3ae003f, +0xafa20010, 0xafa30014, 0x8f870040, 0x24051500, +0xc002b3b, 0x2c03021, 0x10000004, 0x0, +0x3c010001, 0x370821, 0xa02240f4, 0x3c040001, +0x24845d5c, 0x3c050001, 0x24a55b40, 0x3c060001, +0x24c65bac, 0xc53023, 0x8f420010, 0x27b30030, +0x2603821, 0x27b10034, 0x34420a00, 0xaf420010, +0xc0017a3, 0xafb10010, 0x3c040001, 0x24845d70, +0x3c050001, 0x24a5b714, 0x3c060001, 0x24c6ba90, +0xc53023, 0x2603821, 0xaf420108, 0xc0017a3, +0xafb10010, 0x3c040001, 0x24845d8c, 0x3c050001, +0x24a5be58, 0x3c060001, 0x24c6c900, 0xc53023, +0x2603821, 0x3c010001, 0xac226ef4, 0xc0017a3, +0xafb10010, 0x3c040001, 0x24845da4, 0x10000024, +0x24051600, 0x3c040001, 0x24845dac, 0x3c050001, +0x24a5a10c, 0x3c060001, 0x24c6a238, 0xc53023, +0xc0017a3, 0xafb30010, 0x3c040001, 0x24845dbc, +0x3c050001, 0x24a5b2b0, 0x3c060001, 0x24c6b70c, +0xc53023, 0x2203821, 0xaf420108, 0xc0017a3, +0xafb30010, 0x3c040001, 0x24845dd0, 0x3c050001, +0x24a5ba98, 0x3c060001, 0x24c6be50, 0xc53023, +0x2203821, 0x3c010001, 0xac226ef4, 0xc0017a3, +0xafb30010, 0x3c040001, 0x24845de4, 0x24051650, +0x2c03021, 0x3821, 0x3c010001, 0xac226ef8, +0xafa00010, 0xc002b3b, 0xafa00014, 0x32c20020, +0x10400021, 0x27a70030, 0x3c040001, 0x24845df0, +0x3c050001, 0x24a5b13c, 0x3c060001, 0x24c6b2a8, +0xc53023, 0x24022000, 0xaf42001c, 0x27a20034, +0xc0017a3, 0xafa20010, 0x21900, 0x31982, +0x3c040800, 0x641825, 0xae430028, 0x24030010, +0xaf43003c, 0x96e30450, 0xaf430040, 0x8f430040, +0x3c040001, 0x24845e04, 0xafa00014, 0xafa30010, +0x8f47001c, 0x24051660, 0x3c010001, 0xac226ef0, +0x10000025, 0x32c60020, 0x8ee20448, 0x8ee3044c, +0xaf43001c, 0x8f42001c, 0x2442e000, 0x2c422001, +0x1440000a, 0x240e0001, 0x3c040001, 0x24845e10, +0xa3ae003f, 0xafa00010, 0xafa00014, 0x8f46001c, +0x24051700, 0xc002b3b, 0x3821, 0x3c020000, +0x24425cbc, 0x21100, 0x21182, 0x3c030800, +0x431025, 0xae420028, 0x24020008, 0xaf42003c, +0x96e20450, 0xaf420040, 0x8f420040, 0x3c040001, +0x24845e1c, 0xafa00014, 0xafa20010, 0x8f47001c, +0x24051800, 0x32c60020, 0xc002b3b, 0x0, +0x3c050fff, 0x3c030001, 0x8c636ef4, 0x34a5ffff, +0x2403021, 0x3c020001, 0x8c426ef8, 0x3c040800, +0x651824, 0x31882, 0x641825, 0x451024, +0x21082, 0x441025, 0xacc20080, 0x32c20180, +0x10400056, 0xacc30020, 0x8f82005c, 0x3c030080, +0x431024, 0x1040000d, 0x0, 0x8f820050, +0xafa20010, 0x8f82005c, 0x240e0001, 0x3c040001, +0x24845e28, 0xa3ae003f, 0xafa20014, 0x8f870040, +0x24051900, 0xc002b3b, 0x2c03021, 0x8f820050, +0x3c030010, 0x431024, 0x10400016, 0x0, +0x8c020218, 0x30420040, 0x1040000f, 0x24020001, +0x8f820050, 0x8c030218, 0x240e0001, 0x3c040001, +0x24845d50, 0xa3ae003f, 0xafa20010, 0xafa30014, +0x8f870040, 0x24052000, 0xc002b3b, 0x2c03021, +0x10000004, 0x0, 0x3c010001, 0x370821, +0xa02240f4, 0x3c040001, 0x24845e34, 0x3c050001, +0x24a55ac0, 0x3c060001, 0x24c65b38, 0xc53023, +0x8f420008, 0x27b30030, 0x2603821, 0x27b10034, +0x34420e00, 0xaf420008, 0xc0017a3, 0xafb10010, +0x3c040001, 0x24845e4c, 0x3c050001, 0x24a5d8b4, +0x3c060001, 0x24c6e3c8, 0xc53023, 0x2603821, +0xaf42010c, 0xc0017a3, 0xafb10010, 0x3c040001, +0x24845e64, 0x3c050001, 0x24a5e9ac, 0x3c060001, +0x24c6f0f0, 0xc53023, 0x2603821, 0x3c010001, +0xac226f04, 0xc0017a3, 0xafb10010, 0x3c040001, +0x24845e7c, 0x10000027, 0x24052100, 0x3c040001, +0x24845e84, 0x3c050001, 0x24a59fc8, 0x3c060001, +0x24c6a104, 0xc53023, 0x27b10030, 0x2203821, +0x27b30034, 0xc0017a3, 0xafb30010, 0x3c040001, +0x24845e94, 0x3c050001, 0x24a5cad4, 0x3c060001, +0x24c6d8ac, 0xc53023, 0x2203821, 0xaf42010c, +0xc0017a3, 0xafb30010, 0x3c040001, 0x24845ea4, +0x3c050001, 0x24a5e84c, 0x3c060001, 0x24c6e9a4, +0xc53023, 0x2203821, 0x3c010001, 0xac226f04, +0xc0017a3, 0xafb30010, 0x3c040001, 0x24845eb8, +0x24052150, 0x2c03021, 0x3821, 0x3c010001, +0xac226f10, 0xafa00010, 0xc002b3b, 0xafa00014, +0x3c110fff, 0x3c030001, 0x8c636f04, 0x3631ffff, +0x2409821, 0x3c020001, 0x8c426f10, 0x3c0e0800, +0x711824, 0x31882, 0x6e1825, 0x511024, +0x21082, 0x4e1025, 0xae630038, 0xae620078, +0x8c020218, 0x30420040, 0x14400004, 0x24020001, +0x3c010001, 0x370821, 0xa02240f4, 0x3c040001, +0x24845ec4, 0x3c050001, 0x24a5e3d0, 0x3c060001, +0x24c6e52c, 0xc53023, 0x27be0030, 0x3c03821, +0x27b50034, 0xc0017a3, 0xafb50010, 0x3c010001, +0xac226efc, 0x511024, 0x21082, 0x3c0e0800, +0x4e1025, 0xae620050, 0x32c22000, 0x10400006, +0x3c03821, 0x3c020000, 0x24425cbc, 0x2221024, +0x1000000f, 0x21082, 0x3c040001, 0x24845ed8, +0x3c050001, 0x24a5e534, 0x3c060001, 0x24c6e6e4, +0xc53023, 0xc0017a3, 0xafb50010, 0x3c010001, +0xac226f14, 0x511024, 0x21082, 0x3c0e0800, +0x4e1025, 0xae620048, 0x32c24000, 0x10400005, +0x27a70030, 0x3c020000, 0x24425cbc, 0x1000000e, +0x21100, 0x3c040001, 0x24845ef0, 0x3c050001, +0x24a5e6ec, 0x3c060001, 0x24c6e844, 0xc53023, +0x27a20034, 0xc0017a3, 0xafa20010, 0x3c010001, +0xac226f08, 0x21100, 0x21182, 0x3c030800, +0x431025, 0xae420060, 0x3c040001, 0x24845f08, +0x3c050001, 0x24a58230, 0x3c060001, 0x24c68650, +0xc53023, 0x27b10030, 0x2203821, 0x27b30034, +0xc0017a3, 0xafb30010, 0x3c0e0fff, 0x35ceffff, +0x3c040001, 0x24845f14, 0x3c050000, 0x24a56468, +0x3c060000, 0x24c66588, 0xc53023, 0x2203821, +0x240f021, 0x3c010001, 0xac226edc, 0x4e1024, +0x21082, 0x3c150800, 0x551025, 0xafae0044, +0xafc200b8, 0xc0017a3, 0xafb30010, 0x3c040001, +0x24845f20, 0x3c050000, 0x24a56590, 0x3c060000, +0x24c66808, 0x8fae0044, 0xc53023, 0x2203821, +0x3c010001, 0xac226ed0, 0x4e1024, 0x21082, +0x551025, 0xafc200e8, 0xc0017a3, 0xafb30010, +0x3c040001, 0x24845f38, 0x3c050000, 0x24a56810, +0x3c060000, 0x24c66940, 0x8fae0044, 0xc53023, +0x2203821, 0x3c010001, 0xac226ec8, 0x4e1024, +0x21082, 0x551025, 0xafc200c0, 0xc0017a3, +0xafb30010, 0x3c040001, 0x24845f50, 0x3c050001, +0x24a5fad0, 0x3c060001, 0x24c6fba8, 0x8fae0044, +0xc53023, 0x2203821, 0x3c010001, 0xac226ed4, +0x4e1024, 0x21082, 0x551025, 0xafc200c8, +0xc0017a3, 0xafb30010, 0x3c040001, 0x24845f5c, +0x3c050001, 0x24a5c93c, 0x3c060001, 0x24c6ca20, +0xc53023, 0x2203821, 0xaf420110, 0xc0017a3, +0xafb30010, 0x3c040001, 0x24845f6c, 0x3c050001, +0x24a5c910, 0x3c060001, 0x24c6c934, 0xc53023, +0x2203821, 0xaf420124, 0xc0017a3, 0xafb30010, +0x3c040001, 0x24845f7c, 0x3c050001, 0x24a55a80, +0x3c060001, 0x24c65aac, 0xc53023, 0x2203821, +0xaf420120, 0xaf420114, 0xc0017a3, 0xafb30010, +0x3c040001, 0x24845f88, 0x3c050001, 0x24a5f298, +0x3c060001, 0x24c6f6b4, 0xc53023, 0x2203821, +0xaf420118, 0xc0017a3, 0xafb30010, 0x8fae0044, +0x3c010001, 0xac226f18, 0x4e1024, 0x21082, +0x551025, 0xc003fc3, 0xafc200d0, 0xc003c40, +0x0, 0xc0027a8, 0x0, 0xac000228, +0xac00022c, 0x96e20450, 0x2442ffff, 0xaf420038, +0x96e20460, 0xaf420080, 0x32c24000, 0x14400003, +0x0, 0x96e20480, 0xaf420084, 0x96e70490, +0x50e00001, 0x24070800, 0x24e2ffff, 0xaf420088, +0xaf42007c, 0x24020800, 0x10e2000f, 0x32c24000, +0x10400003, 0x24020400, 0x10e2000b, 0x0, +0x240e0001, 0x3c040001, 0x24845f98, 0xa3ae003f, +0x96e60490, 0x24052170, 0x2c03821, 0xafa00010, +0xc002b3b, 0xafa00014, 0x8f430138, 0x8f440138, +0x24020001, 0xa34205c2, 0xaf430094, 0xaf440098, +0xafa00010, 0xafa00014, 0x8f460080, 0x8f470084, +0x3c040001, 0x24845fa4, 0xc002b3b, 0x24052200, +0xc0024a4, 0x3c110800, 0x3c1433d8, 0x3694cb58, +0x3c020800, 0x34420080, 0x3c040001, 0x24845fb0, +0x3c050000, 0x24a55d00, 0x3c060000, 0x24c65d1c, +0xc53023, 0x27a70030, 0xaf820060, 0x2402ffff, +0xaf820064, 0x27a20034, 0xc0017a3, 0xafa20010, +0x3c010001, 0xac226eb8, 0x21100, 0x21182, +0x511025, 0xc0018fc, 0xae420000, 0x8f820240, +0x3c030001, 0x431025, 0xaf820240, 0x3c020000, +0x24424034, 0xaf820244, 0xaf800240, 0x8f820060, +0x511024, 0x14400005, 0x3c030800, 0x8f820060, +0x431024, 0x1040fffd, 0x0, 0xc003c4d, +0x8821, 0x3c020100, 0xafa20020, 0x8f530018, +0x240200ff, 0x56620001, 0x26710001, 0x8c020228, +0x1622000e, 0x1330c0, 0x8f42033c, 0x24420001, +0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, +0x24845c24, 0x3c050009, 0xafa00014, 0xafa20010, +0x8fa60020, 0x1000003f, 0x34a50100, 0xd71021, +0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, +0xc01821, 0x8f440178, 0x8f45017c, 0x1021, +0x24070004, 0xafa70010, 0xafb10014, 0x8f48000c, +0x24c604c0, 0x2e63021, 0xafa80018, 0x8f48010c, +0x24070008, 0xa32821, 0xa3482b, 0x822021, +0x100f809, 0x892021, 0x1440000b, 0x24070008, +0x8f820120, 0xafa20010, 0x8f820124, 0x3c040001, +0x24845c2c, 0x3c050009, 0xafa20014, 0x8fa60020, +0x1000001c, 0x34a50200, 0x8f440160, 0x8f450164, +0x8f43000c, 0xaf510018, 0x8f860120, 0x24020010, +0xafa20010, 0xafb10014, 0xafa30018, 0x8f42010c, +0x40f809, 0x24c6001c, 0x14400010, 0x0, +0x8f420340, 0x24420001, 0xaf420340, 0x8f420340, +0x8f820120, 0xafa20010, 0x8f820124, 0x3c040001, +0x24845c34, 0x3c050009, 0xafa20014, 0x8fa60020, +0x34a50300, 0xc002b3b, 0x2603821, 0x8f4202e4, +0x24420001, 0xaf4202e4, 0x8f4202e4, 0x93a2003f, +0x10400069, 0x3c020700, 0x34423000, 0xafa20028, +0x8f530018, 0x240200ff, 0x12620002, 0x8821, +0x26710001, 0x8c020228, 0x1622000e, 0x1330c0, +0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, +0x8c020228, 0x3c040001, 0x24845c24, 0x3c050009, +0xafa00014, 0xafa20010, 0x8fa60028, 0x1000003f, +0x34a50100, 0xd71021, 0x8fa30028, 0x8fa4002c, +0xac4304c0, 0xac4404c4, 0xc01821, 0x8f440178, +0x8f45017c, 0x1021, 0x24070004, 0xafa70010, +0xafb10014, 0x8f48000c, 0x24c604c0, 0x2e63021, +0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, +0xa3482b, 0x822021, 0x100f809, 0x892021, +0x1440000b, 0x24070008, 0x8f820120, 0xafa20010, +0x8f820124, 0x3c040001, 0x24845c2c, 0x3c050009, +0xafa20014, 0x8fa60028, 0x1000001c, 0x34a50200, +0x8f440160, 0x8f450164, 0x8f43000c, 0xaf510018, +0x8f860120, 0x24020010, 0xafa20010, 0xafb10014, +0xafa30018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x14400010, 0x0, 0x8f420340, 0x24420001, +0xaf420340, 0x8f420340, 0x8f820120, 0xafa20010, +0x8f820124, 0x3c040001, 0x24845c34, 0x3c050009, +0xafa20014, 0x8fa60028, 0x34a50300, 0xc002b3b, +0x2603821, 0x8f4202f0, 0x24420001, 0xaf4202f0, +0x8f4202f0, 0x3c040001, 0x24845fc0, 0xafa00010, +0xafa00014, 0x8fa60028, 0x24052300, 0xc002b3b, +0x3821, 0x10000004, 0x0, 0x8c020264, +0x10400005, 0x0, 0x8f8200a0, 0x30420004, +0x1440fffa, 0x0, 0x8f820044, 0x34420004, +0xaf820044, 0x8f420308, 0x24420001, 0xaf420308, +0x8f420308, 0x8f8200d8, 0x8f8300d4, 0x431023, +0x2442ff80, 0xaf420090, 0x8f420090, 0x2842ff81, +0x10400006, 0x24020001, 0x8f420090, 0x8f430144, +0x431021, 0xaf420090, 0x24020001, 0xaf42008c, +0x32c20008, 0x10400006, 0x0, 0x8f820214, +0x3c038100, 0x3042ffff, 0x431025, 0xaf820214, +0x3c030001, 0x8c636d94, 0x30620002, 0x10400009, +0x30620001, 0x3c040001, 0x24845fcc, 0x3c050000, +0x24a56d50, 0x3c060000, 0x24c671c8, 0x10000012, +0xc53023, 0x10400009, 0x0, 0x3c040001, +0x24845fdc, 0x3c050000, 0x24a571d0, 0x3c060000, +0x24c67678, 0x10000008, 0xc53023, 0x3c040001, +0x24845fec, 0x3c050000, 0x24a56948, 0x3c060000, +0x24c66d48, 0xc53023, 0x27a70030, 0x27a20034, +0xc0017a3, 0xafa20010, 0x3c010001, 0xac226ecc, +0x3c020001, 0x8c426ecc, 0x3c030800, 0x21100, +0x21182, 0x431025, 0xae420040, 0x8f8200a0, +0xafa20010, 0x8f8200b0, 0xafa20014, 0x8f86005c, +0x8f87011c, 0x3c040001, 0x24845ffc, 0x3c010001, +0xac366ea4, 0x3c010001, 0xac206e94, 0x3c010001, +0xac3c6e8c, 0x3c010001, 0xac3b6ebc, 0x3c010001, +0xac376ec0, 0x3c010001, 0xac3a6ea0, 0xc002b3b, +0x24052400, 0x8f820200, 0xafa20010, 0x8f820220, +0xafa20014, 0x8f860044, 0x8f870050, 0x3c040001, +0x24846008, 0xc002b3b, 0x24052500, 0x8f830060, +0x74100b, 0x242000a, 0x200f821, 0x0, +0xd, 0x8fbf0060, 0x8fbe005c, 0x8fb50058, +0x8fb30054, 0x8fb20050, 0x8fb1004c, 0x8fb00048, +0x3e00008, 0x27bd0068, 0x27bdffe0, 0x3c040001, +0x24846014, 0x24052600, 0x3021, 0x3821, +0xafbf0018, 0xafa00010, 0xc002b3b, 0xafa00014, +0x8fbf0018, 0x3e00008, 0x27bd0020, 0x3e00008, +0x0, 0x3e00008, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x3e00008, 0x0, 0x3e00008, 0x0, +0x27bdfde0, 0x27a50018, 0x3c04dead, 0x3484beef, +0xafbf0218, 0x8f820150, 0x3c03001f, 0x3463ffff, +0xafa40018, 0xa22823, 0xa32824, 0x8ca20000, +0x1044000a, 0x0, 0xafa50010, 0x8ca20000, +0xafa20014, 0x8f860150, 0x8f870250, 0x3c040001, +0x2484601c, 0xc002b3b, 0x24052700, 0x8fbf0218, +0x3e00008, 0x27bd0220, 0x27bdffe0, 0x3c06abba, +0x34c6babe, 0xafb00018, 0x3c100004, 0x3c07007f, +0x34e7ffff, 0xafbf001c, 0x102840, 0x8e040000, +0x8ca30000, 0xaca00000, 0xae060000, 0x8ca20000, +0xaca30000, 0x10460005, 0xae040000, 0xa08021, +0xf0102b, 0x1040fff5, 0x102840, 0x3c040001, +0x24846028, 0x24052800, 0x2003021, 0x3821, +0xafa00010, 0xc002b3b, 0xafa00014, 0x2001021, +0x8fbf001c, 0x8fb00018, 0x3e00008, 0x27bd0020, +0x8c020224, 0x3047003f, 0x10e00010, 0x803021, +0x2821, 0x24030020, 0xe31024, 0x10400002, +0x63042, 0xa62821, 0x31842, 0x1460fffb, +0xe31024, 0x2402f000, 0xa22824, 0x3402ffff, +0x45102b, 0x14400003, 0x3c020001, 0x10000008, +0x3c020001, 0x3442ffff, 0x851823, 0x43102b, +0x14400003, 0xa01021, 0x3c02fffe, 0x821021, +0x3e00008, 0x0, 0x27bdffd0, 0xafb50028, +0x8fb50040, 0xafb20020, 0xa09021, 0xafb1001c, +0x24c60003, 0xafbf002c, 0xafb30024, 0xafb00018, +0x8ea20000, 0x2403fffc, 0xc38024, 0x50102b, +0x1440001b, 0xe08821, 0x8e330000, 0xafb00010, +0x8ea20000, 0xafa20014, 0x8e270000, 0x24053000, +0xc002b3b, 0x2403021, 0x8e230000, 0x702021, +0x64102b, 0x10400007, 0x2402821, 0x8ca20000, +0xac620000, 0x24630004, 0x64102b, 0x1440fffb, +0x24a50004, 0x8ea20000, 0x501023, 0xaea20000, +0x8e220000, 0x501021, 0x1000000b, 0xae220000, +0x2402002d, 0xa0820000, 0xafb00010, 0x8ea20000, +0x2409821, 0xafa20014, 0x8e270000, 0x24053100, +0xc002b3b, 0x2603021, 0x2601021, 0x8fbf002c, +0x8fb50028, 0x8fb30024, 0x8fb20020, 0x8fb1001c, +0x8fb00018, 0x3e00008, 0x27bd0030, 0x27bdffe8, +0x3c1cc000, 0x3c05fffe, 0x3c030001, 0x8c636e84, +0x3c040001, 0x8c846e90, 0x34a5bf08, 0x24021ffc, +0x3c010001, 0xac226cd0, 0x3c0200c0, 0x3c010001, +0xac226cd4, 0x3c020020, 0xafbf0010, 0x3c0100c0, +0xac201ffc, 0x431023, 0x441023, 0x245bb000, +0x365b821, 0x3c1d0001, 0x8fbd6ccc, 0x3a0f021, +0x3c0400c0, 0x34840200, 0x3c1a00c0, 0x3c0300c0, +0x346307c8, 0x24021dfc, 0x3c010001, 0xac226cd0, +0x24021834, 0x3c010001, 0xac246cd4, 0x3c010001, +0xac226cd0, 0x3c010001, 0xac236cd4, 0xc00180d, +0x375a0200, 0x8fbf0010, 0x3e00008, 0x27bd0018, +0x27bdffc8, 0x3c040001, 0x24846034, 0x24053200, +0x3c020001, 0x8c426cd0, 0x3c030001, 0x8c636cd4, +0x3021, 0x3603821, 0xafbf0030, 0xafb3002c, +0xafb20028, 0xafb10024, 0xafb00020, 0xafa2001c, +0xafa30018, 0xafb70010, 0xc002b3b, 0xafba0014, +0xc001916, 0x0, 0x8f820240, 0x34420004, +0xaf820240, 0x24020001, 0xaf420000, 0x3c020001, +0x571021, 0x904240f4, 0x10400092, 0x2403fffc, +0x3c100001, 0x2610ac73, 0x3c120001, 0x2652a84c, +0x2121023, 0x438024, 0x8fa3001c, 0x3c040001, +0x24846040, 0x70102b, 0x1440001a, 0x27b30018, +0x8fb10018, 0x24053000, 0x2403021, 0xafb00010, +0xafa30014, 0xc002b3b, 0x2203821, 0x8fa30018, +0x702021, 0x64102b, 0x10400007, 0x2403021, +0x8cc20000, 0xac620000, 0x24630004, 0x64102b, +0x1440fffb, 0x24c60004, 0x8fa2001c, 0x501023, +0xafa2001c, 0x8e620000, 0x501021, 0x1000000a, +0xae620000, 0x2408821, 0x24053100, 0xafb00010, +0xafa30014, 0x8fa70018, 0x2203021, 0x2402002d, +0xc002b3b, 0xa0820000, 0x24070020, 0x8fa3001c, +0x3c040001, 0x2484605c, 0x24120020, 0x3c010001, +0xac316eb0, 0x2c620020, 0x1440001d, 0x27b10018, +0x8fb00018, 0x24053000, 0x3c060001, 0x24c66f50, +0xafa70010, 0xafa30014, 0xc002b3b, 0x2003821, +0x8fa30018, 0x3c040001, 0x24846f50, 0x24650020, +0x65102b, 0x10400007, 0x0, 0x8c820000, +0xac620000, 0x24630004, 0x65102b, 0x1440fffb, +0x24840004, 0x8fa2001c, 0x521023, 0xafa2001c, +0x8e220000, 0x521021, 0x1000000b, 0xae220000, +0x3c100001, 0x26106f50, 0x24053100, 0xafa70010, +0xafa30014, 0x8fa70018, 0x2003021, 0x2402002d, +0xc002b3b, 0xa0820000, 0x24070020, 0x3c040001, +0x24846070, 0x8fa3001c, 0x24120020, 0x3c010001, +0xac306ee4, 0x2c620020, 0x1440001d, 0x27b10018, +0x8fb00018, 0x24053000, 0x3c060001, 0x24c66f70, +0xafa70010, 0xafa30014, 0xc002b3b, 0x2003821, +0x8fa30018, 0x3c040001, 0x24846f70, 0x24650020, +0x65102b, 0x10400007, 0x0, 0x8c820000, +0xac620000, 0x24630004, 0x65102b, 0x1440fffb, +0x24840004, 0x8fa2001c, 0x521023, 0xafa2001c, +0x8e220000, 0x521021, 0x1000000b, 0xae220000, +0x3c100001, 0x26106f70, 0x24053100, 0xafa70010, +0xafa30014, 0x8fa70018, 0x2003021, 0x2402002d, +0xc002b3b, 0xa0820000, 0x3c010001, 0x10000031, +0xac306ee0, 0x3c100001, 0x2610821f, 0x3c120001, +0x2652809c, 0x2121023, 0x438024, 0x8fa3001c, +0x3c040001, 0x24846084, 0x70102b, 0x1440001a, +0x27b30018, 0x8fb10018, 0x24053000, 0x2403021, +0xafb00010, 0xafa30014, 0xc002b3b, 0x2203821, +0x8fa30018, 0x702021, 0x64102b, 0x10400007, +0x2403021, 0x8cc20000, 0xac620000, 0x24630004, +0x64102b, 0x1440fffb, 0x24c60004, 0x8fa2001c, +0x501023, 0xafa2001c, 0x8e620000, 0x501021, +0x1000000a, 0xae620000, 0x2408821, 0x24053100, +0xafb00010, 0xafa30014, 0x8fa70018, 0x2203021, +0x2402002d, 0xc002b3b, 0xa0820000, 0x3c010001, +0xac316eb0, 0x3c030001, 0x8c636eb0, 0x24020400, +0x60f809, 0xaf820070, 0x8fbf0030, 0x8fb3002c, +0x8fb20028, 0x8fb10024, 0x8fb00020, 0x3e00008, +0x27bd0038, 0x0, 0x0, 0x8f820040, +0x3c03f000, 0x431024, 0x3c036000, 0x14430006, +0x0, 0x8f820050, 0x2403ff80, 0x431024, +0x34420055, 0xaf820050, 0x8f820054, 0x244203e8, +0xaf820058, 0x240201f4, 0xaf4200e0, 0x24020004, +0xaf4200e8, 0x24020002, 0xaf4001b0, 0xaf4000e4, +0xaf4200dc, 0xaf4000d8, 0xaf4000d4, 0x3e00008, +0xaf4000d0, 0x8f820054, 0x24420005, 0x3e00008, +0xaf820078, 0x27bdffe8, 0xafbf0010, 0x8f820054, +0x244203e8, 0xaf820058, 0x3c020800, 0x2c21024, +0x10400004, 0x3c02f7ff, 0x3442ffff, 0x2c2b024, +0x36940040, 0x3c020001, 0x8c426da8, 0x10400017, +0x3c020200, 0x3c030001, 0x8c636f1c, 0x10600016, +0x282a025, 0x3c020001, 0x8c426e44, 0x14400012, +0x3c020200, 0x3c020001, 0x8c426d94, 0x30420003, +0x1440000d, 0x3c020200, 0x8f830224, 0x3c020002, +0x8c428fec, 0x10620008, 0x3c020200, 0xc003daf, +0x0, 0x10000004, 0x3c020200, 0xc004196, +0x0, 0x3c020200, 0x2c21024, 0x10400003, +0x0, 0xc001f4b, 0x0, 0x8f4200d8, +0x8f4300dc, 0x24420001, 0xaf4200d8, 0x43102b, +0x14400003, 0x0, 0xaf4000d8, 0x36940080, +0x8c030238, 0x1060000c, 0x0, 0x8f4201b0, +0x244203e8, 0xaf4201b0, 0x43102b, 0x14400006, +0x0, 0x934205c5, 0x14400003, 0x0, +0xc001da0, 0x0, 0x8fbf0010, 0x3e00008, +0x27bd0018, 0x3e00008, 0x0, 0x27bdffd8, +0xafbf0020, 0x8f43002c, 0x8f420038, 0x10620059, +0x0, 0x3c020001, 0x571021, 0x904240f0, +0x10400026, 0x24070008, 0x8f440170, 0x8f450174, +0x8f48000c, 0x8f860120, 0x24020020, 0xafa20010, +0xafa30014, 0xafa80018, 0x8f42010c, 0x40f809, +0x24c6001c, 0x14400011, 0x24020001, 0x3c010001, +0x370821, 0xa02240f0, 0x8f820124, 0xafa20010, +0x8f820128, 0x3c040001, 0x24846128, 0xafa20014, +0x8f46002c, 0x8f870120, 0x3c050009, 0xc002b3b, +0x34a50900, 0x1000005c, 0x0, 0x8f420300, +0x24420001, 0xaf420300, 0x8f420300, 0x8f42002c, +0xa34005c1, 0x10000027, 0xaf420038, 0x8f440170, +0x8f450174, 0x8f43002c, 0x8f48000c, 0x8f860120, +0x24020080, 0xafa20010, 0xafa30014, 0xafa80018, +0x8f42010c, 0x40f809, 0x24c6001c, 0x14400011, +0x24020001, 0x3c010001, 0x370821, 0xa02240f1, +0x8f820124, 0xafa20010, 0x8f820128, 0x3c040001, +0x24846134, 0xafa20014, 0x8f46002c, 0x8f870120, +0x3c050009, 0xc002b3b, 0x34a51100, 0x10000036, +0x0, 0x8f420300, 0x8f43002c, 0x24420001, +0xaf420300, 0x8f420300, 0x24020001, 0xa34205c1, +0xaf430038, 0x3c010001, 0x370821, 0xa02040f1, +0x3c010001, 0x370821, 0xa02040f0, 0x10000026, +0xaf400034, 0x934205c1, 0x1040001d, 0x0, +0xa34005c1, 0x8f820040, 0x30420001, 0x14400008, +0x2021, 0x8c030104, 0x24020001, 0x50620005, +0x24040001, 0x8c020264, 0x10400003, 0x801021, +0x24040001, 0x801021, 0x10400006, 0x0, +0x8f42030c, 0x24420001, 0xaf42030c, 0x10000008, +0x8f42030c, 0x8f820044, 0x34420004, 0xaf820044, +0x8f420308, 0x24420001, 0xaf420308, 0x8f420308, +0x3c010001, 0x370821, 0xa02040f0, 0x3c010001, +0x370821, 0xa02040f1, 0x8f420000, 0x10400007, +0x0, 0xaf80004c, 0x8f82004c, 0x1040fffd, +0x0, 0x10000005, 0x0, 0xaf800048, +0x8f820048, 0x1040fffd, 0x0, 0x8f820060, +0x3c03ff7f, 0x3463ffff, 0x431024, 0xaf820060, +0x8f420000, 0x10400003, 0x0, 0x10000002, +0xaf80004c, 0xaf800048, 0x8fbf0020, 0x3e00008, +0x27bd0028, 0x3e00008, 0x0, 0x27bdffd8, +0xafbf0020, 0x8f430044, 0x8f42007c, 0x10620029, +0x24070008, 0x8f440168, 0x8f45016c, 0x8f48000c, +0x8f860120, 0x24020040, 0xafa20010, 0xafa30014, +0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x14400011, 0x24020001, 0x3c010001, 0x370821, +0xa02240f2, 0x8f820124, 0xafa20010, 0x8f820128, +0x3c040001, 0x2484613c, 0xafa20014, 0x8f460044, +0x8f870120, 0x3c050009, 0xc002b3b, 0x34a51300, +0x1000000f, 0x0, 0x8f420304, 0x24420001, +0xaf420304, 0x8f420304, 0x8f420044, 0xaf42007c, +0x3c010001, 0x370821, 0xa02040f2, 0x10000004, +0xaf400078, 0x3c010001, 0x370821, 0xa02040f2, +0x8f420000, 0x10400007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x3c03feff, 0x3463ffff, +0x431024, 0xaf820060, 0x8f420000, 0x10400003, +0x0, 0x10000002, 0xaf80004c, 0xaf800048, +0x8fbf0020, 0x3e00008, 0x27bd0028, 0x3e00008, +0x0, 0x3c020001, 0x8c426da8, 0x27bdffa8, +0xafbf0050, 0xafbe004c, 0xafb50048, 0xafb30044, +0xafb20040, 0xafb1003c, 0xafb00038, 0x104000d5, +0x8f900044, 0x8f4200d0, 0x24430001, 0x2842000b, +0x144000e4, 0xaf4300d0, 0x8f420004, 0x30420002, +0x1440009c, 0xaf4000d0, 0x8f420004, 0x3c030001, +0x8c636d98, 0x34420002, 0xaf420004, 0x24020001, +0x14620003, 0x3c020600, 0x10000002, 0x34423000, +0x34421000, 0xafa20020, 0x8f4a0018, 0xafaa0034, +0x27aa0020, 0xafaa002c, 0x8faa0034, 0x240200ff, +0x11420002, 0x1821, 0x25430001, 0x8c020228, +0x609821, 0x1662000e, 0x3c050009, 0x8f42033c, +0x24420001, 0xaf42033c, 0x8f42033c, 0x8c020228, +0x8fa70034, 0x3c040001, 0x2484610c, 0xafa00014, +0xafa20010, 0x8fa60020, 0x10000070, 0x34a50500, +0x8faa0034, 0xa38c0, 0xf71021, 0x8fa30020, +0x8fa40024, 0xac4304c0, 0xac4404c4, 0x8f830054, +0x8f820054, 0x247103e8, 0x2221023, 0x2c4203e9, +0x1040001b, 0xa821, 0xe09021, 0x265e04c0, +0x8f440178, 0x8f45017c, 0x2401821, 0x240a0004, +0xafaa0010, 0xafb30014, 0x8f48000c, 0x1021, +0x2fe3021, 0xafa80018, 0x8f48010c, 0x24070008, +0xa32821, 0xa3482b, 0x822021, 0x100f809, +0x892021, 0x54400006, 0x24150001, 0x8f820054, +0x2221023, 0x2c4203e9, 0x1440ffe9, 0x0, +0x32a200ff, 0x54400018, 0xaf530018, 0x8f420378, +0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, +0x8faa002c, 0x8fa70034, 0xafa20010, 0x8f820124, +0x3c040001, 0x24846118, 0xafa20014, 0x8d460000, +0x3c050009, 0x10000035, 0x34a50600, 0x8f420308, +0x24150001, 0x24420001, 0xaf420308, 0x8f420308, +0x1000001e, 0x32a200ff, 0x8f830054, 0x8f820054, +0x247103e8, 0x2221023, 0x2c4203e9, 0x10400016, +0xa821, 0x3c1e0020, 0x24120010, 0x8f42000c, +0x8f440160, 0x8f450164, 0x8f860120, 0xafb20010, +0xafb30014, 0x5e1025, 0xafa20018, 0x8f42010c, +0x24070008, 0x40f809, 0x24c6001c, 0x1440ffe3, +0x0, 0x8f820054, 0x2221023, 0x2c4203e9, +0x1440ffee, 0x0, 0x32a200ff, 0x14400011, +0x3c050009, 0x8f420378, 0x24420001, 0xaf420378, +0x8f420378, 0x8f820120, 0x8faa002c, 0x8fa70034, +0xafa20010, 0x8f820124, 0x3c040001, 0x24846120, +0xafa20014, 0x8d460000, 0x34a50700, 0xc002b3b, +0x0, 0x8f4202ec, 0x24420001, 0xaf4202ec, +0x8f4202ec, 0x8f420004, 0x30420001, 0x50400029, +0x36100040, 0x3c020400, 0x2c21024, 0x10400013, +0x2404ffdf, 0x8f420250, 0x8f430254, 0x8f4401b4, +0x14640006, 0x36100040, 0x8f420270, 0x8f430274, +0x8f4401b8, 0x10640007, 0x2402ffdf, 0x8f420250, +0x8f430254, 0x8f440270, 0x8f450274, 0x10000012, +0x3a100020, 0x1000002b, 0x2028024, 0x8f420250, +0x8f430254, 0x8f4501b4, 0x14650006, 0x2048024, +0x8f420270, 0x8f430274, 0x8f4401b8, 0x50640021, +0x36100040, 0x8f420250, 0x8f430254, 0x8f440270, +0x8f450274, 0x3a100040, 0xaf4301b4, 0x10000019, +0xaf4501b8, 0x8f4200d4, 0x24430001, 0x10000011, +0x28420033, 0x8f420004, 0x30420001, 0x10400009, +0x3c020400, 0x2c21024, 0x10400004, 0x2402ffdf, +0x2028024, 0x1000000b, 0x36100040, 0x10000009, +0x36100060, 0x8f4200d4, 0x36100040, 0x24430001, +0x284201f5, 0x14400003, 0xaf4300d4, 0xaf4000d4, +0x3a100020, 0xaf900044, 0x2402ff7f, 0x282a024, +0x8fbf0050, 0x8fbe004c, 0x8fb50048, 0x8fb30044, +0x8fb20040, 0x8fb1003c, 0x8fb00038, 0x3e00008, +0x27bd0058, 0x3e00008, 0x0, 0x3c020001, +0x8c426da8, 0x27bdffb0, 0xafbf0048, 0xafbe0044, +0xafb50040, 0xafb3003c, 0xafb20038, 0xafb10034, +0x104000c7, 0xafb00030, 0x8f4200d0, 0x24430001, +0x2842000b, 0x144000da, 0xaf4300d0, 0x8f420004, +0x30420002, 0x14400097, 0xaf4000d0, 0x8f420004, +0x3c030001, 0x8c636d98, 0x34420002, 0xaf420004, +0x24020001, 0x14620003, 0x3c020600, 0x10000002, +0x34423000, 0x34421000, 0xafa20020, 0x1821, +0x8f5e0018, 0x27aa0020, 0x240200ff, 0x13c20002, +0xafaa002c, 0x27c30001, 0x8c020228, 0x609021, +0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, +0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, +0x2484610c, 0x3c050009, 0xafa00014, 0xafa20010, +0x8fa60020, 0x1000006d, 0x34a50500, 0xf71021, +0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, +0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, +0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, +0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, +0x240a0004, 0xafaa0010, 0xafb20014, 0x8f48000c, +0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, +0x24070008, 0xa32821, 0xa3482b, 0x822021, +0x100f809, 0x892021, 0x54400006, 0x24130001, +0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, +0x0, 0x326200ff, 0x54400017, 0xaf520018, +0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, +0x8f820120, 0x8faa002c, 0xafa20010, 0x8f820124, +0x3c040001, 0x24846118, 0x3c050009, 0xafa20014, +0x8d460000, 0x10000035, 0x34a50600, 0x8f420308, +0x24130001, 0x24420001, 0xaf420308, 0x8f420308, +0x1000001e, 0x326200ff, 0x8f830054, 0x8f820054, +0x247003e8, 0x2021023, 0x2c4203e9, 0x10400016, +0x9821, 0x3c150020, 0x24110010, 0x8f42000c, +0x8f440160, 0x8f450164, 0x8f860120, 0xafb10010, +0xafb20014, 0x551025, 0xafa20018, 0x8f42010c, +0x24070008, 0x40f809, 0x24c6001c, 0x1440ffe3, +0x0, 0x8f820054, 0x2021023, 0x2c4203e9, +0x1440ffee, 0x0, 0x326200ff, 0x14400011, +0x0, 0x8f420378, 0x24420001, 0xaf420378, +0x8f420378, 0x8f820120, 0x8faa002c, 0xafa20010, +0x8f820124, 0x3c040001, 0x24846120, 0x3c050009, +0xafa20014, 0x8d460000, 0x34a50700, 0xc002b3b, +0x3c03821, 0x8f4202ec, 0x24420001, 0xaf4202ec, +0x8f4202ec, 0x8f420004, 0x30420001, 0x10400018, +0x24040001, 0x8f420250, 0x8f430254, 0x8f4501b4, +0x3c010001, 0x14650006, 0xa0246cf1, 0x8f420270, +0x8f430274, 0x8f4401b8, 0x10640021, 0x0, +0x8f420250, 0x8f430254, 0x3c040001, 0x90846cf0, +0x8f460270, 0x8f470274, 0x38840001, 0xaf4301b4, +0xaf4701b8, 0x3c010001, 0x10000025, 0xa0246cf0, +0x8f4200d4, 0x3c010001, 0xa0206cf0, 0x24430001, +0x28420033, 0x1440001e, 0xaf4300d4, 0x3c020001, +0x90426cf1, 0xaf4000d4, 0x10000017, 0x38420001, +0x8f420004, 0x30420001, 0x10400008, 0x0, +0xc00565a, 0x2021, 0x3c010001, 0xa0206cf1, +0x3c010001, 0x1000000e, 0xa0206cf0, 0x8f4200d4, +0x3c010001, 0xa0206cf0, 0x24430001, 0x284201f5, +0x14400007, 0xaf4300d4, 0x3c020001, 0x90426cf1, +0xaf4000d4, 0x421026, 0x3c010001, 0xa0226cf1, +0x3c030001, 0x8c636d98, 0x24020002, 0x1462000c, +0x3c030002, 0x3c030001, 0x90636cf1, 0x24020001, +0x5462001f, 0x2021, 0x3c020001, 0x90426cf0, +0x1443001b, 0x24040005, 0x10000019, 0x24040006, +0x3c020002, 0x8c428ff4, 0x431024, 0x1040000b, +0x24020001, 0x3c030001, 0x90636cf1, 0x54620010, +0x2021, 0x3c020001, 0x90426cf0, 0x1443000c, +0x24040003, 0x1000000a, 0x24040004, 0x3c030001, +0x90636cf1, 0x14620006, 0x2021, 0x3c020001, +0x90426cf0, 0x24040001, 0x50440001, 0x24040002, +0xc00565a, 0x0, 0x2402ff7f, 0x282a024, +0x8fbf0048, 0x8fbe0044, 0x8fb50040, 0x8fb3003c, +0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, +0x27bd0050, 0x3e00008, 0x0, 0x3c020001, +0x8c426da8, 0x27bdffb0, 0xafbf0048, 0xafbe0044, +0xafb50040, 0xafb3003c, 0xafb20038, 0xafb10034, +0x104000de, 0xafb00030, 0x8f4200d0, 0x3c040001, +0x8c846d98, 0x24430001, 0x2842000b, 0xaf4400e8, +0x144000fe, 0xaf4300d0, 0x8f420004, 0x30420002, +0x14400095, 0xaf4000d0, 0x8f420004, 0x34420002, +0xaf420004, 0x24020001, 0x14820003, 0x3c020600, +0x10000002, 0x34423000, 0x34421000, 0xafa20020, +0x1821, 0x8f5e0018, 0x27aa0020, 0x240200ff, +0x13c20002, 0xafaa002c, 0x27c30001, 0x8c020228, +0x609021, 0x1642000e, 0x1e38c0, 0x8f42033c, +0x24420001, 0xaf42033c, 0x8f42033c, 0x8c020228, +0x3c040001, 0x2484610c, 0x3c050009, 0xafa00014, +0xafa20010, 0x8fa60020, 0x1000006d, 0x34a50500, +0xf71021, 0x8fa30020, 0x8fa40024, 0xac4304c0, +0xac4404c4, 0x8f830054, 0x8f820054, 0x247003e8, +0x2021023, 0x2c4203e9, 0x1040001b, 0x9821, +0xe08821, 0x263504c0, 0x8f440178, 0x8f45017c, +0x2201821, 0x240a0004, 0xafaa0010, 0xafb20014, +0x8f48000c, 0x1021, 0x2f53021, 0xafa80018, +0x8f48010c, 0x24070008, 0xa32821, 0xa3482b, +0x822021, 0x100f809, 0x892021, 0x54400006, +0x24130001, 0x8f820054, 0x2021023, 0x2c4203e9, +0x1440ffe9, 0x0, 0x326200ff, 0x54400017, +0xaf520018, 0x8f420378, 0x24420001, 0xaf420378, +0x8f420378, 0x8f820120, 0x8faa002c, 0xafa20010, +0x8f820124, 0x3c040001, 0x24846118, 0x3c050009, +0xafa20014, 0x8d460000, 0x10000035, 0x34a50600, +0x8f420308, 0x24130001, 0x24420001, 0xaf420308, +0x8f420308, 0x1000001e, 0x326200ff, 0x8f830054, +0x8f820054, 0x247003e8, 0x2021023, 0x2c4203e9, +0x10400016, 0x9821, 0x3c150020, 0x24110010, +0x8f42000c, 0x8f440160, 0x8f450164, 0x8f860120, +0xafb10010, 0xafb20014, 0x551025, 0xafa20018, +0x8f42010c, 0x24070008, 0x40f809, 0x24c6001c, +0x1440ffe3, 0x0, 0x8f820054, 0x2021023, +0x2c4203e9, 0x1440ffee, 0x0, 0x326200ff, +0x14400011, 0x0, 0x8f420378, 0x24420001, +0xaf420378, 0x8f420378, 0x8f820120, 0x8faa002c, +0xafa20010, 0x8f820124, 0x3c040001, 0x24846120, +0x3c050009, 0xafa20014, 0x8d460000, 0x34a50700, +0xc002b3b, 0x3c03821, 0x8f4202ec, 0x24420001, +0xaf4202ec, 0x8f4202ec, 0x8f420004, 0x30420001, +0x10400033, 0x3c020400, 0x2c21024, 0x10400017, +0x0, 0x934205c0, 0x8f440250, 0x8f450254, +0x8f4301b4, 0x34420020, 0x14a30006, 0xa34205c0, +0x8f420270, 0x8f430274, 0x8f4401b8, 0x10640008, +0x0, 0x8f420250, 0x8f430254, 0x934405c0, +0x8f460270, 0x8f470274, 0x10000016, 0x38840040, +0x934205c0, 0x10000048, 0x304200bf, 0x934205c0, +0x8f440250, 0x8f450254, 0x8f4301b4, 0x304200bf, +0x14a30006, 0xa34205c0, 0x8f420270, 0x8f430274, +0x8f4401b8, 0x1064000b, 0x0, 0x8f420250, +0x8f430254, 0x934405c0, 0x8f460270, 0x8f470274, +0x38840020, 0xaf4301b4, 0xaf4701b8, 0x10000033, +0xa34405c0, 0x934205c0, 0x1000002f, 0x34420020, +0x934205c0, 0x8f4300d4, 0x34420020, 0xa34205c0, +0x24620001, 0x10000023, 0x28630033, 0x8f4200e4, +0x8f4300e0, 0x24420001, 0xaf4200e4, 0x43102a, +0x14400006, 0x24030001, 0x8f4200e8, 0x14430002, +0xaf4000e4, 0x24030004, 0xaf4300e8, 0x8f420004, +0x30420001, 0x1040000d, 0x3c020400, 0x2c21024, +0x10400007, 0x0, 0x934205c0, 0x34420040, +0xa34205c0, 0x934205c0, 0x1000000f, 0x304200df, +0x934205c0, 0x1000000c, 0x34420060, 0x934205c0, +0x8f4300d4, 0x34420020, 0xa34205c0, 0x24620001, +0x286300fb, 0x14600005, 0xaf4200d4, 0x934205c0, +0xaf4000d4, 0x38420040, 0xa34205c0, 0x934205c0, +0x8f4300e8, 0x3042007f, 0xa34205c0, 0x24020001, +0x14620005, 0x0, 0x934405c0, 0x42102, +0x10000003, 0x348400f0, 0x934405c0, 0x3484000f, +0xc005640, 0x0, 0x2402ff7f, 0x282a024, +0x8fbf0048, 0x8fbe0044, 0x8fb50040, 0x8fb3003c, +0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, +0x27bd0050, 0x3e00008, 0x0, 0x27bdffb0, +0x274401c0, 0x26e30028, 0x24650400, 0x65102b, +0xafbf0048, 0xafbe0044, 0xafb50040, 0xafb3003c, +0xafb20038, 0xafb10034, 0x10400007, 0xafb00030, +0x8c820000, 0xac620000, 0x24630004, 0x65102b, +0x1440fffb, 0x24840004, 0x8c020080, 0xaee20044, +0x8c0200c0, 0xaee20040, 0x8c020084, 0xaee20030, +0x8c020084, 0xaee2023c, 0x8c020088, 0xaee20240, +0x8c02008c, 0xaee20244, 0x8c020090, 0xaee20248, +0x8c020094, 0xaee2024c, 0x8c020098, 0xaee20250, +0x8c02009c, 0xaee20254, 0x8c0200a0, 0xaee20258, +0x8c0200a4, 0xaee2025c, 0x8c0200a8, 0xaee20260, +0x8c0200ac, 0xaee20264, 0x8c0200b0, 0xaee20268, +0x8c0200b4, 0xaee2026c, 0x8c0200b8, 0xaee20270, +0x8c0200bc, 0x24040001, 0xaee20274, 0xaee00034, +0x41080, 0x571021, 0x8ee30034, 0x8c42023c, +0x24840001, 0x621821, 0x2c82000f, 0xaee30034, +0x1440fff8, 0x41080, 0x8c0200cc, 0xaee20048, +0x8c0200d0, 0xaee2004c, 0x8c0200e0, 0xaee201f8, +0x8c0200e4, 0xaee201fc, 0x8c0200e8, 0xaee20200, +0x8c0200ec, 0xaee20204, 0x8c0200f0, 0xaee20208, +0x8ee400c0, 0x8ee500c4, 0x8c0200fc, 0x45102b, +0x1040000b, 0x0, 0x8ee200c0, 0x8ee300c4, +0x24040001, 0x24050000, 0x651821, 0x65302b, +0x441021, 0x461021, 0xaee200c0, 0xaee300c4, +0x8c0200fc, 0x8ee400c0, 0x8ee500c4, 0x2408ffff, +0x24090000, 0x401821, 0x1021, 0x882024, +0xa92824, 0x822025, 0xa32825, 0xaee400c0, +0xaee500c4, 0x8ee400d0, 0x8ee500d4, 0x8c0200f4, +0x45102b, 0x1040000b, 0x0, 0x8ee200d0, +0x8ee300d4, 0x24040001, 0x24050000, 0x651821, +0x65302b, 0x441021, 0x461021, 0xaee200d0, +0xaee300d4, 0x8c0200f4, 0x8ee400d0, 0x8ee500d4, +0x401821, 0x1021, 0x882024, 0xa92824, +0x822025, 0xa32825, 0xaee400d0, 0xaee500d4, +0x8ee400c8, 0x8ee500cc, 0x8c0200f8, 0x45102b, +0x1040000b, 0x0, 0x8ee200c8, 0x8ee300cc, +0x24040001, 0x24050000, 0x651821, 0x65302b, +0x441021, 0x461021, 0xaee200c8, 0xaee300cc, +0x8c0200f8, 0x8ee400c8, 0x8ee500cc, 0x401821, +0x1021, 0x882024, 0xa92824, 0x822025, +0xa32825, 0x24020008, 0xaee400c8, 0xaee500cc, +0xafa20010, 0xafa00014, 0x8f42000c, 0x8c040208, +0x8c05020c, 0xafa20018, 0x8f42010c, 0x26e60028, +0x40f809, 0x24070400, 0x104000f0, 0x3c020400, +0xafa20020, 0x934205c6, 0x10400089, 0x1821, +0x8f5e0018, 0x27aa0020, 0x240200ff, 0x13c20002, +0xafaa002c, 0x27c30001, 0x8c020228, 0x609021, +0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, +0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, +0x2484610c, 0x3c050009, 0xafa00014, 0xafa20010, +0x8fa60020, 0x1000006b, 0x34a50500, 0xf71021, +0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, +0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, +0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, +0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, +0x240a0004, 0xafaa0010, 0xafb20014, 0x8f48000c, +0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, +0x24070008, 0xa32821, 0xa3482b, 0x822021, +0x100f809, 0x892021, 0x54400006, 0x24130001, +0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, +0x0, 0x326200ff, 0x54400017, 0xaf520018, +0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, +0x8f820120, 0x8faa002c, 0xafa20010, 0x8f820124, +0x3c040001, 0x24846118, 0x3c050009, 0xafa20014, +0x8d460000, 0x10000033, 0x34a50600, 0x8f420308, +0x24130001, 0x24420001, 0xaf420308, 0x8f420308, +0x1000001c, 0x326200ff, 0x8f830054, 0x8f820054, +0x247003e8, 0x2021023, 0x2c4203e9, 0x10400014, +0x9821, 0x24110010, 0x8f42000c, 0x8f440160, +0x8f450164, 0x8f860120, 0xafb10010, 0xafb20014, +0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, +0x24c6001c, 0x1440ffe5, 0x0, 0x8f820054, +0x2021023, 0x2c4203e9, 0x1440ffef, 0x0, +0x326200ff, 0x54400012, 0x24020001, 0x8f420378, +0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, +0x8faa002c, 0xafa20010, 0x8f820124, 0x3c040001, +0x24846120, 0x3c050009, 0xafa20014, 0x8d460000, +0x34a50700, 0xc002b3b, 0x3c03821, 0x1021, +0x1440005b, 0x24020001, 0x10000065, 0x0, +0x8f510018, 0x240200ff, 0x12220002, 0x8021, +0x26300001, 0x8c020228, 0x1602000e, 0x1130c0, +0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, +0x8c020228, 0x3c040001, 0x248460f4, 0x3c050009, +0xafa00014, 0xafa20010, 0x8fa60020, 0x1000003f, +0x34a50100, 0xd71021, 0x8fa30020, 0x8fa40024, +0xac4304c0, 0xac4404c4, 0xc01821, 0x8f440178, +0x8f45017c, 0x1021, 0x24070004, 0xafa70010, +0xafb00014, 0x8f48000c, 0x24c604c0, 0x2e63021, +0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, +0xa3482b, 0x822021, 0x100f809, 0x892021, +0x1440000b, 0x24070008, 0x8f820120, 0xafa20010, +0x8f820124, 0x3c040001, 0x248460fc, 0x3c050009, +0xafa20014, 0x8fa60020, 0x1000001c, 0x34a50200, +0x8f440160, 0x8f450164, 0x8f43000c, 0xaf500018, +0x8f860120, 0x24020010, 0xafa20010, 0xafb00014, +0xafa30018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x54400011, 0x24020001, 0x8f420340, 0x24420001, +0xaf420340, 0x8f420340, 0x8f820120, 0xafa20010, +0x8f820124, 0x3c040001, 0x24846104, 0x3c050009, +0xafa20014, 0x8fa60020, 0x34a50300, 0xc002b3b, +0x2203821, 0x1021, 0x1040000d, 0x24020001, +0x8f4202e8, 0xa34005c6, 0xaf4001b0, 0x24420001, +0xaf4202e8, 0x8f4202e8, 0x8ee20150, 0x24420001, +0xaee20150, 0x10000003, 0x8ee20150, 0x24020001, +0xa34205c6, 0x8fbf0048, 0x8fbe0044, 0x8fb50040, +0x8fb3003c, 0x8fb20038, 0x8fb10034, 0x8fb00030, +0x3e00008, 0x27bd0050, 0x27bdffd8, 0xafbf0020, +0x8f8200b0, 0x30420004, 0x10400068, 0x0, +0x8f430128, 0x8f820104, 0x14620005, 0x0, +0x8f430130, 0x8f8200b4, 0x10620006, 0x0, +0x8f820104, 0xaf420128, 0x8f8200b4, 0x1000005b, +0xaf420130, 0x8f8200b0, 0x3c030080, 0x431024, +0x1040000d, 0x0, 0x8f82011c, 0x34420002, +0xaf82011c, 0x8f8200b0, 0x2403fffb, 0x431024, +0xaf8200b0, 0x8f82011c, 0x2403fffd, 0x431024, +0x1000004a, 0xaf82011c, 0x8f430128, 0x8f820104, +0x14620005, 0x0, 0x8f430130, 0x8f8200b4, +0x10620010, 0x0, 0x8f820104, 0xaf420128, +0x8f8200b4, 0x8f430128, 0xaf420130, 0xafa30010, +0x8f420130, 0x3c040001, 0x24846144, 0xafa20014, +0x8f86011c, 0x8f8700b0, 0x3c050005, 0x10000031, +0x34a50900, 0x8f420128, 0xafa20010, 0x8f420130, +0x3c040001, 0x24846150, 0xafa20014, 0x8f86011c, +0x8f8700b0, 0x3c050005, 0xc002b3b, 0x34a51000, +0x8f82011c, 0x34420002, 0xaf82011c, 0x8f830104, +0x8f8200b0, 0x34420001, 0xaf8200b0, 0x24020008, +0xaf830104, 0xafa20010, 0xafa00014, 0x8f42000c, +0x8c040208, 0x8c05020c, 0xafa20018, 0x8f42010c, +0x26e60028, 0x40f809, 0x24070400, 0x8f82011c, +0x2403fffd, 0x431024, 0xaf82011c, 0x8ee201dc, +0x24420001, 0xaee201dc, 0x8ee201dc, 0x8f420128, +0xafa20010, 0x8f420130, 0x3c040001, 0x2484615c, +0xafa20014, 0x8f86011c, 0x8f8700b0, 0x3c050005, +0x34a51100, 0xc002b3b, 0x0, 0x8f8200a0, +0x30420004, 0x10400069, 0x0, 0x8f43012c, +0x8f820124, 0x14620005, 0x0, 0x8f430134, +0x8f8200a4, 0x10620006, 0x0, 0x8f820124, +0xaf42012c, 0x8f8200a4, 0x1000005c, 0xaf420134, +0x8f8200a0, 0x3c030080, 0x431024, 0x1040000d, +0x0, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f8200a0, 0x2403fffb, 0x431024, 0xaf8200a0, +0x8f82011c, 0x2403fffd, 0x431024, 0x1000004b, +0xaf82011c, 0x8f43012c, 0x8f820124, 0x14620005, +0x0, 0x8f430134, 0x8f8200a4, 0x10620010, +0x0, 0x8f820124, 0xaf42012c, 0x8f8200a4, +0x8f43012c, 0xaf420134, 0xafa30010, 0x8f420134, +0x3c040001, 0x24846168, 0xafa20014, 0x8f86011c, +0x8f8700a0, 0x3c050005, 0x10000032, 0x34a51200, +0x8f42012c, 0xafa20010, 0x8f420134, 0x3c040001, +0x24846174, 0xafa20014, 0x8f86011c, 0x8f8700a0, +0x3c050005, 0xc002b3b, 0x34a51300, 0x8f82011c, +0x34420002, 0xaf82011c, 0x8f830124, 0x8f8200a0, +0x34420001, 0xaf8200a0, 0x24020080, 0xaf830124, +0xafa20010, 0xafa00014, 0x8f420014, 0x8c040208, +0x8c05020c, 0xafa20018, 0x8f420108, 0x3c060001, +0x24c66ed8, 0x40f809, 0x24070004, 0x8f82011c, +0x2403fffd, 0x431024, 0xaf82011c, 0x8ee201dc, +0x24420001, 0xaee201dc, 0x8ee201dc, 0x8f42012c, +0xafa20010, 0x8f420134, 0x3c040001, 0x24846180, +0xafa20014, 0x8f86011c, 0x8f8700a0, 0x3c050005, +0x34a51400, 0xc002b3b, 0x0, 0x8fbf0020, +0x3e00008, 0x27bd0028, 0x3c081000, 0x24070001, +0x3c060080, 0x3c050100, 0x8f820070, 0x481024, +0x1040fffd, 0x0, 0x8f820054, 0x24420005, +0xaf820078, 0x8c040234, 0x10800016, 0x1821, +0x3c020001, 0x571021, 0x8c4240e8, 0x24420005, +0x3c010001, 0x370821, 0xac2240e8, 0x3c020001, +0x571021, 0x8c4240e8, 0x44102b, 0x14400009, +0x0, 0x3c030080, 0x3c010001, 0x370821, +0xac2040e8, 0x3c010001, 0x370821, 0x1000000b, +0xa02740f0, 0x3c020001, 0x571021, 0x904240f0, +0x54400006, 0x661825, 0x3c020001, 0x571021, +0x904240f1, 0x54400001, 0x661825, 0x8c040230, +0x10800013, 0x0, 0x3c020001, 0x571021, +0x8c4240ec, 0x24420005, 0x3c010001, 0x370821, +0xac2240ec, 0x3c020001, 0x571021, 0x8c4240ec, +0x44102b, 0x14400006, 0x0, 0x3c010001, +0x370821, 0xac2040ec, 0x10000006, 0x651825, +0x3c020001, 0x571021, 0x904240f2, 0x54400001, +0x651825, 0x1060ffbc, 0x0, 0x8f420000, +0x10400007, 0x0, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x431025, 0xaf820060, 0x8f420000, +0x10400003, 0x0, 0x1000ffa7, 0xaf80004c, +0x1000ffa5, 0xaf800048, 0x3e00008, 0x0, +0x0, 0x0, 0x0, 0x27bdffe0, +0xafbf0018, 0x8f860064, 0x30c20004, 0x10400025, +0x24040004, 0x8c020114, 0xaf420020, 0xaf840064, +0x8f4202fc, 0x24420001, 0xaf4202fc, 0x8f4202fc, +0x8f820064, 0x30420004, 0x14400005, 0x0, +0x8c030114, 0x8f420020, 0x1462fff2, 0x0, +0x8f420000, 0x10400007, 0x8f43003c, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x431025, 0xaf820060, +0x8f420000, 0x10400073, 0x0, 0x1000006f, +0x0, 0x30c20008, 0x10400020, 0x24040008, +0x8c02011c, 0xaf420048, 0xaf840064, 0x8f4202a8, +0x24420001, 0xaf4202a8, 0x8f4202a8, 0x8f820064, +0x30420008, 0x14400005, 0x0, 0x8c03011c, +0x8f420048, 0x1462fff2, 0x0, 0x8f420000, +0x10400007, 0x0, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x1000ffd9, 0x34420200, 0x30c20020, +0x10400023, 0x24040020, 0x8c02012c, 0xaf420068, +0xaf840064, 0x8f4202d8, 0x24420001, 0xaf4202d8, +0x8f4202d8, 0x8f820064, 0x30420020, 0x14400005, +0x32c24000, 0x8c03012c, 0x8f420068, 0x1462fff2, +0x32c24000, 0x14400002, 0x3c020001, 0x2c2b025, +0x8f420000, 0x10400007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x1000ffb4, 0x34420800, +0x30c20010, 0x10400029, 0x24040010, 0x8c020124, +0xaf420058, 0xaf840064, 0x8f4202d4, 0x24420001, +0xaf4202d4, 0x8f4202d4, 0x8f820064, 0x30420010, +0x14400005, 0x32c22000, 0x8c030124, 0x8f420058, +0x1462fff2, 0x32c22000, 0x50400001, 0x36d68000, +0x8f420000, 0x10400007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x34420100, 0xaf820060, +0x8f420000, 0x10400003, 0x0, 0x1000006c, +0xaf80004c, 0x1000006a, 0xaf800048, 0x30c20001, +0x10400004, 0x24020001, 0xaf820064, 0x10000064, +0x0, 0x30c20002, 0x1440000b, 0x3c050003, +0x3c040001, 0x24846244, 0x34a50500, 0x3821, +0xafa00010, 0xc002b3b, 0xafa00014, 0x2402ffc0, +0x10000057, 0xaf820064, 0x8c05022c, 0x8c02010c, +0x10a20048, 0x51080, 0x8c460300, 0x24a20001, +0x3045003f, 0x24020003, 0xac05022c, 0x61e02, +0x10620005, 0x24020010, 0x1062001d, 0x30c20fff, +0x10000039, 0x0, 0x8f4302a8, 0x8f440000, +0x30c20fff, 0xaf420048, 0x24630001, 0xaf4302a8, +0x10800007, 0x8f4202a8, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x34420200, 0xaf820060, 0x8f420000, +0x1040001f, 0x0, 0x1000001b, 0x0, +0xaf420058, 0x32c22000, 0x50400001, 0x36d68000, +0x8f4202d4, 0x8f430000, 0x24420001, 0xaf4202d4, +0x10600007, 0x8f4202d4, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x34420100, 0xaf820060, 0x8f420000, +0x10400003, 0x0, 0x10000006, 0xaf80004c, +0x10000004, 0xaf800048, 0xc002196, 0xc02021, +0x402821, 0x8c02010c, 0x14a20002, 0x24020002, +0xaf820064, 0x8f820064, 0x30420002, 0x14400004, +0x0, 0x8c02010c, 0x14a2ffac, 0x0, +0x8fbf0018, 0x3e00008, 0x27bd0020, 0x3e00008, +0x0, 0x27bdffa0, 0xafb00040, 0x808021, +0x101602, 0x2442ffff, 0x304300ff, 0x2c620013, +0xafbf0058, 0xafbe0054, 0xafb50050, 0xafb3004c, +0xafb20048, 0xafb10044, 0x104001f3, 0xafa50034, +0x31080, 0x3c010001, 0x220821, 0x8c226288, +0x400008, 0x0, 0x101302, 0x30440fff, +0x24020001, 0x10820005, 0x24020002, 0x1082000c, +0x2402fffe, 0x10000024, 0x3c050003, 0x8f430004, +0x3c020001, 0x8c426f04, 0xaf440200, 0xaf440204, +0x3c040001, 0x8c846e80, 0x10000009, 0x34630001, +0x8f430004, 0xaf440200, 0xaf440204, 0x3c040001, +0x8c846e80, 0x621824, 0x3c020001, 0x2442ca28, +0x21100, 0x21182, 0xaf430004, 0x3c030800, +0x431025, 0xac820038, 0x8f840054, 0x41442, +0x41c82, 0x431021, 0x41cc2, 0x431023, +0x41d02, 0x431021, 0x41d42, 0x431023, +0x10000009, 0xaf420208, 0x3c040001, 0x24846250, +0x34a51000, 0x2003021, 0x3821, 0xafa00010, +0xc002b3b, 0xafa00014, 0x8f4202a0, 0x24420001, +0xaf4202a0, 0x1000021f, 0x8f4202a0, 0x27b00028, +0x2002021, 0x24050210, 0xc002bbf, 0x24060008, +0xc002518, 0x2002021, 0x10000216, 0x0, +0x8faa0034, 0x27a40028, 0xa1880, 0x25420001, +0x3042003f, 0xafa20034, 0x8c650300, 0x8faa0034, +0x21080, 0x8c430300, 0x25420001, 0x3042003f, +0xafa20034, 0xac02022c, 0xafa50028, 0xc002518, +0xafa3002c, 0x10000203, 0x0, 0x27b00028, +0x2002021, 0x24050210, 0xc002bbf, 0x24060008, +0xc002657, 0x2002021, 0x100001fa, 0x0, +0x8faa0034, 0x27a40028, 0xa1880, 0x25420001, +0x3042003f, 0xafa20034, 0x8c650300, 0x8faa0034, +0x21080, 0x8c430300, 0x25420001, 0x3042003f, +0xafa20034, 0xac02022c, 0xafa50028, 0xc002657, +0xafa3002c, 0x100001e7, 0x0, 0x101302, +0x30430fff, 0x24020001, 0x10620005, 0x24020002, +0x1062001e, 0x3c020002, 0x10000033, 0x3c050003, +0x3c030002, 0x2c31024, 0x54400037, 0x2c3b025, +0x8f820228, 0x3c010001, 0x370821, 0xac2238d8, +0x8f82022c, 0x3c010001, 0x370821, 0xac2238dc, +0x8f820230, 0x3c010001, 0x370821, 0xac2238e0, +0x8f820234, 0x3c010001, 0x370821, 0xac2238e4, +0x2402ffff, 0xaf820228, 0xaf82022c, 0xaf820230, +0xaf820234, 0x10000020, 0x2c3b025, 0x2c21024, +0x10400012, 0x3c02fffd, 0x3c020001, 0x571021, +0x8c4238d8, 0xaf820228, 0x3c020001, 0x571021, +0x8c4238dc, 0xaf82022c, 0x3c020001, 0x571021, +0x8c4238e0, 0xaf820230, 0x3c020001, 0x571021, +0x8c4238e4, 0xaf820234, 0x3c02fffd, 0x3442ffff, +0x10000009, 0x2c2b024, 0x3c040001, 0x2484625c, +0x34a51100, 0x2003021, 0x3821, 0xafa00010, +0xc002b3b, 0xafa00014, 0x8f4202cc, 0x24420001, +0xaf4202cc, 0x1000019f, 0x8f4202cc, 0x101302, +0x30450fff, 0x24020001, 0x10a20005, 0x24020002, +0x10a2000d, 0x3c0408ff, 0x10000014, 0x3c050003, +0x3c0208ff, 0x3442ffff, 0x8f830220, 0x3c040004, +0x2c4b025, 0x621824, 0x34630008, 0xaf830220, +0x10000012, 0xaf450298, 0x3484fff7, 0x3c03fffb, +0x8f820220, 0x3463ffff, 0x2c3b024, 0x441024, +0xaf820220, 0x10000009, 0xaf450298, 0x3c040001, +0x24846268, 0x34a51200, 0x2003021, 0x3821, +0xafa00010, 0xc002b3b, 0xafa00014, 0x8f4202bc, +0x24420001, 0xaf4202bc, 0x10000176, 0x8f4202bc, +0x27840208, 0x24050200, 0xc002bbf, 0x24060008, +0x27440224, 0x24050200, 0xc002bbf, 0x24060008, +0x8f4202c4, 0x24420001, 0xaf4202c4, 0x10000169, +0x8f4202c4, 0x101302, 0x30430fff, 0x24020001, +0x10620011, 0x28620002, 0x50400005, 0x24020002, +0x10600007, 0x0, 0x10000017, 0x0, +0x1062000f, 0x0, 0x10000013, 0x0, +0x8c060248, 0x2021, 0xc005104, 0x24050004, +0x10000007, 0x0, 0x8c060248, 0x2021, +0xc005104, 0x24050004, 0x10000010, 0x0, +0x8c06024c, 0x2021, 0xc005104, 0x24050001, +0x1000000a, 0x0, 0x3c040001, 0x24846274, +0x3c050003, 0x34a51300, 0x2003021, 0x3821, +0xafa00010, 0xc002b3b, 0xafa00014, 0x8f4202c0, +0x24420001, 0xaf4202c0, 0x1000013a, 0x8f4202c0, +0xc002426, 0x0, 0x10000136, 0x0, +0x24020001, 0xa34205c5, 0x24100100, 0x8f4401a8, +0x8f4501ac, 0xafb00010, 0xafa00014, 0x8f420014, +0xafa20018, 0x8f420108, 0x26e60028, 0x40f809, +0x24070400, 0x1040fff5, 0x0, 0x10000125, +0x0, 0x3c03ffff, 0x34637fff, 0x8f420368, +0x8f440360, 0x2c3b024, 0x1821, 0xaf400058, +0xaf40005c, 0xaf400060, 0xaf400064, 0x441023, +0xaf420368, 0x3c020900, 0xaf400360, 0xafa20020, +0x8f5e0018, 0x27aa0020, 0x240200ff, 0x13c20002, +0xafaa003c, 0x27c30001, 0x8c020228, 0x609021, +0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, +0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, +0x2484620c, 0x3c050009, 0xafa00014, 0xafa20010, +0x8fa60020, 0x1000006b, 0x34a50500, 0xf71021, +0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, +0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, +0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, +0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, +0x240a0004, 0xafaa0010, 0xafb20014, 0x8f48000c, +0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, +0x24070008, 0xa32821, 0xa3482b, 0x822021, +0x100f809, 0x892021, 0x54400006, 0x24130001, +0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, +0x0, 0x326200ff, 0x54400017, 0xaf520018, +0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, +0x8f820120, 0x8faa003c, 0xafa20010, 0x8f820124, +0x3c040001, 0x24846218, 0x3c050009, 0xafa20014, +0x8d460000, 0x10000033, 0x34a50600, 0x8f420308, +0x24130001, 0x24420001, 0xaf420308, 0x8f420308, +0x1000001c, 0x326200ff, 0x8f830054, 0x8f820054, +0x247003e8, 0x2021023, 0x2c4203e9, 0x10400014, +0x9821, 0x24110010, 0x8f42000c, 0x8f440160, +0x8f450164, 0x8f860120, 0xafb10010, 0xafb20014, +0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, +0x24c6001c, 0x1440ffe5, 0x0, 0x8f820054, +0x2021023, 0x2c4203e9, 0x1440ffef, 0x0, +0x326200ff, 0x14400011, 0x0, 0x8f420378, +0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, +0x8faa003c, 0xafa20010, 0x8f820124, 0x3c040001, +0x24846220, 0x3c050009, 0xafa20014, 0x8d460000, +0x34a50700, 0xc002b3b, 0x3c03821, 0x8f4202b0, +0x24420001, 0xaf4202b0, 0x8f4202b0, 0x8f4202f8, +0x24420001, 0xaf4202f8, 0x1000008a, 0x8f4202f8, +0x8c02025c, 0x27440224, 0xaf4201f0, 0x8c020260, +0x24050200, 0x24060008, 0xc002bbf, 0xaf4201f8, +0x8f820220, 0x30420008, 0x14400002, 0x24020001, +0x24020002, 0xaf420298, 0x8f4202ac, 0x24420001, +0xaf4202ac, 0x10000077, 0x8f4202ac, 0x3c0200ff, +0x3442ffff, 0x2021824, 0x32c20180, 0x14400006, +0x3402fffb, 0x43102b, 0x14400003, 0x0, +0x1000006c, 0xaf4300bc, 0x3c040001, 0x24846280, +0x3c050003, 0x34a51500, 0x2003021, 0x3821, +0xafa00010, 0xc002b3b, 0xafa00014, 0x3c020700, +0x34421000, 0x101e02, 0x621825, 0xafa30020, +0x8f510018, 0x240200ff, 0x12220002, 0x8021, +0x26300001, 0x8c020228, 0x1602000e, 0x1130c0, +0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, +0x8c020228, 0x3c040001, 0x248461f4, 0x3c050009, +0xafa00014, 0xafa20010, 0x8fa60020, 0x1000003f, +0x34a50100, 0xd71021, 0x8fa30020, 0x8fa40024, +0xac4304c0, 0xac4404c4, 0xc01821, 0x8f440178, +0x8f45017c, 0x1021, 0x24070004, 0xafa70010, +0xafb00014, 0x8f48000c, 0x24c604c0, 0x2e63021, +0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, +0xa3482b, 0x822021, 0x100f809, 0x892021, +0x1440000b, 0x24070008, 0x8f820120, 0xafa20010, +0x8f820124, 0x3c040001, 0x248461fc, 0x3c050009, +0xafa20014, 0x8fa60020, 0x1000001c, 0x34a50200, +0x8f440160, 0x8f450164, 0x8f43000c, 0xaf500018, +0x8f860120, 0x24020010, 0xafa20010, 0xafb00014, +0xafa30018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x14400010, 0x0, 0x8f420340, 0x24420001, +0xaf420340, 0x8f420340, 0x8f820120, 0xafa20010, +0x8f820124, 0x3c040001, 0x24846204, 0x3c050009, +0xafa20014, 0x8fa60020, 0x34a50300, 0xc002b3b, +0x2203821, 0x8f4202e0, 0x24420001, 0xaf4202e0, +0x8f4202e0, 0x8f4202f0, 0x24420001, 0xaf4202f0, +0x8f4202f0, 0x8fa20034, 0x8fbf0058, 0x8fbe0054, +0x8fb50050, 0x8fb3004c, 0x8fb20048, 0x8fb10044, +0x8fb00040, 0x3e00008, 0x27bd0060, 0x27bdfff8, +0x2408ffff, 0x10a00014, 0x4821, 0x3c0aedb8, +0x354a8320, 0x90870000, 0x24840001, 0x3021, +0x1071026, 0x30420001, 0x10400002, 0x81842, +0x6a1826, 0x604021, 0x24c60001, 0x2cc20008, +0x1440fff7, 0x73842, 0x25290001, 0x125102b, +0x1440fff0, 0x0, 0x1001021, 0x3e00008, +0x27bd0008, 0x27bdffb0, 0xafbf0048, 0xafbe0044, +0xafb50040, 0xafb3003c, 0xafb20038, 0xafb10034, +0xafb00030, 0x8f870220, 0xafa70024, 0x8f870200, +0xafa7002c, 0x8f820220, 0x3c0308ff, 0x3463ffff, +0x431024, 0x34420004, 0xaf820220, 0x8f820200, +0x3c03c0ff, 0x3463ffff, 0x431024, 0x34420004, +0xaf820200, 0x8f530358, 0x8f55035c, 0x8f5e0360, +0x8f470364, 0xafa70014, 0x8f470368, 0xafa7001c, +0x8f4202d0, 0x274401c0, 0x24420001, 0xaf4202d0, +0x8f5002d0, 0x8f510204, 0x8f520200, 0xc002ba8, +0x24050400, 0xaf530358, 0xaf55035c, 0xaf5e0360, +0x8fa70014, 0xaf470364, 0x8fa7001c, 0xaf470368, +0xaf5002d0, 0xaf510204, 0xaf520200, 0x8c02025c, +0x27440224, 0xaf4201f0, 0x8c020260, 0x24050200, +0x24060008, 0xaf4201f8, 0x24020006, 0xc002bbf, +0xaf4201f4, 0x3c023b9a, 0x3442ca00, 0xaf4201fc, +0x240203e8, 0x24040002, 0x24030001, 0xaf420294, +0xaf440290, 0xaf43029c, 0x8f820220, 0x30420008, +0x10400004, 0x0, 0xaf430298, 0x10000003, +0x3021, 0xaf440298, 0x3021, 0x3c030001, +0x661821, 0x90636d00, 0x3461021, 0x24c60001, +0xa043022c, 0x2cc2000f, 0x1440fff8, 0x3461821, +0x24c60001, 0x8f820040, 0x24040080, 0x24050080, +0x21702, 0x24420030, 0xa062022c, 0x3461021, +0xc002ba8, 0xa040022c, 0x8fa70024, 0x30e20004, +0x14400006, 0x0, 0x8f820220, 0x3c0308ff, +0x3463fffb, 0x431024, 0xaf820220, 0x8fa7002c, +0x30e20004, 0x14400006, 0x0, 0x8f820200, +0x3c03c0ff, 0x3463fffb, 0x431024, 0xaf820200, +0x8fbf0048, 0x8fbe0044, 0x8fb50040, 0x8fb3003c, +0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, +0x27bd0050, 0x0, 0x0, 0xaf400104, +0x24040001, 0x410c0, 0x2e21821, 0x24820001, +0x3c010001, 0x230821, 0xa42234d0, 0x402021, +0x2c820080, 0x1440fff8, 0x410c0, 0x24020001, +0x3c010001, 0x370821, 0xa42038d0, 0xaf420100, +0xaf800228, 0xaf80022c, 0xaf800230, 0xaf800234, +0x3e00008, 0x0, 0x27bdffe8, 0xafbf0014, +0xafb00010, 0x8f420104, 0x28420005, 0x10400026, +0x808021, 0x3c020001, 0x8f430104, 0x344230d0, +0x2e22021, 0x318c0, 0x621821, 0x2e31821, +0x83102b, 0x10400015, 0x1021, 0x96070000, +0x24840006, 0x24660006, 0x9482fffc, 0x14470009, +0x2821, 0x9483fffe, 0x96020002, 0x14620006, +0xa01021, 0x94820000, 0x96030004, 0x431026, +0x2c450001, 0xa01021, 0x14400009, 0x24840008, +0x86102b, 0x1440fff0, 0x1021, 0x304200ff, +0x14400030, 0x24020001, 0x1000002e, 0x1021, +0x1000fffa, 0x24020001, 0x2002021, 0xc00240c, +0x24050006, 0x3042007f, 0x218c0, 0x2e31021, +0x3c010001, 0x220821, 0x942230d0, 0x1040fff2, +0x2e31021, 0x3c060001, 0xc23021, 0x94c630d0, +0x10c0ffed, 0x3c080001, 0x350834d2, 0x96070000, +0x610c0, 0x572021, 0x882021, 0x94820000, +0x14470009, 0x2821, 0x94830002, 0x96020002, +0x14620006, 0xa01021, 0x94820004, 0x96030004, +0x431026, 0x2c450001, 0xa01021, 0x14400007, +0x610c0, 0x2e21021, 0x3c060001, 0xc23021, +0x94c634d0, 0x14c0ffeb, 0x610c0, 0x10c0ffd2, +0x24020001, 0x8fbf0014, 0x8fb00010, 0x3e00008, +0x27bd0018, 0x3e00008, 0x0, 0x27bdffb0, +0x801021, 0xafb00030, 0x24500002, 0x2002021, +0x24050006, 0xafb10034, 0x408821, 0xafbf0048, +0xafbe0044, 0xafb50040, 0xafb3003c, 0xc00240c, +0xafb20038, 0x3047007f, 0x710c0, 0x2e21021, +0x3c050001, 0xa22821, 0x94a530d0, 0x50a0001c, +0xa03021, 0x3c090001, 0x352934d2, 0x96280002, +0x510c0, 0x572021, 0x892021, 0x94820000, +0x14480009, 0x3021, 0x94830002, 0x96020002, +0x14620006, 0xc01021, 0x94820004, 0x96030004, +0x431026, 0x2c460001, 0xc01021, 0x14400007, +0x510c0, 0x2e21021, 0x3c050001, 0xa22821, +0x94a534d0, 0x14a0ffeb, 0x510c0, 0xa03021, +0x10c00014, 0x610c0, 0x571821, 0x3c010001, +0x230821, 0x8c2334d0, 0x571021, 0xafa30010, +0x3c010001, 0x220821, 0x8c2234d4, 0x3c040001, +0x24846394, 0xafa20014, 0x8e260000, 0x8e270004, +0x3c050004, 0xc002b3b, 0x34a50400, 0x10000063, +0x3c020800, 0x8f450100, 0x10a00006, 0x510c0, +0x2e21021, 0x3c010001, 0x220821, 0x942234d0, +0xaf420100, 0xa03021, 0x14c00011, 0x628c0, +0x710c0, 0x2e21021, 0xafa70010, 0x3c010001, +0x220821, 0x942230d0, 0x3c040001, 0x248463a0, +0xafa20014, 0x8e260000, 0x8e270004, 0x3c050004, +0xc002b3b, 0x34a50500, 0x10000048, 0x3c020800, +0xb71821, 0x3c020001, 0x96040000, 0x344234d2, +0x621821, 0xa4640000, 0x8e020002, 0x720c0, +0xac620002, 0x2e41021, 0x3c030001, 0x621821, +0x946330d0, 0x2e51021, 0x3c010001, 0x220821, +0xa42334d0, 0x2e41021, 0x3c010001, 0x220821, +0xa42630d0, 0x8f420104, 0x24420001, 0x28420080, +0x1040000f, 0x3c020002, 0x8f420104, 0x3c040001, +0x348430d2, 0x96030000, 0x210c0, 0x571021, +0x441021, 0xa4430000, 0x8e030002, 0xac430002, +0x8f420104, 0x24420001, 0xaf420104, 0x3c020002, +0x2c21024, 0x10400011, 0x72142, 0x3c030001, +0x346338d8, 0x24020003, 0x441023, 0x21080, +0x572021, 0x832021, 0x571021, 0x431021, +0x30e5001f, 0x8c430000, 0x24020001, 0xa21004, +0x621825, 0x1000000c, 0xac830000, 0x24020003, +0x441023, 0x21080, 0x5c2821, 0x5c1021, +0x30e4001f, 0x8c430228, 0x24020001, 0x821004, +0x621825, 0xaca30228, 0x3c020800, 0x34421000, +0x1821, 0xafa20020, 0x8f5e0018, 0x27aa0020, +0x240200ff, 0x13c20002, 0xafaa002c, 0x27c30001, +0x8c020228, 0x609021, 0x1642000e, 0x1e38c0, +0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, +0x8c020228, 0x3c040001, 0x2484635c, 0x3c050009, +0xafa00014, 0xafa20010, 0x8fa60020, 0x1000006b, +0x34a50500, 0xf71021, 0x8fa30020, 0x8fa40024, +0xac4304c0, 0xac4404c4, 0x8f830054, 0x8f820054, +0x247003e8, 0x2021023, 0x2c4203e9, 0x1040001b, +0x9821, 0xe08821, 0x263504c0, 0x8f440178, +0x8f45017c, 0x2201821, 0x240a0004, 0xafaa0010, +0xafb20014, 0x8f48000c, 0x1021, 0x2f53021, +0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, +0xa3482b, 0x822021, 0x100f809, 0x892021, +0x54400006, 0x24130001, 0x8f820054, 0x2021023, +0x2c4203e9, 0x1440ffe9, 0x0, 0x326200ff, +0x54400017, 0xaf520018, 0x8f420378, 0x24420001, +0xaf420378, 0x8f420378, 0x8f820120, 0x8faa002c, +0xafa20010, 0x8f820124, 0x3c040001, 0x24846368, +0x3c050009, 0xafa20014, 0x8d460000, 0x10000033, +0x34a50600, 0x8f420308, 0x24130001, 0x24420001, +0xaf420308, 0x8f420308, 0x1000001c, 0x326200ff, +0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, +0x2c4203e9, 0x10400014, 0x9821, 0x24110010, +0x8f42000c, 0x8f440160, 0x8f450164, 0x8f860120, +0xafb10010, 0xafb20014, 0xafa20018, 0x8f42010c, +0x24070008, 0x40f809, 0x24c6001c, 0x1440ffe5, +0x0, 0x8f820054, 0x2021023, 0x2c4203e9, +0x1440ffef, 0x0, 0x326200ff, 0x14400011, +0x0, 0x8f420378, 0x24420001, 0xaf420378, +0x8f420378, 0x8f820120, 0x8faa002c, 0xafa20010, +0x8f820124, 0x3c040001, 0x24846370, 0x3c050009, +0xafa20014, 0x8d460000, 0x34a50700, 0xc002b3b, +0x3c03821, 0x8f4202b4, 0x24420001, 0xaf4202b4, +0x8f4202b4, 0x8f4202f4, 0x24420001, 0xaf4202f4, +0x8f4202f4, 0x8fbf0048, 0x8fbe0044, 0x8fb50040, +0x8fb3003c, 0x8fb20038, 0x8fb10034, 0x8fb00030, +0x3e00008, 0x27bd0050, 0x27bdffa0, 0x801021, +0xafb00040, 0x24500002, 0x2002021, 0x24050006, +0xafb10044, 0x408821, 0xafbf0058, 0xafbe0054, +0xafb50050, 0xafb3004c, 0xc00240c, 0xafb20048, +0x3048007f, 0x810c0, 0x2e21021, 0x3c060001, +0xc23021, 0x94c630d0, 0x10c0001c, 0x3821, +0x3c0a0001, 0x354a34d2, 0x96290002, 0x610c0, +0x572021, 0x8a2021, 0x94820000, 0x14490009, +0x2821, 0x94830002, 0x96020002, 0x14620006, +0xa01021, 0x94820004, 0x96030004, 0x431026, +0x2c450001, 0xa01021, 0x14400008, 0x610c0, +0xc03821, 0x2e21021, 0x3c060001, 0xc23021, +0x94c634d0, 0x14c0ffea, 0x610c0, 0x14c00011, +0xafa70028, 0x810c0, 0x2e21021, 0xafa80010, +0x3c010001, 0x220821, 0x942230d0, 0x3c040001, +0x248463ac, 0xafa20014, 0x8e260000, 0x8e270004, +0x3c050004, 0xc002b3b, 0x34a50900, 0x10000075, +0x3c020800, 0x10e0000c, 0x610c0, 0x2e21021, +0x3c030001, 0x621821, 0x946334d0, 0x710c0, +0x2e21021, 0x3c010001, 0x220821, 0xa42334d0, +0x1000000b, 0x3c040001, 0x2e21021, 0x3c030001, +0x621821, 0x946334d0, 0x810c0, 0x2e21021, +0x3c010001, 0x220821, 0xa42330d0, 0x3c040001, +0x348430d0, 0x8f430100, 0x610c0, 0x2e21021, +0x3c010001, 0x220821, 0xa42334d0, 0x8f420104, +0x2e43821, 0x2821, 0x18400029, 0xaf460100, +0x24e60006, 0x94c3fffc, 0x96020000, 0x14620009, +0x2021, 0x94c3fffe, 0x96020002, 0x14620006, +0x801021, 0x94c20000, 0x96030004, 0x431026, +0x2c440001, 0x801021, 0x50400014, 0x24a50001, +0x8f420104, 0x2442ffff, 0xa2102a, 0x1040000b, +0x24e40004, 0x94820006, 0x8c830008, 0xa482fffe, +0xac830000, 0x8f420104, 0x24a50001, 0x2442ffff, +0xa2102a, 0x1440fff7, 0x24840008, 0x8f420104, +0x2442ffff, 0x10000006, 0xaf420104, 0x8f420104, +0x24c60008, 0xa2102a, 0x1440ffda, 0x24e70008, +0x810c0, 0x2e21021, 0x3c010001, 0x220821, +0x942230d0, 0x14400023, 0x3c020800, 0x3c020002, +0x2c21024, 0x10400012, 0x82142, 0x3c030001, +0x346338d8, 0x24020003, 0x441023, 0x21080, +0x572021, 0x832021, 0x571021, 0x431021, +0x3105001f, 0x24030001, 0x8c420000, 0xa31804, +0x31827, 0x431024, 0x1000000d, 0xac820000, +0x24020003, 0x441023, 0x21080, 0x5c2821, +0x5c1021, 0x3104001f, 0x24030001, 0x8c420228, +0x831804, 0x31827, 0x431024, 0xaca20228, +0x3c020800, 0x34422000, 0x1821, 0xafa20020, +0x8f5e0018, 0x27ab0020, 0x240200ff, 0x13c20002, +0xafab0034, 0x27c30001, 0x8c020228, 0x609021, +0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, +0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, +0x2484635c, 0x3c050009, 0xafa00014, 0xafa20010, +0x8fa60020, 0x1000006b, 0x34a50500, 0xf71021, +0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, +0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, +0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, +0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, +0x240b0004, 0xafab0010, 0xafb20014, 0x8f48000c, +0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, +0x24070008, 0xa32821, 0xa3482b, 0x822021, +0x100f809, 0x892021, 0x54400006, 0x24130001, +0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, +0x0, 0x326200ff, 0x54400017, 0xaf520018, +0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, +0x8f820120, 0x8fab0034, 0xafa20010, 0x8f820124, +0x3c040001, 0x24846368, 0x3c050009, 0xafa20014, +0x8d660000, 0x10000033, 0x34a50600, 0x8f420308, +0x24130001, 0x24420001, 0xaf420308, 0x8f420308, +0x1000001c, 0x326200ff, 0x8f830054, 0x8f820054, +0x247003e8, 0x2021023, 0x2c4203e9, 0x10400014, +0x9821, 0x24110010, 0x8f42000c, 0x8f440160, +0x8f450164, 0x8f860120, 0xafb10010, 0xafb20014, +0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, +0x24c6001c, 0x1440ffe5, 0x0, 0x8f820054, +0x2021023, 0x2c4203e9, 0x1440ffef, 0x0, +0x326200ff, 0x14400011, 0x0, 0x8f420378, +0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, +0x8fab0034, 0xafa20010, 0x8f820124, 0x3c040001, +0x24846370, 0x3c050009, 0xafa20014, 0x8d660000, +0x34a50700, 0xc002b3b, 0x3c03821, 0x8f4202b8, +0x24420001, 0xaf4202b8, 0x8f4202b8, 0x8f4202f4, +0x24420001, 0xaf4202f4, 0x8f4202f4, 0x8fbf0058, +0x8fbe0054, 0x8fb50050, 0x8fb3004c, 0x8fb20048, +0x8fb10044, 0x8fb00040, 0x3e00008, 0x27bd0060, +0x0, 0x0, 0x0, 0x27bdffe0, +0x27644000, 0xafbf0018, 0xc002ba8, 0x24051000, +0x3c030001, 0x34632cc0, 0x3c040001, 0x34842ec8, +0x24020020, 0xaf82011c, 0x2e31021, 0xaf800100, +0xaf800104, 0xaf800108, 0xaf800110, 0xaf800114, +0xaf800118, 0xaf800120, 0xaf800124, 0xaf800128, +0xaf800130, 0xaf800134, 0xaf800138, 0xaf4200ec, +0x2e31021, 0xaf4200f0, 0x2e41021, 0xaf4200f4, +0x2e41021, 0xaf4200f8, 0x3c020001, 0x571021, +0x904240f4, 0x1440001c, 0x3c050001, 0x8f82011c, +0x3c040001, 0x24846470, 0x3c050001, 0x34420001, +0xaf82011c, 0xafa00010, 0xafa00014, 0x8f86011c, +0x34a50100, 0xc002b3b, 0x3821, 0x8c020218, +0x30420040, 0x10400014, 0x0, 0x8f82011c, +0x3c040001, 0x2484647c, 0x3c050001, 0x34420004, +0xaf82011c, 0xafa00010, 0xafa00014, 0x8f86011c, +0x10000007, 0x34a50200, 0x3c040001, 0x24846484, +0xafa00010, 0xafa00014, 0x8f86011c, 0x34a50300, +0xc002b3b, 0x3821, 0x8fbf0018, 0x3e00008, +0x27bd0020, 0x8fa90010, 0x8f83012c, 0x8faa0014, +0x8fab0018, 0x1060000a, 0x27624fe0, 0x14620002, +0x24680020, 0x27684800, 0x8f820128, 0x11020004, +0x0, 0x8f820124, 0x15020007, 0x0, +0x8f430334, 0x1021, 0x24630001, 0xaf430334, +0x10000039, 0x8f430334, 0xac640000, 0xac650004, +0xac660008, 0xa467000e, 0xac690018, 0xac6a001c, +0xac6b0010, 0xac620014, 0xaf880120, 0x8f4200fc, +0x8f4400f4, 0x2442ffff, 0xaf4200fc, 0x8c820000, +0x10490005, 0x3042ff8f, 0x10400019, 0x3122ff8f, +0x10400018, 0x3c020001, 0x8c830004, 0x2c620010, +0x10400013, 0x3c020001, 0x24630001, 0xac830004, +0x8f4300f8, 0x344230c8, 0x2e21021, 0x54620004, +0x24620008, 0x3c020001, 0x34422ec8, 0x2e21021, +0x14440015, 0x24020001, 0x8f820128, 0x24420020, +0xaf820128, 0x8f820128, 0x1000000f, 0x24020001, +0x3c020001, 0x344230c8, 0x2e21021, 0x54820004, +0x24820008, 0x3c020001, 0x34422ec8, 0x2e21021, +0x402021, 0x24020001, 0xaf4400f4, 0xac890000, +0xac820004, 0x24020001, 0x3e00008, 0x0, +0x3e00008, 0x0, 0x8fa90010, 0x8f83010c, +0x8faa0014, 0x8fab0018, 0x1060000a, 0x276247e0, +0x14620002, 0x24680020, 0x27684000, 0x8f820108, +0x11020004, 0x0, 0x8f820104, 0x15020007, +0x0, 0x8f430338, 0x1021, 0x24630001, +0xaf430338, 0x10000035, 0x8f430338, 0xac640000, +0xac650004, 0xac660008, 0xa467000e, 0xac690018, +0xac6a001c, 0xac6b0010, 0xac620014, 0xaf880100, +0x8f4400ec, 0x8c820000, 0x30420006, 0x10400019, +0x31220006, 0x10400018, 0x3c020001, 0x8c830004, +0x2c620010, 0x10400013, 0x3c020001, 0x24630001, +0xac830004, 0x8f4300f0, 0x34422ec0, 0x2e21021, +0x54620004, 0x24620008, 0x3c020001, 0x34422cc0, +0x2e21021, 0x14440015, 0x24020001, 0x8f820108, +0x24420020, 0xaf820108, 0x8f820108, 0x1000000f, +0x24020001, 0x3c020001, 0x34422ec0, 0x2e21021, +0x54820004, 0x24820008, 0x3c020001, 0x34422cc0, +0x2e21021, 0x402021, 0x24020001, 0xaf4400ec, +0xac890000, 0xac820004, 0x24020001, 0x3e00008, +0x0, 0x3e00008, 0x0, 0x27bdffd8, +0x3c040001, 0x2484648c, 0x3c050001, 0xafbf0024, +0xafb20020, 0xafb1001c, 0xafb00018, 0x8f900104, +0x8f9100b0, 0x8f92011c, 0x34a52500, 0x8f820100, +0x2403021, 0x2203821, 0xafa20010, 0xc002b3b, +0xafb00014, 0x8e020008, 0xafa20010, 0x8e02000c, +0x3c040001, 0x24846498, 0xafa20014, 0x8e060000, +0x8e070004, 0x3c050001, 0xc002b3b, 0x34a52510, +0x8e020018, 0xafa20010, 0x8e02001c, 0x3c040001, +0x248464a4, 0xafa20014, 0x8e060010, 0x8e070014, +0x3c050001, 0xc002b3b, 0x34a52520, 0x3c027f00, +0x2221024, 0x3c030800, 0x54430016, 0x3c030200, +0x8f82009c, 0x3042ffff, 0x14400012, 0x3c030200, +0x3c040001, 0x248464b0, 0x3c050002, 0x34a5f030, +0x3021, 0x3821, 0x36420002, 0xaf82011c, +0x36220001, 0xaf8200b0, 0xaf900104, 0xaf92011c, +0xafa00010, 0xc002b3b, 0xafa00014, 0x10000024, +0x0, 0x2c31024, 0x1040000d, 0x2231024, +0x1040000b, 0x36420002, 0xaf82011c, 0x36220001, +0xaf8200b0, 0xaf900104, 0xaf92011c, 0x8f420330, +0x24420001, 0xaf420330, 0x10000015, 0x8f420330, +0x3c040001, 0x248464b8, 0x240202a9, 0xafa20010, +0xafa00014, 0x8f860144, 0x3c070001, 0x24e764c0, +0xc002b3b, 0x3405dead, 0x8f82011c, 0x34420002, +0xaf82011c, 0x8f820220, 0x34420004, 0xaf820220, +0x8f820140, 0x3c030001, 0x431025, 0xaf820140, +0x8fbf0024, 0x8fb20020, 0x8fb1001c, 0x8fb00018, +0x3e00008, 0x27bd0028, 0x27bdffd8, 0x3c040001, +0x248464e8, 0x3c050001, 0xafbf0024, 0xafb20020, +0xafb1001c, 0xafb00018, 0x8f900124, 0x8f9100a0, +0x8f92011c, 0x34a52600, 0x8f820120, 0x2403021, +0x2203821, 0xafa20010, 0xc002b3b, 0xafb00014, +0x8e020008, 0xafa20010, 0x8e02000c, 0x3c040001, +0x248464f4, 0xafa20014, 0x8e060000, 0x8e070004, +0x3c050001, 0xc002b3b, 0x34a52610, 0x8e020018, +0xafa20010, 0x8e02001c, 0x3c040001, 0x24846500, +0xafa20014, 0x8e060010, 0x8e070014, 0x3c050001, +0xc002b3b, 0x34a52620, 0x3c027f00, 0x2221024, +0x3c030800, 0x54430016, 0x3c030200, 0x8f8200ac, +0x3042ffff, 0x14400012, 0x3c030200, 0x3c040001, +0x2484650c, 0x3c050001, 0x34a5f030, 0x3021, +0x3821, 0x36420002, 0xaf82011c, 0x36220001, +0xaf8200a0, 0xaf900124, 0xaf92011c, 0xafa00010, +0xc002b3b, 0xafa00014, 0x10000024, 0x0, +0x2c31024, 0x1040000d, 0x2231024, 0x1040000b, +0x36420002, 0xaf82011c, 0x36220001, 0xaf8200a0, +0xaf900124, 0xaf92011c, 0x8f42032c, 0x24420001, +0xaf42032c, 0x10000015, 0x8f42032c, 0x3c040001, +0x248464b8, 0x240202e2, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e764c0, 0xc002b3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x8fbf0024, +0x8fb20020, 0x8fb1001c, 0x8fb00018, 0x3e00008, +0x27bd0028, 0x6021, 0x5021, 0x3021, +0x2821, 0x6821, 0x4821, 0x7821, +0x7021, 0x8f880124, 0x8f870104, 0x1580002e, +0x8f8b011c, 0x11a00014, 0x31620800, 0x8f820120, +0x10460029, 0x0, 0x3c040001, 0x8c846ee4, +0x8cc20000, 0x8cc30004, 0xac820000, 0xac830004, +0x8cc20008, 0xac820008, 0x94c2000e, 0xa482000e, +0x8cc20010, 0x240c0001, 0xac820010, 0x8cc20014, +0x10000012, 0x24c60020, 0x10400017, 0x0, +0x3c040001, 0x8c846ee4, 0x8d020000, 0x8d030004, +0xac820000, 0xac830004, 0x8d020008, 0xac820008, +0x9502000e, 0xa482000e, 0x8d020010, 0x25060020, +0xac820010, 0x8d020014, 0x240c0001, 0xc01821, +0xac820014, 0x27624fe0, 0x43102b, 0x54400001, +0x27634800, 0x603021, 0x1540002f, 0x31620100, +0x11200014, 0x31628000, 0x8f820100, 0x1045002a, +0x31620100, 0x3c040001, 0x8c846ee0, 0x8ca20000, +0x8ca30004, 0xac820000, 0xac830004, 0x8ca20008, +0xac820008, 0x94a2000e, 0xa482000e, 0x8ca20010, +0x240a0001, 0xac820010, 0x8ca20014, 0x10000012, +0x24a50020, 0x10400018, 0x31620100, 0x3c040001, +0x8c846ee0, 0x8ce20000, 0x8ce30004, 0xac820000, +0xac830004, 0x8ce20008, 0xac820008, 0x94e2000e, +0xa482000e, 0x8ce20010, 0x24e50020, 0xac820010, +0x8ce20014, 0x240a0001, 0xa01821, 0xac820014, +0x276247e0, 0x43102b, 0x54400001, 0x27634000, +0x602821, 0x31620100, 0x5440001d, 0x31621000, +0x11a00009, 0x31a20800, 0x10400004, 0x25020020, +0x8f8200a8, 0xa5e20000, 0x25020020, 0xaf820124, +0x8f880124, 0x6821, 0x11800011, 0x31621000, +0x3c040001, 0x8c846ee4, 0x8c820000, 0x8c830004, +0xaf820080, 0xaf830084, 0x8c820008, 0xaf8200a4, +0x9482000e, 0xaf8200ac, 0x8c820010, 0x6021, +0xaf8200a0, 0x8c8d0010, 0x8c8f0014, 0x31621000, +0x1440ff82, 0x0, 0x1120000f, 0x31220800, +0x10400004, 0x3c020002, 0x8f8200b8, 0xa5c20000, +0x3c020002, 0x1221024, 0x10400004, 0x24e20020, +0x8f8200b4, 0xaf8200d4, 0x24e20020, 0xaf820104, +0x8f870104, 0x4821, 0x1140ff70, 0x0, +0x3c040001, 0x8c846ee0, 0x8c820000, 0x8c830004, +0xaf820090, 0xaf830094, 0x8c820008, 0xaf8200b4, +0x9482000e, 0xaf82009c, 0x8c820010, 0x5021, +0xaf8200b0, 0x8c890010, 0x1000ff60, 0x8c8e0014, +0x3e00008, 0x0, 0x6021, 0x5821, +0x3021, 0x2821, 0x6821, 0x5021, +0x7821, 0x7021, 0x8f880124, 0x8f870104, +0x3c180100, 0x1580002e, 0x8f89011c, 0x11a00014, +0x31220800, 0x8f820120, 0x10460029, 0x0, +0x3c040001, 0x8c846ee4, 0x8cc20000, 0x8cc30004, +0xac820000, 0xac830004, 0x8cc20008, 0xac820008, +0x94c2000e, 0xa482000e, 0x8cc20010, 0x240c0001, +0xac820010, 0x8cc20014, 0x10000012, 0x24c60020, +0x10400017, 0x0, 0x3c040001, 0x8c846ee4, +0x8d020000, 0x8d030004, 0xac820000, 0xac830004, +0x8d020008, 0xac820008, 0x9502000e, 0xa482000e, +0x8d020010, 0x25060020, 0xac820010, 0x8d020014, +0x240c0001, 0xc01821, 0xac820014, 0x27624fe0, +0x43102b, 0x54400001, 0x27634800, 0x603021, +0x1560002f, 0x31220100, 0x11400014, 0x31228000, +0x8f820100, 0x1045002a, 0x31220100, 0x3c040001, +0x8c846ee0, 0x8ca20000, 0x8ca30004, 0xac820000, +0xac830004, 0x8ca20008, 0xac820008, 0x94a2000e, +0xa482000e, 0x8ca20010, 0x240b0001, 0xac820010, +0x8ca20014, 0x10000012, 0x24a50020, 0x10400018, +0x31220100, 0x3c040001, 0x8c846ee0, 0x8ce20000, +0x8ce30004, 0xac820000, 0xac830004, 0x8ce20008, +0xac820008, 0x94e2000e, 0xa482000e, 0x8ce20010, +0x24e50020, 0xac820010, 0x8ce20014, 0x240b0001, +0xa01821, 0xac820014, 0x276247e0, 0x43102b, +0x54400001, 0x27634000, 0x602821, 0x31220100, +0x5440001d, 0x31221000, 0x11a00009, 0x31a20800, +0x10400004, 0x25020020, 0x8f8200a8, 0xa5e20000, +0x25020020, 0xaf820124, 0x8f880124, 0x6821, +0x11800011, 0x31221000, 0x3c040001, 0x8c846ee4, +0x8c820000, 0x8c830004, 0xaf820080, 0xaf830084, +0x8c820008, 0xaf8200a4, 0x9482000e, 0xaf8200ac, +0x8c820010, 0x6021, 0xaf8200a0, 0x8c8d0010, +0x8c8f0014, 0x31221000, 0x14400022, 0x0, +0x1140000f, 0x31420800, 0x10400004, 0x3c020002, +0x8f8200b8, 0xa5c20000, 0x3c020002, 0x1421024, +0x10400004, 0x24e20020, 0x8f8200b4, 0xaf8200d4, +0x24e20020, 0xaf820104, 0x8f870104, 0x5021, +0x11600010, 0x0, 0x3c040001, 0x8c846ee0, +0x8c820000, 0x8c830004, 0xaf820090, 0xaf830094, +0x8c820008, 0xaf8200b4, 0x9482000e, 0xaf82009c, +0x8c820010, 0x5821, 0xaf8200b0, 0x8c8a0010, +0x8c8e0014, 0x8f820070, 0x3c031000, 0x431024, +0x1040ff5c, 0x0, 0x8f820054, 0x24420005, +0xaf820078, 0x8c040234, 0x10800016, 0x1821, +0x3c020001, 0x571021, 0x8c4240e8, 0x24420005, +0x3c010001, 0x370821, 0xac2240e8, 0x3c020001, +0x571021, 0x8c4240e8, 0x44102b, 0x14400009, +0x24020001, 0x3c030080, 0x3c010001, 0x370821, +0xac2040e8, 0x3c010001, 0x370821, 0x1000000c, +0xa02240f0, 0x3c020001, 0x571021, 0x904240f0, +0x14400006, 0x3c020080, 0x3c020001, 0x571021, +0x904240f1, 0x10400002, 0x3c020080, 0x621825, +0x8c040230, 0x10800013, 0x0, 0x3c020001, +0x571021, 0x8c4240ec, 0x24420005, 0x3c010001, +0x370821, 0xac2240ec, 0x3c020001, 0x571021, +0x8c4240ec, 0x44102b, 0x14400006, 0x0, +0x3c010001, 0x370821, 0xac2040ec, 0x10000006, +0x781825, 0x3c020001, 0x571021, 0x904240f2, +0x54400001, 0x781825, 0x1060ff1a, 0x0, +0x8f420000, 0x10400007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x431025, 0xaf820060, +0x8f420000, 0x10400003, 0x0, 0x1000ff05, +0xaf80004c, 0x1000ff03, 0xaf800048, 0x3e00008, +0x0, 0x0, 0x0, 0x3c020001, +0x8c426d28, 0x27bdffe8, 0xafbf0014, 0x14400012, +0xafb00010, 0x3c100001, 0x26106f90, 0x2002021, +0xc002ba8, 0x24052000, 0x26021fe0, 0x3c010001, +0xac226eec, 0x3c010001, 0xac226ee8, 0xac020250, +0x24022000, 0xac100254, 0xac020258, 0x24020001, +0x3c010001, 0xac226d28, 0x8fbf0014, 0x8fb00010, +0x3e00008, 0x27bd0018, 0x3c090001, 0x8d296eec, +0x8c820000, 0x8fa30010, 0x8fa80014, 0xad220000, +0x8c820004, 0xad250008, 0xad220004, 0x8f820054, +0xad260010, 0xad270014, 0xad230018, 0xad28001c, +0xad22000c, 0x2529ffe0, 0x3c020001, 0x24426f90, +0x122102b, 0x10400003, 0x0, 0x3c090001, +0x8d296ee8, 0x3c020001, 0x8c426d10, 0xad220000, +0x3c020001, 0x8c426d10, 0x3c010001, 0xac296eec, +0xad220004, 0xac090250, 0x3e00008, 0x0, +0x27bdffd0, 0xafb00010, 0x3c100001, 0x8e106eec, +0x3c020001, 0x8c426d10, 0xafb10014, 0x808821, +0xafbe0024, 0x8fbe0040, 0x8fa40048, 0xafb20018, +0xa09021, 0xafbf0028, 0xafb50020, 0xafb3001c, +0xae020000, 0x3c020001, 0x8c426d10, 0xc09821, +0xe0a821, 0x10800006, 0xae020004, 0x26050008, +0xc002bb3, 0x24060018, 0x10000005, 0x2610ffe0, +0x26040008, 0xc002ba8, 0x24050018, 0x2610ffe0, +0x3c030001, 0x24636f90, 0x203102b, 0x10400003, +0x0, 0x3c100001, 0x8e106ee8, 0x8e220000, +0xae020000, 0x8e220004, 0xae120008, 0xae020004, +0x8f820054, 0xae130010, 0xae150014, 0xae1e0018, +0x8fa80044, 0xae08001c, 0xae02000c, 0x2610ffe0, +0x203102b, 0x10400003, 0x0, 0x3c100001, +0x8e106ee8, 0x3c020001, 0x8c426d10, 0xae020000, +0x3c020001, 0x8c426d10, 0x3c010001, 0xac306eec, +0xae020004, 0xac100250, 0x8fbf0028, 0x8fbe0024, +0x8fb50020, 0x8fb3001c, 0x8fb20018, 0x8fb10014, +0x8fb00010, 0x3e00008, 0x27bd0030, 0x851821, +0x83102b, 0x10400006, 0x0, 0xac800000, +0x24840004, 0x83102b, 0x5440fffd, 0xac800000, +0x3e00008, 0x0, 0xa61821, 0xa3102b, +0x10400007, 0x0, 0x8c820000, 0xaca20000, +0x24a50004, 0xa3102b, 0x1440fffb, 0x24840004, +0x3e00008, 0x0, 0x861821, 0x83102b, +0x10400007, 0x0, 0x8ca20000, 0xac820000, +0x24840004, 0x83102b, 0x1440fffb, 0x24a50004, +0x3e00008, 0x0, 0x63080, 0x861821, +0x83102b, 0x10400006, 0x0, 0xac850000, +0x24840004, 0x83102b, 0x5440fffd, 0xac850000, +0x3e00008, 0x0, 0x0, 0x26e50028, +0xa03021, 0x274301c0, 0x8f4d0358, 0x8f47035c, +0x8f480360, 0x8f490364, 0x8f4a0368, 0x8f4b0204, +0x8f4c0200, 0x24640400, 0x64102b, 0x10400008, +0x3c0208ff, 0x8cc20000, 0xac620000, 0x24630004, +0x64102b, 0x1440fffb, 0x24c60004, 0x3c0208ff, +0x3442ffff, 0x3c03c0ff, 0xaf4d0358, 0xaf47035c, +0xaf480360, 0xaf490364, 0xaf4a0368, 0xaf4b0204, +0xaf4c0200, 0x8f840220, 0x3463ffff, 0x8f860200, +0x821024, 0x34420004, 0xc31824, 0x34630004, +0xaf820220, 0xaf830200, 0x8ca20214, 0xac020084, +0x8ca20218, 0xac020088, 0x8ca2021c, 0xac02008c, +0x8ca20220, 0xac020090, 0x8ca20224, 0xac020094, +0x8ca20228, 0xac020098, 0x8ca2022c, 0xac02009c, +0x8ca20230, 0xac0200a0, 0x8ca20234, 0xac0200a4, +0x8ca20238, 0xac0200a8, 0x8ca2023c, 0xac0200ac, +0x8ca20240, 0xac0200b0, 0x8ca20244, 0xac0200b4, +0x8ca20248, 0xac0200b8, 0x8ca2024c, 0xac0200bc, +0x8ca2001c, 0xac020080, 0x8ca20018, 0xac0200c0, +0x8ca20020, 0xac0200cc, 0x8ca20024, 0xac0200d0, +0x8ca201d0, 0xac0200e0, 0x8ca201d4, 0xac0200e4, +0x8ca201d8, 0xac0200e8, 0x8ca201dc, 0xac0200ec, +0x8ca201e0, 0xac0200f0, 0x8ca20098, 0x8ca3009c, +0xac0300fc, 0x8ca200a8, 0x8ca300ac, 0xac0300f4, +0x8ca200a0, 0x8ca300a4, 0x30840004, 0xac0300f8, +0x14800007, 0x30c20004, 0x8f820220, 0x3c0308ff, +0x3463fffb, 0x431024, 0xaf820220, 0x30c20004, +0x14400006, 0x0, 0x8f820200, 0x3c03c0ff, +0x3463fffb, 0x431024, 0xaf820200, 0x8f4202dc, +0xa34005c5, 0x24420001, 0xaf4202dc, 0x8f4202dc, +0x3e00008, 0x0, 0x27bdffd8, 0xafbf0024, +0xafb00020, 0x8f430024, 0x8f420020, 0x10620038, +0x0, 0x8f430020, 0x8f420024, 0x622023, +0x4810003, 0x0, 0x8f420040, 0x822021, +0x8f430030, 0x8f420024, 0x43102b, 0x14400005, +0x0, 0x8f430040, 0x8f420024, 0x10000005, +0x621023, 0x8f420030, 0x8f430024, 0x431023, +0x2442ffff, 0x406021, 0x8c102a, 0x54400001, +0x806021, 0x8f4a0024, 0x8f490040, 0x8f480024, +0x8f440180, 0x8f450184, 0x8f460024, 0x8f4b001c, +0x24070001, 0xafa70010, 0x84100, 0x1001821, +0x14c5021, 0x2529ffff, 0x1498024, 0xafb00014, +0x8f470014, 0x1021, 0x63100, 0xafa70018, +0xa32821, 0xa3382b, 0x822021, 0x872021, +0x8f420108, 0x1663021, 0x40f809, 0xc3900, +0x54400001, 0xaf500024, 0x8f430024, 0x8f420020, +0x14620018, 0x0, 0x8f420000, 0x10400007, +0x0, 0xaf80004c, 0x8f82004c, 0x1040fffd, +0x0, 0x10000005, 0x0, 0xaf800048, +0x8f820048, 0x1040fffd, 0x0, 0x8f820060, +0x2403ffef, 0x431024, 0xaf820060, 0x8f420000, +0x10400003, 0x0, 0x10000002, 0xaf80004c, +0xaf800048, 0x8fbf0024, 0x8fb00020, 0x3e00008, +0x27bd0028, 0x3e00008, 0x0, 0x27bdffc0, +0x32c20020, 0xafbf0038, 0xafb30034, 0xafb20030, +0xafb1002c, 0x10400004, 0xafb00028, 0x8f530028, +0x10000002, 0x0, 0x8f530020, 0x8f420030, +0x105300eb, 0x21100, 0x8f43001c, 0x628021, +0x8e040000, 0x8e050004, 0x96120008, 0x8f420090, +0x9611000a, 0x3246ffff, 0x46102a, 0x10400017, +0x0, 0x8f8200d8, 0x8f430098, 0x431023, +0x2442dcbe, 0xaf420090, 0x8f420090, 0x2842dcbf, +0x10400005, 0x0, 0x8f420090, 0x8f430144, +0x431021, 0xaf420090, 0x8f420090, 0x46102a, +0x10400006, 0x0, 0x8f420348, 0x24420001, +0xaf420348, 0x100000e1, 0x8f420348, 0x8f8200fc, +0x14400006, 0x0, 0x8f420344, 0x24420001, +0xaf420344, 0x100000d9, 0x8f420344, 0x934205c2, +0x1040000b, 0x32c20008, 0x10400008, 0x32220200, +0x10400006, 0x3c034000, 0x9602000e, 0xaf4300ac, +0x21400, 0x10000002, 0xaf4200b0, 0xaf4000ac, +0x32220004, 0x1040007f, 0x32220800, 0x10400003, +0x3247ffff, 0x10000002, 0x24020020, 0x24020004, +0xafa20010, 0x8f420030, 0xafa20014, 0x8f420010, +0x3c030002, 0x431025, 0xafa20018, 0x8f460098, +0x8f420108, 0x40f809, 0x0, 0x104000b7, +0x0, 0x8f42009c, 0x8f430094, 0x2421021, +0xaf42009c, 0xae03000c, 0x8f4200ac, 0x10400008, +0x3c034000, 0x8f420094, 0x431025, 0xafa20020, +0x8f42009c, 0x8f4300b0, 0x10000004, 0x431025, +0x8f420094, 0xafa20020, 0x8f42009c, 0xafa20024, +0x8f8200fc, 0x8fa30020, 0x8fa40024, 0xac430000, +0xac440004, 0x24420008, 0xaf8200f0, 0x8f42009c, +0x8f440270, 0x8f450274, 0x401821, 0x1021, +0xa32821, 0xa3302b, 0x822021, 0x862021, +0x32230060, 0x24020040, 0xaf440270, 0xaf450274, +0x10620017, 0x2c620041, 0x10400005, 0x24020020, +0x10620008, 0x24020001, 0x10000026, 0x0, +0x24020060, 0x10620019, 0x24020001, 0x10000021, +0x0, 0x8f420278, 0x8f43027c, 0x24630001, +0x2c640001, 0x441021, 0xaf420278, 0xaf43027c, +0x8f420278, 0x8f43027c, 0x10000016, 0x24020001, +0x8f420280, 0x8f430284, 0x24630001, 0x2c640001, +0x441021, 0xaf420280, 0xaf430284, 0x8f420280, +0x8f430284, 0x1000000b, 0x24020001, 0x8f420288, +0x8f43028c, 0x24630001, 0x2c640001, 0x441021, +0xaf420288, 0xaf43028c, 0x8f420288, 0x8f43028c, +0x24020001, 0xa34205c2, 0x8f420098, 0x3244ffff, +0x2406fff8, 0x8f45013c, 0x441021, 0x24420007, +0x461024, 0x24840007, 0xaf420094, 0x8f420090, +0x8f430094, 0x862024, 0x441023, 0x65182b, +0x14600005, 0xaf420090, 0x8f420094, 0x8f430144, +0x431023, 0xaf420094, 0x8f420094, 0x10000023, +0xaf40009c, 0x3247ffff, 0x50e00022, 0x32c20020, +0x14400002, 0x24020010, 0x24020002, 0xafa20010, +0x8f420030, 0xafa20014, 0x8f420010, 0xafa20018, +0x8f460098, 0x8f420108, 0x40f809, 0x0, +0x1040003a, 0x3245ffff, 0x8f420098, 0x8f430090, +0x8f46013c, 0x451021, 0xaf420098, 0x8f42009c, +0x8f440098, 0xa34005c2, 0x651823, 0xaf430090, +0x451021, 0x86202b, 0x14800005, 0xaf42009c, +0x8f420098, 0x8f430144, 0x431023, 0xaf420098, +0x32c20020, 0x10400005, 0x0, 0x8f420358, +0x2442ffff, 0xaf420358, 0x8f420358, 0x8f420030, +0x8f430040, 0x24420001, 0x2463ffff, 0x431024, +0xaf420030, 0x8f420030, 0x14530018, 0x0, +0x8f420000, 0x10400007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x2403fff7, 0x431024, +0xaf820060, 0x8f420000, 0x10400003, 0x0, +0x10000002, 0xaf80004c, 0xaf800048, 0x8fbf0038, +0x8fb30034, 0x8fb20030, 0x8fb1002c, 0x8fb00028, +0x3e00008, 0x27bd0040, 0x3e00008, 0x0, +0x27bdffd0, 0x32c20020, 0xafbf002c, 0xafb20028, +0xafb10024, 0x10400004, 0xafb00020, 0x8f520028, +0x10000002, 0x0, 0x8f520020, 0x8f420030, +0x105200b5, 0x21100, 0x8f43001c, 0x628021, +0x8e040000, 0x8e050004, 0x96110008, 0x8f420090, +0x9607000a, 0x3226ffff, 0x46102a, 0x10400017, +0x0, 0x8f8200d8, 0x8f430098, 0x431023, +0x2442dc46, 0xaf420090, 0x8f420090, 0x2842dc47, +0x10400005, 0x0, 0x8f420090, 0x8f430144, +0x431021, 0xaf420090, 0x8f420090, 0x46102a, +0x10400006, 0x0, 0x8f420348, 0x24420001, +0xaf420348, 0x100000ab, 0x8f420348, 0x8f8600fc, +0x10c0000c, 0x0, 0x8f8200f4, 0x2403fff8, +0x431024, 0x461023, 0x218c3, 0x58600001, +0x24630100, 0x8f42008c, 0x43102b, 0x14400006, +0x712c2, 0x8f420344, 0x24420001, 0xaf420344, +0x10000098, 0x8f420344, 0x934305c2, 0x1060000f, +0x30460001, 0x8f420010, 0x34480400, 0x32c20008, +0x10400008, 0x30e20200, 0x10400006, 0x3c034000, +0x9602000e, 0xaf4300ac, 0x21400, 0x10000004, +0xaf4200b0, 0x10000002, 0xaf4000ac, 0x8f480010, +0x30e20004, 0x10400045, 0x3227ffff, 0x8f4900ac, +0x11200005, 0x30c200ff, 0x14400006, 0x24020040, +0x10000004, 0x24020008, 0x14400002, 0x24020020, +0x24020004, 0xafa20010, 0x8f430030, 0x11200004, +0xafa30014, 0x8f4200b0, 0x621025, 0xafa20014, +0x3c020002, 0x1021025, 0xafa20018, 0x8f460098, +0x8f420108, 0x40f809, 0x0, 0x10400069, +0x3224ffff, 0x8f42008c, 0x8f430094, 0x24420001, +0xaf42008c, 0x24020001, 0xae03000c, 0xa34205c2, +0x8f420098, 0x2406fff8, 0x8f45013c, 0x441021, +0x24420007, 0x461024, 0x24840007, 0xaf420094, +0x8f420090, 0x8f430094, 0x862024, 0x441023, +0x65182b, 0x14600005, 0xaf420090, 0x8f420094, +0x8f430144, 0x431023, 0xaf420094, 0x8f430094, +0x8f420140, 0x43102b, 0x10400009, 0x0, +0x8f43013c, 0x8f440094, 0x8f420090, 0x8f450138, +0x641823, 0x431023, 0xaf420090, 0xaf450094, +0x8f420094, 0x1000001f, 0xaf420098, 0x10e0001d, +0x30c200ff, 0x14400002, 0x24020010, 0x24020002, +0xafa20010, 0x8f420030, 0xafa80018, 0xafa20014, +0x8f460098, 0x8f420108, 0x40f809, 0x0, +0x10400030, 0x3225ffff, 0x8f420098, 0x8f44013c, +0x451021, 0xaf420098, 0x8f420090, 0x8f430098, +0xa34005c2, 0x451023, 0x64182b, 0x14600005, +0xaf420090, 0x8f420098, 0x8f430144, 0x431023, +0xaf420098, 0x8f420030, 0x8f430040, 0x24420001, +0x2463ffff, 0x431024, 0xaf420030, 0x8f420030, +0x14520018, 0x0, 0x8f420000, 0x10400007, +0x0, 0xaf80004c, 0x8f82004c, 0x1040fffd, +0x0, 0x10000005, 0x0, 0xaf800048, +0x8f820048, 0x1040fffd, 0x0, 0x8f820060, +0x2403fff7, 0x431024, 0xaf820060, 0x8f420000, +0x10400003, 0x0, 0x10000002, 0xaf80004c, +0xaf800048, 0x8fbf002c, 0x8fb20028, 0x8fb10024, +0x8fb00020, 0x3e00008, 0x27bd0030, 0x3e00008, +0x0, 0x27bdffd8, 0x3c020001, 0x34422ec0, +0xafbf0020, 0x8f4300f0, 0x8f840108, 0x2e21021, +0x54620004, 0x24620008, 0x3c020001, 0x34422cc0, +0x2e21021, 0x401821, 0xaf4300f0, 0xac600000, +0x8f4200ec, 0x8c660004, 0x14620004, 0x3c020001, +0x24820020, 0x1000000f, 0xaf820108, 0x8f4300f0, +0x34422ec0, 0x2e21021, 0x54620004, 0x24620008, +0x3c020001, 0x34422cc0, 0x2e21021, 0x401821, +0x8c620004, 0x21140, 0x821021, 0xaf820108, +0xac600000, 0x8c850018, 0x30a20036, 0x1040006c, +0x30a20001, 0x8c82001c, 0x8f430040, 0x8f440034, +0x24420001, 0x2463ffff, 0x431024, 0x862021, +0xaf42002c, 0x30a20030, 0x14400006, 0xaf440034, +0x8f420034, 0x8c03023c, 0x43102b, 0x144000b4, +0x0, 0x32c20010, 0x10400028, 0x24070008, +0x8f440170, 0x8f450174, 0x8f43002c, 0x8f48000c, +0x8f860120, 0x24020080, 0xafa20010, 0xafa30014, +0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x14400011, 0x24020001, 0x3c010001, 0x370821, +0xa02240f1, 0x8f820124, 0xafa20010, 0x8f820128, +0x3c040001, 0x248467c4, 0xafa20014, 0x8f46002c, +0x8f870120, 0x3c050009, 0xc002b3b, 0x34a51100, +0x10000036, 0x0, 0x8f420300, 0x8f43002c, +0x24420001, 0xaf420300, 0x8f420300, 0x24020001, +0xa34205c1, 0x10000026, 0xaf430038, 0x8f440170, +0x8f450174, 0x8f43002c, 0x8f48000c, 0x8f860120, +0x24020020, 0xafa20010, 0xafa30014, 0xafa80018, +0x8f42010c, 0x40f809, 0x24c6001c, 0x14400011, +0x24020001, 0x3c010001, 0x370821, 0xa02240f0, +0x8f820124, 0xafa20010, 0x8f820128, 0x3c040001, +0x248467b8, 0xafa20014, 0x8f46002c, 0x8f870120, +0x3c050009, 0xc002b3b, 0x34a50900, 0x1000000f, +0x0, 0x8f420300, 0x24420001, 0xaf420300, +0x8f420300, 0x8f42002c, 0xa34005c1, 0xaf420038, +0x3c010001, 0x370821, 0xa02040f1, 0x3c010001, +0x370821, 0xa02040f0, 0xaf400034, 0x8f420314, +0x24420001, 0xaf420314, 0x10000059, 0x8f420314, +0x10400022, 0x30a27000, 0x8c85001c, 0x8f420028, +0xa22023, 0x4810003, 0x0, 0x8f420040, +0x822021, 0x8f420358, 0x8f430000, 0xaf450028, +0x441021, 0x10600007, 0xaf420358, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x34420008, 0xaf820060, +0x8f420000, 0x10400003, 0x0, 0x10000038, +0xaf80004c, 0x10000036, 0xaf800048, 0x1040002f, +0x30a21000, 0x1040000c, 0x30a24000, 0x8c83001c, +0x8f420050, 0x622023, 0x4820001, 0x24840200, +0x8f42035c, 0x441021, 0xaf42035c, 0x8f420368, +0x1000001a, 0xaf430050, 0x1040000c, 0x32c28000, +0x8c83001c, 0x8f420070, 0x622023, 0x4820001, +0x24840400, 0x8f420364, 0x441021, 0xaf420364, +0x8f420368, 0x1000000d, 0xaf430070, 0x1040000e, +0x3c020800, 0x8c83001c, 0x8f420060, 0x622023, +0x4820001, 0x24840100, 0x8f420360, 0x441021, +0xaf420360, 0x8f420368, 0xaf430060, 0x441021, +0xaf420368, 0x3c020800, 0x2c21024, 0x50400008, +0x36940040, 0x10000006, 0x0, 0x30a20100, +0x10400003, 0x0, 0xc002bd8, 0x0, +0x8fbf0020, 0x3e00008, 0x27bd0028, 0x3e00008, +0x0, 0x27bdffa8, 0xafbf0050, 0xafbe004c, +0xafb50048, 0xafb30044, 0xafb20040, 0xafb1003c, +0xafb00038, 0x8f910108, 0x26220020, 0xaf820108, +0x8e320018, 0xa821, 0x32420024, 0x104001ba, +0xf021, 0x8e26001c, 0x8f43001c, 0x61100, +0x621821, 0x8c70000c, 0x9604000c, 0x962d0016, +0x9473000a, 0x2c8305dd, 0x38828870, 0x2c420001, +0x621825, 0x10600015, 0x2821, 0x32c20040, +0x10400015, 0x24020800, 0x96030014, 0x14620012, +0x3402aaaa, 0x9603000e, 0x14620007, 0x2021, +0x96030010, 0x24020300, 0x14620004, 0x801021, +0x96020012, 0x2c440001, 0x801021, 0x54400006, +0x24050016, 0x10000004, 0x0, 0x24020800, +0x50820001, 0x2405000e, 0x934205c3, 0x14400008, +0x5821, 0x240b0001, 0x32620180, 0xaf4500a8, +0xaf5000a0, 0x10400002, 0xaf4600a4, 0xa34b05c3, +0x10a00085, 0x2054021, 0x91020000, 0x3821, +0x3042000f, 0x25080, 0x32c20002, 0x10400012, +0x10a1821, 0x32620002, 0x10400010, 0x32c20001, +0x1002021, 0x94820000, 0x24840002, 0xe23821, +0x83102b, 0x1440fffb, 0x30e2ffff, 0x71c02, +0x623821, 0x71c02, 0x30e2ffff, 0x623821, +0x71027, 0xa502000a, 0x32c20001, 0x1040006a, +0x32620001, 0x10400068, 0x0, 0x8f4200a8, +0x10400065, 0x0, 0x8f4200a0, 0x8f4300a8, +0x431021, 0x904c0009, 0x318900ff, 0x39230006, +0x3182b, 0x39220011, 0x2102b, 0x621824, +0x1060000c, 0x3c050006, 0x8f4200a4, 0x3c040001, +0x248467d4, 0xafa20010, 0x8f4200a0, 0x34a54600, +0x1203821, 0xc002b3b, 0xafa20014, 0x1000004e, +0x0, 0x32c20004, 0x14400013, 0x2821, +0x316200ff, 0x14400004, 0x0, 0x95020002, +0x1000000d, 0x4a2823, 0x9505000c, 0x9502000e, +0x95030010, 0xa22821, 0xa32821, 0x95030012, +0x91040009, 0x95020002, 0xa32821, 0xa42821, +0x4a1023, 0xa22821, 0x2002021, 0x94820000, +0x24840002, 0xe23821, 0x88102b, 0x1440fffb, +0x71c02, 0x30e2ffff, 0x623821, 0x71c02, +0x30e2ffff, 0x623821, 0x1a52821, 0x51c02, +0x30a2ffff, 0x622821, 0x51c02, 0x30a2ffff, +0x622821, 0xa72823, 0x51402, 0xa22821, +0x30a5ffff, 0x50a00001, 0x3405ffff, 0x316200ff, +0x14400008, 0x318300ff, 0x8f4300a0, 0x8f4200a8, +0x624021, 0x91020000, 0x3042000f, 0x25080, +0x318300ff, 0x24020006, 0x14620003, 0x10a1021, +0x10000002, 0x24440010, 0x24440006, 0x316200ff, +0x14400006, 0x0, 0x94820000, 0xa22821, +0x51c02, 0x30a2ffff, 0x622821, 0x934205c3, +0x10400003, 0x32620100, 0x50400003, 0xa4850000, +0x52827, 0xa4850000, 0x9622000e, 0x8f43009c, +0x621821, 0x32a200ff, 0x10400007, 0xaf43009c, +0x3c024000, 0x2021025, 0xafa20020, 0x8f42009c, +0x10000003, 0x5e1025, 0xafb00020, 0x8f42009c, +0xafa20024, 0x32620080, 0x10400010, 0x32620100, +0x8f4200b4, 0x24430001, 0x210c0, 0x571021, +0xaf4300b4, 0x8fa30020, 0x8fa40024, 0x3c010001, +0x220821, 0xac2338e8, 0x3c010001, 0x220821, +0xac2438ec, 0x100000a5, 0x32c20020, 0x10400064, +0x0, 0x8f4200b4, 0x24430001, 0x210c0, +0x571021, 0xaf4300b4, 0x8fa30020, 0x8fa40024, +0x3c010001, 0x220821, 0xac2338e8, 0x3c010001, +0x220821, 0xac2438ec, 0x8f4200b4, 0x10400051, +0x3821, 0x3c090001, 0x352938e8, 0x3c08001f, +0x3508ffff, 0x240bffff, 0x340affff, 0x710c0, +0x571021, 0x491021, 0x8c430000, 0x8c440004, +0xafa30028, 0xafa4002c, 0x8f8200fc, 0x8fa30028, +0x8fa4002c, 0xac430000, 0xac440004, 0x24420008, +0xaf8200f0, 0x8f42008c, 0x2442ffff, 0xaf42008c, +0x97a2002e, 0x8f440270, 0x8f450274, 0x401821, +0x1021, 0xa32821, 0xa3302b, 0x822021, +0x862021, 0xaf440270, 0xaf450274, 0x8fa20028, +0x481024, 0x90430000, 0x30630001, 0x1460000b, +0x402021, 0x8f420278, 0x8f43027c, 0x24630001, +0x2c640001, 0x441021, 0xaf420278, 0xaf43027c, +0x8f420278, 0x1000001a, 0x8f43027c, 0x8c820000, +0x144b000e, 0x0, 0x94820004, 0x144a000b, +0x0, 0x8f420288, 0x8f43028c, 0x24630001, +0x2c640001, 0x441021, 0xaf420288, 0xaf43028c, +0x8f420288, 0x1000000a, 0x8f43028c, 0x8f420280, +0x8f430284, 0x24630001, 0x2c640001, 0x441021, +0xaf420280, 0xaf430284, 0x8f420280, 0x8f430284, +0x8f4200b4, 0x24e70001, 0xe2102b, 0x1440ffb8, +0x710c0, 0xa34005c3, 0x1000003f, 0xaf4000b4, +0x8f8200fc, 0x8fa30020, 0x8fa40024, 0xac430000, +0xac440004, 0x24420008, 0xaf8200f0, 0x8f42009c, +0x8f46008c, 0x8f440270, 0x8f450274, 0x401821, +0x1021, 0x24c6ffff, 0xaf46008c, 0xa32821, +0xa3302b, 0x822021, 0x862021, 0xaf440270, +0xaf450274, 0x92020000, 0x30420001, 0x1440000c, +0x2402ffff, 0x8f420278, 0x8f43027c, 0x24630001, +0x2c640001, 0x441021, 0xaf420278, 0xaf43027c, +0x8f420278, 0x8f43027c, 0x1000001c, 0x32c20020, +0x8e030000, 0x1462000f, 0x3402ffff, 0x96030004, +0x1462000c, 0x0, 0x8f420288, 0x8f43028c, +0x24630001, 0x2c640001, 0x441021, 0xaf420288, +0xaf43028c, 0x8f420288, 0x8f43028c, 0x1000000b, +0x32c20020, 0x8f420280, 0x8f430284, 0x24630001, +0x2c640001, 0x441021, 0xaf420280, 0xaf430284, +0x8f420280, 0x8f430284, 0x32c20020, 0x10400005, +0xaf40009c, 0x8f420358, 0x2442ffff, 0xaf420358, +0x8f420358, 0x8e22001c, 0x8f430040, 0x24420001, +0x2463ffff, 0x431024, 0xaf42002c, 0x32420060, +0x14400008, 0x32c20010, 0x8f420034, 0x24420001, +0xaf420034, 0x8c03023c, 0x43102b, 0x14400102, +0x32c20010, 0x10400018, 0x24070008, 0x8f440170, +0x8f450174, 0x8f43002c, 0x8f48000c, 0x8f860120, +0x24020080, 0xafa20010, 0xafa30014, 0xafa80018, +0x8f42010c, 0x40f809, 0x24c6001c, 0x10400047, +0x24020001, 0x8f420300, 0x8f43002c, 0x24420001, +0xaf420300, 0x8f420300, 0x24020001, 0xa34205c1, +0x1000007c, 0xaf430038, 0x8f440170, 0x8f450174, +0x8f43002c, 0x8f48000c, 0x8f860120, 0x24020020, +0xafa20010, 0xafa30014, 0xafa80018, 0x8f42010c, +0x40f809, 0x24c6001c, 0x10400057, 0x24020001, +0x10000065, 0x0, 0x32420012, 0x10400075, +0x32420001, 0x9622000e, 0x8f43009c, 0x621821, +0x32c20020, 0x10400005, 0xaf43009c, 0x8f420358, +0x2442ffff, 0xaf420358, 0x8f420358, 0x8e22001c, +0x8f430040, 0x24420001, 0x2463ffff, 0x431024, +0xaf42002c, 0x32420010, 0x14400008, 0x32c20010, +0x8f420034, 0x24420001, 0xaf420034, 0x8c03023c, +0x43102b, 0x144000bc, 0x32c20010, 0x10400028, +0x24070008, 0x8f440170, 0x8f450174, 0x8f43002c, +0x8f48000c, 0x8f860120, 0x24020080, 0xafa20010, +0xafa30014, 0xafa80018, 0x8f42010c, 0x40f809, +0x24c6001c, 0x14400011, 0x24020001, 0x3c010001, +0x370821, 0xa02240f1, 0x8f820124, 0xafa20010, +0x8f820128, 0x3c040001, 0x248467c4, 0xafa20014, +0x8f46002c, 0x8f870120, 0x3c050009, 0xc002b3b, +0x34a51100, 0x10000036, 0x0, 0x8f420300, +0x8f43002c, 0x24420001, 0xaf420300, 0x8f420300, +0x24020001, 0xa34205c1, 0x10000026, 0xaf430038, +0x8f440170, 0x8f450174, 0x8f43002c, 0x8f48000c, +0x8f860120, 0x24020020, 0xafa20010, 0xafa30014, +0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x14400011, 0x24020001, 0x3c010001, 0x370821, +0xa02240f0, 0x8f820124, 0xafa20010, 0x8f820128, +0x3c040001, 0x248467b8, 0xafa20014, 0x8f46002c, +0x8f870120, 0x3c050009, 0xc002b3b, 0x34a50900, +0x1000000f, 0x0, 0x8f420300, 0x24420001, +0xaf420300, 0x8f420300, 0x8f42002c, 0xa34005c1, +0xaf420038, 0x3c010001, 0x370821, 0xa02040f1, +0x3c010001, 0x370821, 0xa02040f0, 0xaf400034, +0x8f420314, 0x24420001, 0xaf420314, 0x10000062, +0x8f420314, 0x10400022, 0x32427000, 0x8e25001c, +0x8f420028, 0xa22023, 0x4810003, 0x0, +0x8f420040, 0x822021, 0x8f420358, 0x8f430000, +0xaf450028, 0x441021, 0x10600007, 0xaf420358, +0xaf80004c, 0x8f82004c, 0x1040fffd, 0x0, +0x10000005, 0x0, 0xaf800048, 0x8f820048, +0x1040fffd, 0x0, 0x8f820060, 0x34420008, +0xaf820060, 0x8f420000, 0x10400003, 0x0, +0x10000041, 0xaf80004c, 0x1000003f, 0xaf800048, +0x1040002f, 0x32421000, 0x1040000c, 0x32424000, +0x8e23001c, 0x8f420050, 0x622023, 0x4820001, +0x24840200, 0x8f42035c, 0x441021, 0xaf42035c, +0x8f420368, 0x1000001a, 0xaf430050, 0x1040000c, +0x32c28000, 0x8e23001c, 0x8f420070, 0x622023, +0x4820001, 0x24840400, 0x8f420364, 0x441021, +0xaf420364, 0x8f420368, 0x1000000d, 0xaf430070, +0x1040000e, 0x3c020800, 0x8e23001c, 0x8f420060, +0x622023, 0x4820001, 0x24840100, 0x8f420360, +0x441021, 0xaf420360, 0x8f420368, 0xaf430060, +0x441021, 0xaf420368, 0x3c020800, 0x2c21024, +0x50400011, 0x36940040, 0x1000000f, 0x0, +0x32420048, 0x10400007, 0x24150001, 0x8e22001c, +0x3c03ffff, 0x43f024, 0x3042ffff, 0x1000fd75, +0xae22001c, 0x32420100, 0x10400003, 0x0, +0xc002bd8, 0x0, 0x8fbf0050, 0x8fbe004c, +0x8fb50048, 0x8fb30044, 0x8fb20040, 0x8fb1003c, +0x8fb00038, 0x3e00008, 0x27bd0058, 0x3e00008, +0x0, 0x0, 0x0, 0x8f8300e4, +0x8f8200e0, 0x2404fff8, 0x441024, 0x621026, +0x2102b, 0x21023, 0x3e00008, 0x621024, +0x3e00008, 0x0, 0x27bdffe0, 0xafbf001c, +0xafb00018, 0x8f8600c4, 0x8f8400e0, 0x8f8500e4, +0x2402fff8, 0x821824, 0x10a30009, 0x27623ff8, +0x14a20002, 0x24a20008, 0x27623000, 0x408021, +0x16030005, 0x30820004, 0x10400004, 0xc02021, +0x10000022, 0x1021, 0x8e040000, 0x8f42011c, +0x14a20003, 0x0, 0x8f420120, 0xaf420114, +0x8ca30000, 0x8f420148, 0x831823, 0x43102b, +0x10400003, 0x0, 0x8f420148, 0x621821, +0x94a20006, 0x24420050, 0x62102b, 0x1440000f, +0xa01021, 0xafa40010, 0xafa30014, 0x8ca60000, +0x8ca70004, 0x3c040001, 0xc002b3b, 0x24846894, +0x8f42020c, 0x24420001, 0xaf42020c, 0x8f42020c, +0x1021, 0xaf9000e8, 0xaf9000e4, 0x8fbf001c, +0x8fb00018, 0x3e00008, 0x27bd0020, 0x3e00008, +0x0, 0x8f8400e0, 0x8f8800c4, 0x8f8300e8, +0x2402fff8, 0x823824, 0xe32023, 0x2c821000, +0x50400001, 0x24841000, 0x420c2, 0x801821, +0x8f440258, 0x8f45025c, 0x1021, 0xa32821, +0xa3302b, 0x822021, 0x862021, 0xaf440258, +0xaf45025c, 0x8f8300c8, 0x8f420148, 0x1032023, +0x82102b, 0x14400004, 0x801821, 0x8f420148, +0x822021, 0x801821, 0x8f440250, 0x8f450254, +0x1021, 0xa32821, 0xa3302b, 0x822021, +0x862021, 0xaf440250, 0xaf450254, 0xaf8800c8, +0xaf8700e4, 0xaf8700e8, 0x3e00008, 0x0, +0x27bdff30, 0x240a0001, 0xafbf00c8, 0xafbe00c4, +0xafb500c0, 0xafb300bc, 0xafb200b8, 0xafb100b4, +0xafb000b0, 0xa3a00097, 0xafa00044, 0xafaa005c, +0x934205c4, 0xa7a0008e, 0x1040000a, 0xa7a00086, +0x8f4b00c4, 0xafab0064, 0x8f4a00c0, 0xafaa006c, +0x8f4b00cc, 0xafab0074, 0x8f4a00c8, 0x10000129, +0xafaa007c, 0x8f420114, 0x40f809, 0x0, +0x403021, 0x10c0034f, 0x0, 0x8cc20000, +0x8cc30004, 0xafa20020, 0xafa30024, 0x8fab0024, +0x8faa0020, 0x3162ffff, 0x2442fffc, 0xafa2006c, +0x3c020006, 0x2c21024, 0xafab007c, 0x14400015, +0xafaa0064, 0x91420000, 0x30420001, 0x10400011, +0x2402ffff, 0x8d430000, 0x14620004, 0x3402ffff, +0x95430004, 0x1062000b, 0x0, 0xc0024bb, +0x8fa40064, 0x304200ff, 0x14400006, 0x0, +0x8f420118, 0x40f809, 0x0, 0x1000032d, +0x0, 0x8fa20024, 0x3c03ffbf, 0x3463ffff, +0x431024, 0x3c03ffff, 0x431824, 0x14600003, +0xafa20024, 0x10000040, 0x1821, 0x3c020080, +0x621024, 0x10400007, 0x0, 0x8f42038c, +0x24420001, 0xaf42038c, 0x8f42038c, 0x10000036, +0x24030001, 0x8f420210, 0x24420001, 0xaf420210, +0x8f420210, 0x3c020001, 0x621024, 0x10400006, +0x3c020002, 0x8f4201c4, 0x24420001, 0xaf4201c4, +0x8f4201c4, 0x3c020002, 0x621024, 0x10400006, +0x3c020004, 0x8f42037c, 0x24420001, 0xaf42037c, +0x8f42037c, 0x3c020004, 0x621024, 0x10400006, +0x3c020008, 0x8f420380, 0x24420001, 0xaf420380, +0x8f420380, 0x3c020008, 0x621024, 0x10400006, +0x3c020010, 0x8f420384, 0x24420001, 0xaf420384, +0x8f420384, 0x3c020010, 0x621024, 0x10400006, +0x3c020020, 0x8f4201c0, 0x24420001, 0xaf4201c0, +0x8f4201c0, 0x3c020020, 0x621024, 0x10400006, +0x24030001, 0x8f420388, 0x24420001, 0xaf420388, +0x8f420388, 0x24030001, 0x8c020260, 0x8fab006c, +0x4b102b, 0x10400014, 0x307000ff, 0x8f4201e8, +0x24420001, 0xaf4201e8, 0x8f4201e8, 0x8faa007c, +0x8f8200e0, 0x354a0100, 0xafaa007c, 0xafa20010, +0x8f8200e4, 0x24100001, 0x3c040001, 0x248468a0, +0xafa20014, 0x8fa60020, 0x8fa70024, 0x3c050007, +0xc002b3b, 0x34a50800, 0x12000010, 0x3c020080, +0x2c21024, 0x1440000e, 0x32c20400, 0x8fab007c, +0x3c020080, 0x34420100, 0x1621024, 0x10400005, +0x0, 0x8f42020c, 0x24420001, 0xaf42020c, +0x8f42020c, 0x100002b0, 0x8fa3006c, 0x32c20400, +0x10400015, 0x34028100, 0x8faa0064, 0x9543000c, +0x14620012, 0x3c020100, 0x240b0200, 0xa7ab008e, +0x9542000e, 0x8d430008, 0x8d440004, 0x8d450000, +0x8faa006c, 0x8fab0064, 0x254afffc, 0xafaa006c, +0xa7a20086, 0xad63000c, 0xad640008, 0xad650004, +0x256b0004, 0xafab0064, 0x3c020100, 0x2c21024, +0x10400004, 0x0, 0x8faa006c, 0x254a0004, +0xafaa006c, 0x8f4200bc, 0x5040000a, 0xafa00074, +0x8fab006c, 0x4b102b, 0x50400006, 0xafa00074, +0x8f4200bc, 0x1621023, 0xafa20074, 0x8f4a00bc, +0xafaa006c, 0x8f420080, 0x8fab006c, 0x4b102b, +0x10400056, 0x32c28000, 0x1040005e, 0x240a0003, +0x32c21000, 0x1040005b, 0xafaa005c, 0x10000058, +0x240b0004, 0x8f420350, 0x2403ffbf, 0x283a024, +0x24420001, 0xaf420350, 0x1000024f, 0x8f420350, +0x2c2b025, 0x2402ffbf, 0x282a024, 0x8f830128, +0x3c040001, 0x248468d0, 0x26620001, 0xafa20014, +0xafa30010, 0x8f860120, 0x8f870124, 0x3c050007, +0xc002b3b, 0x34a52250, 0x1000023f, 0x0, +0x2c2b025, 0x2402ffbf, 0x282a024, 0x8f830128, +0x3c040001, 0x248468d0, 0x24020002, 0xafa20014, +0xafa30010, 0x8f860120, 0x8f870124, 0x3c050007, +0xc002b3b, 0x34a52450, 0x1000022f, 0x0, +0x8ea20000, 0x8ea30004, 0x3c040001, 0x248468e8, +0xafb00010, 0xafbe0014, 0x8ea70018, 0x34a52800, +0xc002b3b, 0x603021, 0x10000223, 0x0, +0xa6b1000a, 0x8f820124, 0x3c040001, 0x248468f0, +0xafbe0014, 0xafa20010, 0x8f460044, 0x8f870120, +0x3c050007, 0xc002b3b, 0x34a53000, 0x10000216, +0x0, 0xa6b1000a, 0xa6b2000e, 0x8f820124, +0x3c040001, 0x248468fc, 0xafbe0014, 0xafa20010, +0x8f460044, 0x8f870120, 0x3c050007, 0xc002b3b, +0x34a53200, 0x10000208, 0x0, 0x8f420084, +0x8faa006c, 0x4a102b, 0x14400007, 0x3c020001, +0x2c21024, 0x10400004, 0x0, 0x240b0002, +0xafab005c, 0x8faa006c, 0x1140021b, 0x27ab0020, +0xafab00a4, 0x3c0a001f, 0x354affff, 0xafaa009c, +0x8fab005c, 0x240a0001, 0x556a0021, 0x240a0002, +0x8f430054, 0x8f420050, 0x1062000b, 0x274b0054, +0x8f5e0054, 0x3403ecc0, 0xafab004c, 0x27c20001, +0x304201ff, 0xafa20054, 0x1e1140, 0x431021, +0x1000006b, 0x2e2a821, 0x8f420044, 0x8faa006c, +0x3c040001, 0x248468ac, 0xafaa0014, 0xafa20010, +0x8f460054, 0x8f470050, 0x3c050007, 0xc002b3b, +0x34a51300, 0x8f430350, 0x2402ffbf, 0x282a024, +0x24630001, 0xaf430350, 0x100001d3, 0x8f420350, +0x156a001d, 0x0, 0x8f430074, 0x8f420070, +0x1062000a, 0x274b0074, 0x8f5e0074, 0xafab004c, +0x27c20001, 0x304203ff, 0xafa20054, 0x1e1140, +0x24426cc0, 0x1000004a, 0x2e2a821, 0x8f420044, +0x8faa006c, 0x3c040001, 0x248468b8, 0x3c050007, +0xafaa0014, 0xafa20010, 0x8f460074, 0x8f470070, +0x34a51500, 0x240b0001, 0xc002b3b, 0xafab005c, +0x1000ffc3, 0x0, 0x8f430064, 0x8f420060, +0x1062001a, 0x274a0064, 0x8f5e0064, 0x8fab005c, +0xafaa004c, 0x27c20001, 0x304200ff, 0xafa20054, +0x24020004, 0x1562000e, 0x1e1140, 0x1e1180, +0x24420cc0, 0x2e21021, 0xafa20044, 0x9442002a, +0x8faa0044, 0x8fab006c, 0x4b102b, 0x10400024, +0x25550020, 0x240a0001, 0x10000021, 0xa3aa0097, +0x24424cc0, 0x1000001e, 0x2e2a821, 0x8f420044, +0x8fab006c, 0x3c040001, 0x248468c4, 0xafab0014, +0xafa20010, 0x8f460064, 0x8f470060, 0x3c050007, +0xc002b3b, 0x34a51800, 0x3c020008, 0x2c21024, +0x1440ff34, 0x0, 0x8f420370, 0x240a0001, +0xafaa005c, 0x24420001, 0xaf420370, 0x1000ff90, +0x8f420370, 0x27a30036, 0x131040, 0x621821, +0x94620000, 0x441021, 0x10000020, 0xa4620000, +0x8fab0064, 0xaeab0018, 0x93a20097, 0x10400072, +0x9821, 0x8faa0044, 0x8fa4006c, 0x8fa300a4, +0x25420020, 0xafa20028, 0x25420008, 0xafa20030, +0x25420010, 0xafaa002c, 0xafa20034, 0x9542002a, +0xa7a20038, 0x95420018, 0xa7a2003a, 0x9542001a, +0xa7a2003c, 0x9542001c, 0xa7a2003e, 0x94620018, +0x24630002, 0x822023, 0x1880ffde, 0x26730001, +0x2e620004, 0x1440fff9, 0x0, 0x8f4200fc, +0x26650001, 0xa2102a, 0x1440002b, 0x24030001, +0x8f83012c, 0x10600023, 0x0, 0x8f820124, +0x431023, 0x22143, 0x58800001, 0x24840040, +0x8f820128, 0x431023, 0x21943, 0x58600001, +0x24630040, 0x64102a, 0x54400001, 0x602021, +0xaf4400fc, 0x8f4200fc, 0xa2102a, 0x10400011, +0x24030001, 0x10000015, 0x306200ff, 0x8fab0064, +0x96070018, 0xafab0010, 0x8e220008, 0x3c040001, +0x248468dc, 0x8c430004, 0x8c420000, 0x34a52400, +0x2403021, 0xc002b3b, 0xafa30014, 0x1000002b, +0x0, 0x8f420334, 0x1821, 0x24420001, +0xaf420334, 0x8f420334, 0x306200ff, 0x5040fedc, +0x3c020800, 0x12600021, 0x9021, 0x8fb100a4, +0x2208021, 0x8e220008, 0x96070018, 0x8fa60064, +0x8c440000, 0x8c450004, 0x240a0001, 0xafaa0010, +0xafbe0014, 0x8f420008, 0xafa20018, 0x8f42010c, +0x40f809, 0x0, 0x1040ffd8, 0x3c050007, +0x96020018, 0x8fab0064, 0x8faa009c, 0x1625821, +0x14b102b, 0x10400004, 0xafab0064, 0x8f420148, +0x1625823, 0xafab0064, 0x26100002, 0x26520001, +0x253102b, 0x1440ffe3, 0x26310004, 0x8fb0006c, +0x10000036, 0x97b10038, 0x8f4200fc, 0x24050002, +0xa2102a, 0x1440001b, 0x24030001, 0x8f83012c, +0x10600013, 0x0, 0x8f820124, 0x431023, +0x22143, 0x58800001, 0x24840040, 0x8f820128, +0x431023, 0x21943, 0x58600001, 0x24630040, +0x64102a, 0x54400001, 0x602021, 0xaf4400fc, +0x8f4200fc, 0xa2102a, 0x14400006, 0x24030001, +0x8f420334, 0x1821, 0x24420001, 0xaf420334, +0x8f420334, 0x306200ff, 0x1040fea5, 0x3c020800, +0x96b1000a, 0x8fb0006c, 0x3223ffff, 0x70102b, +0x54400001, 0x608021, 0x8ea40000, 0x8ea50004, +0x240b0001, 0xafab0010, 0xafbe0014, 0x8f420008, +0x8fa60064, 0xafa20018, 0x8f42010c, 0x40f809, +0x2003821, 0x1040fea2, 0x3c050007, 0x96a3000e, +0x97aa008e, 0x11400007, 0x609021, 0x934205c4, +0x14400004, 0x0, 0x97ab0086, 0x6a1825, +0xa6ab0016, 0x8faa007c, 0x3c02ffff, 0x1421024, +0x10400003, 0xa1402, 0x34630400, 0xa6a20014, +0x8fab006c, 0x560b0072, 0xa6a3000e, 0x34620004, +0xa6a2000e, 0x8faa0074, 0x16a1021, 0xa6a2000a, +0x8f430044, 0x8f4401a0, 0x8f4501a4, 0x34028000, +0xafa20010, 0x8f420044, 0x2a03021, 0x24070020, +0xafa20014, 0x8f42000c, 0x31940, 0x604821, +0xafa20018, 0x8f42010c, 0x4021, 0xa92821, +0xa9182b, 0x882021, 0x40f809, 0x832021, +0x5040fe7f, 0xa6b2000e, 0x8f420368, 0xafa0006c, +0xa34005c4, 0x2442ffff, 0xaf420368, 0x8fab005c, +0x240a0001, 0x8f420368, 0x156a0006, 0x240a0002, +0x8f42035c, 0x2442ffff, 0xaf42035c, 0x1000000c, +0x8f42035c, 0x156a0006, 0x0, 0x8f420364, +0x2442ffff, 0xaf420364, 0x10000005, 0x8f420364, +0x8f420360, 0x2442ffff, 0xaf420360, 0x8f420360, +0x8faa0054, 0x8fab004c, 0xad6a0000, 0x8f420044, +0x8f440088, 0x8f430078, 0x24420001, 0x441024, +0x24630001, 0xaf420044, 0xaf430078, 0x8c020240, +0x62182b, 0x14600075, 0x24070008, 0x8f440168, +0x8f45016c, 0x8f430044, 0x8f48000c, 0x8f860120, +0x24020040, 0xafa20010, 0xafa30014, 0xafa80018, +0x8f42010c, 0x40f809, 0x24c6001c, 0x14400011, +0x240b0001, 0x3c010001, 0x370821, 0xa02b40f2, +0x8f820124, 0xafa20010, 0x8f820128, 0x3c040001, +0x2484688c, 0xafa20014, 0x8f460044, 0x8f870120, +0x3c050009, 0xc002b3b, 0x34a51300, 0x1000000b, +0x0, 0x8f420304, 0x24420001, 0xaf420304, +0x8f420304, 0x8f420044, 0xaf42007c, 0x3c010001, +0x370821, 0xa02040f2, 0xaf400078, 0x8f420318, +0x24420001, 0xaf420318, 0x10000048, 0x8f420318, +0xa6b0000a, 0x8f430044, 0x8f4401a0, 0x8f4501a4, +0x34028000, 0xafa20010, 0x8f420044, 0x2a03021, +0x24070020, 0xafa20014, 0x8f42000c, 0x31940, +0x604821, 0xafa20018, 0x8f42010c, 0x4021, +0xa92821, 0xa9182b, 0x882021, 0x40f809, +0x832021, 0x1040fe1f, 0x240a0001, 0xa34a05c4, +0x8fab006c, 0x8faa0064, 0x1705823, 0xafab006c, +0x8fab009c, 0x1505021, 0x16a102b, 0x10400004, +0xafaa0064, 0x8f420148, 0x1425023, 0xafaa0064, +0x8f420368, 0x2442ffff, 0xaf420368, 0x8faa005c, +0x240b0001, 0x8f420368, 0x154b0006, 0x240b0002, +0x8f42035c, 0x2442ffff, 0xaf42035c, 0x1000000c, +0x8f42035c, 0x114b0006, 0x0, 0x8f420360, +0x2442ffff, 0xaf420360, 0x10000005, 0x8f420360, +0x8f420364, 0x2442ffff, 0xaf420364, 0x8f420364, +0x8fab0054, 0x8faa004c, 0xad4b0000, 0x8f420044, +0x8f440088, 0x8f430078, 0x24420001, 0x441024, +0x24630001, 0xaf420044, 0xaf430078, 0x8faa006c, +0x1540fe0b, 0x0, 0x8fab006c, 0x1160001e, +0x0, 0x934205c4, 0x10400009, 0x0, +0x8faa0064, 0xaf4a00c4, 0xaf4b00c0, 0x8fab007c, +0xaf4b00c8, 0x8faa0074, 0x1000000e, 0xaf4a00cc, +0x97ab008e, 0x1160000b, 0x34038100, 0x8fa20020, +0x8c46000c, 0xa443000c, 0x97aa0086, 0x8c440004, +0x8c450008, 0xa44a000e, 0xac440000, 0xac450004, +0xac460008, 0x8f42034c, 0x24420001, 0xaf42034c, +0x10000010, 0x8f42034c, 0x8fab007c, 0x3164ffff, +0x2484fffc, 0x801821, 0x8f440250, 0x8f450254, +0x8f460118, 0x1021, 0xa32821, 0xa3382b, +0x822021, 0x872021, 0xaf440250, 0xc0f809, +0xaf450254, 0x8fbf00c8, 0x8fbe00c4, 0x8fb500c0, +0x8fb300bc, 0x8fb200b8, 0x8fb100b4, 0x8fb000b0, +0x3e00008, 0x27bd00d0, 0x3e00008, 0x0, +0x27bdff38, 0x240b0001, 0xafbf00c0, 0xafbe00bc, +0xafb500b8, 0xafb300b4, 0xafb200b0, 0xafb100ac, +0xafb000a8, 0xa3a00087, 0xafa00044, 0xafab005c, +0x934205c4, 0xa7a00076, 0x10400007, 0xa7a0007e, +0x8f4c00c0, 0xafac0064, 0x8f4b00c8, 0x8f5e00c4, +0x10000130, 0xafab006c, 0x8f420114, 0x40f809, +0x0, 0x403021, 0x10c002a1, 0x0, +0x8cc20000, 0x8cc30004, 0xafa20020, 0xafa30024, +0x8fac0024, 0x8fbe0020, 0x3182ffff, 0x2442fffc, +0xafa20064, 0x3c020006, 0x2c21024, 0x14400015, +0xafac006c, 0x93c20000, 0x30420001, 0x10400011, +0x2402ffff, 0x8fc30000, 0x14620004, 0x3402ffff, +0x97c30004, 0x1062000b, 0x0, 0xc0024bb, +0x3c02021, 0x304200ff, 0x14400006, 0x0, +0x8f420118, 0x40f809, 0x0, 0x10000280, +0x0, 0x8fa20024, 0x3c03ffbf, 0x3463ffff, +0x431024, 0x3c03ffff, 0x431824, 0x14600003, +0xafa20024, 0x10000040, 0x8021, 0x3c020080, +0x621024, 0x10400007, 0x0, 0x8f42038c, +0x24420001, 0xaf42038c, 0x8f42038c, 0x10000036, +0x24100001, 0x8f420210, 0x24420001, 0xaf420210, +0x8f420210, 0x3c020001, 0x621024, 0x10400006, +0x3c020002, 0x8f4201c4, 0x24420001, 0xaf4201c4, +0x8f4201c4, 0x3c020002, 0x621024, 0x10400006, +0x3c020004, 0x8f42037c, 0x24420001, 0xaf42037c, +0x8f42037c, 0x3c020004, 0x621024, 0x10400006, +0x3c020008, 0x8f420380, 0x24420001, 0xaf420380, +0x8f420380, 0x3c020008, 0x621024, 0x10400006, +0x3c020010, 0x8f420384, 0x24420001, 0xaf420384, +0x8f420384, 0x3c020010, 0x621024, 0x10400006, +0x3c020020, 0x8f4201c0, 0x24420001, 0xaf4201c0, +0x8f4201c0, 0x3c020020, 0x621024, 0x10400006, +0x24100001, 0x8f420388, 0x24420001, 0xaf420388, +0x8f420388, 0x24100001, 0x8c020260, 0x8fab0064, +0x4b102b, 0x10400015, 0x320200ff, 0x8f4201e8, +0x24420001, 0xaf4201e8, 0x8f4201e8, 0x8fac006c, +0x8f8200e0, 0x358c0100, 0xafac006c, 0xafa20010, +0x8f8200e4, 0x24100001, 0x3c040001, 0x248468a0, +0xafa20014, 0x8fa60020, 0x8fa70024, 0x3c050007, +0xc002b3b, 0x34a53600, 0x320200ff, 0x10400010, +0x3c020080, 0x2c21024, 0x1440000e, 0x32c20400, +0x8fab006c, 0x3c020080, 0x34420100, 0x1621024, +0x10400005, 0x0, 0x8f42020c, 0x24420001, +0xaf42020c, 0x8f42020c, 0x10000202, 0x8fa30064, +0x32c20400, 0x10400012, 0x34028100, 0x97c3000c, +0x1462000f, 0x0, 0x240c0200, 0xa7ac0076, +0x97c2000e, 0x8fc30008, 0x8fc40004, 0x8fab0064, +0x8fc50000, 0x256bfffc, 0xafab0064, 0xa7a2007e, +0xafc3000c, 0xafc40008, 0xafc50004, 0x27de0004, +0x8fa70064, 0x320200ff, 0x14400034, 0x3c020100, +0x97c4000c, 0x2c8305dd, 0x38828870, 0x2c420001, +0x621825, 0x10600015, 0x2821, 0x32c20800, +0x10400015, 0x24020800, 0x97c30014, 0x14620012, +0x3402aaaa, 0x97c3000e, 0x14620007, 0x2021, +0x97c30010, 0x24020300, 0x14620004, 0x801021, +0x97c20012, 0x2c440001, 0x801021, 0x54400006, +0x24050016, 0x10000004, 0x0, 0x24020800, +0x50820001, 0x2405000e, 0x10a00013, 0x3c52021, +0x24830009, 0x3c02001f, 0x3442ffff, 0x43102b, +0x10400003, 0x0, 0x8f420148, 0x621823, +0x90620000, 0x38430006, 0x2c630001, 0x38420011, +0x2c420001, 0x621825, 0x10600004, 0x3c020100, +0x94820002, 0x453821, 0x3c020100, 0x2c21024, +0x5040000e, 0xafa70064, 0x8fac0064, 0x10ec0008, +0x3c050007, 0x3c040001, 0x24846908, 0x8fa60064, +0x34a54000, 0xafa00010, 0xc002b3b, 0xafa00014, +0x8fab0064, 0x256b0004, 0xafab0064, 0x8f420080, +0x8fac0064, 0x4c102b, 0x1040002c, 0x32c28000, +0x10400034, 0x240b0003, 0x32c21000, 0x10400031, +0xafab005c, 0x1000002e, 0x240c0004, 0x8f420350, +0x2403ffbf, 0x283a024, 0x24420001, 0xaf420350, +0x10000173, 0x8f420350, 0x3c020800, 0x2c2b025, +0x2402ffbf, 0x282a024, 0x8f830128, 0x3c040001, +0x248468d0, 0x26620001, 0xafa20014, 0xafa30010, +0x8f860120, 0x8f870124, 0x3c050007, 0xc002b3b, +0x34a55300, 0x10000162, 0x0, 0x8ea20000, +0x8ea30004, 0x3c040001, 0x248468e8, 0xafb00010, +0xafb10014, 0x8ea70018, 0x34a55900, 0xc002b3b, +0x603021, 0x10000156, 0x0, 0x8f420084, +0x8fab0064, 0x4b102b, 0x14400007, 0x3c020001, +0x2c21024, 0x10400004, 0x0, 0x240c0002, +0xafac005c, 0x8fab0064, 0x11600166, 0x27ac0020, +0xafac008c, 0x8fab005c, 0x240c0001, 0x556c0021, +0x240c0002, 0x8f430054, 0x8f420050, 0x1062000b, +0x274b0054, 0x8f510054, 0x3403ecc0, 0xafab004c, +0x26220001, 0x304201ff, 0xafa20054, 0x111140, +0x431021, 0x1000006b, 0x2e2a821, 0x8f420044, +0x8fac0064, 0x3c040001, 0x248468ac, 0xafac0014, +0xafa20010, 0x8f460054, 0x8f470050, 0x3c050007, +0xc002b3b, 0x34a54300, 0x8f430350, 0x2402ffbf, +0x282a024, 0x24630001, 0xaf430350, 0x10000124, +0x8f420350, 0x156c001d, 0x0, 0x8f430074, +0x8f420070, 0x1062000a, 0x274b0074, 0x8f510074, +0xafab004c, 0x26220001, 0x304203ff, 0xafa20054, +0x111140, 0x24426cc0, 0x1000004a, 0x2e2a821, +0x8f420044, 0x8fac0064, 0x3c040001, 0x248468b8, +0x3c050007, 0xafac0014, 0xafa20010, 0x8f460074, +0x8f470070, 0x34a54500, 0x240b0001, 0xc002b3b, +0xafab005c, 0x1000ffc3, 0x0, 0x8f430064, +0x8f420060, 0x1062001a, 0x274c0064, 0x8f510064, +0x8fab005c, 0xafac004c, 0x26220001, 0x304200ff, +0xafa20054, 0x24020004, 0x1562000e, 0x111140, +0x111180, 0x24420cc0, 0x2e21021, 0xafa20044, +0x9442002a, 0x8fac0044, 0x8fab0064, 0x4b102b, +0x10400024, 0x25950020, 0x240c0001, 0x10000021, +0xa3ac0087, 0x24424cc0, 0x1000001e, 0x2e2a821, +0x8f420044, 0x8fab0064, 0x3c040001, 0x248468c4, +0xafab0014, 0xafa20010, 0x8f460064, 0x8f470060, +0x3c050007, 0xc002b3b, 0x34a54800, 0x3c020008, +0x2c21024, 0x1440ff61, 0x0, 0x8f420370, +0x240c0001, 0xafac005c, 0x24420001, 0xaf420370, +0x1000ff90, 0x8f420370, 0x27a30036, 0x131040, +0x621821, 0x94620000, 0x441021, 0x1000001f, +0xa4620000, 0xaebe0018, 0x93a20087, 0x10400084, +0x9821, 0x8fab0044, 0x8fa40064, 0x8fa3008c, +0x25620020, 0xafa20028, 0x25620008, 0xafa20030, +0x25620010, 0xafab002c, 0xafa20034, 0x9562002a, +0xa7a20038, 0x95620018, 0xa7a2003a, 0x9562001a, +0xa7a2003c, 0x9562001c, 0xa7a2003e, 0x94620018, +0x24630002, 0x822023, 0x1880ffdf, 0x26730001, +0x2e620004, 0x1440fff9, 0x0, 0x8f4200fc, +0x262102a, 0x14400030, 0x24030001, 0x8f83012c, +0x10600028, 0x0, 0x8f820124, 0x431023, +0x22143, 0x58800001, 0x24840040, 0x8f820128, +0x431023, 0x21943, 0x58600001, 0x24630040, +0x64102a, 0x54400001, 0x602021, 0xaf4400fc, +0x8f4200fc, 0x262102a, 0x10400016, 0x24030001, +0x1000001a, 0x306200ff, 0x8fac008c, 0x101040, +0x4c1021, 0x94470018, 0x101080, 0x4c1021, +0xafbe0010, 0x8c420008, 0x3c040001, 0x248468dc, +0x3c050007, 0x8c430004, 0x8c420000, 0x34a55500, +0x2003021, 0xc002b3b, 0xafa30014, 0x10000039, +0x0, 0x8f420334, 0x1821, 0x24420001, +0xaf420334, 0x8f420334, 0x306200ff, 0x1040ff06, +0x8021, 0x8f430008, 0x2402fbff, 0x1260002d, +0x625024, 0x3c0b4000, 0x22b4025, 0x8fb1008c, +0x2669ffff, 0x2209021, 0x8e420008, 0x96270018, +0x8c440000, 0x8c450004, 0x56090004, 0x240b0001, +0x240c0002, 0x10000002, 0xafac0010, 0xafab0010, +0x16000004, 0xafa80014, 0x8f420008, 0x10000002, +0xafa20018, 0xafaa0018, 0x8f42010c, 0x3c03021, +0xafa80098, 0xafa9009c, 0x40f809, 0xafaa00a0, +0x8fa80098, 0x8fa9009c, 0x8faa00a0, 0x1040ffc2, +0x3c02001f, 0x96230018, 0x3442ffff, 0x3c3f021, +0x5e102b, 0x10400003, 0x26310002, 0x8f420148, +0x3c2f023, 0x26100001, 0x213102b, 0x1440ffda, +0x26520004, 0x8fb00064, 0x1000001a, 0x0, +0x96a3000a, 0x8fb00064, 0x70102b, 0x54400001, +0x608021, 0x8ea40000, 0x8ea50004, 0x8fab005c, +0x240c0002, 0xafac0010, 0x934305c4, 0xb1700, +0x10600003, 0x2223025, 0x3c020800, 0xc23025, +0xafa60014, 0x8f420008, 0xafa20018, 0x8f42010c, +0x3c03021, 0x40f809, 0x2003821, 0x1040fecb, +0x3c050007, 0x97ac0076, 0x11800007, 0x96a3000e, +0x934205c4, 0x14400004, 0x0, 0x97ab007e, +0x6c1825, 0xa6ab0016, 0x8fac006c, 0x3c02ffff, +0x1821024, 0x10400003, 0xc1402, 0x34630400, +0xa6a20014, 0xa6b0000a, 0x8fab0064, 0x560b0006, +0x3d0f021, 0x34620004, 0xafa00064, 0xa6a2000e, +0x1000000d, 0xa34005c4, 0x8fac0064, 0x3c02001f, +0x3442ffff, 0x5e102b, 0x1906023, 0xafac0064, +0xa6a3000e, 0x240b0001, 0x10400003, 0xa34b05c4, +0x8f420148, 0x3c2f023, 0x8fab0054, 0x8fac004c, +0xad8b0000, 0x8fac0064, 0x1580feba, 0x0, +0x8fab0064, 0x1160001b, 0x0, 0x934205c4, +0x10400006, 0x0, 0xaf5e00c4, 0xaf4b00c0, +0x8fac006c, 0x1000000e, 0xaf4c00c8, 0x97ab0076, +0x1160000b, 0x34038100, 0x8fa20020, 0x8c46000c, +0xa443000c, 0x97ac007e, 0x8c440004, 0x8c450008, +0xa44c000e, 0xac440000, 0xac450004, 0xac460008, +0x8f42034c, 0x24420001, 0xaf42034c, 0x10000010, +0x8f42034c, 0x8fab006c, 0x3164ffff, 0x2484fffc, +0x801821, 0x8f440250, 0x8f450254, 0x8f460118, +0x1021, 0xa32821, 0xa3382b, 0x822021, +0x872021, 0xaf440250, 0xc0f809, 0xaf450254, +0x8fbf00c0, 0x8fbe00bc, 0x8fb500b8, 0x8fb300b4, +0x8fb200b0, 0x8fb100ac, 0x8fb000a8, 0x3e00008, +0x27bd00c8, 0x3e00008, 0x0, 0x27bdffd8, +0xafbf0024, 0xafb00020, 0x8f43004c, 0x8f420048, +0x10620034, 0x0, 0x8f430048, 0x8f42004c, +0x622023, 0x4820001, 0x24840200, 0x8f430054, +0x8f42004c, 0x43102b, 0x14400004, 0x24020200, +0x8f43004c, 0x10000005, 0x431023, 0x8f420054, +0x8f43004c, 0x431023, 0x2442ffff, 0x405021, +0x8a102a, 0x54400001, 0x805021, 0x8f49004c, +0x8f48004c, 0x8f440188, 0x8f45018c, 0x8f46004c, +0x24071000, 0xafa70010, 0x84140, 0x1001821, +0x12a4821, 0x313001ff, 0xafb00014, 0x8f470014, +0x1021, 0x63140, 0xafa70018, 0xa32821, +0xa3382b, 0x822021, 0x872021, 0x3402ecc0, +0xc23021, 0x8f420108, 0x2e63021, 0x40f809, +0xa3940, 0x54400001, 0xaf50004c, 0x8f43004c, +0x8f420048, 0x14620018, 0x0, 0x8f420000, +0x10400007, 0x0, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x2403fdff, 0x431024, 0xaf820060, +0x8f420000, 0x10400003, 0x0, 0x10000002, +0xaf80004c, 0xaf800048, 0x8fbf0024, 0x8fb00020, +0x3e00008, 0x27bd0028, 0x3e00008, 0x0, +0x27bdffd8, 0xafbf0024, 0xafb00020, 0x8f43005c, +0x8f420058, 0x10620049, 0x0, 0x8f430058, +0x8f42005c, 0x622023, 0x4820001, 0x24840100, +0x8f430064, 0x8f42005c, 0x43102b, 0x14400004, +0x24020100, 0x8f43005c, 0x10000005, 0x431023, +0x8f420064, 0x8f43005c, 0x431023, 0x2442ffff, +0x403821, 0x87102a, 0x54400001, 0x803821, +0x8f42005c, 0x471021, 0x305000ff, 0x32c21000, +0x10400015, 0x24082000, 0x8f49005c, 0x8f440190, +0x8f450194, 0x8f46005c, 0x73980, 0xafa80010, +0xafb00014, 0x8f480014, 0x94980, 0x1201821, +0x1021, 0xa32821, 0xa3482b, 0x822021, +0x892021, 0x63180, 0xafa80018, 0x8f420108, +0x10000014, 0x24c60cc0, 0x8f49005c, 0x8f440190, +0x8f450194, 0x8f46005c, 0x73940, 0xafa80010, +0xafb00014, 0x8f480014, 0x94940, 0x1201821, +0x1021, 0xa32821, 0xa3482b, 0x822021, +0x892021, 0x63140, 0xafa80018, 0x8f420108, +0x24c64cc0, 0x40f809, 0x2e63021, 0x54400001, +0xaf50005c, 0x8f43005c, 0x8f420058, 0x14620018, +0x0, 0x8f420000, 0x10400007, 0x0, +0xaf80004c, 0x8f82004c, 0x1040fffd, 0x0, +0x10000005, 0x0, 0xaf800048, 0x8f820048, +0x1040fffd, 0x0, 0x8f820060, 0x2403feff, +0x431024, 0xaf820060, 0x8f420000, 0x10400003, +0x0, 0x10000002, 0xaf80004c, 0xaf800048, +0x8fbf0024, 0x8fb00020, 0x3e00008, 0x27bd0028, +0x3e00008, 0x0, 0x27bdffd8, 0xafbf0024, +0xafb00020, 0x8f43006c, 0x8f420068, 0x10620033, +0x0, 0x8f430068, 0x8f42006c, 0x622023, +0x4820001, 0x24840400, 0x8f430074, 0x8f42006c, +0x43102b, 0x14400004, 0x24020400, 0x8f43006c, +0x10000005, 0x431023, 0x8f420074, 0x8f43006c, +0x431023, 0x2442ffff, 0x405021, 0x8a102a, +0x54400001, 0x805021, 0x8f49006c, 0x8f48006c, +0x8f440198, 0x8f45019c, 0x8f46006c, 0x24074000, +0xafa70010, 0x84140, 0x1001821, 0x12a4821, +0x313003ff, 0xafb00014, 0x8f470014, 0x1021, +0x63140, 0x24c66cc0, 0xafa70018, 0xa32821, +0xa3382b, 0x822021, 0x872021, 0x8f420108, +0x2e63021, 0x40f809, 0xa3940, 0x54400001, +0xaf50006c, 0x8f43006c, 0x8f420068, 0x14620018, +0x0, 0x8f420000, 0x10400007, 0x0, +0xaf80004c, 0x8f82004c, 0x1040fffd, 0x0, +0x10000005, 0x0, 0xaf800048, 0x8f820048, +0x1040fffd, 0x0, 0x8f820060, 0x2403f7ff, +0x431024, 0xaf820060, 0x8f420000, 0x10400003, +0x0, 0x10000002, 0xaf80004c, 0xaf800048, +0x8fbf0024, 0x8fb00020, 0x3e00008, 0x27bd0028, +0x3e00008, 0x0, 0x8f4200fc, 0x3c030001, +0x8f4400f8, 0x346330c8, 0x24420001, 0xaf4200fc, +0x8f850128, 0x2e31021, 0x54820004, 0x24820008, +0x3c020001, 0x34422ec8, 0x2e21021, 0x401821, +0xaf4300f8, 0xac600000, 0x8f4200f4, 0x14620004, +0x3c020001, 0x24a20020, 0x1000000f, 0xaf820128, +0x8f4300f8, 0x344230c8, 0x2e21021, 0x54620004, +0x24620008, 0x3c020001, 0x34422ec8, 0x2e21021, +0x401821, 0x8c620004, 0x21140, 0xa21021, +0xaf820128, 0xac600000, 0x8ca30018, 0x30620070, +0x1040002d, 0x30620020, 0x10400004, 0x3c020010, +0x2c21024, 0x1040000d, 0x0, 0x30620040, +0x10400004, 0x3c020020, 0x2c21024, 0x10400007, +0x0, 0x30620010, 0x1040001f, 0x3c020040, +0x2c21024, 0x1440001c, 0x0, 0x8f820040, +0x30420001, 0x14400008, 0x2021, 0x8c030104, +0x24020001, 0x50620005, 0x24040001, 0x8c020264, +0x10400003, 0x801021, 0x24040001, 0x801021, +0x10400006, 0x0, 0x8f42030c, 0x24420001, +0xaf42030c, 0x10000008, 0x8f42030c, 0x8f820044, +0x34420004, 0xaf820044, 0x8f420308, 0x24420001, +0xaf420308, 0x8f420308, 0x3e00008, 0x0, +0x3e00008, 0x0, 0x27bdff98, 0xafbf0060, +0xafbe005c, 0xafb50058, 0xafb30054, 0xafb20050, +0xafb1004c, 0xafb00048, 0x8f4200fc, 0x24420001, +0xaf4200fc, 0x8f880128, 0x25020020, 0xaf820128, +0x8d030018, 0x30620070, 0x1040002e, 0x30620020, +0x10400004, 0x3c020010, 0x2c21024, 0x1040000d, +0x0, 0x30620040, 0x10400004, 0x3c020020, +0x2c21024, 0x10400007, 0x0, 0x30620010, +0x104001a9, 0x3c020040, 0x2c21024, 0x144001a6, +0x0, 0x8f820040, 0x30420001, 0x14400008, +0x2021, 0x8c030104, 0x24020001, 0x50620005, +0x24040001, 0x8c020264, 0x10400003, 0x801021, +0x24040001, 0x801021, 0x10400006, 0x0, +0x8f42030c, 0x24420001, 0xaf42030c, 0x10000192, +0x8f42030c, 0x8f820044, 0x34420004, 0xaf820044, +0x8f420308, 0x24420001, 0xaf420308, 0x1000018a, +0x8f420308, 0x30620002, 0x1040014b, 0x3c020800, +0x8d1e001c, 0x1e5702, 0xafaa0034, 0x950a0016, +0x3c22024, 0xafaa0024, 0x8faa0034, 0x24020001, +0x15420006, 0x33deffff, 0x1e1140, 0x3403ecc0, +0x431021, 0x10000010, 0x2e2a821, 0x24020002, +0x15420005, 0x24020003, 0x1e1140, 0x24426cc0, +0x10000009, 0x2e2a821, 0x15420005, 0x1e1180, +0x1e1140, 0x24424cc0, 0x10000003, 0x2e2a821, +0x571021, 0x24550ce0, 0x96a2000e, 0x304afffc, +0x30420400, 0x10400003, 0xafaa002c, 0x100000e1, +0x8821, 0x10800004, 0x8821, 0x97b10026, +0x100000dd, 0xa6b10012, 0x8eb30018, 0x966a000c, +0xa7aa003e, 0x97a5003e, 0x2ca305dd, 0x38a28870, +0x2c420001, 0x621825, 0x10600015, 0x2021, +0x32c20800, 0x10400015, 0x24020800, 0x96630014, +0x14620012, 0x3402aaaa, 0x9663000e, 0x14620007, +0x2821, 0x96630010, 0x24020300, 0x14620004, +0xa01021, 0x96620012, 0x2c450001, 0xa01021, +0x54400006, 0x24040016, 0x10000004, 0x0, +0x24020800, 0x50a20001, 0x2404000e, 0x108000b9, +0x2649021, 0x92420000, 0x3042000f, 0x28080, +0x32c20100, 0x10400020, 0x2501821, 0x3c020020, +0x43102b, 0x1440000e, 0x2402021, 0x2821, +0x94820000, 0x24840002, 0xa22821, 0x83102b, +0x1440fffb, 0x30a2ffff, 0x51c02, 0x622821, +0x51c02, 0x30a2ffff, 0x10000009, 0x622821, +0x8f470148, 0x8f420110, 0x102842, 0x3c060020, +0x40f809, 0xafa80040, 0x3045ffff, 0x8fa80040, +0x50a00001, 0x3405ffff, 0x8faa002c, 0x354a0002, +0x10000002, 0xafaa002c, 0x2821, 0x32c20080, +0x10400090, 0xa6a50010, 0x26430009, 0x3c02001f, +0x3442ffff, 0x43102b, 0x10400003, 0x0, +0x8f420148, 0x621823, 0x90660000, 0x30c200ff, +0x38430006, 0x2c630001, 0x38420011, 0x2c420001, +0x621825, 0x1060007f, 0x24020800, 0x8821, +0x97a3003e, 0x1462000f, 0x2602021, 0x96710000, +0x96620002, 0x96630004, 0x96640006, 0x2228821, +0x2238821, 0x2248821, 0x96620008, 0x9663000a, +0x9664000c, 0x2228821, 0x2238821, 0x10000007, +0x2248821, 0x94820000, 0x24840002, 0x2228821, +0x92102b, 0x1440fffb, 0x0, 0x111c02, +0x3222ffff, 0x628821, 0x111c02, 0x3222ffff, +0x628821, 0x32c20200, 0x10400003, 0x26440006, +0x1000003e, 0x8021, 0x3c05001f, 0x34a5ffff, +0xa4102b, 0x10400003, 0x0, 0x8f420148, +0x822023, 0x94820000, 0x30421fff, 0x10400004, +0x2644000c, 0x96420002, 0x10000030, 0x508023, +0x96420002, 0x26430014, 0x508023, 0x3c020020, +0x43102b, 0x1440000a, 0xd08021, 0x9642000c, +0x2028021, 0x9642000e, 0x96430010, 0x96440012, +0x2028021, 0x2038021, 0x10000020, 0x2048021, +0xa4102b, 0x10400003, 0x0, 0x8f420148, +0x822023, 0x94820000, 0x24840002, 0x2028021, +0xa4102b, 0x10400003, 0x0, 0x8f420148, +0x822023, 0x94820000, 0x24840002, 0x2028021, +0xa4102b, 0x10400003, 0x0, 0x8f420148, +0x822023, 0x94820000, 0x24840002, 0x2028021, +0xa4102b, 0x10400003, 0x0, 0x8f420148, +0x822023, 0x94820000, 0x2028021, 0x3c020100, +0x2c21024, 0x1040000e, 0x0, 0x8faa002c, +0x31420004, 0x1040000a, 0x0, 0x9504000e, +0x2642021, 0xc003eec, 0x2484fffc, 0x3042ffff, +0x2228821, 0x111c02, 0x3222ffff, 0x628821, +0x8faa0024, 0x1518823, 0x111402, 0x2228821, +0x2308821, 0x111402, 0x2228821, 0x3231ffff, +0x52200001, 0x3411ffff, 0x8faa002c, 0x354a0001, +0xafaa002c, 0xa6b10012, 0x97aa002e, 0xa6aa000e, +0x8faa002c, 0x31420004, 0x10400002, 0x24091000, +0x34098000, 0x8f480044, 0x8f4401a0, 0x8f4501a4, +0xafa90010, 0x8f490044, 0x84140, 0x1001821, +0xafa90014, 0x8f48000c, 0x2a03021, 0x24070020, +0xafa80018, 0x8f48010c, 0x1021, 0xa32821, +0xa3482b, 0x822021, 0x100f809, 0x892021, +0x1440000b, 0x0, 0x8f820128, 0x3c040001, +0x24846914, 0xafbe0014, 0xafa20010, 0x8f860124, +0x8f870120, 0x3c050007, 0xc002b3b, 0x34a59920, +0x8f420368, 0x2442ffff, 0xaf420368, 0x8f420044, +0x8f430088, 0x24420001, 0x431024, 0xaf420044, +0x8faa0034, 0x8f440368, 0x24020001, 0x15420006, +0x24020002, 0x8f42035c, 0x2442ffff, 0xaf42035c, +0x10000049, 0x8f42035c, 0x15420006, 0x0, +0x8f420364, 0x2442ffff, 0xaf420364, 0x10000042, +0x8f420364, 0x8f420360, 0x2442ffff, 0xaf420360, +0x1000003d, 0x8f420360, 0x30621000, 0x10400005, +0x30628000, 0x8f420078, 0x24420001, 0x10000036, +0xaf420078, 0x10400034, 0x0, 0x8f420078, +0x24420001, 0xaf420078, 0x8c030240, 0x43102b, +0x1440002d, 0x24070008, 0x8f440168, 0x8f45016c, +0x8f430044, 0x8f48000c, 0x8f860120, 0x24020040, +0xafa20010, 0xafa30014, 0xafa80018, 0x8f42010c, +0x40f809, 0x24c6001c, 0x14400011, 0x24020001, +0x3c010001, 0x370821, 0xa02240f2, 0x8f820124, +0xafa20010, 0x8f820128, 0x3c040001, 0x2484688c, +0xafa20014, 0x8f460044, 0x8f870120, 0x3c050009, +0xc002b3b, 0x34a51300, 0x1000000b, 0x0, +0x8f420304, 0x24420001, 0xaf420304, 0x8f420304, +0x8f420044, 0xaf42007c, 0x3c010001, 0x370821, +0xa02040f2, 0xaf400078, 0x8f420318, 0x24420001, +0xaf420318, 0x8f420318, 0x8fbf0060, 0x8fbe005c, +0x8fb50058, 0x8fb30054, 0x8fb20050, 0x8fb1004c, +0x8fb00048, 0x3e00008, 0x27bd0068, 0x3e00008, +0x0, 0x0, 0x0, 0x8f42013c, +0xaf8200c0, 0x8f42013c, 0xaf8200c4, 0x8f42013c, +0xaf8200c8, 0x8f420138, 0xaf8200d0, 0x8f420138, +0xaf8200d4, 0x8f420138, 0x3e00008, 0xaf8200d8, +0x27bdffe0, 0x27840208, 0x24050200, 0xafbf0018, +0xc002bbf, 0x24060008, 0x8c020204, 0xc004012, +0xaf820210, 0x3c020001, 0x8c426d94, 0x30420002, +0x1040000e, 0x2021, 0x8c060248, 0x24020002, +0x3c010001, 0xac226d98, 0xc005104, 0x24050002, +0x2021, 0x8c060248, 0x24020001, 0x3c010001, +0xac226d98, 0x10000011, 0x24050001, 0x8c060248, +0x24020004, 0x3c010001, 0xac226d98, 0xc005104, +0x24050004, 0x3c020001, 0x8c426d94, 0x30420001, +0x10400008, 0x24020001, 0x3c010001, 0xac226d98, +0x2021, 0x24050001, 0x3c06601b, 0xc005104, +0x0, 0x3c040001, 0x248469d0, 0x8f420150, +0x8f430154, 0x3c050008, 0x8f460158, 0x21640, +0x31940, 0x34630403, 0x431025, 0x633c0, +0x461025, 0xaf82021c, 0xafa00010, 0xafa00014, +0x8f86021c, 0x34a50200, 0xc002b3b, 0x3821, +0x3c010001, 0xac206d90, 0x3c010001, 0xac206da8, +0x8fbf0018, 0x3e00008, 0x27bd0020, 0x27bdffe0, +0x3c050008, 0x34a50300, 0xafbf0018, 0xafa00010, +0xafa00014, 0x8f860200, 0x3c040001, 0x248469dc, +0xc002b3b, 0x3821, 0x8f420410, 0x24420001, +0xaf420410, 0x8f420410, 0x8fbf0018, 0x3e00008, +0x27bd0020, 0x27bdffd8, 0xafbf0020, 0xafb1001c, +0xafb00018, 0x8f4203a4, 0x24420001, 0xaf4203a4, +0x8f4203a4, 0x8f900220, 0x8f8200e0, 0xafa20010, +0x8f8200e4, 0xafa20014, 0x8f8600c4, 0x8f8700c8, +0x3c040001, 0x248469e8, 0xc002b3b, 0x2002821, +0x3c044000, 0x2041024, 0x504000b4, 0x3c040100, +0x8f4203bc, 0x24420001, 0xaf4203bc, 0x8f4203bc, +0x8f8700c4, 0x8f8300c8, 0x8f420148, 0x671823, +0x43102b, 0x10400003, 0x0, 0x8f420148, +0x621821, 0x10600005, 0x0, 0x8f42014c, +0x43102b, 0x1040000b, 0x0, 0x8f8200e0, +0x8f430124, 0xaf42011c, 0xaf430114, 0x8f820220, +0x3c0308ff, 0x3463fffb, 0x431024, 0x100000ce, +0x441025, 0x8f820220, 0x3c0308ff, 0x3463ffff, +0x431024, 0x34420004, 0xaf820220, 0x8f8200e0, +0x8f430124, 0xaf42011c, 0xaf430114, 0x8f8600c8, +0x8f840120, 0x8f830124, 0x10000005, 0x2821, +0x14620002, 0x24620020, 0x27624800, 0x401821, +0x1064000c, 0x30a200ff, 0x8c620018, 0x30420003, +0x1040fff7, 0x27624fe0, 0x8f4203d0, 0x24050001, +0x24420001, 0xaf4203d0, 0x8f4203d0, 0x8c660008, +0x30a200ff, 0x14400058, 0x0, 0x934205c4, +0x14400055, 0x0, 0x8f8700c4, 0x8f8800e0, +0x8f8400e4, 0x2402fff8, 0x1024024, 0x1041023, +0x218c3, 0x4620001, 0x24630200, 0x10600005, +0x24020001, 0x10620009, 0x0, 0x1000001f, +0x0, 0x8f4203c0, 0xe03021, 0x24420001, +0xaf4203c0, 0x10000040, 0x8f4203c0, 0x8f4203c4, +0x24420001, 0xaf4203c4, 0x8c860000, 0x8f420148, +0x8f4303c4, 0xe61823, 0x43102b, 0x10400004, +0x2c62233f, 0x8f420148, 0x621821, 0x2c62233f, +0x14400031, 0x0, 0x8f42020c, 0x24420001, +0xaf42020c, 0x8f42020c, 0xe03021, 0x24820008, +0xaf8200e4, 0x10000028, 0xaf8200e8, 0x8f4203c8, +0x24420001, 0xaf4203c8, 0x8f4203c8, 0x8c850000, +0x8f420148, 0xa71823, 0x43102b, 0x10400003, +0x0, 0x8f420148, 0x621821, 0x8f42014c, +0x43102b, 0x5440000a, 0xa03021, 0x8f42020c, +0x24420001, 0xaf42020c, 0x8f42020c, 0x24820008, +0xaf8200e4, 0x8f8400e4, 0x1488ffec, 0xaf8400e8, +0x1488000d, 0x27623000, 0x14820002, 0x2482fff8, +0x27623ff8, 0x94430006, 0x3c02001f, 0x3442ffff, +0xc33021, 0x46102b, 0x10400003, 0x0, +0x8f420148, 0xc23023, 0xaf8600c8, 0x8f8300c4, +0x8f420148, 0xc31823, 0x43102b, 0x10400003, +0x0, 0x8f420148, 0x621821, 0x10600005, +0x0, 0x8f42014c, 0x43102b, 0x50400008, +0x3c02fdff, 0x8f820220, 0x3c0308ff, 0x3463fffb, +0x431024, 0x3c034000, 0x1000003f, 0x431025, +0x8f4303cc, 0x3442ffff, 0x282a024, 0x24630001, +0xaf4303cc, 0x10000039, 0x8f4203cc, 0x2041024, +0x1040000e, 0x3c110200, 0x8f4203a8, 0x24420001, +0xaf4203a8, 0x8f4203a8, 0x8f820220, 0x3c0308ff, +0x3463ffff, 0x431024, 0x441025, 0xc003daf, +0xaf820220, 0x10000029, 0x0, 0x2111024, +0x50400008, 0x3c110400, 0x8f4203ac, 0x24420001, +0xaf4203ac, 0xc003daf, 0x8f4203ac, 0x10000019, +0x0, 0x2111024, 0x1040001c, 0x0, +0x8f830224, 0x24021402, 0x14620009, 0x3c050008, +0x3c040001, 0x248469f4, 0xafa00010, 0xafa00014, +0x8f860224, 0x34a50500, 0xc002b3b, 0x3821, +0x8f4203b0, 0x24420001, 0xaf4203b0, 0x8f4203b0, +0x8f820220, 0x2002021, 0x34420002, 0xc004e9c, +0xaf820220, 0x8f820220, 0x3c0308ff, 0x3463ffff, +0x431024, 0x511025, 0xaf820220, 0x8fbf0020, +0x8fb1001c, 0x8fb00018, 0x3e00008, 0x27bd0028, +0x3e00008, 0x0, 0x3c020001, 0x8c426da8, +0x27bdffb0, 0xafbf0048, 0xafbe0044, 0xafb50040, +0xafb3003c, 0xafb20038, 0xafb10034, 0x1040000f, +0xafb00030, 0x3c040001, 0x24846a00, 0x3c050008, +0xafa00010, 0xafa00014, 0x8f860220, 0x34a50600, +0x24020001, 0x3c010001, 0xac206da8, 0x3c010001, +0xac226d9c, 0xc002b3b, 0x3821, 0x3c037fff, +0x8c020268, 0x3463ffff, 0x3c04fdff, 0x431024, +0xac020268, 0x8f420004, 0x3484ffff, 0x30420002, +0x10400092, 0x284a024, 0x3c040600, 0x34842000, +0x8f420004, 0x2821, 0x2403fffd, 0x431024, +0xaf420004, 0xafa40020, 0x8f5e0018, 0x27aa0020, +0x240200ff, 0x13c20002, 0xafaa002c, 0x27c50001, +0x8c020228, 0xa09021, 0x1642000e, 0x1e38c0, +0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, +0x8c020228, 0x3c040001, 0x24846998, 0x3c050009, +0xafa00014, 0xafa20010, 0x8fa60020, 0x1000006d, +0x34a50500, 0xf71021, 0x8fa30020, 0x8fa40024, +0xac4304c0, 0xac4404c4, 0x8f830054, 0x8f820054, +0x247003e8, 0x2021023, 0x2c4203e9, 0x1040001b, +0x9821, 0xe08821, 0x263504c0, 0x8f440178, +0x8f45017c, 0x2201821, 0x240a0004, 0xafaa0010, +0xafb20014, 0x8f48000c, 0x1021, 0x2f53021, +0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, +0xa3482b, 0x822021, 0x100f809, 0x892021, +0x54400006, 0x24130001, 0x8f820054, 0x2021023, +0x2c4203e9, 0x1440ffe9, 0x0, 0x326200ff, +0x54400017, 0xaf520018, 0x8f420378, 0x24420001, +0xaf420378, 0x8f420378, 0x8f820120, 0x8faa002c, +0xafa20010, 0x8f820124, 0x3c040001, 0x248469a4, +0x3c050009, 0xafa20014, 0x8d460000, 0x10000035, +0x34a50600, 0x8f420308, 0x24130001, 0x24420001, +0xaf420308, 0x8f420308, 0x1000001e, 0x326200ff, +0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, +0x2c4203e9, 0x10400016, 0x9821, 0x3c150020, +0x24110010, 0x8f42000c, 0x8f440160, 0x8f450164, +0x8f860120, 0xafb10010, 0xafb20014, 0x551025, +0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, +0x24c6001c, 0x1440ffe3, 0x0, 0x8f820054, +0x2021023, 0x2c4203e9, 0x1440ffee, 0x0, +0x326200ff, 0x14400011, 0x0, 0x8f420378, +0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, +0x8faa002c, 0xafa20010, 0x8f820124, 0x3c040001, +0x248469ac, 0x3c050009, 0xafa20014, 0x8d460000, +0x34a50700, 0xc002b3b, 0x3c03821, 0x8f4202ec, +0x24420001, 0xaf4202ec, 0x8f4202ec, 0x8fbf0048, +0x8fbe0044, 0x8fb50040, 0x8fb3003c, 0x8fb20038, +0x8fb10034, 0x8fb00030, 0x3e00008, 0x27bd0050, +0x3c020001, 0x8c426da8, 0x27bdffe0, 0x1440000d, +0xafbf0018, 0x3c040001, 0x24846a0c, 0x3c050008, +0xafa00010, 0xafa00014, 0x8f860220, 0x34a50700, +0x24020001, 0x3c010001, 0xac226da8, 0xc002b3b, +0x3821, 0x3c020004, 0x2c21024, 0x10400007, +0x0, 0x8f820220, 0x3c0308ff, 0x3463ffff, +0x431024, 0x34420008, 0xaf820220, 0x3c050001, +0x8ca56d98, 0x24020001, 0x14a20007, 0x2021, +0xc00529b, 0x24050001, 0xac02026c, 0x8c03026c, +0x10000006, 0x3c020007, 0xc00529b, 0x2021, +0xac020268, 0x8c030268, 0x3c020007, 0x621824, +0x3c020002, 0x5062000d, 0x3c0205f5, 0x43102b, +0x14400006, 0x3c020004, 0x3c020001, 0x10620009, +0x3c020098, 0x1000000b, 0x0, 0x14620009, +0x3c023b9a, 0x10000004, 0x3442ca00, 0x10000002, +0x3442e100, 0x34429680, 0xaf4201fc, 0x8f4201fc, +0xaee20064, 0x8fbf0018, 0x3e00008, 0x27bd0020, +0x0, 0x0, 0x0, 0x86102b, +0x50400001, 0x872023, 0xc41023, 0x24843, +0x125102b, 0x1040001b, 0x91040, 0x824021, +0x88102b, 0x10400007, 0x1821, 0x94820000, +0x24840002, 0x621821, 0x88102b, 0x1440fffb, +0x0, 0x602021, 0xc73023, 0xa91023, +0x21040, 0xc22821, 0xc5102b, 0x10400007, +0x1821, 0x94c20000, 0x24c60002, 0x621821, +0xc5102b, 0x1440fffb, 0x0, 0x1000000d, +0x832021, 0x51040, 0x822821, 0x85102b, +0x10400007, 0x1821, 0x94820000, 0x24840002, +0x621821, 0x85102b, 0x1440fffb, 0x0, +0x602021, 0x41c02, 0x3082ffff, 0x622021, +0x41c02, 0x3082ffff, 0x622021, 0x3e00008, +0x3082ffff, 0x3e00008, 0x0, 0x802821, +0x30a20001, 0x1040002b, 0x3c03001f, 0x3463ffff, +0x24a20004, 0x62102b, 0x54400007, 0x65102b, +0x90a20001, 0x90a40003, 0x90a30000, 0x90a50002, +0x1000002a, 0x441021, 0x10400003, 0x0, +0x8f420148, 0xa22823, 0x90a40000, 0x24a50001, +0x65102b, 0x10400003, 0x0, 0x8f420148, +0xa22823, 0x90a20000, 0x24a50001, 0x21200, +0x822021, 0x65102b, 0x10400003, 0x0, +0x8f420148, 0xa22823, 0x90a20000, 0x24a50001, +0x822021, 0x65102b, 0x10400003, 0x0, +0x8f420148, 0xa22823, 0x90a20000, 0x1000002d, +0x21200, 0x3463ffff, 0x24a20004, 0x62102b, +0x5440000a, 0x65102b, 0x90a20000, 0x90a40002, +0x90a30001, 0x90a50003, 0x441021, 0x21200, +0x651821, 0x10000020, 0x432021, 0x10400003, +0x0, 0x8f420148, 0xa22823, 0x90a20000, +0x24a50001, 0x22200, 0x65102b, 0x10400003, +0x0, 0x8f420148, 0xa22823, 0x90a20000, +0x24a50001, 0x822021, 0x65102b, 0x10400003, +0x0, 0x8f420148, 0xa22823, 0x90a20000, +0x24a50001, 0x21200, 0x822021, 0x65102b, +0x10400003, 0x0, 0x8f420148, 0xa22823, +0x90a20000, 0x822021, 0x41c02, 0x3082ffff, +0x622021, 0x41c02, 0x3082ffff, 0x622021, +0x3e00008, 0x3082ffff, 0x0, 0x8f820220, +0x34420002, 0xaf820220, 0x3c020002, 0x8c428ff8, +0x30424000, 0x10400054, 0x24040001, 0x8f820200, +0x24067fff, 0x8f830200, 0x30450002, 0x2402fffd, +0x621824, 0xaf830200, 0xaf840204, 0x8f830054, +0x8f820054, 0x10000002, 0x24630001, 0x8f820054, +0x621023, 0x2c420002, 0x1440fffc, 0x0, +0x8f820224, 0x1444004d, 0x42040, 0xc4102b, +0x1040fff1, 0x0, 0x8f820200, 0x451025, +0xaf820200, 0x8f820220, 0x34428000, 0xaf820220, +0x8f830054, 0x8f820054, 0x10000002, 0x24630001, +0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, +0x0, 0x8f820220, 0x3c030004, 0x431024, +0x1440000f, 0x0, 0x8f820220, 0x3c03ffff, +0x34637fff, 0x431024, 0xaf820220, 0x8f830054, +0x8f820054, 0x10000002, 0x24630001, 0x8f820054, +0x621023, 0x2c420002, 0x1440fffc, 0x0, +0x8f820220, 0x3c030004, 0x431024, 0x1440000d, +0x0, 0x8f820220, 0x34428000, 0xaf820220, +0x8f830054, 0x8f820054, 0x10000002, 0x24630001, +0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, +0x0, 0x8f820220, 0x3c030004, 0x431024, +0x1040001b, 0x1021, 0x8f830220, 0x24020001, +0x10000015, 0x3c04f700, 0x8f820220, 0x3c04f700, +0x441025, 0xaf820220, 0x8f820220, 0x2403fffd, +0x431024, 0xaf820220, 0x8f820220, 0x3c030300, +0x431024, 0x14400003, 0x0, 0x10000008, +0x1021, 0x8f820220, 0x34420002, 0xaf820220, +0x8f830220, 0x24020001, 0x641825, 0xaf830220, +0x3e00008, 0x0, 0x2021, 0x3c050100, +0x24020001, 0xaf80021c, 0xaf820200, 0xaf820220, +0x27625000, 0xaf8200c0, 0x27625000, 0xaf8200c4, +0x27625000, 0xaf8200c8, 0x27625000, 0xaf8200d0, +0x27625000, 0xaf8200d4, 0x27625000, 0xaf8200d8, +0x27623000, 0xaf8200e0, 0x27623000, 0xaf8200e4, +0x27623000, 0xaf8200e8, 0x27622800, 0xaf8200f0, +0x27622800, 0xaf8200f4, 0x27622800, 0xaf8200f8, +0x418c0, 0x24840001, 0x3631021, 0xac453004, +0x3631021, 0xac403000, 0x28820200, 0x1440fff9, +0x418c0, 0x2021, 0x418c0, 0x24840001, +0x3631021, 0xac402804, 0x3631021, 0xac402800, +0x28820100, 0x1440fff9, 0x418c0, 0xaf80023c, +0x24030080, 0x24040100, 0xac600000, 0x24630004, +0x64102b, 0x5440fffd, 0xac600000, 0x8f830040, +0x3c02f000, 0x621824, 0x3c025000, 0x1062000c, +0x43102b, 0x14400006, 0x3c026000, 0x3c024000, +0x10620008, 0x24020800, 0x10000008, 0x0, +0x10620004, 0x24020800, 0x10000004, 0x0, +0x24020700, 0x3c010001, 0xac226dac, 0x3e00008, +0x0, 0x3c020001, 0x8c426dbc, 0x27bdffd0, +0xafbf002c, 0xafb20028, 0xafb10024, 0xafb00020, +0x3c010001, 0x10400005, 0xac206d94, 0xc004d9e, +0x0, 0x3c010001, 0xac206dbc, 0x8f830054, +0x8f820054, 0x10000002, 0x24630064, 0x8f820054, +0x621023, 0x2c420065, 0x1440fffc, 0x0, +0xc004db9, 0x0, 0x24040001, 0x2821, +0x27a60018, 0x34028000, 0xc0045be, 0xa7a20018, +0x8f830054, 0x8f820054, 0x10000002, 0x24630064, +0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, +0x24040001, 0x24050001, 0xc00457c, 0x27a60018, +0x8f830054, 0x8f820054, 0x10000002, 0x24630064, +0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, +0x24040001, 0x24050001, 0xc00457c, 0x27a60018, +0x8f830054, 0x8f820054, 0x10000002, 0x24630064, +0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, +0x24040001, 0x3c060001, 0x24c66f24, 0xc00457c, +0x24050002, 0x8f830054, 0x8f820054, 0x10000002, +0x24630064, 0x8f820054, 0x621023, 0x2c420065, +0x1440fffc, 0x24040001, 0x24050003, 0x3c100001, +0x26106f26, 0xc00457c, 0x2003021, 0x97a60018, +0x3c070001, 0x94e76f24, 0x3c040001, 0x24846ae0, +0xafa00014, 0x96020000, 0x3c05000d, 0x34a50100, +0xc002b3b, 0xafa20010, 0x97a20018, 0x1040004d, +0x24036040, 0x96020000, 0x3042fff0, 0x1443000c, +0x24020020, 0x3c030001, 0x94636f24, 0x1462000b, +0x24027830, 0x24020003, 0x3c010001, 0xac226d94, +0x24020005, 0x3c010001, 0x1000003f, 0xac226f34, +0x3c030001, 0x94636f24, 0x24027830, 0x1462000c, +0x24030010, 0x3c020001, 0x94426f26, 0x3042fff0, +0x14430007, 0x24020003, 0x3c010001, 0xac226d94, +0x24020006, 0x3c010001, 0x1000002f, 0xac226f34, +0x3c020001, 0x8c426d94, 0x3c030001, 0x94636f24, +0x34420001, 0x3c010001, 0xac226d94, 0x24020015, +0x1462000b, 0x0, 0x3c020001, 0x94426f26, +0x3042fff0, 0x3843f420, 0x2c630001, 0x3842f430, +0x2c420001, 0x621825, 0x1460001b, 0x24020003, +0x3c030001, 0x94636f24, 0x24027810, 0x14620016, +0x24020002, 0x3c020001, 0x94426f26, 0x3042fff0, +0x14400011, 0x24020002, 0x1000000f, 0x24020004, +0x3c020001, 0x8c426d94, 0x34420008, 0x3c010001, +0xac226d94, 0x1000005e, 0x24020004, 0x3c020001, +0x8c426d94, 0x34420004, 0x3c010001, 0x100000af, +0xac226d94, 0x24020001, 0x3c010001, 0xac226f40, +0x3c020001, 0x8c426d94, 0x30420002, 0x144000b2, +0x3c09fff0, 0x24020e00, 0xaf820238, 0x8f840054, +0x8f820054, 0x24030008, 0x3c010001, 0xac236d98, +0x10000002, 0x248401f4, 0x8f820054, 0x821023, +0x2c4201f5, 0x1440fffc, 0x3c0200c8, 0x344201fb, +0xaf820238, 0x8f830054, 0x8f820054, 0x10000002, +0x246301f4, 0x8f820054, 0x621023, 0x2c4201f5, +0x1440fffc, 0x8021, 0x24120001, 0x24110009, +0xc004482, 0x0, 0x3c010001, 0xac326db4, +0xc004547, 0x0, 0x3c020001, 0x8c426db4, +0x1451fffb, 0x3c0200c8, 0x344201f6, 0xaf820238, +0x8f830054, 0x8f820054, 0x10000002, 0x2463000a, +0x8f820054, 0x621023, 0x2c42000b, 0x1440fffc, +0x0, 0x8f820220, 0x24040001, 0x34420002, +0xaf820220, 0x8f830200, 0x24057fff, 0x2402fffd, +0x621824, 0xaf830200, 0xaf840204, 0x8f830054, +0x8f820054, 0x10000002, 0x24630001, 0x8f820054, +0x621023, 0x2c420002, 0x1440fffc, 0x0, +0x8f820224, 0x14440005, 0x34028000, 0x42040, +0xa4102b, 0x1040fff0, 0x34028000, 0x1082ffa0, +0x26100001, 0x2e020014, 0x1440ffcd, 0x24020004, +0x3c010001, 0xac226d98, 0x8021, 0x24120009, +0x3c11ffff, 0x36313f7f, 0xc004482, 0x0, +0x24020001, 0x3c010001, 0xac226db4, 0xc004547, +0x0, 0x3c020001, 0x8c426db4, 0x1452fffb, +0x0, 0x8f820044, 0x511024, 0x34425080, +0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, +0x2463000a, 0x8f820054, 0x621023, 0x2c42000b, +0x1440fffc, 0x0, 0x8f820044, 0x511024, +0x3442f080, 0xaf820044, 0x8f830054, 0x8f820054, +0x10000002, 0x2463000a, 0x8f820054, 0x621023, +0x2c42000b, 0x1440fffc, 0x0, 0x8f820220, +0x3c03f700, 0x431025, 0xaf820220, 0x8f830054, +0x8f820054, 0x10000002, 0x24630064, 0x8f820054, +0x621023, 0x2c420065, 0x1440fffc, 0x0, +0x8f820220, 0x24040001, 0x34420002, 0xaf820220, +0x8f830200, 0x24057fff, 0x2402fffd, 0x621824, +0xaf830200, 0xaf840204, 0x8f830054, 0x8f820054, +0x10000002, 0x24630001, 0x8f820054, 0x621023, +0x2c420002, 0x1440fffc, 0x0, 0x8f820224, +0x14440005, 0x34028000, 0x42040, 0xa4102b, +0x1040fff0, 0x34028000, 0x1082ff50, 0x26100001, +0x2e020064, 0x1440ffb0, 0x0, 0x3c020001, +0x8c426d94, 0x30420004, 0x14400007, 0x3c09fff0, +0x8f820044, 0x3c03ffff, 0x34633f7f, 0x431024, +0xaf820044, 0x3c09fff0, 0x3529bdc0, 0x3c060001, +0x8cc66d94, 0x3c040001, 0x24846ae0, 0x24020001, +0x3c010001, 0xac226d9c, 0x8f820054, 0x3c070001, +0x8ce76f40, 0x3c030001, 0x94636f24, 0x3c080001, +0x95086f26, 0x3c05000d, 0x34a50100, 0x3c010001, +0xac206d98, 0x491021, 0x3c010001, 0xac226f30, +0xafa30010, 0xc002b3b, 0xafa80014, 0x8fbf002c, +0x8fb20028, 0x8fb10024, 0x8fb00020, 0x3e00008, +0x27bd0030, 0x27bdffe8, 0x3c050001, 0x8ca56d98, +0x24060004, 0x24020001, 0x14a20014, 0xafbf0010, +0x3c020002, 0x8c428ffc, 0x30428000, 0x10400005, +0x3c04000f, 0x3c030001, 0x8c636f40, 0x10000005, +0x34844240, 0x3c040004, 0x3c030001, 0x8c636f40, +0x348493e0, 0x24020005, 0x14620016, 0x0, +0x3c04003d, 0x10000013, 0x34840900, 0x3c020002, +0x8c428ff8, 0x30428000, 0x10400005, 0x3c04001e, +0x3c030001, 0x8c636f40, 0x10000005, 0x34848480, +0x3c04000f, 0x3c030001, 0x8c636f40, 0x34844240, +0x24020005, 0x14620003, 0x0, 0x3c04007a, +0x34841200, 0x3c020001, 0x8c426f30, 0x8f830054, +0x441021, 0x431023, 0x44102b, 0x1440004c, +0x0, 0x3c020001, 0x8c426da0, 0x14400048, +0x0, 0x3c010001, 0x10c00025, 0xac206db0, +0x3c090001, 0x8d296d94, 0x24070001, 0x3c044000, +0x3c080002, 0x25088ffc, 0x250afffc, 0x52842, +0x14a00002, 0x24c6ffff, 0x24050008, 0xa91024, +0x10400010, 0x0, 0x14a70008, 0x0, +0x8d020000, 0x441024, 0x1040000a, 0x0, +0x3c010001, 0x10000007, 0xac256db0, 0x8d420000, +0x441024, 0x10400003, 0x0, 0x3c010001, +0xac276db0, 0x3c020001, 0x8c426db0, 0x6182b, +0x2c420001, 0x431024, 0x5440ffe5, 0x52842, +0x8f820054, 0x3c030001, 0x8c636db0, 0x3c010001, +0xac226f30, 0x1060003b, 0x24020005, 0x3c030001, +0x8c636f40, 0x3c010001, 0xac256d98, 0x14620012, +0x24020001, 0x3c020002, 0x8c428ff8, 0x3c032000, +0x34635000, 0x431024, 0x14400006, 0x24020001, +0x3c010001, 0xac206f1c, 0x3c010001, 0xac226d98, +0x24020001, 0x3c010001, 0xac226e24, 0x3c010001, +0xac226da4, 0x24020001, 0x3c010001, 0xac226d9c, +0x3c020001, 0x8c426db0, 0x1040001e, 0x0, +0x3c020001, 0x8c426d9c, 0x10400008, 0x24020001, +0x3c010001, 0xac206d9c, 0xaee204b8, 0x3c010001, +0xac206e1c, 0x3c010001, 0xac226dd4, 0x8ee304b8, +0x24020008, 0x10620005, 0x24020001, 0xc004239, +0x0, 0x1000000b, 0x0, 0x3c030001, +0x8c636d98, 0x10620007, 0x2402000e, 0x3c030002, +0x8c638f90, 0x10620003, 0x0, 0xc004e9c, +0x8f840220, 0x8fbf0010, 0x3e00008, 0x27bd0018, +0x27bdffe0, 0x3c03fdff, 0x3c040001, 0x8c846d98, +0x3c020001, 0x8c426dc0, 0x3463ffff, 0x283a024, +0x14820006, 0xafbf0018, 0x8ee304b8, 0x3c020001, +0x8c426dc4, 0x10620006, 0x0, 0x8ee204b8, +0x3c010001, 0xac246dc0, 0x3c010001, 0xac226dc4, +0x3c030001, 0x8c636d98, 0x24020002, 0x1062019c, +0x2c620003, 0x10400005, 0x24020001, 0x1062000a, +0x0, 0x10000226, 0x0, 0x24020004, +0x106200b6, 0x24020008, 0x1062010a, 0x24020001, +0x1000021f, 0x0, 0x8ee204b8, 0x2443ffff, +0x2c620008, 0x1040021c, 0x31080, 0x3c010001, +0x220821, 0x8c226af8, 0x400008, 0x0, +0x3c030001, 0x8c636f40, 0x24020005, 0x14620010, +0x0, 0x3c020001, 0x8c426da4, 0x10400008, +0x24020003, 0xc004482, 0x0, 0x24020002, +0xaee204b8, 0x3c010001, 0x10000002, 0xac206da4, +0xaee204b8, 0x3c010001, 0x10000203, 0xac206d30, +0xc004482, 0x0, 0x3c020001, 0x8c426da4, +0x3c010001, 0xac206d30, 0x1440017a, 0x24020002, +0x1000019d, 0x24020007, 0x3c030001, 0x8c636f40, +0x24020005, 0x14620003, 0x24020001, 0x3c010001, +0xac226dd0, 0xc0045ff, 0x0, 0x3c030001, +0x8c636dd0, 0x10000174, 0x24020011, 0x3c050001, +0x8ca56d98, 0x3c060002, 0x8cc68ffc, 0xc005104, +0x2021, 0x24020005, 0x3c010001, 0xac206da4, +0x100001e1, 0xaee204b8, 0x3c040001, 0x24846aec, +0x3c05000f, 0x34a50100, 0x3021, 0x3821, +0xafa00010, 0xc002b3b, 0xafa00014, 0x100001d6, +0x0, 0x8f820220, 0x3c030004, 0x431024, +0x14400175, 0x24020007, 0x8f830054, 0x3c020001, +0x8c426f28, 0x2463d8f0, 0x431023, 0x2c422710, +0x14400003, 0x24020001, 0x3c010001, 0xac226d9c, +0x3c020002, 0x8c428ffc, 0x30425000, 0x104001c2, +0x0, 0x8f820220, 0x30428000, 0x1040017d, +0x0, 0x10000175, 0x0, 0x3c050001, +0x8ca56d98, 0xc00529b, 0x2021, 0xc00551b, +0x2021, 0x3c030002, 0x8c638ff4, 0x46101b0, +0x24020001, 0x3c020008, 0x621024, 0x10400006, +0x0, 0x8f820214, 0x3c03ffff, 0x431024, +0x10000005, 0x3442251f, 0x8f820214, 0x3c03ffff, +0x431024, 0x3442241f, 0xaf820214, 0x8f820220, +0x3c030200, 0x34420002, 0xaf820220, 0x24020008, +0xaee204b8, 0x8f820220, 0x283a025, 0x3c030004, +0x431024, 0x14400016, 0x0, 0x3c020002, +0x8c428ffc, 0x30425000, 0x1040000d, 0x0, +0x8f820220, 0x30428000, 0x10400006, 0x0, +0x8f820220, 0x3c03ffff, 0x34637fff, 0x10000003, +0x431024, 0x8f820220, 0x34428000, 0xaf820220, +0x8f820220, 0x3c03f700, 0x431025, 0xaf820220, +0x3c030001, 0x8c636f40, 0x24020005, 0x1462000a, +0x0, 0x3c020001, 0x94426f26, 0x24429fbc, +0x2c420004, 0x10400004, 0x24040018, 0x24050002, +0xc004ddb, 0x24060020, 0xc003e6d, 0x0, +0x3c010001, 0x10000170, 0xac206e20, 0x8ee204b8, +0x2443ffff, 0x2c620008, 0x1040016b, 0x31080, +0x3c010001, 0x220821, 0x8c226b18, 0x400008, +0x0, 0xc004547, 0x0, 0x3c030001, +0x8c636db4, 0x100000e8, 0x24020009, 0x3c020002, +0x8c428ff8, 0x30424000, 0x10400004, 0x0, +0x8f820044, 0x10000006, 0x3442f080, 0x8f820044, +0x3c03ffff, 0x34633f7f, 0x431024, 0x3442a080, +0xaf820044, 0x8f830054, 0x100000ea, 0x24020004, +0x8f830054, 0x3c020001, 0x8c426f28, 0x2463d8f0, +0x431023, 0x2c422710, 0x14400147, 0x24020005, +0x100000d8, 0x0, 0x8f820220, 0x3c03f700, +0x431025, 0xaf820220, 0xaf800204, 0x3c010002, +0x100000d6, 0xac208fe0, 0x8f830054, 0x3c020001, +0x8c426f28, 0x2463fff6, 0x431023, 0x2c42000a, +0x14400135, 0x24020007, 0x100000d7, 0x0, +0xc003f50, 0x0, 0x1040012d, 0x24020001, +0x8f820214, 0x3c03ffff, 0x3c040001, 0x8c846f1c, +0x431024, 0x3442251f, 0xaf820214, 0x24020008, +0x10800005, 0xaee204b8, 0x3c020001, 0x8c426e44, +0x10400064, 0x24020001, 0x8f820220, 0x3c030008, +0x431024, 0x1040006a, 0x3c020200, 0x10000078, +0x0, 0x8ee204b8, 0x2443ffff, 0x2c620007, +0x10400115, 0x31080, 0x3c010001, 0x220821, +0x8c226b38, 0x400008, 0x0, 0xc003daf, +0x0, 0x3c010001, 0xac206d9c, 0xaf800204, +0x3c010002, 0xc004482, 0xac208fe0, 0x24020001, +0x3c010001, 0xac226db4, 0x24020002, 0x10000102, +0xaee204b8, 0xc004547, 0x0, 0x3c030001, +0x8c636db4, 0x10000084, 0x24020009, 0x3c020002, +0x8c428ff8, 0x30424000, 0x10400003, 0x3c0200c8, +0x10000002, 0x344201f6, 0x344201fe, 0xaf820238, +0x8f830054, 0x1000008b, 0x24020004, 0x8f830054, +0x3c020001, 0x8c426f28, 0x2463d8f0, 0x431023, +0x2c422710, 0x144000e8, 0x24020005, 0x10000079, +0x0, 0x8f820220, 0x3c03f700, 0x431025, +0xaf820220, 0xaf800204, 0x3c010002, 0x10000077, +0xac208fe0, 0x8f830054, 0x3c020001, 0x8c426f28, +0x2463fff6, 0x431023, 0x2c42000a, 0x144000d6, +0x24020007, 0x10000078, 0x0, 0xc003f50, +0x0, 0x104000ce, 0x24020001, 0x8f820214, +0x3c03ffff, 0x3c040001, 0x8c846f1c, 0x431024, +0x3442251f, 0xaf820214, 0x24020008, 0x1080000f, +0xaee204b8, 0x3c020001, 0x8c426e44, 0x1440000b, +0x0, 0x8f820220, 0x34420002, 0xaf820220, +0x24020001, 0x3c010002, 0xac228f90, 0xc004e9c, +0x8f840220, 0x10000016, 0x0, 0x8f820220, +0x3c030008, 0x431024, 0x14400011, 0x3c020200, +0x282a025, 0x2402000e, 0x3c010002, 0xac228f90, +0xc00551b, 0x2021, 0x8f820220, 0x34420002, +0xc003e6d, 0xaf820220, 0x3c050001, 0x8ca56d98, +0xc00529b, 0x2021, 0x100000a3, 0x0, +0x3c020001, 0x8c426e44, 0x1040009f, 0x0, +0x3c020001, 0x8c426e40, 0x2442ffff, 0x3c010001, +0xac226e40, 0x14400098, 0x24020002, 0x3c010001, +0xac206e44, 0x3c010001, 0x10000093, 0xac226e40, +0x8ee204b8, 0x2443ffff, 0x2c620007, 0x1040008e, +0x31080, 0x3c010001, 0x220821, 0x8c226b58, +0x400008, 0x0, 0x3c020001, 0x8c426da4, +0x10400018, 0x24020005, 0xc004482, 0x0, +0x24020002, 0xaee204b8, 0x3c010001, 0x1000007e, +0xac206da4, 0xc004963, 0x0, 0x3c030001, +0x8c636dd4, 0x24020006, 0x14620077, 0x24020003, +0x10000075, 0xaee204b8, 0x3c050001, 0x8ca56d98, +0x3c060002, 0x8cc68ff8, 0xc005104, 0x2021, +0x24020005, 0x1000006c, 0xaee204b8, 0x8f820220, +0x3c03f700, 0x431025, 0xaf820220, 0x8f830054, +0x24020006, 0xaee204b8, 0x3c010001, 0x10000062, +0xac236f28, 0x8f820220, 0x3c030004, 0x431024, +0x10400003, 0x24020007, 0x1000005b, 0xaee204b8, +0x8f830054, 0x3c020001, 0x8c426f28, 0x2463d8f0, +0x431023, 0x2c422710, 0x14400003, 0x24020001, +0x3c010001, 0xac226d9c, 0x3c020002, 0x8c428ff8, +0x30425000, 0x1040004c, 0x0, 0x8f820220, +0x30428000, 0x10400007, 0x0, 0x8f820220, +0x3c03ffff, 0x34637fff, 0x431024, 0x10000042, +0xaf820220, 0x8f820220, 0x34428000, 0x1000003e, +0xaf820220, 0x3c050001, 0x8ca56d98, 0xc00529b, +0x2021, 0xc00551b, 0x2021, 0x3c020002, +0x8c428ff0, 0x4410032, 0x24020001, 0x8f820214, +0x3c03ffff, 0x431024, 0x3442251f, 0xaf820214, +0x24020008, 0xaee204b8, 0x8f820220, 0x34420002, +0xaf820220, 0x8f820220, 0x3c030004, 0x431024, +0x14400016, 0x0, 0x3c020002, 0x8c428ff8, +0x30425000, 0x1040000d, 0x0, 0x8f820220, +0x30428000, 0x10400006, 0x0, 0x8f820220, +0x3c03ffff, 0x34637fff, 0x10000003, 0x431024, +0x8f820220, 0x34428000, 0xaf820220, 0x8f820220, +0x3c03f700, 0x431025, 0xaf820220, 0x3c020001, +0x94426f26, 0x24429fbc, 0x2c420004, 0x10400004, +0x24040018, 0x24050002, 0xc004ddb, 0x24060020, +0xc003e6d, 0x0, 0x10000003, 0x0, +0x3c010001, 0xac226d9c, 0x8fbf0018, 0x3e00008, +0x27bd0020, 0x8f820200, 0x8f820220, 0x8f820220, +0x34420004, 0xaf820220, 0x8f820200, 0x3c050001, +0x8ca56d98, 0x34420004, 0xaf820200, 0x24020002, +0x10a2004b, 0x2ca20003, 0x10400005, 0x24020001, +0x10a2000a, 0x0, 0x100000b1, 0x0, +0x24020004, 0x10a20072, 0x24020008, 0x10a20085, +0x3c02f0ff, 0x100000aa, 0x0, 0x8f830050, +0x3c02f0ff, 0x3442ffff, 0x3c040001, 0x8c846f40, +0x621824, 0x3c020700, 0x621825, 0x24020e00, +0x2484fffb, 0x2c840002, 0xaf830050, 0xaf850200, +0xaf850220, 0x14800006, 0xaf820238, 0x8f820044, +0x3c03ffff, 0x34633f7f, 0x431024, 0xaf820044, +0x3c030001, 0x8c636f40, 0x24020005, 0x14620004, +0x0, 0x8f820044, 0x34425000, 0xaf820044, +0x3c020001, 0x8c426d88, 0x3c030001, 0x8c636f40, +0x34420022, 0x2463fffc, 0x2c630002, 0x1460000c, +0xaf820200, 0x3c020001, 0x8c426dac, 0x3c030001, +0x8c636d90, 0x3c040001, 0x8c846d8c, 0x34428000, +0x621825, 0x641825, 0x1000000a, 0x34620002, +0x3c020001, 0x8c426d90, 0x3c030001, 0x8c636dac, +0x3c040001, 0x8c846d8c, 0x431025, 0x441025, +0x34420002, 0xaf820220, 0x1000002f, 0x24020001, +0x24020e01, 0xaf820238, 0x8f830050, 0x3c02f0ff, +0x3442ffff, 0x3c040001, 0x8c846f1c, 0x621824, +0x3c020d00, 0x621825, 0x24020001, 0xaf830050, +0xaf820200, 0xaf820220, 0x10800005, 0x3c033f00, +0x3c020001, 0x8c426d80, 0x10000004, 0x34630070, +0x3c020001, 0x8c426d80, 0x34630072, 0x431025, +0xaf820200, 0x3c030001, 0x8c636d84, 0x3c02f700, +0x621825, 0x3c020001, 0x8c426d90, 0x3c040001, +0x8c846dac, 0x3c050001, 0x8ca56f40, 0x431025, +0x441025, 0xaf820220, 0x24020005, 0x14a20006, +0x24020001, 0x8f820044, 0x2403afff, 0x431024, +0xaf820044, 0x24020001, 0x1000003d, 0xaf820238, +0x8f830050, 0x3c02f0ff, 0x3442ffff, 0x3c040001, +0x8c846f1c, 0x621824, 0x3c020a00, 0x621825, +0x24020001, 0xaf830050, 0xaf820200, 0x1080001e, +0xaf820220, 0x3c020001, 0x8c426e44, 0x1440001a, +0x3c033f00, 0x3c020001, 0x8c426d80, 0x1000001a, +0x346300e0, 0x8f830050, 0x3c040001, 0x8c846f1c, +0x3442ffff, 0x621824, 0x1080000f, 0xaf830050, +0x3c020001, 0x8c426e44, 0x1440000b, 0x3c043f00, +0x3c030001, 0x8c636d80, 0x348400e0, 0x24020001, +0xaf820200, 0xaf820220, 0x641825, 0xaf830200, +0x10000008, 0x3c05f700, 0x3c020001, 0x8c426d80, +0x3c033f00, 0x346300e2, 0x431025, 0xaf820200, +0x3c05f700, 0x34a58000, 0x3c030001, 0x8c636d84, +0x3c020001, 0x8c426d90, 0x3c040001, 0x8c846dac, +0x651825, 0x431025, 0x441025, 0xaf820220, +0x3e00008, 0x0, 0x3c030001, 0x8c636db4, +0x3c020001, 0x8c426db8, 0x10620003, 0x24020002, +0x3c010001, 0xac236db8, 0x1062001d, 0x2c620003, +0x10400025, 0x24020001, 0x14620023, 0x24020004, +0x3c030001, 0x8c636d98, 0x10620006, 0x24020008, +0x1462000c, 0x3c0200c8, 0x344201fb, 0x10000009, +0xaf820238, 0x24020e01, 0xaf820238, 0x8f820044, +0x3c03ffff, 0x34633f7f, 0x431024, 0x34420080, +0xaf820044, 0x8f830054, 0x24020002, 0x3c010001, +0xac226db4, 0x3c010001, 0x1000000b, 0xac236f2c, +0x8f830054, 0x3c020001, 0x8c426f2c, 0x2463d8f0, +0x431023, 0x2c422710, 0x14400003, 0x24020009, +0x3c010001, 0xac226db4, 0x3e00008, 0x0, +0x0, 0x0, 0x0, 0x27bdffd8, +0xafb20018, 0x809021, 0xafb3001c, 0xa09821, +0xafb10014, 0xc08821, 0xafb00010, 0x8021, +0xafbf0020, 0xa6200000, 0xc004d78, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc004d78, 0x2021, 0xc004d78, 0x24040001, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x24100010, 0x2501024, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fffa, +0x2501024, 0x24100010, 0x2701024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x2701024, 0xc004db9, 0x34108000, +0xc004db9, 0x0, 0xc004d58, 0x0, +0x50400005, 0x108042, 0x96220000, 0x501025, +0xa6220000, 0x108042, 0x1600fff7, 0x0, +0xc004db9, 0x0, 0x8fbf0020, 0x8fb3001c, +0x8fb20018, 0x8fb10014, 0x8fb00010, 0x3e00008, +0x27bd0028, 0x27bdffd8, 0xafb10014, 0x808821, +0xafb20018, 0xa09021, 0xafb3001c, 0xc09821, +0xafb00010, 0x8021, 0xafbf0020, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0x24100010, 0x2301024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x2301024, 0x24100010, 0x2501024, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x2501024, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x34108000, +0x96620000, 0x501024, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fff8, +0x0, 0xc004db9, 0x0, 0x8fbf0020, +0x8fb3001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, +0x3e00008, 0x27bd0028, 0x3c040001, 0x8c846dd0, +0x3c020001, 0x8c426e18, 0x27bdffd8, 0xafbf0020, +0xafb1001c, 0x10820003, 0xafb00018, 0x3c010001, +0xac246e18, 0x3c030001, 0x8c636f40, 0x24020005, +0x14620005, 0x2483ffff, 0xc004963, 0x0, +0x1000034c, 0x0, 0x2c620013, 0x10400349, +0x31080, 0x3c010001, 0x220821, 0x8c226b80, +0x400008, 0x0, 0xc004db9, 0x8021, +0x34028000, 0xa7a20010, 0x27b10010, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0xc004d78, +0x2021, 0x108042, 0x1600fffc, 0x0, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fff8, 0x0, 0xc004db9, 0x0, +0x1000030e, 0x24020002, 0x27b10010, 0xa7a00010, +0x8021, 0xc004d78, 0x24040001, 0x26100001, +0x2e020020, 0x1440fffb, 0x0, 0xc004d78, +0x2021, 0xc004d78, 0x24040001, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x24100010, +0x32020001, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020001, +0x24100010, 0xc004d78, 0x2021, 0x108042, +0x1600fffc, 0x0, 0xc004db9, 0x34108000, +0xc004db9, 0x0, 0xc004d58, 0x0, +0x50400005, 0x108042, 0x96220000, 0x501025, +0xa6220000, 0x108042, 0x1600fff7, 0x0, +0xc004db9, 0x0, 0x97a20010, 0x30428000, +0x144002dc, 0x24020003, 0x100002d8, 0x0, +0x24021200, 0xa7a20010, 0x27b10010, 0x8021, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0xc004d78, 0x2021, 0x108042, 0x1600fffc, +0x0, 0xc004d78, 0x24040001, 0xc004d78, +0x2021, 0x34108000, 0x96220000, 0x501024, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fff8, 0x0, 0xc004db9, +0x0, 0x8f830054, 0x10000296, 0x24020004, +0x8f830054, 0x3c020001, 0x8c426f3c, 0x2463ff9c, +0x431023, 0x2c420064, 0x1440029e, 0x24020002, +0x3c030001, 0x8c636f40, 0x10620297, 0x2c620003, +0x14400296, 0x24020011, 0x24020003, 0x10620005, +0x24020004, 0x10620291, 0x2402000f, 0x1000028f, +0x24020011, 0x1000028d, 0x24020005, 0x24020014, +0xa7a20010, 0x27b10010, 0x8021, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0x32020012, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020012, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x34108000, +0x96220000, 0x501024, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fff8, +0x0, 0xc004db9, 0x0, 0x8f830054, +0x10000248, 0x24020006, 0x8f830054, 0x3c020001, +0x8c426f3c, 0x2463ff9c, 0x431023, 0x2c420064, +0x14400250, 0x24020007, 0x1000024c, 0x0, +0x24020006, 0xa7a20010, 0x27b10010, 0x8021, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020013, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020013, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fff8, 0x0, 0xc004db9, 0x0, +0x8f830054, 0x10000207, 0x24020008, 0x8f830054, +0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, +0x2c420064, 0x1440020f, 0x24020009, 0x1000020b, +0x0, 0x27b10010, 0xa7a00010, 0x8021, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x24040001, +0xc004d78, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020018, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020018, +0xc004db9, 0x34108000, 0xc004db9, 0x0, +0xc004d58, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc004db9, 0x8021, +0x97a20010, 0x27b10010, 0x34420001, 0xa7a20010, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020018, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020018, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fff8, 0x0, 0xc004db9, 0x0, +0x8f830054, 0x10000193, 0x2402000a, 0x8f830054, +0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, +0x2c420064, 0x1440019b, 0x2402000b, 0x10000197, +0x0, 0x27b10010, 0xa7a00010, 0x8021, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x24040001, +0xc004d78, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020017, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020017, +0xc004db9, 0x34108000, 0xc004db9, 0x0, +0xc004d58, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc004db9, 0x8021, +0x97a20010, 0x27b10010, 0x34420700, 0xa7a20010, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020017, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020017, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fff8, 0x0, 0xc004db9, 0x0, +0x8f830054, 0x1000011f, 0x2402000c, 0x8f830054, +0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, +0x2c420064, 0x14400127, 0x24020012, 0x10000123, +0x0, 0x27b10010, 0xa7a00010, 0x8021, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x24040001, +0xc004d78, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020014, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020014, +0xc004db9, 0x34108000, 0xc004db9, 0x0, +0xc004d58, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc004db9, 0x8021, +0x97a20010, 0x27b10010, 0x34420010, 0xa7a20010, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020014, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020014, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fff8, 0x0, 0xc004db9, 0x0, +0x8f830054, 0x100000ab, 0x24020013, 0x8f830054, +0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, +0x2c420064, 0x144000b3, 0x2402000d, 0x100000af, +0x0, 0x27b10010, 0xa7a00010, 0x8021, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x24040001, +0xc004d78, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020018, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020018, +0xc004db9, 0x34108000, 0xc004db9, 0x0, +0xc004d58, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc004db9, 0x8021, +0x97a20010, 0x27b10010, 0x3042fffe, 0xa7a20010, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020018, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020018, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fff8, 0x0, 0xc004db9, 0x0, +0x8f830054, 0x10000037, 0x2402000e, 0x24020840, +0xa7a20010, 0x27b10010, 0x8021, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0x32020013, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020013, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x34108000, +0x96220000, 0x501024, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fff8, +0x0, 0xc004db9, 0x0, 0x8f830054, +0x24020010, 0x3c010001, 0xac226dd0, 0x3c010001, +0x1000000c, 0xac236f3c, 0x8f830054, 0x3c020001, +0x8c426f3c, 0x2463ff9c, 0x431023, 0x2c420064, +0x14400004, 0x0, 0x24020011, 0x3c010001, +0xac226dd0, 0x8fbf0020, 0x8fb1001c, 0x8fb00018, +0x3e00008, 0x27bd0028, 0x3c030001, 0x8c636d98, +0x27bdffc8, 0x24020002, 0xafbf0034, 0xafb20030, +0xafb1002c, 0x14620004, 0xafb00028, 0x3c120002, +0x10000003, 0x8e528ff8, 0x3c120002, 0x8e528ffc, +0x3c030001, 0x8c636dd4, 0x3c020001, 0x8c426e1c, +0x50620004, 0x2463ffff, 0x3c010001, 0xac236e1c, +0x2463ffff, 0x2c620006, 0x10400377, 0x31080, +0x3c010001, 0x220821, 0x8c226bd8, 0x400008, +0x0, 0x2021, 0x2821, 0xc004ddb, +0x34068000, 0x24040010, 0x24050002, 0x24060002, +0x24020002, 0xc004ddb, 0xa7a20018, 0x24020002, +0x3c010001, 0x10000364, 0xac226dd4, 0x27b10018, +0xa7a00018, 0x8021, 0xc004d78, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc004d78, 0x2021, 0xc004d78, 0x24040001, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x24100010, 0x32020001, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fffa, +0x32020001, 0x24100010, 0xc004d78, 0x2021, +0x108042, 0x1600fffc, 0x0, 0xc004db9, +0x34108000, 0xc004db9, 0x0, 0xc004d58, +0x0, 0x50400005, 0x108042, 0x96220000, +0x501025, 0xa6220000, 0x108042, 0x1600fff7, +0x0, 0xc004db9, 0x0, 0x97a20018, +0x30428000, 0x14400004, 0x24020003, 0x3c010001, +0xac226dd4, 0x24020003, 0x3c010001, 0x1000032a, +0xac226dd4, 0x24040010, 0x24050002, 0x24060002, +0x24020002, 0xc004ddb, 0xa7a20018, 0x3c030001, +0x8c636e20, 0x24020001, 0x146201e1, 0x8021, +0x27b10018, 0xa7a00018, 0xc004d78, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc004d78, 0x2021, 0xc004d78, 0x24040001, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x24100010, 0x32020001, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fffa, +0x32020001, 0x24100010, 0x32020018, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020018, 0xc004db9, 0x34108000, +0xc004db9, 0x0, 0xc004d58, 0x0, +0x50400005, 0x108042, 0x96220000, 0x501025, +0xa6220000, 0x108042, 0x1600fff7, 0x0, +0xc004db9, 0x8021, 0x27b10018, 0xa7a00018, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x24040001, +0xc004d78, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020018, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020018, +0xc004db9, 0x34108000, 0xc004db9, 0x0, +0xc004d58, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc004db9, 0x8021, +0x24040018, 0x2821, 0xc004ddb, 0x24060404, +0xa7a0001a, 0xc004d78, 0x24040001, 0x26100001, +0x2e020020, 0x1440fffb, 0x0, 0xc004d78, +0x2021, 0xc004d78, 0x24040001, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x24100010, +0x32020001, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020001, +0x24100010, 0x32020018, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fffa, +0x32020018, 0xc004db9, 0x34108000, 0xc004db9, +0x0, 0xc004d58, 0x0, 0x50400005, +0x108042, 0x97a2001a, 0x501025, 0xa7a2001a, +0x108042, 0x1600fff7, 0x0, 0xc004db9, +0x8021, 0xa7a0001a, 0xc004d78, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc004d78, 0x2021, 0xc004d78, 0x24040001, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x24100010, 0x32020001, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fffa, +0x32020001, 0x24100010, 0x32020018, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020018, 0xc004db9, 0x34108000, +0xc004db9, 0x0, 0xc004d58, 0x0, +0x50400005, 0x108042, 0x97a2001a, 0x501025, +0xa7a2001a, 0x108042, 0x1600fff7, 0x0, +0xc004db9, 0x8021, 0xa7a0001c, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x24040001, 0xc004d78, +0x2021, 0x24100010, 0xc004d78, 0x2021, +0x108042, 0x1600fffc, 0x0, 0x24100010, +0x3202001e, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x3202001e, +0xc004db9, 0x34108000, 0xc004db9, 0x0, +0xc004d58, 0x0, 0x50400005, 0x108042, +0x97a2001c, 0x501025, 0xa7a2001c, 0x108042, +0x1600fff7, 0x0, 0xc004db9, 0x8021, +0xa7a0001c, 0xc004d78, 0x24040001, 0x26100001, +0x2e020020, 0x1440fffb, 0x0, 0xc004d78, +0x2021, 0xc004d78, 0x24040001, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x24100010, +0xc004d78, 0x2021, 0x108042, 0x1600fffc, +0x0, 0x24100010, 0x3202001e, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x3202001e, 0xc004db9, 0x34108000, +0xc004db9, 0x0, 0xc004d58, 0x0, +0x50400005, 0x108042, 0x97a2001c, 0x501025, +0xa7a2001c, 0x108042, 0x1600fff7, 0x0, +0xc004db9, 0x8021, 0x24020002, 0xa7a2001e, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0x24100010, 0xc004d78, +0x2021, 0x108042, 0x1600fffc, 0x0, +0x24100010, 0x3202001e, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fffa, +0x3202001e, 0xc004d78, 0x24040001, 0xc004d78, +0x2021, 0x34108000, 0x97a2001e, 0x501024, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fff8, 0x0, 0xc004db9, +0x8021, 0xa7a00020, 0xc004d78, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc004d78, 0x2021, 0xc004d78, 0x24040001, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x24100010, 0xc004d78, 0x2021, 0x108042, +0x1600fffc, 0x0, 0x24100010, 0x3202001e, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x3202001e, 0xc004db9, +0x34108000, 0xc004db9, 0x0, 0xc004d58, +0x0, 0x50400005, 0x108042, 0x97a20020, +0x501025, 0xa7a20020, 0x108042, 0x1600fff7, +0x0, 0xc004db9, 0x8021, 0xa7a00020, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x24040001, +0xc004d78, 0x2021, 0x24100010, 0xc004d78, +0x2021, 0x108042, 0x1600fffc, 0x0, +0x24100010, 0x3202001e, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fffa, +0x3202001e, 0xc004db9, 0x34108000, 0xc004db9, +0x0, 0xc004d58, 0x0, 0x50400005, +0x108042, 0x97a20020, 0x501025, 0xa7a20020, +0x108042, 0x1600fff7, 0x0, 0xc004db9, +0x8021, 0xa7a00022, 0xc004d78, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc004d78, 0x2021, 0xc004d78, 0x24040001, +0xc004d78, 0x2021, 0xc004d78, 0x24040001, +0x24100010, 0xc004d78, 0x2021, 0x108042, +0x1600fffc, 0x0, 0x24100010, 0xc004d78, +0x2021, 0x108042, 0x1600fffc, 0x0, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x34108000, 0x97a20022, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fff8, 0x0, 0xc004db9, 0x0, +0x24040018, 0x24050002, 0xc004ddb, 0x24060004, +0x3c100001, 0x8e106e24, 0x24020001, 0x1602011d, +0x0, 0x3c020001, 0x94426f26, 0x3c010001, +0xac206e24, 0x24429fbc, 0x2c420004, 0x1040000c, +0x24040009, 0x24050001, 0xc004ddb, 0x24060400, +0x24040018, 0x24050001, 0xc004ddb, 0x24060020, +0x24040018, 0x24050001, 0xc004ddb, 0x24062000, +0x3c024000, 0x2421024, 0x10400123, 0x3c022000, +0x2421024, 0x10400004, 0x0, 0x3c010001, +0x10000003, 0xac306f1c, 0x3c010001, 0xac206f1c, +0x3c030001, 0x8c636f34, 0x24020005, 0x146200f9, +0x0, 0x3c020001, 0x8c426f1c, 0x10400067, +0x3c020004, 0x2421024, 0x10400011, 0xa7a00018, +0x3c020008, 0x2421024, 0x10400002, 0x24020200, +0xa7a20018, 0x3c020010, 0x2421024, 0x10400004, +0x0, 0x97a20018, 0x34420100, 0xa7a20018, +0x97a60018, 0x24040009, 0x10000004, 0x2821, +0x24040009, 0x2821, 0x3021, 0xc004ddb, +0x0, 0x24020001, 0xa7a2001a, 0x3c020008, +0x2421024, 0x1040000c, 0x3c020002, 0x2421024, +0x10400002, 0x24020101, 0xa7a2001a, 0x3c020001, +0x2421024, 0x10400005, 0x3c020010, 0x97a2001a, +0x34420040, 0xa7a2001a, 0x3c020010, 0x2421024, +0x1040000e, 0x3c020002, 0x2421024, 0x10400005, +0x3c020001, 0x97a2001a, 0x34420080, 0xa7a2001a, +0x3c020001, 0x2421024, 0x10400005, 0x3c0300a0, +0x97a2001a, 0x34420020, 0xa7a2001a, 0x3c0300a0, +0x2431024, 0x54430004, 0x3c020020, 0x97a2001a, +0x1000000c, 0x34420400, 0x2421024, 0x50400004, +0x3c020080, 0x97a2001a, 0x10000006, 0x34420800, +0x2421024, 0x10400004, 0x0, 0x97a2001a, +0x34420c00, 0xa7a2001a, 0x97a6001a, 0x24040004, +0xc004ddb, 0x2821, 0x3c020004, 0x2421024, +0x10400004, 0xa7a0001c, 0x32425000, 0x14400004, +0x0, 0x32424000, 0x10400005, 0x2021, +0xc004cf9, 0x2402021, 0x10000096, 0x0, +0x97a6001c, 0x2821, 0x34c61200, 0xc004ddb, +0xa7a6001c, 0x1000008f, 0x0, 0x2421024, +0x10400004, 0xa7a00018, 0x32425000, 0x14400004, +0x0, 0x32424000, 0x10400005, 0x3c020010, +0xc004cf9, 0x2402021, 0x10000019, 0xa7a0001a, +0x2421024, 0x10400004, 0x0, 0x97a20018, +0x10000004, 0xa7a20018, 0x97a20018, 0x34420100, +0xa7a20018, 0x3c020001, 0x2421024, 0x10400004, +0x0, 0x97a20018, 0x10000004, 0xa7a20018, +0x97a20018, 0x34422000, 0xa7a20018, 0x97a60018, +0x2021, 0xc004ddb, 0x2821, 0xa7a0001a, +0x8021, 0xc004d78, 0x24040001, 0x26100001, +0x2e020020, 0x1440fffb, 0x0, 0xc004d78, +0x2021, 0xc004d78, 0x24040001, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x24100010, +0x32020001, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020001, +0x24100010, 0xc004d78, 0x2021, 0x108042, +0x1600fffc, 0x0, 0xc004db9, 0x34108000, +0xc004db9, 0x0, 0xc004d58, 0x0, +0x50400005, 0x108042, 0x97a2001a, 0x501025, +0xa7a2001a, 0x108042, 0x1600fff7, 0x0, +0xc004db9, 0x8021, 0xa7a0001a, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x24040001, 0xc004d78, +0x2021, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0xc004d78, +0x2021, 0x108042, 0x1600fffc, 0x0, +0xc004db9, 0x34108000, 0xc004db9, 0x0, +0xc004d58, 0x0, 0x50400005, 0x108042, +0x97a2001a, 0x501025, 0xa7a2001a, 0x108042, +0x1600fff7, 0x0, 0xc004db9, 0x0, +0x3c040001, 0x24846bcc, 0x97a60018, 0x97a7001a, +0x3c020001, 0x8c426d98, 0x3c030001, 0x8c636f1c, +0x3c05000d, 0x34a50205, 0xafa20010, 0xc002b3b, +0xafa30014, 0x8f830054, 0x24020004, 0x3c010001, +0xac226dd4, 0x3c010001, 0x10000017, 0xac236f38, +0x8f830054, 0x3c020001, 0x8c426f38, 0x2463ff9c, +0x431023, 0x2c420064, 0x1440000f, 0x0, +0x8f820220, 0x24030005, 0x3c010001, 0xac236dd4, +0x3c03f700, 0x431025, 0x10000007, 0xaf820220, +0x24020006, 0x3c010001, 0xac226dd4, 0x24020011, +0x3c010001, 0xac226dd0, 0x8fbf0034, 0x8fb20030, +0x8fb1002c, 0x8fb00028, 0x3e00008, 0x27bd0038, +0x27bdffd8, 0xafb00018, 0x808021, 0xafb1001c, +0x8821, 0x32024000, 0x10400013, 0xafbf0020, +0x3c020010, 0x2021024, 0x2c420001, 0x21023, +0x30434100, 0x3c020001, 0x2021024, 0x14400006, +0x34714000, 0x3c020002, 0x2021024, 0x14400002, +0x34716000, 0x34714040, 0x2021, 0x2821, +0x10000036, 0x2203021, 0x32021000, 0x10400035, +0x2021, 0x2821, 0xc004ddb, 0x24060040, +0x24040018, 0x2821, 0xc004ddb, 0x24060c00, +0x24040017, 0x2821, 0xc004ddb, 0x24060400, +0x24040016, 0x2821, 0xc004ddb, 0x24060006, +0x24040017, 0x2821, 0xc004ddb, 0x24062500, +0x24040016, 0x2821, 0xc004ddb, 0x24060006, +0x24040017, 0x2821, 0xc004ddb, 0x24064600, +0x24040016, 0x2821, 0xc004ddb, 0x24060006, +0x24040017, 0x2821, 0xc004ddb, 0x24066700, +0x24040016, 0x2821, 0xc004ddb, 0x24060006, +0x2404001f, 0x2821, 0xc004ddb, 0x24060010, +0x24040009, 0x2821, 0xc004ddb, 0x24061500, +0x24040009, 0x2821, 0x24061d00, 0xc004ddb, +0x0, 0x3c040001, 0x24846bf0, 0x3c05000e, +0x34a50100, 0x2003021, 0x2203821, 0xafa00010, +0xc002b3b, 0xafa00014, 0x8fbf0020, 0x8fb1001c, +0x8fb00018, 0x3e00008, 0x27bd0028, 0x8f850044, +0x8f820044, 0x3c030001, 0x431025, 0x3c030008, +0xaf820044, 0x8f840054, 0x8f820054, 0xa32824, +0x10000002, 0x24840001, 0x8f820054, 0x821023, +0x2c420002, 0x1440fffc, 0x0, 0x8f820044, +0x3c03fffe, 0x3463ffff, 0x431024, 0xaf820044, +0x8f830054, 0x8f820054, 0x10000002, 0x24630001, +0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, +0x0, 0x3e00008, 0xa01021, 0x8f830044, +0x3c02fff0, 0x3442ffff, 0x42480, 0x621824, +0x3c020002, 0x822025, 0x641825, 0xaf830044, +0x8f820044, 0x3c03fffe, 0x3463ffff, 0x431024, +0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, +0x24630001, 0x8f820054, 0x621023, 0x2c420002, +0x1440fffc, 0x0, 0x8f820044, 0x3c030001, +0x431025, 0xaf820044, 0x8f830054, 0x8f820054, +0x10000002, 0x24630001, 0x8f820054, 0x621023, +0x2c420002, 0x1440fffc, 0x0, 0x3e00008, +0x0, 0x8f820044, 0x2403ff7f, 0x431024, +0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, +0x24630001, 0x8f820054, 0x621023, 0x2c420002, +0x1440fffc, 0x0, 0x8f820044, 0x34420080, +0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, +0x24630001, 0x8f820054, 0x621023, 0x2c420002, +0x1440fffc, 0x0, 0x3e00008, 0x0, +0x8f820044, 0x3c03fff0, 0x3463ffff, 0x431024, +0xaf820044, 0x8f820044, 0x3c030001, 0x431025, +0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, +0x24630001, 0x8f820054, 0x621023, 0x2c420002, +0x1440fffc, 0x0, 0x8f820044, 0x3c03fffe, +0x3463ffff, 0x431024, 0xaf820044, 0x8f830054, +0x8f820054, 0x10000002, 0x24630001, 0x8f820054, +0x621023, 0x2c420002, 0x1440fffc, 0x0, +0x3e00008, 0x0, 0x27bdffc8, 0xafb30024, +0x809821, 0xafbe002c, 0xa0f021, 0xafb20020, +0xc09021, 0x33c2ffff, 0xafbf0030, 0xafb50028, +0xafb1001c, 0xafb00018, 0x14400034, 0xa7b20010, +0x3271ffff, 0x27b20010, 0x8021, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0x2301024, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x2301024, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x34108000, +0x96420000, 0x501024, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x12000075, +0x0, 0x1000fff6, 0x0, 0x3275ffff, +0x27b10010, 0xa7a00010, 0x8021, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x24040001, 0xc004d78, +0x2021, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0x2b01024, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x2b01024, 0xc004db9, +0x34108000, 0xc004db9, 0x0, 0xc004d58, +0x0, 0x50400005, 0x108042, 0x96220000, +0x501025, 0xa6220000, 0x108042, 0x1600fff7, +0x0, 0xc004db9, 0x0, 0x33c5ffff, +0x24020001, 0x54a20004, 0x24020002, 0x97a20010, +0x10000006, 0x521025, 0x14a20006, 0x3271ffff, +0x97a20010, 0x121827, 0x431024, 0xa7a20010, +0x3271ffff, 0x27b20010, 0x8021, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0x2301024, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x2301024, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x34108000, +0x96420000, 0x501024, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fff8, +0x0, 0xc004db9, 0x0, 0x8fbf0030, +0x8fbe002c, 0x8fb50028, 0x8fb30024, 0x8fb20020, +0x8fb1001c, 0x8fb00018, 0x3e00008, 0x27bd0038, +0x0, 0x0, 0x0, 0x27bdffe8, +0xafbf0010, 0x8ee304b8, 0x24020008, 0x146201e0, +0x0, 0x3c020001, 0x8c426f1c, 0x14400005, +0x0, 0xc003daf, 0x8f840224, 0x100001d8, +0x0, 0x8f820220, 0x3c030008, 0x431024, +0x10400026, 0x24020001, 0x8f840224, 0x8f820220, +0x3c030400, 0x431024, 0x10400006, 0x0, +0x3c010002, 0xac208fa0, 0x3c010002, 0x1000000b, +0xac208fc0, 0x3c030002, 0x24638fa0, 0x8c620000, +0x24420001, 0xac620000, 0x2c420002, 0x14400003, +0x24020001, 0x3c010002, 0xac228fc0, 0x3c020002, +0x8c428fc0, 0x10400006, 0x30820040, 0x10400004, +0x24020001, 0x3c010002, 0x10000003, 0xac228fc4, +0x3c010002, 0xac208fc4, 0x3c010002, 0xac248f9c, +0x3c010002, 0x1000000b, 0xac208fd0, 0x3c010002, +0xac228fd0, 0x3c010002, 0xac208fc0, 0x3c010002, +0xac208fa0, 0x3c010002, 0xac208fc4, 0x3c010002, +0xac208f9c, 0x3c030002, 0x8c638f90, 0x3c020002, +0x8c428f94, 0x50620004, 0x2463ffff, 0x3c010002, +0xac238f94, 0x2463ffff, 0x2c62000e, 0x10400194, +0x31080, 0x3c010001, 0x220821, 0x8c226c00, +0x400008, 0x0, 0x24020002, 0x3c010002, +0xac208fc0, 0x3c010002, 0xac208fa0, 0x3c010002, +0xac208f9c, 0x3c010002, 0xac208fc4, 0x3c010002, +0xac208fb8, 0x3c010002, 0xac208fb0, 0xaf800224, +0x3c010002, 0xac228f90, 0x3c020002, 0x8c428fd0, +0x1440004f, 0x3c02fdff, 0x3442ffff, 0xc003daf, +0x282a024, 0xaf800204, 0x8f820200, 0x2403fffd, +0x431024, 0xaf820200, 0x3c010002, 0xac208fe0, +0x8f830054, 0x3c020002, 0x8c428fb8, 0x24040001, +0x3c010002, 0xac248fcc, 0x24420001, 0x3c010002, +0xac228fb8, 0x2c420004, 0x3c010002, 0xac238fb4, +0x14400006, 0x24020003, 0x3c010001, 0xac246d9c, +0x3c010002, 0x1000015e, 0xac208fb8, 0x3c010002, +0x1000015b, 0xac228f90, 0x8f830054, 0x3c020002, +0x8c428fb4, 0x2463d8f0, 0x431023, 0x2c422710, +0x14400003, 0x24020004, 0x3c010002, 0xac228f90, +0x3c020002, 0x8c428fd0, 0x14400021, 0x3c02fdff, +0x3442ffff, 0x1000014a, 0x282a024, 0x3c040001, +0x8c846f20, 0x3c010002, 0xc005084, 0xac208fa8, +0x3c020002, 0x8c428fdc, 0xaf820204, 0x3c020002, +0x8c428fd0, 0x14400012, 0x3c03fdff, 0x8f820204, +0x3463ffff, 0x30420030, 0x1440012f, 0x283a024, +0x3c030002, 0x8c638fdc, 0x24020005, 0x3c010002, +0xac228f90, 0x3c010002, 0x10000131, 0xac238fe0, +0x3c020002, 0x8c428fd0, 0x10400010, 0x3c02fdff, +0x3c020001, 0x8c426e3c, 0x24420001, 0x3c010001, +0xac226e3c, 0x2c420002, 0x14400125, 0x24020001, +0x3c010001, 0xac226e44, 0x3c010001, 0xac206e3c, +0x3c010001, 0x1000011e, 0xac226d9c, 0x3c030002, +0x8c638fc0, 0x3442ffff, 0x10600119, 0x282a024, +0x3c020002, 0x8c428f9c, 0x10400115, 0x0, +0x3c010002, 0xac228fc8, 0x24020003, 0x3c010002, +0xac228fa0, 0x100000b8, 0x24020006, 0x3c010002, +0xac208fa8, 0x8f820204, 0x34420040, 0xaf820204, +0x3c020002, 0x8c428fe0, 0x24030007, 0x3c010002, +0xac238f90, 0x34420040, 0x3c010002, 0xac228fe0, +0x3c020002, 0x8c428fc0, 0x10400005, 0x0, +0x3c020002, 0x8c428f9c, 0x104000f0, 0x24020002, +0x3c050002, 0x24a58fa0, 0x8ca20000, 0x2c424e21, +0x104000ea, 0x24020002, 0x3c020002, 0x8c428fc4, +0x104000ef, 0x2404ffbf, 0x3c020002, 0x8c428f9c, +0x3c030002, 0x8c638fc8, 0x441024, 0x641824, +0x10430004, 0x24020001, 0x3c010002, 0x100000e4, +0xac228f90, 0x24020003, 0xaca20000, 0x24020008, +0x3c010002, 0xac228f90, 0x3c020002, 0x8c428fcc, +0x1040000c, 0x24020001, 0x3c040002, 0xc005091, +0x8c848f9c, 0x3c020002, 0x8c428fe8, 0x14400005, +0x24020001, 0x3c020002, 0x8c428fe4, 0x10400006, +0x24020001, 0x3c010001, 0xac226d9c, 0x3c010002, +0x100000cb, 0xac208fb8, 0x3c020002, 0x8c428fb0, +0x3c030002, 0x8c638f9c, 0x2c420001, 0x210c0, +0x30630008, 0x3c010002, 0xac228fb0, 0x3c010002, +0xac238fac, 0x8f830054, 0x24020009, 0x3c010002, +0xac228f90, 0x3c010002, 0x100000b9, 0xac238fb4, +0x8f830054, 0x3c020002, 0x8c428fb4, 0x2463d8f0, +0x431023, 0x2c422710, 0x1440009f, 0x0, +0x3c020002, 0x8c428fc0, 0x10400005, 0x0, +0x3c020002, 0x8c428f9c, 0x104000a0, 0x24020002, +0x3c030002, 0x24638fa0, 0x8c620000, 0x2c424e21, +0x1040009a, 0x24020002, 0x3c020002, 0x8c428fcc, +0x1040000e, 0x0, 0x3c020002, 0x8c428f9c, +0x3c010002, 0xac208fcc, 0x30420080, 0x1040002f, +0x2402000c, 0x8f820204, 0x30420080, 0x1440000c, +0x24020003, 0x10000029, 0x2402000c, 0x3c020002, +0x8c428f9c, 0x30420080, 0x14400005, 0x24020003, +0x8f820204, 0x30420080, 0x1040001f, 0x24020003, +0xac620000, 0x2402000a, 0x3c010002, 0xac228f90, +0x3c040002, 0x24848fd8, 0x8c820000, 0x3c030002, +0x8c638fb0, 0x431025, 0xaf820204, 0x8c830000, +0x3c040002, 0x8c848fb0, 0x2402000b, 0x3c010002, +0xac228f90, 0x641825, 0x3c010002, 0xac238fe0, +0x3c050002, 0x24a58fa0, 0x8ca20000, 0x2c424e21, +0x10400066, 0x24020002, 0x3c020002, 0x8c428fd0, +0x10400005, 0x0, 0x2402000c, 0x3c010002, +0x10000067, 0xac228f90, 0x3c020002, 0x8c428fc0, +0x10400063, 0x0, 0x3c040002, 0x8c848f9c, +0x10800055, 0x30820008, 0x3c030002, 0x8c638fac, +0x1062005b, 0x24020003, 0x3c010002, 0xac248fc8, +0xaca20000, 0x24020006, 0x3c010002, 0x10000054, +0xac228f90, 0x8f820200, 0x34420002, 0xaf820200, +0x8f830054, 0x2402000d, 0x3c010002, 0xac228f90, +0x3c010002, 0xac238fb4, 0x8f830054, 0x3c020002, +0x8c428fb4, 0x2463d8f0, 0x431023, 0x2c422710, +0x14400031, 0x0, 0x3c020002, 0x8c428fd0, +0x10400020, 0x2402000e, 0x3c030002, 0x8c638fe4, +0x3c010002, 0x14600015, 0xac228f90, 0xc003e6d, +0x0, 0x3c050001, 0x8ca56d98, 0xc00529b, +0x2021, 0x3c030001, 0x8c636d98, 0x24020004, +0x14620005, 0x2403fffb, 0x3c020001, 0x8c426d94, +0x10000003, 0x2403fff7, 0x3c020001, 0x8c426d94, +0x431024, 0x3c010001, 0xac226d94, 0x8f830224, +0x3c020200, 0x3c010002, 0xac238fec, 0x10000020, +0x282a025, 0x3c020002, 0x8c428fc0, 0x10400005, +0x0, 0x3c020002, 0x8c428f9c, 0x1040000f, +0x24020002, 0x3c020002, 0x8c428fa0, 0x2c424e21, +0x1040000a, 0x24020002, 0x3c020002, 0x8c428fc0, +0x1040000f, 0x0, 0x3c020002, 0x8c428f9c, +0x1440000b, 0x0, 0x24020002, 0x3c010002, +0x10000007, 0xac228f90, 0x3c020002, 0x8c428fc0, +0x10400003, 0x0, 0xc003daf, 0x0, +0x8f820220, 0x3c03f700, 0x431025, 0xaf820220, +0x8fbf0010, 0x3e00008, 0x27bd0018, 0x3c030002, +0x24638fe8, 0x8c620000, 0x10400005, 0x34422000, +0x3c010002, 0xac228fdc, 0x10000003, 0xac600000, +0x3c010002, 0xac248fdc, 0x3e00008, 0x0, +0x27bdffe0, 0x30820030, 0xafbf0018, 0x3c010002, +0xac228fe4, 0x14400067, 0x3c02ffff, 0x34421f0e, +0x821024, 0x14400061, 0x24020030, 0x30822000, +0x1040005d, 0x30838000, 0x31a02, 0x30820001, +0x21200, 0x3c040001, 0x8c846f20, 0x621825, +0x331c2, 0x3c030001, 0x24636e48, 0x30828000, +0x21202, 0x30840001, 0x42200, 0x441025, +0x239c2, 0x61080, 0x431021, 0x471021, +0x90430000, 0x24020001, 0x10620025, 0x0, +0x10600007, 0x24020002, 0x10620013, 0x24020003, +0x1062002c, 0x3c05000f, 0x10000037, 0x0, +0x8f820200, 0x2403feff, 0x431024, 0xaf820200, +0x8f820220, 0x3c03fffe, 0x3463ffff, 0x431024, +0xaf820220, 0x3c010002, 0xac209004, 0x3c010002, +0x10000034, 0xac20900c, 0x8f820200, 0x34420100, +0xaf820200, 0x8f820220, 0x3c03fffe, 0x3463ffff, +0x431024, 0xaf820220, 0x24020100, 0x3c010002, +0xac229004, 0x3c010002, 0x10000026, 0xac20900c, +0x8f820200, 0x2403feff, 0x431024, 0xaf820200, +0x8f820220, 0x3c030001, 0x431025, 0xaf820220, +0x3c010002, 0xac209004, 0x3c010002, 0x10000019, +0xac23900c, 0x8f820200, 0x34420100, 0xaf820200, +0x8f820220, 0x3c030001, 0x431025, 0xaf820220, +0x24020100, 0x3c010002, 0xac229004, 0x3c010002, +0x1000000c, 0xac23900c, 0x34a5ffff, 0x3c040001, +0x24846c38, 0xafa30010, 0xc002b3b, 0xafa00014, +0x10000004, 0x0, 0x24020030, 0x3c010002, +0xac228fe8, 0x8fbf0018, 0x3e00008, 0x27bd0020, +0x0, 0x0, 0x0, 0x27bdffc8, +0xafb20028, 0x809021, 0xafb3002c, 0xa09821, +0xafb00020, 0xc08021, 0x3c040001, 0x24846c50, +0x3c050009, 0x3c020001, 0x8c426d98, 0x34a59001, +0x2403021, 0x2603821, 0xafbf0030, 0xafb10024, +0xa7a0001a, 0xafb00014, 0xc002b3b, 0xafa20010, +0x24020002, 0x12620083, 0x2e620003, 0x10400005, +0x24020001, 0x1262000a, 0x0, 0x10000173, +0x0, 0x24020004, 0x126200f8, 0x24020008, +0x126200f7, 0x3c02ffec, 0x1000016c, 0x0, +0x3c020001, 0x8c426d94, 0x30420002, 0x14400004, +0x128940, 0x3c02fffb, 0x3442ffff, 0x2028024, +0x3c010002, 0x310821, 0xac308ffc, 0x3c024000, +0x2021024, 0x1040004e, 0x1023c2, 0x30840030, +0x101382, 0x3042001c, 0x3c030001, 0x24636dd8, +0x431021, 0x823821, 0x3c020020, 0x2021024, +0x10400006, 0x24020100, 0x3c010002, 0x310821, +0xac229000, 0x10000005, 0x3c020080, 0x3c010002, +0x310821, 0xac209000, 0x3c020080, 0x2021024, +0x10400006, 0x121940, 0x3c020001, 0x3c010002, +0x230821, 0x10000005, 0xac229008, 0x121140, +0x3c010002, 0x220821, 0xac209008, 0x94e40000, +0x3c030001, 0x8c636f40, 0x24020005, 0x10620010, +0xa7a40018, 0x32024000, 0x10400002, 0x34824000, +0xa7a20018, 0x24040001, 0x94e20002, 0x24050004, +0x24e60002, 0x34420001, 0xc0045be, 0xa4e20002, +0x24040001, 0x2821, 0xc0045be, 0x27a60018, +0x3c020001, 0x8c426d98, 0x24110001, 0x3c010001, +0xac316da4, 0x14530004, 0x32028000, 0xc003daf, +0x0, 0x32028000, 0x1040011c, 0x0, +0xc003daf, 0x0, 0x3c030001, 0x8c636f40, +0x24020005, 0x10620115, 0x24020002, 0x3c010001, +0xac316d9c, 0x3c010001, 0x10000110, 0xac226d98, +0x24040001, 0x24050004, 0x27b0001a, 0xc0045be, +0x2003021, 0x24040001, 0x2821, 0xc0045be, +0x2003021, 0x3c020002, 0x511021, 0x8c428ff4, +0x3c040001, 0x8c846d98, 0x3c03bfff, 0x3463ffff, +0x3c010001, 0xac336da4, 0x431024, 0x3c010002, +0x310821, 0x109300f7, 0xac228ff4, 0x100000f7, +0x0, 0x3c022000, 0x2021024, 0x10400005, +0x24020001, 0x3c010001, 0xac226f1c, 0x10000004, +0x128940, 0x3c010001, 0xac206f1c, 0x128940, +0x3c010002, 0x310821, 0xac308ff8, 0x3c024000, +0x2021024, 0x14400014, 0x0, 0x3c020001, +0x8c426f1c, 0x10400006, 0x24040004, 0x24050001, +0xc004ddb, 0x24062000, 0x24020001, 0xaee204b8, +0x3c020002, 0x511021, 0x8c428ff0, 0x3c03bfff, +0x3463ffff, 0x431024, 0x3c010002, 0x310821, +0x100000d0, 0xac228ff0, 0x3c020001, 0x8c426f1c, +0x10400028, 0x3c0300a0, 0x2031024, 0x5443000d, +0x3c020020, 0x3c020001, 0x8c426f20, 0x24030100, +0x3c010002, 0x310821, 0xac239004, 0x3c030001, +0x3c010002, 0x310821, 0xac23900c, 0x10000015, +0x34420400, 0x2021024, 0x10400008, 0x24030100, +0x3c020001, 0x8c426f20, 0x3c010002, 0x310821, +0xac239004, 0x1000000b, 0x34420800, 0x3c020080, +0x2021024, 0x1040002e, 0x3c030001, 0x3c020001, +0x8c426f20, 0x3c010002, 0x310821, 0xac23900c, +0x34420c00, 0x3c010001, 0xac226f20, 0x10000025, +0x24040001, 0x3c020020, 0x2021024, 0x10400006, +0x24020100, 0x3c010002, 0x310821, 0xac229004, +0x10000005, 0x3c020080, 0x3c010002, 0x310821, +0xac209004, 0x3c020080, 0x2021024, 0x10400007, +0x121940, 0x3c020001, 0x3c010002, 0x230821, +0xac22900c, 0x10000006, 0x24040001, 0x121140, +0x3c010002, 0x220821, 0xac20900c, 0x24040001, +0x2821, 0x27b0001e, 0xc00457c, 0x2003021, +0x24040001, 0x2821, 0xc00457c, 0x2003021, +0x24040001, 0x24050001, 0x27b0001c, 0xc00457c, +0x2003021, 0x24040001, 0x24050001, 0xc00457c, +0x2003021, 0x10000077, 0x0, 0x3c02ffec, +0x3442ffff, 0x2028024, 0x3c020008, 0x2028025, +0x121140, 0x3c010002, 0x220821, 0xac308ff8, +0x3c022000, 0x2021024, 0x10400009, 0x0, +0x3c020001, 0x8c426e44, 0x14400005, 0x24020001, +0x3c010001, 0xac226f1c, 0x10000004, 0x3c024000, +0x3c010001, 0xac206f1c, 0x3c024000, 0x2021024, +0x1440001d, 0x24020e01, 0x3c030001, 0x8c636f1c, +0xaf820238, 0x3c010001, 0xac206db0, 0x10600005, +0x24022020, 0x3c010001, 0xac226f20, 0x24020001, +0xaee204b8, 0x3c04bfff, 0x121940, 0x3c020002, +0x431021, 0x8c428ff0, 0x3c050001, 0x8ca56d98, +0x3484ffff, 0x441024, 0x3c010002, 0x230821, +0xac228ff0, 0x24020001, 0x10a20044, 0x0, +0x10000040, 0x0, 0x3c020001, 0x8c426f1c, +0x1040001c, 0x24022000, 0x3c010001, 0xac226f20, +0x3c0300a0, 0x2031024, 0x14430005, 0x121140, +0x3402a000, 0x3c010001, 0x1000002d, 0xac226f20, +0x3c030002, 0x621821, 0x8c638ff8, 0x3c020020, +0x621024, 0x10400004, 0x24022001, 0x3c010001, +0x10000023, 0xac226f20, 0x3c020080, 0x621024, +0x1040001f, 0x3402a001, 0x3c010001, 0x1000001c, +0xac226f20, 0x3c020020, 0x2021024, 0x10400007, +0x121940, 0x24020100, 0x3c010002, 0x230821, +0xac229004, 0x10000006, 0x3c020080, 0x121140, +0x3c010002, 0x220821, 0xac209004, 0x3c020080, +0x2021024, 0x10400006, 0x121940, 0x3c020001, +0x3c010002, 0x230821, 0x10000005, 0xac22900c, +0x121140, 0x3c010002, 0x220821, 0xac20900c, +0x3c030001, 0x8c636d98, 0x24020001, 0x10620003, +0x0, 0xc003daf, 0x0, 0x8fbf0030, +0x8fb3002c, 0x8fb20028, 0x8fb10024, 0x8fb00020, +0x3e00008, 0x27bd0038, 0x27bdffb0, 0xafb3003c, +0x9821, 0xafb50040, 0xa821, 0xafb10034, +0x8821, 0x24020002, 0xafbf0048, 0xafbe0044, +0xafb20038, 0xafb00030, 0xafa4002c, 0xa7a0001a, +0xa7a00018, 0xa7a00020, 0xa7a0001e, 0xa7a00022, +0x10a20130, 0xa7a0001c, 0x2ca20003, 0x10400005, +0x24020001, 0x10a2000a, 0x3c024000, 0x1000025d, +0x2201021, 0x24020004, 0x10a2020a, 0x24020008, +0x10a20208, 0x2201021, 0x10000256, 0x0, +0x8fa8002c, 0x88140, 0x3c030002, 0x701821, +0x8c638ffc, 0x621024, 0x14400009, 0x24040001, +0x3c027fff, 0x3442ffff, 0x628824, 0x3c010002, +0x300821, 0xac318ff4, 0x10000246, 0x2201021, +0x24050001, 0xc00457c, 0x27a60018, 0x24040001, +0x24050001, 0xc00457c, 0x27a60018, 0x97a20018, +0x30420004, 0x104000d9, 0x3c114000, 0x3c020001, +0x8c426f40, 0x2443ffff, 0x2c620006, 0x104000d9, +0x31080, 0x3c010001, 0x220821, 0x8c226c68, +0x400008, 0x0, 0x24040001, 0x24050011, +0x27b0001a, 0xc00457c, 0x2003021, 0x24040001, +0x24050011, 0xc00457c, 0x2003021, 0x97a3001a, +0x30624000, 0x10400002, 0x3c150010, 0x3c150008, +0x30628000, 0x104000aa, 0x3c130001, 0x100000a8, +0x3c130002, 0x24040001, 0x24050014, 0x27b0001a, +0xc00457c, 0x2003021, 0x24040001, 0x24050014, +0xc00457c, 0x2003021, 0x97a3001a, 0x30621000, +0x10400002, 0x3c150010, 0x3c150008, 0x30620800, +0x10400097, 0x3c130001, 0x10000095, 0x3c130002, +0x24040001, 0x24050019, 0x27b0001c, 0xc00457c, +0x2003021, 0x24040001, 0x24050019, 0xc00457c, +0x2003021, 0x97a2001c, 0x30430700, 0x24020400, +0x10620027, 0x28620401, 0x1040000e, 0x24020200, +0x1062001f, 0x28620201, 0x10400005, 0x24020100, +0x5062001e, 0x3c130001, 0x1000001e, 0x24040001, +0x24020300, 0x50620019, 0x3c130002, 0x10000019, +0x24040001, 0x24020600, 0x1062000d, 0x28620601, +0x10400005, 0x24020500, 0x5062000b, 0x3c130002, +0x10000010, 0x24040001, 0x24020700, 0x1462000d, +0x24040001, 0x3c130004, 0x1000000a, 0x3c150008, +0x10000006, 0x3c130004, 0x10000005, 0x3c150008, +0x3c130001, 0x10000002, 0x3c150008, 0x3c150010, +0x24040001, 0x24050018, 0x27b0001e, 0xc00457c, +0x2003021, 0x24040001, 0x24050018, 0xc00457c, +0x2003021, 0x8fa8002c, 0x97a7001e, 0x81140, +0x3c060002, 0xc23021, 0x8cc68ff4, 0x97a20022, +0x3c100001, 0x26106c5c, 0x2002021, 0xafa20010, +0x97a2001c, 0x3c05000c, 0x34a50303, 0xc002b3b, +0xafa20014, 0x3c020004, 0x16620010, 0x3c020001, +0x8f840054, 0x24030001, 0x24020002, 0x3c010001, +0xac236d9c, 0x3c010001, 0xac226d98, 0x3c010001, +0xac236da4, 0x3c010001, 0xac236e24, 0x3c010001, +0xac246f30, 0x1000004f, 0x2b38825, 0x16620039, +0x3c028000, 0x3c020001, 0x8c426e20, 0x1440001e, +0x24040018, 0x2021, 0x2821, 0xc004ddb, +0x34068000, 0x8f830054, 0x8f820054, 0x2b38825, +0x10000002, 0x24630032, 0x8f820054, 0x621023, +0x2c420033, 0x1440fffc, 0x0, 0x8f830054, +0x24020001, 0x3c010001, 0xac226e20, 0x3c010001, +0xac226d9c, 0x3c010001, 0xac226d98, 0x3c010001, +0xac226da4, 0x3c010001, 0xac226e24, 0x3c010001, +0x1000002c, 0xac236f30, 0x2821, 0xc004ddb, +0x24060404, 0x2021, 0x2405001e, 0x27a60018, +0x24020002, 0xc0045be, 0xa7a20018, 0x2021, +0x2821, 0x27a60018, 0xc0045be, 0xa7a00018, +0x24040018, 0x24050002, 0xc004ddb, 0x24060004, +0x3c028000, 0x2221025, 0x2b31825, 0x10000015, +0x438825, 0x2221025, 0x2751825, 0x438825, +0x2002021, 0x97a6001c, 0x3c070001, 0x8ce76d98, +0x3c05000c, 0x34a50326, 0xafb30010, 0xc002b3b, +0xafb10014, 0x10000007, 0x0, 0x3c110002, +0x2308821, 0x8e318ffc, 0x3c027fff, 0x3442ffff, +0x2228824, 0x3c020001, 0x8c426da8, 0x1040001e, +0x0, 0x3c020001, 0x8c426f1c, 0x10400002, +0x3c022000, 0x2228825, 0x8fa8002c, 0x81140, +0x3c010002, 0x220821, 0x8c229000, 0x10400003, +0x3c020020, 0x10000005, 0x2228825, 0x3c02ffdf, +0x3442ffff, 0x2228824, 0x8fa8002c, 0x81140, +0x3c010002, 0x220821, 0x8c229008, 0x10400003, +0x3c020080, 0x10000004, 0x2228825, 0x3c02ff7f, +0x3442ffff, 0x2228824, 0x8fa8002c, 0x81140, +0x3c010002, 0x220821, 0xac318ff4, 0x10000135, +0x2201021, 0x8fa8002c, 0x8f140, 0x3c030002, +0x7e1821, 0x8c638ff8, 0x3c024000, 0x621024, +0x14400009, 0x24040001, 0x3c027fff, 0x3442ffff, +0x628824, 0x3c010002, 0x3e0821, 0xac318ff0, +0x10000124, 0x2201021, 0x2821, 0xc00457c, +0x27a60018, 0x24040001, 0x2821, 0xc00457c, +0x27a60018, 0x24040001, 0x24050001, 0x27b20020, +0xc00457c, 0x2403021, 0x24040001, 0x24050001, +0xc00457c, 0x2403021, 0x24040001, 0x24050004, +0x27b1001e, 0xc00457c, 0x2203021, 0x24040001, +0x24050004, 0xc00457c, 0x2203021, 0x24040001, +0x24050005, 0x27b00022, 0xc00457c, 0x2003021, +0x24040001, 0x24050005, 0xc00457c, 0x2003021, +0x24040001, 0x24050010, 0xc00457c, 0x27a60018, +0x24040001, 0x24050010, 0xc00457c, 0x27a60018, +0x24040001, 0x2405000a, 0xc00457c, 0x2403021, +0x24040001, 0x2405000a, 0xc00457c, 0x2403021, +0x24040001, 0x24050018, 0xc00457c, 0x2203021, +0x24040001, 0x24050018, 0xc00457c, 0x2203021, +0x24040001, 0x24050001, 0xc00457c, 0x27a60018, +0x24040001, 0x24050001, 0xc00457c, 0x27a60018, +0x97a20018, 0x30420004, 0x10400066, 0x3c114000, +0x3c030001, 0x8c636f34, 0x24020005, 0x14620067, +0x24040001, 0x24050019, 0x27b0001c, 0xc00457c, +0x2003021, 0x24040001, 0x24050019, 0xc00457c, +0x2003021, 0x97a2001c, 0x30430700, 0x24020400, +0x10620027, 0x28620401, 0x1040000e, 0x24020200, +0x1062001f, 0x28620201, 0x10400005, 0x24020100, +0x5062001e, 0x3c130001, 0x1000001e, 0x3c020004, +0x24020300, 0x50620019, 0x3c130002, 0x10000019, +0x3c020004, 0x24020600, 0x1062000d, 0x28620601, +0x10400005, 0x24020500, 0x5062000b, 0x3c130002, +0x10000010, 0x3c020004, 0x24020700, 0x1462000d, +0x3c020004, 0x3c130004, 0x1000000a, 0x3c150008, +0x10000006, 0x3c130004, 0x10000005, 0x3c150008, +0x3c130001, 0x10000002, 0x3c150008, 0x3c150010, +0x3c020004, 0x12620017, 0x3c028000, 0x8f820054, +0x24100001, 0x3c010001, 0xac306d9c, 0x3c010001, +0xac306d98, 0x3c010001, 0xac306da4, 0x3c010001, +0xac306e24, 0x3c010001, 0xac226f30, 0x3c020001, +0x16620022, 0x2758825, 0x2021, 0x2821, +0xc004ddb, 0x34068000, 0x3c010001, 0x1000001b, +0xac306e20, 0x2221025, 0x2b31825, 0x438825, +0x97a6001c, 0x3c020001, 0x8c426f1c, 0x3c070001, +0x8ce76d98, 0x3c040001, 0x24846c5c, 0xafa20010, +0x97a2001e, 0x3c05000c, 0x34a50323, 0x3c010001, +0xac206e20, 0xc002b3b, 0xafa20014, 0x10000007, +0x0, 0x3c110002, 0x23e8821, 0x8e318ff0, +0x3c027fff, 0x3442ffff, 0x2228824, 0x3c020001, +0x8c426da8, 0x10400069, 0x0, 0x3c020001, +0x8c426f1c, 0x10400002, 0x3c022000, 0x2228825, +0x8fa8002c, 0x81140, 0x3c010002, 0x220821, +0x8c229004, 0x10400003, 0x3c020020, 0x10000005, +0x2228825, 0x3c02ffdf, 0x3442ffff, 0x2228824, +0x8fa8002c, 0x81140, 0x3c010002, 0x220821, +0x8c22900c, 0x10400003, 0x3c020080, 0x1000004f, +0x2228825, 0x3c02ff7f, 0x3442ffff, 0x1000004b, +0x2228824, 0x8fa8002c, 0x82940, 0x3c030002, +0x651821, 0x8c638ff8, 0x3c024000, 0x621024, +0x14400008, 0x3c027fff, 0x3442ffff, 0x628824, +0x3c010002, 0x250821, 0xac318ff0, 0x10000041, +0x2201021, 0x3c020001, 0x8c426da8, 0x10400034, +0x3c11c00c, 0x3c020001, 0x8c426e44, 0x3c04c00c, +0x34842000, 0x3c030001, 0x8c636f1c, 0x2102b, +0x21023, 0x441024, 0x10600003, 0x518825, +0x3c022000, 0x2228825, 0x3c020002, 0x451021, +0x8c429004, 0x10400003, 0x3c020020, 0x10000004, +0x2228825, 0x3c02ffdf, 0x3442ffff, 0x2228824, +0x8fa8002c, 0x81140, 0x3c010002, 0x220821, +0x8c22900c, 0x10400003, 0x3c020080, 0x10000004, +0x2228825, 0x3c02ff7f, 0x3442ffff, 0x2228824, +0x3c020001, 0x8c426e30, 0x10400002, 0x3c020800, +0x2228825, 0x3c020001, 0x8c426e34, 0x10400002, +0x3c020400, 0x2228825, 0x3c020001, 0x8c426e38, +0x10400006, 0x3c020100, 0x10000004, 0x2228825, +0x3c027fff, 0x3442ffff, 0x628824, 0x8fa8002c, +0x81140, 0x3c010002, 0x220821, 0xac318ff0, +0x2201021, 0x8fbf0048, 0x8fbe0044, 0x8fb50040, +0x8fb3003c, 0x8fb20038, 0x8fb10034, 0x8fb00030, +0x3e00008, 0x27bd0050, 0x27bdffd0, 0xafb20028, +0x809021, 0xafbf002c, 0xafb10024, 0xafb00020, +0x8f840200, 0x3c100001, 0x8e106d98, 0x8f860220, +0x24020002, 0x1202005c, 0x2e020003, 0x10400005, +0x24020001, 0x1202000a, 0x121940, 0x1000010c, +0x0, 0x24020004, 0x120200bf, 0x24020008, +0x120200be, 0x128940, 0x10000105, 0x0, +0x3c050002, 0xa32821, 0x8ca58ffc, 0x3c100002, +0x2038021, 0x8e108ff4, 0x3c024000, 0xa21024, +0x10400038, 0x3c020008, 0x2021024, 0x10400020, +0x34840002, 0x3c020002, 0x431021, 0x8c429000, +0x10400005, 0x34840020, 0x34840100, 0x3c020020, +0x10000006, 0x2028025, 0x2402feff, 0x822024, +0x3c02ffdf, 0x3442ffff, 0x2028024, 0x121140, +0x3c010002, 0x220821, 0x8c229008, 0x10400005, +0x3c020001, 0xc23025, 0x3c020080, 0x10000016, +0x2028025, 0x3c02fffe, 0x3442ffff, 0xc23024, +0x3c02ff7f, 0x3442ffff, 0x1000000f, 0x2028024, +0x2402fedf, 0x822024, 0x3c02fffe, 0x3442ffff, +0xc23024, 0x3c02ff5f, 0x3442ffff, 0x2028024, +0x3c010002, 0x230821, 0xac209000, 0x3c010002, +0x230821, 0xac209008, 0xaf840200, 0xaf860220, +0x8f820220, 0x34420002, 0xaf820220, 0x1000000a, +0x121140, 0x3c02bfff, 0x3442ffff, 0x8f830200, +0x2028024, 0x2402fffd, 0x621824, 0xc003daf, +0xaf830200, 0x121140, 0x3c010002, 0x220821, +0x100000b7, 0xac308ff4, 0x3c020001, 0x8c426f1c, +0x10400069, 0x24050004, 0x24040001, 0xc00457c, +0x27a60018, 0x24040001, 0x24050005, 0xc00457c, +0x27a6001a, 0x97a30018, 0x97a2001a, 0x3c040001, +0x24846e48, 0x30630c00, 0x31a82, 0x30420c00, +0x21282, 0xa7a2001a, 0x21080, 0x441021, +0x431021, 0xa7a30018, 0x90480000, 0x24020001, +0x3103ffff, 0x10620029, 0x28620002, 0x10400005, +0x0, 0x10600009, 0x0, 0x1000003d, +0x0, 0x10700013, 0x24020003, 0x1062002c, +0x0, 0x10000037, 0x0, 0x8f820200, +0x2403feff, 0x431024, 0xaf820200, 0x8f820220, +0x3c03fffe, 0x3463ffff, 0x431024, 0xaf820220, +0x3c010002, 0xac209004, 0x3c010002, 0x10000032, +0xac20900c, 0x8f820200, 0x34420100, 0xaf820200, +0x8f820220, 0x3c03fffe, 0x3463ffff, 0x431024, +0xaf820220, 0x24020100, 0x3c010002, 0xac229004, +0x3c010002, 0x10000024, 0xac20900c, 0x8f820200, +0x2403feff, 0x431024, 0xaf820200, 0x8f820220, +0x3c030001, 0x431025, 0xaf820220, 0x3c010002, +0xac209004, 0x3c010002, 0x10000017, 0xac23900c, +0x8f820200, 0x34420100, 0xaf820200, 0x8f820220, +0x3c030001, 0x431025, 0xaf820220, 0x24020100, +0x3c010002, 0xac229004, 0x3c010002, 0x1000000a, +0xac23900c, 0x3c040001, 0x24846c80, 0x97a6001a, +0x97a70018, 0x3c050001, 0x34a5ffff, 0xafa80010, +0xc002b3b, 0xafa00014, 0x8f820200, 0x34420002, +0x1000004b, 0xaf820200, 0x128940, 0x3c050002, +0xb12821, 0x8ca58ff8, 0x3c100002, 0x2118021, +0x8e108ff0, 0x3c024000, 0xa21024, 0x14400010, +0x0, 0x3c020001, 0x8c426f1c, 0x14400005, +0x3c02bfff, 0x8f820200, 0x34420002, 0xaf820200, +0x3c02bfff, 0x3442ffff, 0xc003daf, 0x2028024, +0x3c010002, 0x310821, 0x10000031, 0xac308ff0, +0x3c020001, 0x8c426f1c, 0x10400005, 0x3c020020, +0x3c020001, 0x8c426e44, 0x10400025, 0x3c020020, +0xa21024, 0x10400007, 0x34840020, 0x24020100, +0x3c010002, 0x310821, 0xac229004, 0x10000006, +0x34840100, 0x3c010002, 0x310821, 0xac209004, +0x2402feff, 0x822024, 0x3c020080, 0xa21024, +0x10400007, 0x121940, 0x3c020001, 0x3c010002, +0x230821, 0xac22900c, 0x10000008, 0xc23025, +0x121140, 0x3c010002, 0x220821, 0xac20900c, +0x3c02fffe, 0x3442ffff, 0xc23024, 0xaf840200, +0xaf860220, 0x8f820220, 0x34420002, 0xaf820220, +0x121140, 0x3c010002, 0x220821, 0xac308ff0, +0x8fbf002c, 0x8fb20028, 0x8fb10024, 0x8fb00020, +0x3e00008, 0x27bd0030, 0x0, 0x1821, +0x308400ff, 0x2405ffdf, 0x2406ffbf, 0x641007, +0x30420001, 0x10400004, 0x0, 0x8f820044, +0x10000003, 0x34420040, 0x8f820044, 0x461024, +0xaf820044, 0x8f820044, 0x34420020, 0xaf820044, +0x8f820044, 0x451024, 0xaf820044, 0x24630001, +0x28620008, 0x5440ffee, 0x641007, 0x3e00008, +0x0, 0x2c820008, 0x1040001b, 0x0, +0x2405ffdf, 0x2406ffbf, 0x41880, 0x3c020001, +0x24426e60, 0x621821, 0x24640004, 0x90620000, +0x10400004, 0x0, 0x8f820044, 0x10000003, +0x34420040, 0x8f820044, 0x461024, 0xaf820044, +0x8f820044, 0x34420020, 0xaf820044, 0x8f820044, +0x451024, 0xaf820044, 0x24630001, 0x64102b, +0x1440ffee, 0x0, 0x3e00008, 0x0, +0x0, 0x0, 0x0, 0x8f8400c4, +0x8f8600e0, 0x8f8700e4, 0x2402fff8, 0xc22824, +0x10e5001a, 0x27623ff8, 0x14e20002, 0x24e80008, +0x27683000, 0x55050004, 0x8d0a0000, 0x30c20004, +0x14400012, 0x805021, 0x8ce90000, 0x8f42013c, +0x1494823, 0x49182b, 0x94eb0006, 0x10600002, +0x25630050, 0x494821, 0x123182b, 0x50400003, +0x8f4201fc, 0x3e00008, 0xe01021, 0xaf8800e8, +0x24420001, 0xaf4201fc, 0xaf8800e4, 0x3e00008, +0x1021, 0x3e00008, 0x0, 0x8f8300e4, +0x27623ff8, 0x10620004, 0x24620008, 0xaf8200e8, +0x3e00008, 0xaf8200e4, 0x27623000, 0xaf8200e8, +0x3e00008, 0xaf8200e4, 0x3e00008, 0x0, +0x0, 0x0, 0x0, 0x8f880120, +0x27624fe0, 0x8f830128, 0x15020002, 0x25090020, +0x27694800, 0x11230012, 0x8fa20010, 0xad040000, +0xad050004, 0xad060008, 0xa507000e, 0x8fa30014, +0xad020018, 0x8fa20018, 0xad03001c, 0x25030016, +0xad020010, 0xad030014, 0xaf890120, 0x8f4300fc, +0x24020001, 0x2463ffff, 0x3e00008, 0xaf4300fc, +0x8f430324, 0x1021, 0x24630001, 0x3e00008, +0xaf430324, 0x3e00008, 0x0, 0x8f880100, +0x276247e0, 0x8f830108, 0x15020002, 0x25090020, +0x27694000, 0x1123000f, 0x8fa20010, 0xad040000, +0xad050004, 0xad060008, 0xa507000e, 0x8fa30014, +0xad020018, 0x8fa20018, 0xad03001c, 0x25030016, +0xad020010, 0xad030014, 0xaf890100, 0x3e00008, +0x24020001, 0x8f430328, 0x1021, 0x24630001, +0x3e00008, 0xaf430328, 0x3e00008, 0x0, +0x0, 0x0, 0x0, 0x0 }; +static int tigon2FwRodata[/*(MAX_RODATA_LEN/4) + 1*/] = { +0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f6677, 0x6d61696e, 0x2e632c76, 0x20312e31, +0x2e322e34, 0x35203139, 0x39392f30, 0x312f3234, +0x2030303a, 0x31303a35, 0x35207368, 0x75616e67, +0x20457870, 0x20240000, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x6261644d, 0x656d537a, +0x0, 0x68775665, 0x72000000, 0x62616448, +0x77566572, 0x0, 0x2a2a4441, 0x574e5f41, +0x0, 0x74785278, 0x4266537a, 0x0, +0x62664174, 0x6e4d726b, 0x0, 0x7265645a, +0x6f6e6531, 0x0, 0x70636943, 0x6f6e6600, +0x67656e43, 0x6f6e6600, 0x2a646d61, 0x5244666c, +0x0, 0x2a50414e, 0x49432a00, 0x2e2e2f2e, +0x2e2f2e2e, 0x2f2e2e2f, 0x2e2e2f73, 0x72632f6e, +0x69632f66, 0x77322f63, 0x6f6d6d6f, 0x6e2f6677, +0x6d61696e, 0x2e630000, 0x72636246, 0x6c616773, +0x0, 0x62616452, 0x78526362, 0x0, +0x676c6f62, 0x466c6773, 0x0, 0x2b5f6469, +0x73705f6c, 0x6f6f7000, 0x2b65765f, 0x68616e64, +0x6c657200, 0x63616e74, 0x31446d61, 0x0, +0x2b715f64, 0x6d615f74, 0x6f5f6e69, 0x635f636b, +0x73756d00, 0x2b685f73, 0x656e645f, 0x64617461, +0x5f726561, 0x64795f63, 0x6b73756d, 0x0, +0x2b685f64, 0x6d615f72, 0x645f6173, 0x73697374, +0x5f636b73, 0x756d0000, 0x74436b73, 0x6d4f6e00, +0x2b715f64, 0x6d615f74, 0x6f5f6e69, 0x63000000, +0x2b685f73, 0x656e645f, 0x64617461, 0x5f726561, +0x64790000, 0x2b685f64, 0x6d615f72, 0x645f6173, +0x73697374, 0x0, 0x74436b73, 0x6d4f6666, +0x0, 0x2b685f73, 0x656e645f, 0x62645f72, +0x65616479, 0x0, 0x68737453, 0x52696e67, +0x0, 0x62616453, 0x52696e67, 0x0, +0x6e696353, 0x52696e67, 0x0, 0x77446d61, +0x416c6c41, 0x0, 0x2b715f64, 0x6d615f74, +0x6f5f686f, 0x73745f63, 0x6b73756d, 0x0, +0x2b685f6d, 0x61635f72, 0x785f636f, 0x6d705f63, +0x6b73756d, 0x0, 0x2b685f64, 0x6d615f77, +0x725f6173, 0x73697374, 0x5f636b73, 0x756d0000, +0x72436b73, 0x6d4f6e00, 0x2b715f64, 0x6d615f74, +0x6f5f686f, 0x73740000, 0x2b685f6d, 0x61635f72, +0x785f636f, 0x6d700000, 0x2b685f64, 0x6d615f77, +0x725f6173, 0x73697374, 0x0, 0x72436b73, +0x6d4f6666, 0x0, 0x2b685f72, 0x6563765f, +0x62645f72, 0x65616479, 0x0, 0x2b685f72, +0x6563765f, 0x6a756d62, 0x6f5f6264, 0x5f726561, +0x64790000, 0x2b685f72, 0x6563765f, 0x6d696e69, +0x5f62645f, 0x72656164, 0x79000000, 0x2b6d685f, +0x636f6d6d, 0x616e6400, 0x2b685f74, 0x696d6572, +0x0, 0x2b685f64, 0x6f5f7570, 0x64617465, +0x5f74785f, 0x636f6e73, 0x0, 0x2b685f64, +0x6f5f7570, 0x64617465, 0x5f72785f, 0x70726f64, +0x0, 0x2b636b73, 0x756d3136, 0x0, +0x2b706565, 0x6b5f6d61, 0x635f7278, 0x5f776100, +0x2b706565, 0x6b5f6d61, 0x635f7278, 0x0, +0x2b646571, 0x5f6d6163, 0x5f727800, 0x2b685f6d, +0x61635f72, 0x785f6174, 0x746e0000, 0x62616452, +0x6574537a, 0x0, 0x72784264, 0x4266537a, +0x0, 0x2b6e756c, 0x6c5f6861, 0x6e646c65, +0x72000000, 0x66774f70, 0x4661696c, 0x0, +0x2b685f75, 0x70646174, 0x655f6c65, 0x64340000, +0x2b685f75, 0x70646174, 0x655f6c65, 0x64360000, +0x2b685f75, 0x70646174, 0x655f6c65, 0x64320000, +0x696e7453, 0x74617465, 0x0, 0x2a2a696e, +0x69744370, 0x0, 0x23736372, 0x65616d00, +0x69537461, 0x636b4572, 0x0, 0x70726f62, +0x654d656d, 0x0, 0x2a2a4441, 0x574e5f42, +0x0, 0x2b73775f, 0x646d615f, 0x61737369, +0x73745f70, 0x6c75735f, 0x74696d65, 0x72000000, +0x2b267072, 0x656c6f61, 0x645f7772, 0x5f646573, +0x63720000, 0x2b267072, 0x656c6f61, 0x645f7264, +0x5f646573, 0x63720000, 0x2b685f68, 0x665f7469, +0x6d657200, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f7469, 0x6d65722e, 0x632c7620, 0x312e312e, +0x322e3335, 0x20313939, 0x392f3031, 0x2f323720, +0x31393a30, 0x393a3530, 0x20686179, 0x65732045, +0x78702024, 0x0, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x542d446d, 0x61526432, +0x0, 0x542d446d, 0x61526431, 0x0, +0x542d446d, 0x61526442, 0x0, 0x542d446d, +0x61577232, 0x0, 0x542d446d, 0x61577231, +0x0, 0x542d446d, 0x61577242, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f636f, 0x6d6d616e, 0x642e632c, 0x7620312e, +0x312e322e, 0x32382031, 0x3939392f, 0x30312f32, +0x30203139, 0x3a34393a, 0x34392073, 0x6875616e, +0x67204578, 0x70202400, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x3f48636d, 0x644d6278, +0x0, 0x3f636d64, 0x48737453, 0x0, +0x3f636d64, 0x4d634d64, 0x0, 0x3f636d64, +0x50726f6d, 0x0, 0x3f636d64, 0x4c696e6b, +0x0, 0x3f636d64, 0x45727200, 0x86ac, +0x8e5c, 0x8e5c, 0x8de4, 0x8b78, +0x8e30, 0x8e5c, 0x8790, 0x8800, +0x8990, 0x8a68, 0x8a34, 0x8e5c, +0x8870, 0x8b24, 0x8e5c, 0x8b34, +0x87b4, 0x8824, 0x0, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f6d63, 0x6173742e, 0x632c7620, 0x312e312e, +0x322e3820, 0x31393938, 0x2f31322f, 0x30382030, +0x323a3336, 0x3a333620, 0x73687561, 0x6e672045, +0x78702024, 0x0, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x6164644d, 0x63447570, +0x0, 0x6164644d, 0x6346756c, 0x0, +0x64656c4d, 0x634e6f45, 0x0, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f646d, 0x612e632c, 0x7620312e, 0x312e322e, +0x32342031, 0x3939382f, 0x31322f32, 0x31203030, +0x3a33333a, 0x30392073, 0x6875616e, 0x67204578, +0x70202400, 0x65767452, 0x6e674600, 0x51657674, +0x46000000, 0x51657674, 0x505f4600, 0x4d657674, +0x526e6746, 0x0, 0x4d516576, 0x74460000, +0x4d516576, 0x505f4600, 0x5173436f, 0x6e495f46, +0x0, 0x5173436f, 0x6e734600, 0x51725072, +0x6f644600, 0x7377446d, 0x614f6666, 0x0, +0x31446d61, 0x4f6e0000, 0x7377446d, 0x614f6e00, +0x2372446d, 0x6141544e, 0x0, 0x72446d61, +0x41544e30, 0x0, 0x72446d61, 0x41544e31, +0x0, 0x72446d61, 0x34476200, 0x2a50414e, +0x49432a00, 0x2e2e2f2e, 0x2e2f2e2e, 0x2f2e2e2f, +0x2e2e2f73, 0x72632f6e, 0x69632f66, 0x77322f63, +0x6f6d6d6f, 0x6e2f646d, 0x612e6300, 0x2377446d, +0x6141544e, 0x0, 0x77446d61, 0x41544e30, +0x0, 0x77446d61, 0x41544e31, 0x0, +0x77446d61, 0x34476200, 0x0, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f7472, 0x6163652e, 0x632c7620, 0x312e312e, +0x322e3520, 0x31393938, 0x2f30392f, 0x33302031, +0x383a3530, 0x3a323820, 0x73687561, 0x6e672045, +0x78702024, 0x0, 0x0, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f6461, 0x74612e63, 0x2c762031, 0x2e312e32, +0x2e313220, 0x31393939, 0x2f30312f, 0x32302031, +0x393a3439, 0x3a353120, 0x73687561, 0x6e672045, +0x78702024, 0x0, 0x46575f56, 0x45525349, +0x4f4e3a20, 0x23312046, 0x72692041, 0x70722037, +0x2031373a, 0x35373a35, 0x32205044, 0x54203230, +0x30300000, 0x46575f43, 0x4f4d5049, 0x4c455f54, +0x494d453a, 0x2031373a, 0x35373a35, 0x32000000, +0x46575f43, 0x4f4d5049, 0x4c455f42, 0x593a2064, +0x65767263, 0x73000000, 0x46575f43, 0x4f4d5049, +0x4c455f48, 0x4f53543a, 0x20636f6d, 0x70757465, +0x0, 0x46575f43, 0x4f4d5049, 0x4c455f44, +0x4f4d4149, 0x4e3a2065, 0x6e672e61, 0x6374656f, +0x6e2e636f, 0x6d000000, 0x46575f43, 0x4f4d5049, +0x4c45523a, 0x20676363, 0x20766572, 0x73696f6e, +0x20322e37, 0x2e320000, 0x0, 0x12041100, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f6d65, 0x6d2e632c, 0x7620312e, 0x312e322e, +0x35203139, 0x39382f30, 0x392f3330, 0x2031383a, +0x35303a30, 0x38207368, 0x75616e67, 0x20457870, +0x20240000, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f7365, 0x6e642e63, 0x2c762031, 0x2e312e32, +0x2e343420, 0x31393938, 0x2f31322f, 0x32312030, +0x303a3333, 0x3a313820, 0x73687561, 0x6e672045, +0x78702024, 0x0, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x69736e74, 0x54637055, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f7265, 0x63762e63, 0x2c762031, 0x2e312e32, +0x2e353320, 0x31393939, 0x2f30312f, 0x31362030, +0x323a3535, 0x3a343320, 0x73687561, 0x6e672045, +0x78702024, 0x0, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x724d6163, 0x43686b30, +0x0, 0x72784672, 0x6d324c67, 0x0, +0x72784e6f, 0x53744264, 0x0, 0x72784e6f, +0x4d694264, 0x0, 0x72784e6f, 0x4a6d4264, +0x0, 0x7278436b, 0x446d6146, 0x0, +0x72785144, 0x6d457846, 0x0, 0x72785144, +0x6d614600, 0x72785144, 0x4c426446, 0x0, +0x72785144, 0x6d426446, 0x0, 0x72784372, +0x63506164, 0x0, 0x72536d51, 0x446d6146, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f6d61, 0x632e632c, 0x7620312e, 0x312e322e, +0x32322031, 0x3939382f, 0x31322f30, 0x38203032, +0x3a33363a, 0x33302073, 0x6875616e, 0x67204578, +0x70202400, 0x65767452, 0x6e674600, 0x51657674, +0x46000000, 0x51657674, 0x505f4600, 0x4d657674, +0x526e6746, 0x0, 0x4d516576, 0x74460000, +0x4d516576, 0x505f4600, 0x5173436f, 0x6e495f46, +0x0, 0x5173436f, 0x6e734600, 0x51725072, +0x6f644600, 0x6d616354, 0x68726573, 0x0, +0x23744d61, 0x6341544e, 0x0, 0x23724d61, +0x6341544e, 0x0, 0x72656d41, 0x73737274, +0x0, 0x6c696e6b, 0x444f574e, 0x0, +0x6c696e6b, 0x55500000, 0x0, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f636b, 0x73756d2e, 0x632c7620, 0x312e312e, +0x322e3920, 0x31393939, 0x2f30312f, 0x31342030, +0x303a3033, 0x3a343820, 0x73687561, 0x6e672045, +0x78702024, 0x0, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x0, 0x0, +0x0, 0x50726f62, 0x65506879, 0x0, +0x6c6e6b41, 0x53535254, 0x0, 0x109a4, +0x10a1c, 0x10a50, 0x10a7c, 0x11050, +0x10aa8, 0x10b10, 0x111fc, 0x10dc0, +0x10c68, 0x10c80, 0x10cc4, 0x10cec, +0x10d0c, 0x10d34, 0x111fc, 0x10dc0, +0x10df8, 0x10e10, 0x10e40, 0x10e68, +0x10e88, 0x10eb0, 0x0, 0x10fdc, +0x11008, 0x1102c, 0x111fc, 0x11050, +0x11078, 0x11108, 0x0, 0x0, +0x0, 0x1186c, 0x1193c, 0x11a14, +0x11ae4, 0x11b40, 0x11c1c, 0x11c44, +0x11d20, 0x11d48, 0x11ef0, 0x11f18, +0x120c0, 0x122b8, 0x1254c, 0x12460, +0x1254c, 0x12578, 0x120e8, 0x12290, +0x7273745f, 0x676d6969, 0x0, 0x12608, +0x12640, 0x12728, 0x13374, 0x133b4, +0x133cc, 0x7365746c, 0x6f6f7000, 0x0, +0x0, 0x13bbc, 0x13bfc, 0x13c8c, +0x13cd0, 0x13d34, 0x13dc0, 0x13df4, +0x13e7c, 0x13f14, 0x13fe4, 0x14024, +0x140a8, 0x140cc, 0x141dc, 0x646f4261, +0x73655067, 0x0, 0x0, 0x0, +0x0, 0x73746d61, 0x634c4e4b, 0x0, +0x6765746d, 0x636c6e6b, 0x0, 0x14ed8, +0x14ed8, 0x14b8c, 0x14bd8, 0x14c24, +0x14ed8, 0x7365746d, 0x61636163, 0x74000000, +0x0, 0x0 }; +static int tigon2FwData[/*(MAX_DATA_LEN/4) + 1*/] = { +0x1, +0x1, 0x1, 0xc001fc, 0x3ffc, +0xc00000, 0x416c7465, 0x6f6e2041, 0x63654e49, +0x43205600, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x416c7465, +0x6f6e2041, 0x63654e49, 0x43205600, 0x42424242, +0x0, 0x0, 0x0, 0x1ffffc, +0x1fff7c, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x60cf00, +0x60, 0xcf000000, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x3, 0x0, +0x1, 0x0, 0x0, 0x0, +0x1, 0x0, 0x1, 0x0, +0x0, 0x0, 0x0, 0x1, +0x1, 0x0, 0x0, 0x0, +0x0, 0x0, 0x1000000, 0x21000000, +0x12000140, 0x0, 0x0, 0x20000000, +0x120000a0, 0x0, 0x12000060, 0x12000180, +0x120001e0, 0x0, 0x0, 0x0, +0x1, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x2, +0x0, 0x0, 0x30001, 0x1, +0x30201, 0x0, 0x0, 0x1010101, +0x1010100, 0x10100, 0x1010001, 0x10001, +0x1000101, 0x101, 0x0, 0x0 }; diff --git a/os/pc/etherif.h b/os/pc/etherif.h new file mode 100644 index 00000000..d80e06c9 --- /dev/null +++ b/os/pc/etherif.h @@ -0,0 +1,39 @@ +enum { + MaxEther = 24, + Ntypes = 8, +}; + +typedef struct Ether Ether; +struct Ether { + ISAConf; /* hardware info */ + + int ctlrno; + int tbdf; /* type+busno+devno+funcno */ + int minmtu; + int maxmtu; + uchar ea[Eaddrlen]; + + void (*attach)(Ether*); /* filled in by reset routine */ + void (*detach)(Ether*); + void (*transmit)(Ether*); + void (*interrupt)(Ureg*, void*); + long (*ifstat)(Ether*, void*, long, ulong); + long (*ctl)(Ether*, void*, long); /* custom ctl messages */ + void (*power)(Ether*, int); /* power on/off */ + void (*shutdown)(Ether*); /* shutdown hardware before reboot */ + void *ctlr; + + Queue* oq; + + Netif; +}; + +extern Block* etheriq(Ether*, Block*, int); +extern void addethercard(char*, int(*)(Ether*)); +extern ulong ethercrc(uchar*, int); +extern int parseether(uchar*, char*); + +#define NEXT(x, l) (((x)+1)%(l)) +#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1) +#define HOWMANY(x, y) (((x)+((y)-1))/(y)) +#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) diff --git a/os/pc/etherigbe.c b/os/pc/etherigbe.c new file mode 100644 index 00000000..a8c54267 --- /dev/null +++ b/os/pc/etherigbe.c @@ -0,0 +1,1968 @@ +/* + * Intel 8254[340]NN Gigabit Ethernet Controller + * as found on the Intel PRO/1000 series of adapters: + * 82543GC Intel PRO/1000 T + * 82544EI Intel PRO/1000 XT + * 82540EM Intel PRO/1000 MT + * 82541[GP]I + * 82547GI + * 82546GB + * 82546EB + * To Do: + * finish autonegotiation code; + * integrate fiber stuff back in (this ONLY handles + * the CAT5 cards at the moment); + * add checksum-offload; + * add tuning control via ctl file; + * this driver is little-endian specific. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" +#include "ethermii.h" + +enum { + i82542 = (0x1000<<16)|0x8086, + i82543gc = (0x1004<<16)|0x8086, + i82544ei = (0x1008<<16)|0x8086, + i82547ei = (0x1019<<16)|0x8086, + i82540em = (0x100E<<16)|0x8086, + i82540eplp = (0x101E<<16)|0x8086, + i82545gmc = (0x1026<<16)|0x8086, + i82547gi = (0x1075<<16)|0x8086, + i82541gi = (0x1076<<16)|0x8086, + i82546gb = (0x1079<<16)|0x8086, + i82541pi = (0x107c<<16)|0x8086, + i82546eb = (0x1010<<16)|0x8086, +}; + +enum { + Ctrl = 0x00000000, /* Device Control */ + Ctrldup = 0x00000004, /* Device Control Duplicate */ + Status = 0x00000008, /* Device Status */ + Eecd = 0x00000010, /* EEPROM/Flash Control/Data */ + Ctrlext = 0x00000018, /* Extended Device Control */ + Mdic = 0x00000020, /* MDI Control */ + Fcal = 0x00000028, /* Flow Control Address Low */ + Fcah = 0x0000002C, /* Flow Control Address High */ + Fct = 0x00000030, /* Flow Control Type */ + Icr = 0x000000C0, /* Interrupt Cause Read */ + Ics = 0x000000C8, /* Interrupt Cause Set */ + Ims = 0x000000D0, /* Interrupt Mask Set/Read */ + Imc = 0x000000D8, /* Interrupt mask Clear */ + Rctl = 0x00000100, /* Receive Control */ + Fcttv = 0x00000170, /* Flow Control Transmit Timer Value */ + Txcw = 0x00000178, /* Transmit Configuration Word */ + Rxcw = 0x00000180, /* Receive Configuration Word */ + Tctl = 0x00000400, /* Transmit Control */ + Tipg = 0x00000410, /* Transmit IPG */ + Tbt = 0x00000448, /* Transmit Burst Timer */ + Ait = 0x00000458, /* Adaptive IFS Throttle */ + Fcrtl = 0x00002160, /* Flow Control RX Threshold Low */ + Fcrth = 0x00002168, /* Flow Control Rx Threshold High */ + Rdfh = 0x00002410, /* Receive data fifo head */ + Rdft = 0x00002418, /* Receive data fifo tail */ + Rdfhs = 0x00002420, /* Receive data fifo head saved */ + Rdfts = 0x00002428, /* Receive data fifo tail saved */ + Rdfpc = 0x00002430, /* Receive data fifo packet count */ + Rdbal = 0x00002800, /* Rd Base Address Low */ + Rdbah = 0x00002804, /* Rd Base Address High */ + Rdlen = 0x00002808, /* Receive Descriptor Length */ + Rdh = 0x00002810, /* Receive Descriptor Head */ + Rdt = 0x00002818, /* Receive Descriptor Tail */ + Rdtr = 0x00002820, /* Receive Descriptor Timer Ring */ + Rxdctl = 0x00002828, /* Receive Descriptor Control */ + Radv = 0x0000282C, /* Receive Interrupt Absolute Delay Timer */ + Txdmac = 0x00003000, /* Transfer DMA Control */ + Ett = 0x00003008, /* Early Transmit Control */ + Tdfh = 0x00003410, /* Transmit data fifo head */ + Tdft = 0x00003418, /* Transmit data fifo tail */ + Tdfhs = 0x00003420, /* Transmit data Fifo Head saved */ + Tdfts = 0x00003428, /* Transmit data fifo tail saved */ + Tdfpc = 0x00003430, /* Trasnmit data Fifo packet count */ + Tdbal = 0x00003800, /* Td Base Address Low */ + Tdbah = 0x00003804, /* Td Base Address High */ + Tdlen = 0x00003808, /* Transmit Descriptor Length */ + Tdh = 0x00003810, /* Transmit Descriptor Head */ + Tdt = 0x00003818, /* Transmit Descriptor Tail */ + Tidv = 0x00003820, /* Transmit Interrupt Delay Value */ + Txdctl = 0x00003828, /* Transmit Descriptor Control */ + Tadv = 0x0000382C, /* Transmit Interrupt Absolute Delay Timer */ + + Statistics = 0x00004000, /* Start of Statistics Area */ + Gorcl = 0x88/4, /* Good Octets Received Count */ + Gotcl = 0x90/4, /* Good Octets Transmitted Count */ + Torl = 0xC0/4, /* Total Octets Received */ + Totl = 0xC8/4, /* Total Octets Transmitted */ + Nstatistics = 64, + + Rxcsum = 0x00005000, /* Receive Checksum Control */ + Mta = 0x00005200, /* Multicast Table Array */ + Ral = 0x00005400, /* Receive Address Low */ + Rah = 0x00005404, /* Receive Address High */ + Manc = 0x00005820, /* Management Control */ +}; + +enum { /* Ctrl */ + Bem = 0x00000002, /* Big Endian Mode */ + Prior = 0x00000004, /* Priority on the PCI bus */ + Lrst = 0x00000008, /* Link Reset */ + Asde = 0x00000020, /* Auto-Speed Detection Enable */ + Slu = 0x00000040, /* Set Link Up */ + Ilos = 0x00000080, /* Invert Loss of Signal (LOS) */ + SspeedMASK = 0x00000300, /* Speed Selection */ + SspeedSHIFT = 8, + Sspeed10 = 0x00000000, /* 10Mb/s */ + Sspeed100 = 0x00000100, /* 100Mb/s */ + Sspeed1000 = 0x00000200, /* 1000Mb/s */ + Frcspd = 0x00000800, /* Force Speed */ + Frcdplx = 0x00001000, /* Force Duplex */ + SwdpinsloMASK = 0x003C0000, /* Software Defined Pins - lo nibble */ + SwdpinsloSHIFT = 18, + SwdpioloMASK = 0x03C00000, /* Software Defined Pins - I or O */ + SwdpioloSHIFT = 22, + Devrst = 0x04000000, /* Device Reset */ + Rfce = 0x08000000, /* Receive Flow Control Enable */ + Tfce = 0x10000000, /* Transmit Flow Control Enable */ + Vme = 0x40000000, /* VLAN Mode Enable */ +}; + +enum { /* Status */ + Lu = 0x00000002, /* Link Up */ + Tckok = 0x00000004, /* Transmit clock is running */ + Rbcok = 0x00000008, /* Receive clock is running */ + Txoff = 0x00000010, /* Transmission Paused */ + Tbimode = 0x00000020, /* TBI Mode Indication */ + LspeedMASK = 0x000000C0, /* Link Speed Setting */ + LspeedSHIFT = 6, + Lspeed10 = 0x00000000, /* 10Mb/s */ + Lspeed100 = 0x00000040, /* 100Mb/s */ + Lspeed1000 = 0x00000080, /* 1000Mb/s */ + Mtxckok = 0x00000400, /* MTX clock is running */ + Pci66 = 0x00000800, /* PCI Bus speed indication */ + Bus64 = 0x00001000, /* PCI Bus width indication */ + Pcixmode = 0x00002000, /* PCI-X mode */ + PcixspeedMASK = 0x0000C000, /* PCI-X bus speed */ + PcixspeedSHIFT = 14, + Pcix66 = 0x00000000, /* 50-66MHz */ + Pcix100 = 0x00004000, /* 66-100MHz */ + Pcix133 = 0x00008000, /* 100-133MHz */ +}; + +enum { /* Ctrl and Status */ + Fd = 0x00000001, /* Full-Duplex */ + AsdvMASK = 0x00000300, + AsdvSHIFT = 8, + Asdv10 = 0x00000000, /* 10Mb/s */ + Asdv100 = 0x00000100, /* 100Mb/s */ + Asdv1000 = 0x00000200, /* 1000Mb/s */ +}; + +enum { /* Eecd */ + Sk = 0x00000001, /* Clock input to the EEPROM */ + Cs = 0x00000002, /* Chip Select */ + Di = 0x00000004, /* Data Input to the EEPROM */ + Do = 0x00000008, /* Data Output from the EEPROM */ + Areq = 0x00000040, /* EEPROM Access Request */ + Agnt = 0x00000080, /* EEPROM Access Grant */ + Eesz256 = 0x00000200, /* EEPROM is 256 words not 64 */ + Spi = 0x00002000, /* EEPROM is SPI not Microwire */ +}; + +enum { /* Ctrlext */ + Gpien = 0x0000000F, /* General Purpose Interrupt Enables */ + SwdpinshiMASK = 0x000000F0, /* Software Defined Pins - hi nibble */ + SwdpinshiSHIFT = 4, + SwdpiohiMASK = 0x00000F00, /* Software Defined Pins - I or O */ + SwdpiohiSHIFT = 8, + Asdchk = 0x00001000, /* ASD Check */ + Eerst = 0x00002000, /* EEPROM Reset */ + Ips = 0x00004000, /* Invert Power State */ + Spdbyps = 0x00008000, /* Speed Select Bypass */ +}; + +enum { /* EEPROM content offsets */ + Ea = 0x00, /* Ethernet Address */ + Cf = 0x03, /* Compatibility Field */ + Pba = 0x08, /* Printed Board Assembly number */ + Icw1 = 0x0A, /* Initialization Control Word 1 */ + Sid = 0x0B, /* Subsystem ID */ + Svid = 0x0C, /* Subsystem Vendor ID */ + Did = 0x0D, /* Device ID */ + Vid = 0x0E, /* Vendor ID */ + Icw2 = 0x0F, /* Initialization Control Word 2 */ +}; + +enum { /* Mdic */ + MDIdMASK = 0x0000FFFF, /* Data */ + MDIdSHIFT = 0, + MDIrMASK = 0x001F0000, /* PHY Register Address */ + MDIrSHIFT = 16, + MDIpMASK = 0x03E00000, /* PHY Address */ + MDIpSHIFT = 21, + MDIwop = 0x04000000, /* Write Operation */ + MDIrop = 0x08000000, /* Read Operation */ + MDIready = 0x10000000, /* End of Transaction */ + MDIie = 0x20000000, /* Interrupt Enable */ + MDIe = 0x40000000, /* Error */ +}; + +enum { /* Icr, Ics, Ims, Imc */ + Txdw = 0x00000001, /* Transmit Descriptor Written Back */ + Txqe = 0x00000002, /* Transmit Queue Empty */ + Lsc = 0x00000004, /* Link Status Change */ + Rxseq = 0x00000008, /* Receive Sequence Error */ + Rxdmt0 = 0x00000010, /* Rd Minimum Threshold Reached */ + Rxo = 0x00000040, /* Receiver Overrun */ + Rxt0 = 0x00000080, /* Receiver Timer Interrupt */ + Mdac = 0x00000200, /* MDIO Access Completed */ + Rxcfg = 0x00000400, /* Receiving /C/ ordered sets */ + Gpi0 = 0x00000800, /* General Purpose Interrupts */ + Gpi1 = 0x00001000, + Gpi2 = 0x00002000, + Gpi3 = 0x00004000, +}; + +/* + * The Mdic register isn't implemented on the 82543GC, + * the software defined pins are used instead. + * These definitions work for the Intel PRO/1000 T Server Adapter. + * The direction pin bits are read from the EEPROM. + */ +enum { + Mdd = ((1<<2)<nic+((r)/4))) +#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v)) + +static Ctlr* igbectlrhead; +static Ctlr* igbectlrtail; + +static Lock igberblock; /* free receive Blocks */ +static Block* igberbpool; + +static char* statistics[Nstatistics] = { + "CRC Error", + "Alignment Error", + "Symbol Error", + "RX Error", + "Missed Packets", + "Single Collision", + "Excessive Collisions", + "Multiple Collision", + "Late Collisions", + nil, + "Collision", + "Transmit Underrun", + "Defer", + "Transmit - No CRS", + "Sequence Error", + "Carrier Extension Error", + "Receive Error Length", + nil, + "XON Received", + "XON Transmitted", + "XOFF Received", + "XOFF Transmitted", + "FC Received Unsupported", + "Packets Received (64 Bytes)", + "Packets Received (65-127 Bytes)", + "Packets Received (128-255 Bytes)", + "Packets Received (256-511 Bytes)", + "Packets Received (512-1023 Bytes)", + "Packets Received (1024-1522 Bytes)", + "Good Packets Received", + "Broadcast Packets Received", + "Multicast Packets Received", + "Good Packets Transmitted", + nil, + "Good Octets Received", + nil, + "Good Octets Transmitted", + nil, + nil, + nil, + "Receive No Buffers", + "Receive Undersize", + "Receive Fragment", + "Receive Oversize", + "Receive Jabber", + nil, + nil, + nil, + "Total Octets Received", + nil, + "Total Octets Transmitted", + nil, + "Total Packets Received", + "Total Packets Transmitted", + "Packets Transmitted (64 Bytes)", + "Packets Transmitted (65-127 Bytes)", + "Packets Transmitted (128-255 Bytes)", + "Packets Transmitted (256-511 Bytes)", + "Packets Transmitted (512-1023 Bytes)", + "Packets Transmitted (1024-1522 Bytes)", + "Multicast Packets Transmitted", + "Broadcast Packets Transmitted", + "TCP Segmentation Context Transmitted", + "TCP Segmentation Context Fail", +}; + +static long +igbeifstat(Ether* edev, void* a, long n, ulong offset) +{ + Ctlr *ctlr; + char *p, *s; + int i, l, r; + uvlong tuvl, ruvl; + + ctlr = edev->ctlr; + qlock(&ctlr->slock); + p = malloc(2*READSTR); + l = 0; + for(i = 0; i < Nstatistics; i++){ + r = csr32r(ctlr, Statistics+i*4); + if((s = statistics[i]) == nil) + continue; + switch(i){ + case Gorcl: + case Gotcl: + case Torl: + case Totl: + ruvl = r; + ruvl += ((uvlong)csr32r(ctlr, Statistics+(i+1)*4))<<32; + tuvl = ruvl; + tuvl += ctlr->statistics[i]; + tuvl += ((uvlong)ctlr->statistics[i+1])<<32; + if(tuvl == 0) + continue; + ctlr->statistics[i] = tuvl; + ctlr->statistics[i+1] = tuvl>>32; + l += snprint(p+l, 2*READSTR-l, "%s: %llud %llud\n", + s, tuvl, ruvl); + i++; + break; + + default: + ctlr->statistics[i] += r; + if(ctlr->statistics[i] == 0) + continue; + l += snprint(p+l, 2*READSTR-l, "%s: %ud %ud\n", + s, ctlr->statistics[i], r); + break; + } + } + + l += snprint(p+l, 2*READSTR-l, "lintr: %ud %ud\n", + ctlr->lintr, ctlr->lsleep); + l += snprint(p+l, 2*READSTR-l, "rintr: %ud %ud\n", + ctlr->rintr, ctlr->rsleep); + l += snprint(p+l, 2*READSTR-l, "tintr: %ud %ud\n", + ctlr->tintr, ctlr->txdw); + l += snprint(p+l, 2*READSTR-l, "ixcs: %ud %ud %ud\n", + ctlr->ixsm, ctlr->ipcs, ctlr->tcpcs); + l += snprint(p+l, 2*READSTR-l, "rdtr: %ud\n", ctlr->rdtr); + l += snprint(p+l, 2*READSTR-l, "Ctrlext: %08x\n", csr32r(ctlr, Ctrlext)); + + l += snprint(p+l, 2*READSTR-l, "eeprom:"); + for(i = 0; i < 0x40; i++){ + if(i && ((i & 0x07) == 0)) + l += snprint(p+l, 2*READSTR-l, "\n "); + l += snprint(p+l, 2*READSTR-l, " %4.4uX", ctlr->eeprom[i]); + } + l += snprint(p+l, 2*READSTR-l, "\n"); + + if(ctlr->mii != nil && ctlr->mii->curphy != nil){ + l += snprint(p+l, 2*READSTR, "phy: "); + for(i = 0; i < NMiiPhyr; i++){ + if(i && ((i & 0x07) == 0)) + l += snprint(p+l, 2*READSTR-l, "\n "); + r = miimir(ctlr->mii, i); + l += snprint(p+l, 2*READSTR-l, " %4.4uX", r); + } + snprint(p+l, 2*READSTR-l, "\n"); + } + n = readstr(offset, a, n, p); + free(p); + qunlock(&ctlr->slock); + + return n; +} + +enum { + CMrdtr, +}; + +static Cmdtab igbectlmsg[] = { + CMrdtr, "rdtr", 2, +}; + +static long +igbectl(Ether* edev, void* buf, long n) +{ + int v; + char *p; + Ctlr *ctlr; + Cmdbuf *cb; + Cmdtab *ct; + + if((ctlr = edev->ctlr) == nil) + error(Enonexist); + + cb = parsecmd(buf, n); + if(waserror()){ + free(cb); + nexterror(); + } + + ct = lookupcmd(cb, igbectlmsg, nelem(igbectlmsg)); + switch(ct->index){ + case CMrdtr: + v = strtol(cb->f[1], &p, 0); + if(v < 0 || p == cb->f[1] || v > 0xFFFF) + error(Ebadarg); + ctlr->rdtr = v;; + csr32w(ctlr, Rdtr, Fpd|v); + break; + } + free(cb); + poperror(); + + return n; +} + +static void +igbepromiscuous(void* arg, int on) +{ + int rctl; + Ctlr *ctlr; + Ether *edev; + + edev = arg; + ctlr = edev->ctlr; + + rctl = csr32r(ctlr, Rctl); + rctl &= ~MoMASK; + rctl |= Mo47b36; + if(on) + rctl |= Upe|Mpe; + else + rctl &= ~(Upe|Mpe); + csr32w(ctlr, Rctl, rctl); +} + +static void +igbemulticast(void* arg, uchar* addr, int on) +{ + int bit, x; + Ctlr *ctlr; + Ether *edev; + + edev = arg; + ctlr = edev->ctlr; + + x = addr[5]>>1; + bit = ((addr[5] & 1)<<4)|(addr[4]>>4); + if(on) + ctlr->mta[x] |= 1<mta[x] &= ~(1<mta[x]); +} + +static Block* +igberballoc(void) +{ + Block *bp; + + ilock(&igberblock); + if((bp = igberbpool) != nil){ + igberbpool = bp->next; + bp->next = nil; + } + iunlock(&igberblock); + + return bp; +} + +static void +igberbfree(Block* bp) +{ + bp->rp = bp->lim - Rbsz; + bp->wp = bp->rp; + + ilock(&igberblock); + bp->next = igberbpool; + igberbpool = bp; + iunlock(&igberblock); +} + +static void +igbeim(Ctlr* ctlr, int im) +{ + ilock(&ctlr->imlock); + ctlr->im |= im; + csr32w(ctlr, Ims, ctlr->im); + iunlock(&ctlr->imlock); +} + +static int +igbelim(void* ctlr) +{ + return ((Ctlr*)ctlr)->lim != 0; +} + +static void +igbelproc(void* arg) +{ + Ctlr *ctlr; + Ether *edev; + MiiPhy *phy; + int ctrl, r; + + edev = arg; + ctlr = edev->ctlr; + for(;;){ + if(ctlr->mii == nil || ctlr->mii->curphy == nil) + continue; + + /* + * To do: + * logic to manage status change, + * this is incomplete but should work + * one time to set up the hardware. + * + * MiiPhy.speed, etc. should be in Mii. + */ + if(miistatus(ctlr->mii) < 0) + //continue; + goto enable; + + phy = ctlr->mii->curphy; + ctrl = csr32r(ctlr, Ctrl); + + switch(ctlr->id){ + case i82543gc: + case i82544ei: + default: + if(!(ctrl & Asde)){ + ctrl &= ~(SspeedMASK|Ilos|Fd); + ctrl |= Frcdplx|Frcspd; + if(phy->speed == 1000) + ctrl |= Sspeed1000; + else if(phy->speed == 100) + ctrl |= Sspeed100; + if(phy->fd) + ctrl |= Fd; + } + break; + + case i82540em: + case i82540eplp: + case i82547gi: + case i82541gi: + case i82541pi: + break; + } + + /* + * Collision Distance. + */ + r = csr32r(ctlr, Tctl); + r &= ~ColdMASK; + if(phy->fd) + r |= 64<rfc) + ctrl |= Rfce; + if(phy->tfc) + ctrl |= Tfce; + csr32w(ctlr, Ctrl, ctrl); + +enable: + ctlr->lim = 0; + igbeim(ctlr, Lsc); + + ctlr->lsleep++; + sleep(&ctlr->lrendez, igbelim, ctlr); + } +} + +static void +igbetxinit(Ctlr* ctlr) +{ + int i, r; + Block *bp; + + csr32w(ctlr, Tctl, (0x0F<id){ + default: + r = 6; + break; + case i82543gc: + case i82544ei: + case i82547ei: + case i82540em: + case i82540eplp: + case i82541gi: + case i82541pi: + case i82545gmc: + case i82546gb: + case i82546eb: + case i82547gi: + r = 8; + break; + } + csr32w(ctlr, Tipg, (6<<20)|(8<<10)|r); + csr32w(ctlr, Ait, 0); + csr32w(ctlr, Txdmac, 0); + + csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba)); + csr32w(ctlr, Tdbah, 0); + csr32w(ctlr, Tdlen, ctlr->ntd*sizeof(Td)); + ctlr->tdh = PREV(0, ctlr->ntd); + csr32w(ctlr, Tdh, 0); + ctlr->tdt = 0; + csr32w(ctlr, Tdt, 0); + + for(i = 0; i < ctlr->ntd; i++){ + if((bp = ctlr->tb[i]) != nil){ + ctlr->tb[i] = nil; + freeb(bp); + } + memset(&ctlr->tdba[i], 0, sizeof(Td)); + } + ctlr->tdfree = ctlr->ntd; + + csr32w(ctlr, Tidv, 128); + r = (4<id){ + default: + break; + case i82540em: + case i82540eplp: + case i82547gi: + case i82545gmc: + case i82546gb: + case i82546eb: + case i82541gi: + case i82541pi: + r = csr32r(ctlr, Txdctl); + r &= ~WthreshMASK; + r |= Gran|(4<ctlr; + + ilock(&ctlr->tlock); + + /* + * Free any completed packets + */ + tdh = ctlr->tdh; + while(NEXT(tdh, ctlr->ntd) != csr32r(ctlr, Tdh)){ + if((bp = ctlr->tb[tdh]) != nil){ + ctlr->tb[tdh] = nil; + freeb(bp); + } + memset(&ctlr->tdba[tdh], 0, sizeof(Td)); + tdh = NEXT(tdh, ctlr->ntd); + } + ctlr->tdh = tdh; + + /* + * Try to fill the ring back up. + */ + tdt = ctlr->tdt; + while(NEXT(tdt, ctlr->ntd) != tdh){ + if((bp = qget(edev->oq)) == nil) + break; + td = &ctlr->tdba[tdt]; + td->addr[0] = PCIWADDR(bp->rp); + td->control = ((BLEN(bp) & LenMASK)<control |= Dext|Ifcs|Teop|DtypeDD; + ctlr->tb[tdt] = bp; + tdt = NEXT(tdt, ctlr->ntd); + if(NEXT(tdt, ctlr->ntd) == tdh){ + td->control |= Rs; + ctlr->txdw++; + ctlr->tdt = tdt; + csr32w(ctlr, Tdt, tdt); + igbeim(ctlr, Txdw); + break; + } + ctlr->tdt = tdt; + csr32w(ctlr, Tdt, tdt); + } + + iunlock(&ctlr->tlock); +} + +static void +igbereplenish(Ctlr* ctlr) +{ + Rd *rd; + int rdt; + Block *bp; + + rdt = ctlr->rdt; + while(NEXT(rdt, ctlr->nrd) != ctlr->rdh){ + rd = &ctlr->rdba[rdt]; + if(ctlr->rb[rdt] == nil){ + bp = igberballoc(); + if(bp == nil){ + iprint("no available buffers\n"); + break; + } + ctlr->rb[rdt] = bp; + rd->addr[0] = PCIWADDR(bp->rp); + rd->addr[1] = 0; + } + coherence(); + rd->status = 0; + rdt = NEXT(rdt, ctlr->nrd); + ctlr->rdfree++; + } + ctlr->rdt = rdt; + csr32w(ctlr, Rdt, rdt); +} + +static void +igberxinit(Ctlr* ctlr) +{ + int i; + Block *bp; + + csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF); + + csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba)); + csr32w(ctlr, Rdbah, 0); + csr32w(ctlr, Rdlen, ctlr->nrd*sizeof(Rd)); + ctlr->rdh = 0; + csr32w(ctlr, Rdh, 0); + ctlr->rdt = 0; + csr32w(ctlr, Rdt, 0); + ctlr->rdtr = 0; + csr32w(ctlr, Rdtr, Fpd|0); + + for(i = 0; i < ctlr->nrd; i++){ + if((bp = ctlr->rb[i]) != nil){ + ctlr->rb[i] = nil; + freeb(bp); + } + } + igbereplenish(ctlr); + + switch(ctlr->id){ + case i82540em: + case i82540eplp: + case i82541gi: + case i82541pi: + case i82545gmc: + case i82546gb: + case i82546eb: + case i82547gi: + csr32w(ctlr, Radv, 64); + break; + } + csr32w(ctlr, Rxdctl, (8<rim != 0; +} + +static void +igberproc(void* arg) +{ + Rd *rd; + Block *bp; + Ctlr *ctlr; + int r, rdh; + Ether *edev; + + edev = arg; + ctlr = edev->ctlr; + + igberxinit(ctlr); + r = csr32r(ctlr, Rctl); + r |= Ren; + csr32w(ctlr, Rctl, r); + + for(;;){ + ctlr->rim = 0; + igbeim(ctlr, Rxt0|Rxo|Rxdmt0|Rxseq); + ctlr->rsleep++; + sleep(&ctlr->rrendez, igberim, ctlr); + + rdh = ctlr->rdh; + for(;;){ + rd = &ctlr->rdba[rdh]; + + if(!(rd->status & Rdd)) + break; + + /* + * Accept eop packets with no errors. + * With no errors and the Ixsm bit set, + * the descriptor status Tpcs and Ipcs bits give + * an indication of whether the checksums were + * calculated and valid. + */ + if((rd->status & Reop) && rd->errors == 0){ + bp = ctlr->rb[rdh]; + ctlr->rb[rdh] = nil; + bp->wp += rd->length; + bp->next = nil; + if(!(rd->status & Ixsm)){ + ctlr->ixsm++; + if(rd->status & Ipcs){ + /* + * IP checksum calculated + * (and valid as errors == 0). + */ + ctlr->ipcs++; + bp->flag |= Bipck; + } + if(rd->status & Tcpcs){ + /* + * TCP/UDP checksum calculated + * (and valid as errors == 0). + */ + ctlr->tcpcs++; + bp->flag |= Btcpck|Budpck; + } + bp->checksum = rd->checksum; + bp->flag |= Bpktck; + } + etheriq(edev, bp, 1); + } + else if(ctlr->rb[rdh] != nil){ + freeb(ctlr->rb[rdh]); + ctlr->rb[rdh] = nil; + } + + memset(rd, 0, sizeof(Rd)); + coherence(); + ctlr->rdfree--; + rdh = NEXT(rdh, ctlr->nrd); + } + ctlr->rdh = rdh; + + if(ctlr->rdfree < ctlr->nrd/2 || (ctlr->rim & Rxdmt0)) + igbereplenish(ctlr); + } +} + +static void +igbeattach(Ether* edev) +{ + Block *bp; + Ctlr *ctlr; + char name[KNAMELEN]; + + ctlr = edev->ctlr; + qlock(&ctlr->alock); + if(ctlr->alloc != nil){ + qunlock(&ctlr->alock); + return; + } + + ctlr->nrd = ROUND(Nrd, 8); + ctlr->ntd = ROUND(Ntd, 8); + ctlr->alloc = malloc(ctlr->nrd*sizeof(Rd)+ctlr->ntd*sizeof(Td) + 127); + if(ctlr->alloc == nil){ + qunlock(&ctlr->alock); + return; + } + ctlr->rdba = (Rd*)ROUNDUP((ulong)ctlr->alloc, 128); + ctlr->tdba = (Td*)(ctlr->rdba+ctlr->nrd); + + ctlr->rb = malloc(ctlr->nrd*sizeof(Block*)); + ctlr->tb = malloc(ctlr->ntd*sizeof(Block*)); + + if(waserror()){ + while(ctlr->nrb > 0){ + bp = igberballoc(); + bp->free = nil; + freeb(bp); + ctlr->nrb--; + } + free(ctlr->tb); + ctlr->tb = nil; + free(ctlr->rb); + ctlr->rb = nil; + free(ctlr->alloc); + ctlr->alloc = nil; + qunlock(&ctlr->alock); + nexterror(); + } + + for(ctlr->nrb = 0; ctlr->nrb < Nrb; ctlr->nrb++){ + if((bp = allocb(Rbsz)) == nil) + break; + bp->free = igberbfree; + freeb(bp); + } + + snprint(name, KNAMELEN, "#l%dlproc", edev->ctlrno); + kproc(name, igbelproc, edev, 0); + + snprint(name, KNAMELEN, "#l%drproc", edev->ctlrno); + kproc(name, igberproc, edev, 0); + + igbetxinit(ctlr); + + qunlock(&ctlr->alock); + poperror(); +} + +static void +igbeinterrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *edev; + int icr, im, txdw; + + edev = arg; + ctlr = edev->ctlr; + + ilock(&ctlr->imlock); + csr32w(ctlr, Imc, ~0); + im = ctlr->im; + txdw = 0; + + while((icr = csr32r(ctlr, Icr) & ctlr->im) != 0){ + if(icr & Lsc){ + im &= ~Lsc; + ctlr->lim = icr & Lsc; + wakeup(&ctlr->lrendez); + ctlr->lintr++; + } + if(icr & (Rxt0|Rxo|Rxdmt0|Rxseq)){ + im &= ~(Rxt0|Rxo|Rxdmt0|Rxseq); + ctlr->rim = icr & (Rxt0|Rxo|Rxdmt0|Rxseq); + wakeup(&ctlr->rrendez); + ctlr->rintr++; + } + if(icr & Txdw){ + im &= ~Txdw; + txdw++; + ctlr->tintr++; + } + } + + ctlr->im = im; + csr32w(ctlr, Ims, im); + iunlock(&ctlr->imlock); + + if(txdw) + igbetransmit(edev); +} + +static int +i82543mdior(Ctlr* ctlr, int n) +{ + int ctrl, data, i, r; + + /* + * Read n bits from the Management Data I/O Interface. + */ + ctrl = csr32r(ctlr, Ctrl); + r = (ctrl & ~Mddo)|Mdco; + data = 0; + for(i = n-1; i >= 0; i--){ + if(csr32r(ctlr, Ctrl) & Mdd) + data |= (1<= 0; i--){ + if(bits & (1<ctlr; + + /* + * MII Management Interface Read. + * + * Preamble; + * ST+OP+PHYAD+REGAD; + * TA + 16 data bits. + */ + i82543mdiow(ctlr, 0xFFFFFFFF, 32); + i82543mdiow(ctlr, 0x1800|(pa<<5)|ra, 14); + data = i82543mdior(ctlr, 18); + + if(data & 0x10000) + return -1; + + return data & 0xFFFF; +} + +static int +i82543miimiw(Mii* mii, int pa, int ra, int data) +{ + Ctlr *ctlr; + + ctlr = mii->ctlr; + + /* + * MII Management Interface Write. + * + * Preamble; + * ST+OP+PHYAD+REGAD+TA + 16 data bits; + * Z. + */ + i82543mdiow(ctlr, 0xFFFFFFFF, 32); + data &= 0xFFFF; + data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16); + i82543mdiow(ctlr, data, 32); + + return 0; +} + +static int +igbemiimir(Mii* mii, int pa, int ra) +{ + Ctlr *ctlr; + int mdic, timo; + + ctlr = mii->ctlr; + + csr32w(ctlr, Mdic, MDIrop|(pa<ctlr; + + data &= MDIdMASK; + csr32w(ctlr, Mdic, MDIwop|(pa<mii = malloc(sizeof(Mii))) == nil) + return -1; + ctlr->mii->ctlr = ctlr; + + ctrl = csr32r(ctlr, Ctrl); + ctrl |= Slu; + + switch(ctlr->id){ + case i82543gc: + ctrl |= Frcdplx|Frcspd; + csr32w(ctlr, Ctrl, ctrl); + + /* + * The reset pin direction (Mdro) should already + * be set from the EEPROM load. + * If it's not set this configuration is unexpected + * so bail. + */ + r = csr32r(ctlr, Ctrlext); + if(!(r & Mdro)) + return -1; + csr32w(ctlr, Ctrlext, r); + delay(20); + r = csr32r(ctlr, Ctrlext); + r &= ~Mdr; + csr32w(ctlr, Ctrlext, r); + delay(20); + r = csr32r(ctlr, Ctrlext); + r |= Mdr; + csr32w(ctlr, Ctrlext, r); + delay(20); + + ctlr->mii->mir = i82543miimir; + ctlr->mii->miw = i82543miimiw; + break; + case i82544ei: + case i82547ei: + case i82540em: + case i82540eplp: + case i82547gi: + case i82541gi: + case i82541pi: + case i82545gmc: + case i82546gb: + case i82546eb: + ctrl &= ~(Frcdplx|Frcspd); + csr32w(ctlr, Ctrl, ctrl); + ctlr->mii->mir = igbemiimir; + ctlr->mii->miw = igbemiimiw; + break; + default: + free(ctlr->mii); + ctlr->mii = nil; + return -1; + } + + if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){ + free(ctlr->mii); + ctlr->mii = nil; + return -1; + } + print("oui %X phyno %d\n", phy->oui, phy->phyno); + + /* + * 8254X-specific PHY registers not in 802.3: + * 0x10 PHY specific control + * 0x14 extended PHY specific control + * Set appropriate values then reset the PHY to have + * changes noted. + */ + switch(ctlr->id){ + case i82547gi: + case i82541gi: + case i82541pi: + case i82545gmc: + case i82546gb: + case i82546eb: + break; + default: + r = miimir(ctlr->mii, 16); + r |= 0x0800; /* assert CRS on Tx */ + r |= 0x0060; /* auto-crossover all speeds */ + r |= 0x0002; /* polarity reversal enabled */ + miimiw(ctlr->mii, 16, r); + + r = miimir(ctlr->mii, 20); + r |= 0x0070; /* +25MHz clock */ + r &= ~0x0F00; + r |= 0x0100; /* 1x downshift */ + miimiw(ctlr->mii, 20, r); + + miireset(ctlr->mii); + p = 0; + if(ctlr->txcw & TxcwPs) + p |= AnaP; + if(ctlr->txcw & TxcwAs) + p |= AnaAP; + miiane(ctlr->mii, ~0, p, ~0); + break; + } + return 0; +} + +static int +at93c46io(Ctlr* ctlr, char* op, int data) +{ + char *lp, *p; + int i, loop, eecd, r; + + eecd = csr32r(ctlr, Eecd); + + r = 0; + loop = -1; + lp = nil; + for(p = op; *p != '\0'; p++){ + switch(*p){ + default: + return -1; + case ' ': + continue; + case ':': /* start of loop */ + loop = strtol(p+1, &lp, 0)-1; + lp--; + if(p == lp) + loop = 7; + p = lp; + continue; + case ';': /* end of loop */ + if(lp == nil) + return -1; + loop--; + if(loop >= 0) + p = lp; + else + lp = nil; + continue; + case 'C': /* assert clock */ + eecd |= Sk; + break; + case 'c': /* deassert clock */ + eecd &= ~Sk; + break; + case 'D': /* next bit in 'data' byte */ + if(loop < 0) + return -1; + if(data & (1<= 0) + r |= (i<= 0) + return -1; + return r; +} + +static int +at93c46r(Ctlr* ctlr) +{ + ushort sum; + char rop[20]; + int addr, areq, bits, data, eecd, i; + + eecd = csr32r(ctlr, Eecd); + if(eecd & Spi){ + print("igbe: SPI EEPROM access not implemented\n"); + return 0; + } + if(eecd & Eesz256) + bits = 8; + else + bits = 6; + snprint(rop, sizeof(rop), "S :%dDCc;", bits+3); + + sum = 0; + + switch(ctlr->id){ + default: + areq = 0; + break; + case i82540em: + case i82540eplp: + case i82541gi: + case i82541pi: + case i82547gi: + case i82545gmc: + case i82546gb: + case i82546eb: + areq = 1; + csr32w(ctlr, Eecd, eecd|Areq); + for(i = 0; i < 1000; i++){ + if((eecd = csr32r(ctlr, Eecd)) & Agnt) + break; + microdelay(5); + } + if(!(eecd & Agnt)){ + print("igbe: not granted EEPROM access\n"); + goto release; + } + break; + } + + for(addr = 0; addr < 0x40; addr++){ + /* + * Read a word at address 'addr' from the Atmel AT93C46 + * 3-Wire Serial EEPROM or compatible. The EEPROM access is + * controlled by 4 bits in Eecd. See the AT93C46 datasheet + * for protocol details. + */ + if(at93c46io(ctlr, rop, (0x06<eeprom[addr] = data; + sum += data; + } + +release: + if(areq) + csr32w(ctlr, Eecd, eecd & ~Areq); + return sum; +} + +static int +igbedetach(Ctlr* ctlr) +{ + int r, timeo; + + /* + * Perform a device reset to get the chip back to the + * power-on state, followed by an EEPROM reset to read + * the defaults for some internal registers. + */ + csr32w(ctlr, Imc, ~0); + csr32w(ctlr, Rctl, 0); + csr32w(ctlr, Tctl, 0); + + delay(10); + + csr32w(ctlr, Ctrl, Devrst); + delay(1); + for(timeo = 0; timeo < 1000; timeo++){ + if(!(csr32r(ctlr, Ctrl) & Devrst)) + break; + delay(1); + } + if(csr32r(ctlr, Ctrl) & Devrst) + return -1; + r = csr32r(ctlr, Ctrlext); + csr32w(ctlr, Ctrlext, r|Eerst); + delay(1); + for(timeo = 0; timeo < 1000; timeo++){ + if(!(csr32r(ctlr, Ctrlext) & Eerst)) + break; + delay(1); + } + if(csr32r(ctlr, Ctrlext) & Eerst) + return -1; + + switch(ctlr->id){ + default: + break; + case i82540em: + case i82540eplp: + case i82541gi: + case i82541pi: + case i82547gi: + case i82545gmc: + case i82546gb: + case i82546eb: + r = csr32r(ctlr, Manc); + r &= ~Arpen; + csr32w(ctlr, Manc, r); + break; + } + + csr32w(ctlr, Imc, ~0); + delay(1); + for(timeo = 0; timeo < 1000; timeo++){ + if(!csr32r(ctlr, Icr)) + break; + delay(1); + } + if(csr32r(ctlr, Icr)) + return -1; + + return 0; +} + +static void +igbeshutdown(Ether* ether) +{ + igbedetach(ether->ctlr); +} + +static int +igbereset(Ctlr* ctlr) +{ + int ctrl, i, pause, r, swdpio, txcw; + + if(igbedetach(ctlr)) + return -1; + + /* + * Read the EEPROM, validate the checksum + * then get the device back to a power-on state. + */ + if((r = at93c46r(ctlr)) != 0xBABA){ + print("igbe: bad EEPROM checksum - 0x%4.4uX\n", r); + return -1; + } + + /* + * Snarf and set up the receive addresses. + * There are 16 addresses. The first should be the MAC address. + * The others are cleared and not marked valid (MS bit of Rah). + */ + if ((ctlr->id == i82546gb || ctlr->id == i82546eb) && BUSFNO(ctlr->pcidev->tbdf) == 1) + ctlr->eeprom[Ea+2] += 0x100; // second interface + for(i = Ea; i < Eaddrlen/2; i++){ + ctlr->ra[2*i] = ctlr->eeprom[i]; + ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8; + } + r = (ctlr->ra[3]<<24)|(ctlr->ra[2]<<16)|(ctlr->ra[1]<<8)|ctlr->ra[0]; + csr32w(ctlr, Ral, r); + r = 0x80000000|(ctlr->ra[5]<<8)|ctlr->ra[4]; + csr32w(ctlr, Rah, r); + for(i = 1; i < 16; i++){ + csr32w(ctlr, Ral+i*8, 0); + csr32w(ctlr, Rah+i*8, 0); + } + + /* + * Clear the Multicast Table Array. + * It's a 4096 bit vector accessed as 128 32-bit registers. + */ + memset(ctlr->mta, 0, sizeof(ctlr->mta)); + for(i = 0; i < 128; i++) + csr32w(ctlr, Mta+i*4, 0); + + /* + * Just in case the Eerst didn't load the defaults + * (doesn't appear to fully on the 8243GC), do it manually. + */ + if (ctlr->id == i82543gc) { // 82543 + txcw = csr32r(ctlr, Txcw); + txcw &= ~(TxcwAne|TxcwPauseMASK|TxcwFd); + ctrl = csr32r(ctlr, Ctrl); + ctrl &= ~(SwdpioloMASK|Frcspd|Ilos|Lrst|Fd); + + if(ctlr->eeprom[Icw1] & 0x0400){ + ctrl |= Fd; + txcw |= TxcwFd; + } + if(ctlr->eeprom[Icw1] & 0x0200) + ctrl |= Lrst; + if(ctlr->eeprom[Icw1] & 0x0010) + ctrl |= Ilos; + if(ctlr->eeprom[Icw1] & 0x0800) + ctrl |= Frcspd; + swdpio = (ctlr->eeprom[Icw1] & 0x01E0)>>5; + ctrl |= swdpio<eeprom[Icw2] & 0x00F0)>>4; + if(ctlr->eeprom[Icw1] & 0x1000) + ctrl |= Ips; + ctrl |= swdpio<eeprom[Icw2] & 0x0800) + txcw |= TxcwAne; + pause = (ctlr->eeprom[Icw2] & 0x3000)>>12; + txcw |= pause<fcrtl = 0x00002000; + ctlr->fcrth = 0x00004000; + txcw |= TxcwAs|TxcwPs; + break; + case 0: + ctlr->fcrtl = 0x00002000; + ctlr->fcrth = 0x00004000; + break; + case 2: + ctlr->fcrtl = 0; + ctlr->fcrth = 0; + txcw |= TxcwAs; + break; + } + ctlr->txcw = txcw; + csr32w(ctlr, Txcw, txcw); + } + + + /* + * Flow control - values from the datasheet. + */ + csr32w(ctlr, Fcal, 0x00C28001); + csr32w(ctlr, Fcah, 0x00000100); + csr32w(ctlr, Fct, 0x00008808); + csr32w(ctlr, Fcttv, 0x00000100); + + csr32w(ctlr, Fcrtl, ctlr->fcrtl); + csr32w(ctlr, Fcrth, ctlr->fcrth); + + if(!(csr32r(ctlr, Status) & Tbimode) && igbemii(ctlr) < 0) + return -1; + + return 0; +} + +static void +igbepci(void) +{ + int port, cls; + Pcidev *p; + Ctlr *ctlr; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + + switch((p->did<<16)|p->vid){ + default: + continue; + case i82543gc: + case i82544ei: + case i82547ei: + case i82540em: + case i82540eplp: + case i82547gi: + case i82541gi: + case i82541pi: + case i82545gmc: + case i82546gb: + case i82546eb: + break; + } + + port = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0); + if(port == 0){ + print("igbe: can't map %8.8luX\n", p->mem[0].bar); + continue; + } + cls = pcicfgr8(p, PciCLS); + switch(cls){ + default: + print("igbe: unexpected CLS - %d\n", cls*4); + break; + case 0x00: + case 0xFF: + print("igbe: unusable CLS\n"); + continue; + case 0x08: + case 0x10: + break; + } + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = port; + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + ctlr->cls = cls*4; + ctlr->nic = KADDR(ctlr->port); + + if(igbereset(ctlr)){ + free(ctlr); + continue; + } + pcisetbme(p); + + if(igbectlrhead != nil) + igbectlrtail->next = ctlr; + else + igbectlrhead = ctlr; + igbectlrtail = ctlr; + } +} + +static int +igbepnp(Ether* edev) +{ + Ctlr *ctlr; + + if(igbectlrhead == nil) + igbepci(); + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = igbectlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(edev->port == 0 || edev->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pcidev->intl; + edev->tbdf = ctlr->pcidev->tbdf; + edev->mbps = 1000; + memmove(edev->ea, ctlr->ra, Eaddrlen); + + /* + * Linkage to the generic ethernet driver. + */ + edev->attach = igbeattach; + edev->transmit = igbetransmit; + edev->interrupt = igbeinterrupt; + edev->ifstat = igbeifstat; + edev->ctl = igbectl; + + edev->arg = edev; + edev->promiscuous = igbepromiscuous; + edev->shutdown = igbeshutdown; + edev->multicast = igbemulticast; + + return 0; +} + +void +etherigbelink(void) +{ + addethercard("i82543", igbepnp); + addethercard("igbe", igbepnp); +} diff --git a/os/pc/etherrhine.c b/os/pc/etherrhine.c new file mode 100644 index 00000000..5d25a6f0 --- /dev/null +++ b/os/pc/etherrhine.c @@ -0,0 +1,734 @@ + /* + Via Rhine driver, written for VT6102. + Uses the ethermii to control PHY. + + Currently always copies on both, tx and rx. + rx side could be copy-free, and tx-side might be made + (almost) copy-free by using (possibly) two descriptors (if it allows + arbitrary tx lengths, which it should..): first for alignment and + second for rest of the frame. Rx-part should be worth doing. +*/ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" +#include "etherif.h" + +#include "ethermii.h" + +typedef struct Desc Desc; +typedef struct Ctlr Ctlr; + +enum { + Ntxd = 16, + Nrxd = 64, + Nwait = 50, + Ntxstats = 9, + Nrxstats = 8, + BIGSTR = 8192, +}; + +struct Desc { + ulong stat; + ulong size; + ulong addr; + ulong next; + char *buf; + ulong pad[3]; +}; + +struct Ctlr { + Pcidev *pci; + int attached; + int txused; + int txhead; + int txtail; + int rxtail; + ulong port; + + Mii mii; + + ulong txstats[Ntxstats]; + ulong rxstats[Nrxstats]; + + Desc *txd; /* wants to be aligned on 16-byte boundary */ + Desc *rxd; + + QLock attachlck; + Lock lock; +}; + +#define ior8(c, r) (inb((c)->port+(r))) +#define ior16(c, r) (ins((c)->port+(r))) +#define ior32(c, r) (inl((c)->port+(r))) +#define iow8(c, r, b) (outb((c)->port+(r), (int)(b))) +#define iow16(c, r, w) (outs((c)->port+(r), (ushort)(w))) +#define iow32(c, r, l) (outl((c)->port+(r), (ulong)(l))) + +enum Regs { + Eaddr = 0x0, + Rcr = 0x6, + Tcr = 0x7, + Cr = 0x8, + Isr = 0xc, + Imr = 0xe, + McastAddr = 0x10, + RxdAddr = 0x18, + TxdAddr = 0x1C, + Bcr = 0x6e, + RhineMiiPhy = 0x6C, + RhineMiiSr = 0x6D, + RhineMiiCr = 0x70, + RhineMiiAddr = 0x71, + RhineMiiData = 0x72, + Eecsr = 0x74, + ConfigB = 0x79, + ConfigD = 0x7B, + MiscCr = 0x80, + HwSticky = 0x83, + MiscIsr = 0x84, + MiscImr = 0x86, + WolCrSet = 0xA0, + WolCfgSet = 0xA1, + WolCgSet = 0xA3, + WolCrClr = 0xA4, + PwrCfgClr = 0xA5, + WolCgClr = 0xA7, +}; + +enum Rcrbits { + RxErrX = 1<<0, + RxSmall = 1<<1, + RxMcast = 1<<2, + RxBcast = 1<<3, + RxProm = 1<<4, + RxFifo64 = 0<<5, RxFifo32 = 1<<5, RxFifo128 = 2<<5, RxFifo256 = 3<<5, + RxFifo512 = 4<<5, RxFifo768 = 5<<5, RxFifo1024 = 6<<5, + RxFifoStoreForward = 7<<5, +}; + +enum Tcrbits { + TxLoopback0 = 1<<1, + TxLoopback1 = 1<<2, + TxBackoff = 1<<3, + TxFifo128 = 0<<5, TxFifo256 = 1<<5, TxFifo512 = 2<<5, TxFifo1024 = 3<<5, + TxFifoStoreForward = 7<<5, +}; + +enum Crbits { + Init = 1<<0, + Start = 1<<1, + Stop = 1<<2, + RxOn = 1<<3, + TxOn = 1<<4, + Tdmd = 1<<5, + Rdmd = 1<<6, + EarlyRx = 1<<8, + Reserved0 = 1<<9, + FullDuplex = 1<<10, + NoAutoPoll = 1<<11, + Reserved1 = 1<<12, + Tdmd1 = 1<<13, + Rdmd1 = 1<<14, + Reset = 1<<15, +}; + +enum Isrbits { + RxOk = 1<<0, + TxOk = 1<<1, + RxErr = 1<<2, + TxErr = 1<<3, + TxBufUdf = 1<<4, + RxBufLinkErr = 1<<5, + BusErr = 1<<6, + CrcOvf = 1<<7, + EarlyRxInt = 1<<8, + TxFifoUdf = 1<<9, + RxFifoOvf = 1<<10, + TxPktRace = 1<<11, + NoRxbuf = 1<<12, + TxCollision = 1<<13, + PortCh = 1<<14, + GPInt = 1<<15 +}; + +enum Bcrbits { + Dma32 = 0<<0, Dma64 = 1<<0, Dma128 = 2<<0, + Dma256 = 3<<0, Dma512 = 4<<0, Dma1024 = 5<<0, + DmaStoreForward = 7<<0, + DupRxFifo0 = 1<<3, DupRxFifo1 = 1<<4, DupRxFifo2 = 1<<5, + ExtraLed = 1<<6, + MediumSelect = 1<<7, + PollTimer0 = 1<<8, PollTimer1 = 1<<9, PollTimer2 = 1<<10, + DupTxFifo0 = 1<<11, DupTxFifo1 = 1<<12, DupTxFifo2 = 1<<13, +}; + +enum Eecsrbits { + EeAutoLoad = 1<<5, +}; + +enum MiscCrbits { + Timer0Enable= 1<<0, + Timer0Suspend = 1<<1, + HalfDuplexFlowControl = 1<<2, + FullDuplexFlowControl = 1<<3, + Timer1Enable = 1<<8, + ForceSoftReset = 1<<14, +}; + +enum HwStickybits { + StickyDS0 = 1<<0, + StickyDS1 = 1<<1, + WOLEna = 1<<2, + WOLStat = 1<<3, +}; + +enum WolCgbits { + PmeOvr = 1<<7, +}; + +enum Descbits { + OwnNic = 1<<31, /* stat */ + TxAbort = 1<<8, /* stat */ + TxError = 1<<15, /* stat */ + RxChainbuf = 1<<10, /* stat */ + RxChainStart = 1<<9, /* stat */ + RxChainEnd = 1<<8, /* stat */ + Chainbuf = 1<<15, /* size rx & tx*/ + TxDisableCrc = 1<<16, /* size */ + TxChainStart = 1<<21, /* size */ + TxChainEnd = 1<<22, /* size */ + TxInt = 1<<23, /* size */ +}; + +enum ConfigDbits { + BackoffOptional = 1<<0, + BackoffAMD = 1<<1, + BackoffDEC = 1<<2, + BackoffRandom = 1<<3, + PmccTestMode = 1<<4, + PciReadlineCap = 1<<5, + DiagMode = 1<<6, + MmioEnable = 1<<7, +}; + +enum ConfigBbits { + LatencyTimer = 1<<0, + WriteWaitState = 1<<1, + ReadWaitState = 1<<2, + RxArbit = 1<<3, + TxArbit = 1<<4, + NoMemReadline = 1<<5, + NoParity = 1<<6, + NoTxQueuing = 1<<7, +}; + +enum RhineMiiCrbits { + Mdc = 1<<0, + Mdi = 1<<1, + Mdo = 1<<2, + Mdout = 1<<3, + Mdpm = 1<<4, + Wcmd = 1<<5, + Rcmd = 1<<6, + Mauto = 1<<7, +}; + +enum RhineMiiSrbits { + Speed10M = 1<<0, + LinkFail = 1<<1, + PhyError = 1<<3, + DefaultPhy = 1<<4, + ResetPhy = 1<<7, +}; + +enum RhineMiiAddrbits { + Mdone = 1<<5, + Msrcen = 1<<6, + Midle = 1<<7, +}; + +static char * +txstatnames[Ntxstats] = { + "aborts (excess collisions)", + "out of window collisions", + "carrier sense losses", + "fifo underflows", + "invalid descriptor format or underflows", + "system errors", + "reserved", + "transmit errors", + "collisions", +}; + +static char * +rxstatnames[Nrxstats] = { + "receiver errors", + "crc errors", + "frame alignment errors", + "fifo overflows", + "long packets", + "run packets", + "system errors", + "buffer underflows", +}; + +static void +attach(Ether *edev) +{ + Ctlr *ctlr; + Desc *txd, *rxd, *td, *rd; + Mii *mi; + MiiPhy *phy; + int i, s; + + ctlr = edev->ctlr; + qlock(&ctlr->attachlck); + if (ctlr->attached == 0) { + txd = ctlr->txd; + rxd = ctlr->rxd; + for (i = 0; i < Ntxd; ++i) { + td = &txd[i]; + td->next = PCIWADDR(&txd[(i+1) % Ntxd]); + td->buf = xspanalloc(sizeof(Etherpkt)+4, 4, 0); + td->addr = PCIWADDR(td->buf); + td->size = 0; + coherence(); + td->stat = 0; + } + for (i = 0; i < Nrxd; ++i) { + rd = &rxd[i]; + rd->next = PCIWADDR(&rxd[(i+1) % Nrxd]); + rd->buf = xspanalloc(sizeof(Etherpkt)+4, 4, 0); + rd->addr = PCIWADDR(rd->buf); + rd->size = sizeof(Etherpkt)+4; + coherence(); + rd->stat = OwnNic; + } + + ctlr->txhead = ctlr->txtail = ctlr->rxtail = 0; + mi = &ctlr->mii; + miistatus(mi); + phy = mi->curphy; + s = splhi(); + iow32(ctlr, TxdAddr, PCIWADDR(&txd[0])); + iow32(ctlr, RxdAddr, PCIWADDR(&rxd[0])); + iow16(ctlr, Cr, (phy->fd ? FullDuplex : 0) | NoAutoPoll | TxOn | RxOn | Start | Rdmd); + iow16(ctlr, Isr, 0xFFFF); + iow16(ctlr, Imr, 0xFFFF); + iow8(ctlr, MiscIsr, 0xFF); + iow8(ctlr, MiscImr, ~(3<<5)); + splx(s); + } + ctlr->attached++; + qunlock(&ctlr->attachlck); +} + +static void +txstart(Ether *edev) +{ + Ctlr *ctlr; + Desc *txd, *td; + Block *b; + int i, txused, n; + ulong size; + + ctlr = edev->ctlr; + + txd = ctlr->txd; + i = ctlr->txhead; + txused = ctlr->txused; + n = 0; + while (txused < Ntxd) { + if ((b = qget(edev->oq)) == nil) + break; + + td = &txd[i]; + + size = BLEN(b); + memmove(td->buf, b->rp, size); + freeb(b); + td->size = size | TxChainStart | TxChainEnd | TxInt; /* could reduce number of ints here */ + coherence(); + td->stat = OwnNic; + i = (i + 1) % Ntxd; + txused++; + n++; + } + if (n) + iow16(ctlr, Cr, ior16(ctlr, Cr) | Tdmd); + + ctlr->txhead = i; + ctlr->txused = txused; +} + +static void +transmit(Ether *edev) +{ + Ctlr *ctlr; + ctlr = edev->ctlr; + ilock(&ctlr->lock); + txstart(edev); + iunlock(&ctlr->lock); +} + +static void +txcomplete(Ether *edev) +{ + Ctlr *ctlr; + Desc *txd, *td; + int i, txused, j; + ulong stat; + + ctlr = edev->ctlr; + txd = ctlr->txd; + txused = ctlr->txused; + i = ctlr->txtail; + while (txused > 0) { + td = &txd[i]; + stat = td->stat; + + if (stat & OwnNic) + break; + + ctlr->txstats[Ntxstats-1] += stat & 0xF; + for (j = 0; j < Ntxstats-1; ++j) + if (stat & (1<<(j+8))) + ctlr->txstats[j]++; + + i = (i + 1) % Ntxd; + txused--; + } + ctlr->txused = txused; + ctlr->txtail = i; + + if (txused <= Ntxd/2) + txstart(edev); +} + +static void +interrupt(Ureg *, void *arg) +{ + Ether *edev; + Ctlr *ctlr; + ushort isr, misr; + ulong stat; + Desc *rxd, *rd; + int i, n, j; + + edev = (Ether*)arg; + ctlr = edev->ctlr; + iow16(ctlr, Imr, 0); + isr = ior16(ctlr, Isr); + iow16(ctlr, Isr, 0xFFFF); + misr = ior16(ctlr, MiscIsr) & ~(3<<5); /* don't care about used defined ints */ + + if (isr & RxOk) { + Block *b; + int size; + rxd = ctlr->rxd; + i = ctlr->rxtail; + + n = 0; + while ((rxd[i].stat & OwnNic) == 0) { + rd = &rxd[i]; + stat = rd->stat; + for (j = 0; j < Nrxstats; ++j) + if (stat & (1<rxstats[j]++; + + if (stat & 0xFF) + iprint("rx: %lux\n", stat & 0xFF); + + size = ((rd->stat>>16) & 2047) - 4; + b = iallocb(sizeof(Etherpkt)); + memmove(b->wp, rd->buf, size); + b->wp += size; + etheriq(edev, b, 1); + rd->size = sizeof(Etherpkt)+4; + coherence(); + rd->stat = OwnNic; + i = (i + 1) % Nrxd; + n++; + } + if (n) + iow16(ctlr, Cr, ior16(ctlr, Cr) | Rdmd); + ctlr->rxtail = i; + isr &= ~RxOk; + } + if (isr & TxOk) { + txcomplete(edev); + isr &= ~TxOk; + } + if (isr | misr) + iprint("etherrhine: unhandled irq(s). isr:%x misr:%x\n", isr, misr); + + iow16(ctlr, Imr, 0xFFFF); +} + +static void +promiscuous(void *arg, int enable) +{ + Ether *edev; + Ctlr *ctlr; + + edev = arg; + ctlr = edev->ctlr; + ilock(&ctlr->lock); + iow8(ctlr, Rcr, (ior8(ctlr, Rcr) & ~(RxProm|RxBcast)) | + (enable ? RxProm : RxBcast)); + iunlock(&ctlr->lock); +} + +static int +miiread(Mii *mii, int phy, int reg) +{ + Ctlr *ctlr; + int n; + + ctlr = mii->ctlr; + + n = Nwait; + while (n-- && ior8(ctlr, RhineMiiCr) & (Rcmd | Wcmd)) + microdelay(1); + if (n == Nwait) + iprint("etherrhine: miiread: timeout\n"); + + iow8(ctlr, RhineMiiCr, 0); + iow8(ctlr, RhineMiiPhy, phy); + iow8(ctlr, RhineMiiAddr, reg); + iow8(ctlr, RhineMiiCr, Rcmd); + + n = Nwait; + while (n-- && ior8(ctlr, RhineMiiCr) & Rcmd) + microdelay(1); + if (n == Nwait) + iprint("etherrhine: miiread: timeout\n"); + + n = ior16(ctlr, RhineMiiData); + + return n; +} + +static int +miiwrite(Mii *mii, int phy, int reg, int data) +{ + int n; + Ctlr *ctlr; + + ctlr = mii->ctlr; + + n = Nwait; + while (n-- && ior8(ctlr, RhineMiiCr) & (Rcmd | Wcmd)) + microdelay(1); + if (n == Nwait) + iprint("etherrhine: miiwrite: timeout\n"); + + iow8(ctlr, RhineMiiCr, 0); + iow8(ctlr, RhineMiiPhy, phy); + iow8(ctlr, RhineMiiAddr, reg); + iow16(ctlr, RhineMiiData, data); + iow8(ctlr, RhineMiiCr, Wcmd); + + n = Nwait; + while (n-- && ior8(ctlr, RhineMiiCr) & Wcmd) + microdelay(1); + if (n == Nwait) + iprint("etherrhine: miiwrite: timeout\n"); + + return 0; +} + +/* multicast already on, don't need to do anything */ +static void +multicast(void*, uchar*, int) +{ +} + +static void +shutdown(Ether *edev) +{ + int i; + Ctlr *ctlr = edev->ctlr; + + ilock(&ctlr->lock); + pcisetbme(ctlr->pci); + + iow16(ctlr, Cr, ior16(ctlr, Cr) | Stop); + iow16(ctlr, Cr, ior16(ctlr, Cr) | Reset); + + for (i = 0; i < Nwait; ++i) { + if ((ior16(ctlr, Cr) & Reset) == 0) + break; + delay(5); + } + if (i == Nwait) + iprint("etherrhine: reset timeout\n"); + iunlock(&ctlr->lock); +} + +static void +init(Ether *edev) +{ + Ctlr *ctlr; + MiiPhy *phy; + int i; + + shutdown(edev); + + ctlr = edev->ctlr; + ilock(&ctlr->lock); + iow8(ctlr, Eecsr, ior8(ctlr, Eecsr) | EeAutoLoad); + for (i = 0; i < Nwait; ++i) { + if ((ior8(ctlr, Eecsr) & EeAutoLoad) == 0) + break; + delay(5); + } + if (i == Nwait) + iprint("etherrhine: eeprom autoload timeout\n"); + + for (i = 0; i < Eaddrlen; ++i) + edev->ea[i] = ior8(ctlr, Eaddr + i); + + ctlr->mii.mir = miiread; + ctlr->mii.miw = miiwrite; + ctlr->mii.ctlr = ctlr; + + if(mii(&ctlr->mii, ~0) == 0 || ctlr->mii.curphy == nil){ + iprint("etherrhine: init mii failure\n"); + return; + } + for (i = 0; i < NMiiPhy; ++i) + if (ctlr->mii.phy[i]) + if (ctlr->mii.phy[i]->oui != 0xFFFFF) + ctlr->mii.curphy = ctlr->mii.phy[i]; + + miistatus(&ctlr->mii); + phy = ctlr->mii.curphy; + edev->mbps = phy->speed; + + iow16(ctlr, Imr, 0); + iow16(ctlr, Cr, ior16(ctlr, Cr) | Stop); + iow8(ctlr, Rcr, ior8(ctlr, Rcr) | RxMcast); + + iunlock(&ctlr->lock); +} + +static Pcidev * +rhinematch(ulong) +{ + static int nrhines = 0; + int nfound = 0; + Pcidev *p = nil; + + while (p = pcimatch(p, 0x1106, 0)) + if (p->did == 0x3065) + if (++nfound > nrhines) { + nrhines++; + break; + } + return p; +} +static long +ifstat(Ether* edev, void* a, long n, ulong offset) +{ + int l = 0, i; + char *p; + Ctlr *ctlr; + ctlr = edev->ctlr; + p = malloc(BIGSTR); + + for (i = 0; i < Ntxstats; ++i) + if (txstatnames[i]) + l += snprint(p+l, BIGSTR - l, "tx: %s: %lud\n", txstatnames[i], ctlr->txstats[i]); + + for (i = 0; i < Nrxstats; ++i) + if (rxstatnames[i]) + l += snprint(p+l, BIGSTR - l, "rx: %s: %lud\n", rxstatnames[i], ctlr->rxstats[i]); + +/* + for (i = 0; i < NMiiPhyr; ++i) { + if ((i % 8) == 0) + l += snprint(p + l, BIGSTR - l, "\nmii 0x%02x:", i); + reg=miimir(&ctlr->mii, i); + reg=miimir(&ctlr->mii, i); + l += snprint(p + l, BIGSTR - l, " %4ux", reg); + } + + for (i = 0; i < 0x100; i+=1) { + if ((i % 16) == 0) + l += snprint(p + l, BIGSTR - l, "\nreg 0x%02x:", i); + else if ((i % 2) == 0) + l += snprint(p + l, BIGSTR - l, " "); + reg=ior8(ctlr, i); + l += snprint(p + l, BIGSTR - l, "%02x", reg); + } + l += snprint(p + l, BIGSTR - l, " \n"); +*/ + + + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static int +pnp(Ether *edev) +{ + Pcidev *p; + Ctlr *ctlr; + ulong port; + ulong size; + + p = rhinematch(edev->port); + if (p == nil) + return -1; + + port = p->mem[0].bar & ~1; + size = p->mem[0].size; + if (ioalloc(port, size, 0, "rhine") < 0) { + print("etherrhine: couldn't allocate port %lud\n", port); + return -1; + } + + if ((ctlr = malloc(sizeof(Ctlr))) == nil) { + print("etherrhine: couldn't allocate memory for ctlr\n"); + return -1; + } + memset(ctlr, 0, sizeof(Ctlr)); + ctlr->txd = xspanalloc(sizeof(Desc) * Ntxd, 16, 0); + ctlr->rxd = xspanalloc(sizeof(Desc) * Nrxd, 16, 0); + + ctlr->pci = p; + ctlr->port = port; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = p->intl; + edev->tbdf = p->tbdf; + + init(edev); + + edev->interrupt = interrupt; + edev->arg = edev; + + edev->attach = attach; + edev->transmit = transmit; + edev->ifstat = ifstat; + edev->promiscuous = promiscuous; + edev->multicast = multicast; + edev->shutdown = shutdown; + return 0; +} + +void +etherrhinelink(void) +{ + addethercard("rhine", pnp); +} diff --git a/os/pc/ethersmc.c b/os/pc/ethersmc.c new file mode 100644 index 00000000..ddd3f729 --- /dev/null +++ b/os/pc/ethersmc.c @@ -0,0 +1,780 @@ +/* + * SMC EtherEZ (SMC91cXX chip) PCMCIA card support. + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" +#include "etherif.h" + +enum { + IoSize = 0x10, /* port pool size */ + TxTimeout = 150, +}; + +enum { /* PCMCIA related */ + TupleFunce = 0x22, + TfNodeId = 0x04, +}; + +enum { /* bank 0 registers */ + Tcr = 0x0000, /* transmit control */ + Eph = 0x0002, /* ethernet protocol handler */ + Rcr = 0x0004, /* receiver control */ + Counter = 0x0006, /* statistics counter */ + MemInfo = 0x0008, + MemCfg = 0x000A, +}; + +enum { /* bank 1 registers */ + Config = 0x0000, + BaseAddr = 0x0002, + Addr0 = 0x0004, /* ethernet address */ + Addr1 = 0x0006, + Addr2 = 0x0008, + General = 0x000A, + Control = 0x000C, +}; + +enum { /* bank 2 registers */ + MmuCmd = 0x0000, + PktNo = 0x0002, + AllocRes = 0x0003, + FifoPorts = 0x0004, + Pointer = 0x0006, + Data1 = 0x0008, + Interrupt = 0x000C, + IntrMask = 0x000D, +}; + +enum { /* bank 3 registers */ + Mcast0 = 0x0000, + Mcast2 = 0x0002, + Mcast4 = 0x0004, + Mcast6 = 0x0006, + Revision = 0x000A, +}; + +enum { + BankSelect = 0x000E /* bank select register */ +}; + +enum { + BsrMask = 0xFF00, /* mask for chip identification */ + BsrId = 0x3300, +}; + + +enum { /* Tcr values */ + TcrClear = 0x0000, + TcrEnable = 0x0001, /* enable transmit */ + TcrLoop = 0x0002, /* enable internal analogue loopback */ + TcrForceCol = 0x0004, /* force collision on next tx */ + TcrPadEn = 0x0080, /* pad short packets to 64 bytes */ + TcrNoCrc = 0x0100, /* do not append CRC */ + TcrMonCns = 0x0400, /* monitor carrier status */ + TcrFduplx = 0x0800, + TcrStpSqet = 0x1000, + TcrEphLoop = 0x2000, + TcrNormal = TcrEnable, +}; + +enum { /* Eph values */ + EphTxOk = 0x0001, + Eph1Col = 0x0002, /* single collision */ + EphMCol = 0x0004, /* multiple collisions */ + EphTxMcast = 0x0008, /* multicast transmit */ + Eph16Col = 0x0010, /* 16 collisions, tx disabled */ + EphSqet = 0x0020, /* SQE test failed, tx disabled */ + EphTxBcast = 0x0040, /* broadcast tx */ + EphDefr = 0x0080, /* deffered tx */ + EphLatCol = 0x0200, /* late collision, tx disabled */ + EphLostCarr = 0x0400, /* lost carrier, tx disabled */ + EphExcDefr = 0x0800, /* excessive defferals */ + EphCntRol = 0x1000, /* ECR counter(s) rolled over */ + EphRxOvrn = 0x2000, /* receiver overrun, packets dropped */ + EphLinkOk = 0x4000, + EphTxUnrn = 0x8000, /* tx underrun */ +}; + +enum { /* Rcr values */ + RcrClear = 0x0000, + RcrPromisc = 0x0002, + RcrAllMcast = 0x0004, + RcrEnable = 0x0100, + RcrStripCrc = 0x0200, + RcrSoftReset = 0x8000, + RcrNormal = RcrStripCrc | RcrEnable, +}; + +enum { /* Counter value masks */ + CntColMask = 0x000F, /* collisions */ + CntMColMask = 0x00F0, /* multiple collisions */ + CntDtxMask = 0x0F00, /* deferred transmits */ + CntExDtxMask = 0xF000, /* excessively deferred transmits */ + + CntColShr = 1, + CntMColShr = 4, + CntDtxShr = 8, +}; + +enum { /* MemInfo value masks */ + MirTotalMask = 0x00FF, + MirFreeMask = 0xFF00, +}; + +enum { /* Config values */ + CfgIrqSel0 = 0x0002, + CfgIrqSel1 = 0x0004, + CfgDisLink = 0x0040, /* disable 10BaseT link test */ + Cfg16Bit = 0x0080, + CfgAuiSelect = 0x0100, + CfgSetSqlch = 0x0200, + CfgFullStep = 0x0400, + CfgNoWait = 0x1000, + CfgMiiSelect = 0x8000, +}; + +enum { /* Control values */ + CtlStore = 0x0001, /* store to EEPROM */ + CtlReload = 0x0002, /* reload EEPROM into registers */ + CtlEeSelect = 0x0004, /* select registers for reload/store */ + CtlTeEnable = 0x0020, /* tx error detection via eph irq */ + CtlCrEnable = 0x0040, /* counter rollover via eph irq */ + CtlLeEnable = 0x0080, /* link error detection via eph irq*/ + CtlAutoRls = 0x0800, /* auto release mode */ + CtlPowerDn = 0x2000, +}; + +enum { /* MmuCmd values */ + McBusy = 0x0001, + McAlloc = 0x0020, /* | with number of 256 byte packets - 1 */ + McReset = 0x0040, + McRelease = 0x0080, /* dequeue (but not free) current rx packet */ + McFreePkt = 0x00A0, /* dequeue and free current rx packet */ + McEnqueue = 0x00C0, /* enqueue the packet for tx */ + McTxReset = 0x00E0, /* reset transmit queues */ +}; + +enum { /* AllocRes values */ + ArFailed = 0x80, +}; + +enum { /* FifoPorts values */ + FpTxEmpty = 0x0080, + FpRxEmpty = 0x8000, + FpTxMask = 0x007F, + FpRxMask = 0x7F00, +}; + +enum { /* Pointer values */ + PtrRead = 0x2000, + PtrAutoInc = 0x4000, + PtrRcv = 0x8000, +}; + +enum { /* Interrupt values */ + IntRcv = 0x0001, + IntTxError = 0x0002, + IntTxEmpty = 0x0004, + IntAlloc = 0x0008, + IntRxOvrn = 0x0010, + IntEph = 0x0020, +}; + +enum { /* transmit status bits */ + TsSuccess = 0x0001, + Ts16Col = 0x00A0, + TsLatCol = 0x0200, + TsLostCar = 0x0400, +}; + +enum { /* receive status bits */ + RsMcast = 0x0001, + RsTooShort = 0x0400, + RsTooLong = 0x0800, + RsOddFrame = 0x1000, + RsBadCrc = 0x2000, + RsAlgnErr = 0x8000, + RsError = RsAlgnErr | RsBadCrc | RsTooLong | RsTooShort, +}; + +enum { + RxLenMask = 0x07FF, /* significant rx len bits */ + HdrSize = 6, /* packet header length */ + PageSize = 256, /* page length */ +}; + +typedef struct Smc91xx Smc91xx; +struct Smc91xx { + Lock; + ushort rev; + int attached; + Block *txbp; + ulong txtime; + + ulong rovrn; + ulong lcar; + ulong col; + ulong scol; + ulong mcol; + ulong lcol; + ulong dfr; +}; + +#define SELECT_BANK(x) outs(port + BankSelect, x) + +static int +readnodeid(int slot, Ether* ether) +{ + uchar data[Eaddrlen + 1]; + int len; + + len = sizeof(data); + if (pcmcistuple(slot, TupleFunce, TfNodeId, data, len) != len) + return -1; + + if (data[0] != Eaddrlen) + return -1; + + memmove(ether->ea, &data[1], Eaddrlen); + return 0; +} + +static void +chipreset(Ether* ether) +{ + int port; + int i; + + port = ether->port; + + /* reset the chip */ + SELECT_BANK(0); + outs(port + Rcr, RcrSoftReset); + delay(1); + outs(port + Rcr, RcrClear); + outs(port + Tcr, TcrClear); + SELECT_BANK(1); + outs(port + Control, CtlAutoRls | CtlTeEnable | + CtlCrEnable); + + for(i = 0; i < 6; i++) { + outb(port + Addr0 + i, ether->ea[i]); + } + + SELECT_BANK(2); + outs(port + MmuCmd, McReset); +} + +static void +chipenable(Ether* ether) +{ + int port; + + port = ether->port; + SELECT_BANK(0); + outs(port + Tcr, TcrNormal); + outs(port + Rcr, RcrNormal); + SELECT_BANK(2); + outb(port + IntrMask, IntEph | IntRxOvrn | IntRcv); +} + +static void +attach(Ether *ether) +{ + Smc91xx* ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + + if (ctlr->attached) { + iunlock(ctlr); + return; + } + + chipenable(ether); + ctlr->attached = 1; + iunlock(ctlr); +} + +static void +txstart(Ether* ether) +{ + int port; + Smc91xx* ctlr; + Block* bp; + int len, npages; + int pno; + + /* assumes ctlr is locked and bank 2 is selected */ + /* leaves bank 2 selected on return */ + port = ether->port; + ctlr = ether->ctlr; + + if (ctlr->txbp) { + bp = ctlr->txbp; + ctlr->txbp = 0; + } else { + bp = qget(ether->oq); + if (bp == 0) + return; + + len = BLEN(bp); + npages = (len + HdrSize) / PageSize; + outs(port + MmuCmd, McAlloc | npages); + } + + pno = inb(port + AllocRes); + if (pno & ArFailed) { + outb(port + IntrMask, inb(port + IntrMask) | IntAlloc); + ctlr->txbp = bp; + ctlr->txtime = MACHP(0)->ticks; + return; + } + + outb(port + PktNo, pno); + outs(port + Pointer, PtrAutoInc); + + len = BLEN(bp); + outs(port + Data1, 0); + outb(port + Data1, (len + HdrSize) & 0xFF); + outb(port + Data1, (len + HdrSize) >> 8); + outss(port + Data1, bp->rp, len / 2); + if ((len & 1) == 0) { + outs(port + Data1, 0); + } else { + outb(port + Data1, bp->rp[len - 1]); + outb(port + Data1, 0x20); /* no info what 0x20 means */ + } + + outb(port + IntrMask, inb(port + IntrMask) | + IntTxError | IntTxEmpty); + + outs(port + MmuCmd, McEnqueue); + freeb(bp); +} + +static void +receive(Ether* ether) +{ + int port; + Block* bp; + int pktno, status, len; + + /* assumes ctlr is locked and bank 2 is selected */ + /* leaves bank 2 selected on return */ + port = ether->port; + + pktno = ins(port + FifoPorts); + if (pktno & FpRxEmpty) { + return; + } + + outs(port + Pointer, PtrRead | PtrRcv | PtrAutoInc); + status = ins(port + Data1); + len = ins(port + Data1) & RxLenMask - HdrSize; + + if (status & RsOddFrame) + len++; + + if ((status & RsError) || (bp = iallocb(len)) == 0) { + + if (status & RsAlgnErr) + ether->frames++; + if (status & (RsTooShort | RsTooLong)) + ether->buffs++; + if (status & RsBadCrc) + ether->crcs++; + + outs(port + MmuCmd, McRelease); + return; + } + + /* packet length is padded to word */ + inss(port + Data1, bp->rp, len / 2); + bp->wp = bp->rp + (len & ~1); + + if (len & 1) { + *bp->wp = inb(port + Data1); + bp->wp++; + } + + etheriq(ether, bp, 1); + ether->inpackets++; + outs(port + MmuCmd, McRelease); +} + +static void +txerror(Ether* ether) +{ + int port; + Smc91xx* ctlr; + int save_pkt; + int pktno, status; + + /* assumes ctlr is locked and bank 2 is selected */ + /* leaves bank 2 selected on return */ + port = ether->port; + ctlr = ether->ctlr; + + save_pkt = inb(port + PktNo); + + pktno = ins(port + FifoPorts) & FpTxMask; + outb(port + PktNo, pktno); + outs(port + Pointer, PtrAutoInc | PtrRead); + status = ins(port + Data1); + + if (status & TsLostCar) + ctlr->lcar++; + + if (status & TsLatCol) + ctlr->lcol++; + + if (status & Ts16Col) + ctlr->scol++; + + ether->oerrs++; + + SELECT_BANK(0); + outs(port + Tcr, ins(port + Tcr) | TcrEnable); + + SELECT_BANK(2); + outs(port + MmuCmd, McFreePkt); + + outb(port + PktNo, save_pkt); +} + +static void +eph_irq(Ether* ether) +{ + int port; + Smc91xx* ctlr; + ushort status; + int n; + + /* assumes ctlr is locked and bank 2 is selected */ + /* leaves bank 2 selected on return */ + port = ether->port; + ctlr = ether->ctlr; + + SELECT_BANK(0); + status = ins(port + Eph); + + if (status & EphCntRol) { + /* read the counter register even if we don't need it */ + /* otherwise we will keep getting this interrupt */ + n = ins(port + Counter); + ctlr->col += (n & CntColMask) >> CntColShr; + ctlr->mcol += (n & CntMColMask) >> CntMColShr; + ctlr->dfr += (n & CntDtxMask) >> CntDtxShr; + } + + /* if there was a transmit error, Tcr is disabled */ + outs(port + Tcr, ins(port + Tcr) | TcrEnable); + + /* clear a link error interrupt */ + SELECT_BANK(1); + outs(port + Control, CtlAutoRls); + outs(port + Control, CtlAutoRls | CtlTeEnable | CtlCrEnable); + + SELECT_BANK(2); +} + +static void +transmit(Ether* ether) +{ + Smc91xx* ctlr; + int port, n; + + ctlr = ether->ctlr; + port = ether->port; + ilock(ctlr); + + if (ctlr->txbp) { + n = TK2MS(MACHP(0)->ticks - ctlr->txtime); + if (n > TxTimeout) { + chipreset(ether); + chipenable(ether); + freeb(ctlr->txbp); + ctlr->txbp = 0; + } + iunlock(ctlr); + return; + } + + SELECT_BANK(2); + txstart(ether); + iunlock(ctlr); +} + +static void +interrupt(Ureg*, void *arg) +{ + int port; + Smc91xx* ctlr; + Ether* ether; + int save_bank; + int save_pointer; + int mask, status; + + ether = arg; + port = ether->port; + ctlr = ether->ctlr; + + ilock(ctlr); + save_bank = ins(port + BankSelect); + SELECT_BANK(2); + save_pointer = ins(port + Pointer); + + mask = inb(port + IntrMask); + outb(port + IntrMask, 0); + + while ((status = inb(port + Interrupt) & mask) != 0) { + if (status & IntRcv) { + receive(ether); + } + + if (status & IntTxError) { + txerror(ether); + } + + if (status & IntTxEmpty) { + outb(port + Interrupt, IntTxEmpty); + outb(port + IntrMask, mask & ~IntTxEmpty); + txstart(ether); + mask = inb(port + IntrMask); + } + + if (status & IntAlloc) { + outb(port + IntrMask, mask & ~IntAlloc); + txstart(ether);; + mask = inb(port + IntrMask); + } + + if (status & IntRxOvrn) { + ctlr->rovrn++; + ether->misses++; + outb(port + Interrupt,IntRxOvrn); + } + + if (status & IntEph) + eph_irq(ether); + } + + outb(port + IntrMask, mask); + outs(port + Pointer, save_pointer); + outs(port + BankSelect, save_bank); + iunlock(ctlr); +} + +static void +promiscuous(void* arg, int on) +{ + int port; + Smc91xx *ctlr; + Ether* ether; + ushort x; + + ether = arg; + port = ether->port; + ctlr = ether->ctlr; + + ilock(ctlr); + SELECT_BANK(0); + x = ins(port + Rcr); + if (on) + x |= RcrPromisc; + else + x &= ~RcrPromisc; + + outs(port + Rcr, x); + iunlock(ctlr); +} + +static void +multicast(void* arg, uchar *addr, int on) +{ + int port; + Smc91xx*ctlr; + Ether *ether; + ushort x; + + USED(addr, on); + + ether = arg; + port = ether->port; + ctlr = ether->ctlr; + ilock(ctlr); + + SELECT_BANK(0); + x = ins(port + Rcr); + + if (ether->nmaddr) + x |= RcrAllMcast; + else + x &= ~RcrAllMcast; + + outs(port + Rcr, x); + iunlock(ctlr); +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + static char *chiprev[] = { + [3] "92", + [5] "95", + [7] "100", + [8] "100-FD", + [9] "110", + }; + + Smc91xx* ctlr; + char* p; + int r, len; + char* s; + + if (n == 0) + return 0; + + ctlr = ether->ctlr; + p = malloc(READSTR); + + s = 0; + if (ctlr->rev > 0) { + r = ctlr->rev >> 4; + if (r < nelem(chiprev)) + s = chiprev[r]; + + if (r == 4) { + if ((ctlr->rev & 0x0F) >= 6) + s = "96"; + else + s = "94"; + } + } + + len = snprint(p, READSTR, "rev: 91c%s\n", (s) ? s : "???"); + len += snprint(p + len, READSTR - len, "rxovrn: %uld\n", ctlr->rovrn); + len += snprint(p + len, READSTR - len, "lcar: %uld\n", ctlr->lcar); + len += snprint(p + len, READSTR - len, "col: %uld\n", ctlr->col); + len += snprint(p + len, READSTR - len, "16col: %uld\n", ctlr->scol); + len += snprint(p + len, READSTR - len, "mcol: %uld\n", ctlr->mcol); + len += snprint(p + len, READSTR - len, "lcol: %uld\n", ctlr->lcol); + len += snprint(p + len, READSTR - len, "dfr: %uld\n", ctlr->dfr); + USED(len); + + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static int +reset(Ether* ether) +{ + int port; + int i, x; + char* type; + Smc91xx* ctlr; + int slot; + uchar ea[Eaddrlen]; + + if (ether->irq == 0) + ether->irq = 9; + + if (ether->port == 0) + ether->port = 0x100; + + type = "8020"; + for(i = 0; i < ether->nopt; i++) { + if (cistrncmp(ether->opt[i], "id=", 3)) + continue; + type = ðer->opt[i][3]; + break; + } + + if ((slot = pcmspecial(type, ether)) < 0) + return -1; + + if (ioalloc(ether->port, IoSize, 0, "smc91cXX") < 0) { + pcmspecialclose(slot); + return -1; + } + + ether->ctlr = malloc(sizeof(Smc91xx)); + ctlr = ether->ctlr; + if (ctlr == 0) { + iofree(ether->port); + pcmspecialclose(slot); + } + + ilock(ctlr); + ctlr->rev = 0; + ctlr->txbp = nil; + ctlr->attached = 0; + ctlr->rovrn = 0; + ctlr->lcar = 0; + ctlr->col = 0; + ctlr->scol = 0; + ctlr->mcol = 0; + ctlr->lcol = 0; + ctlr->dfr = 0; + + port = ether->port; + + SELECT_BANK(1); + if ((ins(port + BankSelect) & BsrMask) != BsrId) { + outs(port + Control, 0); /* try powering up the chip */ + delay(55); + } + + outs(port + Config, ins(port + Config) | Cfg16Bit); + x = ins(port + BaseAddr); + + if (((ins(port + BankSelect) & BsrMask) != BsrId) || + ((x >> 8) == (x & 0xFF))) { + iunlock(ctlr); + iofree(port); + pcmspecialclose(slot); + return -1; + } + + SELECT_BANK(3); + ctlr->rev = ins(port + Revision) & 0xFF; + + memset(ea, 0, Eaddrlen); + if (memcmp(ea, ether->ea, Eaddrlen) == 0) { + if (readnodeid(slot, ether) < 0) { + print("Smc91cXX: cannot find ethernet address\n"); + iunlock(ctlr); + iofree(port); + pcmspecialclose(slot); + return -1; + } + } + + chipreset(ether); + + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = ifstat; + ether->promiscuous = promiscuous; + ether->multicast = multicast; + ether->arg = ether; + iunlock(ctlr); + return 0; +} + +void +ethersmclink(void) +{ + addethercard("smc91cXX", reset); +} diff --git a/os/pc/etherwavelan.c b/os/pc/etherwavelan.c new file mode 100644 index 00000000..6fa9f250 --- /dev/null +++ b/os/pc/etherwavelan.c @@ -0,0 +1,196 @@ +/* Pci/pcmcia code for wavelan.c */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" +#include "etherif.h" + +#include "wavelan.h" + +static int +wavelanpcmciareset(Ether *ether) +{ + int i; + char *p; + Ctlr *ctlr; + + if((ctlr = malloc(sizeof(Ctlr))) == nil) + return -1; + + ilock(ctlr); + ctlr->ctlrno = ether->ctlrno; + + if (ether->port==0) + ether->port=WDfltIOB; + ctlr->iob = ether->port; + + if (ether->irq==0) + ether->irq=WDfltIRQ; + + if (ioalloc(ether->port,WIOLen,0,"wavelan")<0){ + // print("#l%d: port 0x%lx in use\n", + // ether->ctlrno, ether->port); + goto abort1; + } + + /* + * If id= is specified, card must match. Otherwise try generic. + */ + ctlr->slot = -1; + for(i=0; inopt; i++){ + if(cistrncmp(ether->opt[i], "id=", 3) == 0){ + if((ctlr->slot = pcmspecial(ðer->opt[i][3], ether)) < 0) + goto abort; + break; + } + } + if(ctlr->slot == -1){ + for (i=0; wavenames[i]; i++) + if((ctlr->slot = pcmspecial(wavenames[i], ether))>=0) + break; + if(!wavenames[i]){ + DEBUG("no wavelan found\n"); + goto abort; + } + } + + // DEBUG("#l%d: port=0x%lx irq=%ld\n", + // ether->ctlrno, ether->port, ether->irq); + + if(wavelanreset(ether, ctlr) < 0){ + abort: + iofree(ether->port); + abort1: + iunlock(ctlr); + free(ctlr); + ether->ctlr = nil; + return -1; + } + + for(i = 0; i < ether->nopt; i++){ + if(p = strchr(ether->opt[i], '=')) + *p = ' '; + w_option(ctlr, ether->opt[i], strlen(ether->opt[i])); + } + + iunlock(ctlr); + return 0; +} + +static struct { + int vid; + int did; +} wavelanpci[] = { + 0x1260, 0x3873, /* Intersil Prism2.5 */ +}; + +static Ctlr *ctlrhead, *ctlrtail; + +static void +wavelanpciscan(void) +{ + int i; + ulong pa; + Pcidev *p; + Ctlr *ctlr; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + for(i=0; ivid == wavelanpci[i].vid && p->did == wavelanpci[i].did) + break; + if(i==nelem(wavelanpci)) + continue; + + /* + * On the Prism, bar[0] is the memory-mapped register address (4KB), + */ + if(p->mem[0].size != 4096){ + print("wavelanpci: %.4ux %.4ux: unlikely mmio size\n", p->vid, p->did); + continue; + } + + ctlr = malloc(sizeof(Ctlr)); + ctlr->pcidev = p; + pa = upamalloc(p->mem[0].bar&~0xF, p->mem[0].size, 0); + if(pa == 0){ + print("wavelanpci: %.4ux %.4ux: upamalloc 0x%.8lux %d failed\n", p->vid, p->did, p->mem[0].bar&~0xF, p->mem[0].size); + free(ctlr); + continue; + } + ctlr->mmb = (ushort*)KADDR(pa); + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + pcisetbme(p); + } +} + +static int +wavelanpcireset(Ether *ether) +{ + int i; + char *p; + Ctlr *ctlr; + + if(ctlrhead == nil) + wavelanpciscan(); + + /* + * Allow plan9.ini to set vid, did? + */ + for(ctlr=ctlrhead; ctlr!=nil; ctlr=ctlr->next) + if(ctlr->active == 0) + break; + if(ctlr == nil) + return -1; + + ctlr->active = 1; + ilock(ctlr); + ether->irq = ctlr->pcidev->intl; + ether->tbdf = ctlr->pcidev->tbdf; + + /* + * Really hard reset. + */ + csr_outs(ctlr, WR_PciCor, 0x0080); + delay(250); + csr_outs(ctlr, WR_PciCor, 0x0000); + delay(500); + for(i=0; i<2*10; i++){ + if(!(csr_ins(ctlr, WR_Cmd)&WCmdBusy)) + break; + delay(100); + } + if(i >= 2*10) + print("wavelan pci %.4ux %.4ux: reset timeout %.4ux\n", + ctlr->pcidev->vid, ctlr->pcidev->did, csr_ins(ctlr, WR_Cmd)); + + if(wavelanreset(ether, ctlr) < 0){ + iunlock(ctlr); + ether->ctlr = nil; + return -1; + } + + for(i = 0; i < ether->nopt; i++){ + if(p = strchr(ether->opt[i], '=')) + *p = ' '; + w_option(ctlr, ether->opt[i], strlen(ether->opt[i])); + } + iunlock(ctlr); + return 0; +} + +void +etherwavelanlink(void) +{ + addethercard("wavelan", wavelanpcmciareset); + addethercard("wavelanpci", wavelanpcireset); +} diff --git a/os/pc/flashif.h b/os/pc/flashif.h new file mode 100644 index 00000000..b5391798 --- /dev/null +++ b/os/pc/flashif.h @@ -0,0 +1,82 @@ +typedef struct Flash Flash; +typedef struct Flashpart Flashpart; +typedef struct Flashregion Flashregion; + +/* + * logical partitions + */ +enum { + Maxflashpart = 8 +}; + +struct Flashpart { + char* name; + ulong start; + ulong end; +}; + +enum { + Maxflashregion = 8 +}; + +/* + * physical erase block regions + */ +struct Flashregion { + int n; /* number of blocks in region */ + ulong start; /* physical base address (allowing for banks) */ + ulong end; + ulong erasesize; +}; + +/* + * structure defining a flash memory card + */ +struct Flash { + QLock; /* interlock on flash operations */ + Flash* next; + + /* the following are filled in by devflash before Flash.reset called */ + char* name; + void* addr; + ulong size; + int (*reset)(Flash*); + + /* the following are filled in by the reset routine */ + int (*eraseall)(Flash*); + int (*erasezone)(Flash*, int); + int (*write)(Flash*, ulong, void*, long); /* writes of correct width and alignment */ + int (*suspend)(Flash*); + int (*resume)(Flash*); + + /* the following might be filled in by either archflashreset or the reset routine */ + int nr; + Flashregion regions[Maxflashregion]; + + uchar id; /* flash manufacturer ID */ + uchar devid; /* flash device ID */ + int width; /* bytes per flash line */ + int erasesize; /* size of erasable unit (accounting for width) */ + void* data; /* flash type routines' private storage, or nil */ + ulong unusable; /* bit mask of unusable sections */ + Flashpart part[Maxflashpart]; /* logical partitions */ + int protect; /* software protection */ +}; + +/* + * called by link routine of driver for specific flash type: arguments are + * conventional name for card type/model, and card driver's reset routine. + */ +void addflashcard(char*, int (*)(Flash*)); + +/* + * called by devflash.c:/^flashreset; if flash exists, + * sets type, address, and size in bytes of flash + * and returns 0; returns -1 if flash doesn't exist + */ +int archflashreset(char*, void**, long*); + +/* + * enable/disable write protect + */ +void archflashwp(int); diff --git a/os/pc/flashzpc.c b/os/pc/flashzpc.c new file mode 100644 index 00000000..c360f7bf --- /dev/null +++ b/os/pc/flashzpc.c @@ -0,0 +1,371 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "flashif.h" + +#define FLASHMEM 0xfff80000 +#define FLASHPGSZ 0x40000 +#define FLASHBKSZ (FLASHPGSZ>>2) +#define LOG2FPGSZ 18 +#define FLASHEND (FLASHMEM+FLASHPGSZ) +#define SYSREG0 0x78 +#define SYSREG1 0x878 + +/* Intel28F016SA flash memory family (8SA and (DD)32SA as well) in byte mode */ + +/* + * word mode does not work - a 2 byte write to a location results in the lower address + * byte being unchanged (4 byte writes are even stranger) and no indication of error. + * Perhaps the bridge is interfering with the address lines. + * Looks like the BIOS code doesn't use it either but that's not certain. + */ + +/* + * When port 0x78 bit 2 is set to 1 (flash device 1) + * 0xfff80000-0xfffbffff seems to be free but has dos block headers + * 0xfffc0000-0xfffdffff seems to be the DOS P: drive + * 0xfffe0000-0xffffffff is the BIOS + * When port 0x78 bit 2 is set to 0 (flash device 0) + * 0xfff80000-0xffffffff is a mixture of used and unused DOS blocks and apparently + * many copies of the BIOS + * + * In the absence of information from Ziatech and to preserve the BIOS and DOS sections, + * this driver only uses the first range for a total of 8 x 0x40000 = 2Mb + */ + +enum { + DQ7 = 0x80, + DQ6 = 0x40, + DQ5 = 0x20, + DQ4 = 0x10, + DQ3 = 0x08, + DQ2 = 0x04, + DQ1 = 0x02, + DQ0 = 0x01, +}; + +enum { + FLRDM = 0xFF, /* read */ + FLWTM = 0x10, /* write/program */ + FLCLR = 0x50, /* clear SR */ + FLBE1 = 0x20, /* block erase */ + FLBE2 = 0xD0, /* block erase */ + FLRSR = 0x70, /* read SR */ + FLDID = 0x90, /* read id */ +}; + +#define DPRINT if(0)print +#define EPRINT if(1)print + +static int +zpcwait(uchar *p, ulong ticks) +{ + uchar csr; + + ticks += m->ticks+1; + while((*p & DQ7) != DQ7){ + sched(); + if(m->ticks >= ticks){ + EPRINT("flash: timed out: %8.8lux\n", (ulong)*p); + return -1; + } + } + csr = *p; + if(csr & (DQ5|DQ4|DQ3)){ + EPRINT("flash: DQ5 error: %8.8lux %8.8lux\n", p, (ulong)csr); + return 0; + } + return 1; +} + +static int +eraseall(Flash *f) +{ + uchar r; + uchar *p; + int i, j, s; + + DPRINT("flash: erase all\n"); + for (i = 0; i < 8; i++) { /* page */ + /* set page */ + r = inb(SYSREG0); + r &= 0x8f; + r |= i<<4; + outb(SYSREG0, r); + p = (uchar *)f->addr; + for (j = 0; j < 4; j++) { /* block within page */ + DPRINT("erasing page %d block %d addr %lux\n", i, j, p); + s = splhi(); + *p = FLBE1; + *p = FLBE2; + splx(s); + if(zpcwait(p, MS2TK(16*1000)) <= 0){ + *p = FLCLR; /* clr SR */ + *p = FLRDM; /* read mode */ + f->unusable = ~0; + return -1; + } + *p = FLCLR; + *p = FLRDM; + p += FLASHPGSZ>>2; + } + } + return 0; +} + +static int +erasezone(Flash *f, int zone) +{ + uchar r; + uchar *p; + int s, pg, blk; + + DPRINT("flash: erase zone %d\n", zone); + if(zone & ~31) { + EPRINT("flash: bad erasezone %d\n", zone); + return -1; /* bad zone */ + } + pg = zone>>2; + blk = zone&3; + /* set page */ + r = inb(SYSREG0); + r &= 0x8f; + r |= pg<<4; + outb(SYSREG0, r); + p = (uchar *)f->addr + blk*(FLASHPGSZ>>2); + DPRINT("erasing zone %d pg %d blk %d addr %lux\n", zone, pg, blk, p); + s = splhi(); + *p = FLBE1; + *p = FLBE2; + splx(s); + if(zpcwait(p, MS2TK(8*1000)) <= 0){ + *p = FLCLR; + *p = FLRDM; /* reset */ + f->unusable |= 1<>LOG2FPGSZ; + o = offset&(FLASHPGSZ-1); + while (n > 0) { + if (pg < 0 || pg > 7) { + EPRINT("flash: bad read %ld %ld\n", offset, n); + return -1; + } + /* set page */ + r = inb(SYSREG0); + r &= 0x8f; + r |= pg<<4; + outb(SYSREG0, r); + if (o+n > FLASHPGSZ) + m = FLASHPGSZ-o; + else + m = n; + DPRINT("flash: read page %ld offset %lux buf %lux n %ld\n", pg, o, p-(uchar*)buf, m); + memmove(p, (uchar *)f->addr + o, m); + p += m; + n -= m; + pg++; + o = 0; + } + return 0; +} + +static int +writex(Flash *f, ulong offset, void *buf, long n) +{ + int i, s; + uchar r; + ulong pg, o; + long m; + uchar *a, *v = buf; + + DPRINT("flash: writex\n"); + pg = offset>>LOG2FPGSZ; + o = offset&(FLASHPGSZ-1); + while (n > 0) { + if (pg < 0 || pg > 7) { + EPRINT("flash: bad write %ld %ld\n", offset, n); + return -1; + } + /* set page */ + r = inb(SYSREG0); + r &= 0x8f; + r |= pg<<4; + outb(SYSREG0, r); + if (o+n > FLASHPGSZ) + m = FLASHPGSZ-o; + else + m = n; + a = (uchar *)f->addr + o; + DPRINT("flash: write page %ld offset %lux buf %lux n %ld\n", pg, o, v-(uchar*)buf, m); + for (i = 0; i < m; i++, v++, a++) { + if (~*a & *v) { + EPRINT("flash: bad write: %lux %lux -> %lux\n", (ulong)a, (ulong)*a, (ulong)*v); + return -1; + } + if (*a == *v) + continue; + s = splhi(); + *a = FLWTM; /* program */ + *a = *v; + splx(s); + microdelay(8); + if(zpcwait(a, 5) <= 0){ + *a = FLCLR; /* clr SR */ + *a = FLRDM; /* read mode */ + f->unusable = ~0; + return -1; + } + *a = FLCLR; + *a = FLRDM; + if (*a != *v) { + EPRINT("flash: write %lux %lux -> %lux failed\n", (ulong)a, (ulong)*a, (ulong)*v); + return -1; + } + } + n -= m; + pg++; + o = 0; + } + return 0; +} + +#ifdef ZERO +/* search the whole of flash */ +static void +flashsearch(Flash *f) +{ + int d, m, p, b, n, i; + uchar r, buf[64]; + + for (d = 0; d < 2; d++) { /* flash device */ + r = inb(SYSREG0); + r &= 0xfb; + r |= (d<<2); + outb(SYSREG0, r); + for (m = 0; m < 2; m++) { /* lower/upper mem */ + if (m == 0) + f->addr = (void *)FLASHMEM; + else + f->addr = (void *)FLASHEND; + for (p = 0; p < 8; p++) { /* page */ + for (b = 0; b < 4; b++) { /* block */ + n = readx(f, (4*p+b)*FLASHBKSZ, buf, 64); + if (n != 0) { + print("bad read in search %d\n", n); + goto end; + } + print("%d %d %d %d : ", d, m, p, b); + if (buf[0] == 0x5a && buf[1] == 0x54) { /* DOS block */ + n = 0; + for (i = 0; i < 64; i++) { + if (buf[i] == 0xff) + n++; + } + if (n == 64-28) + print("un"); + print("used dos\n"); + } + else if (buf[0] == 0x55 && buf[1] == 0xaa) + print("bios start\n"); + else + print("bios ?\n"); + } + } + } + } +end: + r = inb(SYSREG0); + r |= 4; + outb(SYSREG0, r); + f->addr = (void *)FLASHMEM; +} +#endif + +static int +reset(Flash *f) +{ + uchar r; + int s; + ulong pa; + Pcidev *bridge; + + /* get bridge device */ + bridge = pcimatch(nil, 0x8086, 0x7000); /* Intel PIIX3 ISA bridge device */ + if (bridge == nil) { + EPRINT("flash : failed to find bridge device\n"); + return 1; + } + /* enable extended BIOS and read/write */ + s = splhi(); + r = pcicfgr8(bridge, 0x4e); + r |= 0x84; + pcicfgw8(bridge, 0x4e, r); + splx(s); + /* set system register bits */ + r = inb(SYSREG0); + r |= 0x86; /* chip enable, non-BIOS part, set r/w */ + outb(SYSREG0, r); + /* + * might have to grab memory starting at PADDR(FLASHMEM) ie 0x7ff80000 + * because if this is mapped via virtual address FLASHMEM we would get a + * a kernel panic in mmukmap(). + * va = 0xfff80000 pa = 0xfff80000 for flash + * va = 0xfff80000 pa = 0x7ff80000 if lower memory grabbed by anything + */ + /* + * upafree(FLASHMEM, FLASHPGSZ); + * pa = upamalloc(FLASHMEM, FLASHPGSZ, 0); + * if (pa != FLASHMEM) + * error + */ + pa = mmukmap(FLASHMEM, FLASHMEM, FLASHPGSZ); + if (pa != FLASHEND) { + EPRINT("failed to map flash memory"); + return 1; + } +/* + pa = mmukmap(FLASHEND, FLASHEND, FLASHPGSZ); + if (pa != 0) { + EPRINT("failed to map flash memory"); + return 1; + } +*/ + f->id = 0x0089; /* can't use autoselect: might be running in flash */ + f->devid = 0x66a0; + f->read = readx; + f->write = writex; + f->eraseall = eraseall; + f->erasezone = erasezone; + f->suspend = nil; + f->resume = nil; + f->width = 1; /* must be 1 since devflash.c must not read directly */ + f->erasesize = 64*1024; + *(uchar*)f->addr = FLCLR; /* clear status registers */ + *(uchar*)f->addr = FLRDM; /* reset to read mode */ + return 0; +} + +void +flashzpclink(void) +{ + addflashcard("DD28F032SA", reset); +} diff --git a/os/pc/floppy.h b/os/pc/floppy.h new file mode 100644 index 00000000..f84c9eae --- /dev/null +++ b/os/pc/floppy.h @@ -0,0 +1,183 @@ +typedef struct FController FController; +typedef struct FDrive FDrive; +typedef struct FType FType; + +static void floppyintr(Ureg*); +static int floppyon(FDrive*); +static void floppyoff(FDrive*); +static void floppysetdef(FDrive*); + +/* + * a floppy drive + */ +struct FDrive +{ + FType *t; /* floppy type */ + int dt; /* drive type */ + int dev; + + ulong lasttouched; /* time last touched */ + int cyl; /* current arm position */ + int confused; /* needs to be recalibrated */ + int vers; + int maxtries; /* max read attempts before Eio */ + + int tcyl; /* target cylinder */ + int thead; /* target head */ + int tsec; /* target sector */ + long len; /* size of xfer */ + + uchar *cache; /* track cache */ + int ccyl; + int chead; +}; + +/* + * controller for 4 floppys + */ +struct FController +{ + QLock; /* exclusive access to the contoller */ + + int ndrive; + FDrive *d; /* the floppy drives */ + FDrive *selected; + int rate; /* current rate selected */ + uchar cmd[14]; /* command */ + int ncmd; /* # command bytes */ + uchar stat[14]; /* command status */ + int nstat; /* # status bytes */ + int confused; /* controler needs to be reset */ + Rendez r; /* wait here for command termination */ + int motor; /* bit mask of spinning disks */ +}; + +/* + * floppy types (all MFM encoding) + */ +struct FType +{ + char *name; + int dt; /* compatible drive type */ + int bytes; /* bytes/sector */ + int sectors; /* sectors/track */ + int heads; /* number of heads */ + int steps; /* steps per cylinder */ + int tracks; /* tracks/disk */ + int gpl; /* intersector gap length for read/write */ + int fgpl; /* intersector gap length for format */ + int rate; /* rate code */ + + /* + * these depend on previous entries and are set filled in + * by floppyinit + */ + int bcode; /* coded version of bytes for the controller */ + long cap; /* drive capacity in bytes */ + long tsize; /* track size in bytes */ +}; +/* bits in the registers */ +enum +{ + /* status registers a & b */ + Psra= 0x3f0, + Psrb= 0x3f1, + + /* digital output register */ + Pdor= 0x3f2, + Fintena= 0x8, /* enable floppy interrupt */ + Fena= 0x4, /* 0 == reset controller */ + + /* main status register */ + Pmsr= 0x3f4, + Fready= 0x80, /* ready to be touched */ + Ffrom= 0x40, /* data from controller */ + Ffloppybusy= 0x10, /* operation not over */ + + /* data register */ + Pfdata= 0x3f5, + Frecal= 0x07, /* recalibrate cmd */ + Fseek= 0x0f, /* seek cmd */ + Fsense= 0x08, /* sense cmd */ + Fread= 0x66, /* read cmd */ + Freadid= 0x4a, /* read track id */ + Fspec= 0x03, /* set hold times */ + Fwrite= 0x45, /* write cmd */ + Fformat= 0x4d, /* format cmd */ + Fmulti= 0x80, /* or'd with Fread or Fwrite for multi-head */ + Fdumpreg= 0x0e, /* dump internal registers */ + + /* digital input register */ + Pdir= 0x3F7, /* disk changed port (read only) */ + Pdsr= 0x3F7, /* data rate select port (write only) */ + Fchange= 0x80, /* disk has changed */ + + /* status 0 byte */ + Drivemask= 3<<0, + Seekend= 1<<5, + Codemask= (3<<6)|(3<<3), + Cmdexec= 1<<6, + + /* status 1 byte */ + Overrun= 0x10, +}; + + +static void +pcfloppyintr(Ureg *ur, void *a) +{ + USED(a); + + floppyintr(ur); +} + +void +floppysetup0(FController *fl) +{ + fl->ndrive = 0; + if(ioalloc(Psra, 6, 0, "floppy") < 0) + return; + if(ioalloc(Pdir, 1, 0, "floppy") < 0){ + iofree(Psra); + return; + } + fl->ndrive = 2; +} + +void +floppysetup1(FController *fl) +{ + uchar equip; + + /* + * read nvram for types of floppies 0 & 1 + */ + equip = nvramread(0x10); + if(fl->ndrive > 0){ + fl->d[0].dt = (equip >> 4) & 0xf; + floppysetdef(&fl->d[0]); + } + if(fl->ndrive > 1){ + fl->d[1].dt = equip & 0xf; + floppysetdef(&fl->d[1]); + } + intrenable(IrqFLOPPY, pcfloppyintr, fl, BUSUNKNOWN, "floppy"); +} + +/* + * eject disk ( unknown on safari ) + */ +void +floppyeject(FDrive *dp) +{ + floppyon(dp); + dp->vers++; + floppyoff(dp); +} + +int +floppyexec(char *a, long b, int c) +{ + USED(a, b, c); + return b; +} diff --git a/os/pc/fns.h b/os/pc/fns.h new file mode 100644 index 00000000..028162ac --- /dev/null +++ b/os/pc/fns.h @@ -0,0 +1,162 @@ +#include "../port/portfns.h" +void aamloop(int); +Dirtab* addarchfile(char*, int, long(*)(Chan*,void*,long,vlong), long(*)(Chan*,void*,long,vlong)); +void archinit(void); +void bootargs(ulong); +int cistrcmp(char*, char*); +int cistrncmp(char*, char*, int); +#define clearmmucache() /* x86 doesn't have one */ +void clockintr(Ureg*, void*); +void (*coherence)(void); +void cpuid(char*, int*, int*); +int cpuidentify(void); +void cpuidprint(void); +void (*cycles)(uvlong*); +void delay(int); +int dmacount(int); +int dmadone(int); +void dmaend(int); +int dmainit(int, int); +long dmasetup(int, void*, long, int); +void dumpregs(Ureg*); +#define evenaddr(x) /* x86 doesn't care */ +void fpinit(void); +void fpoff(void); +void fprestore(FPU*); +void fpsave(FPU*); +ulong fpstatus(void); +ulong getcr0(void); +ulong getcr2(void); +ulong getcr3(void); +ulong getcr4(void); +char* getconf(char*); +void guesscpuhz(int); +int i8042auxcmd(int); +int i8042auxcmdval(int); +void i8042auxenable(void (*)(int, int)); +int i8042auxdetect(void); +void i8042reset(void); +void i8250console(void); +void i8253enable(void); +void i8253init(void); +void i8253link(void); +uvlong i8253read(uvlong*); +void i8253timerset(uvlong); +void i8259init(void); +int i8259isr(int); +int i8259enable(Vctl*); +int i8259vecno(int); +int i8259disable(int); +void idle(void); +#define idlehands() /* nothing to do in the runproc */ +int inb(int); +void insb(int, void*, int); +ushort ins(int); +void inss(int, void*, int); +ulong inl(int); +void insl(int, void*, int); +int intrdisable(int, void (*)(Ureg *, void *), void*, int, char*); +void intrenable(int, void (*)(Ureg*, void*), void*, int, char*); +void iofree(int); +void ioinit(void); +int iounused(int, int); +int ioalloc(int, int, int, char*); +int ioreserve(int, int, int, char*); +int iprint(char*, ...); +int isaconfig(char*, int, ISAConf*); +int isvalid_va(void*); +void kbdenable(void); +void kbdinit(void); +void kdbenable(void); +#define kmapinval() +void lapicclock(Ureg*, void*); +void lapictimerset(uvlong); +void lgdt(ushort[3]); +void lidt(ushort[3]); +void links(void); +void ltr(ulong); +void mach0init(void); +void machinit(void); +void mathinit(void); +void mb386(void); +void mb586(void); +void meminit(ulong); +#define mmuflushtlb(pdb) putcr3(pdb) +void mmuinit(void); +ulong mmukmap(ulong, ulong, int); +int mmukmapsync(ulong); +ulong* mmuwalk(ulong*, ulong, int, int); +uchar nvramread(int); +void nvramwrite(int, uchar); +void outb(int, int); +void outsb(int, void*, int); +void outs(int, ushort); +void outss(int, void*, int); +void outl(int, ulong); +void outsl(int, void*, int); +int pciscan(int, Pcidev**); +ulong pcibarsize(Pcidev*, int); +int pcicfgr8(Pcidev*, int); +int pcicfgr16(Pcidev*, int); +int pcicfgr32(Pcidev*, int); +void pcicfgw8(Pcidev*, int, int); +void pcicfgw16(Pcidev*, int, int); +void pcicfgw32(Pcidev*, int, int); +void pciclrbme(Pcidev*); +void pciclrioe(Pcidev*); +int pcigetpms(Pcidev*); +void pcihinv(Pcidev*); +uchar pciipin(Pcidev*, uchar); +Pcidev* pcimatch(Pcidev*, int, int); +Pcidev* pcimatchtbdf(int); +void pcireset(void); +void pcisetbme(Pcidev*); +void pcisetioe(Pcidev*); +int pcisetpms(Pcidev*, int); +void pcmcisread(PCMslot*); +int pcmcistuple(int, int, int, void*, int); +PCMmap* pcmmap(int, ulong, int, int); +int pcmspecial(char*, ISAConf*); +int (*_pcmspecial)(char *, ISAConf *); +void pcmspecialclose(int); +void (*_pcmspecialclose)(int); +void pcmunmap(int, PCMmap*); +void poolinit(void); +void poolsizeinit(void); +void procsave(Proc*); +void procsetup(Proc*); +void putcr3(ulong); +void putcr4(ulong); +void rdmsr(int, vlong*); +ulong rdtsc32(void); +void screeninit(void); +int screenprint(char*, ...); /* debugging */ +void (*screenputs)(char*, int); +#define segflush(a,n) +void syncclock(void); +uvlong tscticks(uvlong*); +void trapenable(int, void (*)(Ureg*, void*), void*, char*); +void trapinit(void); +ulong _tas(ulong*); +ulong umbmalloc(ulong, int, int); +void umbfree(ulong, int); +ulong umbrwmalloc(ulong, int, int); +void umbrwfree(ulong, int); +ulong upamalloc(ulong, int, int); +void upafree(ulong, int); +void vectortable(void); +void wrmsr(ulong, ulong); +int xchgw(ushort*, int); +ulong kzeromap(ulong, ulong, int); +void nmiscreen(void); +int kbdinready(void); + +#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) +#define getcallerpc(x) (((ulong*)(x))[-1]) +#define KADDR(a) ((void*)((ulong)(a)|KZERO)) +#define PADDR(a) ((ulong)(a)&~KZERO) + +#define dcflush(a, b) +#define clockcheck(); +#define dumplongs(x, y, z) +#define setpanic() diff --git a/os/pc/fpi.h b/os/pc/fpi.h new file mode 100644 index 00000000..7a368d18 --- /dev/null +++ b/os/pc/fpi.h @@ -0,0 +1,61 @@ +typedef int Word; +typedef unsigned long Single; +typedef struct { + unsigned long l; + unsigned long h; +} Double; + +enum { + FractBits = 28, + CarryBit = 0x10000000, + HiddenBit = 0x08000000, + MsBit = HiddenBit, + NGuardBits = 3, + GuardMask = 0x07, + LsBit = (1<e >= ExpInfinity) +#define IsInfinity(n) (IsWeird(n) && (n)->h == HiddenBit && (n)->l == 0) +#define SetInfinity(n) ((n)->e = ExpInfinity, (n)->h = HiddenBit, (n)->l = 0) +#define IsNaN(n) (IsWeird(n) && (((n)->h & ~HiddenBit) || (n)->l)) +#define SetQNaN(n) ((n)->s = 0, (n)->e = ExpInfinity, \ + (n)->h = HiddenBit|(LsBit<<1), (n)->l = 0) +#define IsZero(n) ((n)->e == 1 && (n)->h == 0 && (n)->l == 0) +#define SetZero(n) ((n)->e = 1, (n)->h = 0, (n)->l = 0) + +/* + * fpi.c + */ +extern void fpiround(Internal *); +extern void fpiadd(Internal *, Internal *, Internal *); +extern void fpisub(Internal *, Internal *, Internal *); +extern void fpimul(Internal *, Internal *, Internal *); +extern void fpidiv(Internal *, Internal *, Internal *); +extern int fpicmp(Internal *, Internal *); +extern void fpinormalise(Internal*); + +/* + * fpimem.c + */ +extern void fpis2i(Internal *, void *); +extern void fpid2i(Internal *, void *); +extern void fpiw2i(Internal *, void *); +extern void fpii2s(void *, Internal *); +extern void fpii2d(void *, Internal *); +extern void fpii2w(Word *, Internal *); diff --git a/os/pc/fpi387.c b/os/pc/fpi387.c new file mode 100644 index 00000000..d516bcf5 --- /dev/null +++ b/os/pc/fpi387.c @@ -0,0 +1,743 @@ +/* + * Copyright © 1999 Vita Nuova Limited + * + * this doesn't attempt to implement 387 floating-point properties + * that aren't visible in the Inferno environment. in particular, + * all arithmetic is done in double precision, not extended precision. + * furthermore, the FP trap status isn't updated. + */ + +#ifdef TEST +#include +#include +#include +#include "fpi.h" +#include "tst.h" +#else +#include +#include "ureg.h" +#include "fpi.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#endif + +#define fabs Fabs + +typedef struct FPI FPI; + +struct FPI { + char* name; + void (*f)(Ureg*, int, void*, Internal*, Internal*); + int dstf; +}; + +enum { + RndNearest = 0, + RndDown, + RndUp, + Rnd0, + + C0 = 1<<8, + C1 = 1<<9, + C2 = 1<<10, + C3 = 1<<14, +}; + + +int fpemudebug = 0; + +static Internal fpconst[7] = { /* indexed by op&7 */ + /* s, e, l, h */ + {0, 0x3FF, 0x00000000, 0x08000000}, /* 1 */ + {0, 0x400, 0x0BCD1B8A, 0x0D49A784}, /* l2t */ + {0, 0x3FF, 0x095C17F0, 0x0B8AA3B2}, /* l2e */ + {0, 0x400, 0x022168C2, 0x0C90FDAA}, /* pi */ + {0, 0x3FD, 0x04FBCFF7, 0x09A209A8}, /* lg2 */ + {0, 0x3FE, 0x07D1CF79, 0x0B17217F}, /* ln2 */ + {0, 0x1, 0x00000000, 0x00000000}, /* z */ +}; + +static Internal *fpstk(int i); +#define ST(x) (*fpstk((x))) + +#define I387 (up->env->fpu) + +/* BUG: check fetch (not worthwhile in Inferno) */ +#define getubyte(a) (*(uchar*)(a)) +#define getuword(a) (*(ushort*)(a)) +#define getulong(a) (*(ulong*)(a)) + +static void +popfp(void) +{ + ushort *s; + + s = &I387.status; + *s = (*s & ~0x3800) | ((*s + 0x0800) & 0x3800); +} + +static void +pushfp(void) +{ + ushort *s; + + s = &I387.status; + *s = (*s & ~0x3800) | ((*s + 0x3800) & 0x3800); +} + +static Internal * +fpstk(int i) +{ + return (Internal*)I387.istack[(i+(I387.status>>11))&7]; +} + +static void +fldc(Ureg*, int op, void*, Internal*, Internal *d) +{ + *d = fpconst[op&7]; +} + +static void +fabs(Ureg*, int, void*, Internal*, Internal *d) +{ + d->s = 0; +} + +static void +fchs(Ureg*, int, void*, Internal*, Internal *d) +{ + d->s ^= 1; +} + +static void +fadd(Ureg*, int, void*, Internal *s, Internal *d) +{ + Internal l, r; + + l = *s; + r = *d; + (l.s == r.s? fpiadd: fpisub)(&l, &r, d); +} + +static void +fsub(Ureg*, int, void*, Internal *s, Internal *d) +{ + Internal l, r; + + l = *s; + r = *d; + l.s ^= 1; + (l.s == r.s? fpiadd: fpisub)(&l, &r, d); +} + +static void +fsubr(Ureg*, int, void*, Internal *s, Internal *d) +{ + Internal l, r; + + l = *s; + r = *d; + r.s ^= 1; + (l.s == r.s? fpiadd: fpisub)(&r, &l, d); +} + +static void +fmul(Ureg*, int, void*, Internal *s, Internal *d) +{ + Internal l, r; + + l = *s; + r = *d; + fpimul(&l, &r, d); +} + +static void +fdiv(Ureg*, int, void*, Internal *s, Internal *d) +{ + Internal l, r; + + l = *s; + r = *d; + fpidiv(&l, &r, d); +} + +static void +fdivr(Ureg*, int, void*, Internal *s, Internal *d) +{ + Internal l, r; + + l = *s; + r = *d; + fpidiv(&r, &l, d); +} + +static void +fcom(Ureg*, int, void*, Internal *s, Internal *d) +{ + int i; + ushort *p; + + p = &I387.status; + if(IsWeird(s) || IsWeird(d)){ + *p |= C0|C2|C3; + /* BUG: should trap if not masked */ + return; + } + *p &= ~(C0|C2|C3); + i = fpicmp(d, s); + if(i < 0) + *p |= C0; + else if(i == 0) + *p |= C3; +} + +static void +fpush(Ureg*, int op, void*, Internal*, Internal*) +{ + Internal *p; + + p = &ST(op & 7); + pushfp(); + ST(0) = *p; +} + +static void +fmov(Ureg*, int, void*, Internal *s, Internal *d) +{ + *d = *s; +} + +static void +fmovr(Ureg*, int, void*, Internal *s, Internal *d) +{ + *s = *d; +} + +static void +fxch(Ureg*, int, void*, Internal *s, Internal *d) +{ + Internal t; + + t = *s; *s = *d; *d = t; +} + +static void +frstor(Ureg*, int, void *s, Internal*, Internal*) +{ + validaddr(s, 108, 0); + memmove(&I387, s, 108); +} + +static void +fsave(Ureg*, int, void *d, Internal*, Internal*) +{ + validaddr(d, 108, 1); + memmove(d, &I387, 108); + I387.control = 0x037F; + I387.status = 0; + I387.tag = 0; +} + +static void +fstsw(Ureg*, int, void *d, Internal*, Internal*) +{ + validaddr(d, 2, 1); + *(short*)d = I387.status; +} + +static void +fldenv(Ureg*, int, void *s, Internal*, Internal*) +{ + validaddr(s, 28, 0); + memmove(&I387, s, 28); +} + +static void +fldcw(Ureg*, int, void *s, Internal*, Internal*) +{ + validaddr(s, 2, 0); + I387.control = *(short*)s; +} + +static void +fstenv(Ureg*, int, void *d, Internal*, Internal*) +{ + validaddr(d, 4*7, 1); + memmove(d, &I387, 4*7); +} + +static void +fstcw(Ureg*, int, void *d, Internal*, Internal*) +{ + validaddr(d, 2, 1); + *(short*)d = I387.control; +} + +static void +fincstp(Ureg*, int, void*, Internal*, Internal*) +{ + popfp(); +} + +static void +fdecstp(Ureg*, int, void*, Internal*, Internal*) +{ + pushfp(); +} + +static void +fscale(Ureg*, int, void*, Internal *s, Internal *d) +{ + Word w; + + fpii2w(&w, s); /* should truncate towards zero ... */ + d->e += w; +} + +static void +fstswax(Ureg *ur, int, void*, Internal*, Internal*) +{ + ur->ax = (ur->ax & ~0xFFFF) | (I387.status & 0xFFFF); +} + +static void +ftst(Ureg*, int, void*, Internal*, Internal *d) +{ + ushort *p; + + p = &I387.status; + if(IsWeird(d)){ + *p |= C0|C2|C3; + return; + } + *p &= ~(C0|C2|C3); + fpinormalise(d); + if(IsZero(d)) + *p |= C3; + else if(d->s) + *p |=C0; +} + +static void +frndint(Ureg*, int, void*, Internal*, Internal *d) +{ + fpiround(d); /* BUG: doesn't look at rounding mode */ +} + +static void +fnop(Ureg*, int, void*, Internal*, Internal*) +{ +} + +enum { + Fpop1= 1<<0, + Fpop2 = 1<<1, + Fload = 1<<2, +}; + +/* + * %e - effective address - Mod R/M value + * %f - floating point register F0-F7 - from Mod R/M register + */ + +static void fload(Ureg*, int, void*, Internal*, Internal*); +static void fstore(Ureg*, int, void*, Internal*, Internal*); + +#define X(a,b) (((a)<<2)|(b)) + +static FPI optab1[4][4] = { /* normal mod r/m operand */ +[0] { + [0] {"FLDENV %e", fldenv, 0}, + [1] {"FLDCW %e", fldcw, 0}, + [2] {"FSTENV %e", fstenv, 0}, + [3] {"FSTCW %e", fstcw, 0}, + }, +[1] { + [1] {"FMOVX %e,F0", nil, Fload}, + [3] {"FMOVXP F0,%e", nil, Fpop1}, + }, +[2] { + [0] {"FRSTOR %e", frstor, 0}, + [2] {"FSAVE %e", fsave, 0}, + [3] {"FSTSW %e", fstsw, 0}, + }, +[3] { + [0] {"FMOVB %e", nil, 0}, + [1] {"FMOVV %e,F0", nil, Fload}, + [2] {"FMOVBP %e", nil, Fpop1}, + [3] {"FMOVVP F0,%e", nil, Fpop1}, + }, +}; + +#undef X + +static FPI optab2a[1<<3] = { /* A=0 */ +[0] {"FADDx %e,F0", fadd, 0}, +[1] {"FMULx %e,F0", fmul, 0}, +[2] {"FCOMx %e,F0", fcom, 0}, +[3] {"FCOMxP %e,F0", fcom, Fpop1}, +[4] {"FSUBx %e,F0", fsub, 0}, +[5] {"FSUBRx %e,F0", fsubr, 0}, /* ?? */ +[6] {"FDIVx %e,F0", fdiv, 0}, +[7] {"FDIVRx %e,F0", fdivr, 0}, /* ?? */ +}; + +static FPI optab2b[1<<2] = { /* A=1, B=0,2,3 */ +[0] {"FMOVx %e,F0", fload, Fload}, +[2] {"FMOVx F0,%e", fstore, 0}, +[3] {"FMOVxP F0,%e", fstore, Fpop1}, +}; + +#define X(d,P,B) ((d<<4)|(P<<3)|B) + +static FPI optab3a[1<<5] = { /* A=0 */ +[X(0,0,0)] {"FADDD %f,F0", fadd, 0}, +[X(1,0,0)] {"FADDD F0,%f", fadd, 0}, +[X(1,1,0)] {"FADDDP F0,%f", fadd, Fpop1}, +[X(0,0,1)] {"FMULD %f,F0", fmul, 0}, +[X(1,0,1)] {"FMULD F0,%f", fmul, 0}, +[X(1,1,1)] {"FMULDP F0,%f", fmul, Fpop1}, +[X(0,0,2)] {"FCOMD %f,F0", fcom, 0}, +[X(0,0,3)] {"FCOMDP %f,F0", fcom, Fpop1}, +[X(1,1,3)] {"FCOMDPP", fcom, Fpop1|Fpop2}, +[X(0,0,4)] {"FSUBD %f,F0", fsub, 0}, +[X(1,0,4)] {"FSUBRD F0,%f", fsubr, 0}, +[X(1,1,4)] {"FSUBRDP F0,%f", fsubr, Fpop1}, +[X(0,0,5)] {"FSUBRD %f,F0", fsubr, 0}, +[X(1,0,5)] {"FSUBD F0,%f", fsub, 0}, +[X(1,1,5)] {"FSUBDP F0,%f", fsub, Fpop1}, +[X(0,1,5)] {"FUCOMPP", fcom, Fpop1|Fpop2}, +[X(0,0,6)] {"FDIVD %f,F0", fdiv, 0}, +[X(1,0,6)] {"FDIVRD F0,%f", fdivr, 0}, +[X(1,1,6)] {"FDIVRDP F0,%f", fdivr, Fpop1}, +[X(0,0,7)] {"FDIVRD %f,F0", fdivr, 0}, +[X(1,0,7)] {"FDIVD F0,%f", fdiv, 0}, +[X(1,1,7)] {"FDIVDP F0,%f", fdiv, Fpop1}, +}; + +static FPI optab3b[1<<5] = { /* A=1 */ +[X(0,0,0)] {"FMOVD %f,F0", fmov, Fload}, +[X(0,0,1)] {"FXCHD %f,F0", fxch, 0}, +[X(0,0,2)] {"FNOP", fnop, 0}, /* F0 only */ +[X(1,0,0)] {"FFREED %f", fnop, 0}, +[X(1,0,2)] {"FMOVD F0,%f", fmovr, 0}, +[X(1,0,3)] {"FMOVDP F0,%f", fmovr, Fpop1}, +[X(1,1,4)] {"FSTSW AX", fstswax, 0}, +[X(1,0,4)] {"FUCOMD %f,F0", fcom, 0}, +[X(1,0,5)] {"FUCOMDP %f,F0", fcom, Fpop1}, +}; + +#undef X + +static FPI optab4[1<<6] = { +[0x00] {"FCHS", fchs, 0}, +[0x01] {"FABS", fabs, 0}, +[0x04] {"FTST", ftst, 0}, +[0x05] {"FXAM", nil, 0}, +[0x08] {"FLD1", fldc, Fload}, +[0x09] {"FLDL2T", fldc, Fload}, +[0x0a] {"FLDL2E", fldc, Fload}, +[0x0b] {"FLDPI", fldc, Fload}, +[0x0c] {"FLDLG2", fldc, Fload}, +[0x0d] {"FLDLN2", fldc, Fload}, +[0x0e] {"FLDZ", fldc, Fload}, +[0x10] {"F2XM1", nil, 0}, +[0x11] {"FYL2X", nil, 0}, +[0x12] {"FPTAN", nil, 0}, +[0x13] {"FPATAN", nil, 0}, +[0x14] {"FXTRACT", nil, 0}, +[0x15] {"FPREM1", nil, 0}, +[0x16] {"FDECSTP", fdecstp, 0}, +[0x17] {"FINCSTP", fincstp, 0}, +[0x18] {"FPREM", nil, 0}, +[0x19] {"FYL2XP1", nil, 0}, +[0x1a] {"FSQRT", nil, 0}, +[0x1b] {"FSINCOS", nil, 0}, +[0x1c] {"FRNDINT", frndint, 0}, +[0x1d] {"FSCALE", fscale, 0}, +[0x1e] {"FSIN", nil, 0}, +[0x1f] {"FCOS", nil, 0}, +}; + +static void +loadr32(void *s, Internal *d) +{ + validaddr(s, 4, 0); + fpis2i(d, s); +} + +static void +loadi32(void *s, Internal *d) +{ + validaddr(s, 4, 0); + fpiw2i(d, s); +} + +static void +loadr64(void *s, Internal *d) +{ + validaddr(s, 8, 0); + fpid2i(d, s); +} + +static void +loadi16(void *s, Internal *d) +{ + Word w; + + validaddr(s, 2, 0); + w = *(short*)s; + fpiw2i(d, &w); +} + +static void (*loadf[4])(void*, Internal*) ={ + loadr32, loadi32, loadr64, loadi16 +}; + +static void +storer32(Internal s, void *d) +{ + validaddr(d, 4, 1); + fpii2s(d, &s); +} + +static void +storei32(Internal s, void *d) +{ + validaddr(d, 4, 1); + fpii2w(d, &s); +} + +static void +storer64(Internal s, void *d) +{ + validaddr(d, 8, 1); + fpii2d(d, &s); +} + +static void +storei16(Internal s, void *d) +{ + Word w; + + validaddr(d, 2, 1); + fpii2w(&w, &s); + if((short)w != w) + ; /* overflow */ + *(short*)d = w; +} + +static void (*storef[4])(Internal, void*) ={ + storer32, storei32, storer64, storei16 +}; + +static void +fload(Ureg*, int op, void *mem, Internal*, Internal *d) +{ + (*loadf[(op>>9)&3])(mem, d); +} + +static void +fstore(Ureg*, int op, void *mem, Internal *s, Internal*) +{ + (*storef[(op>>9)&3])(*s, mem); +} + +#define REG(x) (*(ulong*)(((char*)ur)+roff[(x)])) +#define offsetof(X) ((ulong)&((Ureg*)0)->X) + +static int roff[] = { + offsetof(ax), + offsetof(cx), + offsetof(dx), + offsetof(bx), + offsetof(ecode), /* ksp */ + offsetof(bp), + offsetof(si), + offsetof(di), +}; + +static long +getdisp(Ureg *ur, int mod, int rm) +{ + uchar c; + long disp; + + if(mod > 2) + return 0; + disp = 0; + if(mod == 1) { + c = getubyte(ur->pc++); + if(c&0x80) + disp = c|(~0<<8); + else + disp = c; + } else if(mod == 2 || rm == 5) { + disp = getulong(ur->pc); + ur->pc += 4; + } + if(mod || rm != 5) + disp += REG(rm); /* base */ + return disp; +} + +static ulong +modrm(Ureg *ur, uchar c) +{ + uchar rm, mod; + int reg; + ulong base; + + mod = (c>>6)&3; + rm = c&7; + if(mod == 3) /* register */ + error("sys: fpemu: invalid addr mode"); + /* no 16-bit mode */ + if(rm == 4) { /* scummy sib byte */ + c = getubyte(ur->pc++); + reg = (c>>3)&0x07; /* index */ + base = getdisp(ur, mod, c&7); + if(reg != 4) + base += (REG(reg) << (c>>6)); /* index */ + if(fpemudebug>1) + print("ur=#%lux sib=#%x reg=%d mod=%d base=%d basev=#%lux sp=%lux\n", ur, c, reg, mod, c&7, base, ur->usp); + return base; + } + if(rm == 5 && mod == 0){ + ur->pc += 4; + return getulong(ur->pc-4); + } + return getdisp(ur, mod, rm); +} + +static void * +ea(Ureg *ur, uchar op) +{ + ulong addr; + + addr = modrm(ur, op); + I387.operand = addr; + if(fpemudebug>1) + print("EA=#%lux\n", addr); + return (void*)addr; +} + +void +fpi387(Ureg *ur) +{ + int op, i; + ulong pc; + FPenv *ufp; + FPI *fp; + Internal tmp, *s, *d; + void *mem; + char buf[60]; + + ur->ecode = (ulong)&ur->sp; /* BUG: TEMPORARY compensation for incorrect Ureg for kernel mode */ + ufp = &up->env->fpu; /* because all the state is in Osenv, it need not be saved/restored */ + if(ufp->fpistate != FPACTIVE) { + ufp->fpistate = FPACTIVE; + ufp->control = 0x037f; + ufp->status = 0; + ufp->tag = 0; + ufp->oselector = 0x17; + } + while((op = getubyte(ur->pc)) >= 0xd8 && op <= 0xdf || op == 0x9B){ + if(op == 0x9B){ /* WAIT */ + ur->pc++; + continue; + } + if(ufp->control & ufp->status & 0x3F) + ufp->status |= 0x8000; + else + ufp->status &= 0x7FFF; + pc = ur->pc; + op = (op<<8) | getubyte(pc+1); + ufp->selector = ur->cs; + ufp->r4 = op-0xD800; + ur->pc += 2; + mem = nil; + s = nil; + d = nil; + /* decode op, following table 10.2.4 in i486 handbook */ + i = op & 0xFFE0; + if(i == 0xD9E0){ + fp = &optab4[op&0x1F]; + s = &ST(0); + if(fp->dstf & Fload) + pushfp(); + d = &ST(0); + } else if(i == 0xDBE0){ + i = op & 0x1F; + if(i == 2){ /* FCLEX */ + ufp->status &= 0x7f00; + continue; + } else if(i == 3){ /* FINIT */ + ufp->control = 0x037f; + ufp->status = 0; + ufp->tag = 0; + continue; + } + fp = nil; + } else if((op & 0xF8C0) == 0xD8C0){ + i = ((op>>6)&030)|((op>>3)&7); + if(op & (1<<8)){ + fp = &optab3b[i]; + s = &ST(op&7); + if(fp->dstf & Fload) + pushfp(); + d = &ST(0); + } else { + fp = &optab3a[i]; + i = op & 7; + if(op & (1<<10)){ + s = &ST(0); + d = &ST(i); + }else{ + s = &ST(i); + d = &ST(0); + } + } + } else if((op & 0xF920) == 0xD920){ + mem = ea(ur, op&0xFF); + fp = &optab1[(op>>9)&3][(op>>3)&3]; + } else { + mem = ea(ur, op&0xFF); + if(op & (1<<8)){ + /* load/store */ + fp = &optab2b[(op>>3)&7]; + if(fp->dstf & Fload){ + pushfp(); + d = &ST(0); + } else + s = &ST(0); + } else { + /* mem OP reg */ + fp = &optab2a[(op>>3)&7]; + (*loadf[(op>>9)&3])(mem, &tmp); + s = &tmp; + d = &ST(0); + } + } + if(fp == nil || fp->f == nil){ + if(fp == nil || fp->name == nil) + snprint(buf, sizeof(buf), "sys: fp: pc=%lux invalid fp 0x%.4x", pc, op); + else + snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.4x (%s)", pc, op, fp->name); + error(buf); + } + if(fpemudebug) + print("%8.8lux %.4x %s\n", pc, op, fp->name); + (*fp->f)(ur, op, mem, s, d); + if(fp->dstf & Fpop1){ + popfp(); + if(fp->dstf & Fpop2) + popfp(); + } + if(anyhigher()) + sched(); + } +} diff --git a/os/pc/fpsave.s b/os/pc/fpsave.s new file mode 100644 index 00000000..8bf9448e --- /dev/null +++ b/os/pc/fpsave.s @@ -0,0 +1,9 @@ +TEXT FPsave(SB), 1, $0 /* save FPU environment without waiting */ + MOVL fpu+0(FP), AX + FSTENV 0(AX) + RET + +TEXT FPrestore(SB), 1, $0 /* restore FPU environment without waiting */ + MOVL fpu+0(FP), AX + FLDENV 0(AX) + RET diff --git a/os/pc/i8250.c b/os/pc/i8250.c new file mode 100644 index 00000000..c625013e --- /dev/null +++ b/os/pc/i8250.c @@ -0,0 +1,328 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* + * INS8250 uart + */ +enum +{ + /* + * register numbers + */ + Data= 0, /* xmit/rcv buffer */ + Iena= 1, /* interrupt enable */ + Ircv= (1<<0), /* for char rcv'd */ + Ixmt= (1<<1), /* for xmit buffer empty */ + Irstat=(1<<2), /* for change in rcv'er status */ + Imstat=(1<<3), /* for change in modem status */ + Istat= 2, /* interrupt flag (read) */ + Fenabd=(3<<6), /* on if fifo's enabled */ + Fifoctl=2, /* fifo control (write) */ + Fena= (1<<0), /* enable xmit/rcv fifos */ + Ftrig= (1<<6), /* trigger after 4 input characters */ + Fclear=(3<<1), /* clear xmit & rcv fifos */ + Format= 3, /* byte format */ + Bits8= (3<<0), /* 8 bits/byte */ + Stop2= (1<<2), /* 2 stop bits */ + Pena= (1<<3), /* generate parity */ + Peven= (1<<4), /* even parity */ + Pforce=(1<<5), /* force parity */ + Break= (1<<6), /* generate a break */ + Dra= (1<<7), /* address the divisor */ + Mctl= 4, /* modem control */ + Dtr= (1<<0), /* data terminal ready */ + Rts= (1<<1), /* request to send */ + Ri= (1<<2), /* ring */ + Inton= (1<<3), /* turn on interrupts */ + Loop= (1<<4), /* loop back */ + Lstat= 5, /* line status */ + Inready=(1<<0), /* receive buffer full */ + Oerror=(1<<1), /* receiver overrun */ + Perror=(1<<2), /* receiver parity error */ + Ferror=(1<<3), /* rcv framing error */ + Outready=(1<<5), /* output buffer empty */ + Mstat= 6, /* modem status */ + Ctsc= (1<<0), /* clear to send changed */ + Dsrc= (1<<1), /* data set ready changed */ + Rire= (1<<2), /* rising edge of ring indicator */ + Dcdc= (1<<3), /* data carrier detect changed */ + Cts= (1<<4), /* complement of clear to send line */ + Dsr= (1<<5), /* complement of data set ready line */ + Ring= (1<<6), /* complement of ring indicator line */ + Dcd= (1<<7), /* complement of data carrier detect line */ + Scratch=7, /* scratchpad */ + Dlsb= 0, /* divisor lsb */ + Dmsb= 1, /* divisor msb */ + + Serial= 0, + Modem= 1, +}; + +typedef struct Uart Uart; +struct Uart +{ + int port; + uchar sticky[8]; /* sticky write register values */ + int nofifo; + + void (*rx)(int); /* routine to take a received character */ + int (*tx)(void); /* routine to get a character to transmit */ + + ulong frame; + ulong overrun; +}; + +static Uart i8250uart[1]; + +#define UartFREQ 1843200 + +#define i8250regw(u, r, v) outb((u)->port+(r), (u)->sticky[(r)]|(v)) +#define i8250regr(u, r) inb((u)->port+(r)) + +/* + * set the baud rate by calculating and setting the baudrate + * generator constant. This will work with fairly non-standard + * baud rates. + */ +static void +i8250setbaud(Uart* uart, int rate) +{ + ulong brconst; + + brconst = (UartFREQ+8*rate-1)/(16*rate); + + i8250regw(uart, Format, Dra); + outb(uart->port+Dmsb, (brconst>>8) & 0xff); + outb(uart->port+Dlsb, brconst & 0xff); + i8250regw(uart, Format, 0); +} + +/* + * toggle DTR + */ +static void +i8250dtr(Uart* uart, int n) +{ + if(n) + uart->sticky[Mctl] |= Dtr; + else + uart->sticky[Mctl] &= ~Dtr; + i8250regw(uart, Mctl, 0); +} + +/* + * toggle RTS + */ +static void +i8250rts(Uart* uart, int n) +{ + if(n) + uart->sticky[Mctl] |= Rts; + else + uart->sticky[Mctl] &= ~Rts; + i8250regw(uart, Mctl, 0); +} + +/* + * Enable/disable FIFOs (if possible). + */ +static void +i8250fifo(Uart* uart, int n) +{ + int i, s; + + if(uart->nofifo) + return; + + s = splhi(); + + /* reset fifos */ + i8250regw(uart, Fifoctl, Fclear); + + /* empty buffer and interrupt conditions */ + for(i = 0; i < 16; i++){ + if(i8250regr(uart, Istat)) + {} + if(i8250regr(uart, Data)) + {} + } + + /* turn on fifo */ + if(n){ + i8250regw(uart, Fifoctl, Fena|Ftrig); + + if((i8250regr(uart, Istat) & Fenabd) == 0){ + /* didn't work, must be an earlier chip type */ + uart->nofifo = 1; + } + } + + splx(s); +} + +#ifdef notdef +static void +i8250intr(Ureg*, void* arg) +{ + Uart *uart; + int ch; + int s, l, loops; + + uart = arg; + for(loops = 0; loops < 1024; loops++){ + s = i8250regr(uart, Istat); + switch(s & 0x3F){ + case 6: /* receiver line status */ + l = i8250regr(uart, Lstat); + if(l & Ferror) + uart->frame++; + if(l & Oerror) + uart->overrun++; + break; + + case 4: /* received data available */ + case 12: + ch = inb(uart->port+Data); + if(uart->rx) + (*uart->rx)(ch & 0x7F); + break; + + case 2: /* transmitter empty */ + ch = -1; + if(uart->tx) + ch = (*uart->tx)(); + if(ch != -1) + outb(uart->port+Data, ch); + break; + + case 0: /* modem status */ + i8250regr(uart, Mstat); + break; + + default: + if(s&1) + return; + print("weird modem interrupt #%2.2ux\n", s); + break; + } + } + panic("i8250intr: 0x%2.2ux\n", i8250regr(uart, Istat)); +} +#endif /* notdef */ + +/* + * turn on a port's interrupts. set DTR and RTS + */ +static void +i8250enable(Uart* uart) +{ + /* + * turn on interrupts + */ + uart->sticky[Iena] = 0; +#ifdef notdef + if(uart->tx) + uart->sticky[Iena] |= Ixmt; + if(uart->rx) + uart->sticky[Iena] |= Ircv|Irstat; +#endif /* notdef */ + + /* + * turn on DTR and RTS + */ + i8250dtr(uart, 1); + i8250rts(uart, 1); + i8250fifo(uart, 1); + + i8250regw(uart, Iena, 0); +} + +void +i8250special(int port, void (*rx)(int), int (*tx)(void), int baud) +{ + Uart *uart = &i8250uart[0]; + + if(uart->port) + return; + + switch(port){ + + case 0: + uart->port = 0x3F8; +#ifdef notdef + intrenable(VectorUART0, i8250intr, uart, BUSUNKNOWN); +#endif /* notdef */ + break; + + case 1: + uart->port = 0x2F8; +#ifdef notdef + intrenable(VectorUART1, i8250intr, uart, BUSUNKNOWN); +#endif /* notdef */ + break; + + default: + return; + } + + /* + * set rate to 9600 baud. + * 8 bits/character. + * 1 stop bit. + * interrupts enabled. + */ + i8250setbaud(uart, 9600); + uart->sticky[Format] = Bits8; + i8250regw(uart, Format, 0); + uart->sticky[Mctl] |= Inton; + i8250regw(uart, Mctl, 0x0); + + uart->rx = rx; + uart->tx = tx; + i8250enable(uart); + if(baud) + i8250setbaud(uart, baud); +} + +int +i8250getc(void) +{ + Uart *uart = &i8250uart[0]; + + if(i8250regr(uart, Lstat) & Inready) + return inb(uart->port+Data); + return 0; +} + +void +i8250putc(int c) +{ + Uart *uart = &i8250uart[0]; + int i; + + for(i = 0; i < 100; i++){ + if(i8250regr(uart, Lstat) & Outready) + break; + delay(1); + } + outb(uart->port+Data, c); +} + +void +i8250puts(char* s, int n) +{ + int x; + + x = splhi(); + while(n--){ + if(*s == '\n') + i8250putc('\r'); + i8250putc(*s++); + } + splx(x); +} diff --git a/os/pc/i8253.c b/os/pc/i8253.c new file mode 100644 index 00000000..d7cff39e --- /dev/null +++ b/os/pc/i8253.c @@ -0,0 +1,314 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +/* + * 8253 timer + */ +enum +{ + T0cntr= 0x40, /* counter ports */ + T1cntr= 0x41, /* ... */ + T2cntr= 0x42, /* ... */ + Tmode= 0x43, /* mode port (control word register) */ + T2ctl= 0x61, /* counter 2 control port */ + + /* commands */ + Latch0= 0x00, /* latch counter 0's value */ + Load0l= 0x10, /* load counter 0's lsb */ + Load0m= 0x20, /* load counter 0's msb */ + Load0= 0x30, /* load counter 0 with 2 bytes */ + + Latch1= 0x40, /* latch counter 1's value */ + Load1l= 0x50, /* load counter 1's lsb */ + Load1m= 0x60, /* load counter 1's msb */ + Load1= 0x70, /* load counter 1 with 2 bytes */ + + Latch2= 0x80, /* latch counter 2's value */ + Load2l= 0x90, /* load counter 2's lsb */ + Load2m= 0xa0, /* load counter 2's msb */ + Load2= 0xb0, /* load counter 2 with 2 bytes */ + + /* 8254 read-back command: everything > pc-at has an 8254 */ + Rdback= 0xc0, /* readback counters & status */ + Rdnstat=0x10, /* don't read status */ + Rdncnt= 0x20, /* don't read counter value */ + Rd0cntr=0x02, /* read back for which counter */ + Rd1cntr=0x04, + Rd2cntr=0x08, + + /* modes */ + ModeMsk=0xe, + Square= 0x6, /* periodic square wave */ + Trigger=0x0, /* interrupt on terminal count */ + Sstrobe=0x8, /* software triggered strobe */ + + /* T2ctl bits */ + T2gate= (1<<0), /* enable T2 counting */ + T2spkr= (1<<1), /* connect T2 out to speaker */ + T2out= (1<<5), /* output of T2 */ + + Freq= 1193182, /* Real clock frequency */ + Tickshift=8, /* extra accuracy */ + MaxPeriod=Freq/HZ, + MinPeriod=Freq/(100*HZ), +}; + +typedef struct I8253 I8253; +struct I8253 +{ + Lock; + ulong period; /* current clock period */ + int enabled; + uvlong hz; + + ushort last; /* last value of clock 1 */ + uvlong ticks; /* cumulative ticks of counter 1 */ + + ulong periodset; +}; +I8253 i8253; + +void +i8253init(void) +{ + int loops, x; + + ioalloc(T0cntr, 4, 0, "i8253"); + ioalloc(T2ctl, 1, 0, "i8253.cntr2ctl"); + + i8253.period = Freq/HZ; + + /* + * enable a 1/HZ interrupt for providing scheduling interrupts + */ + outb(Tmode, Load0|Square); + outb(T0cntr, (Freq/HZ)); /* low byte */ + outb(T0cntr, (Freq/HZ)>>8); /* high byte */ + + /* + * enable a longer period counter to use as a clock + */ + outb(Tmode, Load2|Square); + outb(T2cntr, 0); /* low byte */ + outb(T2cntr, 0); /* high byte */ + x = inb(T2ctl); + x |= T2gate; + outb(T2ctl, x); + + /* + * Introduce a little delay to make sure the count is + * latched and the timer is counting down; with a fast + * enough processor this may not be the case. + * The i8254 (which this probably is) has a read-back + * command which can be used to make sure the counting + * register has been written into the counting element. + */ + x = (Freq/HZ); + for(loops = 0; loops < 100000 && x >= (Freq/HZ); loops++){ + outb(Tmode, Latch0); + x = inb(T0cntr); + x |= inb(T0cntr)<<8; + } +} + +void +guesscpuhz(int aalcycles) +{ + int loops, incr, x, y; + uvlong a, b, cpufreq; + + /* find biggest loop that doesn't wrap */ + incr = 16000000/(aalcycles*HZ*2); + x = 2000; + for(loops = incr; loops < 64*1024; loops += incr) { + + /* + * measure time for the loop + * + * MOVL loops,CX + * aaml1: AAM + * LOOP aaml1 + * + * the time for the loop should be independent of external + * cache and memory system since it fits in the execution + * prefetch buffer. + * + */ + outb(Tmode, Latch0); + cycles(&a); + x = inb(T0cntr); + x |= inb(T0cntr)<<8; + aamloop(loops); + outb(Tmode, Latch0); + cycles(&b); + y = inb(T0cntr); + y |= inb(T0cntr)<<8; + x -= y; + + if(x < 0) + x += Freq/HZ; + + if(x > Freq/(3*HZ)) + break; + } + + /* + * figure out clock frequency and a loop multiplier for delay(). + * n.b. counter goes up by 2*Freq + */ + cpufreq = (vlong)loops*((aalcycles*2*Freq)/x); + m->loopconst = (cpufreq/1000)/aalcycles; /* AAM+LOOP's for 1 ms */ + + if(m->havetsc){ + /* counter goes up by 2*Freq */ + b = (b-a)<<1; + b *= Freq; + b /= x; + + /* + * round to the nearest megahz + */ + m->cpumhz = (b+500000)/1000000L; + m->cpuhz = b; + m->cyclefreq = b; + } else { + /* + * add in possible 0.5% error and convert to MHz + */ + m->cpumhz = (cpufreq + cpufreq/200)/1000000; + m->cpuhz = cpufreq; + } + + i8253.hz = Freq<>Tickshift; + now = i8253.ticks; /* assuming whomever called us just did fastticks() */ + + period = want - now; + if(period < MinPeriod) + period = MinPeriod; + else if(period > (4*MaxPeriod)/5) /* strong attraction to MaxPeriod */ + period = MaxPeriod; + } + + /* hysteresis */ + if(i8253.period != period){ + ilock(&i8253); + /* load new value */ + outb(Tmode, Load0|Square); + outb(T0cntr, period); /* low byte */ + outb(T0cntr, period >> 8); /* high byte */ + + /* remember period */ + i8253.period = period; + i8253.periodset++; + iunlock(&i8253); + } +} + +static void +i8253clock(Ureg* ureg, void*) +{ + timerintr(ureg, 0); +} + +void +i8253enable(void) +{ + i8253.enabled = 1; + i8253.period = Freq/HZ; + intrenable(IrqCLOCK, i8253clock, 0, BUSUNKNOWN, "clock"); +} + +void +i8253link(void) +{ +} + +/* + * return the total ticks of counter 2. We shift by + * 8 to give timesync more wriggle room for interpretation + * of the frequency + */ +uvlong +i8253read(uvlong *hz) +{ + ushort y, x; + uvlong ticks; + + if(hz) + *hz = i8253.hz; + + ilock(&i8253); + outb(Tmode, Latch2); + y = inb(T2cntr); + y |= inb(T2cntr)<<8; + + if(y < i8253.last) + x = i8253.last - y; + else { + x = i8253.last + (0x10000 - y); + if (x > 3*MaxPeriod) { + outb(Tmode, Load2|Square); + outb(T2cntr, 0); /* low byte */ + outb(T2cntr, 0); /* high byte */ + y = 0xFFFF; + x = i8253.period; + } + } + i8253.last = y; + i8253.ticks += x>>1; + ticks = i8253.ticks; + iunlock(&i8253); + + return ticks<loopconst; + if(millisecs <= 0) + millisecs = 1; + aamloop(millisecs); +} + +void +microdelay(int microsecs) +{ + microsecs *= m->loopconst; + microsecs /= 1000; + if(microsecs <= 0) + microsecs = 1; + aamloop(microsecs); +} + +/* + * performance measurement ticks. must be low overhead. + * doesn't have to count over a second. + */ +ulong +perfticks(void) +{ + uvlong x; + + if(m->havetsc) + cycles(&x); + else + x = 0; + return x; +} diff --git a/os/pc/i8259.c b/os/pc/i8259.c new file mode 100644 index 00000000..562196b9 --- /dev/null +++ b/os/pc/i8259.c @@ -0,0 +1,199 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +/* + * 8259 interrupt controllers + */ +enum +{ + Int0ctl= 0x20, /* control port (ICW1, OCW2, OCW3) */ + Int0aux= 0x21, /* everything else (ICW2, ICW3, ICW4, OCW1) */ + Int1ctl= 0xA0, /* control port */ + Int1aux= 0xA1, /* everything else (ICW2, ICW3, ICW4, OCW1) */ + + Icw1= 0x10, /* select bit in ctl register */ + Ocw2= 0x00, + Ocw3= 0x08, + + EOI= 0x20, /* non-specific end of interrupt */ + + Elcr1= 0x4D0, /* Edge/Level Triggered Register */ + Elcr2= 0x4D1, +}; + +static Lock i8259lock; +static int i8259mask = 0xFFFF; /* disabled interrupts */ +int i8259elcr; /* mask of level-triggered interrupts */ + +void +i8259init(void) +{ + int x; + + ioalloc(Int0ctl, 2, 0, "i8259.0"); + ioalloc(Int1ctl, 2, 0, "i8259.1"); + ilock(&i8259lock); + + /* + * Set up the first 8259 interrupt processor. + * Make 8259 interrupts start at CPU vector VectorPIC. + * Set the 8259 as master with edge triggered + * input with fully nested interrupts. + */ + outb(Int0ctl, (1<<4)|(0<<3)|(1<<0)); /* ICW1 - master, edge triggered, + ICW4 will be sent */ + outb(Int0aux, VectorPIC); /* ICW2 - interrupt vector offset */ + outb(Int0aux, 0x04); /* ICW3 - have slave on level 2 */ + outb(Int0aux, 0x01); /* ICW4 - 8086 mode, not buffered */ + + /* + * Set up the second 8259 interrupt processor. + * Make 8259 interrupts start at CPU vector VectorPIC+8. + * Set the 8259 as slave with edge triggered + * input with fully nested interrupts. + */ + outb(Int1ctl, (1<<4)|(0<<3)|(1<<0)); /* ICW1 - master, edge triggered, + ICW4 will be sent */ + outb(Int1aux, VectorPIC+8); /* ICW2 - interrupt vector offset */ + outb(Int1aux, 0x02); /* ICW3 - I am a slave on level 2 */ + outb(Int1aux, 0x01); /* ICW4 - 8086 mode, not buffered */ + outb(Int1aux, (i8259mask>>8) & 0xFF); + + /* + * pass #2 8259 interrupts to #1 + */ + i8259mask &= ~0x04; + outb(Int0aux, i8259mask & 0xFF); + + /* + * Set Ocw3 to return the ISR when ctl read. + * After initialisation status read is set to IRR. + * Read IRR first to possibly deassert an outstanding + * interrupt. + */ + inb(Int0ctl); + outb(Int0ctl, Ocw3|0x03); + inb(Int1ctl); + outb(Int1ctl, Ocw3|0x03); + + /* + * Check for Edge/Level register. + * This check may not work for all chipsets. + * First try a non-intrusive test - the bits for + * IRQs 13, 8, 2, 1 and 0 must be edge (0). If + * that's OK try a R/W test. + */ + x = (inb(Elcr2)<<8)|inb(Elcr1); + if(!(x & 0x2107)){ + outb(Elcr1, 0); + if(inb(Elcr1) == 0){ + outb(Elcr1, 0x20); + if(inb(Elcr1) == 0x20) + i8259elcr = x; + outb(Elcr1, x & 0xFF); + print("ELCR: %4.4uX\n", i8259elcr); + } + } + iunlock(&i8259lock); +} + +int +i8259isr(int vno) +{ + int irq, isr; + + if(vno < VectorPIC || vno > VectorPIC+MaxIrqPIC) + return 0; + irq = vno-VectorPIC; + + /* + * tell the 8259 that we're done with the + * highest level interrupt (interrupts are still + * off at this point) + */ + ilock(&i8259lock); + isr = inb(Int0ctl); + outb(Int0ctl, EOI); + if(irq >= 8){ + isr |= inb(Int1ctl)<<8; + outb(Int1ctl, EOI); + } + iunlock(&i8259lock); + + return isr & (1<irq; + if(irq < 0 || irq > MaxIrqPIC){ + print("i8259enable: irq %d out of range\n", irq); + return -1; + } + irqbit = 1<>8) & 0xFF); + + if(i8259elcr & irqbit) + v->eoi = i8259isr; + else + v->isr = i8259isr; + iunlock(&i8259lock); + + return VectorPIC+irq; +} + +int +i8259vecno(int irq) +{ + return VectorPIC+irq; +} + +int +i8259disable(int irq) +{ + int irqbit; + + /* + * Given an IRQ, disable the corresponding interrupt + * in the 8259. + */ + if(irq < 0 || irq > MaxIrqPIC){ + print("i8259disable: irq %d out of range\n", irq); + return -1; + } + irqbit = 1<>8) & 0xFF); + } + iunlock(&i8259lock); + return 0; +} diff --git a/os/pc/io.h b/os/pc/io.h new file mode 100644 index 00000000..cd35265e --- /dev/null +++ b/os/pc/io.h @@ -0,0 +1,323 @@ +#define X86STEPPING(x) ((x) & 0x0F) +#define X86MODEL(x) (((x)>>4) & 0x0F) +#define X86FAMILY(x) (((x)>>8) & 0x0F) + +enum { + VectorDBG = 1, /* debug exception */ + VectorNMI = 2, /* non-maskable interrupt */ + VectorBPT = 3, /* breakpoint */ + VectorUD = 6, /* invalid opcode exception */ + VectorCNA = 7, /* coprocessor not available */ + Vector2F = 8, /* double fault */ + VectorCSO = 9, /* coprocessor segment overrun */ + VectorPF = 14, /* page fault */ + Vector15 = 15, /* reserved */ + VectorCERR = 16, /* coprocessor error */ + + VectorPIC = 32, /* external i8259 interrupts */ + IrqCLOCK = 0, + IrqKBD = 1, + IrqUART1 = 3, + IrqUART0 = 4, + IrqPCMCIA = 5, + IrqFLOPPY = 6, + IrqLPT = 7, + IrqIRQ7 = 7, + IrqAUX = 12, /* PS/2 port */ + IrqIRQ13 = 13, /* coprocessor on 386 */ + IrqATA0 = 14, + IrqATA1 = 15, + MaxIrqPIC = 15, + + VectorLAPIC = VectorPIC+16, /* local APIC interrupts */ + IrqLINT0 = 16, /* LINT[01] must be offsets 0 and 1 */ + IrqLINT1 = 17, + IrqTIMER = 18, + IrqERROR = 19, + IrqPCINT = 20, + IrqSPURIOUS = 31, /* must have bits [3-0] == 0x0F */ + MaxIrqLAPIC = 31, + + VectorSYSCALL = 64, + + VectorAPIC = 65, /* external APIC interrupts */ + MaxVectorAPIC = 255, +}; + +typedef struct Vctl { + Vctl* next; /* handlers on this vector */ + + char name[KNAMELEN]; /* of driver */ + int isintr; /* interrupt or fault/trap */ + int irq; + int tbdf; + int (*isr)(int); /* get isr bit for this irq */ + int (*eoi)(int); /* eoi */ + + void (*f)(Ureg*, void*); /* handler to call */ + void* a; /* argument to call it with */ +} Vctl; + +enum { + BusCBUS = 0, /* Corollary CBUS */ + BusCBUSII, /* Corollary CBUS II */ + BusEISA, /* Extended ISA */ + BusFUTURE, /* IEEE Futurebus */ + BusINTERN, /* Internal bus */ + BusISA, /* Industry Standard Architecture */ + BusMBI, /* Multibus I */ + BusMBII, /* Multibus II */ + BusMCA, /* Micro Channel Architecture */ + BusMPI, /* MPI */ + BusMPSA, /* MPSA */ + BusNUBUS, /* Apple Macintosh NuBus */ + BusPCI, /* Peripheral Component Interconnect */ + BusPCMCIA, /* PC Memory Card International Association */ + BusTC, /* DEC TurboChannel */ + BusVL, /* VESA Local bus */ + BusVME, /* VMEbus */ + BusXPRESS, /* Express System Bus */ +}; + +#define MKBUS(t,b,d,f) (((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8)) +#define BUSFNO(tbdf) (((tbdf)>>8)&0x07) +#define BUSDNO(tbdf) (((tbdf)>>11)&0x1F) +#define BUSBNO(tbdf) (((tbdf)>>16)&0xFF) +#define BUSTYPE(tbdf) ((tbdf)>>24) +#define BUSBDF(tbdf) ((tbdf)&0x00FFFF00) +#define BUSUNKNOWN (-1) + +enum { + MaxEISA = 16, + CfgEISA = 0xC80, +}; + +/* + * PCI support code. + */ +enum { /* type 0 and type 1 pre-defined header */ + PciVID = 0x00, /* vendor ID */ + PciDID = 0x02, /* device ID */ + PciPCR = 0x04, /* command */ + PciPSR = 0x06, /* status */ + PciRID = 0x08, /* revision ID */ + PciCCRp = 0x09, /* programming interface class code */ + PciCCRu = 0x0A, /* sub-class code */ + PciCCRb = 0x0B, /* base class code */ + PciCLS = 0x0C, /* cache line size */ + PciLTR = 0x0D, /* latency timer */ + PciHDT = 0x0E, /* header type */ + PciBST = 0x0F, /* BIST */ + + PciBAR0 = 0x10, /* base address */ + PciBAR1 = 0x14, + + PciINTL = 0x3C, /* interrupt line */ + PciINTP = 0x3D, /* interrupt pin */ +}; + +enum { /* type 0 pre-defined header */ + PciCIS = 0x28, /* cardbus CIS pointer */ + PciSVID = 0x2C, /* subsystem vendor ID */ + PciSID = 0x2E, /* cardbus CIS pointer */ + PciEBAR0 = 0x30, /* expansion ROM base address */ + PciMGNT = 0x3E, /* burst period length */ + PciMLT = 0x3F, /* maximum latency between bursts */ +}; + +enum { /* type 1 pre-defined header */ + PciPBN = 0x18, /* primary bus number */ + PciSBN = 0x19, /* secondary bus number */ + PciUBN = 0x1A, /* subordinate bus number */ + PciSLTR = 0x1B, /* secondary latency timer */ + PciIBR = 0x1C, /* I/O base */ + PciILR = 0x1D, /* I/O limit */ + PciSPSR = 0x1E, /* secondary status */ + PciMBR = 0x20, /* memory base */ + PciMLR = 0x22, /* memory limit */ + PciPMBR = 0x24, /* prefetchable memory base */ + PciPMLR = 0x26, /* prefetchable memory limit */ + PciPUBR = 0x28, /* prefetchable base upper 32 bits */ + PciPULR = 0x2C, /* prefetchable limit upper 32 bits */ + PciIUBR = 0x30, /* I/O base upper 16 bits */ + PciIULR = 0x32, /* I/O limit upper 16 bits */ + PciEBAR1 = 0x28, /* expansion ROM base address */ + PciBCR = 0x3E, /* bridge control register */ +}; + +enum { /* type 2 pre-defined header */ + PciCBExCA = 0x10, + PciCBSPSR = 0x16, + PciCBPBN = 0x18, /* primary bus number */ + PciCBSBN = 0x19, /* secondary bus number */ + PciCBUBN = 0x1A, /* subordinate bus number */ + PciCBSLTR = 0x1B, /* secondary latency timer */ + PciCBMBR0 = 0x1C, + PciCBMLR0 = 0x20, + PciCBMBR1 = 0x24, + PciCBMLR1 = 0x28, + PciCBIBR0 = 0x2C, /* I/O base */ + PciCBILR0 = 0x30, /* I/O limit */ + PciCBIBR1 = 0x34, /* I/O base */ + PciCBILR1 = 0x38, /* I/O limit */ + PciCBSVID = 0x40, /* subsystem vendor ID */ + PciCBSID = 0x42, /* subsystem ID */ + PciCBLMBAR = 0x44, /* legacy mode base address */ +}; + +typedef struct Pcisiz Pcisiz; +struct Pcisiz +{ + Pcidev* dev; + int siz; + int bar; +}; + +typedef struct Pcidev Pcidev; +struct Pcidev +{ + int tbdf; /* type+bus+device+function */ + ushort vid; /* vendor ID */ + ushort did; /* device ID */ + + ushort pcr; + + uchar rid; + uchar ccrp; + uchar ccru; + uchar ccrb; + uchar cls; + uchar ltr; + + struct { + ulong bar; /* base address */ + int size; + } mem[6]; + + struct { + ulong bar; + int size; + } rom; + uchar intl; /* interrupt line */ + + Pcidev* list; + Pcidev* link; /* next device on this bno */ + + Pcidev* bridge; /* down a bus */ + struct { + ulong bar; + int size; + } ioa, mema; + + int pmrb; /* power management register block */ +}; + +#define PCIWINDOW 0 +#define PCIWADDR(va) (PADDR(va)+PCIWINDOW) +#define ISAWINDOW 0 +#define ISAWADDR(va) (PADDR(va)+ISAWINDOW) + +/* SMBus transactions */ +enum +{ + SMBquick, /* sends address only */ + + /* write */ + SMBsend, /* sends address and cmd */ + SMBbytewrite, /* sends address and cmd and 1 byte */ + SMBwordwrite, /* sends address and cmd and 2 bytes */ + + /* read */ + SMBrecv, /* sends address, recvs 1 byte */ + SMBbyteread, /* sends address and cmd, recv's byte */ + SMBwordread, /* sends address and cmd, recv's 2 bytes */ +}; + +typedef struct SMBus SMBus; +struct SMBus { + QLock; /* mutex */ + Rendez r; /* rendezvous point for completion interrupts */ + void *arg; /* implementation dependent */ + ulong base; /* port or memory base of smbus */ + int busy; + void (*transact)(SMBus*, int, int, int, uchar*); +}; + +/* + * PCMCIA support code. + */ + +typedef struct PCMslot PCMslot; +typedef struct PCMconftab PCMconftab; + +/* + * Map between ISA memory space and PCMCIA card memory space. + */ +struct PCMmap { + ulong ca; /* card address */ + ulong cea; /* card end address */ + ulong isa; /* ISA address */ + int len; /* length of the ISA area */ + int attr; /* attribute memory */ + int ref; +}; + +/* configuration table entry */ +struct PCMconftab +{ + int index; + ushort irqs; /* legal irqs */ + uchar irqtype; + uchar bit16; /* true for 16 bit access */ + struct { + ulong start; + ulong len; + } io[16]; + int nio; + uchar vpp1; + uchar vpp2; + uchar memwait; + ulong maxwait; + ulong readywait; + ulong otherwait; +}; + +/* a card slot */ +struct PCMslot +{ + Lock; + int ref; + + void *cp; /* controller for this slot */ + long memlen; /* memory length */ + uchar base; /* index register base */ + uchar slotno; /* slot number */ + + /* status */ + uchar special; /* in use for a special device */ + uchar already; /* already inited */ + uchar occupied; + uchar battery; + uchar wrprot; + uchar powered; + uchar configed; + uchar enabled; + uchar busy; + + /* cis info */ + ulong msec; /* time of last slotinfo call */ + char verstr[512]; /* version string */ + int ncfg; /* number of configurations */ + struct { + ushort cpresent; /* config registers present */ + ulong caddr; /* relative address of config registers */ + } cfg[8]; + int nctab; /* number of config table entries */ + PCMconftab ctab[8]; + PCMconftab *def; /* default conftab */ + + /* memory maps */ + Lock mlock; /* lock down the maps */ + int time; + PCMmap mmap[4]; /* maps, last is always for the kernel */ +}; diff --git a/os/pc/kbd.c b/os/pc/kbd.c new file mode 100644 index 00000000..8db7009c --- /dev/null +++ b/os/pc/kbd.c @@ -0,0 +1,477 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +enum { + Data= 0x60, /* data port */ + + Status= 0x64, /* status port */ + Inready= 0x01, /* input character ready */ + Outbusy= 0x02, /* output busy */ + Sysflag= 0x04, /* system flag */ + Cmddata= 0x08, /* cmd==0, data==1 */ + Inhibit= 0x10, /* keyboard/mouse inhibited */ + Minready= 0x20, /* mouse character ready */ + Rtimeout= 0x40, /* general timeout */ + Parity= 0x80, + + Cmd= 0x64, /* command port (write only) */ + + Spec= 0x80, + + PF= Spec|0x20, /* num pad function key */ + View= Spec|0x00, /* view (shift window up) */ + KF= 0xF000, /* function key (begin Unicode private space) */ + Shift= Spec|0x60, + Break= Spec|0x61, + Ctrl= Spec|0x62, + Latin= Spec|0x63, + Caps= Spec|0x64, + Num= Spec|0x65, + Middle= Spec|0x66, + No= 0x00, /* peter */ + + Home= KF|13, + Up= KF|14, + Pgup= KF|15, + Print= KF|16, + Left= KF|17, + Right= KF|18, + End= '\r', + Down= View, + Pgdown= KF|19, + Ins= KF|20, + Del= 0x7F, + Scroll= KF|21, +}; + +/* + * The codes at 0x79 and 0x81 are produed by the PFU Happy Hacking keyboard. + * A 'standard' keyboard doesn't produce anything above 0x58. + */ +Rune kbtab[] = +{ +[0x00] No, 0x1b, '1', '2', '3', '4', '5', '6', +[0x08] '7', '8', '9', '0', '-', '=', '\b', '\t', +[0x10] 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', +[0x18] 'o', 'p', '[', ']', '\n', Ctrl, 'a', 's', +[0x20] 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', +[0x28] '\'', '`', Shift, '\\', 'z', 'x', 'c', 'v', +[0x30] 'b', 'n', 'm', ',', '.', '/', Shift, '*', +[0x38] Latin, ' ', Ctrl, KF|1, KF|2, KF|3, KF|4, KF|5, +[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, Scroll, '7', +[0x48] '8', '9', '-', '4', '5', '6', '+', '1', +[0x50] '2', '3', '0', '.', No, No, No, KF|11, +[0x58] KF|12, No, No, No, No, No, No, No, +[0x60] No, No, No, No, No, No, No, No, +[0x68] No, No, No, No, No, No, No, No, +[0x70] No, No, No, No, No, No, No, No, +[0x78] No, View, No, Up, No, No, No, No, +}; + +Rune kbtabshift[] = +{ +[0x00] No, 0x1b, '!', '@', '#', '$', '%', '^', +[0x08] '&', '*', '(', ')', '_', '+', '\b', '\t', +[0x10] 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', +[0x18] 'O', 'P', '{', '}', '\n', Ctrl, 'A', 'S', +[0x20] 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', +[0x28] '"', '~', Shift, '|', 'Z', 'X', 'C', 'V', +[0x30] 'B', 'N', 'M', '<', '>', '?', Shift, '*', +[0x38] Latin, ' ', Ctrl, KF|1, KF|2, KF|3, KF|4, KF|5, +[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, Scroll, '7', +[0x48] '8', '9', '-', '4', '5', '6', '+', '1', +[0x50] '2', '3', '0', '.', No, No, No, KF|11, +[0x58] KF|12, No, No, No, No, No, No, No, +[0x60] No, No, No, No, No, No, No, No, +[0x68] No, No, No, No, No, No, No, No, +[0x70] No, No, No, No, No, No, No, No, +[0x78] No, Up, No, Up, No, No, No, No, +}; + +Rune kbtabesc1[] = +{ +[0x00] No, No, No, No, No, No, No, No, +[0x08] No, No, No, No, No, No, No, No, +[0x10] No, No, No, No, No, No, No, No, +[0x18] No, No, No, No, '\n', Ctrl, No, No, +[0x20] No, No, No, No, No, No, No, No, +[0x28] No, No, Shift, No, No, No, No, No, +[0x30] No, No, No, No, No, '/', No, Print, +[0x38] Latin, No, No, No, No, No, No, No, +[0x40] No, No, No, No, No, No, Break, Home, +[0x48] Up, Pgup, No, Left, No, Right, No, End, +[0x50] Down, Pgdown, Ins, Del, No, No, No, No, +[0x58] No, No, No, No, No, No, No, No, +[0x60] No, No, No, No, No, No, No, No, +[0x68] No, No, No, No, No, No, No, No, +[0x70] No, No, No, No, No, No, No, No, +[0x78] No, Up, No, No, No, No, No, No, +}; + +enum +{ + /* controller command byte */ + Cscs1= (1<<6), /* scan code set 1 */ + Cauxdis= (1<<5), /* mouse disable */ + Ckbddis= (1<<4), /* kbd disable */ + Csf= (1<<2), /* system flag */ + Cauxint= (1<<1), /* mouse interrupt enable */ + Ckbdint= (1<<0), /* kbd interrupt enable */ +}; + +int mouseshifted; + +static Lock i8042lock; +static uchar ccc; +static void (*auxputc)(int, int); + +/* + * wait for output no longer busy + */ +static int +outready(void) +{ + int tries; + + for(tries = 0; (inb(Status) & Outbusy); tries++){ + if(tries > 500) + return -1; + delay(2); + } + return 0; +} + +/* + * wait for input + */ +static int +inready(void) +{ + int tries; + + for(tries = 0; !(inb(Status) & Inready); tries++){ + if(tries > 500) + return -1; + delay(2); + } + return 0; +} + +/* + * ask 8042 to reset the machine + */ +void +i8042reset(void) +{ + ushort *s = KADDR(0x472); + int i, x; + + *s = 0x1234; /* BIOS warm-boot flag */ + + /* + * newer reset the machine command + */ + outready(); + outb(Cmd, 0xFE); + outready(); + + /* + * Pulse it by hand (old somewhat reliable) + */ + x = 0xDF; + for(i = 0; i < 5; i++){ + x ^= 1; + outready(); + outb(Cmd, 0xD1); + outready(); + outb(Data, x); /* toggle reset */ + delay(100); + } +} + +int +i8042auxcmd(int cmd) +{ + unsigned int c; + int tries; + + c = 0; + tries = 0; + + ilock(&i8042lock); + do{ + if(tries++ > 2) + break; + if(outready() < 0) + break; + outb(Cmd, 0xD4); + if(outready() < 0) + break; + outb(Data, cmd); + if(outready() < 0) + break; + if(inready() < 0) + break; + c = inb(Data); + } while(c == 0xFE || c == 0); + iunlock(&i8042lock); + + if(c != 0xFA){ + print("i8042: %2.2ux returned to the %2.2ux command\n", c, cmd); + return -1; + } + return 0; +} + +int +i8042auxcmds(uchar *cmd, int ncmd) +{ + int i; + + ilock(&i8042lock); + for(i=0; i sizeof kbtab){ + c |= keyup; + if(c != 0xFF) /* these come fairly often: CAPSLOCK U Y */ + print("unknown key %ux\n", c); + return; + } + + if(esc1){ + c = kbtabesc1[c]; + esc1 = 0; + } else if(esc2){ + esc2--; + return; + } else if(shift) + c = kbtabshift[c]; + else + c = kbtab[c]; + + if(caps && c<='z' && c>='a') + c += 'A' - 'a'; + + /* + * keyup only important for shifts + */ + if(keyup){ + switch(c){ + case Latin: + alt = 0; + break; + case Shift: + shift = 0; + mouseshifted = 0; + break; + case Ctrl: + ctl = 0; + break; + } + return; + } + + /* + * normal character + */ + if(!(c & (Spec|KF))){ + if(ctl){ + if(alt && c == Del) + exit(0); + c &= 0x1f; + } + if(!collecting){ + kbdputc(kbdq, c); + return; + } + kc[nk++] = c; + c = latin1(kc, nk); + if(c < -1) /* need more keystrokes */ + return; + if(c != -1) /* valid sequence */ + kbdputc(kbdq, c); + else /* dump characters */ + for(i=0; i>22) & 0x03FF)<<2) +#define PTO(a) (((((a))>>12) & 0x03FF)<<2) + +/* + * For backwards compatiblity with 9load - should go away when 9load is changed + * 9load currently sets up the mmu, however the first 16MB of memory is identity + * mapped, so behave as if the mmu was not setup + */ +TEXT _start0x80100020(SB), $0 + MOVL $_start0x00100020(SB), AX + ANDL $~KZERO, AX + JMP* AX + +/* + * Must be 4-byte aligned. + */ +TEXT _multibootheader(SB), $0 + LONG $0x1BADB002 /* magic */ + LONG $0x00010003 /* flags */ + LONG $-(0x1BADB002 + 0x00010003) /* checksum */ + LONG $_multibootheader-KZERO(SB) /* header_addr */ + LONG $_start0x80100020-KZERO(SB) /* load_addr */ + LONG $edata-KZERO(SB) /* load_end_addr */ + LONG $end-KZERO(SB) /* bss_end_addr */ + LONG $_start0x80100020-KZERO(SB) /* entry_addr */ + LONG $0 /* mode_type */ + LONG $0 /* width */ + LONG $0 /* height */ + LONG $0 /* depth */ + +/* + * In protected mode with paging turned off and segment registers setup to linear map all memory. + * Entered via a jump to 0x00100020, the physical address of the virtual kernel entry point of 0x80100020 + * Make the basic page tables for processor 0. Four pages are needed for the basic set: + * a page directory, a page table for mapping the first 4MB of physical memory to KZERO, + * and virtual and physical pages for mapping the Mach structure. + * The remaining PTEs will be allocated later when memory is sized. + * An identity mmu map is also needed for the switch to virtual mode. This + * identity mapping is removed once the MMU is going and the JMP has been made + * to virtual memory. + */ +TEXT _start0x00100020(SB), $0 + CLI /* make sure interrupts are off */ + + /* set up the gdt so we have sane plan 9 style gdts. */ + MOVL $tgdtptr(SB), AX + ANDL $~KZERO, AX + MOVL (AX), GDTR + MOVW $1, AX + MOVW AX, MSW + + /* clear prefetch queue (weird code to avoid optimizations) */ + DELAY + + /* set segs to something sane (avoid traps later) */ + MOVW $(1<<3), AX + MOVW AX, DS + MOVW AX, SS + MOVW AX, ES + MOVW AX, FS + MOVW AX, GS + +/* JMP $(2<<3):$mode32bit(SB) /**/ + BYTE $0xEA + LONG $mode32bit-KZERO(SB) + WORD $(2<<3) + +/* + * gdt to get us to 32-bit/segmented/unpaged mode + */ +TEXT tgdt(SB), $0 + + /* null descriptor */ + LONG $0 + LONG $0 + + /* data segment descriptor for 4 gigabytes (PL 0) */ + LONG $(0xFFFF) + LONG $(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW) + + /* exec segment descriptor for 4 gigabytes (PL 0) */ + LONG $(0xFFFF) + LONG $(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR) + +/* + * pointer to initial gdt + * Note the -KZERO which puts the physical address in the gdtptr. + * that's needed as we start executing in physical addresses. + */ +TEXT tgdtptr(SB), $0 + + WORD $(3*8) + LONG $tgdt-KZERO(SB) + +TEXT mode32bit(SB), $0 + /* At this point, the GDT setup is done. */ + + MOVL $PADDR(CPU0PDB), DI /* clear 4 pages for the tables etc. */ + XORL AX, AX + MOVL $(4*BY2PG), CX + SHRL $2, CX + + CLD + REP; STOSL + + MOVL $PADDR(CPU0PDB), AX + ADDL $PDO(KZERO), AX /* page directory offset for KZERO */ + MOVL $PADDR(CPU0PTE), (AX) /* PTE's for 0x80000000 */ + MOVL $(PTEWRITE|PTEVALID), BX /* page permissions */ + ORL BX, (AX) + + MOVL $PADDR(CPU0PTE), AX /* first page of page table */ + MOVL $1024, CX /* 1024 pages in 4MB */ +_setpte: + MOVL BX, (AX) + ADDL $(1<machno */ + + ADDL $(MACHSIZE-4), SP /* initialise stack */ + +/* + * Need to do one final thing to ensure a clean machine environment, + * clear the EFLAGS register, which can only be done once there is a stack. + */ + MOVL $0, AX + PUSHL AX + POPFL + + CALL main(SB) + +/* + * Park a processor. Should never fall through a return from main to here, + * should only be called by application processors when shutting down. + */ +TEXT idle(SB), $0 +_idle: + STI + HLT + JMP _idle + +/* + * Port I/O. + * in[bsl] input a byte|short|long + * ins[bsl] input a string of bytes|shorts|longs + * out[bsl] output a byte|short|long + * outs[bsl] output a string of bytes|shorts|longs + */ +TEXT inb(SB), $0 + MOVL port+0(FP), DX + XORL AX, AX + INB + RET + +TEXT insb(SB), $0 + MOVL port+0(FP), DX + MOVL address+4(FP), DI + MOVL count+8(FP), CX + CLD + REP; INSB + RET + +TEXT ins(SB), $0 + MOVL port+0(FP), DX + XORL AX, AX + OP16; INL + RET + +TEXT inss(SB), $0 + MOVL port+0(FP), DX + MOVL address+4(FP), DI + MOVL count+8(FP), CX + CLD + REP; OP16; INSL + RET + +TEXT inl(SB), $0 + MOVL port+0(FP), DX + INL + RET + +TEXT insl(SB), $0 + MOVL port+0(FP), DX + MOVL address+4(FP), DI + MOVL count+8(FP), CX + CLD + REP; INSL + RET + +TEXT outb(SB), $0 + MOVL port+0(FP), DX + MOVL byte+4(FP), AX + OUTB + RET + +TEXT outsb(SB), $0 + MOVL port+0(FP), DX + MOVL address+4(FP), SI + MOVL count+8(FP), CX + CLD + REP; OUTSB + RET + +TEXT outs(SB), $0 + MOVL port+0(FP), DX + MOVL short+4(FP), AX + OP16; OUTL + RET + +TEXT outss(SB), $0 + MOVL port+0(FP), DX + MOVL address+4(FP), SI + MOVL count+8(FP), CX + CLD + REP; OP16; OUTSL + RET + +TEXT outl(SB), $0 + MOVL port+0(FP), DX + MOVL long+4(FP), AX + OUTL + RET + +TEXT outsl(SB), $0 + MOVL port+0(FP), DX + MOVL address+4(FP), SI + MOVL count+8(FP), CX + CLD + REP; OUTSL + RET + +/* there's a macro in fns.h but libinterp can't see it */ +TEXT getcallerpc(SB), $0 + MOVL a+0(FP), AX + RET + +/* + * Read/write various system registers. + * CR4 and the 'model specific registers' should only be read/written + * after it has been determined the processor supports them + */ +TEXT lgdt(SB), $0 /* GDTR - global descriptor table */ + MOVL gdtptr+0(FP), AX + MOVL (AX), GDTR + RET + +TEXT lidt(SB), $0 /* IDTR - interrupt descriptor table */ + MOVL idtptr+0(FP), AX + MOVL (AX), IDTR + RET + +TEXT ltr(SB), $0 /* TR - task register */ + MOVL tptr+0(FP), AX + MOVW AX, TASK + RET + +TEXT getcr0(SB), $0 /* CR0 - processor control */ + MOVL CR0, AX + RET + +TEXT getcr2(SB), $0 /* CR2 - page fault linear address */ + MOVL CR2, AX + RET + +TEXT getcr3(SB), $0 /* CR3 - page directory base */ + MOVL CR3, AX + RET + +TEXT putcr3(SB), $0 + MOVL cr3+0(FP), AX + MOVL AX, CR3 + RET + +TEXT getcr4(SB), $0 /* CR4 - extensions */ + MOVL CR4, AX + RET + +TEXT putcr4(SB), $0 + MOVL cr4+0(FP), AX + MOVL AX, CR4 + RET + +TEXT _cycles(SB), $0 /* time stamp counter; cycles since power up */ + RDTSC + MOVL vlong+0(FP), CX /* &vlong */ + MOVL AX, 0(CX) /* lo */ + MOVL DX, 4(CX) /* hi */ + RET + +TEXT rdmsr(SB), $0 /* model-specific register */ + MOVL index+0(FP), CX + RDMSR + MOVL vlong+4(FP), CX /* &vlong */ + MOVL AX, 0(CX) /* lo */ + MOVL DX, 4(CX) /* hi */ + RET + +TEXT wrmsr(SB), $0 + MOVL index+0(FP), CX + MOVL lo+4(FP), AX + MOVL hi+8(FP), DX + WRMSR + RET + +TEXT wbinvd(SB), $0 + WBINVD + RET + +TEXT rdtsc32(SB), $0 + CPUID + RDTSC + RET + +/* + * Try to determine the CPU type which requires fiddling with EFLAGS. + * If the Id bit can be toggled then the CPUID instruction can be used + * to determine CPU identity and features. First have to check if it's + * a 386 (Ac bit can't be set). If it's not a 386 and the Id bit can't be + * toggled then it's an older 486 of some kind. + * + * cpuid(id[], &ax, &dx); + */ +TEXT cpuid(SB), $0 + MOVL $0x240000, AX + PUSHL AX + POPFL /* set Id|Ac */ + + PUSHFL + POPL BX /* retrieve value */ + + MOVL $0, AX + PUSHL AX + POPFL /* clear Id|Ac, EFLAGS initialised */ + + PUSHFL + POPL AX /* retrieve value */ + XORL BX, AX + TESTL $0x040000, AX /* Ac */ + JZ _cpu386 /* can't set this bit on 386 */ + TESTL $0x200000, AX /* Id */ + JZ _cpu486 /* can't toggle this bit on some 486 */ + + MOVL $0, AX + CPUID + MOVL id+0(FP), BP + MOVL BX, 0(BP) /* "Genu" "Auth" "Cyri" */ + MOVL DX, 4(BP) /* "ineI" "enti" "xIns" */ + MOVL CX, 8(BP) /* "ntel" "cAMD" "tead" */ + + MOVL $1, AX + CPUID + JMP _cpuid + +_cpu486: + MOVL $0x400, AX + MOVL $0, DX + JMP _cpuid + +_cpu386: + MOVL $0x300, AX + MOVL $0, DX + +_cpuid: + MOVL ax+4(FP), BP + MOVL AX, 0(BP) + MOVL dx+8(FP), BP + MOVL DX, 0(BP) + RET + +/* + * Basic timing loop to determine CPU frequency. + */ +TEXT aamloop(SB), $0 + MOVL count+0(FP), CX +_aamloop: + AAM + LOOP _aamloop + RET + +/* + * Floating point. + * Note: the encodings for the FCLEX, FINIT, FSAVE, FSTCW, FSENV and FSTSW + * instructions do NOT have the WAIT prefix byte (i.e. they act like their + * FNxxx variations) so WAIT instructions must be explicitly placed in the + * code as necessary. + */ +#define FPOFF(l) ;\ + MOVL CR0, AX ;\ + ANDL $0xC, AX /* EM, TS */ ;\ + CMPL AX, $0x8 ;\ + JEQ l ;\ + WAIT ;\ +l: ;\ + MOVL CR0, AX ;\ + ANDL $~0x4, AX /* EM=0 */ ;\ + ORL $0x28, AX /* NE=1, TS=1 */ ;\ + MOVL AX, CR0 + +#define FPON ;\ + MOVL CR0, AX ;\ + ANDL $~0xC, AX /* EM=0, TS=0 */ ;\ + MOVL AX, CR0 + +TEXT fpoff(SB), $0 /* disable */ + FPOFF(l1) + RET + +TEXT fpinit(SB), $0 /* enable and init */ + FPON + FINIT + WAIT + /* setfcr(FPPDBL|FPRNR|FPINVAL|FPZDIV|FPOVFL) */ + /* note that low 6 bits are masks, not enables, on this chip */ + PUSHW $0x0232 + FLDCW 0(SP) + POPW AX + WAIT + RET + +TEXT fpsave(SB), $0 /* save state and disable */ + MOVL p+0(FP), AX + FSAVE 0(AX) /* no WAIT */ + FPOFF(l2) + RET + +TEXT fprestore(SB), $0 /* enable and restore state */ + FPON + MOVL p+0(FP), AX + FRSTOR 0(AX) + WAIT + RET + +TEXT fpstatus(SB), $0 /* get floating point status */ + FSTSW AX + RET + +TEXT fpenv(SB), $0 /* save state without waiting */ + MOVL p+0(FP), AX + FSTENV 0(AX) + RET + +TEXT fpclear(SB), $0 /* clear pending exceptions */ + FPON + FCLEX /* no WAIT */ + FPOFF(l3) + RET + +/* + */ +TEXT splhi(SB), $0 + MOVL $(MACHADDR+0x04), AX /* save PC in m->splpc */ + MOVL (SP), BX + MOVL BX, (AX) + + PUSHFL + POPL AX + CLI + RET + +TEXT spllo(SB), $0 + PUSHFL + POPL AX + STI + RET + +TEXT splx(SB), $0 + MOVL $(MACHADDR+0x04), AX /* save PC in m->splpc */ + MOVL (SP), BX + MOVL BX, (AX) + /*FALLTHROUGH*/ + +TEXT splxpc(SB), $0 /* for iunlock */ + MOVL s+0(FP), AX + PUSHL AX + POPFL + RET + +TEXT spldone(SB), $0 + RET + +TEXT islo(SB), $0 + PUSHFL + POPL AX + ANDL $0x200, AX /* interrupt enable flag */ + RET + +/* + * Test-And-Set + */ +TEXT _tas(SB), $0 + MOVL $0xDEADDEAD, AX + MOVL lock+0(FP), BX + XCHGL AX, (BX) /* lock->key */ + RET + +TEXT _xinc(SB), $0 /* void _xinc(long*); */ + MOVL l+0(FP), AX + LOCK; INCL 0(AX) + RET + +TEXT _xdec(SB), $0 /* long _xdec(long*); */ + MOVL l+0(FP), BX + XORL AX, AX + LOCK; DECL 0(BX) + JLT _xdeclt + JGT _xdecgt + RET +_xdecgt: + INCL AX + RET +_xdeclt: + DECL AX + RET + +TEXT mb386(SB), $0 + POPL AX /* return PC */ + PUSHFL + PUSHL CS + PUSHL AX + IRETL + +TEXT mb586(SB), $0 + XORL AX, AX + CPUID + RET + +TEXT xchgw(SB), $0 + MOVL v+4(FP), AX + MOVL p+0(FP), BX + XCHGW AX, (BX) + RET + +TEXT mul64fract(SB), $0 +/* + * Multiply two 64-bit number s and keep the middle 64 bits from the 128-bit result + * See ../port/tod.c for motivation. + */ + MOVL r+0(FP), CX + XORL BX, BX /* BX = 0 */ + + MOVL a+8(FP), AX + MULL b+16(FP) /* a1*b1 */ + MOVL AX, 4(CX) /* r2 = lo(a1*b1) */ + + MOVL a+8(FP), AX + MULL b+12(FP) /* a1*b0 */ + MOVL AX, 0(CX) /* r1 = lo(a1*b0) */ + ADDL DX, 4(CX) /* r2 += hi(a1*b0) */ + + MOVL a+4(FP), AX + MULL b+16(FP) /* a0*b1 */ + ADDL AX, 0(CX) /* r1 += lo(a0*b1) */ + ADCL DX, 4(CX) /* r2 += hi(a0*b1) + carry */ + + MOVL a+4(FP), AX + MULL b+12(FP) /* a0*b0 */ + ADDL DX, 0(CX) /* r1 += hi(a0*b0) */ + ADCL BX, 4(CX) /* r2 += carry */ + RET + +/* + * label consists of a stack pointer and a PC + */ +TEXT gotolabel(SB), $0 + MOVL label+0(FP), AX + MOVL 0(AX), SP /* restore sp */ + MOVL 4(AX), AX /* put return pc on the stack */ + MOVL AX, 0(SP) + MOVL $1, AX /* return 1 */ + RET + +TEXT setlabel(SB), $0 + MOVL label+0(FP), AX + MOVL SP, 0(AX) /* store sp */ + MOVL 0(SP), BX /* store return pc */ + MOVL BX, 4(AX) + MOVL $0, AX /* return 0 */ + RET + +TEXT halt(SB), $0 + STI + HLT + RET + +/* + * Interrupt/exception handling. + * Each entry in the vector table calls either _strayintr or _strayintrx depending + * on whether an error code has been automatically pushed onto the stack + * (_strayintrx) or not, in which case a dummy entry must be pushed before retrieving + * the trap type from the vector table entry and placing it on the stack as part + * of the Ureg structure. + * The size of each entry in the vector table (6 bytes) is known in trapinit(). + */ +TEXT _strayintr(SB), $0 + PUSHL AX /* save AX */ + MOVL 4(SP), AX /* return PC from vectortable(SB) */ + JMP intrcommon + +TEXT _strayintrx(SB), $0 + XCHGL AX, (SP) /* exchange AX with pointer to trap type */ +intrcommon: + PUSHL DS + MOVBLZX (AX), AX /* trap type -> AX */ + XCHGL AX, 4(SP) /* exchange trap type with AX */ + PUSHL ES + PUSHL FS + PUSHL GS + PUSHAL + MOVL $(KDSEL), AX + MOVW AX, DS + MOVW AX, ES + PUSHL SP /* Ureg* argument to trap */ + CALL trap(SB) + +TEXT forkret(SB), $0 + POPL AX + POPAL + POPL GS + POPL FS + POPL ES + POPL DS + ADDL $8, SP /* pop error code and trap type */ + IRETL + +TEXT vectortable(SB), $0 + CALL _strayintr(SB); BYTE $0x00 /* divide error */ + CALL _strayintr(SB); BYTE $0x01 /* debug exception */ + CALL _strayintr(SB); BYTE $0x02 /* NMI interrupt */ + CALL _strayintr(SB); BYTE $0x03 /* breakpoint */ + CALL _strayintr(SB); BYTE $0x04 /* overflow */ + CALL _strayintr(SB); BYTE $0x05 /* bound */ + CALL _strayintr(SB); BYTE $0x06 /* invalid opcode */ + CALL _strayintr(SB); BYTE $0x07 /* no coprocessor available */ + CALL _strayintrx(SB); BYTE $0x08 /* double fault */ + CALL _strayintr(SB); BYTE $0x09 /* coprocessor segment overflow */ + CALL _strayintrx(SB); BYTE $0x0A /* invalid TSS */ + CALL _strayintrx(SB); BYTE $0x0B /* segment not available */ + CALL _strayintrx(SB); BYTE $0x0C /* stack exception */ + CALL _strayintrx(SB); BYTE $0x0D /* general protection error */ + CALL _strayintrx(SB); BYTE $0x0E /* page fault */ + CALL _strayintr(SB); BYTE $0x0F /* */ + CALL _strayintr(SB); BYTE $0x10 /* coprocessor error */ + CALL _strayintrx(SB); BYTE $0x11 /* alignment check */ + CALL _strayintr(SB); BYTE $0x12 /* machine check */ + CALL _strayintr(SB); BYTE $0x13 + CALL _strayintr(SB); BYTE $0x14 + CALL _strayintr(SB); BYTE $0x15 + CALL _strayintr(SB); BYTE $0x16 + CALL _strayintr(SB); BYTE $0x17 + CALL _strayintr(SB); BYTE $0x18 + CALL _strayintr(SB); BYTE $0x19 + CALL _strayintr(SB); BYTE $0x1A + CALL _strayintr(SB); BYTE $0x1B + CALL _strayintr(SB); BYTE $0x1C + CALL _strayintr(SB); BYTE $0x1D + CALL _strayintr(SB); BYTE $0x1E + CALL _strayintr(SB); BYTE $0x1F + CALL _strayintr(SB); BYTE $0x20 /* VectorLAPIC */ + CALL _strayintr(SB); BYTE $0x21 + CALL _strayintr(SB); BYTE $0x22 + CALL _strayintr(SB); BYTE $0x23 + CALL _strayintr(SB); BYTE $0x24 + CALL _strayintr(SB); BYTE $0x25 + CALL _strayintr(SB); BYTE $0x26 + CALL _strayintr(SB); BYTE $0x27 + CALL _strayintr(SB); BYTE $0x28 + CALL _strayintr(SB); BYTE $0x29 + CALL _strayintr(SB); BYTE $0x2A + CALL _strayintr(SB); BYTE $0x2B + CALL _strayintr(SB); BYTE $0x2C + CALL _strayintr(SB); BYTE $0x2D + CALL _strayintr(SB); BYTE $0x2E + CALL _strayintr(SB); BYTE $0x2F + CALL _strayintr(SB); BYTE $0x30 + CALL _strayintr(SB); BYTE $0x31 + CALL _strayintr(SB); BYTE $0x32 + CALL _strayintr(SB); BYTE $0x33 + CALL _strayintr(SB); BYTE $0x34 + CALL _strayintr(SB); BYTE $0x35 + CALL _strayintr(SB); BYTE $0x36 + CALL _strayintr(SB); BYTE $0x37 + CALL _strayintr(SB); BYTE $0x38 + CALL _strayintr(SB); BYTE $0x39 + CALL _strayintr(SB); BYTE $0x3A + CALL _strayintr(SB); BYTE $0x3B + CALL _strayintr(SB); BYTE $0x3C + CALL _strayintr(SB); BYTE $0x3D + CALL _strayintr(SB); BYTE $0x3E + CALL _strayintr(SB); BYTE $0x3F + CALL _strayintr(SB); BYTE $0x40 /* VectorSYSCALL */ + CALL _strayintr(SB); BYTE $0x41 + CALL _strayintr(SB); BYTE $0x42 + CALL _strayintr(SB); BYTE $0x43 + CALL _strayintr(SB); BYTE $0x44 + CALL _strayintr(SB); BYTE $0x45 + CALL _strayintr(SB); BYTE $0x46 + CALL _strayintr(SB); BYTE $0x47 + CALL _strayintr(SB); BYTE $0x48 + CALL _strayintr(SB); BYTE $0x49 + CALL _strayintr(SB); BYTE $0x4A + CALL _strayintr(SB); BYTE $0x4B + CALL _strayintr(SB); BYTE $0x4C + CALL _strayintr(SB); BYTE $0x4D + CALL _strayintr(SB); BYTE $0x4E + CALL _strayintr(SB); BYTE $0x4F + CALL _strayintr(SB); BYTE $0x50 + CALL _strayintr(SB); BYTE $0x51 + CALL _strayintr(SB); BYTE $0x52 + CALL _strayintr(SB); BYTE $0x53 + CALL _strayintr(SB); BYTE $0x54 + CALL _strayintr(SB); BYTE $0x55 + CALL _strayintr(SB); BYTE $0x56 + CALL _strayintr(SB); BYTE $0x57 + CALL _strayintr(SB); BYTE $0x58 + CALL _strayintr(SB); BYTE $0x59 + CALL _strayintr(SB); BYTE $0x5A + CALL _strayintr(SB); BYTE $0x5B + CALL _strayintr(SB); BYTE $0x5C + CALL _strayintr(SB); BYTE $0x5D + CALL _strayintr(SB); BYTE $0x5E + CALL _strayintr(SB); BYTE $0x5F + CALL _strayintr(SB); BYTE $0x60 + CALL _strayintr(SB); BYTE $0x61 + CALL _strayintr(SB); BYTE $0x62 + CALL _strayintr(SB); BYTE $0x63 + CALL _strayintr(SB); BYTE $0x64 + CALL _strayintr(SB); BYTE $0x65 + CALL _strayintr(SB); BYTE $0x66 + CALL _strayintr(SB); BYTE $0x67 + CALL _strayintr(SB); BYTE $0x68 + CALL _strayintr(SB); BYTE $0x69 + CALL _strayintr(SB); BYTE $0x6A + CALL _strayintr(SB); BYTE $0x6B + CALL _strayintr(SB); BYTE $0x6C + CALL _strayintr(SB); BYTE $0x6D + CALL _strayintr(SB); BYTE $0x6E + CALL _strayintr(SB); BYTE $0x6F + CALL _strayintr(SB); BYTE $0x70 + CALL _strayintr(SB); BYTE $0x71 + CALL _strayintr(SB); BYTE $0x72 + CALL _strayintr(SB); BYTE $0x73 + CALL _strayintr(SB); BYTE $0x74 + CALL _strayintr(SB); BYTE $0x75 + CALL _strayintr(SB); BYTE $0x76 + CALL _strayintr(SB); BYTE $0x77 + CALL _strayintr(SB); BYTE $0x78 + CALL _strayintr(SB); BYTE $0x79 + CALL _strayintr(SB); BYTE $0x7A + CALL _strayintr(SB); BYTE $0x7B + CALL _strayintr(SB); BYTE $0x7C + CALL _strayintr(SB); BYTE $0x7D + CALL _strayintr(SB); BYTE $0x7E + CALL _strayintr(SB); BYTE $0x7F + CALL _strayintr(SB); BYTE $0x80 /* Vector[A]PIC */ + CALL _strayintr(SB); BYTE $0x81 + CALL _strayintr(SB); BYTE $0x82 + CALL _strayintr(SB); BYTE $0x83 + CALL _strayintr(SB); BYTE $0x84 + CALL _strayintr(SB); BYTE $0x85 + CALL _strayintr(SB); BYTE $0x86 + CALL _strayintr(SB); BYTE $0x87 + CALL _strayintr(SB); BYTE $0x88 + CALL _strayintr(SB); BYTE $0x89 + CALL _strayintr(SB); BYTE $0x8A + CALL _strayintr(SB); BYTE $0x8B + CALL _strayintr(SB); BYTE $0x8C + CALL _strayintr(SB); BYTE $0x8D + CALL _strayintr(SB); BYTE $0x8E + CALL _strayintr(SB); BYTE $0x8F + CALL _strayintr(SB); BYTE $0x90 + CALL _strayintr(SB); BYTE $0x91 + CALL _strayintr(SB); BYTE $0x92 + CALL _strayintr(SB); BYTE $0x93 + CALL _strayintr(SB); BYTE $0x94 + CALL _strayintr(SB); BYTE $0x95 + CALL _strayintr(SB); BYTE $0x96 + CALL _strayintr(SB); BYTE $0x97 + CALL _strayintr(SB); BYTE $0x98 + CALL _strayintr(SB); BYTE $0x99 + CALL _strayintr(SB); BYTE $0x9A + CALL _strayintr(SB); BYTE $0x9B + CALL _strayintr(SB); BYTE $0x9C + CALL _strayintr(SB); BYTE $0x9D + CALL _strayintr(SB); BYTE $0x9E + CALL _strayintr(SB); BYTE $0x9F + CALL _strayintr(SB); BYTE $0xA0 + CALL _strayintr(SB); BYTE $0xA1 + CALL _strayintr(SB); BYTE $0xA2 + CALL _strayintr(SB); BYTE $0xA3 + CALL _strayintr(SB); BYTE $0xA4 + CALL _strayintr(SB); BYTE $0xA5 + CALL _strayintr(SB); BYTE $0xA6 + CALL _strayintr(SB); BYTE $0xA7 + CALL _strayintr(SB); BYTE $0xA8 + CALL _strayintr(SB); BYTE $0xA9 + CALL _strayintr(SB); BYTE $0xAA + CALL _strayintr(SB); BYTE $0xAB + CALL _strayintr(SB); BYTE $0xAC + CALL _strayintr(SB); BYTE $0xAD + CALL _strayintr(SB); BYTE $0xAE + CALL _strayintr(SB); BYTE $0xAF + CALL _strayintr(SB); BYTE $0xB0 + CALL _strayintr(SB); BYTE $0xB1 + CALL _strayintr(SB); BYTE $0xB2 + CALL _strayintr(SB); BYTE $0xB3 + CALL _strayintr(SB); BYTE $0xB4 + CALL _strayintr(SB); BYTE $0xB5 + CALL _strayintr(SB); BYTE $0xB6 + CALL _strayintr(SB); BYTE $0xB7 + CALL _strayintr(SB); BYTE $0xB8 + CALL _strayintr(SB); BYTE $0xB9 + CALL _strayintr(SB); BYTE $0xBA + CALL _strayintr(SB); BYTE $0xBB + CALL _strayintr(SB); BYTE $0xBC + CALL _strayintr(SB); BYTE $0xBD + CALL _strayintr(SB); BYTE $0xBE + CALL _strayintr(SB); BYTE $0xBF + CALL _strayintr(SB); BYTE $0xC0 + CALL _strayintr(SB); BYTE $0xC1 + CALL _strayintr(SB); BYTE $0xC2 + CALL _strayintr(SB); BYTE $0xC3 + CALL _strayintr(SB); BYTE $0xC4 + CALL _strayintr(SB); BYTE $0xC5 + CALL _strayintr(SB); BYTE $0xC6 + CALL _strayintr(SB); BYTE $0xC7 + CALL _strayintr(SB); BYTE $0xC8 + CALL _strayintr(SB); BYTE $0xC9 + CALL _strayintr(SB); BYTE $0xCA + CALL _strayintr(SB); BYTE $0xCB + CALL _strayintr(SB); BYTE $0xCC + CALL _strayintr(SB); BYTE $0xCD + CALL _strayintr(SB); BYTE $0xCE + CALL _strayintr(SB); BYTE $0xCF + CALL _strayintr(SB); BYTE $0xD0 + CALL _strayintr(SB); BYTE $0xD1 + CALL _strayintr(SB); BYTE $0xD2 + CALL _strayintr(SB); BYTE $0xD3 + CALL _strayintr(SB); BYTE $0xD4 + CALL _strayintr(SB); BYTE $0xD5 + CALL _strayintr(SB); BYTE $0xD6 + CALL _strayintr(SB); BYTE $0xD7 + CALL _strayintr(SB); BYTE $0xD8 + CALL _strayintr(SB); BYTE $0xD9 + CALL _strayintr(SB); BYTE $0xDA + CALL _strayintr(SB); BYTE $0xDB + CALL _strayintr(SB); BYTE $0xDC + CALL _strayintr(SB); BYTE $0xDD + CALL _strayintr(SB); BYTE $0xDE + CALL _strayintr(SB); BYTE $0xDF + CALL _strayintr(SB); BYTE $0xE0 + CALL _strayintr(SB); BYTE $0xE1 + CALL _strayintr(SB); BYTE $0xE2 + CALL _strayintr(SB); BYTE $0xE3 + CALL _strayintr(SB); BYTE $0xE4 + CALL _strayintr(SB); BYTE $0xE5 + CALL _strayintr(SB); BYTE $0xE6 + CALL _strayintr(SB); BYTE $0xE7 + CALL _strayintr(SB); BYTE $0xE8 + CALL _strayintr(SB); BYTE $0xE9 + CALL _strayintr(SB); BYTE $0xEA + CALL _strayintr(SB); BYTE $0xEB + CALL _strayintr(SB); BYTE $0xEC + CALL _strayintr(SB); BYTE $0xED + CALL _strayintr(SB); BYTE $0xEE + CALL _strayintr(SB); BYTE $0xEF + CALL _strayintr(SB); BYTE $0xF0 + CALL _strayintr(SB); BYTE $0xF1 + CALL _strayintr(SB); BYTE $0xF2 + CALL _strayintr(SB); BYTE $0xF3 + CALL _strayintr(SB); BYTE $0xF4 + CALL _strayintr(SB); BYTE $0xF5 + CALL _strayintr(SB); BYTE $0xF6 + CALL _strayintr(SB); BYTE $0xF7 + CALL _strayintr(SB); BYTE $0xF8 + CALL _strayintr(SB); BYTE $0xF9 + CALL _strayintr(SB); BYTE $0xFA + CALL _strayintr(SB); BYTE $0xFB + CALL _strayintr(SB); BYTE $0xFC + CALL _strayintr(SB); BYTE $0xFD + CALL _strayintr(SB); BYTE $0xFE + CALL _strayintr(SB); BYTE $0xFF diff --git a/os/pc/main.c b/os/pc/main.c new file mode 100644 index 00000000..df4f240e --- /dev/null +++ b/os/pc/main.c @@ -0,0 +1,457 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +extern int main_pool_pcnt; +extern int heap_pool_pcnt; +extern int image_pool_pcnt; +int pckdebug; + +Mach *m; + +static uchar *sp; /* stack pointer for /boot */ + +/* + * Where configuration info is left for the loaded programme. + * This will turn into a structure as more is done by the boot loader + * (e.g. why parse the .ini file twice?). + * There are 3584 bytes available at CONFADDR. + */ +#define BOOTLINE ((char*)CONFADDR) +#define BOOTLINELEN 64 +#define BOOTARGS ((char*)(CONFADDR+BOOTLINELEN)) +#define BOOTARGSLEN (4096-0x200-BOOTLINELEN) +#define MAXCONF 64 + +char bootdisk[KNAMELEN]; +char *confname[MAXCONF]; +char *confval[MAXCONF]; +int nconf; + +static void +options(void) +{ + long i, n; + char *cp, *line[MAXCONF], *p, *q; + + /* + * parse configuration args from dos file plan9.ini + */ + cp = BOOTARGS; /* where b.com leaves its config */ + cp[BOOTARGSLEN-1] = 0; + + /* + * Strip out '\r', change '\t' -> ' '. + */ + p = cp; + for(q = cp; *q; q++){ + if(*q == '\r') + continue; + if(*q == '\t') + *q = ' '; + *p++ = *q; + } + *p = 0; + + n = getfields(cp, line, MAXCONF, 1, "\n"); + for(i = 0; i < n; i++){ + if(*line[i] == '#') + continue; + cp = strchr(line[i], '='); + if(cp == nil) + continue; + *cp++ = '\0'; + confname[nconf] = line[i]; + confval[nconf] = cp; + nconf++; + } +} + +static void +doc(char *m) +{ + int i; + print("%s...\n", m); + for(i = 0; i < 100*1024*1024; i++) + i++; +} + +void +main(void) +{ + outb(0x3F2, 0x00); /* botch: turn off the floppy motor */ + + mach0init(); + options(); + ioinit(); + i8250console(); + quotefmtinstall(); + kbdinit(); + i8253init(); + cpuidentify(); + confinit(); + archinit(); + xinit(); + poolsizeinit(); + trapinit(); + printinit(); + screeninit(); + cpuidprint(); + mmuinit(); + eve = strdup("inferno"); + if(arch->intrinit){ /* launches other processors on an mp */ + doc("intrinit"); + arch->intrinit(); + } + doc("timersinit"); + timersinit(); + doc("mathinit"); + mathinit(); + doc("kbdenable"); + kbdenable(); + if(arch->clockenable){ + doc("clockinit"); + arch->clockenable(); + } + doc("procinit"); + procinit(); + doc("links"); + links(); + doc("chandevreset"); + chandevreset(); + doc("userinit"); + userinit(); + doc("schedinit"); + active.thunderbirdsarego = 1; + schedinit(); + +} + +void +mach0init(void) +{ + conf.nmach = 1; + MACHP(0) = (Mach*)CPU0MACH; + m->pdb = (ulong*)CPU0PDB; + m->gdt = (Segdesc*)CPU0GDT; + + machinit(); + + active.machs = 1; + active.exiting = 0; +} + +void +machinit(void) +{ + int machno; + ulong *pdb; + Segdesc *gdt; + + machno = m->machno; + pdb = m->pdb; + gdt = m->gdt; + memset(m, 0, sizeof(Mach)); + m->machno = machno; + m->pdb = pdb; + m->gdt = gdt; + + /* + * For polled uart output at boot, need + * a default delay constant. 100000 should + * be enough for a while. Cpuidentify will + * calculate the real value later. + */ + m->loopconst = 100000; +} + +void +init0(void) +{ + Osenv *o; + int i; + char buf[2*KNAMELEN]; + + up->nerrlab = 0; + + spllo(); + if(waserror()) + panic("init0: %r"); + /* + * These are o.k. because rootinit is null. + * Then early kproc's will have a root and dot. + */ + o = up->env; + o->pgrp->slash = namec("#/", Atodir, 0, 0); + cnameclose(o->pgrp->slash->name); + o->pgrp->slash->name = newcname("/"); + o->pgrp->dot = cclone(o->pgrp->slash); + + chandevinit(); + + if(!waserror()){ + ksetenv("cputype", "386", 0); + snprint(buf, sizeof(buf), "386 %s", conffile); + ksetenv("terminal", buf, 0); + for(i = 0; i < nconf; i++){ + if(confname[i][0] != '*') + ksetenv(confname[i], confval[i], 0); + ksetenv(confname[i], confval[i], 1); + } + poperror(); + } + + poperror(); + + disinit("/osinit.dis"); +} + +void +userinit(void) +{ + Proc *p; + Osenv *o; + + p = newproc(); + o = p->env; + + o->fgrp = newfgrp(nil); + + o->pgrp = newpgrp(); + kstrdup(&o->user, eve); + + strcpy(p->text, "interp"); + + p->fpstate = FPINIT; + fpoff(); + + /* + * Kernel Stack + * + * N.B. make sure there's + * 4 bytes for gotolabel's return PC + */ + p->sched.pc = (ulong)init0; + p->sched.sp = (ulong)p->kstack+KSTACK-BY2WD; + + ready(p); +} + +Conf conf; + +char* +getconf(char *name) +{ + int i; + + for(i = 0; i < nconf; i++) + if(cistrcmp(confname[i], name) == 0) + return confval[i]; + return 0; +} + +void +confinit(void) +{ + char *p; + int pcnt; + ulong maxmem; + + if(p = getconf("*maxmem")) + maxmem = strtoul(p, 0, 0); + else + maxmem = 0; + if(p = getconf("*kernelpercent")) + pcnt = 100 - strtol(p, 0, 0); + else + pcnt = 0; + + meminit(maxmem); + + conf.npage = conf.npage0 + conf.npage1; + if(pcnt < 10) + pcnt = 70; + conf.ialloc = (((conf.npage*(100-pcnt))/100)/2)*BY2PG; + + conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5; +} + +void +poolsizeinit(void) +{ + ulong nb; + + nb = conf.npage*BY2PG; + poolsize(mainmem, (nb*main_pool_pcnt)/100, 0); + poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0); + poolsize(imagmem, (nb*image_pool_pcnt)/100, 1); +} + +static char *mathmsg[] = +{ + "invalid operation", + "denormalized operand", + "division by zero", + "numeric overflow", + "numeric underflow", + "precision loss", + "stack", + "error", +}; + +/* + * math coprocessor error + */ +void +matherror(Ureg* ureg, void* arg) +{ + ulong status; + int i; + char *msg; + char note[ERRMAX]; + + USED(arg); + + /* + * a write cycle to port 0xF0 clears the interrupt latch attached + * to the error# line from the 387 + */ + if(!(m->cpuiddx & 0x01)) + outb(0xF0, 0xFF); + + /* + * save floating point state to check out error + */ + FPsave(&up->fpsave.env); + status = up->fpsave.env.status; + + msg = 0; + for(i = 0; i < 8; i++) + if((1<fpsave.env.pc); + error(note); + break; + } + if(msg == 0){ + sprint(note, "sys: fp: unknown fppc=0x%lux", up->fpsave.env.pc); + error(note); + } + if(ureg->pc & KZERO) + panic("fp: status %lux fppc=0x%lux pc=0x%lux", status, + up->fpsave.env.pc, ureg->pc); +} + +/* + * math coprocessor emulation fault + */ +void +mathemu(Ureg* ureg, void* arg) +{ + USED(ureg, arg); + switch(up->fpstate){ + case FPINIT: + fpinit(); + up->fpstate = FPACTIVE; + break; + case FPINACTIVE: + fprestore(&up->fpsave); + up->fpstate = FPACTIVE; + break; + case FPACTIVE: + panic("math emu"); + break; + } +} + +/* + * math coprocessor segment overrun + */ +void +mathover(Ureg* ureg, void* arg) +{ + USED(arg); + print("sys: fp: math overrun pc 0x%lux pid %ld\n", ureg->pc, up->pid); + pexit("math overrun", 0); +} + +void +mathinit(void) +{ + trapenable(VectorCERR, matherror, 0, "matherror"); + if(X86FAMILY(m->cpuidax) == 3) + intrenable(IrqIRQ13, matherror, 0, BUSUNKNOWN, "matherror"); + trapenable(VectorCNA, mathemu, 0, "mathemu"); + trapenable(VectorCSO, mathover, 0, "mathover"); +} + +/* + * Save the mach dependent part of the process state. + */ +void +procsave(Proc *p) +{ + if(p->fpstate == FPACTIVE){ + if(p->state == Moribund) + fpoff(); + else + fpsave(&up->fpsave); + p->fpstate = FPINACTIVE; + } +} + +void +exit(int ispanic) +{ + USED(ispanic); + + up = 0; + print("exiting\n"); + + /* Shutdown running devices */ + chandevshutdown(); + + arch->reset(); +} + +void +reboot(void) +{ + exit(0); +} + +int +isaconfig(char *class, int ctlrno, ISAConf *isa) +{ + char cc[32], *p; + int i; + + snprint(cc, sizeof cc, "%s%d", class, ctlrno); + p = getconf(cc); + if(p == nil) + return 0; + + isa->nopt = tokenize(p, isa->opt, NISAOPT); + for(i = 0; i < isa->nopt; i++){ + p = isa->opt[i]; + if(cistrncmp(p, "type=", 5) == 0) + isa->type = p + 5; + else if(cistrncmp(p, "port=", 5) == 0) + isa->port = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "irq=", 4) == 0) + isa->irq = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "dma=", 4) == 0) + isa->dma = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "mem=", 4) == 0) + isa->mem = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "size=", 5) == 0) + isa->size = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "freq=", 5) == 0) + isa->freq = strtoul(p+5, &p, 0); + } + return 1; +} diff --git a/os/pc/mem.h b/os/pc/mem.h new file mode 100644 index 00000000..b67252ee --- /dev/null +++ b/os/pc/mem.h @@ -0,0 +1,144 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ + +/* + * Sizes + */ +#define BI2BY 8 /* bits per byte */ +#define BI2WD 32 /* bits per word */ +#define BY2WD 4 /* bytes per word */ +#define BY2V 8 /* bytes per double word */ +#define BY2PG 4096 /* bytes per page */ +#define WD2PG (BY2PG/BY2WD) /* words per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define ROUND(s, sz) (((s)+((sz)-1))&~((sz)-1)) +#define PGROUND(s) ROUND(s, BY2PG) +#define BLOCKALIGN 8 + +#define MAXMACH 8 /* max # cpus system can run */ +#define KSTACK 8192 /* Size of kernel stack */ + +/* + * Time + */ +#define HZ (100) /* clock frequency */ +#define MS2HZ (1000/HZ) /* millisec per clock tick */ +#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ +#define MS2TK(t) ((((ulong)(t))*HZ)/1000) /* milliseconds to ticks */ + +/* + * Fundamental addresses + */ +#define IDTADDR 0x80000800 /* idt */ +#define REBOOTADDR 0x00001000 /* reboot code - physical address */ +#define APBOOTSTRAP 0x80001000 /* AP bootstrap code */ +#define CONFADDR 0x80001200 /* info passed from boot loader */ +#define CPU0PDB 0x80002000 /* bootstrap processor PDB */ +#define CPU0PTE 0x80003000 /* bootstrap processor PTE's for 0-4MB */ +#define CPU0GDT 0x80004000 /* bootstrap processor GDT */ +#define MACHADDR 0x80005000 /* as seen by current processor */ +#define CPU0MACH 0x80006000 /* Mach for bootstrap processor */ +#define MACHSIZE BY2PG +/* + * N.B. ramscan knows that CPU0MACH+BY2PG is the end of reserved data + * N.B. _start0x00100020 knows that CPU0PDB is the first reserved page + * and that there are 5 of them. + */ + +/* + * Address spaces + * + * User is at 0-2GB + * Kernel is at 2GB-4GB + */ +#define UZERO 0 /* base of user address space */ +#define UTZERO (UZERO+BY2PG) /* first address in user text */ +#define KZERO 0x80000000 /* base of kernel address space */ +#define KTZERO 0x80100000 /* first address in kernel text */ +#define USTKTOP (KZERO-BY2PG) /* byte just beyond user stack */ +#define USTKSIZE (16*1024*1024) /* size of user stack */ +#define TSTKTOP (USTKTOP-USTKSIZE) /* end of new stack in sysexec */ +#define TSTKSIZ 100 + +/* + * known x86 segments (in GDT) and their selectors + */ +#define NULLSEG 0 /* null segment */ +#define KDSEG 1 /* kernel data/stack */ +#define KESEG 2 /* kernel executable */ +#define UDSEG 3 /* user data/stack */ +#define UESEG 4 /* user executable */ +#define TSSSEG 5 /* task segment */ +#define APMCSEG 6 /* APM code segment */ +#define APMCSEG16 7 /* APM 16-bit code segment */ +#define APMDSEG 8 /* APM data segment */ +#define NGDT 10 /* number of GDT entries required */ +/* #define APM40SEG 8 /* APM segment 0x40 */ + +#define SELGDT (0<<2) /* selector is in gdt */ +#define SELLDT (1<<2) /* selector is in ldt */ + +#define SELECTOR(i, t, p) (((i)<<3) | (t) | (p)) + +#define NULLSEL SELECTOR(NULLSEG, SELGDT, 0) +#define KDSEL SELECTOR(KDSEG, SELGDT, 0) +#define KESEL SELECTOR(KESEG, SELGDT, 0) +#define UESEL SELECTOR(UESEG, SELGDT, 3) +#define UDSEL SELECTOR(UDSEG, SELGDT, 3) +#define TSSSEL SELECTOR(TSSSEG, SELGDT, 0) +#define APMCSEL SELECTOR(APMCSEG, SELGDT, 0) +#define APMCSEL16 SELECTOR(APMCSEG16, SELGDT, 0) +#define APMDSEL SELECTOR(APMDSEG, SELGDT, 0) +/* #define APM40SEL SELECTOR(APM40SEG, SELGDT, 0) */ + +/* + * fields in segment descriptors + */ +#define SEGDATA (0x10<<8) /* data/stack segment */ +#define SEGEXEC (0x18<<8) /* executable segment */ +#define SEGTSS (0x9<<8) /* TSS segment */ +#define SEGCG (0x0C<<8) /* call gate */ +#define SEGIG (0x0E<<8) /* interrupt gate */ +#define SEGTG (0x0F<<8) /* trap gate */ +#define SEGTYPE (0x1F<<8) + +#define SEGP (1<<15) /* segment present */ +#define SEGPL(x) ((x)<<13) /* priority level */ +#define SEGB (1<<22) /* granularity 1==4k (for expand-down) */ +#define SEGG (1<<23) /* granularity 1==4k (for other) */ +#define SEGE (1<<10) /* expand down */ +#define SEGW (1<<9) /* writable (for data/stack) */ +#define SEGR (1<<9) /* readable (for code) */ +#define SEGD (1<<22) /* default 1==32bit (for code) */ + +/* + * virtual MMU + */ +#define PTEMAPMEM (1024*1024) +#define PTEPERTAB (PTEMAPMEM/BY2PG) +#define SEGMAPSIZE 1984 +#define SSEGMAPSIZE 16 +#define PPN(x) ((x)&~(BY2PG-1)) + +/* + * physical MMU + */ +#define PTEVALID (1<<0) +#define PTEWT (1<<3) +#define PTEUNCACHED (1<<4) +#define PTEWRITE (1<<1) +#define PTERONLY (0<<1) +#define PTEKERNEL (0<<2) +#define PTEUSER (1<<2) +#define PTESIZE (1<<7) +#define PTEGLOBAL (1<<8) + +/* + * Macros for calculating offsets within the page directory base + * and page tables. + */ +#define PDX(va) ((((ulong)(va))>>22) & 0x03FF) +#define PTX(va) ((((ulong)(va))>>12) & 0x03FF) + +#define getpgcolor(a) 0 diff --git a/os/pc/memory.c b/os/pc/memory.c new file mode 100644 index 00000000..a58119c6 --- /dev/null +++ b/os/pc/memory.c @@ -0,0 +1,570 @@ +/* + * Size memory and create the kernel page-tables on the fly while doing so. + * Called from main(), this code should only be run by the bootstrap processor. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#define MEMDEBUG 0 + +enum { + MemUPA = 0, /* unbacked physical address */ + MemRAM = 1, /* physical memory */ + MemUMB = 2, /* upper memory block (<16MB) */ + NMemType = 3, + + KB = 1024, + + MemMinMB = 4, /* minimum physical memory (<=4MB) */ + MemMaxMB = 768, /* maximum physical memory to check */ + + NMemBase = 10, +}; + +typedef struct { + int size; + ulong addr; +} Map; + +typedef struct { + char* name; + Map* map; + Map* mapend; + + Lock; +} RMap; + +static Map mapupa[16]; +static RMap rmapupa = { + "unallocated unbacked physical memory", + mapupa, + &mapupa[nelem(mapupa)-1], +}; + +static Map xmapupa[16]; +static RMap xrmapupa = { + "unbacked physical memory", + xmapupa, + &xmapupa[nelem(xmapupa)-1], +}; + +static Map mapram[16]; +static RMap rmapram = { + "physical memory", + mapram, + &mapram[nelem(mapram)-1], +}; + +static Map mapumb[64]; +static RMap rmapumb = { + "upper memory block", + mapumb, + &mapumb[nelem(mapumb)-1], +}; + +static Map mapumbrw[16]; +static RMap rmapumbrw = { + "UMB device memory", + mapumbrw, + &mapumbrw[nelem(mapumbrw)-1], +}; + +void +mapprint(RMap *rmap) +{ + Map *mp; + + print("%s\n", rmap->name); + for(mp = rmap->map; mp->size; mp++) + print("\t%8.8luX %8.8uX %8.8luX\n", mp->addr, mp->size, mp->addr+mp->size); +} + +void +memdebug(void) +{ + ulong maxpa, maxpa1, maxpa2; + + if(MEMDEBUG == 0) + return; + + maxpa = (nvramread(0x18)<<8)|nvramread(0x17); + maxpa1 = (nvramread(0x31)<<8)|nvramread(0x30); + maxpa2 = (nvramread(0x16)<<8)|nvramread(0x15); + print("maxpa = %luX -> %luX, maxpa1 = %luX maxpa2 = %luX\n", + maxpa, MB+maxpa*KB, maxpa1, maxpa2); + + mapprint(&rmapram); + mapprint(&rmapumb); + mapprint(&rmapumbrw); + mapprint(&rmapupa); +} + +void +mapfree(RMap* rmap, ulong addr, ulong size) +{ + Map *mp; + ulong t; + + if(size <= 0) + return; + + lock(rmap); + for(mp = rmap->map; mp->addr <= addr && mp->size; mp++) + ; + + if(mp > rmap->map && (mp-1)->addr+(mp-1)->size == addr){ + (mp-1)->size += size; + if(addr+size == mp->addr){ + (mp-1)->size += mp->size; + while(mp->size){ + mp++; + (mp-1)->addr = mp->addr; + (mp-1)->size = mp->size; + } + } + } + else{ + if(addr+size == mp->addr && mp->size){ + mp->addr -= size; + mp->size += size; + } + else do{ + if(mp >= rmap->mapend){ + print("mapfree: %s: losing 0x%luX, %ld\n", + rmap->name, addr, size); + break; + } + t = mp->addr; + mp->addr = addr; + addr = t; + t = mp->size; + mp->size = size; + mp++; + }while(size = t); + } + unlock(rmap); +} + +ulong +mapalloc(RMap* rmap, ulong addr, int size, int align) +{ + Map *mp; + ulong maddr, oaddr; + + lock(rmap); + for(mp = rmap->map; mp->size; mp++){ + maddr = mp->addr; + + if(addr){ + /* + * A specific address range has been given: + * if the current map entry is greater then + * the address is not in the map; + * if the current map entry does not overlap + * the beginning of the requested range then + * continue on to the next map entry; + * if the current map entry does not entirely + * contain the requested range then the range + * is not in the map. + */ + if(maddr > addr) + break; + if(mp->size < addr - maddr) /* maddr+mp->size < addr, but no overflow */ + continue; + if(addr - maddr > mp->size - size) /* addr+size > maddr+mp->size, but no overflow */ + break; + maddr = addr; + } + + if(align > 0) + maddr = ((maddr+align-1)/align)*align; + if(mp->addr+mp->size-maddr < size) + continue; + + oaddr = mp->addr; + mp->addr = maddr+size; + mp->size -= maddr-oaddr+size; + if(mp->size == 0){ + do{ + mp++; + (mp-1)->addr = mp->addr; + }while((mp-1)->size = mp->size); + } + + unlock(rmap); + if(oaddr != maddr) + mapfree(rmap, oaddr, maddr-oaddr); + + return maddr; + } + unlock(rmap); + + return 0; +} + +static void +umbscan(void) +{ + uchar *p; + + /* + * Scan the Upper Memory Blocks (0xA0000->0xF0000) for pieces + * which aren't used; they can be used later for devices which + * want to allocate some virtual address space. + * Check for two things: + * 1) device BIOS ROM. This should start with a two-byte header + * of 0x55 0xAA, followed by a byte giving the size of the ROM + * in 512-byte chunks. These ROM's must start on a 2KB boundary. + * 2) device memory. This is read-write. + * There are some assumptions: there's VGA memory at 0xA0000 and + * the VGA BIOS ROM is at 0xC0000. Also, if there's no ROM signature + * at 0xE0000 then the whole 64KB up to 0xF0000 is theoretically up + * for grabs; check anyway. + */ + p = KADDR(0xD0000); + while(p < (uchar*)KADDR(0xE0000)){ + /* + * Test for 0x55 0xAA before poking obtrusively, + * some machines (e.g. Thinkpad X20) seem to map + * something dynamic here (cardbus?) causing weird + * problems if it is changed. + */ + if(p[0] == 0x55 && p[1] == 0xAA){ + p += p[2]*512; + continue; + } + + p[0] = 0xCC; + p[2*KB-1] = 0xCC; + if(p[0] != 0xCC || p[2*KB-1] != 0xCC){ + p[0] = 0x55; + p[1] = 0xAA; + p[2] = 4; + if(p[0] == 0x55 && p[1] == 0xAA){ + p += p[2]*512; + continue; + } + if(p[0] == 0xFF && p[1] == 0xFF) + mapfree(&rmapumb, PADDR(p), 2*KB); + } + else + mapfree(&rmapumbrw, PADDR(p), 2*KB); + p += 2*KB; + } + + p = KADDR(0xE0000); + if(p[0] != 0x55 || p[1] != 0xAA){ + p[0] = 0xCC; + p[64*KB-1] = 0xCC; + if(p[0] != 0xCC && p[64*KB-1] != 0xCC) + mapfree(&rmapumb, PADDR(p), 64*KB); + } +} + + +static void +ramscan(ulong maxmem) +{ + ulong *k0, kzero, map, maxpa, pa, *pte, *table, *va, x, n; + int nvalid[NMemType]; + uchar *bda; + + /* + * The bootstrap code has has created a prototype page + * table which maps the first MemMinMB of physical memory to KZERO. + * The page directory is at m->pdb and the first page of + * free memory is after the per-processor MMU information. + */ + /* + * Initialise the memory bank information for conventional memory + * (i.e. less than 640KB). The base is the first location after the + * bootstrap processor MMU information and the limit is obtained from + * the BIOS data area. + */ + x = PADDR(CPU0MACH+BY2PG); + bda = (uchar*)KADDR(0x400); + n = ((bda[0x14]<<8)|bda[0x13])*KB-x; + mapfree(&rmapram, x, n); +// memset(KADDR(x), 0, n); /* keep us honest */ + + x = PADDR(PGROUND((ulong)end)); + pa = MemMinMB*MB; + mapfree(&rmapram, x, pa-x); +// memset(KADDR(x), 0, pa-x); /* keep us honest */ + + /* + * Check if the extended memory size can be obtained from the CMOS. + * If it's 0 then it's either not known or >= 64MB. Always check + * at least 24MB in case there's a memory gap (up to 8MB) below 16MB; + * in this case the memory from the gap is remapped to the top of + * memory. + * The value in CMOS is supposed to be the number of KB above 1MB. + */ + if(maxmem == 0){ + x = (nvramread(0x18)<<8)|nvramread(0x17); + if(x == 0 || x >= (63*KB)) + maxpa = MemMaxMB*MB; + else + maxpa = MB+x*KB; + if(maxpa < 24*MB) + maxpa = 24*MB; + maxmem = MemMaxMB*MB; + } + else + maxpa = maxmem; + + /* + * March up memory from MemMinMB to maxpa 1MB at a time, + * mapping the first page and checking the page can + * be written and read correctly. The page tables are created here + * on the fly, allocating from low memory as necessary. + */ + k0 = (ulong*)KADDR(0); + kzero = *k0; + map = 0; + x = 0x12345678; + memset(nvalid, 0, sizeof(nvalid)); + while(pa < maxpa){ + /* + * Map the page. Use mapalloc(&rmapram, ...) to make + * the page table if necessary, it will be returned to the + * pool later if it isn't needed. + */ + va = KADDR(pa); + table = &m->pdb[PDX(va)]; + if(*table == 0){ + if(map == 0 && (map = mapalloc(&rmapram, 0, BY2PG, BY2PG)) == 0) + break; + memset(KADDR(map), 0, BY2PG); + *table = map|PTEWRITE|PTEVALID; + memset(nvalid, 0, sizeof(nvalid)); + } + table = KADDR(PPN(*table)); + pte = &table[PTX(va)]; + + *pte = pa|PTEWRITE|PTEUNCACHED|PTEVALID; + mmuflushtlb(PADDR(m->pdb)); + + /* + * Write a pattern to the page and write a different + * pattern to a possible mirror at KZER0. If the data + * reads back correctly the chunk is some type of RAM (possibly + * a linearly-mapped VGA framebuffer, for instance...) and + * can be cleared and added to the memory pool. If not, the + * chunk is marked uncached and added to the UMB pool if <16MB + * or is marked invalid and added to the UPA pool. + */ + *va = x; + *k0 = ~x; + if(*va == x){ + nvalid[MemRAM] += MB/BY2PG; + mapfree(&rmapram, pa, MB); + + do{ + *pte++ = pa|PTEWRITE|PTEVALID; + pa += BY2PG; + }while(pa % MB); + mmuflushtlb(PADDR(m->pdb)); + /* memset(va, 0, MB); so damn slow to memset all of memory */ + } + else if(pa < 16*MB){ + nvalid[MemUMB] += MB/BY2PG; + mapfree(&rmapumb, pa, MB); + + do{ + *pte++ = pa|PTEWRITE|PTEUNCACHED|PTEVALID; + pa += BY2PG; + }while(pa % MB); + } + else{ + nvalid[MemUPA] += MB/BY2PG; + mapfree(&rmapupa, pa, MB); + + *pte = 0; + pa += MB; + } + + /* + * Done with this 4MB chunk, review the options: + * 1) not physical memory and >=16MB - invalidate the PDB entry; + * 2) physical memory - use the 4MB page extension if possible; + * 3) not physical memory and <16MB - use the 4MB page extension + * if possible; + * 4) mixed or no 4MB page extension - commit the already + * initialised space for the page table. + */ + if((pa % (4*MB)) == 0){ + table = &m->pdb[PDX(va)]; + if(nvalid[MemUPA] == (4*MB)/BY2PG) + *table = 0; + else if(nvalid[MemRAM] == (4*MB)/BY2PG && (m->cpuiddx & 0x08)) + *table = (pa - 4*MB)|PTESIZE|PTEWRITE|PTEVALID; + else if(nvalid[MemUMB] == (4*MB)/BY2PG && (m->cpuiddx & 0x08)) + *table = (pa - 4*MB)|PTESIZE|PTEWRITE|PTEUNCACHED|PTEVALID; + else + map = 0; + } + + mmuflushtlb(PADDR(m->pdb)); + x += 0x3141526; + } + + /* + * If we didn't reach the end of the 4MB chunk, that part won't + * be mapped. Commit the already initialised space for the page table. + */ + if(pa % (4*MB)) + map = 0; + + if(map) + mapfree(&rmapram, map, BY2PG); + if(pa < maxmem) + mapfree(&rmapupa, pa, maxmem-pa); + if(maxmem < 0xFFE00000) + mapfree(&rmapupa, maxmem, 0x00000000-maxmem); + if(MEMDEBUG) + print("maxmem %luX %luX\n", maxmem, 0x00000000-maxmem); + *k0 = kzero; +} + +void +meminit(ulong maxmem) +{ + Map *mp, *xmp; + ulong pa, *pte; + + /* + * Set special attributes for memory between 640KB and 1MB: + * VGA memory is writethrough; + * BIOS ROM's/UMB's are uncached; + * then scan for useful memory. + */ + for(pa = 0xA0000; pa < 0xC0000; pa += BY2PG){ + pte = mmuwalk(m->pdb, (ulong)KADDR(pa), 2, 0); + *pte |= PTEWT; + } + for(pa = 0xC0000; pa < 0x100000; pa += BY2PG){ + pte = mmuwalk(m->pdb, (ulong)KADDR(pa), 2, 0); + *pte |= PTEUNCACHED; + } + mmuflushtlb(PADDR(m->pdb)); + + umbscan(); + ramscan(maxmem); + + /* + * Set the conf entries describing two banks of allocatable memory. + * Grab the first and largest entries in rmapram as left by ramscan(). + * + * It would be nice to have more than 2 memory banks describable in conf. + */ + mp = rmapram.map; + conf.base0 = mp->addr; + conf.npage0 = mp->size/BY2PG; + mp++; + for(xmp = 0; mp->size; mp++){ + if(xmp == 0 || mp->size > xmp->size) + xmp = mp; + } + + if(xmp){ + conf.base1 = xmp->addr; + conf.npage1 = xmp->size/BY2PG; + } + if(MEMDEBUG) + memdebug(); +} + +ulong +umbmalloc(ulong addr, int size, int align) +{ + ulong a; + + if(a = mapalloc(&rmapumb, addr, size, align)) + return (ulong)KADDR(a); + + return 0; +} + +void +umbfree(ulong addr, int size) +{ + mapfree(&rmapumb, PADDR(addr), size); +} + +ulong +umbrwmalloc(ulong addr, int size, int align) +{ + ulong a; + uchar *p; + + if(a = mapalloc(&rmapumbrw, addr, size, align)) + return(ulong)KADDR(a); + + /* + * Perhaps the memory wasn't visible before + * the interface is initialised, so try again. + */ + if((a = umbmalloc(addr, size, align)) == 0) + return 0; + p = (uchar*)a; + p[0] = 0xCC; + p[size-1] = 0xCC; + if(p[0] == 0xCC && p[size-1] == 0xCC) + return a; + umbfree(a, size); + + return 0; +} + +void +umbrwfree(ulong addr, int size) +{ + mapfree(&rmapumbrw, PADDR(addr), size); +} + +ulong +upamalloc(ulong pa, int size, int align) +{ + ulong a, ae; + + if(a = mapalloc(&xrmapupa, pa, size, align)) + return a; + + if((a = mapalloc(&rmapupa, pa, size, align)) == 0){ + memdebug(); + return 0; + } + + /* + * Upamalloc is a request to map a range of physical addresses. + * Therefore, if pa is 0 mapalloc will choose the base address. + * Note, however, mmukmap is always asked to give a 1-to-1 mapping + * of va to pa. + */ + ae = mmukmap(a, a, size); + + /* + * Should check here that it was all delivered + * and put it back and barf if not. + */ + USED(ae); + + /* + * Be very careful this returns a PHYSICAL address + * mapped 1-to-1 with the virtual address. + * If a < KZERO it's probably not a good idea to + * try KADDR(a)... + */ + return a; +} + +void +upafree(ulong pa, int size) +{ + mapfree(&xrmapupa, pa, size); +} diff --git a/os/pc/mkfile b/os/pc/mkfile new file mode 100644 index 00000000..c7366fd2 --- /dev/null +++ b/os/pc/mkfile @@ -0,0 +1,83 @@ +<../../mkconfig + +#Configurable parameters + +CONF=pc #default configuration +CONFLIST=pc pcdisk +CLEANCONFLIST=pc pcdisk zpc zpcrem pix pcsoe + +SYSTARG=$OSTARG +OBJTYPE=386 +INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin #path of directory where kernel is installed +#INSTALLDIR=/$OBJTYPE + +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS + +OBJ=\ + l.$O\ + fpsave.$O\ + portclock.$O\ + tod.$O\ + i8250.$O\ + i8253.$O\ + i8259.$O\ + kbd.$O\ + main.$O\ + memory.$O\ + mmu.$O\ + trap.$O\ + $CONF.root.$O\ + $IP\ + $DEVS\ + $ETHERS\ + $LINKS\ + $PORT\ + $MISC\ + $OTHERS\ + +LIBNAMES=${LIBS:%=lib%.a} + +HFILES=\ + mem.h\ + dat.h\ + fns.h\ + io.h\ + +CFLAGS=-wFVT -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp -I../port +KERNDATE=`{$NDATE} + +default:V: i$CONF + +i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD -o $target -T0x80100020 -l $OBJ $CONF.$O $LIBFILES + $KSIZE $target + +install:V: i$CONF + cp i$CONF $INSTALLDIR/i$CONF + +<../port/portmkfile + +clock.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +devether.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +fault386.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +main.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +trap.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h + +devether.$O $ETHERS: etherif.h ../port/netif.h +$IP devip.$O: ../ip/ip.h + +# to be moved to port/interp +bench.h:D: ../../module/bench.m + rm -f $target && limbo -a -I../../module ../../module/bench.m > $target +benchmod.h:D: ../../module/bench.m + rm -f $target && limbo -t Bench -I../../module ../../module/bench.m > $target +devbench.$O: bench.h benchmod.h +$VGA screen.$O: screen.h vga.h + +devuart.$O: ../port/devuart.c ../port/uart.h + $CC $CFLAGS ../port/devuart.c diff --git a/os/pc/mmu.c b/os/pc/mmu.c new file mode 100644 index 00000000..6bd4ddfb --- /dev/null +++ b/os/pc/mmu.c @@ -0,0 +1,321 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#define DATASEGM(p) { 0xFFFF, SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(p)|SEGDATA|SEGW } +#define EXECSEGM(p) { 0xFFFF, SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(p)|SEGEXEC|SEGR } +#define TSSSEGM(b,p) { ((b)<<16)|sizeof(Tss),\ + ((b)&0xFF000000)|(((b)>>16)&0xFF)|SEGTSS|SEGPL(p)|SEGP } + +Segdesc gdt[NGDT] = +{ +[NULLSEG] { 0, 0}, /* null descriptor */ +[KDSEG] DATASEGM(0), /* kernel data/stack */ +[KESEG] EXECSEGM(0), /* kernel code */ +[UDSEG] DATASEGM(3), /* user data/stack */ +[UESEG] EXECSEGM(3), /* user code */ +[TSSSEG] TSSSEGM(0,0), /* tss segment */ +}; + +static void +taskswitch(ulong pdb, ulong stack) +{ + Tss *tss; + + tss = m->tss; + tss->ss0 = KDSEL; + tss->esp0 = stack; + tss->ss1 = KDSEL; + tss->esp1 = stack; + tss->ss2 = KDSEL; + tss->esp2 = stack; + tss->cr3 = pdb; + putcr3(pdb); +} + +/* + * On processors that support it, we set the PTEGLOBAL bit in + * page table and page directory entries that map kernel memory. + * Doing this tells the processor not to bother flushing them + * from the TLB when doing the TLB flush associated with a + * context switch (write to CR3). Since kernel memory mappings + * are never removed, this is safe. (If we ever remove kernel memory + * mappings, we can do a full flush by turning off the PGE bit in CR4, + * writing to CR3, and then turning the PGE bit back on.) + * + * See also mmukmap below. + * + * Processor support for the PTEGLOBAL bit is enabled in devarch.c. + */ +static void +memglobal(void) +{ + int i, j; + ulong *pde, *pte; + + /* only need to do this once, on bootstrap processor */ + if(m->machno != 0) + return; + + if(!m->havepge) + return; + + pde = m->pdb; + for(i=512; i<1024; i++){ /* 512: start at entry for virtual 0x80000000 */ + if(pde[i] & PTEVALID){ + pde[i] |= PTEGLOBAL; + if(!(pde[i] & PTESIZE)){ + pte = KADDR(pde[i]&~(BY2PG-1)); + for(j=0; j<1024; j++) + if(pte[j] & PTEVALID) + pte[j] |= PTEGLOBAL; + } + } + } +} + +void +mmuinit(void) +{ + ulong x, *p; + ushort ptr[3]; + + memglobal(); + + m->tss = malloc(sizeof(Tss)); + memset(m->tss, 0, sizeof(Tss)); + m->tss->iomap = 0xDFFF<<16; + + /* + * We used to keep the GDT in the Mach structure, but it + * turns out that that slows down access to the rest of the + * page. Since the Mach structure is accessed quite often, + * it pays off anywhere from a factor of 1.25 to 2 on real + * hardware to separate them (the AMDs are more sensitive + * than Intels in this regard). Under VMware it pays off + * a factor of about 10 to 100. + */ + + memmove(m->gdt, gdt, sizeof gdt); + x = (ulong)m->tss; + m->gdt[TSSSEG].d0 = (x<<16)|sizeof(Tss); + m->gdt[TSSSEG].d1 = (x&0xFF000000)|((x>>16)&0xFF)|SEGTSS|SEGPL(0)|SEGP; + + ptr[0] = sizeof(gdt)-1; + x = (ulong)m->gdt; + ptr[1] = x & 0xFFFF; + ptr[2] = (x>>16) & 0xFFFF; + lgdt(ptr); + + ptr[0] = sizeof(Segdesc)*256-1; + x = IDTADDR; + ptr[1] = x & 0xFFFF; + ptr[2] = (x>>16) & 0xFFFF; + lidt(ptr); + + /* make kernel text unwritable */ + for(x = KTZERO; x < (ulong)etext; x += BY2PG){ + p = mmuwalk(m->pdb, x, 2, 0); + if(p == nil) + panic("mmuinit"); + *p &= ~PTEWRITE; + } + + taskswitch(PADDR(m->pdb), (ulong)m + BY2PG); + ltr(TSSSEL); +} + + + + +ulong* +mmuwalk(ulong* pdb, ulong va, int level, int create) +{ + ulong pa, *table; + + /* + * Walk the page-table pointed to by pdb and return a pointer + * to the entry for virtual address va at the requested level. + * If the entry is invalid and create isn't requested then bail + * out early. Otherwise, for the 2nd level walk, allocate a new + * page-table page and register it in the 1st level. + */ + table = &pdb[PDX(va)]; + if(!(*table & PTEVALID) && create == 0) + return 0; + + switch(level){ + + default: + return 0; + + case 1: + return table; + + case 2: + if(*table & PTESIZE) + panic("mmuwalk2: va %luX entry %luX\n", va, *table); + if(!(*table & PTEVALID)){ + pa = PADDR(xspanalloc(BY2PG, BY2PG, 0)); + *table = pa|PTEWRITE|PTEVALID; + } + table = KADDR(PPN(*table)); + + return &table[PTX(va)]; + } +} + +static Lock mmukmaplock; + +int +mmukmapsync(ulong va) +{ + Mach *mach0; + ulong entry, *pte; + + mach0 = MACHP(0); + + ilock(&mmukmaplock); + + if((pte = mmuwalk(mach0->pdb, va, 1, 0)) == nil){ + iunlock(&mmukmaplock); + return 0; + } + if(!(*pte & PTESIZE) && mmuwalk(mach0->pdb, va, 2, 0) == nil){ + iunlock(&mmukmaplock); + return 0; + } + entry = *pte; + + if(!(m->pdb[PDX(va)] & PTEVALID)) + m->pdb[PDX(va)] = entry; + +// if(up && up->mmupdb){ +// ((ulong*)up->mmupdb->va)[PDX(va)] = entry; +// mmuflushtlb(up->mmupdb->pa); +// } +// else + mmuflushtlb(PADDR(m->pdb)); + + iunlock(&mmukmaplock); + + return 1; +} + +ulong +mmukmap(ulong pa, ulong va, int size) +{ + Mach *mach0; + ulong ova, pae, *table, pgsz, *pte, x; + int pse, sync; + + mach0 = MACHP(0); + if((mach0->cpuiddx & 0x08) && (getcr4() & 0x10)) + pse = 1; + else + pse = 0; + sync = 0; + + pa = PPN(pa); + if(va == 0) + va = (ulong)KADDR(pa); + else + va = PPN(va); + ova = va; + + pae = pa + size; + ilock(&mmukmaplock); + while(pa < pae){ + table = &mach0->pdb[PDX(va)]; + /* + * Possibly already mapped. + */ + if(*table & PTEVALID){ + if(*table & PTESIZE){ + /* + * Big page. Does it fit within? + * If it does, adjust pgsz so the correct end can be + * returned and get out. + * If not, adjust pgsz up to the next 4MB boundary + * and continue. + */ + x = PPN(*table); + if(x != pa) + panic("mmukmap1: pa %luX entry %luX\n", + pa, *table); + x += 4*MB; + if(pae <= x){ + pa = pae; + break; + } + pgsz = x - pa; + pa += pgsz; + va += pgsz; + + continue; + } + else{ + /* + * Little page. Walk to the entry. + * If the entry is valid, set pgsz and continue. + * If not, make it so, set pgsz, sync and continue. + */ + pte = mmuwalk(mach0->pdb, va, 2, 0); + if(pte && *pte & PTEVALID){ + x = PPN(*pte); + if(x != pa) + panic("mmukmap2: pa %luX entry %luX\n", + pa, *pte); + pgsz = BY2PG; + pa += pgsz; + va += pgsz; + sync++; + + continue; + } + } + } + + /* + * Not mapped. Check if it can be mapped using a big page - + * starts on a 4MB boundary, size >= 4MB and processor can do it. + * If not a big page, walk the walk, talk the talk. + * Sync is set. + * + * If we're creating a kernel mapping, we know that it will never + * expire and thus we can set the PTEGLOBAL bit to make the entry + * persist in the TLB across flushes. If we do add support later for + * unmapping kernel addresses, see devarch.c for instructions on + * how to do a full TLB flush. + */ + if(pse && (pa % (4*MB)) == 0 && (pae >= pa+4*MB)){ + *table = pa|PTESIZE|PTEWRITE|PTEUNCACHED|PTEVALID; + if((va&KZERO) && m->havepge) + *table |= PTEGLOBAL; + pgsz = 4*MB; + } + else{ + pte = mmuwalk(mach0->pdb, va, 2, 1); + *pte = pa|PTEWRITE|PTEUNCACHED|PTEVALID; + if((va&KZERO) && m->havepge) + *pte |= PTEGLOBAL; + pgsz = BY2PG; + } + pa += pgsz; + va += pgsz; + sync++; + } + iunlock(&mmukmaplock); + + /* + * If something was added + * then need to sync up. + */ + if(sync) + mmukmapsync(ova); + + return pa; +} diff --git a/os/pc/mouse.c b/os/pc/mouse.c new file mode 100644 index 00000000..49654bad --- /dev/null +++ b/os/pc/mouse.c @@ -0,0 +1,84 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +/* + * mouse types + */ +enum +{ + Mouseother= 0, + Mouseserial= 1, + MousePS2= 2, +}; + +static int mousetype; + +/* + * ps/2 mouse message is three bytes + * + * byte 0 - 0 0 SDY SDX 1 M R L + * byte 1 - DX + * byte 2 - DY + * + * shift & left button is the same as middle button + */ +static void +ps2mouseputc(int c, int shift) +{ + static short msg[3]; + static int nb; + static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 5, 2, 3, 6, 7 }; + int buttons, dx, dy; + + /* + * check byte 0 for consistency + */ + if(nb==0 && (c&0xc8)!=0x08) + return; + + msg[nb] = c; + if(++nb == 3){ + nb = 0; + if(msg[0] & 0x10) + msg[1] |= 0xFF00; + if(msg[0] & 0x20) + msg[2] |= 0xFF00; + + buttons = b[(msg[0]&7) | (shift ? 8 : 0)]; + dx = msg[1]; + dy = -msg[2]; + mousetrack(buttons, dx, dy, 1); + } + return; +} + +/* + * set up a ps2 mouse + */ +static void +ps2mouse(void) +{ + if(mousetype == MousePS2) + return; + + i8042auxenable(ps2mouseputc); + /* make mouse streaming, enabled */ + i8042auxcmd(0xEA); + i8042auxcmd(0xF4); + + mousetype = MousePS2; +} + +void +ps2mouselink(void) +{ + /* + * hack + */ + ps2mouse(); +} diff --git a/os/pc/mp.c b/os/pc/mp.c new file mode 100644 index 00000000..f16fab91 --- /dev/null +++ b/os/pc/mp.c @@ -0,0 +1,815 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +#include "mp.h" +#include "apbootstrap.h" + +static Bus* mpbus; +static Bus* mpbuslast; +static int mpisabus = -1; +static int mpeisabus = -1; +extern int i8259elcr; /* mask of level-triggered interrupts */ +static Apic mpapic[MaxAPICNO+1]; +static int machno2apicno[MaxAPICNO+1]; /* inverse map: machno -> APIC ID */ +static Lock mprdthilock; +static int mprdthi; +static Ref mpvnoref; /* unique vector assignment */ +static int mpmachno = 1; + +static char* buses[] = { + "CBUSI ", + "CBUSII", + "EISA ", + "FUTURE", + "INTERN", + "ISA ", + "MBI ", + "MBII ", + "MCA ", + "MPI ", + "MPSA ", + "NUBUS ", + "PCI ", + "PCMCIA", + "TC ", + "VL ", + "VME ", + "XPRESS", + 0, +}; + +static Apic* +mkprocessor(PCMPprocessor* p) +{ + Apic *apic; + + if(!(p->flags & PcmpEN) || p->apicno > MaxAPICNO) + return 0; + + apic = &mpapic[p->apicno]; + apic->type = PcmpPROCESSOR; + apic->apicno = p->apicno; + apic->flags = p->flags; + apic->lintr[0] = ApicIMASK; + apic->lintr[1] = ApicIMASK; + + if(p->flags & PcmpBP){ + machno2apicno[0] = p->apicno; + apic->machno = 0; + } + else{ + machno2apicno[mpmachno] = p->apicno; + apic->machno = mpmachno; + mpmachno++; + } + + return apic; +} + +static Bus* +mkbus(PCMPbus* p) +{ + Bus *bus; + int i; + + for(i = 0; buses[i]; i++){ + if(strncmp(buses[i], p->string, sizeof(p->string)) == 0) + break; + } + if(buses[i] == 0) + return 0; + + bus = xalloc(sizeof(Bus)); + if(mpbus) + mpbuslast->next = bus; + else + mpbus = bus; + mpbuslast = bus; + + bus->type = i; + bus->busno = p->busno; + if(bus->type == BusEISA){ + bus->po = PcmpLOW; + bus->el = PcmpLEVEL; + if(mpeisabus != -1) + print("mkbus: more than one EISA bus\n"); + mpeisabus = bus->busno; + } + else if(bus->type == BusPCI){ + bus->po = PcmpLOW; + bus->el = PcmpLEVEL; + } + else if(bus->type == BusISA){ + bus->po = PcmpHIGH; + bus->el = PcmpEDGE; + if(mpisabus != -1) + print("mkbus: more than one ISA bus\n"); + mpisabus = bus->busno; + } + else{ + bus->po = PcmpHIGH; + bus->el = PcmpEDGE; + } + + return bus; +} + +static Bus* +mpgetbus(int busno) +{ + Bus *bus; + + for(bus = mpbus; bus; bus = bus->next){ + if(bus->busno == busno) + return bus; + } + print("mpgetbus: can't find bus %d\n", busno); + + return 0; +} + +static Apic* +mkioapic(PCMPioapic* p) +{ + Apic *apic; + + if(!(p->flags & PcmpEN) || p->apicno > MaxAPICNO) + return 0; + + /* + * Map the I/O APIC. + */ + if(mmukmap(p->addr, 0, 1024) == 0) + return 0; + + apic = &mpapic[p->apicno]; + apic->type = PcmpIOAPIC; + apic->apicno = p->apicno; + apic->addr = KADDR(p->addr); + apic->flags = p->flags; + + return apic; +} + +static Aintr* +mkiointr(PCMPintr* p) +{ + Bus *bus; + Aintr *aintr; + + /* + * According to the MultiProcessor Specification, a destination + * I/O APIC of 0xFF means the signal is routed to all I/O APICs. + * It's unclear how that can possibly be correct so treat it as + * an error for now. + */ + if(p->apicno == 0xFF) + return 0; + if((bus = mpgetbus(p->busno)) == 0) + return 0; + + aintr = xalloc(sizeof(Aintr)); + aintr->intr = p; + aintr->apic = &mpapic[p->apicno]; + aintr->next = bus->aintr; + bus->aintr = aintr; + + return aintr; +} + +static int +mpintrinit(Bus* bus, PCMPintr* intr, int vno, int /*irq*/) +{ + int el, po, v; + + /* + * Parse an I/O or Local APIC interrupt table entry and + * return the encoded vector. + */ + v = vno; + + po = intr->flags & PcmpPOMASK; + el = intr->flags & PcmpELMASK; + + switch(intr->intr){ + + default: /* PcmpINT */ + v |= ApicLOWEST; + break; + + case PcmpNMI: + v |= ApicNMI; + po = PcmpHIGH; + el = PcmpEDGE; + break; + + case PcmpSMI: + v |= ApicSMI; + break; + + case PcmpExtINT: + v |= ApicExtINT; + /* + * The AMI Goliath doesn't boot successfully with it's LINTR0 + * entry which decodes to low+level. The PPro manual says ExtINT + * should be level, whereas the Pentium is edge. Setting the + * Goliath to edge+high seems to cure the problem. Other PPro + * MP tables (e.g. ASUS P/I-P65UP5 have a entry which decodes + * to edge+high, so who knows. + * Perhaps it would be best just to not set an ExtINT entry at + * all, it shouldn't be needed for SMP mode. + */ + po = PcmpHIGH; + el = PcmpEDGE; + break; + } + + /* + */ + if(bus->type == BusEISA && !po && !el /*&& !(i8259elcr & (1<po; + if(po == PcmpLOW) + v |= ApicLOW; + else if(po != PcmpHIGH){ + print("mpintrinit: bad polarity 0x%uX\n", po); + return ApicIMASK; + } + + if(!el) + el = bus->el; + if(el == PcmpLEVEL) + v |= ApicLEVEL; + else if(el != PcmpEDGE){ + print("mpintrinit: bad trigger 0x%uX\n", el); + return ApicIMASK; + } + + return v; +} + +static int +mklintr(PCMPintr* p) +{ + Apic *apic; + Bus *bus; + int intin, v; + + /* + * The offsets of vectors for LINT[01] are known to be + * 0 and 1 from the local APIC vector space at VectorLAPIC. + */ + if((bus = mpgetbus(p->busno)) == 0) + return 0; + intin = p->intin; + + /* + * Pentium Pros have problems if LINT[01] are set to ExtINT + * so just bag it, SMP mode shouldn't need ExtINT anyway. + */ + if(p->intr == PcmpExtINT || p->intr == PcmpNMI) + v = ApicIMASK; + else + v = mpintrinit(bus, p, VectorLAPIC+intin, p->irq); + + if(p->apicno == 0xFF){ + for(apic = mpapic; apic <= &mpapic[MaxAPICNO]; apic++){ + if((apic->flags & PcmpEN) + && apic->type == PcmpPROCESSOR) + apic->lintr[intin] = v; + } + } + else{ + apic = &mpapic[p->apicno]; + if((apic->flags & PcmpEN) && apic->type == PcmpPROCESSOR) + apic->lintr[intin] = v; + } + + return v; +} + +static void +checkmtrr(void) +{ + int i, vcnt; + Mach *mach0; + + /* + * If there are MTRR registers, snarf them for validation. + */ + if(!(m->cpuiddx & 0x1000)) + return; + + rdmsr(0x0FE, &m->mtrrcap); + rdmsr(0x2FF, &m->mtrrdef); + if(m->mtrrcap & 0x0100){ + rdmsr(0x250, &m->mtrrfix[0]); + rdmsr(0x258, &m->mtrrfix[1]); + rdmsr(0x259, &m->mtrrfix[2]); + for(i = 0; i < 8; i++) + rdmsr(0x268+i, &m->mtrrfix[(i+3)]); + } + vcnt = m->mtrrcap & 0x00FF; + if(vcnt > nelem(m->mtrrvar)) + vcnt = nelem(m->mtrrvar); + for(i = 0; i < vcnt; i++) + rdmsr(0x200+i, &m->mtrrvar[i]); + + /* + * If not the bootstrap processor, compare. + */ + if(m->machno == 0) + return; + + mach0 = MACHP(0); + if(mach0->mtrrcap != m->mtrrcap) + print("mtrrcap%d: %lluX %lluX\n", + m->machno, mach0->mtrrcap, m->mtrrcap); + if(mach0->mtrrdef != m->mtrrdef) + print("mtrrdef%d: %lluX %lluX\n", + m->machno, mach0->mtrrdef, m->mtrrdef); + for(i = 0; i < 11; i++){ + if(mach0->mtrrfix[i] != m->mtrrfix[i]) + print("mtrrfix%d: i%d: %lluX %lluX\n", + m->machno, i, mach0->mtrrfix[i], m->mtrrfix[i]); + } + for(i = 0; i < vcnt; i++){ + if(mach0->mtrrvar[i] != m->mtrrvar[i]) + print("mtrrvar%d: i%d: %lluX %lluX\n", + m->machno, i, mach0->mtrrvar[i], m->mtrrvar[i]); + } +} + +static void +squidboy(Apic* apic) +{ +// iprint("Hello Squidboy\n"); + + machinit(); + mmuinit(); + + cpuidentify(); + cpuidprint(); + checkmtrr(); + + lock(&mprdthilock); + mprdthi |= (1<apicno)<<24; + unlock(&mprdthilock); + + lapicinit(apic); + lapiconline(); + syncclock(); + timersinit(); + + fpoff(); + + lock(&active); + active.machs |= 1<machno; + unlock(&active); + + while(!active.thunderbirdsarego) + microdelay(100); + + schedinit(); +} + +static void +mpstartap(Apic* apic) +{ + ulong *apbootp, *pdb, *pte; + Mach *mach, *mach0; + int i, machno; + uchar *p; + + mach0 = MACHP(0); + + /* + * Initialise the AP page-tables and Mach structure. The page-tables + * are the same as for the bootstrap processor with the exception of + * the PTE for the Mach structure. + * Xspanalloc will panic if an allocation can't be made. + */ + p = xspanalloc(4*BY2PG, BY2PG, 0); + pdb = (ulong*)p; + memmove(pdb, mach0->pdb, BY2PG); + p += BY2PG; + + if((pte = mmuwalk(pdb, MACHADDR, 1, 0)) == nil) + return; + memmove(p, KADDR(PPN(*pte)), BY2PG); + *pte = PADDR(p)|PTEWRITE|PTEVALID; + if(mach0->havepge) + *pte |= PTEGLOBAL; + p += BY2PG; + + mach = (Mach*)p; + if((pte = mmuwalk(pdb, MACHADDR, 2, 0)) == nil) + return; + *pte = PADDR(mach)|PTEWRITE|PTEVALID; + if(mach0->havepge) + *pte |= PTEGLOBAL; + p += BY2PG; + + machno = apic->machno; + MACHP(machno) = mach; + mach->machno = machno; + mach->pdb = pdb; + mach->gdt = (Segdesc*)p; /* filled by mmuinit */ + + /* + * Tell the AP where its kernel vector and pdb are. + * The offsets are known in the AP bootstrap code. + */ + apbootp = (ulong*)(APBOOTSTRAP+0x08); + *apbootp++ = (ulong)squidboy; + *apbootp++ = PADDR(pdb); + *apbootp = (ulong)apic; + + /* + * Universal Startup Algorithm. + */ + p = KADDR(0x467); + *p++ = PADDR(APBOOTSTRAP); + *p++ = PADDR(APBOOTSTRAP)>>8; + i = (PADDR(APBOOTSTRAP) & ~0xFFFF)/16; + *p++ = i; + *p = i>>8; + + nvramwrite(0x0F, 0x0A); + lapicstartap(apic, PADDR(APBOOTSTRAP)); + for(i = 0; i < 1000; i++){ + lock(&mprdthilock); + if(mprdthi & ((1<apicno)<<24)){ + unlock(&mprdthilock); + break; + } + unlock(&mprdthilock); + delay(10); + } + nvramwrite(0x0F, 0x00); +} + +void +mpinit(void) +{ + int ncpu; + char *cp; + PCMP *pcmp; + uchar *e, *p; + Apic *apic, *bpapic; + + i8259init(); + syncclock(); + + if(_mp_ == 0) + return; + pcmp = KADDR(_mp_->physaddr); + + /* + * Map the local APIC. + */ + if(mmukmap(pcmp->lapicbase, 0, 1024) == 0) + return; + + bpapic = nil; + + /* + * Run through the table saving information needed for starting + * application processors and initialising any I/O APICs. The table + * is guaranteed to be in order such that only one pass is necessary. + */ + p = ((uchar*)pcmp)+sizeof(PCMP); + e = ((uchar*)pcmp)+pcmp->length; + while(p < e) switch(*p){ + + default: + print("mpinit: unknown PCMP type 0x%uX (e-p 0x%luX)\n", + *p, e-p); + while(p < e){ + print("%uX ", *p); + p++; + } + break; + + case PcmpPROCESSOR: + if(apic = mkprocessor((PCMPprocessor*)p)){ + /* + * Must take a note of bootstrap processor APIC + * now as it will be needed in order to start the + * application processors later and there's no + * guarantee that the bootstrap processor appears + * first in the table before the others. + */ + apic->addr = KADDR(pcmp->lapicbase); + if(apic->flags & PcmpBP) + bpapic = apic; + } + p += sizeof(PCMPprocessor); + continue; + + case PcmpBUS: + mkbus((PCMPbus*)p); + p += sizeof(PCMPbus); + continue; + + case PcmpIOAPIC: + if(apic = mkioapic((PCMPioapic*)p)) + ioapicinit(apic, ((PCMPioapic*)p)->apicno); + p += sizeof(PCMPioapic); + continue; + + case PcmpIOINTR: + mkiointr((PCMPintr*)p); + p += sizeof(PCMPintr); + continue; + + case PcmpLINTR: + mklintr((PCMPintr*)p); + p += sizeof(PCMPintr); + continue; + } + + /* + * No bootstrap processor, no need to go further. + */ + if(bpapic == 0) + return; + + lapicinit(bpapic); + lock(&mprdthilock); + mprdthi |= (1<apicno)<<24; + unlock(&mprdthilock); + + /* + * These interrupts are local to the processor + * and do not appear in the I/O APIC so it is OK + * to set them now. + */ + intrenable(IrqTIMER, lapicclock, 0, BUSUNKNOWN, "clock"); + intrenable(IrqERROR, lapicerror, 0, BUSUNKNOWN, "lapicerror"); + intrenable(IrqSPURIOUS, lapicspurious, 0, BUSUNKNOWN, "lapicspurious"); + lapiconline(); + + checkmtrr(); + + /* + * Initialise the application processors. + */ + if(cp = getconf("*ncpu")){ + ncpu = strtol(cp, 0, 0); + if(ncpu < 1) + ncpu = 1; + } + else + ncpu = MaxAPICNO; + memmove((void*)APBOOTSTRAP, apbootstrap, sizeof(apbootstrap)); + for(apic = mpapic; apic <= &mpapic[MaxAPICNO]; apic++){ + if(ncpu <= 1) + break; + if((apic->flags & (PcmpBP|PcmpEN)) == PcmpEN + && apic->type == PcmpPROCESSOR){ + mpstartap(apic); + conf.nmach++; + ncpu--; + } + } + + /* + * we don't really know the number of processors till + * here. + * + * set conf.copymode here if nmach > 1. + * Should look for an ExtINT line and enable it. + */ + if(X86FAMILY(m->cpuidax) == 3 || conf.nmach > 1) + conf.copymode = 1; +} + +static int +mpintrenablex(Vctl* v, int tbdf) +{ + Bus *bus; + Aintr *aintr; + Apic *apic; + Pcidev *pcidev; + int bno, dno, irq, lo, n, type, vno; + + /* + * Find the bus. + */ + type = BUSTYPE(tbdf); + bno = BUSBNO(tbdf); + dno = BUSDNO(tbdf); + n = 0; + for(bus = mpbus; bus != nil; bus = bus->next){ + if(bus->type != type) + continue; + if(n == bno) + break; + n++; + } + if(bus == nil){ + print("ioapicirq: can't find bus type %d\n", type); + return -1; + } + + /* + * For PCI devices the interrupt pin (INT[ABCD]) and device + * number are encoded into the entry irq field, so create something + * to match on. The interrupt pin used by the device has to be + * obtained from the PCI config space. + */ + if(bus->type == BusPCI){ + pcidev = pcimatchtbdf(tbdf); + if(pcidev != nil && (n = pcicfgr8(pcidev, PciINTP)) != 0) + irq = (dno<<2)|(n-1); + else + irq = -1; + //print("pcidev %uX: irq %uX v->irq %uX\n", tbdf, irq, v->irq); + } + else + irq = v->irq; + + /* + * Find a matching interrupt entry from the list of interrupts + * attached to this bus. + */ + for(aintr = bus->aintr; aintr; aintr = aintr->next){ + if(aintr->intr->irq != irq) + continue; + + /* + * Check if already enabled. Multifunction devices may share + * INT[A-D]# so, if already enabled, check the polarity matches + * and the trigger is level. + * + * Should check the devices differ only in the function number, + * but that can wait for the planned enable/disable rewrite. + * The RDT read here is safe for now as currently interrupts + * are never disabled once enabled. + */ + apic = aintr->apic; + ioapicrdtr(apic, aintr->intr->intin, 0, &lo); + if(!(lo & ApicIMASK)){ + vno = lo & 0xFF; + n = mpintrinit(bus, aintr->intr, vno, v->irq); + n |= ApicLOGICAL; + if(n != lo || !(n & ApicLEVEL)){ + print("mpintrenable: multiple botch irq%d, tbdf %uX, lo %8.8uX, n %8.8uX\n", + v->irq, tbdf, lo, n); + return -1; + } + + v->isr = lapicisr; + v->eoi = lapiceoi; + + return vno; + } + + /* + * With the APIC a unique vector can be assigned to each + * request to enable an interrupt. There are two reasons this + * is a good idea: + * 1) to prevent lost interrupts, no more than 2 interrupts + * should be assigned per block of 16 vectors (there is an + * in-service entry and a holding entry for each priority + * level and there is one priority level per block of 16 + * interrupts). + * 2) each input pin on the IOAPIC will receive a different + * vector regardless of whether the devices on that pin use + * the same IRQ as devices on another pin. + */ + vno = VectorAPIC + (incref(&mpvnoref)-1)*8; + if(vno > MaxVectorAPIC){ + print("mpintrenable: vno %d, irq %d, tbdf %uX\n", + vno, v->irq, tbdf); + return -1; + } + lo = mpintrinit(bus, aintr->intr, vno, v->irq); + //print("lo 0x%uX: busno %d intr %d vno %d irq %d elcr 0x%uX\n", + // lo, bus->busno, aintr->intr->irq, vno, + // v->irq, i8259elcr); + if(lo & ApicIMASK) + return -1; + lo |= ApicLOGICAL; + + if((apic->flags & PcmpEN) && apic->type == PcmpIOAPIC){ + lock(&mprdthilock); + ioapicrdtw(apic, aintr->intr->intin, mprdthi, lo); + unlock(&mprdthilock); + } + //else + // print("lo not enabled 0x%uX %d\n", + // apic->flags, apic->type); + + v->isr = lapicisr; + v->eoi = lapiceoi; + + return vno; + } + + return -1; +} + +int +mpintrenable(Vctl* v) +{ + int irq, tbdf, vno; + + /* + * If the bus is known, try it. + * BUSUNKNOWN is given both by [E]ISA devices and by + * interrupts local to the processor (local APIC, coprocessor + * breakpoint and page-fault). + */ + tbdf = v->tbdf; + if(tbdf != BUSUNKNOWN && (vno = mpintrenablex(v, tbdf)) != -1) + return vno; + + irq = v->irq; + if(irq >= IrqLINT0 && irq <= MaxIrqLAPIC){ + if(irq != IrqSPURIOUS) + v->isr = lapiceoi; + return VectorPIC+irq; + } + if(irq < 0 || irq > MaxIrqPIC){ + print("mpintrenable: irq %d out of range\n", irq); + return -1; + } + + /* + * Either didn't find it or have to try the default buses + * (ISA and EISA). This hack is due to either over-zealousness + * or laziness on the part of some manufacturers. + * + * The MP configuration table on some older systems + * (e.g. ASUS PCI/E-P54NP4) has an entry for the EISA bus + * but none for ISA. It also has the interrupt type and + * polarity set to 'default for this bus' which wouldn't + * be compatible with ISA. + */ + if(mpeisabus != -1){ + vno = mpintrenablex(v, MKBUS(BusEISA, 0, 0, 0)); + if(vno != -1) + return vno; + } + if(mpisabus != -1){ + vno = mpintrenablex(v, MKBUS(BusISA, 0, 0, 0)); + if(vno != -1) + return vno; + } + + return -1; +} + +static Lock mpshutdownlock; + +void +mpshutdown(void) +{ + /* + * To be done... + */ + if(!canlock(&mpshutdownlock)){ + /* + * If this processor received the CTRL-ALT-DEL from + * the keyboard, acknowledge it. Send an INIT to self. + */ +#ifdef FIXTHIS + if(lapicisr(VectorKBD)) + lapiceoi(VectorKBD); +#endif /* FIX THIS */ + idle(); + } + + print("apshutdown: active = 0x%2.2uX\n", active.machs); + delay(1000); + splhi(); + + /* + * INIT all excluding self. + */ + lapicicrw(0, 0x000C0000|ApicINIT); + +#ifdef notdef + /* + * Often the BIOS hangs during restart if a conventional 8042 + * warm-boot sequence is tried. The following is Intel specific and + * seems to perform a cold-boot, but at least it comes back. + */ + *(ushort*)KADDR(0x472) = 0x1234; /* BIOS warm-boot flag */ + outb(0xCF9, 0x02); + outb(0xCF9, 0x06); +#else + pcireset(); + i8042reset(); +#endif /* notdef */ +} diff --git a/os/pc/mp.h b/os/pc/mp.h new file mode 100644 index 00000000..9195c9e9 --- /dev/null +++ b/os/pc/mp.h @@ -0,0 +1,225 @@ +/* + * MultiProcessor Specification Version 1.[14]. + */ +typedef struct { /* floating pointer */ + uchar signature[4]; /* "_MP_" */ + long physaddr; /* physical address of MP configuration table */ + uchar length; /* 1 */ + uchar specrev; /* [14] */ + uchar checksum; /* all bytes must add up to 0 */ + uchar type; /* MP system configuration type */ + uchar imcrp; + uchar reserved[3]; +} _MP_; + +typedef struct { /* configuration table header */ + uchar signature[4]; /* "PCMP" */ + ushort length; /* total table length */ + uchar version; /* [14] */ + uchar checksum; /* all bytes must add up to 0 */ + uchar product[20]; /* product id */ + ulong oemtable; /* OEM table pointer */ + ushort oemlength; /* OEM table length */ + ushort entry; /* entry count */ + ulong lapicbase; /* address of local APIC */ + ushort xlength; /* extended table length */ + uchar xchecksum; /* extended table checksum */ + uchar reserved; +} PCMP; + +typedef struct { /* processor table entry */ + uchar type; /* entry type (0) */ + uchar apicno; /* local APIC id */ + uchar version; /* local APIC verison */ + uchar flags; /* CPU flags */ + uchar signature[4]; /* CPU signature */ + ulong feature; /* feature flags from CPUID instruction */ + uchar reserved[8]; +} PCMPprocessor; + +typedef struct { /* bus table entry */ + uchar type; /* entry type (1) */ + uchar busno; /* bus id */ + char string[6]; /* bus type string */ +} PCMPbus; + +typedef struct { /* I/O APIC table entry */ + uchar type; /* entry type (2) */ + uchar apicno; /* I/O APIC id */ + uchar version; /* I/O APIC version */ + uchar flags; /* I/O APIC flags */ + ulong addr; /* I/O APIC address */ +} PCMPioapic; + +typedef struct { /* interrupt table entry */ + uchar type; /* entry type ([34]) */ + uchar intr; /* interrupt type */ + ushort flags; /* interrupt flag */ + uchar busno; /* source bus id */ + uchar irq; /* source bus irq */ + uchar apicno; /* destination APIC id */ + uchar intin; /* destination APIC [L]INTIN# */ +} PCMPintr; + +typedef struct { /* system address space mapping entry */ + uchar type; /* entry type (128) */ + uchar length; /* of this entry (20) */ + uchar busno; /* bus id */ + uchar addrtype; + ulong addrbase[2]; + ulong addrlength[2]; +} PCMPsasm; + +typedef struct { /* bus hierarchy descriptor entry */ + uchar type; /* entry type (129) */ + uchar length; /* of this entry (8) */ + uchar busno; /* bus id */ + uchar info; /* bus info */ + uchar parent; /* parent bus */ + uchar reserved[3]; +} PCMPhierarchy; + +typedef struct { /* compatibility bus address space modifier entry */ + uchar type; /* entry type (130) */ + uchar length; /* of this entry (8) */ + uchar busno; /* bus id */ + uchar modifier; /* address modifier */ + ulong range; /* predefined range list */ +} PCMPcbasm; + +enum { /* table entry types */ + PcmpPROCESSOR = 0x00, /* one entry per processor */ + PcmpBUS = 0x01, /* one entry per bus */ + PcmpIOAPIC = 0x02, /* one entry per I/O APIC */ + PcmpIOINTR = 0x03, /* one entry per bus interrupt source */ + PcmpLINTR = 0x04, /* one entry per system interrupt source */ + + PcmpSASM = 0x80, + PcmpHIERARCHY = 0x81, + PcmpCBASM = 0x82, + + /* PCMPprocessor and PCMPioapic flags */ + PcmpEN = 0x01, /* enabled */ + PcmpBP = 0x02, /* bootstrap processor */ + + /* PCMPiointr and PCMPlintr flags */ + PcmpPOMASK = 0x03, /* polarity conforms to specifications of bus */ + PcmpHIGH = 0x01, /* active high */ + PcmpLOW = 0x03, /* active low */ + PcmpELMASK = 0x0C, /* trigger mode of APIC input signals */ + PcmpEDGE = 0x04, /* edge-triggered */ + PcmpLEVEL = 0x0C, /* level-triggered */ + + /* PCMPiointr and PCMPlintr interrupt type */ + PcmpINT = 0x00, /* vectored interrupt from APIC Rdt */ + PcmpNMI = 0x01, /* non-maskable interrupt */ + PcmpSMI = 0x02, /* system management interrupt */ + PcmpExtINT = 0x03, /* vectored interrupt from external PIC */ + + /* PCMPsasm addrtype */ + PcmpIOADDR = 0x00, /* I/O address */ + PcmpMADDR = 0x01, /* memory address */ + PcmpPADDR = 0x02, /* prefetch address */ + + /* PCMPhierarchy info */ + PcmpSD = 0x01, /* subtractive decode bus */ + + /* PCMPcbasm modifier */ + PcmpPR = 0x01, /* predefined range list */ +}; + +/* + * Condensed form of the MP Configuration Table. + * This is created during a single pass through the MP Configuration + * table. + */ +typedef struct Aintr Aintr; +typedef struct Bus Bus; +typedef struct Apic Apic; + +typedef struct Bus { + uchar type; + uchar busno; + uchar po; + uchar el; + + Aintr* aintr; /* interrupts tied to this bus */ + Bus* next; +} Bus; + +typedef struct Aintr { + PCMPintr* intr; + Apic* apic; + Aintr* next; +}; + +typedef struct Apic { + int type; + int apicno; + ulong* addr; /* register base address */ + int flags; /* PcmpBP|PcmpEN */ + + Lock; /* I/O APIC: register access */ + int mre; /* I/O APIC: maximum redirection entry */ + + int lintr[2]; /* Local APIC */ + int machno; +} Apic; + +enum { + MaxAPICNO = 31, +}; + +enum { /* I/O APIC registers */ + IoapicID = 0x00, /* ID */ + IoapicVER = 0x01, /* version */ + IoapicARB = 0x02, /* arbitration ID */ + IoapicRDT = 0x10, /* redirection table */ +}; + +/* + * Common bits for + * I/O APIC Redirection Table Entry; + * Local APIC Local Interrupt Vector Table; + * Local APIC Inter-Processor Interrupt; + * Local APIC Timer Vector Table. + */ +enum { + ApicFIXED = 0x00000000, /* [10:8] Delivery Mode */ + ApicLOWEST = 0x00000100, /* Lowest priority */ + ApicSMI = 0x00000200, /* System Management Interrupt */ + ApicRR = 0x00000300, /* Remote Read */ + ApicNMI = 0x00000400, + ApicINIT = 0x00000500, /* INIT/RESET */ + ApicSTARTUP = 0x00000600, /* Startup IPI */ + ApicExtINT = 0x00000700, + + ApicPHYSICAL = 0x00000000, /* [11] Destination Mode (RW) */ + ApicLOGICAL = 0x00000800, + + ApicDELIVS = 0x00001000, /* [12] Delivery Status (RO) */ + ApicHIGH = 0x00000000, /* [13] Interrupt Input Pin Polarity (RW) */ + ApicLOW = 0x00002000, + ApicRemoteIRR = 0x00004000, /* [14] Remote IRR (RO) */ + ApicEDGE = 0x00000000, /* [15] Trigger Mode (RW) */ + ApicLEVEL = 0x00008000, + ApicIMASK = 0x00010000, /* [16] Interrupt Mask */ +}; + +extern void ioapicrdtr(Apic*, int, int*, int*); +extern void ioapicrdtw(Apic*, int, int, int); +extern void ioapicinit(Apic*, int); +extern void lapiconline(void); +extern void lapicinit(Apic*); +extern void lapicstartap(Apic*, int); +extern void lapicerror(Ureg*, void*); +extern void lapicspurious(Ureg*, void*); +extern int lapicisr(int); +extern int lapiceoi(int); +extern void lapicicrw(int, int); + +extern void mpinit(void); +extern void mpshutdown(void); +extern int mpintrenable(Vctl*); + +extern _MP_ *_mp_; diff --git a/os/pc/pc b/os/pc/pc new file mode 100644 index 00000000..56d82a89 --- /dev/null +++ b/os/pc/pc @@ -0,0 +1,139 @@ +dev + root + cons + arch + env + mnt + pipe + prog + rtc + srv + dup + ssl + cap + + draw screen vga vgax cga + pointer + vga + + ip bootp ip ipv6 ipaux iproute arp netlog ptclbsum386 iprouter plan9 nullmedium pktmedium + ether netif netaux ethermedium + +# ata + audio dma + uart +# floppy dma + tinyfs +# mouse +# dbg x86break +ip + tcp + udp +# rudp +# igmp + ipifc + icmp + icmp6 + ipmux +lib + interp + keyring + draw + memlayer + memdraw + tk + sec + mp + math + kern + +link + ether2114x pci +# ether82557 pci + ether83815 pci + etherelnk3 pci + ps2mouse + ethermedium +# pppmedium ppp compress + +misc + vgas3 +cur vgasavage + vgamach64xx + cga + uarti8250 + +mod + sys + draw + tk + keyring + math + +init + wminit + +code + int kernel_pool_pcnt = 10; + int main_pool_pcnt = 40; + int heap_pool_pcnt = 20; + int image_pool_pcnt = 40; + int cflag=0; + int swcursor=0; + int consoleprint=0; + +port + alarm + alloc + allocb + chan + dev + dial + dis + discall + exception + exportfs + inferno + latin1 + nocache + nodynld + parse + pgrp + print + proc + qio + qlock + random + sysfile + taslock + xalloc + +root + /chan / + /dev / + /dis + /env / + /fd / + /n + /n/remote + /net / + /nvfs / + /prog / + /dis/lib + /dis/svc + /dis/wm + /osinit.dis + /dis/sh.dis + /dis/ls.dis + /dis/cat.dis + /dis/bind.dis + /dis/mount.dis + /dis/pwd.dis + /dis/echo.dis + /dis/cd.dis + /dis/lib/bufio.dis + /dis/lib/string.dis + /dis/lib/readdir.dis + /dis/lib/workdir.dis + /dis/lib/daytime.dis + /dis/lib/auth.dis + /dis/lib/ssl.dis diff --git a/os/pc/pc4e b/os/pc/pc4e new file mode 100644 index 00000000..0507118c --- /dev/null +++ b/os/pc/pc4e @@ -0,0 +1,145 @@ +dev + root + cons + arch + pnp pci + env + mnt + pipe + prog + rtc + srv + dup + ssl + cap + draw + ip bootp ip ipv6 ipaux iproute arp netlog ptclbsum386 iprouter plan9 nullmedium pktmedium netaux + ether netif netaux ethermedium + +# ata +# audio + uart + floppy dma +# tinyfs +# mouse +# dbg x86break +ip + tcp + udp +# rudp +# igmp + ipifc + icmp + icmp6 + ipmux +lib + interp + keyring + draw + memlayer + memdraw + tk + sec + mp + math + kern + +link +# ether2114x pci +# ether82557 pci + ether83815 pci + etherelnk3 pci +# ps2mouse +# pppmedium ppp compress + ethermedium + +misc + cgamemscr +# vgaclgd542x +# vgas3 +# vgamach64ct + uarti8250 + +mod + sys + draw + tk + keyring + math + +init + i4e + +code + int kernel_pool_pcnt = 10; + int main_pool_pcnt = 40; + int heap_pool_pcnt = 20; + int image_pool_pcnt = 40; + int cflag=0; + int swcursor=0; + int consoleprint=1; + +port + alarm + alloc + allocb + chan + dev + dial + dis + discall + exception + exportfs + inferno + latin1 + nocache + nodynld + parse + pgrp + print + proc + qio + qlock + random + sysfile + taslock + xalloc + +root + /chan / + /dev / + /dis / + /env / + /fd / + /n + /n/remote + /net / + /net.alt / + /nvfs / + /prog / + /dis/lib + /dis/svc + /dis/wm + /osinit.dis + /dis/sh.dis + /dis/ls.dis + /dis/cat.dis + /dis/bind.dis + /dis/mount.dis + /dis/pwd.dis + /dis/echo.dis + /dis/cd.dis + /dis/lib/arg.dis + /dis/lib/bufio.dis + /dis/lib/dhcpclient.dis + /dis/lib/filepat.dis + /dis/lib/ip.dis + /dis/lib/string.dis + /dis/lib/readdir.dis + /dis/lib/workdir.dis + /dis/lib/daytime.dis + /dis/lib/auth.dis + /dis/lib/ssl.dis + /keydb / + /keydb/mutual /usr/i4e/keyring/mutual + /keydb/spree /usr/i4e/keyring/spree diff --git a/os/pc/pcdisk b/os/pc/pcdisk new file mode 100644 index 00000000..4ae6338a --- /dev/null +++ b/os/pc/pcdisk @@ -0,0 +1,154 @@ +dev + root + cons + arch + env + mnt + pipe + prog + rtc + srv + dup + ssl + cap + + ip bootp ip ipv6 ipaux iproute arp netlog ptclbsum386 iprouter plan9 nullmedium pktmedium + ether netif netaux ethermedium + + draw screen vga vgax cga + pointer + vga + + sd + ds + floppy dma + +# audio + uart + tinyfs + +# dbg x86break + +ip + tcp + udp +# rudp +# igmp + ipifc + icmp + icmp6 + ipmux +lib + interp + keyring + sec + mp + draw + memlayer + memdraw + tk + math + kern + +link + ether2114x pci +# ether82557 pci + ether83815 pci + etherelnk3 pci + ps2mouse + ethermedium +# pppmedium ppp compress + +misc + vgas3 +cur vgasavage + vgamach64xx + cga + + sdata pci sdscsi + sd53c8xx pci sdscsi + + uarti8250 + +mod + sys + draw + tk + keyring + math + +init + wminit + +code + int kernel_pool_pcnt = 10; + int main_pool_pcnt = 40; + int heap_pool_pcnt = 20; + int image_pool_pcnt = 40; + int cflag=0; + int swcursor=0; + int consoleprint=0; + int novgascreen=1; + +port + alarm + alloc + allocb + chan + dev + dial + dis + discall + exception + exportfs + inferno + latin1 + nocache + nodynld + parse + pgrp + print + proc + qio + qlock + random + sysfile + taslock + xalloc + +root + /chan + /dev + /dis + /env + /fd / + /n + /n/remote + /net + /nvfs + /prog + /dis/lib + /dis/svc + /dis/wm + /osinit.dis + /dis/sh.dis + /dis/ls.dis + /dis/cat.dis + /dis/bind.dis + /dis/mount.dis + /dis/pwd.dis + /dis/echo.dis + /dis/cd.dis + /dis/lib/bufio.dis + /dis/lib/string.dis + /dis/lib/readdir.dis + /dis/lib/workdir.dis + /dis/lib/daytime.dis + /dis/lib/auth.dis + /dis/lib/ssl.dis + +# kfs + /dis/disk/kfs.dis + /dis/lib/arg.dis + /dis/lib/daytime.dis + /dis/lib/string.dis + /dis/lib/styx.dis diff --git a/os/pc/pci.acid b/os/pc/pci.acid new file mode 100644 index 00000000..46da501e --- /dev/null +++ b/os/pc/pci.acid @@ -0,0 +1,252 @@ +// ACID PCI support + +pciclassdb = { + {0x0000, "Unclassified device", { + {0x0000,"Non-VGA unclassified device"}, + {0x0001,"VGA compatible unclassified device"}, + }}, + {0x0001, "Mass storage controller", { + {0x0000,"SCSI storage controller"}, + {0x0001,"IDE interface"}, + {0x0002,"Floppy disk controller"}, + {0x0003,"IPI bus controller"}, + {0x0004,"RAID bus controller"}, + {0x0080,"Unknown mass storage controller"}, + }}, + {0x0002, "Network controller", { + {0x0000,"Ethernet controller"}, + {0x0001,"Token ring network controller"}, + {0x0002,"FDDI network controller"}, + {0x0003,"ATM network controller"}, + {0x0080,"Network controller"}, + }}, + {0x0003, "Display controller", { + {0x0000,"VGA compatible controller"}, + {0x0001,"XGA compatible controller"}, + {0x0080,"Display controller"}, + }}, + {0x0004, "Multimedia controller", { + {0x0000,"Multimedia video controller"}, + {0x0001,"Multimedia audio controller"}, + {0x0080,"Multimedia controller"}, + }}, + {0x0005, "Memory controller", { + {0x0000,"RAM memory"}, + {0x0001,"FLASH memory"}, + {0x0080,"Memory"}, + }}, + {0x0006, "Bridge", { + {0x0000,"Host bridge"}, + {0x0001,"ISA bridge"}, + {0x0002,"EISA bridge"}, + {0x0003,"MicroChannel bridge"}, + {0x0004,"PCI bridge"}, + {0x0005,"PCMCIA bridge"}, + {0x0006,"NuBus bridge"}, + {0x0007,"CardBus bridge"}, + {0x0080,"Bridge"}, + }}, + {0x0007, "Communication controller", { + {0x0000,"Serial controller"}, + {0x0001,"Parallel controller"}, + {0x0080,"Communication controller"}, + }}, + {0x0008, "Generic system peripheral", { + {0x0000,"PIC"}, + {0x0001,"DMA controller"}, + {0x0002,"Timer"}, + {0x0003,"RTC"}, + {0x0080,"System peripheral"}, + }}, + {0x0009, "Input device controller", { + {0x0000,"Keyboard controller"}, + {0x0001,"Digitizer Pen"}, + {0x0002,"Mouse controller"}, + {0x0080,"Input device controller"}, + }}, + {0x000A, "Docking station", { + {0x0000,"Generic Docking Station"}, + {0x0080,"Docking Station"}, + }}, + {0x000B, "Processor", { + {0x0000,"386"}, + {0x0001,"486"}, + {0x0002,"Pentium"}, + {0x0010,"Alpha"}, + {0x0020,"Power PC"}, + {0x0040,"Co-processor"}, + }}, + {0x000C, "Serial bus controller", { + {0x0000,"FireWire (IEEE 1394)"}, + {0x0001,"ACCESS Bus"}, + {0x0002,"SSA"}, + {0x0003,"USB Controller"}, + {0x0004,"Fiber Channel"}, + }}, +}; + +// +// Include the vendor and device id database +// + +include( "pcidb.acid" ); + +defn test(thingy) +{ + return thingy; +} + +defn BUSFNO(tbdf) +{ + return (((tbdf\X)>>8)&0x07); +} + +defn BUSDNO(tbdf) +{ + return (((tbdf\X)>>11)&0x1F); +} + +defn BUSBNO(tbdf) +{ + return (((tbdf\X)>>16)&0xFF); +} + +defn lookup( key, dictionary ) +{ + local d, e; + + d = dictionary; + while(d != {}) do { + e = head d; + if e[0] == key then + return e; + d = tail d; + } + return {}; +} + +defn pciclass( ccru ) +{ + local c, sc; + local class, subclass; + c = ccru >> 8; + sc = ccru & 0xff; + + class = lookup( c, pciclassdb ); + subclass = lookup( sc, class[2] ); + + if (subclass != {}) then { + print(" ",subclass[1]); + return 1; + } + if (class != {}) then { + print(" ",class[1]); + return 1; + } + + print(" type=",ccru\x); + return 0; +} + +defn pcivendor( vid ) +{ + local v; + v = lookup( vid, pcivendordb ); + if (v != {}) then { + print(" ",v[1]); + } else { + print(" VendorID=",vid\x,":"); + } +} + +defn pcidev( vid, did ) +{ + local v; + local d; + v = lookup( vid, pcivendordb ); + if (v != {}) then { + d = lookup( did, v[2] ); + if (d != {}) then { + print(" ",d[1]); + return 1; + } + } + print(" DeviceID=",did\x); + return 0; +} + +// +// Dump PCI Info (short form) +// + +defn pciinfo( r ) +{ + local t; + local pcicount; + t = r; + pcicount = 0\d; + while t != 0 do { + print(pcicount\d,":","Bus:",BUSBNO(t.tbdf)\u," dev:",BUSDNO(t.tbdf)\u," fn:",BUSFNO(t.tbdf)\u,":"); + pciclass(t.ccru); + print(":"); + pcivendor(t.vid); + pcidev(t.vid, t.did); + print("\n"); + pcicount = pcicount+1 ; + t = t.link; + } + t = r; + while t!= 0 do { + if (t.bridge !=0) then + pciinfo(t.bridge); + t = t.link; + } +} + +defn lspci() +{ + pciinfo( *pciroot ); +} + +// +// Dump PCI Info (long form - includes interrupt lines & memory segments) +// + +defn pcidumper( r ) +{ + local t; + local m; + t = r; + while t != 0 do { + print("Bus\t",BUSBNO(t.tbdf)\u); + print(", device\t",BUSDNO(t.tbdf)\u); + print(", function\t",BUSFNO(t.tbdf)\u,":\n"); + print(" "); + pciclass(t.ccru); + print(":"); + pcivendor(t.vid); + pcidev(t.vid, t.did); + print("\n"); + print(" Interrupt Line: ",t.intl\u,"\n"); + m =0\d; + loop 0,5 do { + if t.mem[m] != 0 then + print("\t",(m/2)\d,":",t.mem[m]\X," ",t.mem[m+1]\X,"\n"); + m = m + 2; + } + t = t.link; + } + t = r; + while t!= 0 do { + if (t.bridge !=0) then + pcidumper(t.bridge); + t = t.link; + } +} + +defn pcidump() +{ + pcidumper( *pciroot ); +} + +print("/os/pc/pci"); diff --git a/os/pc/pci.c b/os/pc/pci.c new file mode 100644 index 00000000..4f7e79b6 --- /dev/null +++ b/os/pc/pci.c @@ -0,0 +1,1341 @@ +/* + * PCI support code. + * Needs a massive rewrite. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define DBG if(0) pcilog + +struct +{ + char output[16384]; + int ptr; +}PCICONS; + +int +pcilog(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + + memmove(PCICONS.output+PCICONS.ptr, buf, n); + PCICONS.ptr += n; + return n; +} + +enum +{ /* configuration mechanism #1 */ + PciADDR = 0xCF8, /* CONFIG_ADDRESS */ + PciDATA = 0xCFC, /* CONFIG_DATA */ + + /* configuration mechanism #2 */ + PciCSE = 0xCF8, /* configuration space enable */ + PciFORWARD = 0xCFA, /* which bus */ + + MaxFNO = 7, + MaxUBN = 255, +}; + +enum +{ /* command register */ + IOen = (1<<0), + MEMen = (1<<1), + MASen = (1<<2), + MemWrInv = (1<<4), + PErrEn = (1<<6), + SErrEn = (1<<8), +}; + +static Lock pcicfglock; +static Lock pcicfginitlock; +static int pcicfgmode = -1; +static int pcimaxbno = 7; +static int pcimaxdno; +static Pcidev* pciroot; +static Pcidev* pcilist; +static Pcidev* pcitail; +static int nobios, nopcirouting; + +static int pcicfgrw32(int, int, int, int); +static int pcicfgrw16(int, int, int, int); +static int pcicfgrw8(int, int, int, int); + +static char* bustypes[] = { + "CBUSI", + "CBUSII", + "EISA", + "FUTURE", + "INTERN", + "ISA", + "MBI", + "MBII", + "MCA", + "MPI", + "MPSA", + "NUBUS", + "PCI", + "PCMCIA", + "TC", + "VL", + "VME", + "XPRESS", +}; + +#pragma varargck type "T" int + +static int +tbdffmt(Fmt* fmt) +{ + char *p; + int l, r, type, tbdf; + + if((p = malloc(READSTR)) == nil) + return fmtstrcpy(fmt, "(tbdfconv)"); + + switch(fmt->r){ + case 'T': + tbdf = va_arg(fmt->args, int); + type = BUSTYPE(tbdf); + if(type < nelem(bustypes)) + l = snprint(p, READSTR, bustypes[type]); + else + l = snprint(p, READSTR, "%d", type); + snprint(p+l, READSTR-l, ".%d.%d.%d", + BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf)); + break; + + default: + snprint(p, READSTR, "(tbdfconv)"); + break; + } + r = fmtstrcpy(fmt, p); + free(p); + + return r; +} + +ulong +pcibarsize(Pcidev *p, int rno) +{ + ulong v, size; + + v = pcicfgrw32(p->tbdf, rno, 0, 1); + pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0); + size = pcicfgrw32(p->tbdf, rno, 0, 1); + if(v & 1) + size |= 0xFFFF0000; + pcicfgrw32(p->tbdf, rno, v, 0); + + return -(size & ~0x0F); +} + +static int +pcisizcmp(void *a, void *b) +{ + Pcisiz *aa, *bb; + + aa = a; + bb = b; + return aa->siz - bb->siz; +} + +static ulong +pcimask(ulong v) +{ + ulong m; + + m = BI2BY*sizeof(v); + for(m = 1<<(m-1); m != 0; m >>= 1) { + if(m & v) + break; + } + + m--; + if((v & m) == 0) + return v; + + v |= m; + return v+1; +} + +static void +pcibusmap(Pcidev *root, ulong *pmema, ulong *pioa, int wrreg) +{ + Pcidev *p; + int ntb, i, size, rno, hole; + ulong v, mema, ioa, sioa, smema, base, limit; + Pcisiz *table, *tptr, *mtb, *itb; + extern void qsort(void*, long, long, int (*)(void*, void*)); + + if(!nobios) + return; + + ioa = *pioa; + mema = *pmema; + + DBG("pcibusmap wr=%d %T mem=%luX io=%luX\n", + wrreg, root->tbdf, mema, ioa); + + ntb = 0; + for(p = root; p != nil; p = p->link) + ntb++; + + ntb *= (PciCIS-PciBAR0)/4; + table = malloc(2*ntb*sizeof(Pcisiz)); + itb = table; + mtb = table+ntb; + + /* + * Build a table of sizes + */ + for(p = root; p != nil; p = p->link) { + if(p->ccrb == 0x06) { + if(p->ccru != 0x04 || p->bridge == nil) { +// DBG("pci: ignored bridge %T\n", p->tbdf); + continue; + } + + sioa = ioa; + smema = mema; + pcibusmap(p->bridge, &smema, &sioa, 0); + + hole = pcimask(smema-mema); + if(hole < (1<<20)) + hole = 1<<20; + p->mema.size = hole; + + hole = pcimask(sioa-ioa); + if(hole < (1<<12)) + hole = 1<<12; + + p->ioa.size = hole; + + itb->dev = p; + itb->bar = -1; + itb->siz = p->ioa.size; + itb++; + + mtb->dev = p; + mtb->bar = -1; + mtb->siz = p->mema.size; + mtb++; + continue; + } + + for(i = 0; i <= 5; i++) { + rno = PciBAR0 + i*4; + v = pcicfgrw32(p->tbdf, rno, 0, 1); + size = pcibarsize(p, rno); + if(size == 0) + continue; + + if(v & 1) { + itb->dev = p; + itb->bar = i; + itb->siz = size; + itb++; + } + else { + mtb->dev = p; + mtb->bar = i; + mtb->siz = size; + mtb++; + } + + p->mem[i].size = size; + } + } + + /* + * Sort both tables IO smallest first, Memory largest + */ + qsort(table, itb-table, sizeof(Pcisiz), pcisizcmp); + tptr = table+ntb; + qsort(tptr, mtb-tptr, sizeof(Pcisiz), pcisizcmp); + + /* + * Allocate IO address space on this bus + */ + for(tptr = table; tptr < itb; tptr++) { + hole = tptr->siz; + if(tptr->bar == -1) + hole = 1<<12; + ioa = (ioa+hole-1) & ~(hole-1); + + p = tptr->dev; + if(tptr->bar == -1) + p->ioa.bar = ioa; + else { + p->pcr |= IOen; + p->mem[tptr->bar].bar = ioa|1; + if(wrreg) + pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), ioa|1, 0); + } + + ioa += tptr->siz; + } + + /* + * Allocate Memory address space on this bus + */ + for(tptr = table+ntb; tptr < mtb; tptr++) { + hole = tptr->siz; + if(tptr->bar == -1) + hole = 1<<20; + mema = (mema+hole-1) & ~(hole-1); + + p = tptr->dev; + if(tptr->bar == -1) + p->mema.bar = mema; + else { + p->pcr |= MEMen; + p->mem[tptr->bar].bar = mema; + if(wrreg) + pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), mema, 0); + } + mema += tptr->siz; + } + + *pmema = mema; + *pioa = ioa; + free(table); + + if(wrreg == 0) + return; + + /* + * Finally set all the bridge addresses & registers + */ + for(p = root; p != nil; p = p->link) { + if(p->bridge == nil) { + pcicfgrw8(p->tbdf, PciLTR, 64, 0); + + p->pcr |= MASen; + pcicfgrw16(p->tbdf, PciPCR, p->pcr, 0); + continue; + } + + base = p->ioa.bar; + limit = base+p->ioa.size-1; + v = pcicfgrw32(p->tbdf, PciIBR, 0, 1); + v = (v&0xFFFF0000)|(limit & 0xF000)|((base & 0xF000)>>8); + pcicfgrw32(p->tbdf, PciIBR, v, 0); + v = (limit & 0xFFFF0000)|(base>>16); + pcicfgrw32(p->tbdf, PciIUBR, v, 0); + + base = p->mema.bar; + limit = base+p->mema.size-1; + v = (limit & 0xFFF00000)|((base & 0xFFF00000)>>16); + pcicfgrw32(p->tbdf, PciMBR, v, 0); + + /* + * Disable memory prefetch + */ + pcicfgrw32(p->tbdf, PciPMBR, 0x0000FFFF, 0); + pcicfgrw8(p->tbdf, PciLTR, 64, 0); + + /* + * Enable the bridge + */ + p->pcr |= IOen|MEMen|MASen; + pcicfgrw32(p->tbdf, PciPCR, 0xFFFF0000|p->pcr , 0); + + sioa = p->ioa.bar; + smema = p->mema.bar; + pcibusmap(p->bridge, &smema, &sioa, 1); + } +} + +static int +pcilscan(int bno, Pcidev** list) +{ + Pcidev *p, *head, *tail; + int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn; + + maxubn = bno; + head = nil; + tail = nil; + for(dno = 0; dno <= pcimaxdno; dno++){ + maxfno = 0; + for(fno = 0; fno <= maxfno; fno++){ + /* + * For this possible device, form the + * bus+device+function triplet needed to address it + * and try to read the vendor and device ID. + * If successful, allocate a device struct and + * start to fill it in with some useful information + * from the device's configuration space. + */ + tbdf = MKBUS(BusPCI, bno, dno, fno); + l = pcicfgrw32(tbdf, PciVID, 0, 1); + if(l == 0xFFFFFFFF || l == 0) + continue; + p = malloc(sizeof(*p)); + p->tbdf = tbdf; + p->vid = l; + p->did = l>>16; + + if(pcilist != nil) + pcitail->list = p; + else + pcilist = p; + pcitail = p; + + p->pcr = pcicfgr16(p, PciPCR); + p->rid = pcicfgr8(p, PciRID); + p->ccrp = pcicfgr8(p, PciCCRp); + p->ccru = pcicfgr8(p, PciCCRu); + p->ccrb = pcicfgr8(p, PciCCRb); + p->cls = pcicfgr8(p, PciCLS); + p->ltr = pcicfgr8(p, PciLTR); + + p->intl = pcicfgr8(p, PciINTL); + + /* + * If the device is a multi-function device adjust the + * loop count so all possible functions are checked. + */ + hdt = pcicfgr8(p, PciHDT); + if(hdt & 0x80) + maxfno = MaxFNO; + + /* + * If appropriate, read the base address registers + * and work out the sizes. + */ + switch(p->ccrb) { + case 0x01: /* mass storage controller */ + case 0x02: /* network controller */ + case 0x03: /* display controller */ + case 0x04: /* multimedia device */ + case 0x07: /* simple comm. controllers */ + case 0x08: /* base system peripherals */ + case 0x09: /* input devices */ + case 0x0A: /* docking stations */ + case 0x0B: /* processors */ + case 0x0C: /* serial bus controllers */ + if((hdt & 0x7F) != 0) + break; + rno = PciBAR0 - 4; + for(i = 0; i < nelem(p->mem); i++) { + rno += 4; + p->mem[i].bar = pcicfgr32(p, rno); + p->mem[i].size = pcibarsize(p, rno); + } + break; + + case 0x00: + case 0x05: /* memory controller */ + case 0x06: /* bridge device */ + default: + break; + } + + if(head != nil) + tail->link = p; + else + head = p; + tail = p; + } + } + + *list = head; + for(p = head; p != nil; p = p->link){ + /* + * Find PCI-PCI bridges and recursively descend the tree. + */ + if(p->ccrb != 0x06 || p->ccru != 0x04) + continue; + + /* + * If the secondary or subordinate bus number is not + * initialised try to do what the PCI BIOS should have + * done and fill in the numbers as the tree is descended. + * On the way down the subordinate bus number is set to + * the maximum as it's not known how many buses are behind + * this one; the final value is set on the way back up. + */ + sbn = pcicfgr8(p, PciSBN); + ubn = pcicfgr8(p, PciUBN); + + if(sbn == 0 || ubn == 0 || nobios) { + sbn = maxubn+1; + /* + * Make sure memory, I/O and master enables are + * off, set the primary, secondary and subordinate + * bus numbers and clear the secondary status before + * attempting to scan the secondary bus. + * + * Initialisation of the bridge should be done here. + */ + pcicfgw32(p, PciPCR, 0xFFFF0000); + l = (MaxUBN<<16)|(sbn<<8)|bno; + pcicfgw32(p, PciPBN, l); + pcicfgw16(p, PciSPSR, 0xFFFF); + maxubn = pcilscan(sbn, &p->bridge); + l = (maxubn<<16)|(sbn<<8)|bno; + + pcicfgw32(p, PciPBN, l); + } + else { + if(ubn > maxubn) + maxubn = ubn; + pcilscan(sbn, &p->bridge); + } + } + + return maxubn; +} + +int +pciscan(int bno, Pcidev **list) +{ + int ubn; + + lock(&pcicfginitlock); + ubn = pcilscan(bno, list); + unlock(&pcicfginitlock); + return ubn; +} + +static uchar +pIIxget(Pcidev *router, uchar link) +{ + uchar pirq; + + /* link should be 0x60, 0x61, 0x62, 0x63 */ + pirq = pcicfgr8(router, link); + return (pirq < 16)? pirq: 0; +} + +static void +pIIxset(Pcidev *router, uchar link, uchar irq) +{ + pcicfgw8(router, link, irq); +} + +static uchar +viaget(Pcidev *router, uchar link) +{ + uchar pirq; + + /* link should be 1, 2, 3, 5 */ + pirq = (link < 6)? pcicfgr8(router, 0x55 + (link>>1)): 0; + + return (link & 1)? (pirq >> 4): (pirq & 15); +} + +static void +viaset(Pcidev *router, uchar link, uchar irq) +{ + uchar pirq; + + pirq = pcicfgr8(router, 0x55 + (link >> 1)); + pirq &= (link & 1)? 0x0f: 0xf0; + pirq |= (link & 1)? (irq << 4): (irq & 15); + pcicfgw8(router, 0x55 + (link>>1), pirq); +} + +static uchar +optiget(Pcidev *router, uchar link) +{ + uchar pirq = 0; + + /* link should be 0x02, 0x12, 0x22, 0x32 */ + if ((link & 0xcf) == 0x02) + pirq = pcicfgr8(router, 0xb8 + (link >> 5)); + return (link & 0x10)? (pirq >> 4): (pirq & 15); +} + +static void +optiset(Pcidev *router, uchar link, uchar irq) +{ + uchar pirq; + + pirq = pcicfgr8(router, 0xb8 + (link >> 5)); + pirq &= (link & 0x10)? 0x0f : 0xf0; + pirq |= (link & 0x10)? (irq << 4): (irq & 15); + pcicfgw8(router, 0xb8 + (link >> 5), pirq); +} + +static uchar +aliget(Pcidev *router, uchar link) +{ + /* No, you're not dreaming */ + static const uchar map[] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 }; + uchar pirq; + + /* link should be 0x01..0x08 */ + pirq = pcicfgr8(router, 0x48 + ((link-1)>>1)); + return (link & 1)? map[pirq&15]: map[pirq>>4]; +} + +static void +aliset(Pcidev *router, uchar link, uchar irq) +{ + /* Inverse of map in aliget */ + static const uchar map[] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 }; + uchar pirq; + + pirq = pcicfgr8(router, 0x48 + ((link-1)>>1)); + pirq &= (link & 1)? 0x0f: 0xf0; + pirq |= (link & 1)? (map[irq] << 4): (map[irq] & 15); + pcicfgw8(router, 0x48 + ((link-1)>>1), pirq); +} + +static uchar +cyrixget(Pcidev *router, uchar link) +{ + uchar pirq; + + /* link should be 1, 2, 3, 4 */ + pirq = pcicfgr8(router, 0x5c + ((link-1)>>1)); + return ((link & 1)? pirq >> 4: pirq & 15); +} + +static void +cyrixset(Pcidev *router, uchar link, uchar irq) +{ + uchar pirq; + + pirq = pcicfgr8(router, 0x5c + (link>>1)); + pirq &= (link & 1)? 0x0f: 0xf0; + pirq |= (link & 1)? (irq << 4): (irq & 15); + pcicfgw8(router, 0x5c + (link>>1), pirq); +} + +typedef struct Bridge Bridge; +struct Bridge +{ + ushort vid; + ushort did; + uchar (*get)(Pcidev *, uchar); + void (*set)(Pcidev *, uchar, uchar); +}; + +static Bridge southbridges[] = { + { 0x8086, 0x122e, pIIxget, pIIxset }, // Intel 82371FB + { 0x8086, 0x1234, pIIxget, pIIxset }, // Intel 82371MX + { 0x8086, 0x7000, pIIxget, pIIxset }, // Intel 82371SB + { 0x8086, 0x7110, pIIxget, pIIxset }, // Intel 82371AB + { 0x8086, 0x7198, pIIxget, pIIxset }, // Intel 82443MX (fn 1) + { 0x8086, 0x2410, pIIxget, pIIxset }, // Intel 82801AA + { 0x8086, 0x2420, pIIxget, pIIxset }, // Intel 82801AB + { 0x8086, 0x2440, pIIxget, pIIxset }, // Intel 82801BA + { 0x8086, 0x244c, pIIxget, pIIxset }, // Intel 82801BAM + { 0x8086, 0x248c, pIIxget, pIIxset }, // Intel 82801CAM + { 0x8086, 0x24cc, pIIxget, pIIxset }, // Intel 82801DBM + { 0x8086, 0x24d0, pIIxget, pIIxset }, // Intel 82801EB + { 0x8086, 0x2640, pIIxget, pIIxset }, // Intel 82801FB + { 0x1106, 0x0586, viaget, viaset }, // Viatech 82C586 + { 0x1106, 0x0596, viaget, viaset }, // Viatech 82C596 + { 0x1106, 0x0686, viaget, viaset }, // Viatech 82C686 + { 0x1106, 0x3227, viaget, viaset }, // Viatech VT8237 + { 0x1045, 0xc700, optiget, optiset }, // Opti 82C700 + { 0x10b9, 0x1533, aliget, aliset }, // Al M1533 + { 0x1039, 0x0008, pIIxget, pIIxset }, // SI 503 + { 0x1039, 0x0496, pIIxget, pIIxset }, // SI 496 + { 0x1078, 0x0100, cyrixget, cyrixset }, // Cyrix 5530 Legacy + + { 0x1022, 0x746B, nil, nil }, // AMD 8111 + { 0x10DE, 0x00D1, nil, nil }, // NVIDIA nForce 3 + { 0x1166, 0x0200, nil, nil }, // ServerWorks ServerSet III LE +}; + +typedef struct Slot Slot; +struct Slot { + uchar bus; // Pci bus number + uchar dev; // Pci device number + uchar maps[12]; // Avoid structs! Link and mask. + uchar slot; // Add-in/built-in slot + uchar reserved; +}; + +typedef struct Router Router; +struct Router { + uchar signature[4]; // Routing table signature + uchar version[2]; // Version number + uchar size[2]; // Total table size + uchar bus; // Interrupt router bus number + uchar devfn; // Router's devfunc + uchar pciirqs[2]; // Exclusive PCI irqs + uchar compat[4]; // Compatible PCI interrupt router + uchar miniport[4]; // Miniport data + uchar reserved[11]; + uchar checksum; +}; + +static ushort pciirqs; // Exclusive PCI irqs +static Bridge *southbridge; // Which southbridge to use. + +static void +pcirouting(void) +{ + Slot *e; + Router *r; + int size, i, fn, tbdf; + Pcidev *sbpci, *pci; + uchar *p, pin, irq, link, *map; + + // Search for PCI interrupt routing table in BIOS + for(p = (uchar *)KADDR(0xf0000); p < (uchar *)KADDR(0xfffff); p += 16) + if(p[0] == '$' && p[1] == 'P' && p[2] == 'I' && p[3] == 'R') + break; + + if(p >= (uchar *)KADDR(0xfffff)) + return; + + r = (Router *)p; + + // print("PCI interrupt routing table version %d.%d at %.6uX\n", + // r->version[0], r->version[1], (ulong)r & 0xfffff); + + tbdf = (BusPCI << 24)|(r->bus << 16)|(r->devfn << 8); + sbpci = pcimatchtbdf(tbdf); + if(sbpci == nil) { + print("pcirouting: Cannot find south bridge %T\n", tbdf); + return; + } + + for(i = 0; i != nelem(southbridges); i++) + if(sbpci->vid == southbridges[i].vid && sbpci->did == southbridges[i].did) + break; + + if(i == nelem(southbridges)) { + print("pcirouting: ignoring south bridge %T %.4uX/%.4uX\n", tbdf, sbpci->vid, sbpci->did); + return; + } + southbridge = &southbridges[i]; + if(southbridge->get == nil || southbridge->set == nil) + return; + + pciirqs = (r->pciirqs[1] << 8)|r->pciirqs[0]; + + size = (r->size[1] << 8)|r->size[0]; + for(e = (Slot *)&r[1]; (uchar *)e < p + size; e++) { + // print("%.2uX/%.2uX %.2uX: ", e->bus, e->dev, e->slot); + // for (i = 0; i != 4; i++) { + // uchar *m = &e->maps[i * 3]; + // print("[%d] %.2uX %.4uX ", + // i, m[0], (m[2] << 8)|m[1]); + // } + // print("\n"); + + for(fn = 0; fn != 8; fn++) { + tbdf = (BusPCI << 24)|(e->bus << 16)|((e->dev | fn) << 8); + pci = pcimatchtbdf(tbdf); + if(pci == nil) + continue; + pin = pcicfgr8(pci, PciINTP); + if(pin == 0 || pin == 0xff) + continue; + + map = &e->maps[(pin - 1) * 3]; + link = map[0]; + irq = southbridge->get(sbpci, link); + if(irq == 0 || irq == pci->intl) + continue; + if(pci->intl != 0 && pci->intl != 0xFF) { + print("pcirouting: BIOS workaround: %T at pin %d link %d irq %d -> %d\n", + tbdf, pin, link, irq, pci->intl); + southbridge->set(sbpci, link, pci->intl); + continue; + } + print("pcirouting: %T at pin %d link %d irq %d\n", tbdf, pin, link, irq); + pcicfgw8(pci, PciINTL, irq); + pci->intl = irq; + } + } +} + +static void +pcicfginit(void) +{ + char *p; + Pcidev **list; + ulong mema, ioa; + int bno, n, pcibios; + + lock(&pcicfginitlock); + if(pcicfgmode != -1) + goto out; + + pcibios = 0; + if(getconf("*nobios")) + nobios = 1; + else if(getconf("*pcibios")) + pcibios = 1; + if(getconf("*nopcirouting")) + nopcirouting = 1; + + /* + * Try to determine which PCI configuration mode is implemented. + * Mode2 uses a byte at 0xCF8 and another at 0xCFA; Mode1 uses + * a DWORD at 0xCF8 and another at 0xCFC and will pass through + * any non-DWORD accesses as normal I/O cycles. There shouldn't be + * a device behind these addresses so if Mode1 accesses fail try + * for Mode2 (Mode2 is deprecated). + */ + if(!pcibios){ + /* + * Bits [30:24] of PciADDR must be 0, + * according to the spec. + */ + n = inl(PciADDR); + if(!(n & 0x7FF00000)){ + outl(PciADDR, 0x80000000); + outb(PciADDR+3, 0); + if(inl(PciADDR) & 0x80000000){ + pcicfgmode = 1; + pcimaxdno = 31; + } + } + outl(PciADDR, n); + + if(pcicfgmode < 0){ + /* + * The 'key' part of PciCSE should be 0. + */ + n = inb(PciCSE); + if(!(n & 0xF0)){ + outb(PciCSE, 0x0E); + if(inb(PciCSE) == 0x0E){ + pcicfgmode = 2; + pcimaxdno = 15; + } + } + outb(PciCSE, n); + } + } + + if(pcicfgmode < 0) + goto out; + + fmtinstall('T', tbdffmt); + + if(p = getconf("*pcimaxbno")){ + n = strtoul(p, 0, 0); + if(n < pcimaxbno) + pcimaxbno = n; + } + if(p = getconf("*pcimaxdno")){ + n = strtoul(p, 0, 0); + if(n < pcimaxdno) + pcimaxdno = n; + } + + list = &pciroot; + for(bno = 0; bno <= pcimaxbno; bno++) { + int sbno = bno; + bno = pcilscan(bno, list); + + while(*list) + list = &(*list)->link; + + if (sbno == 0) { + Pcidev *pci; + + /* + * If we have found a PCI-to-Cardbus bridge, make sure + * it has no valid mappings anymore. + */ + pci = pciroot; + while (pci) { + if (pci->ccrb == 6 && pci->ccru == 7) { + ushort bcr; + + /* reset the cardbus */ + bcr = pcicfgr16(pci, PciBCR); + pcicfgw16(pci, PciBCR, 0x40 | bcr); + delay(50); + } + pci = pci->link; + } + } + } + + if(pciroot == nil) + goto out; + + if(nobios) { + /* + * Work out how big the top bus is + */ + mema = 0; + ioa = 0; + pcibusmap(pciroot, &mema, &ioa, 0); + + DBG("Sizes: mem=%8.8lux size=%8.8lux io=%8.8lux\n", + mema, pcimask(mema), ioa); + + /* + * Align the windows and map it + */ + ioa = 0x1000; + mema = 0x90000000; + + pcilog("Mask sizes: mem=%lux io=%lux\n", mema, ioa); + + pcibusmap(pciroot, &mema, &ioa, 1); + DBG("Sizes2: mem=%lux io=%lux\n", mema, ioa); + + unlock(&pcicfginitlock); + return; + } + + if (!nopcirouting) + pcirouting(); + +out: + unlock(&pcicfginitlock); + + if(getconf("*pcihinv")) + pcihinv(nil); +} + +static int +pcicfgrw8(int tbdf, int rno, int data, int read) +{ + int o, type, x; + + if(pcicfgmode == -1) + pcicfginit(); + + if(BUSBNO(tbdf)) + type = 0x01; + else + type = 0x00; + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + switch(pcicfgmode){ + + case 1: + o = rno & 0x03; + rno &= ~0x03; + outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type); + if(read) + x = inb(PciDATA+o); + else + outb(PciDATA+o, data); + outl(PciADDR, 0); + break; + + case 2: + outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1)); + outb(PciFORWARD, BUSBNO(tbdf)); + if(read) + x = inb((0xC000|(BUSDNO(tbdf)<<8)) + rno); + else + outb((0xC000|(BUSDNO(tbdf)<<8)) + rno, data); + outb(PciCSE, 0); + break; + } + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr8(Pcidev* pcidev, int rno) +{ + return pcicfgrw8(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw8(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw8(pcidev->tbdf, rno, data, 0); +} + +static int +pcicfgrw16(int tbdf, int rno, int data, int read) +{ + int o, type, x; + + if(pcicfgmode == -1) + pcicfginit(); + + if(BUSBNO(tbdf)) + type = 0x01; + else + type = 0x00; + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + switch(pcicfgmode){ + + case 1: + o = rno & 0x02; + rno &= ~0x03; + outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type); + if(read) + x = ins(PciDATA+o); + else + outs(PciDATA+o, data); + outl(PciADDR, 0); + break; + + case 2: + outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1)); + outb(PciFORWARD, BUSBNO(tbdf)); + if(read) + x = ins((0xC000|(BUSDNO(tbdf)<<8)) + rno); + else + outs((0xC000|(BUSDNO(tbdf)<<8)) + rno, data); + outb(PciCSE, 0); + break; + } + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr16(Pcidev* pcidev, int rno) +{ + return pcicfgrw16(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw16(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw16(pcidev->tbdf, rno, data, 0); +} + +static int +pcicfgrw32(int tbdf, int rno, int data, int read) +{ + int type, x; + + if(pcicfgmode == -1) + pcicfginit(); + + if(BUSBNO(tbdf)) + type = 0x01; + else + type = 0x00; + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + switch(pcicfgmode){ + + case 1: + rno &= ~0x03; + outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type); + if(read) + x = inl(PciDATA); + else + outl(PciDATA, data); + outl(PciADDR, 0); + break; + + case 2: + outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1)); + outb(PciFORWARD, BUSBNO(tbdf)); + if(read) + x = inl((0xC000|(BUSDNO(tbdf)<<8)) + rno); + else + outl((0xC000|(BUSDNO(tbdf)<<8)) + rno, data); + outb(PciCSE, 0); + break; + } + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr32(Pcidev* pcidev, int rno) +{ + return pcicfgrw32(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw32(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw32(pcidev->tbdf, rno, data, 0); +} + +Pcidev* +pcimatch(Pcidev* prev, int vid, int did) +{ + if(pcicfgmode == -1) + pcicfginit(); + + if(prev == nil) + prev = pcilist; + else + prev = prev->list; + + while(prev != nil){ + if((vid == 0 || prev->vid == vid) + && (did == 0 || prev->did == did)) + break; + prev = prev->list; + } + return prev; +} + +Pcidev* +pcimatchtbdf(int tbdf) +{ + Pcidev *pcidev; + + if(pcicfgmode == -1) + pcicfginit(); + + for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) { + if(pcidev->tbdf == tbdf) + break; + } + return pcidev; +} + +uchar +pciipin(Pcidev *pci, uchar pin) +{ + if (pci == nil) + pci = pcilist; + + while (pci) { + uchar intl; + + if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff) + return pci->intl; + + if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0) + return intl; + + pci = pci->list; + } + return 0; +} + +static void +pcilhinv(Pcidev* p) +{ + int i; + Pcidev *t; + + if(p == nil) { + putstrn(PCICONS.output, PCICONS.ptr); + p = pciroot; + print("bus dev type vid did intl memory\n"); + } + for(t = p; t != nil; t = t->link) { + print("%d %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d ", + BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf), + t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl); + + for(i = 0; i < nelem(p->mem); i++) { + if(t->mem[i].size == 0) + continue; + print("%d:%.8lux %d ", i, + t->mem[i].bar, t->mem[i].size); + } + if(t->ioa.bar || t->ioa.size) + print("ioa:%.8lux %d ", t->ioa.bar, t->ioa.size); + if(t->mema.bar || t->mema.size) + print("mema:%.8lux %d ", t->mema.bar, t->mema.size); + if(t->bridge) + print("->%d", BUSBNO(t->bridge->tbdf)); + print("\n"); + } + while(p != nil) { + if(p->bridge != nil) + pcilhinv(p->bridge); + p = p->link; + } +} + +void +pcihinv(Pcidev* p) +{ + if(pcicfgmode == -1) + pcicfginit(); + lock(&pcicfginitlock); + pcilhinv(p); + unlock(&pcicfginitlock); +} + +void +pcireset(void) +{ + Pcidev *p; + + if(pcicfgmode == -1) + pcicfginit(); + + for(p = pcilist; p != nil; p = p->list) { + /* don't mess with the bridges */ + if(p->ccrb == 0x06) + continue; + pciclrbme(p); + } +} + +void +pcisetioe(Pcidev* p) +{ + p->pcr |= IOen; + pcicfgw16(p, PciPCR, p->pcr); +} + +void +pciclrioe(Pcidev* p) +{ + p->pcr &= ~IOen; + pcicfgw16(p, PciPCR, p->pcr); +} + +void +pcisetbme(Pcidev* p) +{ + p->pcr |= MASen; + pcicfgw16(p, PciPCR, p->pcr); +} + +void +pciclrbme(Pcidev* p) +{ + p->pcr &= ~MASen; + pcicfgw16(p, PciPCR, p->pcr); +} + +void +pcisetmwi(Pcidev* p) +{ + p->pcr |= MemWrInv; + pcicfgw16(p, PciPCR, p->pcr); +} + +void +pciclrmwi(Pcidev* p) +{ + p->pcr &= ~MemWrInv; + pcicfgw16(p, PciPCR, p->pcr); +} + +static int +pcigetpmrb(Pcidev* p) +{ + int ptr; + + if(p->pmrb != 0) + return p->pmrb; + p->pmrb = -1; + + /* + * If there are no extended capabilities implemented, + * (bit 4 in the status register) assume there's no standard + * power management method. + * Find the capabilities pointer based on PCI header type. + */ + if(!(p->pcr & 0x0010)) + return -1; + switch(pcicfgr8(p, PciHDT)){ + default: + return -1; + case 0: /* all other */ + case 1: /* PCI to PCI bridge */ + ptr = 0x34; + break; + case 2: /* CardBus bridge */ + ptr = 0x14; + break; + } + ptr = pcicfgr32(p, ptr); + + while(ptr != 0){ + /* + * Check for validity. + * Can't be in standard header and must be double + * word aligned. + */ + if(ptr < 0x40 || (ptr & ~0xFC)) + return -1; + if(pcicfgr8(p, ptr) == 0x01){ + p->pmrb = ptr; + return ptr; + } + + ptr = pcicfgr8(p, ptr+1); + } + + return -1; +} + +int +pcigetpms(Pcidev* p) +{ + int pmcsr, ptr; + + if((ptr = pcigetpmrb(p)) == -1) + return -1; + + /* + * Power Management Register Block: + * offset 0: Capability ID + * 1: next item pointer + * 2: capabilities + * 4: control/status + * 6: bridge support extensions + * 7: data + */ + pmcsr = pcicfgr16(p, ptr+4); + + return pmcsr & 0x0003; +} + +int +pcisetpms(Pcidev* p, int state) +{ + int ostate, pmc, pmcsr, ptr; + + if((ptr = pcigetpmrb(p)) == -1) + return -1; + + pmc = pcicfgr16(p, ptr+2); + pmcsr = pcicfgr16(p, ptr+4); + ostate = pmcsr & 0x0003; + pmcsr &= ~0x0003; + + switch(state){ + default: + return -1; + case 0: + break; + case 1: + if(!(pmc & 0x0200)) + return -1; + break; + case 2: + if(!(pmc & 0x0400)) + return -1; + break; + case 3: + break; + } + pmcsr |= state; + pcicfgw16(p, ptr+4, pmcsr); + + return ostate; +} diff --git a/os/pc/pcidb.acid b/os/pc/pcidb.acid new file mode 100644 index 00000000..0ab443d5 --- /dev/null +++ b/os/pc/pcidb.acid @@ -0,0 +1,2848 @@ +pcivendordb = { + {0x0000, "Gammagraphx, Inc.", { + }}, + {0x001a, "Ascend Communications, Inc.", { + }}, + {0x003d, "Lockheed Martin-Marietta Corp", { + }}, + {0x0e11, "Compaq Computer Corporation", { + {0x3032,"QVision 1280/p Rev 0"}, + {0x3033,"QVision 1280/p Rev 1"}, + {0x3034,"QVision 1280/p Rev 2"}, + {0x4000,"4000 [Triflex]"}, + {0xae10,"Smart-2/P RAID Controller"}, + {0xae29,"MIS-L"}, + {0xae2a,"MPC"}, + {0xae2b,"MIS-E"}, + {0xae32,"Netelligent 10/100"}, + {0xae34,"Netelligent 10"}, + {0xae35,"Integrated NetFlex-3/P"}, + {0xae40,"Netelligent 10/100 Dual"}, + {0xae43,"ProLiant Integrated Netelligent 10/100"}, + {0xae69,"CETUS-L"}, + {0xae6c,"Northstar"}, + {0xb011,"Integrated Netelligent 10/100"}, + {0xb012,"Netelligent 10 T/2"}, + {0xf130,"NetFlex-3/P ThunderLAN 1.0"}, + {0xf150,"NetFlex-3/P ThunderLAN 2.3"}, + }}, + {0x1000, "Symbios Logic Inc. (formerly NCR)", { + {0x0001,"53c810"}, + {0x0002,"53c820"}, + {0x0003,"53c825"}, + {0x0004,"53c815"}, + {0x0005,"53c810AP"}, + {0x0006,"53c860"}, + {0x000b,"53c896"}, + {0x000c,"53c895"}, + {0x000d,"53c885"}, + {0x000f,"53c875"}, + {0x008f,"53c875J"}, + {0x0901,"61C102"}, + {0x1000,"63C815"}, + }}, + {0x1002, "ATI Technologies Inc", { + {0x4158,"68800AX [Mach32]"}, + {0x4354,"215CT [Mach64 CT]"}, + {0x4358,"210888CX [Mach64 CX]"}, + {0x4554,"210888ET [Mach64 ET]"}, + {0x4742,"215GB [Mach64 GB]"}, + {0x4744,"215GD [Mach64 GD]"}, + {0x4749,"215GI [Mach64 GI]"}, + {0x4750,"215GP [Mach64 GP]"}, + {0x4751,"215GQ [Mach64 GQ]"}, + {0x4754,"215GT [Mach64 GT]"}, + {0x4755,"215GTB [Mach64 GTB]"}, + {0x4756,"215IIC [Mach64 GT IIC]"}, + {0x4758,"210888GX [Mach64 GX]"}, + {0x4c47,"215LG [Mach64 LG]"}, + {0x4c54,"264LT [Mach64 LT]"}, + {0x5654,"264VT [Mach64 VT]"}, + {0x5655,"264VTB [Mach64 VTB]"}, + {0x5656,"264VT4 [Mach64 VT4]"}, + }}, + {0x1003, "ULSI Systems", { + {0x0201,"US201"}, + }}, + {0x1004, "VLSI Technology Inc", { + {0x0005,"82C592-FC1"}, + {0x0006,"82C593-FC1"}, + {0x0007,"82C594-AFC2"}, + {0x0008,"82C596/7 [Wildcat]"}, + {0x0009,"82C597-AFC2"}, + {0x000c,"82C541 [Lynx]"}, + {0x000d,"82C543 [Lynx]"}, + {0x0101,"82C532"}, + {0x0102,"82C534"}, + {0x0103,"82C538"}, + {0x0104,"82C535"}, + {0x0105,"82C147"}, + {0x0200,"82C975"}, + {0x0280,"82C925"}, + {0x0702,"VAS96011 [Golden Gate II]"}, + }}, + {0x1005, "Avance Logic, Inc.", { + {0x2301,"ALG2301"}, + {0x2302,"ALG2302"}, + {0x2364,"ALG2364"}, + }}, + {0x1006, "Reply Group", { + }}, + {0x1007, "NetFrame Systems Inc", { + }}, + {0x1008, "Epson", { + }}, + {0x100a, "Phoenix Technologies", { + }}, + {0x100b, "National Semiconductor Corporation", { + {0x0001,"DP83810"}, + {0x0002,"87415"}, + {0xd001,"87410"}, + }}, + {0x100c, "Tseng Labs Inc", { + {0x3202,"ET4000/W32p rev A"}, + {0x3205,"ET4000/W32p rev B"}, + {0x3206,"ET4000/W32p rev C"}, + {0x3207,"ET4000/W32p rev D"}, + {0x3208,"ET6000"}, + }}, + {0x100d, "AST Research Inc", { + }}, + {0x100e, "Weitek", { + {0x9001,"P9000"}, + {0x9100,"P9100"}, + }}, + {0x1010, "Video Logic, Ltd.", { + }}, + {0x1011, "Digital Equipment Corporation", { + {0x0001,"DECchip 21050"}, + {0x0002,"DECchip 21040 [Tulip]"}, + {0x0004,"DECchip 21030 [TGA]"}, + {0x0007,"NVRAM [Zephyr NVRAM]"}, + {0x0008,"KZPSA [KZPSA]"}, + {0x0009,"DECchip 21140 [FasterNet]"}, + {0x000d,"PBXGB [TGA2]"}, + {0x000f,"DEFPA"}, + {0x0014,"DECchip 21041 [Tulip Pass 3]"}, + {0x0016,"DGLPB [OPPO]"}, + {0x0019,"DECchip 21142/43"}, + {0x0021,"DECchip 21052"}, + {0x0022,"DECchip 21150"}, + {0x0024,"DECchip 21152"}, + }}, + {0x1012, "Micronics Computers Inc", { + }}, + {0x1013, "Cirrus Logic", { + {0x0038,"GD 7548"}, + {0x00a0,"GD 5430/40 [Alpine]"}, + {0x00a4,"GD 5434-4 [Alpine]"}, + {0x00a8,"GD 5434-8 [Alpine]"}, + {0x00ac,"GD 5436 [Alpine]"}, + {0x00b8,"GD 5446"}, + {0x00bc,"GD 5480"}, + {0x00d0,"GD 5462"}, + {0x00d4,"GD 5464 [Laguna]"}, + {0x00d6,"GD 5465 [Laguna]"}, + {0x1100,"CL 6729"}, + {0x1110,"PD 6832"}, + {0x1200,"GD 7542 [Nordic]"}, + {0x1202,"GD 7543 [Viking]"}, + {0x1204,"GD 7541 [Nordic Light]"}, + }}, + {0x1014, "IBM", { + {0x000a,"Fire Coral"}, + {0x0018,"TR"}, + {0x001b,"GXT-150P"}, + {0x001d,"82G2675"}, + {0x0020,"MCA"}, + {0x0022,"IBM27-82351"}, + {0x002e,"ServeRAID controller"}, + {0x0036,"Miami"}, + {0x003e,"TR_Wake"}, + {0x0046,"MPIC interrupt controller"}, + {0x007d,"3780IDSP [MWave]"}, + {0xffff,"MPIC-2 interrupt controller"}, + }}, + {0x1015, "LSI Logic Corp of Canada", { + }}, + {0x1016, "ICL Personal Systems", { + }}, + {0x1017, "SPEA Software AG", { + }}, + {0x1018, "Unisys Systems", { + }}, + {0x1019, "Elitegroup Computer Systems", { + }}, + {0x101a, "AT&T GIS (NCR)", { + }}, + {0x101b, "Vitesse Semiconductor", { + }}, + {0x101c, "Western Digital", { + {0x0193,"33C193A"}, + {0x0197,"33C197A"}, + {0x0296,"33C296A"}, + {0x3193,"7193"}, + {0x3197,"WD 7197"}, + {0x3296,"33C296A"}, + {0x4296,"34C296"}, + {0xc24a,"90C"}, + }}, + {0x101e, "American Megatrends Inc.", { + {0x9010,"MegaRAID"}, + }}, + {0x101f, "PictureTel", { + }}, + {0x1020, "Hitachi Computer Products", { + }}, + {0x1021, "OKI Electric Industry Co. Ltd.", { + }}, + {0x1022, "Advanced Micro Devices", { + {0x2000,"79c970 [PCnet LANCE]"}, + {0x2020,"53c974 [PCscsi]"}, + {0x2040,"79c974"}, + }}, + {0x1023, "Trident Microsystems", { + {0x9320,"TGUI 9320"}, + {0x9397,"Cyber9397"}, + {0x9420,"TGUI 9420"}, + {0x9430,"TGUI 9430"}, + {0x9440,"TGUI 9440"}, + {0x9660,"TGUI 9660/9680/9682"}, + {0x9750,"3DIm`age 975"}, + }}, + {0x1024, "Zenith Data Systems", { + }}, + {0x1025, "Acer Incorporated", { + {0x1435,"M1435"}, + {0x1445,"M1445"}, + {0x1449,"M1449"}, + {0x1451,"M1451"}, + {0x1461,"M1461"}, + {0x3141,"M3141"}, + {0x3143,"M3143"}, + {0x3145,"M3145"}, + {0x3147,"M3147"}, + {0x3149,"M3149"}, + {0x3151,"M3151"}, + }}, + {0x1028, "Dell Computer Corporation", { + }}, + {0x1029, "Siemens Nixdorf IS", { + }}, + {0x102a, "LSI Logic", { + {0x0000,"HYDRA"}, + {0x0010,"ASPEN"}, + }}, + {0x102b, "Matrox Graphics, Inc.", { + {0x0518,"2085PX [Atlas MGA-2]"}, + {0x0519,"MGA 2064W [Millennium]"}, + {0x051a,"MGA 1064SG [Mystique]"}, + {0x051b,"MGA 2164W [Millennium II]"}, + {0x051f,"MGA 2164W AGP [Millennium II AGP]"}, + {0x0d10,"MGA Ultima/Impression"}, + }}, + {0x102c, "Chips and Technologies", { + {0x00b8,"64310"}, + {0x00d8,"65545"}, + {0x00dc,"65548"}, + {0x00e0,"65550"}, + {0x00e4,"65554"}, + {0x00e5,"65555"}, + }}, + {0x102d, "Wyse Technology Inc.", { + }}, + {0x102e, "Olivetti Advanced Technology", { + }}, + {0x102f, "Toshiba America", { + }}, + {0x1030, "TMC Research", { + }}, + {0x1031, "Miro Computer Products AG", { + {0x5601,"DC20 ASIC"}, + }}, + {0x1033, "NEC Corporation", { + {0x0035,"USB"}, + {0x0046,"PowerVR PCX2 [midas]"}, + }}, + {0x1034, "Framatome Connectors USA Inc.", { + }}, + {0x1035, "Comp. & Comm. Research Lab", { + }}, + {0x1036, "Future Domain Corp.", { + {0x0000,"TMC-18C30 [36C70]"}, + }}, + {0x1037, "Hitachi Micro Systems", { + }}, + {0x1038, "AMP, Inc", { + }}, + {0x1039, "Silicon Integrated Systems", { + {0x0001,"5591/5592 AGP"}, + {0x0002,"SG86C202"}, + {0x0008,"85C503"}, + {0x0009,"ACPI"}, + {0x0200,"5597/5598 VGA"}, + {0x0204,"82C204"}, + {0x0205,"SG86C205"}, + {0x0406,"85C501/2"}, + {0x0496,"85C496"}, + {0x0597,"5513C"}, + {0x0601,"85C601"}, + {0x5107,"5107"}, + {0x5511,"5511/5512"}, + {0x5513,"5513"}, + {0x5571,"5571"}, + {0x5591,"5591/5592 Host"}, + {0x5597,"5597 [SiS5582]"}, + {0x7001,"7001"}, + }}, + {0x103a, "Seiko Epson Corporation", { + }}, + {0x103b, "Tatung Co. of America", { + }}, + {0x103c, "Hewlett-Packard Company", { + {0x1030,"J2585A"}, + {0x1031,"J2585B"}, + {0x2910,"E2910A"}, + {0x2925,"E2925A"}, + }}, + {0x103e, "Solliday Engineering", { + }}, + {0x103f, "Synopsys/Logic Modeling Group", { + }}, + {0x1040, "Accelgraphics Inc.", { + }}, + {0x1041, "Computrend", { + }}, + {0x1042, "Micron", { + {0x1000,"FDC 37C665"}, + {0x1001,"37C922"}, + {0x3000,"Samurai_0"}, + {0x3010,"Samurai_1"}, + {0x3020,"Samurai_IDE"}, + }}, + {0x1043, "Asustek Computer, Inc.", { + }}, + {0x1044, "Distributed Processing Technology", { + {0xa400,"SmartCache/Raid III or IV"}, + }}, + {0x1045, "OPTi Inc.", { + {0xc178,"92C178"}, + {0xc557,"82C557 [Viper-M]"}, + {0xc558,"82C558 [Viper-M ISA+IDE]"}, + {0xc621,"82C621"}, + {0xc700,"82C700"}, + {0xc701,"82C701 [FireStar Plus]"}, + {0xc814,"82C814 [Firebridge 1]"}, + {0xc822,"82C822"}, + {0xc824,"82C824"}, + {0xd568,"82C825 [Firebridge 2]"}, + }}, + {0x1046, "IPC Corporation, Ltd.", { + }}, + {0x1047, "Genoa Systems Corp", { + }}, + {0x1048, "Elsa AG", { + }}, + {0x1049, "Fountain Technologies, Inc.", { + }}, + {0x104a, "SGS Thomson Microelectronics", { + {0x0008,"STG 2000X"}, + {0x0009,"STG 1764X"}, + }}, + {0x104b, "BusLogic", { + {0x0140,"BT-946C (old) [multimaster 01]"}, + {0x1040,"BT-946C (BA80C30) [MultiMaster 10]"}, + {0x8130,"Flashpoint LT"}, + }}, + {0x104c, "Texas Instruments", { + {0x3d04,"TVP4010 [Permedia]"}, + {0x3d07,"TVP4020 [Permedia 2]"}, + {0xa001,"TDC1570"}, + {0xa100,"TDC1561"}, + {0xac10,"PCI1050"}, + {0xac11,"PCI1053"}, + {0xac12,"PCI1130"}, + {0xac13,"PCI1031"}, + {0xac15,"PCI1131"}, + {0xac16,"PCI-1250"}, + {0xac17,"PCI-1220"}, + }}, + {0x104d, "Sony Corporation", { + }}, + {0x104e, "Oak Technology, Inc", { + {0x0107,"OTI107"}, + }}, + {0x104f, "Co-time Computer Ltd", { + }}, + {0x1050, "Winbond Electronics Corp", { + {0x0940,"89C940"}, + }}, + {0x1051, "Anigma, Inc.", { + }}, + {0x1053, "Young Micro Systems", { + }}, + {0x1054, "Hitachi, Ltd", { + }}, + {0x1055, "EFAR Microsystems", { + }}, + {0x1056, "ICL", { + }}, + {0x1057, "Motorola Computer Group", { + {0x0001,"MPC105 [Eagle]"}, + {0x0002,"MPC106 [Grackle]"}, + {0x4801,"Raven"}, + }}, + {0x1058, "Electronics & Telecommunications RSH", { + }}, + {0x1059, "Teknor Industrial Computers Inc", { + }}, + {0x105a, "Promise Technology, Inc.", { + {0x4d33,"20246"}, + {0x5300,"DC5300"}, + }}, + {0x105b, "Foxconn International, Inc.", { + }}, + {0x105c, "Wipro Infotech Limited", { + }}, + {0x105d, "Number 9 Computer Company", { + {0x2309,"Imagine 128"}, + {0x2339,"Imagine 128-II"}, + {0x493d,"Imagine 128 T2R [Ticket to Ride]"}, + }}, + {0x105e, "Vtech Computers Ltd", { + }}, + {0x105f, "Infotronic America Inc", { + }}, + {0x1060, "United Microelectronics", { + {0x0001,"UM82C881"}, + {0x0002,"UM82C886"}, + {0x0101,"UM8673F"}, + {0x0881,"UM8881"}, + {0x0886,"UM8886F"}, + {0x0891,"UM8891A"}, + {0x1001,"UM886A"}, + {0x673a,"UM8886BF"}, + {0x8710,"UM8710"}, + {0x886a,"UM8886A"}, + {0x8881,"UM8881F"}, + {0x8886,"UM8886F"}, + {0x888a,"UM8886A"}, + {0x8891,"UM8891A"}, + {0x9017,"UM9017F"}, + {0xe881,"UM8881N"}, + {0xe886,"UM8886N"}, + {0xe891,"UM8891N"}, + }}, + {0x1061, "I.I.T.", { + {0x0001,"AGX016"}, + {0x0002,"IIT3204/3501"}, + }}, + {0x1062, "Maspar Computer Corp", { + }}, + {0x1063, "Ocean Office Automation", { + }}, + {0x1064, "Alcatel CIT", { + }}, + {0x1065, "Texas Microsystems", { + }}, + {0x1066, "PicoPower Technology", { + {0x0000,"PT80C826"}, + {0x0001,"PT86C52x [Vesuvius]"}, + {0x0002,"PT80C524 [Nile]"}, + }}, + {0x1067, "Mitsubishi Electronics", { + }}, + {0x1068, "Diversified Technology", { + }}, + {0x1069, "Mylex Corporation", { + {0x0001,"DAC960P"}, + }}, + {0x106a, "Aten Research Inc", { + }}, + {0x106b, "Apple Computer Inc.", { + {0x0001,"Bandit PowerPC host bridge"}, + {0x0002,"Grand Central I/O"}, + {0x000e,"Hydra Mac I/O"}, + }}, + {0x106c, "Hyundai Electronics America", { + }}, + {0x106d, "Sequent Computer Systems", { + }}, + {0x106e, "DFI, Inc", { + }}, + {0x106f, "City Gate Development Ltd", { + }}, + {0x1070, "Daewoo Telecom Ltd", { + }}, + {0x1071, "Mitac", { + }}, + {0x1072, "GIT Co Ltd", { + }}, + {0x1073, "Yamaha Corporation", { + {0x0002,"YGV615 [RPA3 3D-Graphics Controller]"}, + }}, + {0x1074, "NexGen Microsystems", { + {0x4e78,"82c501"}, + }}, + {0x1075, "Advanced Integrations Research", { + }}, + {0x1076, "Chaintech Computer Co. Ltd", { + }}, + {0x1077, "Q Logic", { + {0x1020,"ISP1020"}, + {0x1022,"ISP1022"}, + }}, + {0x1078, "Cyrix Corporation", { + {0x0000,"5510 [Grappa]"}, + {0x0001,"PCI_Master"}, + {0x0002,"5520 [Cognac]"}, + {0x0100,"5530_Legacy [Kahlua]"}, + {0x0101,"5530_SMI [Kahlua]"}, + {0x0102,"5530_IDE [Kahlua]"}, + {0x0103,"5530_Audio [Kahlua]"}, + {0x0104,"5530_Video [Kahlua]"}, + }}, + {0x1079, "I-Bus", { + }}, + {0x107a, "NetWorth", { + }}, + {0x107b, "Gateway 2000", { + }}, + {0x107c, "Goldstar", { + }}, + {0x107d, "LeadTek Research Inc.", { + {0x0000,"P86C850"}, + }}, + {0x107e, "Interphase Corporation", { + }}, + {0x107f, "Data Technology Corporation", { + {0x0802,"SL82C105"}, + }}, + {0x1080, "Contaq Microsystems", { + {0x0600,"82C599"}, + {0xc693,"82c693"}, + }}, + {0x1081, "Supermac Technology", { + }}, + {0x1082, "EFA Corporation of America", { + }}, + {0x1083, "Forex Computer Corporation", { + {0x0001,"FR710"}, + }}, + {0x1084, "Parador", { + }}, + {0x1085, "Tulip Computers Int.B.V.", { + }}, + {0x1086, "J. Bond Computer Systems", { + }}, + {0x1087, "Cache Computer", { + }}, + {0x1088, "Microcomputer Systems (M) Son", { + }}, + {0x1089, "Data General Corporation", { + }}, + {0x108a, "Bit3 Computer Corp.", { + }}, + {0x108c, "Oakleigh Systems Inc.", { + }}, + {0x108d, "Olicom", { + {0x0001,"OC-3136/3137"}, + {0x0011,"OC-2315"}, + {0x0012,"OC-2325"}, + {0x0013,"OC-2183/2185"}, + {0x0014,"OC-2326"}, + {0x0021,"OC-6151/6152 [RapidFire ATM PCI 155]"}, + }}, + {0x108e, "Sun Microsystems Computer Corp.", { + {0x1000,"EBUS"}, + {0x1001,"Happy Meal"}, + {0x5000,"Advanced PCI Bridge"}, + {0x8000,"PCI Bus Module"}, + {0xa000,"Ultra IIi PCI"}, + }}, + {0x108f, "Systemsoft", { + }}, + {0x1090, "Encore Computer Corporation", { + }}, + {0x1091, "Intergraph Corporation", { + }}, + {0x1092, "Diamond Multimedia Systems", { + }}, + {0x1093, "National Instruments", { + {0xc801,"PCI_GPIB"}, + }}, + {0x1094, "First International Computers", { + }}, + {0x1095, "CMD Technology Inc", { + {0x0640,"PCI0640"}, + {0x0643,"PCI0643"}, + {0x0646,"PCI0646"}, + {0x0650,"PBC0650A"}, + {0x0670,"670"}, + }}, + {0x1096, "Alacron", { + }}, + {0x1097, "Appian Technology", { + }}, + {0x1098, "Quantum Designs (H.K.) Ltd", { + {0x0001,"QD-8500"}, + {0x0002,"QD-8580"}, + }}, + {0x1099, "Samsung Electronics Co., Ltd", { + }}, + {0x109a, "Packard Bell", { + }}, + {0x109b, "Gemlight Computer Ltd.", { + }}, + {0x109c, "Megachips Corporation", { + }}, + {0x109d, "Zida Technologies Ltd.", { + }}, + {0x109e, "Brooktree Corporation", { + {0x0350,"Bt848"}, + {0x0351,"Bt849A"}, + {0x8472,"Bt8472"}, + {0x8474,"Bt8474"}, + }}, + {0x109f, "Trigem Computer Inc.", { + }}, + {0x10a0, "Meidensha Corporation", { + }}, + {0x10a1, "Juko Electronics Ind. Co. Ltd", { + }}, + {0x10a2, "Quantum Corporation", { + }}, + {0x10a3, "Everex Systems Inc", { + }}, + {0x10a4, "Globe Manufacturing Sales", { + }}, + {0x10a5, "Racal Interlan", { + }}, + {0x10a6, "Informtech Industrial Ltd.", { + }}, + {0x10a7, "Benchmarq Microelectronics", { + }}, + {0x10a8, "Sierra Semiconductor", { + {0x0000,"STB Horizon 64"}, + }}, + {0x10a9, "Silicon Graphics", { + }}, + {0x10aa, "ACC Microelectronics", { + {0x0000,"ACCM 2188"}, + }}, + {0x10ab, "Digicom", { + }}, + {0x10ac, "Honeywell IAC", { + }}, + {0x10ad, "Symphony Labs", { + {0x0001,"W83769F"}, + {0x0103,"SL82c103"}, + {0x0105,"SL82c105"}, + {0x0565,"W83C553"}, + }}, + {0x10ae, "Cornerstone Technology", { + }}, + {0x10af, "Micro Computer Systems Inc", { + }}, + {0x10b0, "CardExpert Technology", { + }}, + {0x10b1, "Cabletron Systems Inc", { + }}, + {0x10b2, "Raytheon Company", { + }}, + {0x10b3, "Databook Inc", { + {0x3106,"DB87144"}, + {0xb106,"DB87144"}, + }}, + {0x10b4, "STB Systems Inc", { + }}, + {0x10b5, "PLX Technology, Inc.", { + {0x9036,"9036"}, + {0x9060,"9060"}, + {0x906e,"9060ES"}, + {0x9080,"9080"}, + }}, + {0x10b6, "Madge Networks", { + {0x0001,"Smart"}, + {0x0002,"Smart 16/4 BM Mk2 PCI Ringnode"}, + {0x1001,"Collage 155 Server"}, + }}, + {0x10b7, "3Com Corporation", { + {0x0001,"3c985 1000BaseSX"}, + {0x3390,"Token Link Velocity"}, + {0x5900,"3c590 10BaseT [Vortex]"}, + {0x5950,"3c595 100BaseTX [Vortex]"}, + {0x5951,"3c595 100BaseT4 [Vortex]"}, + {0x5952,"3c595 100Base-MII [Vortex]"}, + {0x9000,"3c900 10BaseT [Boomerang]"}, + {0x9001,"3c900 Combo [Boomerang]"}, + {0x9050,"3c905 100BaseTX [Boomerang]"}, + {0x9051,"3c905 100BaseT4"}, + {0x9055,"3c905B 100BaseTX [Cyclone]"}, + }}, + {0x10b8, "Standard Microsystems", { + {0x0005,"9432 TX"}, + {0x1000,"37c665"}, + {0x1001,"37C922"}, + }}, + {0x10b9, "Acer Laboratories Inc.", { + {0x1435,"M1435"}, + {0x1445,"M1445"}, + {0x1449,"M1449"}, + {0x1451,"M1451"}, + {0x1461,"M1461"}, + {0x1489,"M1489"}, + {0x1511,"M1511"}, + {0x1513,"M1513"}, + {0x1521,"M1521"}, + {0x1523,"M1523"}, + {0x1531,"M1531"}, + {0x1533,"M1533"}, + {0x1541,"M1541"}, + {0x1543,"M1543"}, + {0x3141,"M3141"}, + {0x3143,"M3143"}, + {0x3145,"M3145"}, + {0x3147,"M3147"}, + {0x3149,"M3149"}, + {0x3151,"M3151"}, + {0x3307,"M3307"}, + {0x5215,"M4803"}, + {0x5217,"m5217h"}, + {0x5219,"M5219"}, + {0x5229,"M5229"}, + {0x5235,"m5225"}, + {0x5237,"M5237"}, + {0x7101,"M7101"}, + }}, + {0x10ba, "Mitsubishi Electric Corp.", { + }}, + {0x10bb, "Dapha Electronics Corporation", { + }}, + {0x10bc, "Advanced Logic Research", { + }}, + {0x10bd, "Surecom Technology", { + {0x0e34,"NE-34PCI LAN"}, + }}, + {0x10be, "Tseng Labs International Co.", { + }}, + {0x10bf, "Most Inc", { + }}, + {0x10c0, "Boca Research Inc.", { + }}, + {0x10c1, "ICM Co., Ltd.", { + }}, + {0x10c2, "Auspex Systems Inc.", { + }}, + {0x10c3, "Samsung Semiconductors, Inc.", { + }}, + {0x10c4, "Award Software International Inc.", { + }}, + {0x10c5, "Xerox Corporation", { + }}, + {0x10c6, "Rambus Inc.", { + }}, + {0x10c7, "Media Vision", { + }}, + {0x10c8, "Neomagic Corporation", { + {0x0001,"NM2070 [MagicGraph NM2070]"}, + {0x0002,"NM2090 [MagicGraph 128V]"}, + {0x0003,"NM2093 [MagicGraph 128ZV]"}, + {0x0004,"NM2160 [MagicGraph 128XD]"}, + }}, + {0x10c9, "Dataexpert Corporation", { + }}, + {0x10ca, "Fujitsu Microelectr., Inc.", { + }}, + {0x10cb, "Omron Corporation", { + }}, + {0x10cc, "Mentor ARC Inc", { + }}, + {0x10cd, "Advanced System Products, Inc", { + {0x1200,"ASC1200 [(abp940) Fast SCSI-II]"}, + {0x1300,"ABP940-U"}, + {0x2300,"ABP940-UW"}, + }}, + {0x10ce, "Radius", { + }}, + {0x10cf, "Citicorp TTI", { + {0x2001,"mb86605"}, + }}, + {0x10d0, "Fujitsu Limited", { + }}, + {0x10d1, "FuturePlus Systems Corp.", { + }}, + {0x10d2, "Molex Incorporated", { + }}, + {0x10d3, "Jabil Circuit Inc", { + }}, + {0x10d4, "Hualon Microelectronics", { + }}, + {0x10d5, "Autologic Inc.", { + }}, + {0x10d6, "Cetia", { + }}, + {0x10d7, "BCM Advanced Research", { + }}, + {0x10d8, "Advanced Peripherals Labs", { + }}, + {0x10d9, "Macronix, Inc.", { + {0x0512,"MX98713"}, + {0x0531,"MX987x5"}, + }}, + {0x10da, "Compaq IPG-Austin", { + }}, + {0x10db, "Rohm LSI Systems, Inc.", { + }}, + {0x10dc, "CERN/ECP/EDU", { + {0x0001,"STAR/RD24 SCI-PCI (PMC)"}, + {0x0002,"TAR/RD24 SCI-PCI (PMC) [ATT 2C15-3 (FPGA) SCI bridge on PCI 5 Volt card]"}, + {0x0021,"HIPPI destination"}, + {0x0022,"HIPPI source"}, + }}, + {0x10dd, "Evans & Sutherland", { + }}, + {0x10de, "Nvidia Corporation", { + {0x0008,"NV1"}, + {0x0009,"DAC64"}, + }}, + {0x10df, "Emulex Corporation", { + }}, + {0x10e0, "Integrated Micro Solutions Inc.", { + {0x5026,"IMS5026/27/28"}, + {0x8849,"8849"}, + {0x9128,"IMS9129"}, + }}, + {0x10e1, "Tekram Technology Co.,Ltd.", { + {0x690c,"690c"}, + {0xdc29,"DC290"}, + }}, + {0x10e2, "Aptix Corporation", { + }}, + {0x10e3, "Tundra Semiconductor Corp.", { + {0x0000,"CA91C042 [Universe]"}, + {0x0860,"CA91C860 [QSpan]"}, + }}, + {0x10e4, "Tandem Computers", { + }}, + {0x10e5, "Micro Industries Corporation", { + }}, + {0x10e6, "Gainbery Computer Products Inc.", { + }}, + {0x10e7, "Vadem", { + }}, + {0x10e8, "Applied Micro Circuits Corporation", { + {0x5920,"S5920"}, + {0x8043,"LANai4.x [Myrinet LANai interface chip]"}, + {0x8062,"S5933_PARASTATION"}, + {0x807d,"S5933 [Matchmaker]"}, + {0x809c,"S5933_HEPC3"}, + }}, + {0x10e9, "Alps Electric Co., Ltd.", { + }}, + {0x10ea, "Intergraphics Systems", { + {0x1680,"IGA-1680"}, + {0x1682,"IGA-1682"}, + }}, + {0x10eb, "Artists Graphics", { + {0x0101,"3GA"}, + }}, + {0x10ec, "Realtek Semiconductor Co., Ltd.", { + {0x8029,"8029"}, + {0x8129,"8129"}, + {0x8139,"8139"}, + }}, + {0x10ed, "Ascii Corporation", { + {0x7310,"V7310"}, + }}, + {0x10ee, "Xilinx, Inc.", { + }}, + {0x10ef, "Racore Computer Products, Inc.", { + }}, + {0x10f0, "Peritek Corporation", { + }}, + {0x10f1, "Tyan Computer", { + }}, + {0x10f2, "Achme Computer, Inc.", { + }}, + {0x10f3, "Alaris, Inc.", { + }}, + {0x10f4, "S-MOS Systems, Inc.", { + }}, + {0x10f5, "NKK Corporation", { + {0xa001,"NDR4000 [NR4600 Bridge]"}, + }}, + {0x10f6, "Creative Electronic Systems SA", { + }}, + {0x10f7, "Matsushita Electric Industrial Co., Ltd.", { + }}, + {0x10f8, "Altos India Ltd", { + }}, + {0x10f9, "PC Direct", { + }}, + {0x10fa, "Truevision", { + {0x000c,"TARGA 1000"}, + }}, + {0x10fb, "Thesys Gesellschaft f€r Mikroelektronik mbH", { + }}, + {0x10fc, "I-O Data Device, Inc.", { + }}, + {0x10fd, "Soyo Computer, Inc", { + }}, + {0x10fe, "Fast Multimedia AG", { + }}, + {0x10ff, "NCube", { + }}, + {0x1100, "Jazz Multimedia", { + }}, + {0x1101, "Initio Corporation", { + {0x9100,"320 P"}, + {0x9500,"360P"}, + }}, + {0x1102, "Creative Labs", { + }}, + {0x1103, "Triones Technologies, Inc.", { + }}, + {0x1104, "RasterOps Corp.", { + }}, + {0x1105, "Sigma Designs, Inc.", { + }}, + {0x1106, "VIA Technologies, Inc.", { + {0x0505,"VT 82C505"}, + {0x0561,"VT 82C561"}, + {0x0571,"VT82C586 IDE [Apollo]"}, + {0x0576,"VT 82C576 3V [Apollo Master]"}, + {0x0585,"VT82C585VP [Apollo VP1/VPX]"}, + {0x0586,"VT82C586 ISA [Apollo VP]"}, + {0x0595,"VT82C595 [Apollo VP2]"}, + {0x0597,"VT82C597 [Apollo VP3]"}, + {0x0926,"VT82C926 [Amazon]"}, + {0x1000,"82C570MV"}, + {0x1106,"82C570MV"}, + {0x1571,"VT 82C416MV"}, + {0x1595,"VT82C595/97 [Apollo VP2/97]"}, + {0x3038,"VT82C586B USB"}, + {0x3040,"VT82C586B ACPI"}, + {0x6100,"VT85C100A [Rhine II]"}, + {0x8597,"VT82C597 [Apollo VP3 AGP]"}, + }}, + {0x1107, "Stratus Computers", { + }}, + {0x1108, "Proteon, Inc.", { + {0x0100,"p1690plus_AA"}, + {0x0101,"p1690plus_AB"}, + }}, + {0x1109, "Cogent Data Technologies, Inc.", { + {0x1400,"EM110TX [EX110TX PCI Fast Ethernet Adapter]"}, + }}, + {0x110a, "Siemens Nixdorf AG", { + {0x6120,"SZB6120"}, + }}, + {0x110b, "Chromatic Research Inc.", { + }}, + {0x110c, "Mini-Max Technology, Inc.", { + }}, + {0x110d, "Znyx Advanced Systems", { + }}, + {0x110e, "CPU Technology", { + }}, + {0x110f, "Ross Technology", { + }}, + {0x1110, "Powerhouse Systems", { + }}, + {0x1111, "Santa Cruz Operation", { + }}, + {0x1112, "RNS - Div. of Meret Communications Inc", { + }}, + {0x1113, "Accton Technology Corporation", { + }}, + {0x1114, "Atmel Corporation", { + }}, + {0x1115, "3D Labs", { + }}, + {0x1116, "Data Translation", { + }}, + {0x1117, "Datacube, Inc", { + }}, + {0x1118, "Berg Electronics", { + }}, + {0x1119, "ICP Vortex Computersysteme GmbH", { + {0x0000,"GDT6000/6020/6050"}, + {0x0001,"GDT6000b/6010"}, + {0x0002,"GDT6110/6510"}, + {0x0003,"GDT6120/6520"}, + {0x0004,"GDT6530"}, + {0x0005,"GDT6550"}, + {0x0006,"GDT6x17"}, + {0x0007,"GDT6x27"}, + {0x0008,"GDT6537"}, + {0x0009,"GDT5557"}, + {0x000a,"GDT6x15"}, + {0x000b,"GDT6x25"}, + {0x000c,"GDT6535"}, + {0x000d,"GDT6555"}, + {0x0100,"GDT 6117RP/6517RP"}, + {0x0101,"GDT 6127RP/6527RP"}, + {0x0102,"GDT 6537RP"}, + {0x0103,"GDT 6557RP"}, + {0x0104,"GDT 6111RP/6511RP"}, + {0x0105,"GDT 6121RP/6521RP"}, + {0x0110,"GDT 6117RP1/6517RP1"}, + {0x0111,"GDT 6127RP1/6527RP1"}, + {0x0112,"GDT 6537RP1"}, + {0x0113,"GDT 6557RP1"}, + {0x0114,"GDT 6111RP1/6511RP1"}, + {0x0115,"GDT 6121RP1/6521RP1"}, + {0x0120,"GDT 6117RP2/6517RP2"}, + {0x0121,"GDT 6127RP2/6527RP2"}, + {0x0122,"GDT 6537RP2"}, + {0x0123,"GDT 6557RP2"}, + {0x0124,"GDT 6111RP2/6511RP2"}, + {0x0125,"GDT 6121RP2/6521RP2"}, + }}, + {0x111a, "Efficient Networks, Inc", { + {0x0000,"155P-MF1 (FPGA)"}, + {0x0002,"155P-MF1 (ASIC)"}, + }}, + {0x111b, "Teledyne Electronic Systems", { + }}, + {0x111c, "Tricord Systems Inc.", { + }}, + {0x111d, "Integrated Device Tech", { + }}, + {0x111e, "Eldec", { + }}, + {0x111f, "Precision Digital Images", { + }}, + {0x1120, "EMC Corporation", { + }}, + {0x1121, "Zilog", { + }}, + {0x1122, "Multi-tech Systems, Inc.", { + }}, + {0x1123, "Excellent Design, Inc.", { + }}, + {0x1124, "Leutron Vision AG", { + }}, + {0x1125, "Eurocore", { + }}, + {0x1127, "FORE Systems Inc", { + {0x0210,"PCA-200PC"}, + {0x0300,"PCA-200E"}, + }}, + {0x1129, "Firmworks", { + }}, + {0x112a, "Hermes Electronics Company, Ltd.", { + }}, + {0x112b, "Linotype - Hell AG", { + }}, + {0x112c, "Zenith Data Systems", { + }}, + {0x112d, "Ravicad", { + }}, + {0x112e, "Infomedia Microelectronics Inc.", { + }}, + {0x112f, "Imaging Technology Inc", { + {0x0000,"MVC IC-PCI"}, + }}, + {0x1130, "Computervision", { + }}, + {0x1131, "Philips Semiconductors", { + {0x7145,"SAA7145"}, + {0x7146,"SAA7146"}, + }}, + {0x1132, "Mitel Corp.", { + }}, + {0x1133, "Eicon Technology Corporation", { + {0xe001,"DIVA20PRO"}, + {0xe002,"DIVA20"}, + {0xe003,"DIVA20PRO_U"}, + {0xe004,"DIVA20_U"}, + }}, + {0x1134, "Mercury Computer Systems", { + }}, + {0x1135, "Fuji Xerox Co Ltd", { + }}, + {0x1136, "Momentum Data Systems", { + }}, + {0x1137, "Cisco Systems Inc", { + }}, + {0x1138, "Ziatech Corporation", { + {0x8905,"8905 [STD 32 Bridge]"}, + }}, + {0x1139, "Dynamic Pictures, Inc", { + }}, + {0x113a, "FWB Inc", { + }}, + {0x113b, "Network Computing Devices", { + }}, + {0x113c, "Cyclone Microsystems, Inc.", { + {0x0001,"PCI-SDK [PCI i960 Evaluation Platform]"}, + {0x0911,"PCI-911 [PCI-based i960Jx Intelligent I/O Controller]"}, + {0x0912,"PCI-912 [i960CF-based Intelligent I/O Controller]"}, + {0x0913,"PCI-913"}, + }}, + {0x113d, "Leading Edge Products Inc", { + }}, + {0x113e, "Sanyo Electric Co - Computer Engineering Dept", { + }}, + {0x113f, "Equinox Systems, Inc.", { + }}, + {0x1140, "Intervoice Inc", { + }}, + {0x1141, "Crest Microsystem Inc", { + }}, + {0x1142, "Alliance Semiconductor Corporation", { + {0x3210,"AP6410"}, + {0x6422,"AP6422"}, + {0x6424,"AT24"}, + {0x643d,"AT3D"}, + }}, + {0x1143, "NetPower, Inc", { + }}, + {0x1144, "Cincinnati Milacron", { + }}, + {0x1145, "Workbit Corporation", { + }}, + {0x1146, "Force Computers", { + }}, + {0x1147, "Interface Corp", { + }}, + {0x1148, "Schneider & Koch", { + }}, + {0x1149, "Win System Corporation", { + }}, + {0x114a, "VMIC", { + {0x7587,"VMIVME-7587"}, + }}, + {0x114b, "Canopus Co., Ltd", { + }}, + {0x114c, "Annabooks", { + }}, + {0x114d, "IC Corporation", { + }}, + {0x114e, "Nikon Systems Inc", { + }}, + {0x114f, "Digi International", { + {0x0002,"AccelePort EPC"}, + {0x0003,"RightSwitch SE-6"}, + {0x0004,"AccelePort Xem"}, + {0x0006,"AccelePort Xr,C/X"}, + {0x0009,"AccelePort Xr/J"}, + {0x000a,"AccelePort EPC/J"}, + {0x0027,"AccelePort Xr 920"}, + }}, + {0x1150, "Thinking Machines Corp", { + }}, + {0x1151, "JAE Electronics Inc.", { + }}, + {0x1152, "Megatek", { + }}, + {0x1153, "Land Win Electronic Corp", { + }}, + {0x1154, "Melco Inc", { + }}, + {0x1155, "Pine Technology Ltd", { + }}, + {0x1156, "Periscope Engineering", { + }}, + {0x1157, "Avsys Corporation", { + }}, + {0x1158, "Voarx R & D Inc", { + }}, + {0x1159, "Mutech Corp", { + {0x0001,"MV-1000"}, + }}, + {0x115a, "Harlequin Ltd", { + }}, + {0x115b, "Parallax Graphics", { + }}, + {0x115c, "Photron Ltd.", { + }}, + {0x115d, "Xircom", { + }}, + {0x115e, "Peer Protocols Inc", { + }}, + {0x115f, "Maxtor Corporation", { + }}, + {0x1160, "Megasoft Inc", { + }}, + {0x1161, "PFU Limited", { + }}, + {0x1162, "OA Laboratory Co Ltd", { + }}, + {0x1163, "Rendition", { + {0x0001,"Verite 1000 PCI"}, + {0x2000,"Verite V2100"}, + }}, + {0x1164, "Advanced Peripherals Technologies", { + }}, + {0x1165, "Imagraph Corporation", { + }}, + {0x1166, "Pequr Technology", { + }}, + {0x1167, "Mutoh Industries Inc", { + }}, + {0x1168, "Thine Electronics Inc", { + }}, + {0x1169, "Centre for Development of Advanced Computing", { + }}, + {0x116a, "Polaris Communications", { + }}, + {0x116b, "Connectware Inc", { + }}, + {0x116c, "Intelligent Resources Integrated Systems", { + }}, + {0x116d, "Martin-Marietta", { + }}, + {0x116e, "Electronics for Imaging", { + }}, + {0x116f, "Workstation Technology", { + }}, + {0x1170, "Inventec Corporation", { + }}, + {0x1171, "Loughborough Sound Images Plc", { + }}, + {0x1172, "Altera Corporation", { + }}, + {0x1173, "Adobe Systems, Inc", { + }}, + {0x1174, "Bridgeport Machines", { + }}, + {0x1175, "Mitron Computer Inc.", { + }}, + {0x1176, "SBE Incorporated", { + }}, + {0x1177, "Silicon Engineering", { + }}, + {0x1178, "Alfa, Inc.", { + }}, + {0x1179, "Toshiba America Info Systems", { + {0x0601,"601"}, + {0x060a,"ToPIC95"}, + {0x060f,"ToPIC97"}, + {0x0701,"Lucent DSP1645 [Mars]"}, + }}, + {0x117b, "L G Electronics, Inc.", { + }}, + {0x117c, "Atto Technology", { + }}, + {0x117d, "Becton & Dickinson", { + }}, + {0x117e, "T/R Systems", { + }}, + {0x117f, "Integrated Circuit Systems", { + }}, + {0x1180, "Ricoh Co Ltd", { + {0x0466,"RL5C466"}, + }}, + {0x1181, "Telmatics International", { + }}, + {0x1183, "Fujikura Ltd", { + }}, + {0x1184, "Forks Inc", { + }}, + {0x1185, "Dataworld International Ltd", { + }}, + {0x1186, "D-Link System Inc", { + }}, + {0x1187, "Advanced Technology Laboratories, Inc.", { + }}, + {0x1188, "Shima Seiki Manufacturing Ltd.", { + }}, + {0x1189, "Matsushita Electronics Co Ltd", { + }}, + {0x118a, "Hilevel Technology", { + }}, + {0x118b, "Hypertec Pty Limited", { + }}, + {0x118c, "Corollary, Inc", { + {0x0014,"PCIB [C-bus II to PCI bus host bridge chip]"}, + }}, + {0x118d, "BitFlow Inc", { + {0x0001,"n/a [Raptor-PCI framegrabber]"}, + }}, + {0x118e, "Hermstedt GmbH", { + }}, + {0x118f, "Green Logic", { + }}, + {0x1191, "Artop Electronic Corp", { + {0x0004,"ATP8400"}, + {0x0005,"ATP850UF"}, + }}, + {0x1192, "Densan Company Ltd", { + }}, + {0x1193, "Zeitnet Inc.", { + {0x0001,"1221"}, + {0x0002,"1225"}, + }}, + {0x1194, "Toucan Technology", { + }}, + {0x1195, "Ratoc System Inc", { + }}, + {0x1196, "Hytec Electronics Ltd", { + }}, + {0x1197, "Gage Applied Sciences, Inc.", { + }}, + {0x1198, "Lambda Systems Inc", { + }}, + {0x1199, "Attachmate Corporation", { + }}, + {0x119a, "Mind Share, Inc.", { + }}, + {0x119b, "Omega Micro Inc.", { + {0x1221,"82C092G"}, + }}, + {0x119c, "Information Technology Inst.", { + }}, + {0x119d, "Bug, Inc. Sapporo Japan", { + }}, + {0x119e, "Fujitsu Microelectronics Ltd.", { + }}, + {0x119f, "Bull HN Information Systems", { + }}, + {0x11a0, "Convex Computer Corporation", { + }}, + {0x11a1, "Hamamatsu Photonics K.K.", { + }}, + {0x11a2, "Sierra Research and Technology", { + }}, + {0x11a3, "Deuretzbacher GmbH & Co. Eng. KG", { + }}, + {0x11a4, "Barco Graphics NV", { + }}, + {0x11a5, "Microunity Systems Eng. Inc", { + }}, + {0x11a6, "Pure Data Ltd.", { + }}, + {0x11a7, "Power Computing Corp.", { + }}, + {0x11a8, "Systech Corp.", { + }}, + {0x11a9, "InnoSys Inc.", { + }}, + {0x11aa, "Actel", { + }}, + {0x11ab, "Galileo Technology Ltd.", { + {0x0146,"GT-64010"}, + {0x4801,"GT-48001"}, + }}, + {0x11ac, "Canon Information Systems Research Aust.", { + }}, + {0x11ad, "Lite-On Communications Inc", { + {0x0002,"LNE100TX"}, + }}, + {0x11ae, "Aztech System Ltd", { + }}, + {0x11af, "Avid Technology Inc.", { + }}, + {0x11b0, "V3 Semiconductor Inc.", { + {0x0292,"V292PBC [Am29030/40 Bridge]"}, + {0x0960,"V96xPBC"}, + {0xc960,"V96DPC"}, + }}, + {0x11b1, "Apricot Computers", { + }}, + {0x11b2, "Eastman Kodak", { + }}, + {0x11b3, "Barr Systems Inc.", { + }}, + {0x11b4, "Leitch Technology International", { + }}, + {0x11b5, "Radstone Technology Plc", { + }}, + {0x11b6, "United Video Corp", { + }}, + {0x11b8, "XPoint Technologies, Inc", { + }}, + {0x11b9, "Pathlight Technology Inc.", { + }}, + {0x11ba, "Videotron Corp", { + }}, + {0x11bb, "Pyramid Technology", { + }}, + {0x11bc, "Network Peripherals Inc", { + {0x0001,"NP-PCI"}, + }}, + {0x11bd, "Pinnacle Systems Inc.", { + }}, + {0x11be, "International Microcircuits Inc", { + }}, + {0x11bf, "Astrodesign, Inc.", { + }}, + {0x11c0, "Hewlett Packard", { + }}, + {0x11c1, "Lucent Microelectronics", { + {0x0440,"L56xMF"}, + }}, + {0x11c2, "Sand Microelectronics", { + }}, + {0x11c4, "Document Technologies, Inc", { + }}, + {0x11c5, "Shiva Corporation", { + }}, + {0x11c6, "Dainippon Screen Mfg. Co. Ltd", { + }}, + {0x11c7, "D.C.M. Data Systems", { + }}, + {0x11c8, "Dolphin Interconnect Solutions AS", { + {0x0658,"PSB"}, + }}, + {0x11c9, "Magma", { + }}, + {0x11ca, "LSI Systems, Inc", { + }}, + {0x11cb, "Specialix Research Ltd.", { + {0x2000,"PCI_9050"}, + {0x4000,"SUPI_1"}, + {0x8000,"T225"}, + }}, + {0x11cc, "Michels & Kleberhoff Computer GmbH", { + }}, + {0x11cd, "HAL Computer Systems, Inc.", { + }}, + {0x11ce, "Netaccess", { + }}, + {0x11cf, "Pioneer Electronic Corporation", { + }}, + {0x11d0, "Lockheed Martin Federal Systems-Manassas", { + }}, + {0x11d1, "Auravision", { + {0x01f7,"VxP524"}, + }}, + {0x11d2, "Intercom Inc.", { + }}, + {0x11d3, "Trancell Systems Inc", { + }}, + {0x11d4, "Analog Devices", { + }}, + {0x11d5, "Ikon Corporation", { + {0x0115,"10115"}, + {0x0117,"10117"}, + }}, + {0x11d6, "Tekelec Telecom", { + }}, + {0x11d7, "Trenton Technology, Inc.", { + }}, + {0x11d8, "Image Technologies Development", { + }}, + {0x11d9, "TEC Corporation", { + }}, + {0x11da, "Novell", { + }}, + {0x11db, "Sega Enterprises Ltd", { + }}, + {0x11dc, "Questra Corporation", { + }}, + {0x11dd, "Crosfield Electronics Limited", { + }}, + {0x11de, "Zoran Corporation", { + {0x6057,"ZR36057"}, + {0x6120,"ZR36120"}, + }}, + {0x11df, "New Wave PDG", { + }}, + {0x11e0, "Cray Communications A/S", { + }}, + {0x11e1, "GEC Plessey Semi Inc.", { + }}, + {0x11e2, "Samsung Information Systems America", { + }}, + {0x11e3, "Quicklogic Corporation", { + }}, + {0x11e4, "Second Wave Inc", { + }}, + {0x11e5, "IIX Consulting", { + }}, + {0x11e6, "Mitsui-Zosen System Research", { + }}, + {0x11e7, "Toshiba America, Elec. Company", { + }}, + {0x11e8, "Digital Processing Systems Inc.", { + }}, + {0x11e9, "Highwater Designs Ltd.", { + }}, + {0x11ea, "Elsag Bailey", { + }}, + {0x11eb, "Formation Inc.", { + }}, + {0x11ec, "Coreco Inc", { + }}, + {0x11ed, "Mediamatics", { + }}, + {0x11ee, "Dome Imaging Systems Inc", { + }}, + {0x11ef, "Nicolet Technologies B.V.", { + }}, + {0x11f0, "Compu-Shack GmbH", { + }}, + {0x11f1, "Symbios Logic Inc", { + }}, + {0x11f2, "Picture Tel Japan K.K.", { + }}, + {0x11f3, "Keithley Metrabyte", { + }}, + {0x11f4, "Kinetic Systems Corporation", { + {0x2915,"CAMAC controller"}, + }}, + {0x11f5, "Computing Devices International", { + }}, + {0x11f6, "Compex", { + {0x0112,"ENet100VG4"}, + {0x1401,"ReadyLink 2000"}, + }}, + {0x11f7, "Scientific Atlanta", { + }}, + {0x11f8, "PMC-Sierra Inc.", { + {0x7375,"PM7375 [LASAR-155 ATM SAR]"}, + }}, + {0x11f9, "I-Cube Inc", { + }}, + {0x11fa, "Kasan Electronics Company, Ltd.", { + }}, + {0x11fb, "Datel Inc", { + }}, + {0x11fc, "Silicon Magic", { + }}, + {0x11fd, "High Street Consultants", { + }}, + {0x11fe, "Comtrol Corporation", { + {0x0001,"RocketPort 8 Oct"}, + {0x0002,"RocketPort 8 Intf"}, + {0x0003,"RocketPort 16 Intf"}, + {0x0004,"RocketPort 32 Intf"}, + }}, + {0x11ff, "Scion Corporation", { + }}, + {0x1200, "CSS Corporation", { + }}, + {0x1201, "Vista Controls Corp", { + }}, + {0x1202, "Network General Corp.", { + }}, + {0x1203, "Bayer Corporation, Agfa Division", { + }}, + {0x1204, "Lattice Semiconductor Corporation", { + }}, + {0x1205, "Array Corporation", { + }}, + {0x1206, "Amdahl Corporation", { + }}, + {0x1208, "Parsytec GmbH", { + }}, + {0x1209, "SCI Systems Inc", { + }}, + {0x120a, "Synaptel", { + }}, + {0x120b, "Adaptive Solutions", { + }}, + {0x120c, "Technical Corp.", { + }}, + {0x120d, "Compression Labs, Inc.", { + }}, + {0x120e, "Cyclades Corporation", { + {0x0100,"Cyclom_Y"}, + {0x0200,"Cyclom_Z"}, + }}, + {0x120f, "Essential Communications", { + {0x0001,"Roadrunner serial HIPPI"}, + }}, + {0x1210, "Hyperparallel Technologies", { + }}, + {0x1211, "Braintech Inc", { + }}, + {0x1212, "Kingston Technology Corp.", { + }}, + {0x1213, "Applied Intelligent Systems, Inc.", { + }}, + {0x1214, "Performance Technologies, Inc.", { + }}, + {0x1215, "Interware Co., Ltd", { + }}, + {0x1216, "Purup Prepress A/S", { + }}, + {0x1217, "O2 Micro, Inc.", { + {0x6729,"6729"}, + {0x673a,"6730"}, + {0x6832,"6832"}, + }}, + {0x1218, "Hybricon Corp.", { + }}, + {0x1219, "First Virtual Corporation", { + }}, + {0x121a, "3Dfx Interactive, Inc.", { + {0x0001,"Voodoo"}, + {0x0002,"Voodoo2"}, + }}, + {0x121b, "Advanced Telecommunications Modules", { + }}, + {0x121c, "Nippon Texaco., Ltd", { + }}, + {0x121d, "Lippert Automationstechnik GmbH", { + }}, + {0x121e, "CSPI", { + }}, + {0x121f, "Arcus Technology, Inc.", { + }}, + {0x1220, "Ariel Corporation", { + }}, + {0x1221, "Contec Co., Ltd", { + }}, + {0x1222, "Ancor Communications, Inc.", { + }}, + {0x1223, "Heurikon/Computer Products", { + }}, + {0x1224, "Interactive Images", { + }}, + {0x1225, "Power I/O, Inc.", { + }}, + {0x1227, "Tech-Source", { + }}, + {0x1228, "Norsk Elektro Optikk A/S", { + }}, + {0x1229, "Data Kinesis Inc.", { + }}, + {0x122a, "Integrated Telecom", { + }}, + {0x122b, "LG Industrial Systems Co., Ltd", { + }}, + {0x122c, "Sican GmbH", { + }}, + {0x122d, "Aztech System Ltd", { + }}, + {0x122e, "Xyratex", { + }}, + {0x122f, "Andrew Corporation", { + }}, + {0x1230, "Fishcamp Engineering", { + }}, + {0x1231, "Woodward McCoach, Inc.", { + }}, + {0x1232, "GPT Limited", { + }}, + {0x1233, "Bus-Tech, Inc.", { + }}, + {0x1234, "Technical Corp.", { + }}, + {0x1235, "Risq Modular Systems, Inc.", { + }}, + {0x1236, "Sigma Designs Corporation", { + {0x6401,"REALmagic 64/GX (SD 6425)"}, + }}, + {0x1237, "Alta Technology Corporation", { + }}, + {0x1238, "Adtran", { + }}, + {0x1239, "3DO Company", { + }}, + {0x123a, "Visicom Laboratories, Inc.", { + }}, + {0x123b, "Seeq Technology, Inc.", { + }}, + {0x123c, "Century Systems, Inc.", { + }}, + {0x123d, "Engineering Design Team, Inc.", { + }}, + {0x123e, "Simutech, Inc.", { + }}, + {0x123f, "C-Cube Microsystems", { + {0x00e4,"MPEG"}, + }}, + {0x1240, "Marathon Technologies Corp.", { + }}, + {0x1241, "DSC Communications", { + }}, + {0x1243, "Delphax", { + }}, + {0x1244, "AVM Audiovisuelles MKTG & Computer System GmbH", { + }}, + {0x1245, "A.P.D., S.A.", { + }}, + {0x1246, "Dipix Technologies, Inc.", { + }}, + {0x1247, "Xylon Research, Inc.", { + }}, + {0x1248, "Central Data Corporation", { + }}, + {0x1249, "Samsung Electronics Co., Ltd.", { + }}, + {0x124a, "AEG Electrocom GmbH", { + }}, + {0x124b, "SBS/Greenspring Modular I/O", { + }}, + {0x124c, "Solitron Technologies, Inc.", { + }}, + {0x124d, "Stallion Technologies, Inc.", { + {0x0000,"EasyConnection 8/32 - PCI"}, + {0x0002,"EasyConnection 8/64 - PCI"}, + {0x0003,"EasyIO - PCI"}, + }}, + {0x124e, "Cylink", { + }}, + {0x124f, "Infotrend Technology, Inc.", { + }}, + {0x1250, "Hitachi Microcomputer System Ltd", { + }}, + {0x1251, "VLSI Solutions Oy", { + }}, + {0x1253, "Guzik Technical Enterprises", { + }}, + {0x1254, "Linear Systems Ltd.", { + }}, + {0x1255, "Optibase Ltd", { + {0x1110,"MPEG Forge"}, + {0x1210,"MPEG Fusion"}, + {0x2110,"VideoPlex"}, + {0x2120,"VideoPlex CC"}, + {0x2130,"VideoQuest"}, + }}, + {0x1256, "Perceptive Solutions, Inc.", { + }}, + {0x1257, "Vertex Networks, Inc.", { + }}, + {0x1258, "Gilbarco, Inc.", { + }}, + {0x1259, "Allied Telesyn International", { + }}, + {0x125a, "ABB Power Systems", { + }}, + {0x125b, "Asix Electronics Corporation", { + }}, + {0x125c, "Aurora Technologies, Inc.", { + }}, + {0x125d, "ESS Technology", { + }}, + {0x125e, "Specialvideo Engineering SRL", { + }}, + {0x125f, "Concurrent Technologies, Inc.", { + }}, + {0x1260, "Harris Semiconductor", { + }}, + {0x1261, "Matsushita-Kotobuki Electronics Industries, Ltd.", { + }}, + {0x1262, "ES Computer Company, Ltd.", { + }}, + {0x1263, "Sonic Solutions", { + }}, + {0x1264, "Aval Nagasaki Corporation", { + }}, + {0x1265, "Casio Computer Co., Ltd.", { + }}, + {0x1266, "Microdyne Corporation", { + }}, + {0x1267, "S. A. Telecommunications", { + {0x5352,"PCR2101"}, + {0x5a4b,"Telsat Turbo"}, + }}, + {0x1268, "Tektronix", { + }}, + {0x1269, "Thomson-CSF/TTM", { + }}, + {0x126a, "Lexmark International, Inc.", { + }}, + {0x126b, "Adax, Inc.", { + }}, + {0x126c, "Northern Telecom", { + }}, + {0x126d, "Splash Technology, Inc.", { + }}, + {0x126e, "Sumitomo Metal Industries, Ltd.", { + }}, + {0x126f, "Silicon Motion, Inc.", { + }}, + {0x1270, "Olympus Optical Co., Ltd.", { + }}, + {0x1271, "GW Instruments", { + }}, + {0x1272, "Telematics International", { + }}, + {0x1273, "Hughes Network Systems", { + {0x0002,"DirecPC"}, + }}, + {0x1274, "Ensoniq", { + {0x5000,"AudioPCI"}, + }}, + {0x1275, "Network Appliance Corporation", { + }}, + {0x1276, "Switched Network Technologies, Inc.", { + }}, + {0x1277, "Comstream", { + }}, + {0x1278, "Transtech Parallel Systems Ltd.", { + }}, + {0x1279, "Transmeta Corporation", { + }}, + {0x127a, "Rockwell International", { + }}, + {0x127b, "Pixera Corporation", { + }}, + {0x127c, "Crosspoint Solutions, Inc.", { + }}, + {0x127d, "Vela Research", { + }}, + {0x127e, "Winnov, L.P.", { + }}, + {0x127f, "Fujifilm", { + }}, + {0x1280, "Photoscript Group Ltd.", { + }}, + {0x1281, "Yokogawa Electric Corporation", { + }}, + {0x1282, "Davicom Semiconductor, Inc.", { + }}, + {0x1283, "Integrated Technology Express, Inc.", { + }}, + {0x1284, "Sahara Networks, Inc.", { + }}, + {0x1285, "Platform Technologies, Inc.", { + }}, + {0x1286, "Mazet GmbH", { + }}, + {0x1287, "M-Pact, Inc.", { + }}, + {0x1288, "Timestep Corporation", { + }}, + {0x1289, "AVC Technology, Inc.", { + }}, + {0x128a, "Asante Technologies, Inc.", { + }}, + {0x128b, "Transwitch Corporation", { + }}, + {0x128c, "Retix Corporation", { + }}, + {0x128d, "G2 Networks, Inc.", { + }}, + {0x128e, "Samho Multi Tech Ltd.", { + }}, + {0x128f, "Tateno Dennou, Inc.", { + }}, + {0x1290, "Sord Computer Corporation", { + }}, + {0x1291, "NCS Computer Italia", { + }}, + {0x1292, "Tritech Microelectronics Inc", { + }}, + {0x1293, "Media Reality Technology", { + }}, + {0x1294, "Rhetorex, Inc.", { + }}, + {0x1295, "Imagenation Corporation", { + }}, + {0x1296, "Kofax Image Products", { + }}, + {0x1297, "Holco Enterprise Co, Ltd/Shuttle Computer", { + }}, + {0x1298, "Spellcaster Telecommunications Inc.", { + }}, + {0x1299, "Knowledge Technology Lab.", { + }}, + {0x129b, "Image Access", { + }}, + {0x129c, "Jaycor", { + }}, + {0x129d, "Compcore Multimedia, Inc.", { + }}, + {0x129e, "Victor Company of Japan, Ltd.", { + }}, + {0x129f, "OEC Medical Systems, Inc.", { + }}, + {0x12a0, "Allen-Bradley Company", { + }}, + {0x12a1, "Simpact Associates, Inc.", { + }}, + {0x12a2, "Newgen Systems Corporation", { + }}, + {0x12a3, "Lucent Technologies", { + }}, + {0x12a4, "NTT Electronics Technology Company", { + }}, + {0x12a5, "Vision Dynamics Ltd.", { + }}, + {0x12a6, "Scalable Networks, Inc.", { + }}, + {0x12a7, "AMO GmbH", { + }}, + {0x12a8, "News Datacom", { + }}, + {0x12a9, "Xiotech Corporation", { + }}, + {0x12aa, "SDL Communications, Inc.", { + }}, + {0x12ab, "Yuan Yuan Enterprise Co., Ltd.", { + }}, + {0x12ac, "Measurex Corporation", { + }}, + {0x12ad, "Multidata GmbH", { + }}, + {0x12ae, "Alteon Networks Inc.", { + {0x0001,"AceNIC Gigabit Ethernet"}, + }}, + {0x12af, "TDK USA Corp", { + }}, + {0x12b0, "Jorge Scientific Corp", { + }}, + {0x12b1, "GammaLink", { + }}, + {0x12b2, "General Signal Networks", { + }}, + {0x12b3, "Inter-Face Co Ltd", { + }}, + {0x12b4, "FutureTel Inc", { + }}, + {0x12b5, "Granite Systems Inc.", { + }}, + {0x12b6, "Natural Microsystems", { + }}, + {0x12b7, "Cognex Modular Vision Systems Div. - Acumen Inc.", { + }}, + {0x12b8, "Korg", { + }}, + {0x12b9, "US Robotics", { + }}, + {0x12ba, "PMC Sierra", { + }}, + {0x12bb, "Nippon Unisoft Corporation", { + }}, + {0x12bc, "Array Microsystems", { + }}, + {0x12bd, "Computerm Corp.", { + }}, + {0x12be, "Anchor Chips Inc.", { + }}, + {0x12bf, "Fujifilm Microdevices", { + }}, + {0x12c0, "Infimed", { + }}, + {0x12c1, "GMM Research Corp", { + }}, + {0x12c2, "Mentec Limited", { + }}, + {0x12c3, "Holtek Microelectronics Inc", { + }}, + {0x12c4, "Connect Tech Inc", { + }}, + {0x12c5, "Picture Elements Incorporated", { + {0x0081,"PCIVST [PCI Grayscale Thresholding Engine]"}, + }}, + {0x12c6, "Mitani Corporation", { + }}, + {0x12c7, "Dialogic Corp", { + }}, + {0x12c8, "G Force Co, Ltd", { + }}, + {0x12c9, "Gigi Operations", { + }}, + {0x12ca, "Integrated Computing Engines", { + }}, + {0x12cb, "Antex Electronics Corporation", { + }}, + {0x12cc, "Pluto Technologies International", { + }}, + {0x12cd, "Aims Lab", { + }}, + {0x12ce, "Netspeed Inc.", { + }}, + {0x12cf, "Prophet Systems, Inc.", { + }}, + {0x12d0, "GDE Systems, Inc.", { + }}, + {0x12d1, "PSITech", { + }}, + {0x12d2, "NVidia / SGS Thomson (Joint Venture)", { + {0x0018,"Riva128"}, + }}, + {0x12d3, "Vingmed Sound A/S", { + }}, + {0x12d4, "DGM&S", { + }}, + {0x12d5, "Equator Technologies", { + }}, + {0x12d6, "Analogic Corp", { + }}, + {0x12d7, "Biotronic SRL", { + }}, + {0x12d8, "Pericom Semiconductor", { + }}, + {0x12d9, "Aculab PLC", { + }}, + {0x12da, "True Time Inc.", { + }}, + {0x12db, "Annapolis Micro Systems, Inc", { + }}, + {0x12dc, "Symicron Computer Communication Ltd.", { + }}, + {0x12dd, "Management Graphics", { + }}, + {0x12de, "Rainbow Technologies", { + }}, + {0x12df, "SBS Technologies Inc", { + }}, + {0x12e0, "Chase Research", { + }}, + {0x12e1, "Nintendo Co, Ltd", { + }}, + {0x12e2, "Datum Inc. Bancomm-Timing Division", { + }}, + {0x12e3, "Imation Corp - Medical Imaging Systems", { + }}, + {0x12e4, "Brooktrout Technology Inc", { + }}, + {0x12e5, "Apex Semiconductor Inc", { + }}, + {0x12e6, "Cirel Systems", { + }}, + {0x12e7, "Sunsgroup Corporation", { + }}, + {0x12e8, "Crisc Corp", { + }}, + {0x12e9, "GE Spacenet", { + }}, + {0x12ea, "Zuken", { + }}, + {0x12eb, "Aureal Semiconductor", { + }}, + {0x12ec, "3A International, Inc.", { + }}, + {0x12ed, "Optivision Inc.", { + }}, + {0x12ee, "Orange Micro", { + }}, + {0x12ef, "Vienna Systems", { + }}, + {0x12f0, "Pentek", { + }}, + {0x12f1, "Sorenson Vision Inc", { + }}, + {0x12f2, "Gammagraphx, Inc.", { + }}, + {0x12f3, "Radstone Technology", { + }}, + {0x12f4, "Megatel", { + }}, + {0x12f5, "Forks", { + }}, + {0x12f6, "Dawson France", { + }}, + {0x12f7, "Cognex", { + }}, + {0x12f8, "Electronic Design GmbH", { + }}, + {0x12f9, "Four Fold Ltd", { + }}, + {0x12fb, "Spectrum Signal Processing", { + }}, + {0x12fc, "Capital Equipment Corp", { + }}, + {0x12fd, "I2S", { + }}, + {0x12fe, "ESD Electronic System Design GmbH", { + }}, + {0x12ff, "Lexicon", { + }}, + {0x1300, "Harman International Industries Inc", { + }}, + {0x1302, "Computer Sciences Corp", { + }}, + {0x1303, "Innovative Integration", { + }}, + {0x1304, "Juniper Networks", { + }}, + {0x1305, "Netphone, Inc", { + }}, + {0x1306, "Duet Technologies", { + }}, + {0x1307, "Computer Boards", { + {0x0001,"DAS1602/16"}, + }}, + {0x1308, "Jato Technologies Inc.", { + }}, + {0x1309, "AB Semiconductor Ltd", { + }}, + {0x130a, "Mitsubishi Electric Microcomputer", { + }}, + {0x130b, "Colorgraphic Communications Corp", { + }}, + {0x130c, "Ambex Technologies, Inc", { + }}, + {0x130d, "Accelerix Inc", { + }}, + {0x130e, "Yamatake-Honeywell Co. Ltd", { + }}, + {0x130f, "Advanet Inc", { + }}, + {0x1310, "Gespac", { + }}, + {0x1311, "Videoserver, Inc", { + }}, + {0x1312, "Acuity Imaging, Inc", { + }}, + {0x1313, "Yaskawa Electric Co.", { + }}, + {0x1316, "Teradyne Inc", { + }}, + {0x1317, "Bridgecom, Inc", { + }}, + {0x1318, "Packet Engines Inc.", { + }}, + {0x1319, "Fortemedia, Inc", { + }}, + {0x131a, "Finisar Corp.", { + }}, + {0x131c, "Nippon Electro-Sensory Devices Corp", { + }}, + {0x131d, "Sysmic, Inc.", { + }}, + {0x131e, "Xinex Networks Inc", { + }}, + {0x131f, "Siig Inc", { + }}, + {0x1320, "Crypto AG", { + }}, + {0x1321, "Arcobel Graphics BV", { + }}, + {0x1322, "MTT Co., Ltd", { + }}, + {0x1323, "Dome Inc", { + }}, + {0x1324, "Sphere Communications", { + }}, + {0x1325, "Salix Technologies, Inc", { + }}, + {0x1326, "Seachange international", { + }}, + {0x1327, "Voss scientific", { + }}, + {0x1328, "quadrant international", { + }}, + {0x1329, "Productivity Enhancement", { + }}, + {0x132a, "Microcom Inc.", { + }}, + {0x132b, "Broadband Technologies", { + }}, + {0x132c, "Micrel Inc", { + }}, + {0x132d, "Integrated Silicon Solution, Inc.", { + }}, + {0x1330, "MMC Networks", { + }}, + {0x1331, "Radisys Corp.", { + }}, + {0x1332, "Micro Memory", { + }}, + {0x1334, "Redcreek Communications, Inc", { + }}, + {0x1335, "Videomail, Inc", { + }}, + {0x1337, "Third Planet Publishing", { + }}, + {0x1338, "BT Electronics", { + }}, + {0x133a, "Vtel Corp", { + }}, + {0x133b, "Softcom Microsystems", { + }}, + {0x133c, "Holontech Corp", { + }}, + {0x133d, "SS Technologies", { + }}, + {0x133e, "Virtual Computer Corp", { + }}, + {0x133f, "SCM Microsystems", { + }}, + {0x1340, "Atalla Corp", { + }}, + {0x1341, "Kyoto Microcomputer Co", { + }}, + {0x1342, "Promax Systems Inc", { + }}, + {0x1343, "Phylon Communications Inc", { + }}, + {0x1344, "Crucial Technology", { + }}, + {0x1345, "Arescom Inc", { + }}, + {0x1347, "Odetics", { + }}, + {0x1349, "Sumitomo Electric Industries, Ltd.", { + }}, + {0x134a, "DTC Technology Corp.", { + }}, + {0x134b, "ARK Research Corp.", { + }}, + {0x134c, "Chori Joho System Co. Ltd", { + }}, + {0x134d, "PCTel Inc", { + }}, + {0x134e, "CSTI", { + }}, + {0x134f, "Algo System Co Ltd", { + }}, + {0x1350, "Systec Co. Ltd", { + }}, + {0x1351, "Sonix Inc", { + }}, + {0x1353, "Dassault A.T.", { + }}, + {0x1354, "Dwave System Inc", { + }}, + {0x1355, "Kratos Analytical Ltd", { + }}, + {0x1356, "The Logical Co", { + }}, + {0x1359, "Prisa Networks", { + }}, + {0x135a, "Brain Boxes", { + }}, + {0x135b, "Giganet Inc", { + }}, + {0x135c, "Quatech Inc", { + }}, + {0x135d, "ABB Network Partner AB", { + }}, + {0x135e, "Sealevel Systems Inc", { + }}, + {0x135f, "I-Data International A-S", { + }}, + {0x1360, "Meinberg Funkuhren", { + }}, + {0x1361, "Soliton Systems K.K.", { + }}, + {0x1362, "Fujifacom Corporation", { + }}, + {0x1363, "Phoenix Technology Ltd", { + }}, + {0x1364, "ATM Communications Inc", { + }}, + {0x1365, "Hypercope GmbH", { + }}, + {0x1366, "Teijin Seiki Co. Ltd", { + }}, + {0x1367, "Hitachi Zosen Corporation", { + }}, + {0x1368, "Skyware Corporation", { + }}, + {0x1369, "Digigram", { + }}, + {0x136a, "High Soft Tech", { + }}, + {0x136b, "Kawasaki Steel Corporation", { + }}, + {0x136c, "Adtek System Science Co Ltd", { + }}, + {0x136d, "Gigalabs Inc", { + }}, + {0x136f, "Applied Magic Inc", { + }}, + {0x1370, "ATL Products", { + }}, + {0x1371, "CNet Technology Inc", { + }}, + {0x1373, "Silicon Vision Inc", { + }}, + {0x1374, "Silicom Ltd", { + }}, + {0x1375, "Argosystems Inc", { + }}, + {0x1376, "LMC", { + }}, + {0x1377, "Electronic Equipment Production & Distribution GmbH", { + }}, + {0x1378, "Telemann Co. Ltd", { + }}, + {0x1379, "Asahi Kasei Microsystems Co Ltd", { + }}, + {0x137a, "Mark of the Unicorn Inc", { + }}, + {0x137b, "PPT Vision", { + }}, + {0x137c, "Iwatsu Electric Co Ltd", { + }}, + {0x137d, "Dynachip Corporation", { + }}, + {0x137e, "Patriot Scientific Corporation", { + }}, + {0x137f, "Japan Satellite Systems Inc", { + }}, + {0x1380, "Sanritz Automation Co Ltd", { + }}, + {0x1381, "Brains Co. Ltd", { + }}, + {0x1382, "Marian - Electronic & Software", { + }}, + {0x1383, "Controlnet Inc", { + }}, + {0x1384, "Reality Simulation Systems Inc", { + }}, + {0x1385, "Netgear", { + }}, + {0x1386, "Video Domain Technologies", { + }}, + {0x1387, "Systran Corp", { + }}, + {0x1388, "Hitachi Information Technology Co Ltd", { + }}, + {0x1389, "Applicom International", { + }}, + {0x138a, "Fusion Micromedia Corp", { + }}, + {0x138b, "Tokimec Inc", { + }}, + {0x138c, "Silicon Reality", { + }}, + {0x138d, "Future Techno Designs pte Ltd", { + }}, + {0x138e, "Basler GmbH", { + }}, + {0x138f, "Patapsco Designs Inc", { + }}, + {0x1390, "Concept Development Inc", { + }}, + {0x1391, "Development Concepts Inc", { + }}, + {0x1392, "Medialight Inc", { + }}, + {0x1393, "Moxa Technologies Co Ltd", { + }}, + {0x1394, "Level One Communications", { + }}, + {0x1395, "Ambicom Inc", { + }}, + {0x1396, "Cipher Systems Inc", { + }}, + {0x1397, "Cologne Chip Designs GmbH", { + }}, + {0x1398, "Clarion co. Ltd", { + }}, + {0x1399, "Rios systems Co Ltd", { + }}, + {0x139a, "Alacritech Inc", { + }}, + {0x139b, "Mediasonic Multimedia Systems Ltd", { + }}, + {0x139c, "Quantum 3d Inc", { + }}, + {0x139d, "EPL limited", { + }}, + {0x139e, "Media4", { + }}, + {0x139f, "Aethra s.r.l.", { + }}, + {0x13a0, "Crystal Group Inc", { + }}, + {0x13a1, "Kawasaki Heavy Industries Ltd", { + }}, + {0x13a2, "Ositech Communications Inc", { + }}, + {0x13a3, "Hi-Fn", { + }}, + {0x13a4, "Rascom Inc", { + }}, + {0x13a5, "Audio Digital Imaging Inc", { + }}, + {0x13a6, "Videonics Inc", { + }}, + {0x13a7, "Teles AG", { + }}, + {0x13a8, "Exar Corp.", { + }}, + {0x13a9, "Siemens Medical Systems, Ultrasound Group", { + }}, + {0x13aa, "Broadband Networks Inc", { + }}, + {0x13ab, "Arcom Control Systems Ltd", { + }}, + {0x13ac, "Motion Media Technology Ltd", { + }}, + {0x13ad, "Nexus Inc", { + }}, + {0x13ae, "ALD Technology Ltd", { + }}, + {0x13af, "T.Sqware", { + }}, + {0x13b0, "Maxspeed Corp", { + }}, + {0x13b1, "Tamura corporation", { + }}, + {0x13b2, "Techno Chips Co. Ltd", { + }}, + {0x13b3, "Lanart Corporation", { + }}, + {0x13b4, "Wellbean Co Inc", { + }}, + {0x13b5, "ARM", { + }}, + {0x13b6, "Dlog GmbH", { + }}, + {0x13b7, "Logic Devices Inc", { + }}, + {0x13b8, "Nokia Telecommunications oy", { + }}, + {0x13b9, "Elecom Co Ltd", { + }}, + {0x13ba, "Oxford Instruments", { + }}, + {0x13bb, "Sanyo Technosound Co Ltd", { + }}, + {0x13bc, "Bitran Corporation", { + }}, + {0x13bd, "Sharp corporation", { + }}, + {0x13be, "Miroku Jyoho Service Co. Ltd", { + }}, + {0x13bf, "Sharewave Inc", { + }}, + {0x13c0, "Microgate Corporation", { + }}, + {0x13c1, "3ware Inc", { + }}, + {0x13c2, "Technotrend Systemtechnik GmbH", { + }}, + {0x13c3, "Janz Computer AG", { + }}, + {0x13c4, "Phase Metrics", { + }}, + {0x13c5, "Alphi Technology Corp", { + }}, + {0x13c6, "Condor Engineering Inc", { + }}, + {0x13c7, "Blue Chip Technology Ltd", { + }}, + {0x13c8, "Apptech Inc", { + }}, + {0x13c9, "Eaton Corporation", { + }}, + {0x13ca, "Iomega Corporation", { + }}, + {0x13cb, "Yano Electric Co Ltd", { + }}, + {0x13cc, "Metheus Corporation", { + }}, + {0x13cd, "Compatible Systems Corporation", { + }}, + {0x13ce, "Cocom A/S", { + }}, + {0x13cf, "Studio Audio & Video Ltd", { + }}, + {0x13d0, "Techsan Electronics Co Ltd", { + }}, + {0x13d1, "Abocom Systems Inc", { + }}, + {0x13d2, "Shark Multimedia Inc", { + }}, + {0x13d3, "IMC Networks", { + }}, + {0x13d4, "Graphics Microsystems Inc", { + }}, + {0x13d5, "Media 100 Inc", { + }}, + {0x13d6, "K.I. Technology Co Ltd", { + }}, + {0x13d7, "Toshiba Engineering Corporation", { + }}, + {0x13d8, "Phobos corporation", { + }}, + {0x13d9, "Apex PC Solutions Inc", { + }}, + {0x13da, "Intresource Systems pte Ltd", { + }}, + {0x13db, "Janich & Klass Computertechnik GmbH", { + }}, + {0x13dc, "Netboost Corporation", { + }}, + {0x13dd, "Multimedia Bundle Inc", { + }}, + {0x13de, "ABB Robotics Products AB", { + }}, + {0x13df, "E-Tech Inc", { + }}, + {0x13e0, "GVC Corporation", { + }}, + {0x13e1, "Silicom Multimedia Systems Inc", { + }}, + {0x13e2, "Dynamics Research Corporation", { + }}, + {0x13e3, "Nest Inc", { + }}, + {0x13e4, "Calculex Inc", { + }}, + {0x13e5, "Telesoft Design Ltd", { + }}, + {0x13e6, "Argosy research Inc", { + }}, + {0x13e7, "NAC Incorporated", { + }}, + {0x13e8, "Chip Express Corporation", { + }}, + {0x13e9, "Chip Express Corporation", { + }}, + {0x13ea, "Dallas Semiconductor", { + }}, + {0x13eb, "Hauppauge Computer Works Inc", { + }}, + {0x13ec, "Zydacron Inc", { + }}, + {0x13ed, "Raytheion E-Systems", { + }}, + {0x13ee, "Hayes Microcomputer Products Inc", { + }}, + {0x13ef, "Coppercom Inc", { + }}, + {0x13f0, "Sundance technology Inc", { + }}, + {0x13f1, "Oce' - Technologies B.V.", { + }}, + {0x13f2, "Ford Microelectronics Inc", { + }}, + {0x13f3, "Mcdata Corporation", { + }}, + {0x13f4, "Troika Design Inc", { + }}, + {0x13f5, "Kansai Electric Co. Ltd", { + }}, + {0x13f6, "C-Media Electronics Inc", { + }}, + {0x13f7, "Wildfire Communications", { + }}, + {0x13f8, "Ad Lib Multimedia Inc", { + }}, + {0x13f9, "NTT Advanced Technology Corp.", { + }}, + {0x13fa, "Pentland Systems Ltd", { + }}, + {0x13fb, "Aydin Corp", { + }}, + {0x13fc, "Computer Peripherals International", { + }}, + {0x13fd, "Micro Science Inc", { + }}, + {0x13fe, "Advantech Co. Ltd", { + }}, + {0x13ff, "Silicon Spice Inc", { + }}, + {0x1400, "Artx Inc", { + }}, + {0x1401, "CR-Systems A/S", { + }}, + {0x1402, "Meilhaus Electronic GmbH", { + }}, + {0x1403, "Ascor Inc", { + }}, + {0x1404, "Fundamental Software Inc", { + }}, + {0x1405, "Excalibur Systems Inc", { + }}, + {0x1406, "Oce' Printing Systems GmbH", { + }}, + {0x1407, "Lava Computer mfg Inc", { + }}, + {0x1408, "Aloka Co. Ltd", { + }}, + {0x1409, "Timedia Technology Co Ltd", { + }}, + {0x140a, "DSP Research Inc", { + }}, + {0x140b, "Ramix Inc", { + }}, + {0x140c, "Elmic Systems Inc", { + }}, + {0x140d, "Matsushita Electric Works Ltd", { + }}, + {0x140e, "Goepel Electronic GmbH", { + }}, + {0x140f, "Salient Systems Corp", { + }}, + {0x1410, "Midas lab Inc", { + }}, + {0x1411, "Ikos Systems Inc", { + }}, + {0x1412, "IC Ensemble Inc", { + }}, + {0x1413, "Addonics", { + }}, + {0x1414, "Microsoft Corporation", { + }}, + {0x1415, "Oxford Semiconductor Ltd", { + }}, + {0x1416, "Multiwave Innovation pte Ltd", { + }}, + {0x1417, "Convergenet Technologies Inc", { + }}, + {0x1418, "Kyushu electronics systems Inc", { + }}, + {0x1419, "Excel Switching Corp", { + }}, + {0x141a, "Apache Micro Peripherals Inc", { + }}, + {0x141b, "Zoom Telephonics Inc", { + }}, + {0x141d, "Digitan Systems Inc", { + }}, + {0x141e, "Fanuc Ltd", { + }}, + {0x141f, "Visiontech Ltd", { + }}, + {0x1420, "Psion Dacom plc", { + }}, + {0x1421, "Ads Technologies Inc", { + }}, + {0x1422, "Ygrec Systems Co Ltd", { + }}, + {0x1423, "Custom Technology Corp.", { + }}, + {0x1424, "Videoserver Connections", { + }}, + {0x1425, "ASIC Designers Inc", { + }}, + {0x1426, "Storage Technology Corp.", { + }}, + {0x1427, "Better On-Line Solutions", { + }}, + {0x1428, "Edec Co Ltd", { + }}, + {0x1429, "Unex Technology Corp.", { + }}, + {0x142a, "Kingmax Technology Inc", { + }}, + {0x142b, "Radiolan", { + }}, + {0x142c, "Minton Optic Industry Co Ltd", { + }}, + {0x142d, "Pix stream Inc", { + }}, + {0x142e, "Vitec Multimedia", { + }}, + {0x142f, "Radicom Research Inc", { + }}, + {0x1430, "ITT Aerospace/Communications Division", { + }}, + {0x1431, "Gilat Satellite Networks", { + }}, + {0x1432, "Edimax Computer Co.", { + }}, + {0x1433, "Eltec Elektronik GmbH", { + }}, + {0x1435, "Real Time Devices US Inc.", { + }}, + {0x1436, "CIS Technology Inc", { + }}, + {0x1668, "Action Tec Electronics Inc", { + }}, + {0x1b13, "Jaton Corp", { + }}, + {0x1c1c, "Symphony", { + {0x0001,"82C101"}, + }}, + {0x21c3, "21st Century Computer Corp.", { + }}, + {0x270b, "Xantel Corporation", { + }}, + {0x270f, "Chaintech Computer Co. Ltd", { + }}, + {0x3388, "Hint Corp", { + }}, + {0x3d3d, "3DLabs", { + {0x0001,"GLINT 300SX"}, + {0x0002,"GLINT 500TX"}, + {0x0003,"GLINT Delta"}, + {0x0004,"Permedia"}, + {0x0006,"GLINT MX"}, + }}, + {0x4005, "Avance Logic Inc.", { + {0x2064,"ALG2064i"}, + {0x2301,"ALG2301"}, + {0x2302,"ALG2302"}, + }}, + {0x4444, "Internext Compression Inc", { + }}, + {0x4680, "Umax Computer Corp", { + }}, + {0x4843, "Hercules Computer Technology Inc", { + }}, + {0x4978, "Axil Computer Inc", { + }}, + {0x4a14, "NetVin", { + {0x5000,"PCI NV5000SC"}, + }}, + {0x4ddc, "ILC Data Device Corp", { + }}, + {0x5053, "Voyetra Technologies", { + }}, + {0x5143, "Qualcomm Inc", { + }}, + {0x5333, "S3 Inc.", { + {0x0551,"Plato/PX (system)"}, + {0x5631,"86C325 [ViRGE]"}, + {0x8810,"86C764_0 [Trio 32 vers 0]"}, + {0x8811,"86C764_1 [Trio 32/64 vers 1]"}, + {0x8812,"Aurora64V+"}, + {0x8813,"86C764_3 [Trio 32/64 vers 3]"}, + {0x8814,"Trio64UV+"}, + {0x8815,"Aurora128"}, + {0x883d,"ViRGE/VX"}, + {0x8880,"Vision 868 vers 0"}, + {0x8881,"Vision 868 vers 1"}, + {0x8882,"Vision 868 vers 2"}, + {0x8883,"Vision 868 vers 3"}, + {0x88b0,"Vision 928 vers 0"}, + {0x88b1,"Vision 928 vers 1"}, + {0x88b2,"Vision 928 vers 2"}, + {0x88b3,"Vision 928 vers 3"}, + {0x88c0,"Vision 864 vers 0"}, + {0x88c1,"Vision 864 vers 1"}, + {0x88c2,"86C864"}, + {0x88c3,"86C864"}, + {0x88d0,"Vision 964 vers 0"}, + {0x88d1,"Vision 964 vers 1"}, + {0x88d2,"86C964"}, + {0x88d3,"86C964"}, + {0x88f0,"Vision 968"}, + {0x88f1,"86C968"}, + {0x88f2,"86C968"}, + {0x88f3,"86C968"}, + {0x8901,"Trio64V2/DX or /GX"}, + {0x8902,"Plato/PX (graphics)"}, + {0x8a01,"ViRGE/DX or /GX"}, + {0x8a10,"ViRGE/GX2"}, + {0x8c01,"ViRGE/MX"}, + {0x8c02,"ViRGE/MX+"}, + {0x8c03,"ViRGE/MX+MV"}, + {0xca00,"SonicVibes"}, + }}, + {0x5555, "Genroco, Inc", { + {0x0003,"TURBOstor HFP-832 [HiPPI NIC]"}, + }}, + {0x6374, "c't Magazin f€r Computertechnik", { + {0x6773,"GPPCI"}, + }}, + {0x6666, "Decision Computer International Co.", { + }}, + {0x8008, "Quancm Electronic GmbH", { + {0x0010,"WDOG1 [PCI-Watchdog 1]"}, + {0x0011,"PWDOG2 [Watchdog2/PCI]"}, + }}, + {0x8086, "Intel Corporation", { + {0x0482,"82375EB"}, + {0x0483,"82424ZX [Saturn]"}, + {0x0484,"82378IB [SIO ISA Bridge]"}, + {0x0486,"82430ZX [Aries]"}, + {0x04a3,"82434LX [Mercury/Neptune]"}, + {0x0960,"80960RP [i960 RP Microprocessor/Bridge]"}, + {0x1221,"82092AA_0"}, + {0x1222,"82092AA_1"}, + {0x1223,"SAA7116"}, + {0x1226,"82596"}, + {0x1227,"82865"}, + {0x1228,"82556"}, + {0x1229,"82557"}, + {0x122d,"430FX - 82437FX TSC [Triton I]"}, + {0x122e,"82371FB PIIX ISA [Triton I]"}, + {0x1230,"82371FB PIIX IDE [Triton I]"}, + {0x1234,"430MX - 82371MX MPIIX [430MX PCIset - 82371MX Mobile PCI I/O IDE Xcelerator (MPIIX)]"}, + {0x1235,"430MX - 82437MX MTSC [430MX PCIset - 82437MX Mobile System Controller (MTSC) and 82438MX Mobile Data Path (MTDP)]"}, + {0x1237,"440FX - 82441FX PMC [Natoma]"}, + {0x124b,"82380FB"}, + {0x1250,"430HX - 82439HX TXC [Triton II]"}, + {0x1960,"80960RP [i960RP Microprocessor]"}, + {0x7000,"82371SB PIIX3 ISA [Natoma/Triton II]"}, + {0x7010,"82371SB PIIX3 IDE [Natoma/Triton II]"}, + {0x7020,"82371SB PIIX3 USB [Natoma/Triton II]"}, + {0x7030,"430VX - 82437VX TVX [Triton VX]"}, + {0x7100,"430TX - 82439TX MTXC"}, + {0x7110,"82371AB PIIX4 ISA"}, + {0x7111,"82371AB PIIX4 IDE"}, + {0x7112,"82371AB PIIX4 USB"}, + {0x7113,"82371AB PIIX4 ACPI"}, + {0x7180,"440LX - 82443LX PAC Host"}, + {0x7181,"440LX - 82443LX PAC AGP"}, + {0x7190,"440BX - 82443BX Host"}, + {0x7191,"440BX - 82443BX AGP"}, + {0x7192,"440BX - 82443BX (AGP disabled)"}, + {0x7800,"i740"}, + {0x84c4,"82450KX [Orion]"}, + {0x84c5,"82450GX [Orion]"}, + }}, + {0x8800, "Trigem Computer Inc.", { + }}, + {0x8888, "Silicon Magic", { + }}, + {0x8e0e, "Computone Corporation", { + }}, + {0x8e2e, "KTI", { + {0x3000,"ET32P2"}, + }}, + {0x9004, "Adaptec", { + {0x1078,"AIC-7810"}, + {0x5078,"AIC-7850"}, + {0x5178,"7851"}, + {0x5278,"7852"}, + {0x5575,"2930"}, + {0x5578,"AIC-7855"}, + {0x5800,"AIC-5800"}, + {0x6075,"AIC-1480"}, + {0x6078,"AIC-7860"}, + {0x6178,"AIC-7861"}, + {0x6278,"AIC-7860"}, + {0x6378,"AIC-7860"}, + {0x7078,"AIC-7870"}, + {0x7178,"AIC-7871"}, + {0x7278,"AIC-7872"}, + {0x7378,"AIC-7873"}, + {0x7478,"AIC-7874 [AHA-2944]"}, + {0x7578,"7875"}, + {0x7678,"7876"}, + {0x7895,"AIC-7895"}, + {0x8078,"AIC-7880U"}, + {0x8178,"AIC-7881U"}, + {0x8278,"AIC-7882U"}, + {0x8378,"AIC-7883U"}, + {0x8478,"AIC-7884U"}, + {0x8578,"7885"}, + {0x8678,"7886"}, + {0x8b78,"ABA-1030"}, + }}, + {0x907f, "Atronics", { + {0x2015,"IDE-2015PL"}, + }}, + {0x9412, "Holtek", { + {0x6565,"6565"}, + }}, + {0xa200, "NEC Corporation", { + }}, + {0xa259, "Hewlett Packard", { + }}, + {0xa25b, "Hewlett Packard GmbH PL24-MKT", { + }}, + {0xa304, "Sony", { + }}, + {0xa727, "3Com Corporation", { + }}, + {0xaa42, "Scitex Digital Video", { + }}, + {0xb1b3, "Shiva Europe Limited", { + }}, + {0xc001, "TSI Telsys", { + }}, + {0xc0a9, "Micron/Crucial Technology", { + }}, + {0xc0de, "Motorola", { + }}, + {0xc0fe, "Motion Engineering, Inc.", { + }}, + {0xcafe, "Chrysalis-ITS", { + }}, + {0xd4d4, "Dy4 Systems Inc", { + }}, + {0xe159, "Tiger Jet Network Inc.", { + {0x0001,"300"}, + }}, + {0xecc0, "Echo Corporation", { + }}, + {0xedd8, "ARK Logic Inc", { + {0xa091,"1000PV [Stingray]"}, + {0xa099,"2000PV [Stingray]"}, + {0xa0a1,"2000MT"}, + {0xa0a9,"2000MI"}, + }}, +}; + diff --git a/os/pc/pcmciamodem.c b/os/pc/pcmciamodem.c new file mode 100644 index 00000000..800f406c --- /dev/null +++ b/os/pc/pcmciamodem.c @@ -0,0 +1,75 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +/* + * PCMCIA modem. + * By default, this will set it up with the port and irq of + * COM2 unless a serialx=type=com line is found in plan9.ini. + * The assumption is that a laptop with a pcmcia will have only + * one com port. + */ + +enum { + Maxcard= 8, +}; + +static char* modems[] = { + "IBM 33.6 Data/Fax/Voice Modem", + "CM-56G", /* Xircom CreditCard Modem 56 - GlobalACCESS */ + "KeepInTouch", + "CEM56", + "MONTANA V.34 FAX/MODEM", /* Motorola */ + "REM10", + "GSM/GPRS", + "AirCard 555", + "Gold Card Global", /* Psion V90 Gold card */ + "Merlin UMTS Modem", /* Novatel card */ + 0, +}; + +void +pcmciamodemlink(void) +{ + ISAConf isa; + int i, j, slot, com2used, usingcom2; + + i = 0; + com2used = 0; + for(j = 0; modems[j]; j++){ + memset(&isa, 0, sizeof(isa)); + + /* look for a configuration line */ + for(; i < Maxcard; i++){ + if(isaconfig("serial", i, &isa)) + if(cistrcmp(isa.type, "com") == 0) + break; + memset(&isa, 0, sizeof(isa)); + } + + usingcom2 = 0; + if (isa.irq == 0 && isa.port == 0) { + if (com2used == 0) { + /* default is COM2 */ + isa.irq = 3; + isa.port = 0x2F8; + usingcom2 = 1; + } else + break; + } + slot = pcmspecial(modems[j], &isa); + if(slot >= 0){ + if(usingcom2) + com2used = 1; + if(ioalloc(isa.port, 8, 0, modems[j]) < 0) + print("%s port %lux already in use\n", modems[j], isa.port); + print("%s in pcmcia slot %d port 0x%lux irq %d\n", + modems[j], slot, isa.port, isa.irq); + } + } +} diff --git a/os/pc/piix4smbus.c b/os/pc/piix4smbus.c new file mode 100644 index 00000000..ced41001 --- /dev/null +++ b/os/pc/piix4smbus.c @@ -0,0 +1,213 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +// +// SMBus support for the PIIX4 +// +enum +{ + IntelVendID= 0x8086, + Piix4PMID= 0x7113, /* PIIX4 power management function */ + + // SMBus configuration registers (function 3) + SMBbase= 0x90, // 4 byte base address (bit 0 == 1, bit 3:1 == 0) + SMBconfig= 0xd2, + SMBintrselect= (7<<1), + SMIenable= (0<<1), // interrupts sent to SMI# + IRQ9enable= (4<<1), // intettupts sent to IRQ9 + SMBenable= (1<<0), // 1 enables + + // SMBus IO space registers + Hoststatus= 0x0, // (writing 1 bits reset the interrupt bits) + Failed= (1<<4), // transaction terminated by KILL + Bus_error= (1<<3), // transactio collision + Dev_error= (1<<2), // device error interrupt + Host_complete= (1<<1), // host command completion interrupt + Host_busy= (1<<0), // + Slavestatus= 0x1, // (writing 1 bits reset) + Alert_sts= (1<<5), // someone asserted SMBALERT# + Shdw2_sts= (1<<4), // slave accessed shadow 2 port + Shdw1_sts= (1<<3), // slave accessed shadow 1 port + Slv_sts= (1<<2), // slave accessed shadow 1 port + Slv_bsy= (1<<0), + Hostcontrol= 0x2, + Start= (1<<6), // start execution + Cmd_prot= (7<<2), // command protocol mask + Quick= (0<<2), // address only + Byte= (1<<2), // address + cmd + ByteData= (2<<2), // address + cmd + data + WordData= (3<<2), // address + cmd + data + data + Kill= (1<<1), // abort in progress command + Ienable= (1<<0), // enable completion interrupts + Hostcommand= 0x3, + Hostaddress= 0x4, + AddressMask= (0x7f<<1), // target address + Read= (1<<0), // 1 == read, 0 == write + Hostdata0= 0x5, + Hostdata1= 0x6, + Blockdata= 0x7, + Slavecontrol= 0x8, + Alert_en= (1<<3), // enable inter on SMBALERT# + Shdw2_en= (1<<2), // enable inter on external shadow 2 access + Shdw1_en= (1<<1), // enable inter on external shadow 1 access + Slv_en= (1<<0), // enable inter on access of host ctlr slave port + Shadowcommand= 0x9, + Slaveevent= 0xa, + Slavedata= 0xc, +}; + +static struct +{ + int rw; + int cmd; + int len; + int proto; +} proto[] = +{ + [SMBquick] { 0, 0, 0, Quick }, + [SMBsend] { 0, 1, 0, Byte }, + [SMBbytewrite] { 0, 1, 1, ByteData }, + [SMBwordwrite] { 0, 1, 2, WordData }, + [SMBrecv] { Read, 0, 1, Byte }, + [SMBbyteread] { Read, 1, 1, ByteData }, + [SMBwordread] { Read, 1, 2, WordData }, +}; + +static void +transact(SMBus *s, int type, int addr, int cmd, uchar *data) +{ + int tries, status; + char err[256]; + + if(type < 0 || type > nelem(proto)) + panic("piix4smbus: illegal transaction type %d", type); + + if(waserror()){ + qunlock(s); + nexterror(); + } + qlock(s); + + // wait a while for the host interface to be available + for(tries = 0; tries < 1000000; tries++){ + if((inb(s->base+Hoststatus) & Host_busy) == 0) + break; + sched(); + } + if(tries >= 1000000){ + // try aborting current transaction + outb(s->base+Hostcontrol, Kill); + for(tries = 0; tries < 1000000; tries++){ + if((inb(s->base+Hoststatus) & Host_busy) == 0) + break; + sched(); + } + if(tries >= 1000000){ + snprint(err, sizeof(err), "SMBus jammed: %2.2ux", inb(s->base+Hoststatus)); + error(err); + } + } + + // set up for transaction + outb(s->base+Hostaddress, (addr<<1)|proto[type].rw); + if(proto[type].cmd) + outb(s->base+Hostcommand, cmd); + if(proto[type].rw != Read){ + switch(proto[type].len){ + case 2: + outb(s->base+Hostdata1, data[1]); + // fall through + case 1: + outb(s->base+Hostdata0, data[0]); + break; + } + } + + + // reset the completion/error bits and start transaction + outb(s->base+Hoststatus, Failed|Bus_error|Dev_error|Host_complete); + outb(s->base+Hostcontrol, Start|proto[type].proto); + + // wait for completion + status = 0; + for(tries = 0; tries < 1000000; tries++){ + status = inb(s->base+Hoststatus); + if(status & (Failed|Bus_error|Dev_error|Host_complete)) + break; + sched(); + } + if((status & Host_complete) == 0){ + snprint(err, sizeof(err), "SMBus request failed: %2.2ux", status); + error(err); + } + + // get results + if(proto[type].rw == Read){ + switch(proto[type].len){ + case 2: + data[1] = inb(s->base+Hostdata1); + // fall through + case 1: + data[0] = inb(s->base+Hostdata0); + break; + } + } + qunlock(s); + poperror(); +} + +static SMBus smbusproto = +{ + .transact = transact, +}; + +// +// return 0 if this is a piix4 with an smbus interface +// +SMBus* +piix4smbus(void) +{ + Pcidev *p; + static SMBus *s; + + if(s != nil) + return s; + + p = pcimatch(nil, IntelVendID, Piix4PMID); + if(p == nil) + return nil; + + s = smalloc(sizeof(*s)); + memmove(s, &smbusproto, sizeof(*s)); + s->arg = p; + + // disable the smbus + pcicfgw8(p, SMBconfig, IRQ9enable|0); + + // see if bios gave us a viable port space + s->base = pcicfgr32(p, SMBbase) & ~1; +print("SMB base from bios is 0x%lux\n", s->base); + if(ioalloc(s->base, 0xd, 0, "piix4smbus") < 0){ + s->base = ioalloc(-1, 0xd, 2, "piix4smbus"); + if(s->base < 0){ + free(s); + print("piix4smbus: can't allocate io port\n"); + return nil; + } +print("SMB base ialloc is 0x%lux\n", s->base); + pcicfgw32(p, SMBbase, s->base|1); + } + + // disable SMBus interrupts, abort any transaction in progress + outb(s->base+Hostcontrol, Kill); + outb(s->base+Slavecontrol, 0); + + // enable the smbus + pcicfgw8(p, SMBconfig, IRQ9enable|SMBenable); + + return s; +} diff --git a/os/pc/pix b/os/pc/pix new file mode 100644 index 00000000..d856cfdb --- /dev/null +++ b/os/pc/pix @@ -0,0 +1,153 @@ +dev + root + cons + arch + env + mnt + pipe + prog + rtc + srv + dup + ssl + cap + +# draw screen vga vgax cga +# pointer +# vga + + ip bootp ip ipv6 ipaux iproute arp netlog ptclbsum386 iprouter plan9 nullmedium pktmedium + ether netif netaux + + sd + ds + uart + floppy dma + tinyfs +# mouse +# dbg x86break + +ip + il + tcp + udp + rudp + gre + ipifc + icmp + icmp6 + ipmux +lib + interp + keyring + sec + mp +# draw +# memlayer +# memdraw +# tk + math + kern + +link +# ps2mouse + ether82557 pci + ethermedium + loopbackmedium + netdevmedium + +misc +# vgaclgd542x +# vgas3 +cur vgasavage +# cga + sdata pci sdscsi + + uarti8250 + +mod + sys +# draw +# tk + keyring + math + +init + soeinit # it will do + +code + int kernel_pool_pcnt = 10; + int main_pool_pcnt = 40; + int heap_pool_pcnt = 20; + int image_pool_pcnt = 0; + int cflag=0; + int swcursor=0; + int consoleprint=0; + void screeninit(void){} + +port + alarm + alloc + allocb + chan + dev + dial + dis + discall + exception + exportfs + inferno + latin1 + nocache + nodynld + parse + pgrp + print + proc + qio + qlock + random + sysfile + taslock + xalloc + +root + /chan + /dev + /dis + /env + /fd / + /n + /n/remote + /net + /nvfs + /prog + /dis/lib + /dis/svc + /dis/wm + /osinit.dis + /dis/sh.dis + /dis/ls.dis + /dis/cat.dis + /dis/bind.dis + /dis/mount.dis + /dis/pwd.dis + /dis/echo.dis + /dis/cd.dis + /dis/lib/bufio.dis + /dis/lib/string.dis + /dis/lib/readdir.dis + /dis/lib/workdir.dis + /dis/lib/daytime.dis + /dis/lib/auth.dis + /dis/lib/ssl.dis + /dis/lib/filepat.dis + +# kfs + /dis/disk/kfs.dis + /dis/lib/arg.dis + /dis/lib/daytime.dis + /dis/lib/string.dis + /dis/lib/styx.dis + +# auth + /nvfs/default /usr/inferno/keyring/default diff --git a/os/pc/ps2mouse.c b/os/pc/ps2mouse.c new file mode 100644 index 00000000..49654bad --- /dev/null +++ b/os/pc/ps2mouse.c @@ -0,0 +1,84 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +/* + * mouse types + */ +enum +{ + Mouseother= 0, + Mouseserial= 1, + MousePS2= 2, +}; + +static int mousetype; + +/* + * ps/2 mouse message is three bytes + * + * byte 0 - 0 0 SDY SDX 1 M R L + * byte 1 - DX + * byte 2 - DY + * + * shift & left button is the same as middle button + */ +static void +ps2mouseputc(int c, int shift) +{ + static short msg[3]; + static int nb; + static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 5, 2, 3, 6, 7 }; + int buttons, dx, dy; + + /* + * check byte 0 for consistency + */ + if(nb==0 && (c&0xc8)!=0x08) + return; + + msg[nb] = c; + if(++nb == 3){ + nb = 0; + if(msg[0] & 0x10) + msg[1] |= 0xFF00; + if(msg[0] & 0x20) + msg[2] |= 0xFF00; + + buttons = b[(msg[0]&7) | (shift ? 8 : 0)]; + dx = msg[1]; + dy = -msg[2]; + mousetrack(buttons, dx, dy, 1); + } + return; +} + +/* + * set up a ps2 mouse + */ +static void +ps2mouse(void) +{ + if(mousetype == MousePS2) + return; + + i8042auxenable(ps2mouseputc); + /* make mouse streaming, enabled */ + i8042auxcmd(0xEA); + i8042auxcmd(0xF4); + + mousetype = MousePS2; +} + +void +ps2mouselink(void) +{ + /* + * hack + */ + ps2mouse(); +} diff --git a/os/pc/ptclbsum386.s b/os/pc/ptclbsum386.s new file mode 100644 index 00000000..ba0a6a4d --- /dev/null +++ b/os/pc/ptclbsum386.s @@ -0,0 +1,126 @@ +TEXT ptclbsum(SB), $0 + MOVL addr+0(FP), SI + MOVL len+4(FP), CX + + XORL AX, AX /* sum */ + + TESTL $1, SI /* byte aligned? */ + MOVL SI, DI + JEQ _2align + + DECL CX + JLT _return + + MOVB 0x00(SI), AH + INCL SI + +_2align: + TESTL $2, SI /* word aligned? */ + JEQ _32loop + + CMPL CX, $2 /* less than 2 bytes? */ + JLT _1dreg + SUBL $2, CX + + XORL BX, BX + MOVW 0x00(SI), BX + ADDL BX, AX + ADCL $0, AX + LEAL 2(SI), SI + +_32loop: + CMPL CX, $0x20 + JLT _8loop + + MOVL CX, BP + SHRL $5, BP + ANDL $0x1F, CX + +_32loopx: + MOVL 0x00(SI), BX + MOVL 0x1C(SI), DX + ADCL BX, AX + MOVL 0x04(SI), BX + ADCL DX, AX + MOVL 0x10(SI), DX + ADCL BX, AX + MOVL 0x08(SI), BX + ADCL DX, AX + MOVL 0x14(SI), DX + ADCL BX, AX + MOVL 0x0C(SI), BX + ADCL DX, AX + MOVL 0x18(SI), DX + ADCL BX, AX + LEAL 0x20(SI), SI + ADCL DX, AX + + DECL BP + JNE _32loopx + + ADCL $0, AX + +_8loop: + CMPL CX, $0x08 + JLT _2loop + + MOVL CX, BP + SHRL $3, BP + ANDL $0x07, CX + +_8loopx: + MOVL 0x00(SI), BX + ADCL BX, AX + MOVL 0x04(SI), DX + ADCL DX, AX + + LEAL 0x08(SI), SI + DECL BP + JNE _8loopx + + ADCL $0, AX + +_2loop: + CMPL CX, $0x02 + JLT _1dreg + + MOVL CX, BP + SHRL $1, BP + ANDL $0x01, CX + +_2loopx: + MOVWLZX 0x00(SI), BX + ADCL BX, AX + + LEAL 0x02(SI), SI + DECL BP + JNE _2loopx + + ADCL $0, AX + +_1dreg: + TESTL $1, CX /* 1 byte left? */ + JEQ _fold + + XORL BX, BX + MOVB 0x00(SI), BX + ADDL BX, AX + ADCL $0, AX + +_fold: + MOVL AX, BX + SHRL $16, BX + JEQ _swab + + ANDL $0xFFFF, AX + ADDL BX, AX + JMP _fold + +_swab: + TESTL $1, addr+0(FP) + /*TESTL $1, DI*/ + JNE _return + XCHGB AH, AL + +_return: + RET diff --git a/os/pc/screen.c b/os/pc/screen.c new file mode 100644 index 00000000..e879b050 --- /dev/null +++ b/os/pc/screen.c @@ -0,0 +1,410 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +#include +#include +#include +#include "screen.h" + +#define RGB2K(r,g,b) ((156763*(r)+307758*(g)+59769*(b))>>19) + +Point ZP = {0, 0}; + +Rectangle physgscreenr; +Cursorinfo cursor; /* TO DO */ + +Memdata gscreendata; +Memimage *gscreen; + +VGAscr vgascreen[1]; + +Cursor arrow = { + { -1, -1 }, + { 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04, + 0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04, + 0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40, + }, + { 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0, + 0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8, + 0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8, + 0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00, + }, +}; + +int +screensize(int x, int y, int z, ulong chan) +{ + VGAscr *scr; + + memimageinit(); + scr = &vgascreen[0]; + + /* + * BUG: need to check if any xalloc'ed memory needs to + * be given back if aperture is set. + */ + if(scr->aperture == 0){ + int width = (x*z)/BI2WD; + + gscreendata.bdata = xalloc(width*BY2WD*y); + if(gscreendata.bdata == 0) + error("screensize: vga soft memory"); +/* memset(gscreendata.bdata, 0x72, width*BY2WD*y); /* not really black */ + scr->useflush = 1; + scr->aperture = VGAMEM(); + scr->apsize = 1<<16; + } + else + gscreendata.bdata = KADDR(scr->aperture); + + if(gscreen) + freememimage(gscreen); + + gscreen = allocmemimaged(Rect(0,0,x,y), chan, &gscreendata); + vgaimageinit(chan); + if(gscreen == nil) + return -1; + + if(scr->dev && scr->dev->flush) + scr->useflush = 1; + + scr->palettedepth = 6; /* default */ + scr->gscreendata = &gscreendata; + scr->memdefont = getmemdefont(); + scr->gscreen = gscreen; + + physgscreenr = gscreen->r; + + drawcmap(); + return 0; +} + +int +screenaperture(int size, int align) +{ + VGAscr *scr; + ulong aperture; + + scr = &vgascreen[0]; + + if(size == 0){ + if(scr->aperture && scr->isupamem) + upafree(scr->aperture, scr->apsize); + scr->aperture = 0; + scr->isupamem = 0; + return 0; + } + if(scr->dev && scr->dev->linear){ + aperture = scr->dev->linear(scr, &size, &align); + if(aperture == 0) + return 1; + }else{ + aperture = upamalloc(0, size, align); + if(aperture == 0) + return 1; + + if(scr->aperture && scr->isupamem) + upafree(scr->aperture, scr->apsize); + scr->isupamem = 1; + } + + scr->aperture = aperture; + scr->apsize = size; + + return 0; +} + +uchar* +attachscreen(Rectangle* r, ulong* chan, int* d, int* width, int *softscreen) +{ + VGAscr *scr; + + scr = &vgascreen[0]; + if(scr->gscreen == nil || scr->gscreendata == nil) + return nil; + + *r = scr->gscreen->clipr; + *chan = scr->gscreen->chan; + *d = scr->gscreen->depth; + *width = scr->gscreen->width; + *softscreen = scr->useflush; + + return scr->gscreendata->bdata; +} + +/* + * It would be fair to say that this doesn't work for >8-bit screens. + */ +void +flushmemscreen(Rectangle r) +{ + VGAscr *scr; + uchar *sp, *disp, *sdisp, *edisp; + int y, len, incs, off, page; + + scr = &vgascreen[0]; + if(scr->dev && scr->dev->flush){ + scr->dev->flush(scr, r); + return; + } + if(scr->gscreen == nil || scr->useflush == 0) + return; + if(scr->dev == nil || scr->dev->page == nil) + return; + + if(rectclip(&r, scr->gscreen->r) == 0) + return; + + incs = scr->gscreen->width * BY2WD; + + switch(scr->gscreen->depth){ + default: + len = 0; + panic("flushmemscreen: depth\n"); + break; + case 8: + len = Dx(r); + break; + } + if(len < 1) + return; + + off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x*scr->gscreen->depth)/8; + page = off/scr->apsize; + off %= scr->apsize; + disp = KADDR(scr->aperture); + sdisp = disp+off; + edisp = disp+scr->apsize; + + off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x*scr->gscreen->depth)/8; + + sp = scr->gscreendata->bdata + off; + + scr->dev->page(scr, page); + for(y = r.min.y; y < r.max.y; y++) { + if(sdisp + incs < edisp) { + memmove(sdisp, sp, len); + sp += incs; + sdisp += incs; + } + else { + off = edisp - sdisp; + page++; + if(off <= len){ + if(off > 0) + memmove(sdisp, sp, off); + scr->dev->page(scr, page); + if(len - off > 0) + memmove(disp, sp+off, len - off); + } + else { + memmove(sdisp, sp, len); + scr->dev->page(scr, page); + } + sp += incs; + sdisp += incs - scr->apsize; + } + } +} + +void +getcolor(ulong p, ulong* pr, ulong* pg, ulong* pb) +{ + VGAscr *scr; + ulong x; + + scr = &vgascreen[0]; + if(scr->gscreen == nil) + return; + + switch(scr->gscreen->depth){ + default: + x = 0x0F; + break; + case 8: + x = 0xFF; + break; + } + p &= x; + + lock(&cursor); + *pr = scr->colormap[p][0]; + *pg = scr->colormap[p][1]; + *pb = scr->colormap[p][2]; + unlock(&cursor); +} + +int +setpalette(ulong p, ulong r, ulong g, ulong b) +{ + VGAscr *scr; + int d; + + scr = &vgascreen[0]; + d = scr->palettedepth; + + lock(&cursor); + scr->colormap[p][0] = r; + scr->colormap[p][1] = g; + scr->colormap[p][2] = b; + vgao(PaddrW, p); + vgao(Pdata, r>>(32-d)); + vgao(Pdata, g>>(32-d)); + vgao(Pdata, b>>(32-d)); + unlock(&cursor); + + return ~0; +} + +/* + * On some video cards (e.g. Mach64), the palette is used as the + * DAC registers for >8-bit modes. We don't want to set them when the user + * is trying to set a colormap and the card is in one of these modes. + */ +int +setcolor(ulong p, ulong r, ulong g, ulong b) +{ + VGAscr *scr; + int x; + + scr = &vgascreen[0]; + if(scr->gscreen == nil) + return 0; + + switch(scr->gscreen->depth){ + case 1: + case 2: + case 4: + x = 0x0F; + break; + case 8: + x = 0xFF; + break; + default: + return 0; + } + p &= x; + + return setpalette(p, r, g, b); +} + +int +cursoron(int dolock) +{ + VGAscr *scr; + int v; + + scr = &vgascreen[0]; + if(scr->cur == nil || scr->cur->move == nil) + return 0; + + if(dolock) + lock(&cursor); + v = scr->cur->move(scr, mousexy()); + if(dolock) + unlock(&cursor); + + return v; +} + +void +cursoroff(int) +{ +} + +void +setcursor(Cursor* curs) +{ + VGAscr *scr; + + scr = &vgascreen[0]; + if(scr->cur == nil || scr->cur->load == nil) + return; + + scr->cur->load(scr, curs); +} + +int hwaccel = 1; +int hwblank = 0; /* turned on by drivers that are known good */ +int panning = 0; + +int +hwdraw(Memdrawparam *par) +{ + VGAscr *scr; + Memimage *dst, *src; + int m; + + if(hwaccel == 0) + return 0; + + dst = par->dst; + scr = &vgascreen[0]; + if(dst == nil || dst->data == nil) + return 0; + + if(dst->data->bdata != gscreendata.bdata) + return 0; + + if(scr->fill==nil && scr->scroll==nil) + return 0; + + /* + * If we have an opaque mask and source is one opaque + * pixel we can convert to the destination format and just + * replicate with memset. + */ + m = Simplesrc|Simplemask|Fullmask; + if(scr->fill + && (par->state&m)==m + && ((par->srgba&0xFF) == 0xFF) + && (par->op&S) == S) + return scr->fill(scr, par->r, par->sdval); + + /* + * If no source alpha, an opaque mask, we can just copy the + * source onto the destination. If the channels are the same and + * the source is not replicated, memmove suffices. + */ + m = Simplemask|Fullmask; + src = par->src; + if(scr->scroll + && src->data->bdata==dst->data->bdata + && !(src->flags&Falpha) + && (par->state&m)==m + && (par->op&S) == S) + return scr->scroll(scr, par->r, par->sr); + + return 0; +} + +void +blankscreen(int blank) +{ + VGAscr *scr; + + scr = &vgascreen[0]; + if(hwblank){ + if(scr->blank) + scr->blank(scr, blank); + else + vgablank(scr, blank); + } +} + +void +cursorenable(void) +{ +} + +void +cursordisable(void) +{ +} diff --git a/os/pc/screen.h b/os/pc/screen.h new file mode 100644 index 00000000..bcc1f230 --- /dev/null +++ b/os/pc/screen.h @@ -0,0 +1,173 @@ +typedef struct Cursor Cursor; + +enum { + CURSWID = 16, + CURSHGT = 16, +}; + +struct Cursor +{ + Point offset; + uchar clr[CURSWID/BI2BY*CURSHGT]; + uchar set[CURSWID/BI2BY*CURSHGT]; +}; +typedef struct Cursorinfo Cursorinfo; +struct Cursorinfo { + Cursor; + Lock; +}; + +/* devmouse.c */ +extern void mousetrack(int, int, int, int); +extern Point mousexy(void); + +extern void mouseaccelerate(int); +extern int m3mouseputc(Queue*, int); +extern int m5mouseputc(Queue*, int); +extern int mouseputc(Queue*, int); + +extern Cursorinfo cursor; +extern Cursor arrow; + +/* + * Generic VGA registers. + */ +enum { + MiscW = 0x03C2, /* Miscellaneous Output (W) */ + MiscR = 0x03CC, /* Miscellaneous Output (R) */ + Status0 = 0x03C2, /* Input status 0 (R) */ + Status1 = 0x03DA, /* Input Status 1 (R) */ + FeatureR = 0x03CA, /* Feature Control (R) */ + FeatureW = 0x03DA, /* Feature Control (W) */ + + Seqx = 0x03C4, /* Sequencer Index, Data at Seqx+1 */ + Crtx = 0x03D4, /* CRT Controller Index, Data at Crtx+1 */ + Grx = 0x03CE, /* Graphics Controller Index, Data at Grx+1 */ + Attrx = 0x03C0, /* Attribute Controller Index and Data */ + + PaddrW = 0x03C8, /* Palette Address Register, write */ + Pdata = 0x03C9, /* Palette Data Register */ + Pixmask = 0x03C6, /* Pixel Mask Register */ + PaddrR = 0x03C7, /* Palette Address Register, read */ + Pstatus = 0x03C7, /* DAC Status (RO) */ + + Pcolours = 256, /* Palette */ + Pred = 0, + Pgreen = 1, + Pblue = 2, + + Pblack = 0x00, + Pwhite = 0xFF, +}; + +#define VGAMEM() 0xA0000 +#define vgai(port) inb(port) +#define vgao(port, data) outb(port, data) + +extern int vgaxi(long, uchar); +extern int vgaxo(long, uchar, uchar); + +/* + */ +typedef struct VGAdev VGAdev; +typedef struct VGAcur VGAcur; +typedef struct VGAscr VGAscr; + +struct VGAdev { + char* name; + + void (*enable)(VGAscr*); + void (*disable)(VGAscr*); + void (*page)(VGAscr*, int); + ulong (*linear)(VGAscr*, int*, int*); + void (*drawinit)(VGAscr*); + int (*fill)(VGAscr*, Rectangle, ulong); + void (*ovlctl)(VGAscr*, Chan*, void*, int); + int (*ovlwrite)(VGAscr*, void*, int, vlong); + void (*flush)(VGAscr*, Rectangle); +}; + +struct VGAcur { + char* name; + + void (*enable)(VGAscr*); + void (*disable)(VGAscr*); + void (*load)(VGAscr*, Cursor*); + int (*move)(VGAscr*, Point); + + int doespanning; +}; + +/* + */ +struct VGAscr { + Lock devlock; + VGAdev* dev; + + VGAcur* cur; + ulong storage; + Cursor; + + int useflush; + + ulong aperture; /* physical address */ + int isupamem; + int apsize; + + ulong io; /* device specific registers */ + + ulong colormap[Pcolours][3]; + int palettedepth; + + ulong *mmio; + Memimage* gscreen; + Memdata* gscreendata; + Memsubfont* memdefont; + + int (*fill)(VGAscr*, Rectangle, ulong); + int (*scroll)(VGAscr*, Rectangle, Rectangle); + void (*blank)(VGAscr*, int); + ulong id; /* internal identifier for driver use */ + int isblank; + int overlayinit; +}; + +extern VGAscr vgascreen[]; + +enum { + Backgnd = 0, /* black */ +}; + +/* mouse.c */ +extern void mousectl(Cmdbuf*); + +/* screen.c */ +extern int hwaccel; /* use hw acceleration; default on */ +extern int hwblank; /* use hw blanking; default on */ +extern int panning; /* use virtual screen panning; default off */ +extern void addvgaseg(char*, ulong, ulong); +extern uchar* attachscreen(Rectangle*, ulong*, int*, int*, int*); +extern void flushmemscreen(Rectangle); +extern int cursoron(int); +extern void cursoroff(int); +extern void setcursor(Cursor*); +extern int screensize(int, int, int, ulong); +extern int screenaperture(int, int); +extern Rectangle physgscreenr; /* actual monitor size */ +extern void blankscreen(int); + +/* devdraw.c */ +extern void deletescreenimage(void); +extern int drawhasclients(void); +extern ulong blanktime; +extern void setscreenimageclipr(Rectangle); +extern void drawflush(void); +extern int drawidletime(void); + +/* vga.c */ +extern void vgascreenwin(VGAscr*); +extern void vgaimageinit(ulong); +extern ulong vgapcilinear(VGAscr*, int*, int*, int, int); + +extern void drawblankscreen(int); +extern void vgablank(VGAscr*, int); diff --git a/os/pc/sd53c8xx.c b/os/pc/sd53c8xx.c new file mode 100644 index 00000000..88a679c8 --- /dev/null +++ b/os/pc/sd53c8xx.c @@ -0,0 +1,2138 @@ +/* + * NCR/Symbios/LSI Logic 53c8xx driver for Plan 9 + * Nigel Roles (nigel@9fs.org) + * + * 27/5/02 Fixed problems with transfers >= 256 * 512 + * + * 13/3/01 Fixed microcode to support targets > 7 + * + * 01/12/00 Removed previous comments. Fixed a small problem in + * mismatch recovery for targets with synchronous offsets of >=16 + * connected to >=875s. Thanks, Jean. + * + * Known problems + * + * Read/write mismatch recovery may fail on 53c1010s. Really need to get a manual. + */ + +#define MAXTARGET 16 /* can be 8 or 16 */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "../port/sd.h" +extern SDifc sd53c8xxifc; + +/**********************************/ +/* Portable configuration macros */ +/**********************************/ + +//#define BOOTDEBUG +//#define ASYNC_ONLY +//#define INTERNAL_SCLK +//#define ALWAYS_DO_WDTR +#define WMR_DEBUG + +/**********************************/ +/* CPU specific macros */ +/**********************************/ + +#define PRINTPREFIX "sd53c8xx: " + +#ifdef BOOTDEBUG + +#define KPRINT oprint +#define IPRINT intrprint +#define DEBUG(n) 1 +#define IFLUSH() iflush() + +#else + +#define KPRINT if(0) print +#define IPRINT if(0) print +#define DEBUG(n) (0) +#define IFLUSH() + +#endif /* BOOTDEBUG */ + +/*******************************/ +/* General */ +/*******************************/ + +#ifndef DMASEG +#define DMASEG(x) PCIWADDR(x) +#define legetl(x) (*(ulong*)(x)) +#define lesetl(x,v) (*(ulong*)(x) = (v)) +#define swabl(a,b,c) +#else +#endif /*DMASEG */ +#define DMASEG_TO_KADDR(x) KADDR((x)-PCIWINDOW) +#define KPTR(x) ((x) == 0 ? 0 : DMASEG_TO_KADDR(x)) + +#define MEGA 1000000L +#ifdef INTERNAL_SCLK +#define SCLK (33 * MEGA) +#else +#define SCLK (40 * MEGA) +#endif /* INTERNAL_SCLK */ +#define ULTRA_NOCLOCKDOUBLE_SCLK (80 * MEGA) + +#define MAXSYNCSCSIRATE (5 * MEGA) +#define MAXFASTSYNCSCSIRATE (10 * MEGA) +#define MAXULTRASYNCSCSIRATE (20 * MEGA) +#define MAXULTRA2SYNCSCSIRATE (40 * MEGA) +#define MAXASYNCCORERATE (25 * MEGA) +#define MAXSYNCCORERATE (25 * MEGA) +#define MAXFASTSYNCCORERATE (50 * MEGA) +#define MAXULTRASYNCCORERATE (80 * MEGA) +#define MAXULTRA2SYNCCORERATE (160 * MEGA) + + +#define X_MSG 1 +#define X_MSG_SDTR 1 +#define X_MSG_WDTR 3 + +struct na_patch { + unsigned lwoff; + unsigned char type; +}; + +typedef struct Ncr { + uchar scntl0; /* 00 */ + uchar scntl1; + uchar scntl2; + uchar scntl3; + + uchar scid; /* 04 */ + uchar sxfer; + uchar sdid; + uchar gpreg; + + uchar sfbr; /* 08 */ + uchar socl; + uchar ssid; + uchar sbcl; + + uchar dstat; /* 0c */ + uchar sstat0; + uchar sstat1; + uchar sstat2; + + uchar dsa[4]; /* 10 */ + + uchar istat; /* 14 */ + uchar istatpad[3]; + + uchar ctest0; /* 18 */ + uchar ctest1; + uchar ctest2; + uchar ctest3; + + uchar temp[4]; /* 1c */ + + uchar dfifo; /* 20 */ + uchar ctest4; + uchar ctest5; + uchar ctest6; + + uchar dbc[3]; /* 24 */ + uchar dcmd; /* 27 */ + + uchar dnad[4]; /* 28 */ + uchar dsp[4]; /* 2c */ + uchar dsps[4]; /* 30 */ + + uchar scratcha[4]; /* 34 */ + + uchar dmode; /* 38 */ + uchar dien; + uchar dwt; + uchar dcntl; + + uchar adder[4]; /* 3c */ + + uchar sien0; /* 40 */ + uchar sien1; + uchar sist0; + uchar sist1; + + uchar slpar; /* 44 */ + uchar slparpad0; + uchar macntl; + uchar gpcntl; + + uchar stime0; /* 48 */ + uchar stime1; + uchar respid; + uchar respidpad0; + + uchar stest0; /* 4c */ + uchar stest1; + uchar stest2; + uchar stest3; + + uchar sidl; /* 50 */ + uchar sidlpad[3]; + + uchar sodl; /* 54 */ + uchar sodlpad[3]; + + uchar sbdl; /* 58 */ + uchar sbdlpad[3]; + + uchar scratchb[4]; /* 5c */ +} Ncr; + +typedef struct Movedata { + uchar dbc[4]; + uchar pa[4]; +} Movedata; + +typedef enum NegoState { + NeitherDone, WideInit, WideResponse, WideDone, + SyncInit, SyncResponse, BothDone +} NegoState; + +typedef enum State { + Allocated, Queued, Active, Done +} State; + +typedef struct Dsa { + uchar stateb; + uchar result; + uchar dmablks; + uchar flag; /* setbyte(state,3,...) */ + + union { + ulong dmancr; /* For block transfer: NCR order (little-endian) */ + uchar dmaaddr[4]; + }; + + uchar target; /* Target */ + uchar pad0[3]; + + uchar lun; /* Logical Unit Number */ + uchar pad1[3]; + + uchar scntl3; + uchar sxfer; + uchar pad2[2]; + + uchar next[4]; /* chaining for SCRIPT (NCR byte order) */ + struct Dsa *freechain; /* chaining for freelist */ + Rendez; + uchar scsi_id_buf[4]; + Movedata msg_out_buf; + Movedata cmd_buf; + Movedata data_buf; + Movedata status_buf; + uchar msg_out[10]; /* enough to include SDTR */ + uchar status; + int p9status; + uchar parityerror; +} Dsa; + +typedef enum Feature { + BigFifo = 1, /* 536 byte fifo */ + BurstOpCodeFetch = 2, /* burst fetch opcodes */ + Prefetch = 4, /* prefetch 8 longwords */ + LocalRAM = 8, /* 4K longwords of local RAM */ + Differential = 16, /* Differential support */ + Wide = 32, /* Wide capable */ + Ultra = 64, /* Ultra capable */ + ClockDouble = 128, /* Has clock doubler */ + ClockQuad = 256, /* Has clock quadrupler (same as Ultra2) */ + Ultra2 = 256, +} Feature; + +typedef enum Burst { + Burst2 = 0, + Burst4 = 1, + Burst8 = 2, + Burst16 = 3, + Burst32 = 4, + Burst64 = 5, + Burst128 = 6 +} Burst; + +typedef struct Variant { + ushort did; + uchar maxrid; /* maximum allowed revision ID */ + char *name; + Burst burst; /* codings for max burst */ + uchar maxsyncoff; /* max synchronous offset */ + uchar registers; /* number of 32 bit registers */ + unsigned feature; +} Variant; + +static unsigned char cf2[] = { 6, 2, 3, 4, 6, 8, 12, 16 }; +#define NULTRA2SCF (sizeof(cf2)/sizeof(cf2[0])) +#define NULTRASCF (NULTRA2SCF - 2) +#define NSCF (NULTRASCF - 1) + +typedef struct Controller { + Lock; + struct { + uchar scntl3; + uchar stest2; + } bios; + uchar synctab[NULTRA2SCF - 1][8];/* table of legal tpfs */ + NegoState s[MAXTARGET]; + uchar scntl3[MAXTARGET]; + uchar sxfer[MAXTARGET]; + uchar cap[MAXTARGET]; /* capabilities byte from Identify */ + ushort capvalid; /* bit per target for validity of cap[] */ + ushort wide; /* bit per target set if wide negotiated */ + ulong sclk; /* clock speed of controller */ + uchar clockmult; /* set by synctabinit */ + uchar ccf; /* CCF bits */ + uchar tpf; /* best tpf value for this controller */ + uchar feature; /* requested features */ + int running; /* is the script processor running? */ + int ssm; /* single step mode */ + Ncr *n; /* pointer to registers */ + Variant *v; /* pointer to variant type */ + ulong *script; /* where the real script is */ + ulong scriptpa; /* where the real script is */ + Pcidev* pcidev; + SDev* sdev; + + struct { + Lock; + uchar head[4]; /* head of free list (NCR byte order) */ + Dsa *tail; + Dsa *freechain; + } dsalist; + + QLock q[MAXTARGET]; /* queues for each target */ +} Controller; + +#define SYNCOFFMASK(c) (((c)->v->maxsyncoff * 2) - 1) +#define SSIDMASK(c) (((c)->v->feature & Wide) ? 15 : 7) + +/* ISTAT */ +enum { Abrt = 0x80, Srst = 0x40, Sigp = 0x20, Sem = 0x10, Con = 0x08, Intf = 0x04, Sip = 0x02, Dip = 0x01 }; + +/* DSTAT */ +enum { Dfe = 0x80, Mdpe = 0x40, Bf = 0x20, Abrted = 0x10, Ssi = 0x08, Sir = 0x04, Iid = 0x01 }; + +/* SSTAT */ +enum { DataOut, DataIn, Cmd, Status, ReservedOut, ReservedIn, MessageOut, MessageIn }; + +static void setmovedata(Movedata*, ulong, ulong); +static void advancedata(Movedata*, long); +static int bios_set_differential(Controller *c); + +static char *phase[] = { + "data out", "data in", "command", "status", + "reserved out", "reserved in", "message out", "message in" +}; + +#ifdef BOOTDEBUG +#define DEBUGSIZE 10240 +char debugbuf[DEBUGSIZE]; +char *debuglast; + +static void +intrprint(char *format, ...) +{ + if (debuglast == 0) + debuglast = debugbuf; + debuglast = vseprint(debuglast, debugbuf + (DEBUGSIZE - 1), format, (&format + 1)); +} + +static void +iflush() +{ + int s; + char *endp; + s = splhi(); + if (debuglast == 0) + debuglast = debugbuf; + if (debuglast == debugbuf) { + splx(s); + return; + } + endp = debuglast; + splx(s); + screenputs(debugbuf, endp - debugbuf); + s = splhi(); + memmove(debugbuf, endp, debuglast - endp); + debuglast -= endp - debugbuf; + splx(s); +} + +static void +oprint(char *format, ...) +{ + int s; + + iflush(); + s = splhi(); + if (debuglast == 0) + debuglast = debugbuf; + debuglast = vseprint(debuglast, debugbuf + (DEBUGSIZE - 1), format, (&format + 1)); + splx(s); + iflush(); +} +#endif + +#include "sd53c8xx.i" + +static Dsa * +dsaalloc(Controller *c, int target, int lun) +{ + Dsa *d; + + ilock(&c->dsalist); + if ((d = c->dsalist.freechain) == 0) { + d = xalloc(sizeof(*d)); + if (DEBUG(1)) { + KPRINT(PRINTPREFIX "%d/%d: allocated new dsa %lux\n", target, lun, (ulong)d); + } + lesetl(d->next, 0); + lesetl(&d->stateb, A_STATE_ALLOCATED); + if (legetl(c->dsalist.head) == 0) + lesetl(c->dsalist.head, DMASEG(d)); /* ATOMIC?!? */ + else + lesetl(c->dsalist.tail->next, DMASEG(d)); /* ATOMIC?!? */ + c->dsalist.tail = d; + } + else { + if (DEBUG(1)) { + KPRINT(PRINTPREFIX "%d/%d: reused dsa %lux\n", target, lun, (ulong)d); + } + c->dsalist.freechain = d->freechain; + lesetl(&d->stateb, A_STATE_ALLOCATED); + } + iunlock(&c->dsalist); + d->target = target; + d->lun = lun; + return d; +} + +static void +dsafree(Controller *c, Dsa *d) +{ + ilock(&c->dsalist); + d->freechain = c->dsalist.freechain; + c->dsalist.freechain = d; + lesetl(&d->stateb, A_STATE_FREE); + iunlock(&c->dsalist); +} + +static Dsa * +dsafind(Controller *c, uchar target, uchar lun, uchar state) +{ + Dsa *d; + for (d = KPTR(legetl(c->dsalist.head)); d; d = KPTR(legetl(d->next))) { + if (d->target != 0xff && d->target != target) + continue; + if (lun != 0xff && d->lun != lun) + continue; + if (state != 0xff && d->stateb != state) + continue; + break; + } + return d; +} + +static void +dumpncrregs(Controller *c, int intr) +{ + int i; + Ncr *n = c->n; + int depth = c->v->registers / 4; + + if (intr) { + IPRINT("sa = %.8lux\n", c->scriptpa); + } + else { + KPRINT("sa = %.8lux\n", c->scriptpa); + } + for (i = 0; i < depth; i++) { + int j; + for (j = 0; j < 4; j++) { + int k = j * depth + i; + uchar *p; + + /* display little-endian to make 32-bit values readable */ + p = (uchar*)n+k*4; + if (intr) { + IPRINT(" %.2x%.2x%.2x%.2x %.2x %.2x", p[3], p[2], p[1], p[0], k * 4, (k * 4) + 0x80); + } + else { + KPRINT(" %.2x%.2x%.2x%.2x %.2x %.2x", p[3], p[2], p[1], p[0], k * 4, (k * 4) + 0x80); + } + USED(p); + } + if (intr) { + IPRINT("\n"); + } + else { + KPRINT("\n"); + } + } +} + +static int +chooserate(Controller *c, int tpf, int *scfp, int *xferpp) +{ + /* find lowest entry >= tpf */ + int besttpf = 1000; + int bestscfi = 0; + int bestxferp = 0; + int scf, xferp; + int maxscf; + + if (c->v->feature & Ultra2) + maxscf = NULTRA2SCF; + else if (c->v->feature & Ultra) + maxscf = NULTRASCF; + else + maxscf = NSCF; + + /* + * search large clock factors first since this should + * result in more reliable transfers + */ + for (scf = maxscf; scf >= 1; scf--) { + for (xferp = 0; xferp < 8; xferp++) { + unsigned char v = c->synctab[scf - 1][xferp]; + if (v == 0) + continue; + if (v >= tpf && v < besttpf) { + besttpf = v; + bestscfi = scf; + bestxferp = xferp; + } + } + } + if (besttpf == 1000) + return 0; + if (scfp) + *scfp = bestscfi; + if (xferpp) + *xferpp = bestxferp; + return besttpf; +} + +static void +synctabinit(Controller *c) +{ + int scf; + unsigned long scsilimit; + int xferp; + unsigned long cr, sr; + int tpf; + int fast; + int maxscf; + + if (c->v->feature & Ultra2) + maxscf = NULTRA2SCF; + else if (c->v->feature & Ultra) + maxscf = NULTRASCF; + else + maxscf = NSCF; + + /* + * for chips with no clock doubler, but Ultra capable (e.g. 860, or interestingly the + * first spin of the 875), assume 80MHz + * otherwise use the internal (33 Mhz) or external (40MHz) default + */ + + if ((c->v->feature & Ultra) != 0 && (c->v->feature & (ClockDouble | ClockQuad)) == 0) + c->sclk = ULTRA_NOCLOCKDOUBLE_SCLK; + else + c->sclk = SCLK; + + /* + * otherwise, if the chip is Ultra capable, but has a slow(ish) clock, + * invoke the doubler + */ + + if (SCLK <= 40000000) { + if (c->v->feature & ClockDouble) { + c->sclk *= 2; + c->clockmult = 1; + } + else if (c->v->feature & ClockQuad) { + c->sclk *= 4; + c->clockmult = 1; + } + else + c->clockmult = 0; + } + else + c->clockmult = 0; + + /* derive CCF from sclk */ + /* woebetide anyone with SCLK < 16.7 or > 80MHz */ + if (c->sclk <= 25 * MEGA) + c->ccf = 1; + else if (c->sclk <= 3750000) + c->ccf = 2; + else if (c->sclk <= 50 * MEGA) + c->ccf = 3; + else if (c->sclk <= 75 * MEGA) + c->ccf = 4; + else if ((c->v->feature & ClockDouble) && c->sclk <= 80 * MEGA) + c->ccf = 5; + else if ((c->v->feature & ClockQuad) && c->sclk <= 120 * MEGA) + c->ccf = 6; + else if ((c->v->feature & ClockQuad) && c->sclk <= 160 * MEGA) + c->ccf = 7; + + for (scf = 1; scf < maxscf; scf++) { + /* check for legal core rate */ + /* round up so we run slower for safety */ + cr = (c->sclk * 2 + cf2[scf] - 1) / cf2[scf]; + if (cr <= MAXSYNCCORERATE) { + scsilimit = MAXSYNCSCSIRATE; + fast = 0; + } + else if (cr <= MAXFASTSYNCCORERATE) { + scsilimit = MAXFASTSYNCSCSIRATE; + fast = 1; + } + else if ((c->v->feature & Ultra) && cr <= MAXULTRASYNCCORERATE) { + scsilimit = MAXULTRASYNCSCSIRATE; + fast = 2; + } + else if ((c->v->feature & Ultra2) && cr <= MAXULTRA2SYNCCORERATE) { + scsilimit = MAXULTRA2SYNCSCSIRATE; + fast = 3; + } + else + continue; + for (xferp = 11; xferp >= 4; xferp--) { + int ok; + int tp; + /* calculate scsi rate - round up again */ + /* start from sclk for accuracy */ + int totaldivide = xferp * cf2[scf]; + sr = (c->sclk * 2 + totaldivide - 1) / totaldivide; + if (sr > scsilimit) + break; + /* + * now work out transfer period + * round down now so that period is pessimistic + */ + tp = (MEGA * 1000) / sr; + /* + * bounds check it + */ + if (tp < 25 || tp > 255 * 4) + continue; + /* + * spot stupid special case for Ultra or Ultra2 + * while working out factor + */ + if (tp == 25) + tpf = 10; + else if (tp == 50) + tpf = 12; + else if (tp < 52) + continue; + else + tpf = tp / 4; + /* + * now check tpf looks sensible + * given core rate + */ + switch (fast) { + case 0: + /* scf must be ccf for SCSI 1 */ + ok = tpf >= 50 && scf == c->ccf; + break; + case 1: + ok = tpf >= 25 && tpf < 50; + break; + case 2: + /* + * must use xferp of 4, or 5 at a pinch + * for an Ultra transfer + */ + ok = xferp <= 5 && tpf >= 12 && tpf < 25; + break; + case 3: + ok = xferp == 4 && (tpf == 10 || tpf == 11); + break; + default: + ok = 0; + } + if (!ok) + continue; + c->synctab[scf - 1][xferp - 4] = tpf; + } + } + +#ifndef NO_ULTRA2 + if (c->v->feature & Ultra2) + tpf = 10; + else +#endif + if (c->v->feature & Ultra) + tpf = 12; + else + tpf = 25; + for (; tpf < 256; tpf++) { + if (chooserate(c, tpf, &scf, &xferp) == tpf) { + unsigned tp = tpf == 10 ? 25 : (tpf == 12 ? 50 : tpf * 4); + unsigned long khz = (MEGA + tp - 1) / (tp); + KPRINT(PRINTPREFIX "tpf=%d scf=%d.%.1d xferp=%d mhz=%ld.%.3ld\n", + tpf, cf2[scf] / 2, (cf2[scf] & 1) ? 5 : 0, + xferp + 4, khz / 1000, khz % 1000); + USED(khz); + if (c->tpf == 0) + c->tpf = tpf; /* note lowest value for controller */ + } + } +} + +static void +synctodsa(Dsa *dsa, Controller *c) +{ +/* + KPRINT("synctodsa(dsa=%lux, target=%d, scntl3=%.2lx sxfer=%.2x)\n", + dsa, dsa->target, c->scntl3[dsa->target], c->sxfer[dsa->target]); +*/ + dsa->scntl3 = c->scntl3[dsa->target]; + dsa->sxfer = c->sxfer[dsa->target]; +} + +static void +setsync(Dsa *dsa, Controller *c, int target, uchar ultra, uchar scf, uchar xferp, uchar reqack) +{ + c->scntl3[target] = + (c->scntl3[target] & 0x08) | (((scf << 4) | c->ccf | (ultra << 7)) & ~0x08); + c->sxfer[target] = (xferp << 5) | reqack; + c->s[target] = BothDone; + if (dsa) { + synctodsa(dsa, c); + c->n->scntl3 = c->scntl3[target]; + c->n->sxfer = c->sxfer[target]; + } +} + +static void +setasync(Dsa *dsa, Controller *c, int target) +{ + setsync(dsa, c, target, 0, c->ccf, 0, 0); +} + +static void +setwide(Dsa *dsa, Controller *c, int target, uchar wide) +{ + c->scntl3[target] = wide ? (1 << 3) : 0; + setasync(dsa, c, target); + c->s[target] = WideDone; +} + +static int +buildsdtrmsg(uchar *buf, uchar tpf, uchar offset) +{ + *buf++ = X_MSG; + *buf++ = 3; + *buf++ = X_MSG_SDTR; + *buf++ = tpf; + *buf = offset; + return 5; +} + +static int +buildwdtrmsg(uchar *buf, uchar expo) +{ + *buf++ = X_MSG; + *buf++ = 2; + *buf++ = X_MSG_WDTR; + *buf = expo; + return 4; +} + +static void +start(Controller *c, long entry) +{ + ulong p; + + if (c->running) + panic(PRINTPREFIX "start called while running"); + c->running = 1; + p = c->scriptpa + entry; + lesetl(c->n->dsp, p); + if (c->ssm) + c->n->dcntl |= 0x4; /* start DMA in SSI mode */ +} + +static void +ncrcontinue(Controller *c) +{ + if (c->running) + panic(PRINTPREFIX "ncrcontinue called while running"); + /* set the start DMA bit to continue execution */ + c->running = 1; + c->n->dcntl |= 0x4; +} + +static void +softreset(Controller *c) +{ + Ncr *n = c->n; + + n->istat = Srst; /* software reset */ + n->istat = 0; + /* general initialisation */ + n->scid = (1 << 6) | 7; /* respond to reselect, ID 7 */ + n->respid = 1 << 7; /* response ID = 7 */ + +#ifdef INTERNAL_SCLK + n->stest1 = 0x80; /* disable external scsi clock */ +#else + n->stest1 = 0x00; +#endif + + n->stime0 = 0xdd; /* about 0.5 second timeout on each device */ + n->scntl0 |= 0x8; /* Enable parity checking */ + + /* continued setup */ + n->sien0 = 0x8f; + n->sien1 = 0x04; + n->dien = 0x7d; + n->stest3 = 0x80; /* TolerANT enable */ + c->running = 0; + + if (c->v->feature & BigFifo) + n->ctest5 = (1 << 5); + n->dmode = c->v->burst << 6; /* set burst length bits */ + if (c->v->burst & 4) + n->ctest5 |= (1 << 2); /* including overflow into ctest5 bit 2 */ + if (c->v->feature & Prefetch) + n->dcntl |= (1 << 5); /* prefetch enable */ + else if (c->v->feature & BurstOpCodeFetch) + n->dmode |= (1 << 1); /* burst opcode fetch */ + if (c->v->feature & Differential) { + /* chip capable */ + if ((c->feature & Differential) || bios_set_differential(c)) { + /* user enabled, or some evidence bios set differential */ + if (n->sstat2 & (1 << 2)) + print(PRINTPREFIX "can't go differential; wrong cable\n"); + else { + n->stest2 = (1 << 5); + print(PRINTPREFIX "differential mode set\n"); + } + } + } + if (c->clockmult) { + n->stest1 |= (1 << 3); /* power up doubler */ + delay(2); + n->stest3 |= (1 << 5); /* stop clock */ + n->stest1 |= (1 << 2); /* enable doubler */ + n->stest3 &= ~(1 << 5); /* start clock */ + /* pray */ + } +} + +static void +msgsm(Dsa *dsa, Controller *c, int msg, int *cont, int *wakeme) +{ + uchar histpf, hisreqack; + int tpf; + int scf, xferp; + int len; + + Ncr *n = c->n; + + switch (c->s[dsa->target]) { + case SyncInit: + switch (msg) { + case A_SIR_MSG_SDTR: + /* reply to my SDTR */ + histpf = n->scratcha[2]; + hisreqack = n->scratcha[3]; + KPRINT(PRINTPREFIX "%d: SDTN response %d %d\n", + dsa->target, histpf, hisreqack); + + if (hisreqack == 0) + setasync(dsa, c, dsa->target); + else { + /* hisreqack should be <= c->v->maxsyncoff */ + tpf = chooserate(c, histpf, &scf, &xferp); + KPRINT(PRINTPREFIX "%d: SDTN: using %d %d\n", + dsa->target, tpf, hisreqack); + setsync(dsa, c, dsa->target, tpf < 25, scf, xferp, hisreqack); + } + *cont = -2; + return; + case A_SIR_EV_PHASE_SWITCH_AFTER_ID: + /* target ignored ATN for message after IDENTIFY - not SCSI-II */ + KPRINT(PRINTPREFIX "%d: illegal phase switch after ID message - SCSI-1 device?\n", dsa->target); + KPRINT(PRINTPREFIX "%d: SDTN: async\n", dsa->target); + setasync(dsa, c, dsa->target); + *cont = E_to_decisions; + return; + case A_SIR_MSG_REJECT: + /* rejection of my SDTR */ + KPRINT(PRINTPREFIX "%d: SDTN: rejected SDTR\n", dsa->target); + //async: + KPRINT(PRINTPREFIX "%d: SDTN: async\n", dsa->target); + setasync(dsa, c, dsa->target); + *cont = -2; + return; + } + break; + case WideInit: + switch (msg) { + case A_SIR_MSG_WDTR: + /* reply to my WDTR */ + KPRINT(PRINTPREFIX "%d: WDTN: response %d\n", + dsa->target, n->scratcha[2]); + setwide(dsa, c, dsa->target, n->scratcha[2]); + *cont = -2; + return; + case A_SIR_EV_PHASE_SWITCH_AFTER_ID: + /* target ignored ATN for message after IDENTIFY - not SCSI-II */ + KPRINT(PRINTPREFIX "%d: illegal phase switch after ID message - SCSI-1 device?\n", dsa->target); + setwide(dsa, c, dsa->target, 0); + *cont = E_to_decisions; + return; + case A_SIR_MSG_REJECT: + /* rejection of my SDTR */ + KPRINT(PRINTPREFIX "%d: WDTN: rejected WDTR\n", dsa->target); + setwide(dsa, c, dsa->target, 0); + *cont = -2; + return; + } + break; + + case NeitherDone: + case WideDone: + case BothDone: + switch (msg) { + case A_SIR_MSG_WDTR: { + uchar hiswide, mywide; + hiswide = n->scratcha[2]; + mywide = (c->v->feature & Wide) != 0; + KPRINT(PRINTPREFIX "%d: WDTN: target init %d\n", + dsa->target, hiswide); + if (hiswide < mywide) + mywide = hiswide; + KPRINT(PRINTPREFIX "%d: WDTN: responding %d\n", + dsa->target, mywide); + setwide(dsa, c, dsa->target, mywide); + len = buildwdtrmsg(dsa->msg_out, mywide); + setmovedata(&dsa->msg_out_buf, DMASEG(dsa->msg_out), len); + *cont = E_response; + c->s[dsa->target] = WideResponse; + return; + } + case A_SIR_MSG_SDTR: +#ifdef ASYNC_ONLY + *cont = E_reject; + return; +#else + /* target decides to renegotiate */ + histpf = n->scratcha[2]; + hisreqack = n->scratcha[3]; + KPRINT(PRINTPREFIX "%d: SDTN: target init %d %d\n", + dsa->target, histpf, hisreqack); + if (hisreqack == 0) { + /* he wants asynchronous */ + setasync(dsa, c, dsa->target); + tpf = 0; + } + else { + /* he wants synchronous */ + tpf = chooserate(c, histpf, &scf, &xferp); + if (hisreqack > c->v->maxsyncoff) + hisreqack = c->v->maxsyncoff; + KPRINT(PRINTPREFIX "%d: using %d %d\n", + dsa->target, tpf, hisreqack); + setsync(dsa, c, dsa->target, tpf < 25, scf, xferp, hisreqack); + } + /* build my SDTR message */ + len = buildsdtrmsg(dsa->msg_out, tpf, hisreqack); + setmovedata(&dsa->msg_out_buf, DMASEG(dsa->msg_out), len); + *cont = E_response; + c->s[dsa->target] = SyncResponse; + return; +#endif + } + break; + case WideResponse: + switch (msg) { + case A_SIR_EV_RESPONSE_OK: + c->s[dsa->target] = WideDone; + KPRINT(PRINTPREFIX "%d: WDTN: response accepted\n", dsa->target); + *cont = -2; + return; + case A_SIR_MSG_REJECT: + setwide(dsa, c, dsa->target, 0); + KPRINT(PRINTPREFIX "%d: WDTN: response REJECTed\n", dsa->target); + *cont = -2; + return; + } + break; + case SyncResponse: + switch (msg) { + case A_SIR_EV_RESPONSE_OK: + c->s[dsa->target] = BothDone; + KPRINT(PRINTPREFIX "%d: SDTN: response accepted (%s)\n", + dsa->target, phase[n->sstat1 & 7]); + *cont = -2; + return; /* chf */ + case A_SIR_MSG_REJECT: + setasync(dsa, c, dsa->target); + KPRINT(PRINTPREFIX "%d: SDTN: response REJECTed\n", dsa->target); + *cont = -2; + return; + } + break; + } + KPRINT(PRINTPREFIX "%d: msgsm: state %d msg %d\n", + dsa->target, c->s[dsa->target], msg); + *wakeme = 1; + return; +} + +static void +calcblockdma(Dsa *d, ulong base, ulong count) +{ + ulong blocks; + if (DEBUG(3)) + blocks = 0; + else { + blocks = count / A_BSIZE; + if (blocks > 255) + blocks = 255; + } + d->dmablks = blocks; + d->dmaaddr[0] = base; + d->dmaaddr[1] = base >> 8; + d->dmaaddr[2] = base >> 16; + d->dmaaddr[3] = base >> 24; + setmovedata(&d->data_buf, base + blocks * A_BSIZE, count - blocks * A_BSIZE); + d->flag = legetl(d->data_buf.dbc) == 0; +} + +static ulong +read_mismatch_recover(Controller *c, Ncr *n, Dsa *dsa) +{ + ulong dbc; + uchar dfifo = n->dfifo; + int inchip; + + dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0]; + if (n->ctest5 & (1 << 5)) + inchip = ((dfifo | ((n->ctest5 & 3) << 8)) - (dbc & 0x3ff)) & 0x3ff; + else + inchip = ((dfifo & 0x7f) - (dbc & 0x7f)) & 0x7f; + if (inchip) { + IPRINT(PRINTPREFIX "%d/%d: read_mismatch_recover: DMA FIFO = %d\n", + dsa->target, dsa->lun, inchip); + } + if (n->sxfer & SYNCOFFMASK(c)) { + /* SCSI FIFO */ + uchar fifo = n->sstat1 >> 4; + if (c->v->maxsyncoff > 8) + fifo |= (n->sstat2 & (1 << 4)); + if (fifo) { + inchip += fifo; + IPRINT(PRINTPREFIX "%d/%d: read_mismatch_recover: SCSI FIFO = %d\n", + dsa->target, dsa->lun, fifo); + } + } + else { + if (n->sstat0 & (1 << 7)) { + inchip++; + IPRINT(PRINTPREFIX "%d/%d: read_mismatch_recover: SIDL full\n", + dsa->target, dsa->lun); + } + if (n->sstat2 & (1 << 7)) { + inchip++; + IPRINT(PRINTPREFIX "%d/%d: read_mismatch_recover: SIDL msb full\n", + dsa->target, dsa->lun); + } + } + USED(inchip); + return dbc; +} + +static ulong +write_mismatch_recover(Controller *c, Ncr *n, Dsa *dsa) +{ + ulong dbc; + uchar dfifo = n->dfifo; + int inchip; + + dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0]; + USED(dsa); + if (n->ctest5 & (1 << 5)) + inchip = ((dfifo | ((n->ctest5 & 3) << 8)) - (dbc & 0x3ff)) & 0x3ff; + else + inchip = ((dfifo & 0x7f) - (dbc & 0x7f)) & 0x7f; +#ifdef WMR_DEBUG + if (inchip) { + IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: DMA FIFO = %d\n", + dsa->target, dsa->lun, inchip); + } +#endif + if (n->sstat0 & (1 << 5)) { + inchip++; +#ifdef WMR_DEBUG + IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: SODL full\n", dsa->target, dsa->lun); +#endif + } + if (n->sstat2 & (1 << 5)) { + inchip++; +#ifdef WMR_DEBUG + IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: SODL msb full\n", dsa->target, dsa->lun); +#endif + } + if (n->sxfer & SYNCOFFMASK(c)) { + /* synchronous SODR */ + if (n->sstat0 & (1 << 6)) { + inchip++; +#ifdef WMR_DEBUG + IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: SODR full\n", + dsa->target, dsa->lun); +#endif + } + if (n->sstat2 & (1 << 6)) { + inchip++; +#ifdef WMR_DEBUG + IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: SODR msb full\n", + dsa->target, dsa->lun); +#endif + } + } + /* clear the dma fifo */ + n->ctest3 |= (1 << 2); + /* wait till done */ + while ((n->dstat & Dfe) == 0) + ; + return dbc + inchip; +} + +static void +sd53c8xxinterrupt(Ureg *ur, void *a) +{ + uchar istat; + ushort sist; + uchar dstat; + int wakeme = 0; + int cont = -1; + Dsa *dsa; + Controller *c = a; + Ncr *n = c->n; + + USED(ur); + if (DEBUG(1)) { + IPRINT(PRINTPREFIX "int\n"); + } + ilock(c); + istat = n->istat; + if (istat & Intf) { + Dsa *d; + int wokesomething = 0; + if (DEBUG(1)) { + IPRINT(PRINTPREFIX "Intfly\n"); + } + n->istat = Intf; + /* search for structures in A_STATE_DONE */ + for (d = KPTR(legetl(c->dsalist.head)); d; d = KPTR(legetl(d->next))) { + if (d->stateb == A_STATE_DONE) { + d->p9status = d->status; + if (DEBUG(1)) { + IPRINT(PRINTPREFIX "waking up dsa %lux\n", (ulong)d); + } + wakeup(d); + wokesomething = 1; + } + } + if (!wokesomething) { + IPRINT(PRINTPREFIX "nothing to wake up\n"); + } + } + + if ((istat & (Sip | Dip)) == 0) { + if (DEBUG(1)) { + IPRINT(PRINTPREFIX "int end %x\n", istat); + } + iunlock(c); + return; + } + + sist = (n->sist1<<8)|n->sist0; /* BUG? can two-byte read be inconsistent? */ + dstat = n->dstat; + dsa = (Dsa *)DMASEG_TO_KADDR(legetl(n->dsa)); + c->running = 0; + if (istat & Sip) { + if (DEBUG(1)) { + IPRINT("sist = %.4x\n", sist); + } + if (sist & 0x80) { + ulong addr; + ulong sa; + ulong dbc; + ulong tbc; + int dmablks; + ulong dmaaddr; + + addr = legetl(n->dsp); + sa = addr - c->scriptpa; + if (DEBUG(1) || DEBUG(2)) { + IPRINT(PRINTPREFIX "%d/%d: Phase Mismatch sa=%.8lux\n", + dsa->target, dsa->lun, sa); + } + /* + * now recover + */ + if (sa == E_data_in_mismatch) { + /* + * though this is a failure in the residue, there may have been blocks + * as well. if so, dmablks will not have been zeroed, since the state + * was not saved by the microcode. + */ + dbc = read_mismatch_recover(c, n, dsa); + tbc = legetl(dsa->data_buf.dbc) - dbc; + dsa->dmablks = 0; + n->scratcha[2] = 0; + advancedata(&dsa->data_buf, tbc); + if (DEBUG(1) || DEBUG(2)) { + IPRINT(PRINTPREFIX "%d/%d: transferred = %ld residue = %ld\n", + dsa->target, dsa->lun, tbc, legetl(dsa->data_buf.dbc)); + } + cont = E_data_mismatch_recover; + } + else if (sa == E_data_in_block_mismatch) { + dbc = read_mismatch_recover(c, n, dsa); + tbc = A_BSIZE - dbc; + /* recover current state from registers */ + dmablks = n->scratcha[2]; + dmaaddr = legetl(n->scratchb); + /* we have got to dmaaddr + tbc */ + /* we have dmablks * A_BSIZE - tbc + residue left to do */ + /* so remaining transfer is */ + IPRINT("in_block_mismatch: dmaaddr = 0x%lux tbc=%lud dmablks=%d\n", + dmaaddr, tbc, dmablks); + calcblockdma(dsa, dmaaddr + tbc, + dmablks * A_BSIZE - tbc + legetl(dsa->data_buf.dbc)); + /* copy changes into scratch registers */ + IPRINT("recalc: dmablks %d dmaaddr 0x%lx pa 0x%lx dbc %ld\n", + dsa->dmablks, legetl(dsa->dmaaddr), + legetl(dsa->data_buf.pa), legetl(dsa->data_buf.dbc)); + n->scratcha[2] = dsa->dmablks; + lesetl(n->scratchb, dsa->dmancr); + cont = E_data_block_mismatch_recover; + } + else if (sa == E_data_out_mismatch) { + dbc = write_mismatch_recover(c, n, dsa); + tbc = legetl(dsa->data_buf.dbc) - dbc; + dsa->dmablks = 0; + n->scratcha[2] = 0; + advancedata(&dsa->data_buf, tbc); + if (DEBUG(1) || DEBUG(2)) { + IPRINT(PRINTPREFIX "%d/%d: transferred = %ld residue = %ld\n", + dsa->target, dsa->lun, tbc, legetl(dsa->data_buf.dbc)); + } + cont = E_data_mismatch_recover; + } + else if (sa == E_data_out_block_mismatch) { + dbc = write_mismatch_recover(c, n, dsa); + tbc = legetl(dsa->data_buf.dbc) - dbc; + /* recover current state from registers */ + dmablks = n->scratcha[2]; + dmaaddr = legetl(n->scratchb); + /* we have got to dmaaddr + tbc */ + /* we have dmablks blocks - tbc + residue left to do */ + /* so remaining transfer is */ + IPRINT("out_block_mismatch: dmaaddr = %lux tbc=%lud dmablks=%d\n", + dmaaddr, tbc, dmablks); + calcblockdma(dsa, dmaaddr + tbc, + dmablks * A_BSIZE - tbc + legetl(dsa->data_buf.dbc)); + /* copy changes into scratch registers */ + n->scratcha[2] = dsa->dmablks; + lesetl(n->scratchb, dsa->dmancr); + cont = E_data_block_mismatch_recover; + } + else if (sa == E_id_out_mismatch) { + /* + * target switched phases while attention held during + * message out. The possibilities are: + * 1. It didn't like the last message. This is indicated + * by the new phase being message_in. Use script to recover + * + * 2. It's not SCSI-II compliant. The new phase will be other + * than message_in. We should also indicate that the device + * is asynchronous, if it's the SDTR that got ignored + * + * For now, if the phase switch is not to message_in, and + * and it happens after IDENTIFY and before SDTR, we + * notify the negotiation state machine. + */ + ulong lim = legetl(dsa->msg_out_buf.dbc); + uchar p = n->sstat1 & 7; + dbc = write_mismatch_recover(c, n, dsa); + tbc = lim - dbc; + IPRINT(PRINTPREFIX "%d/%d: msg_out_mismatch: %lud/%lud sent, phase %s\n", + dsa->target, dsa->lun, tbc, lim, phase[p]); + if (p != MessageIn && tbc == 1) { + msgsm(dsa, c, A_SIR_EV_PHASE_SWITCH_AFTER_ID, &cont, &wakeme); + } + else + cont = E_id_out_mismatch_recover; + } + else if (sa == E_cmd_out_mismatch) { + /* + * probably the command count is longer than the device wants ... + */ + ulong lim = legetl(dsa->cmd_buf.dbc); + uchar p = n->sstat1 & 7; + dbc = write_mismatch_recover(c, n, dsa); + tbc = lim - dbc; + IPRINT(PRINTPREFIX "%d/%d: cmd_out_mismatch: %lud/%lud sent, phase %s\n", + dsa->target, dsa->lun, tbc, lim, phase[p]); + USED(p, tbc); + cont = E_to_decisions; + } + else { + IPRINT(PRINTPREFIX "%d/%d: ma sa=%.8lux wanted=%s got=%s\n", + dsa->target, dsa->lun, sa, + phase[n->dcmd & 7], + phase[n->sstat1 & 7]); + dumpncrregs(c, 1); + dsa->p9status = SDeio; /* chf */ + wakeme = 1; + } + } + /*else*/ if (sist & 0x400) { + if (DEBUG(0)) { + IPRINT(PRINTPREFIX "%d/%d Sto\n", dsa->target, dsa->lun); + } + dsa->p9status = SDtimeout; + dsa->stateb = A_STATE_DONE; + softreset(c); + cont = E_issue_check; + wakeme = 1; + } + if (sist & 0x1) { + IPRINT(PRINTPREFIX "%d/%d: parity error\n", dsa->target, dsa->lun); + dsa->parityerror = 1; + } + if (sist & 0x4) { + IPRINT(PRINTPREFIX "%d/%d: unexpected disconnect\n", + dsa->target, dsa->lun); + dumpncrregs(c, 1); + //wakeme = 1; + dsa->p9status = SDeio; + } + } + if (istat & Dip) { + if (DEBUG(1)) { + IPRINT("dstat = %.2x\n", dstat); + } + /*else*/ if (dstat & Ssi) { + ulong *p = DMASEG_TO_KADDR(legetl(n->dsp)); + ulong w = (uchar *)p - (uchar *)c->script; + IPRINT("[%lux]", w); + USED(w); + cont = -2; /* restart */ + } + if (dstat & Sir) { + switch (legetl(n->dsps)) { + case A_SIR_MSG_IO_COMPLETE: + dsa->p9status = dsa->status; + wakeme = 1; + break; + case A_SIR_MSG_SDTR: + case A_SIR_MSG_WDTR: + case A_SIR_MSG_REJECT: + case A_SIR_EV_RESPONSE_OK: + msgsm(dsa, c, legetl(n->dsps), &cont, &wakeme); + break; + case A_SIR_MSG_IGNORE_WIDE_RESIDUE: + /* back up one in the data transfer */ + IPRINT(PRINTPREFIX "%d/%d: ignore wide residue %d, WSR = %d\n", + dsa->target, dsa->lun, n->scratcha[1], n->scntl2 & 1); + if (dsa->flag == 2) { + IPRINT(PRINTPREFIX "%d/%d: transfer over; residue ignored\n", + dsa->target, dsa->lun); + } + else { + calcblockdma(dsa, legetl(dsa->dmaaddr) - 1, + dsa->dmablks * A_BSIZE + legetl(dsa->data_buf.dbc) + 1); + } + cont = -2; + break; + case A_SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT: + IPRINT(PRINTPREFIX "%d: not msg_in after reselect (%s)", + n->ssid & SSIDMASK(c), phase[n->sstat1 & 7]); + dsa = dsafind(c, n->ssid & SSIDMASK(c), -1, A_STATE_DISCONNECTED); + dumpncrregs(c, 1); + wakeme = 1; + break; + case A_SIR_NOTIFY_MSG_IN: + IPRINT(PRINTPREFIX "%d/%d: msg_in %d\n", + dsa->target, dsa->lun, n->sfbr); + cont = -2; + break; + case A_SIR_NOTIFY_DISC: + IPRINT(PRINTPREFIX "%d/%d: disconnect:", dsa->target, dsa->lun); + goto dsadump; + case A_SIR_NOTIFY_STATUS: + IPRINT(PRINTPREFIX "%d/%d: status\n", dsa->target, dsa->lun); + cont = -2; + break; + case A_SIR_NOTIFY_COMMAND: + IPRINT(PRINTPREFIX "%d/%d: commands\n", dsa->target, dsa->lun); + cont = -2; + break; + case A_SIR_NOTIFY_DATA_IN: + IPRINT(PRINTPREFIX "%d/%d: data in a %lx b %lx\n", + dsa->target, dsa->lun, legetl(n->scratcha), legetl(n->scratchb)); + cont = -2; + break; + case A_SIR_NOTIFY_BLOCK_DATA_IN: + IPRINT(PRINTPREFIX "%d/%d: block data in: a2 %x b %lx\n", + dsa->target, dsa->lun, n->scratcha[2], legetl(n->scratchb)); + cont = -2; + break; + case A_SIR_NOTIFY_DATA_OUT: + IPRINT(PRINTPREFIX "%d/%d: data out\n", dsa->target, dsa->lun); + cont = -2; + break; + case A_SIR_NOTIFY_DUMP: + IPRINT(PRINTPREFIX "%d/%d: dump\n", dsa->target, dsa->lun); + dumpncrregs(c, 1); + cont = -2; + break; + case A_SIR_NOTIFY_DUMP2: + IPRINT(PRINTPREFIX "%d/%d: dump2:", dsa->target, dsa->lun); + IPRINT(" sa %lux", legetl(n->dsp) - c->scriptpa); + IPRINT(" dsa %lux", legetl(n->dsa)); + IPRINT(" sfbr %ux", n->sfbr); + IPRINT(" a %lux", legetl(n->scratcha)); + IPRINT(" b %lux", legetl(n->scratchb)); + IPRINT(" ssid %ux", n->ssid); + IPRINT("\n"); + cont = -2; + break; + case A_SIR_NOTIFY_WAIT_RESELECT: + IPRINT(PRINTPREFIX "wait reselect\n"); + cont = -2; + break; + case A_SIR_NOTIFY_RESELECT: + IPRINT(PRINTPREFIX "reselect: ssid %.2x sfbr %.2x at %ld\n", + n->ssid, n->sfbr, TK2MS(m->ticks)); + cont = -2; + break; + case A_SIR_NOTIFY_ISSUE: + IPRINT(PRINTPREFIX "%d/%d: issue:", dsa->target, dsa->lun); + dsadump: + IPRINT(" tgt=%d", dsa->target); + IPRINT(" time=%ld", TK2MS(m->ticks)); + IPRINT("\n"); + cont = -2; + break; + case A_SIR_NOTIFY_ISSUE_CHECK: + IPRINT(PRINTPREFIX "issue check\n"); + cont = -2; + break; + case A_SIR_NOTIFY_SIGP: + IPRINT(PRINTPREFIX "responded to SIGP\n"); + cont = -2; + break; + case A_SIR_NOTIFY_DUMP_NEXT_CODE: { + ulong *dsp = DMASEG_TO_KADDR(legetl(n->dsp)); + int x; + IPRINT(PRINTPREFIX "code at %lux", dsp - c->script); + for (x = 0; x < 6; x++) { + IPRINT(" %.8lux", dsp[x]); + } + IPRINT("\n"); + USED(dsp); + cont = -2; + break; + } + case A_SIR_NOTIFY_WSR: + IPRINT(PRINTPREFIX "%d/%d: WSR set\n", dsa->target, dsa->lun); + cont = -2; + break; + case A_SIR_NOTIFY_LOAD_SYNC: + IPRINT(PRINTPREFIX "%d/%d: scntl=%.2x sxfer=%.2x\n", + dsa->target, dsa->lun, n->scntl3, n->sxfer); + cont = -2; + break; + case A_SIR_NOTIFY_RESELECTED_ON_SELECT: + if (DEBUG(2)) { + IPRINT(PRINTPREFIX "%d/%d: reselected during select\n", + dsa->target, dsa->lun); + } + cont = -2; + break; + case A_error_reselected: /* dsa isn't valid here */ + print(PRINTPREFIX "reselection error\n"); + dumpncrregs(c, 1); + for (dsa = KPTR(legetl(c->dsalist.head)); dsa; dsa = KPTR(legetl(dsa->next))) { + IPRINT(PRINTPREFIX "dsa target %d lun %d state %d\n", dsa->target, dsa->lun, dsa->stateb); + } + break; + default: + IPRINT(PRINTPREFIX "%d/%d: script error %ld\n", + dsa->target, dsa->lun, legetl(n->dsps)); + dumpncrregs(c, 1); + wakeme = 1; + } + } + /*else*/ if (dstat & Iid) { + ulong addr = legetl(n->dsp); + ulong dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0]; + IPRINT(PRINTPREFIX "%d/%d: Iid pa=%.8lux sa=%.8lux dbc=%lux\n", + dsa->target, dsa->lun, + addr, addr - c->scriptpa, dbc); + addr = (ulong)DMASEG_TO_KADDR(addr); + IPRINT("%.8lux %.8lux %.8lux\n", + *(ulong *)(addr - 12), *(ulong *)(addr - 8), *(ulong *)(addr - 4)); + USED(addr, dbc); + dsa->p9status = SDeio; + wakeme = 1; + } + /*else*/ if (dstat & Bf) { + IPRINT(PRINTPREFIX "%d/%d: Bus Fault\n", dsa->target, dsa->lun); + dumpncrregs(c, 1); + dsa->p9status = SDeio; + wakeme = 1; + } + } + if (cont == -2) + ncrcontinue(c); + else if (cont >= 0) + start(c, cont); + if (wakeme){ + if(dsa->p9status == SDnostatus) + dsa->p9status = SDeio; + wakeup(dsa); + } + iunlock(c); + if (DEBUG(1)) { + IPRINT(PRINTPREFIX "int end 1\n"); + } +} + +static int +done(void *arg) +{ + return ((Dsa *)arg)->p9status != SDnostatus; +} + +static void +setmovedata(Movedata *d, ulong pa, ulong bc) +{ + d->pa[0] = pa; + d->pa[1] = pa>>8; + d->pa[2] = pa>>16; + d->pa[3] = pa>>24; + d->dbc[0] = bc; + d->dbc[1] = bc>>8; + d->dbc[2] = bc>>16; + d->dbc[3] = bc>>24; +} + +static void +advancedata(Movedata *d, long v) +{ + lesetl(d->pa, legetl(d->pa) + v); + lesetl(d->dbc, legetl(d->dbc) - v); +} + +static void +dumpwritedata(uchar *data, int datalen) +{ + int i; + uchar *bp; + if (!DEBUG(0)){ + USED(data, datalen); + return; + } + + if (datalen) { + KPRINT(PRINTPREFIX "write:"); + for (i = 0, bp = data; i < 50 && i < datalen; i++, bp++) { + KPRINT("%.2ux", *bp); + } + if (i < datalen) { + KPRINT("..."); + } + KPRINT("\n"); + } +} + +static void +dumpreaddata(uchar *data, int datalen) +{ + int i; + uchar *bp; + if (!DEBUG(0)){ + USED(data, datalen); + return; + } + + if (datalen) { + KPRINT(PRINTPREFIX "read:"); + for (i = 0, bp = data; i < 50 && i < datalen; i++, bp++) { + KPRINT("%.2ux", *bp); + } + if (i < datalen) { + KPRINT("..."); + } + KPRINT("\n"); + } +} + +static void +busreset(Controller *c) +{ + int x, ntarget; + + /* bus reset */ + c->n->scntl1 |= (1 << 3); + delay(500); + c->n->scntl1 &= ~(1 << 3); + if(!(c->v->feature & Wide)) + ntarget = 8; + else + ntarget = MAXTARGET; + for (x = 0; x < ntarget; x++) { + setwide(0, c, x, 0); +#ifndef ASYNC_ONLY + c->s[x] = NeitherDone; +#endif + } + c->capvalid = 0; +} + +static void +reset(Controller *c) +{ + /* should wakeup all pending tasks */ + softreset(c); + busreset(c); +} + +static int +sd53c8xxrio(SDreq* r) +{ + Dsa *d; + uchar *bp; + Controller *c; + uchar target_expo, my_expo; + int bc, check, i, status, target; + + if((target = r->unit->subno) == 0x07) + return r->status = SDtimeout; /* assign */ + c = r->unit->dev->ctlr; + + check = 0; + d = dsaalloc(c, target, r->lun); + + qlock(&c->q[target]); /* obtain access to target */ +docheck: + /* load the transfer control stuff */ + d->scsi_id_buf[0] = 0; + d->scsi_id_buf[1] = c->sxfer[target]; + d->scsi_id_buf[2] = target; + d->scsi_id_buf[3] = c->scntl3[target]; + synctodsa(d, c); + + bc = 0; + + d->msg_out[bc] = 0x80 | r->lun; + +#ifndef NO_DISCONNECT + d->msg_out[bc] |= (1 << 6); +#endif + bc++; + + /* work out what to do about negotiation */ + switch (c->s[target]) { + default: + KPRINT(PRINTPREFIX "%d: strange nego state %d\n", target, c->s[target]); + c->s[target] = NeitherDone; + /* fall through */ + case NeitherDone: + if ((c->capvalid & (1 << target)) == 0) + break; + target_expo = (c->cap[target] >> 5) & 3; + my_expo = (c->v->feature & Wide) != 0; + if (target_expo < my_expo) + my_expo = target_expo; +#ifdef ALWAYS_DO_WDTR + bc += buildwdtrmsg(d->msg_out + bc, my_expo); + KPRINT(PRINTPREFIX "%d: WDTN: initiating expo %d\n", target, my_expo); + c->s[target] = WideInit; + break; +#else + if (my_expo) { + bc += buildwdtrmsg(d->msg_out + bc, (c->v->feature & Wide) ? 1 : 0); + KPRINT(PRINTPREFIX "%d: WDTN: initiating expo %d\n", target, my_expo); + c->s[target] = WideInit; + break; + } + KPRINT(PRINTPREFIX "%d: WDTN: narrow\n", target); + /* fall through */ +#endif + case WideDone: + if (c->cap[target] & (1 << 4)) { + KPRINT(PRINTPREFIX "%d: SDTN: initiating %d %d\n", target, c->tpf, c->v->maxsyncoff); + bc += buildsdtrmsg(d->msg_out + bc, c->tpf, c->v->maxsyncoff); + c->s[target] = SyncInit; + break; + } + KPRINT(PRINTPREFIX "%d: SDTN: async only\n", target); + c->s[target] = BothDone; + break; + + case BothDone: + break; + } + + setmovedata(&d->msg_out_buf, DMASEG(d->msg_out), bc); + setmovedata(&d->cmd_buf, DMASEG(r->cmd), r->clen); + calcblockdma(d, DMASEG(r->data), r->dlen); + + if (DEBUG(0)) { + KPRINT(PRINTPREFIX "%d/%d: exec: ", target, r->lun); + for (bp = r->cmd; bp < &r->cmd[r->clen]; bp++) { + KPRINT("%.2ux", *bp); + } + KPRINT("\n"); + if (!r->write) { + KPRINT(PRINTPREFIX "%d/%d: exec: limit=(%d)%ld\n", + target, r->lun, d->dmablks, legetl(d->data_buf.dbc)); + } + else + dumpwritedata(r->data, r->dlen); + } + + setmovedata(&d->status_buf, DMASEG(&d->status), 1); + + d->p9status = SDnostatus; + d->parityerror = 0; + + d->stateb = A_STATE_ISSUE; /* start operation */ + + ilock(c); + if (c->ssm) + c->n->dcntl |= 0x10; /* SSI */ + if (c->running) { + c->n->istat |= Sigp; + } + else { + start(c, E_issue_check); + } + iunlock(c); + + while(waserror()) + ; + tsleep(d, done, d, 600 * 1000); + poperror(); + + if (!done(d)) { + KPRINT(PRINTPREFIX "%d/%d: exec: Timed out\n", target, r->lun); + dumpncrregs(c, 0); + dsafree(c, d); + reset(c); + qunlock(&c->q[target]); + r->status = SDtimeout; + return r->status = SDtimeout; /* assign */ + } + + if((status = d->p9status) == SDeio) + c->s[target] = NeitherDone; + if (d->parityerror) { + status = SDeio; + } + + /* + * adjust datalen + */ + r->rlen = r->dlen; + if (DEBUG(0)) { + KPRINT(PRINTPREFIX "%d/%d: exec: before rlen adjust: dmablks %d flag %d dbc %lud\n", + target, r->lun, d->dmablks, d->flag, legetl(d->data_buf.dbc)); + } + r->rlen = r->dlen; + if (d->flag != 2) { + r->rlen -= d->dmablks * A_BSIZE; + r->rlen -= legetl(d->data_buf.dbc); + } + if(!r->write) + dumpreaddata(r->data, r->rlen); + if (DEBUG(0)) { + KPRINT(PRINTPREFIX "%d/%d: exec: p9status=%d status %d rlen %ld\n", + target, r->lun, d->p9status, status, r->rlen); + } + /* + * spot the identify + */ + if ((c->capvalid & (1 << target)) == 0 + && (status == SDok || status == SDcheck) + && r->cmd[0] == 0x12 && r->dlen >= 8) { + c->capvalid |= 1 << target; + bp = r->data; + c->cap[target] = bp[7]; + KPRINT(PRINTPREFIX "%d: capabilities %.2x\n", target, bp[7]); + } + if(!check && status == SDcheck && !(r->flags & SDnosense)){ + check = 1; + r->write = 0; + memset(r->cmd, 0, sizeof(r->cmd)); + r->cmd[0] = 0x03; + r->cmd[1] = r->lun<<5; + r->cmd[4] = sizeof(r->sense)-1; + r->clen = 6; + r->data = r->sense; + r->dlen = sizeof(r->sense)-1; + /* + * Clear out the microcode state + * so the Dsa can be re-used. + */ + lesetl(&d->stateb, A_STATE_ALLOCATED); + goto docheck; + } + qunlock(&c->q[target]); + dsafree(c, d); + + if(status == SDok && check){ + status = SDcheck; + r->flags |= SDvalidsense; + } + if(DEBUG(0)) + KPRINT(PRINTPREFIX "%d: r flags %8.8uX status %d rlen %ld\n", + target, r->flags, status, r->rlen); + if(r->flags & SDvalidsense){ + if(!DEBUG(0)) + KPRINT(PRINTPREFIX "%d: r flags %8.8uX status %d rlen %ld\n", + target, r->flags, status, r->rlen); + for(i = 0; i < r->rlen; i++) + KPRINT(" %2.2uX", r->sense[i]); + KPRINT("\n"); + } + return r->status = status; +} + +static void +cribbios(Controller *c) +{ + c->bios.scntl3 = c->n->scntl3; + c->bios.stest2 = c->n->stest2; + KPRINT(PRINTPREFIX "bios scntl3(%.2x) stest2(%.2x)\n", c->bios.scntl3, c->bios.stest2); +} + +static int +bios_set_differential(Controller *c) +{ + /* Concept lifted from FreeBSD - thanks Gerard */ + /* basically, if clock conversion factors are set, then there is + * evidence the bios had a go at the chip, and if so, it would + * have set the differential enable bit in stest2 + */ + return (c->bios.scntl3 & 7) != 0 && (c->bios.stest2 & 0x20) != 0; +} + +#define NCR_VID 0x1000 +#define NCR_810_DID 0x0001 +#define NCR_820_DID 0x0002 /* don't know enough about this one to support it */ +#define NCR_825_DID 0x0003 +#define NCR_815_DID 0x0004 +#define SYM_810AP_DID 0x0005 +#define SYM_860_DID 0x0006 +#define SYM_896_DID 0x000b +#define SYM_895_DID 0x000c +#define SYM_885_DID 0x000d /* ditto */ +#define SYM_875_DID 0x000f /* ditto */ +#define SYM_1010_DID 0x0020 +#define SYM_1011_DID 0x0021 +#define SYM_875J_DID 0x008f + +static Variant variant[] = { +{ NCR_810_DID, 0x0f, "NCR53C810", Burst16, 8, 24, 0 }, +{ NCR_810_DID, 0x1f, "SYM53C810ALV", Burst16, 8, 24, Prefetch }, +{ NCR_810_DID, 0xff, "SYM53C810A", Burst16, 8, 24, Prefetch }, +{ SYM_810AP_DID, 0xff, "SYM53C810AP", Burst16, 8, 24, Prefetch }, +{ NCR_815_DID, 0xff, "NCR53C815", Burst16, 8, 24, BurstOpCodeFetch }, +{ NCR_825_DID, 0x0f, "NCR53C825", Burst16, 8, 24, Wide|BurstOpCodeFetch|Differential }, +{ NCR_825_DID, 0xff, "SYM53C825A", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide }, +{ SYM_860_DID, 0x0f, "SYM53C860", Burst16, 8, 24, Prefetch|Ultra }, +{ SYM_860_DID, 0xff, "SYM53C860LV", Burst16, 8, 24, Prefetch|Ultra }, +{ SYM_875_DID, 0x01, "SYM53C875r1", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra }, +{ SYM_875_DID, 0xff, "SYM53C875", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra|ClockDouble }, +{ SYM_875J_DID, 0xff, "SYM53C875j", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra|ClockDouble }, +{ SYM_885_DID, 0xff, "SYM53C885", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Wide|Ultra|ClockDouble }, +{ SYM_895_DID, 0xff, "SYM53C895", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 }, +{ SYM_896_DID, 0xff, "SYM53C896", Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 }, +{ SYM_1010_DID, 0xff, "SYM53C1010", Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 }, +{ SYM_1011_DID, 0xff, "SYM53C1010", Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 }, +}; + +#define offsetof(s, t) ((ulong)&((s *)0)->t) + +static int +xfunc(Controller *c, enum na_external x, unsigned long *v) +{ + switch (x) + { + case X_scsi_id_buf: + *v = offsetof(Dsa, scsi_id_buf[0]); return 1; + case X_msg_out_buf: + *v = offsetof(Dsa, msg_out_buf); return 1; + case X_cmd_buf: + *v = offsetof(Dsa, cmd_buf); return 1; + case X_data_buf: + *v = offsetof(Dsa, data_buf); return 1; + case X_status_buf: + *v = offsetof(Dsa, status_buf); return 1; + case X_dsa_head: + *v = DMASEG(&c->dsalist.head[0]); return 1; + case X_ssid_mask: + *v = SSIDMASK(c); return 1; + default: + print("xfunc: can't find external %d\n", x); + return 0; + } + return 1; +} + +static int +na_fixup(Controller *c, ulong pa_reg, + struct na_patch *patch, int patches, + int (*externval)(Controller*, int, ulong*)) +{ + int p; + int v; + ulong *script, pa_script; + unsigned long lw, lv; + + script = c->script; + pa_script = c->scriptpa; + for (p = 0; p < patches; p++) { + switch (patch[p].type) { + case 1: + /* script relative */ + script[patch[p].lwoff] += pa_script; + break; + case 2: + /* register i/o relative */ + script[patch[p].lwoff] += pa_reg; + break; + case 3: + /* data external */ + lw = script[patch[p].lwoff]; + v = (lw >> 8) & 0xff; + if (!(*externval)(c, v, &lv)) + return 0; + v = lv & 0xff; + script[patch[p].lwoff] = (lw & 0xffff00ffL) | (v << 8); + break; + case 4: + /* 32 bit external */ + lw = script[patch[p].lwoff]; + if (!(*externval)(c, lw, &lv)) + return 0; + script[patch[p].lwoff] = lv; + break; + case 5: + /* 24 bit external */ + lw = script[patch[p].lwoff]; + if (!(*externval)(c, lw & 0xffffff, &lv)) + return 0; + script[patch[p].lwoff] = (lw & 0xff000000L) | (lv & 0xffffffL); + break; + } + } + return 1; +} + +static SDev* +sd53c8xxpnp(void) +{ + char *cp; + Pcidev *p; + Variant *v; + int ba, nctlr; + void *scriptma; + Controller *ctlr; + SDev *sdev, *head, *tail; + ulong regpa, *script, scriptpa; + + if(cp = getconf("*maxsd53c8xx")) + nctlr = strtoul(cp, 0, 0); + else + nctlr = 32; + + p = nil; + head = tail = nil; + while((p = pcimatch(p, NCR_VID, 0)) != nil && nctlr > 0){ + for(v = variant; v < &variant[nelem(variant)]; v++){ + if(p->did == v->did && p->rid <= v->maxrid) + break; + } + if(v >= &variant[nelem(variant)]) { + print("no match\n"); + continue; + } + print(PRINTPREFIX "%s rev. 0x%2.2x intr=%d command=%4.4uX\n", + v->name, p->rid, p->intl, p->pcr); + + regpa = p->mem[1].bar; + ba = 2; + if(regpa & 0x04){ + if(p->mem[2].bar) + continue; + ba++; + } + regpa = upamalloc(regpa & ~0x0F, p->mem[1].size, 0); + if(regpa == 0) + continue; + + script = nil; + scriptpa = 0; + scriptma = nil; + if((v->feature & LocalRAM) && sizeof(na_script) <= 4096){ + scriptpa = p->mem[ba].bar; + if((scriptpa & 0x04) && p->mem[ba+1].bar){ + upafree(regpa, p->mem[1].size); + continue; + } + scriptpa = upamalloc(scriptpa & ~0x0F, + p->mem[ba].size, 0); + if(scriptpa) + script = KADDR(scriptpa); + } + if(scriptpa == 0){ + /* + * Either the map failed, or this chip does not have + * local RAM. It will need a copy of the microcode. + */ + scriptma = malloc(sizeof(na_script)); + if(scriptma == nil){ + upafree(regpa, p->mem[1].size); + continue; + } + scriptpa = DMASEG(scriptma); + script = scriptma; + } + + ctlr = malloc(sizeof(Controller)); + sdev = malloc(sizeof(SDev)); + if(ctlr == nil || sdev == nil){ +buggery: + if(ctlr) + free(ctlr); + if(sdev) + free(sdev); + if(scriptma) + free(scriptma); + else + upafree(scriptpa, p->mem[ba].size); + upafree(regpa, p->mem[1].size); + continue; + } + + ctlr->n = KADDR(regpa); + ctlr->v = v; + ctlr->script = script; + memmove(ctlr->script, na_script, sizeof(na_script)); + + /* + * Because we don't yet have an abstraction for the + * addresses as seen from the controller side (and on + * the 386 it doesn't matter), the follwong two lines + * are different between the 386 and alpha copies of + * this driver. + */ + ctlr->scriptpa = scriptpa; + if(!na_fixup(ctlr, regpa, na_patches, NA_PATCHES, xfunc)){ + print("script fixup failed\n"); + goto buggery; + } + swabl(ctlr->script, ctlr->script, sizeof(na_script)); + + ctlr->dsalist.freechain = 0; + lesetl(ctlr->dsalist.head, 0); + + ctlr->pcidev = p; + + sdev->ifc = &sd53c8xxifc; + sdev->ctlr = ctlr; + if(!(v->feature & Wide)) + sdev->nunit = 8; + else + sdev->nunit = MAXTARGET; + ctlr->sdev = sdev; + + if(head != nil) + tail->next = sdev; + else + head = sdev; + tail = sdev; + + nctlr--; + } + + return head; +} + +static SDev* +sd53c8xxid(SDev* sdev) +{ + return scsiid(sdev, &sd53c8xxifc); +} + +static int +sd53c8xxenable(SDev* sdev) +{ + Pcidev *pcidev; + Controller *ctlr; + char name[32]; + + ctlr = sdev->ctlr; + pcidev = ctlr->pcidev; + + pcisetbme(pcidev); + snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name); + intrenable(pcidev->intl, sd53c8xxinterrupt, ctlr, pcidev->tbdf, name); + + ilock(ctlr); + synctabinit(ctlr); + cribbios(ctlr); + reset(ctlr); + iunlock(ctlr); + + return 1; +} + +SDifc sd53c8xxifc = { + "53c8xx", /* name */ + + sd53c8xxpnp, /* pnp */ + nil, /* legacy */ + sd53c8xxid, /* id */ + sd53c8xxenable, /* enable */ + nil, /* disable */ + + scsiverify, /* verify */ + scsionline, /* online */ + sd53c8xxrio, /* rio */ + nil, /* rctl */ + nil, /* wctl */ + + scsibio, /* bio */ + nil, /* probe */ + nil, /* clear */ + nil, /* stat */ +}; diff --git a/os/pc/sd53c8xx.i b/os/pc/sd53c8xx.i new file mode 100644 index 00000000..6ef50e02 --- /dev/null +++ b/os/pc/sd53c8xx.i @@ -0,0 +1,773 @@ +unsigned long na_script[] = { + /* extern scsi_id_buf */ + /* extern msg_out_buf */ + /* extern cmd_buf */ + /* extern data_buf */ + /* extern status_buf */ + /* extern msgin_buf */ + /* extern dsa_0 */ + /* extern dsa_1 */ + /* extern dsa_head */ + /* extern ssid_mask */ + /* SIR_MSG_IO_COMPLETE = 0 */ + /* error_not_cmd_complete = 1 */ + /* error_disconnected = 2 */ + /* error_reselected = 3 */ + /* error_unexpected_phase = 4 */ + /* error_weird_message = 5 */ + /* SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT = 6 */ + /* error_not_identify_after_reselect = 7 */ + /* error_too_much_data = 8 */ + /* error_too_little_data = 9 */ + /* SIR_MSG_REJECT = 10 */ + /* SIR_MSG_SDTR = 11 */ + /* SIR_EV_RESPONSE_OK = 12 */ + /* error_sigp_set = 13 */ + /* SIR_EV_PHASE_SWITCH_AFTER_ID = 14 */ + /* SIR_MSG_WDTR = 15 */ + /* SIR_MSG_IGNORE_WIDE_RESIDUE = 16 */ + /* SIR_NOTIFY_DISC = 100 */ + /* SIR_NOTIFY_RESELECT = 101 */ + /* SIR_NOTIFY_MSG_IN = 102 */ + /* SIR_NOTIFY_STATUS = 103 */ + /* SIR_NOTIFY_DUMP = 104 */ + /* SIR_NOTIFY_DUMP2 = 105 */ + /* SIR_NOTIFY_SIGP = 106 */ + /* SIR_NOTIFY_ISSUE = 107 */ + /* SIR_NOTIFY_WAIT_RESELECT = 108 */ + /* SIR_NOTIFY_ISSUE_CHECK = 109 */ + /* SIR_NOTIFY_DUMP_NEXT_CODE = 110 */ + /* SIR_NOTIFY_COMMAND = 111 */ + /* SIR_NOTIFY_DATA_IN = 112 */ + /* SIR_NOTIFY_DATA_OUT = 113 */ + /* SIR_NOTIFY_BLOCK_DATA_IN = 114 */ + /* SIR_NOTIFY_WSR = 115 */ + /* SIR_NOTIFY_LOAD_SYNC = 116 */ + /* SIR_NOTIFY_RESELECTED_ON_SELECT = 117 */ + /* STATE_FREE = 0 */ + /* STATE_ALLOCATED = 1 */ + /* STATE_ISSUE = 2 */ + /* STATE_DISCONNECTED = 3 */ + /* STATE_DONE = 4 */ + /* RESULT_OK = 0 */ + /* MSG_IDENTIFY = 0x80 */ + /* MSG_DISCONNECT = 0x04 */ + /* MSG_SAVE_DATA_POINTER = 0x02 */ + /* MSG_RESTORE_POINTERS = 0x03 */ + /* MSG_IGNORE_WIDE_RESIDUE = 0x23 */ + /* X_MSG = 0x01 */ + /* X_MSG_SDTR = 0x01 */ + /* X_MSG_WDTR = 0x03 */ + /* MSG_REJECT = 0x07 */ + /* BSIZE = 512 */ +/* 0000 */ 0x80880000L, /* jump wait_for_reselection */ +/* 0004 */ 0x00000514L, +/* 0008 */ 0x88880000L, /* call load_sync */ +/* 000c */ 0x0000074cL, +/* 0010 */ 0x60000200L, /* clear target */ +/* 0014 */ 0x00000000L, +/* 0018 */ 0x47000000L, /* select atn from scsi_id_buf, reselected_on_select */ +/* 001c */ 0x000004ecL, +/* 0020 */ 0x878b0000L, /* jump start1, when msg_in */ +/* 0024 */ 0x00000000L, +/* 0028 */ 0x1e000000L, /* move from msg_out_buf, when msg_out */ +/* 002c */ 0x00000001L, +/* 0030 */ 0x868b0000L, /* jump start1, when msg_out */ +/* 0034 */ 0x00fffff0L, +/* 0038 */ 0x82830000L, /* jump to_decisions, when not cmd */ +/* 003c */ 0x000005f0L, +/* 0040 */ 0x60000008L, /* clear atn */ +/* 0044 */ 0x00000000L, +/* 0048 */ 0x1a000000L, /* move from cmd_buf, when cmd */ +/* 004c */ 0x00000002L, +/* 0050 */ 0x81830000L, /* jump to_decisions, when not data_in */ +/* 0054 */ 0x000005d8L, +/* 0058 */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 005c */ 0x00000678L, +/* 0060 */ 0x00000034L, +/* 0064 */ 0xc0000004L, /* move memory 4, dmaaddr, scratchb */ +/* 0068 */ 0x0000067cL, +/* 006c */ 0x0000005cL, +/* 0070 */ 0x72360000L, /* move scratcha2 to sfbr */ +/* 0074 */ 0x00000000L, +/* 0078 */ 0x808c0000L, /* jump data_in_normal, if 0 */ +/* 007c */ 0x00000078L, +/* 0080 */ 0x29000200L, /* move BSIZE, ptr dmaaddr, when data_in */ +/* 0084 */ 0x0000067cL, +/* 0088 */ 0x7e5d0200L, /* move scratchb1 + BSIZE / 256 to scratchb1 */ +/* 008c */ 0x00000000L, +/* 0090 */ 0x7f5e0000L, /* move scratchb2 + 0 to scratchb2 with carry */ +/* 0094 */ 0x00000000L, +/* 0098 */ 0x7f5f0000L, /* move scratchb3 + 0 to scratchb3 with carry */ +/* 009c */ 0x00000000L, +/* 00a0 */ 0x7e36ff00L, /* move scratcha2 + 255 to scratcha2 */ +/* 00a4 */ 0x00000000L, +/* 00a8 */ 0xc0000004L, /* move memory 4, scratchb, dmaaddr */ +/* 00ac */ 0x0000005cL, +/* 00b0 */ 0x0000067cL, +/* 00b4 */ 0x818b0000L, /* jump data_in_block_loop, when data_in */ +/* 00b8 */ 0x00ffffb4L, +/* 00bc */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 00c0 */ 0x00000034L, +/* 00c4 */ 0x00000678L, +/* 00c8 */ 0x88880000L, /* call save_state */ +/* 00cc */ 0x000005e0L, +/* 00d0 */ 0x80880000L, /* jump to_decisions */ +/* 00d4 */ 0x00000558L, +/* 00d8 */ 0xc0000004L, /* move memory 4, scratchb, dmaaddr */ +/* 00dc */ 0x0000005cL, +/* 00e0 */ 0x0000067cL, +/* 00e4 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 00e8 */ 0x00000034L, +/* 00ec */ 0x00000678L, +/* 00f0 */ 0x80880000L, /* jump to_decisions */ +/* 00f4 */ 0x00000538L, +/* 00f8 */ 0x72370000L, /* move scratcha3 to sfbr */ +/* 00fc */ 0x00000000L, +/* 0100 */ 0x98040000L, /* int error_too_much_data, if not 0 */ +/* 0104 */ 0x00000008L, +/* 0108 */ 0x19000000L, /* move from data_buf, when data_in */ +/* 010c */ 0x00000003L, +/* 0110 */ 0x78370200L, /* move 2 to scratcha3 */ +/* 0114 */ 0x00000000L, +/* 0118 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 011c */ 0x00000034L, +/* 0120 */ 0x00000678L, +/* 0124 */ 0x88880000L, /* call save_state */ +/* 0128 */ 0x00000584L, +/* 012c */ 0x80880000L, /* jump post_data_to_decisions */ +/* 0130 */ 0x0000052cL, +/* 0134 */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 0138 */ 0x00000678L, +/* 013c */ 0x00000034L, +/* 0140 */ 0xc0000004L, /* move memory 4, dmaaddr, scratchb */ +/* 0144 */ 0x0000067cL, +/* 0148 */ 0x0000005cL, +/* 014c */ 0x72360000L, /* move scratcha2 to sfbr */ +/* 0150 */ 0x00000000L, +/* 0154 */ 0x808c0000L, /* jump data_out_normal, if 0 */ +/* 0158 */ 0x0000005cL, +/* 015c */ 0xc0000004L, /* move memory 4, dmaaddr, scratchb */ +/* 0160 */ 0x0000067cL, +/* 0164 */ 0x0000005cL, +/* 0168 */ 0x28000200L, /* move BSIZE, ptr dmaaddr, when data_out */ +/* 016c */ 0x0000067cL, +/* 0170 */ 0x7e5d0200L, /* move scratchb1 + BSIZE / 256 to scratchb1 */ +/* 0174 */ 0x00000000L, +/* 0178 */ 0x7f5e0000L, /* move scratchb2 + 0 to scratchb2 with carry */ +/* 017c */ 0x00000000L, +/* 0180 */ 0x7f5f0000L, /* move scratchb3 + 0 to scratchb3 with carry */ +/* 0184 */ 0x00000000L, +/* 0188 */ 0x7e36ff00L, /* move scratcha2 + 255 to scratcha2 */ +/* 018c */ 0x00000000L, +/* 0190 */ 0xc0000004L, /* move memory 4, scratchb, dmaaddr */ +/* 0194 */ 0x0000005cL, +/* 0198 */ 0x0000067cL, +/* 019c */ 0x808b0000L, /* jump data_out_block_loop, when data_out */ +/* 01a0 */ 0x00ffffa8L, +/* 01a4 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 01a8 */ 0x00000034L, +/* 01ac */ 0x00000678L, +/* 01b0 */ 0x80880000L, /* jump to_decisions */ +/* 01b4 */ 0x00000478L, +/* 01b8 */ 0x72370000L, /* move scratcha3 to sfbr */ +/* 01bc */ 0x00000000L, +/* 01c0 */ 0x98040000L, /* int error_too_little_data, if not 0 */ +/* 01c4 */ 0x00000009L, +/* 01c8 */ 0x18000000L, /* move from data_buf, when data_out */ +/* 01cc */ 0x00000003L, +/* 01d0 */ 0x78370200L, /* move 2 to scratcha3 */ +/* 01d4 */ 0x00000000L, +/* 01d8 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 01dc */ 0x00000034L, +/* 01e0 */ 0x00000678L, +/* 01e4 */ 0x88880000L, /* call save_state */ +/* 01e8 */ 0x000004c4L, +/* 01ec */ 0x80880000L, /* jump post_data_to_decisions */ +/* 01f0 */ 0x0000046cL, +/* 01f4 */ 0x1b000000L, /* move from status_buf, when status */ +/* 01f8 */ 0x00000004L, +/* 01fc */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0200 */ 0x00000004L, +/* 0204 */ 0x0f000001L, /* move 1, scratcha, when msg_in */ +/* 0208 */ 0x00000034L, +/* 020c */ 0x808c0007L, /* jump rejected, if MSG_REJECT */ +/* 0210 */ 0x00000088L, +/* 0214 */ 0x808c0004L, /* jump disconnected, if MSG_DISCONNECT */ +/* 0218 */ 0x00000298L, +/* 021c */ 0x808c0002L, /* jump msg_in_skip, if MSG_SAVE_DATA_POINTER */ +/* 0220 */ 0x00000090L, +/* 0224 */ 0x808c0003L, /* jump msg_in_skip, if MSG_RESTORE_POINTERS */ +/* 0228 */ 0x00000088L, +/* 022c */ 0x808c0023L, /* jump ignore_wide, if MSG_IGNORE_WIDE_RESIDUE */ +/* 0230 */ 0x000001f0L, +/* 0234 */ 0x808c0001L, /* jump extended, if X_MSG */ +/* 0238 */ 0x00000088L, +/* 023c */ 0x98040000L, /* int error_not_cmd_complete, if not 0 */ +/* 0240 */ 0x00000001L, +/* 0244 */ 0x7c027e00L, /* move scntl2&0x7e to scntl2 */ +/* 0248 */ 0x00000000L, +/* 024c */ 0x60000040L, /* clear ack */ +/* 0250 */ 0x00000000L, +/* 0254 */ 0x48000000L, /* wait disconnect */ +/* 0258 */ 0x00000000L, +/* 025c */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 0260 */ 0x00000678L, +/* 0264 */ 0x00000034L, +/* 0268 */ 0x78340400L, /* move STATE_DONE to scratcha0 */ +/* 026c */ 0x00000000L, +/* 0270 */ 0x78350000L, /* move RESULT_OK to scratcha1 */ +/* 0274 */ 0x00000000L, +/* 0278 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 027c */ 0x00000034L, +/* 0280 */ 0x00000678L, +/* 0284 */ 0x88880000L, /* call save_state */ +/* 0288 */ 0x00000424L, +/* 028c */ 0x98180000L, /* intfly 0 */ +/* 0290 */ 0x00000000L, +/* 0294 */ 0x80880000L, /* jump issue_check */ +/* 0298 */ 0x0000043cL, +/* 029c */ 0x98080000L, /* int SIR_MSG_REJECT */ +/* 02a0 */ 0x0000000aL, +/* 02a4 */ 0x60000040L, /* clear ack */ +/* 02a8 */ 0x00000000L, +/* 02ac */ 0x80880000L, /* jump to_decisions */ +/* 02b0 */ 0x0000037cL, +/* 02b4 */ 0x60000040L, /* clear ack */ +/* 02b8 */ 0x00000000L, +/* 02bc */ 0x80880000L, /* jump to_decisions */ +/* 02c0 */ 0x0000036cL, +/* 02c4 */ 0x60000040L, /* clear ack */ +/* 02c8 */ 0x00000000L, +/* 02cc */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 02d0 */ 0x00000004L, +/* 02d4 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */ +/* 02d8 */ 0x00000035L, +/* 02dc */ 0x808c0003L, /* jump ext_3, if 3 */ +/* 02e0 */ 0x00000030L, +/* 02e4 */ 0x808c0002L, /* jump ext_2, if 2 */ +/* 02e8 */ 0x00000098L, +/* 02ec */ 0x98040001L, /* int error_weird_message, if not 1 */ +/* 02f0 */ 0x00000005L, +/* 02f4 */ 0x60000040L, /* clear ack */ +/* 02f8 */ 0x00000000L, +/* 02fc */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0300 */ 0x00000004L, +/* 0304 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */ +/* 0308 */ 0x00000035L, +/* 030c */ 0x80880000L, /* jump ext_done */ +/* 0310 */ 0x000000c8L, +/* 0314 */ 0x60000040L, /* ext_3: clear ack */ +/* 0318 */ 0x00000000L, +/* 031c */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0320 */ 0x00000004L, +/* 0324 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */ +/* 0328 */ 0x00000035L, +/* 032c */ 0x60000040L, /* clear ack */ +/* 0330 */ 0x00000000L, +/* 0334 */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0338 */ 0x00000004L, +/* 033c */ 0x0f000001L, /* move 1, scratcha2, when msg_in */ +/* 0340 */ 0x00000036L, +/* 0344 */ 0x60000040L, /* clear ack */ +/* 0348 */ 0x00000000L, +/* 034c */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0350 */ 0x00000004L, +/* 0354 */ 0x0f000001L, /* move 1, scratcha3, when msg_in */ +/* 0358 */ 0x00000037L, +/* 035c */ 0x72350000L, /* move scratcha1 to sfbr */ +/* 0360 */ 0x00000000L, +/* 0364 */ 0x80840001L, /* jump ext_done, if not X_MSG_SDTR */ +/* 0368 */ 0x00000070L, +/* 036c */ 0x98080000L, /* sdtr: int SIR_MSG_SDTR */ +/* 0370 */ 0x0000000bL, +/* 0374 */ 0x60000040L, /* clear ack */ +/* 0378 */ 0x00000000L, +/* 037c */ 0x80880000L, /* jump to_decisions */ +/* 0380 */ 0x000002acL, +/* 0384 */ 0x60000040L, /* ext_2: clear ack */ +/* 0388 */ 0x00000000L, +/* 038c */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0390 */ 0x00000004L, +/* 0394 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */ +/* 0398 */ 0x00000035L, +/* 039c */ 0x60000040L, /* clear ack */ +/* 03a0 */ 0x00000000L, +/* 03a4 */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 03a8 */ 0x00000004L, +/* 03ac */ 0x0f000001L, /* move 1, scratcha2, when msg_in */ +/* 03b0 */ 0x00000036L, +/* 03b4 */ 0x72350000L, /* move scratcha1 to sfbr */ +/* 03b8 */ 0x00000000L, +/* 03bc */ 0x80840003L, /* jump ext_done, if not X_MSG_WDTR */ +/* 03c0 */ 0x00000018L, +/* 03c4 */ 0x98080000L, /* wdtr: int SIR_MSG_WDTR */ +/* 03c8 */ 0x0000000fL, +/* 03cc */ 0x60000040L, /* clear ack */ +/* 03d0 */ 0x00000000L, +/* 03d4 */ 0x80880000L, /* jump to_decisions */ +/* 03d8 */ 0x00000254L, +/* 03dc */ 0x58000008L, /* set atn */ +/* 03e0 */ 0x00000000L, +/* 03e4 */ 0x60000040L, /* clear ack */ +/* 03e8 */ 0x00000000L, +/* 03ec */ 0x78340700L, /* move MSG_REJECT to scratcha */ +/* 03f0 */ 0x00000000L, +/* 03f4 */ 0x9e030000L, /* int error_unexpected_phase, when not msg_out */ +/* 03f8 */ 0x00000004L, +/* 03fc */ 0x60000008L, /* clear atn */ +/* 0400 */ 0x00000000L, +/* 0404 */ 0x0e000001L, /* move 1, scratcha, when msg_out */ +/* 0408 */ 0x00000034L, +/* 040c */ 0x60000040L, /* clear ack */ +/* 0410 */ 0x00000000L, +/* 0414 */ 0x868b0000L, /* jump reject, when msg_out */ +/* 0418 */ 0x00ffffc0L, +/* 041c */ 0x80880000L, /* jump to_decisions */ +/* 0420 */ 0x0000020cL, +/* 0424 */ 0x60000040L, /* clear ack */ +/* 0428 */ 0x00000000L, +/* 042c */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0430 */ 0x00000004L, +/* 0434 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */ +/* 0438 */ 0x00000035L, +/* 043c */ 0x98080000L, /* int SIR_MSG_IGNORE_WIDE_RESIDUE */ +/* 0440 */ 0x00000010L, +/* 0444 */ 0x60000040L, /* clear ack */ +/* 0448 */ 0x00000000L, +/* 044c */ 0x80880000L, /* jump to_decisions */ +/* 0450 */ 0x000001dcL, +/* 0454 */ 0x58000008L, /* set atn */ +/* 0458 */ 0x00000000L, +/* 045c */ 0x60000040L, /* clear ack */ +/* 0460 */ 0x00000000L, +/* 0464 */ 0x9e030000L, /* int error_unexpected_phase, when not msg_out */ +/* 0468 */ 0x00000004L, +/* 046c */ 0x1e000000L, /* move from msg_out_buf, when msg_out */ +/* 0470 */ 0x00000001L, +/* 0474 */ 0x868b0000L, /* jump response_repeat, when msg_out */ +/* 0478 */ 0x00fffff0L, +/* 047c */ 0x878b0000L, /* jump response_msg_in, when msg_in */ +/* 0480 */ 0x00000010L, +/* 0484 */ 0x98080000L, /* int SIR_EV_RESPONSE_OK */ +/* 0488 */ 0x0000000cL, +/* 048c */ 0x80880000L, /* jump to_decisions */ +/* 0490 */ 0x0000019cL, +/* 0494 */ 0x0f000001L, /* move 1, scratcha, when msg_in */ +/* 0498 */ 0x00000034L, +/* 049c */ 0x808c0007L, /* jump rejected, if MSG_REJECT */ +/* 04a0 */ 0x00fffdf8L, +/* 04a4 */ 0x98080000L, /* int SIR_EV_RESPONSE_OK */ +/* 04a8 */ 0x0000000cL, +/* 04ac */ 0x80880000L, /* jump msg_in_not_reject */ +/* 04b0 */ 0x00fffd60L, +/* 04b4 */ 0x7c027e00L, /* move scntl2&0x7e to scntl2 */ +/* 04b8 */ 0x00000000L, +/* 04bc */ 0x60000040L, /* clear ack */ +/* 04c0 */ 0x00000000L, +/* 04c4 */ 0x48000000L, /* wait disconnect */ +/* 04c8 */ 0x00000000L, +/* 04cc */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 04d0 */ 0x00000678L, +/* 04d4 */ 0x00000034L, +/* 04d8 */ 0x78340300L, /* move STATE_DISCONNECTED to scratcha0 */ +/* 04dc */ 0x00000000L, +/* 04e0 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 04e4 */ 0x00000034L, +/* 04e8 */ 0x00000678L, +/* 04ec */ 0x88880000L, /* call save_state */ +/* 04f0 */ 0x000001bcL, +/* 04f4 */ 0x74020100L, /* move scntl2&0x01 to sfbr */ +/* 04f8 */ 0x00000000L, +/* 04fc */ 0x98040000L, /* int SIR_NOTIFY_WSR, if not 0 */ +/* 0500 */ 0x00000073L, +/* 0504 */ 0x80880000L, /* jump issue_check */ +/* 0508 */ 0x000001ccL, +/* 050c */ 0x98080000L, /* int SIR_NOTIFY_RESELECTED_ON_SELECT */ +/* 0510 */ 0x00000075L, +/* 0514 */ 0x80880000L, /* jump reselected */ +/* 0518 */ 0x00000008L, +/* 051c */ 0x54000000L, /* wait reselect sigp_set */ +/* 0520 */ 0x000001acL, +/* 0524 */ 0x60000200L, /* clear target */ +/* 0528 */ 0x00000000L, +/* 052c */ 0x9f030000L, /* int SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT, when not msg_in */ +/* 0530 */ 0x00000006L, +/* 0534 */ 0x0f000001L, /* move 1, scratchb, when msg_in */ +/* 0538 */ 0x0000005cL, +/* 053c */ 0x98041f80L, /* int error_not_identify_after_reselect, if not MSG_IDENTIFY and mask 0x1f */ +/* 0540 */ 0x00000007L, +/* 0544 */ 0xc0000004L, /* move memory 4, dsa_head, dsa */ +/* 0548 */ 0x00000008L, +/* 054c */ 0x00000010L, +/* 0550 */ 0x72100000L, /* move dsa0 to sfbr */ +/* 0554 */ 0x00000000L, +/* 0558 */ 0x80840000L, /* jump find_dsa_1, if not 0 */ +/* 055c */ 0x00000030L, +/* 0560 */ 0x72110000L, /* move dsa1 to sfbr */ +/* 0564 */ 0x00000000L, +/* 0568 */ 0x80840000L, /* jump find_dsa_1, if not 0 */ +/* 056c */ 0x00000020L, +/* 0570 */ 0x72120000L, /* move dsa2 to sfbr */ +/* 0574 */ 0x00000000L, +/* 0578 */ 0x80840000L, /* jump find_dsa_1, if not 0 */ +/* 057c */ 0x00000010L, +/* 0580 */ 0x72130000L, /* move dsa3 to sfbr */ +/* 0584 */ 0x00000000L, +/* 0588 */ 0x980c0000L, /* int error_reselected, if 0 */ +/* 058c */ 0x00000003L, +/* 0590 */ 0x88880000L, /* call load_state */ +/* 0594 */ 0x000000f8L, +/* 0598 */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 059c */ 0x00000678L, +/* 05a0 */ 0x00000034L, +/* 05a4 */ 0x72340000L, /* move scratcha0 to sfbr */ +/* 05a8 */ 0x00000000L, +/* 05ac */ 0x80840003L, /* jump find_dsa_next, if not STATE_DISCONNECTED */ +/* 05b0 */ 0x00000038L, +/* 05b4 */ 0x740a0900L, /* move ssid & ssid_mask to sfbr */ +/* 05b8 */ 0x00000000L, +/* 05bc */ 0xc0000001L, /* move memory 1, targ, find_dsa_smc1 */ +/* 05c0 */ 0x00000680L, +/* 05c4 */ 0x000005c8L, +/* 05c8 */ 0x808400ffL, /* jump find_dsa_next, if not 255 */ +/* 05cc */ 0x0000001cL, +/* 05d0 */ 0xc0000001L, /* move memory 1, lun, find_dsa_smc2 */ +/* 05d4 */ 0x00000684L, +/* 05d8 */ 0x000005e4L, +/* 05dc */ 0x725c0000L, /* move scratchb0 to sfbr */ +/* 05e0 */ 0x00000000L, +/* 05e4 */ 0x808cf8ffL, /* jump reload_sync, if 255 and mask ~7 */ +/* 05e8 */ 0x00000034L, +/* 05ec */ 0xc0000004L, /* move memory 4, next, dsa */ +/* 05f0 */ 0x0000068cL, +/* 05f4 */ 0x00000010L, +/* 05f8 */ 0x80880000L, /* jump find_dsa_loop */ +/* 05fc */ 0x00ffff50L, +/* 0600 */ 0x60000008L, /* clear atn */ +/* 0604 */ 0x00000000L, +/* 0608 */ 0x878b0000L, /* jump msg_in_phase, when msg_in */ +/* 060c */ 0x00fffbf4L, +/* 0610 */ 0x98080000L, /* int SIR_MSG_REJECT */ +/* 0614 */ 0x0000000aL, +/* 0618 */ 0x80880000L, /* jump to_decisions */ +/* 061c */ 0x00000010L, +/* 0620 */ 0x88880000L, /* call load_sync */ +/* 0624 */ 0x00000134L, +/* 0628 */ 0x60000040L, /* clear ack */ +/* 062c */ 0x00000000L, +/* 0630 */ 0x818b0000L, /* jump data_in_phase, when data_in */ +/* 0634 */ 0x00fffa20L, +/* 0638 */ 0x828a0000L, /* jump cmd_phase, if cmd */ +/* 063c */ 0x00fffa00L, +/* 0640 */ 0x808a0000L, /* jump data_out_phase, if data_out */ +/* 0644 */ 0x00fffaecL, +/* 0648 */ 0x838a0000L, /* jump status_phase, if status */ +/* 064c */ 0x00fffba4L, +/* 0650 */ 0x878a0000L, /* jump msg_in_phase, if msg_in */ +/* 0654 */ 0x00fffbacL, +/* 0658 */ 0x98080000L, /* int error_unexpected_phase */ +/* 065c */ 0x00000004L, +/* 0660 */ 0x838b0000L, /* jump status_phase, when status */ +/* 0664 */ 0x00fffb8cL, +/* 0668 */ 0x878a0000L, /* jump msg_in_phase, if msg_in */ +/* 066c */ 0x00fffb94L, +/* 0670 */ 0x98080000L, /* int error_unexpected_phase */ +/* 0674 */ 0x00000004L, +/* 0678 */ 0x00000000L, /* state: defw 0 */ +/* 067c */ 0x00000000L, /* dmaaddr: defw 0 */ +/* 0680 */ 0x00000000L, /* targ: defw 0 */ +/* 0684 */ 0x00000000L, /* lun: defw 0 */ +/* 0688 */ 0x00000000L, /* sync: defw 0 */ +/* 068c */ 0x00000000L, /* next: defw 0 */ + /* dsa_load_len = dsa_load_end - dsa_copy */ + /* dsa_save_len = dsa_save_end - dsa_copy */ +/* 0690 */ 0xc0000004L, /* move memory 4, dsa, load_state_smc0 + 4 */ +/* 0694 */ 0x00000010L, +/* 0698 */ 0x000006a0L, +/* 069c */ 0xc0000018L, /* move memory dsa_load_len, 0, dsa_copy */ +/* 06a0 */ 0x00000000L, +/* 06a4 */ 0x00000678L, +/* 06a8 */ 0x90080000L, /* return */ +/* 06ac */ 0x00000000L, +/* 06b0 */ 0xc0000004L, /* move memory 4, dsa, save_state_smc0 + 8 */ +/* 06b4 */ 0x00000010L, +/* 06b8 */ 0x000006c4L, +/* 06bc */ 0xc0000008L, /* move memory dsa_save_len, dsa_copy, 0 */ +/* 06c0 */ 0x00000678L, +/* 06c4 */ 0x00000000L, +/* 06c8 */ 0x90080000L, /* return */ +/* 06cc */ 0x00000000L, +/* 06d0 */ 0x721a0000L, /* move ctest2 to sfbr */ +/* 06d4 */ 0x00000000L, +/* 06d8 */ 0xc0000004L, /* move memory 4, dsa_head, dsa */ +/* 06dc */ 0x00000008L, +/* 06e0 */ 0x00000010L, +/* 06e4 */ 0x72100000L, /* move dsa0 to sfbr */ +/* 06e8 */ 0x00000000L, +/* 06ec */ 0x80840000L, /* jump issue_check_1, if not 0 */ +/* 06f0 */ 0x00000030L, +/* 06f4 */ 0x72110000L, /* move dsa1 to sfbr */ +/* 06f8 */ 0x00000000L, +/* 06fc */ 0x80840000L, /* jump issue_check_1, if not 0 */ +/* 0700 */ 0x00000020L, +/* 0704 */ 0x72120000L, /* move dsa2 to sfbr */ +/* 0708 */ 0x00000000L, +/* 070c */ 0x80840000L, /* jump issue_check_1, if not 0 */ +/* 0710 */ 0x00000010L, +/* 0714 */ 0x72130000L, /* move dsa3 to sfbr */ +/* 0718 */ 0x00000000L, +/* 071c */ 0x808c0000L, /* jump wait_for_reselection, if 0 */ +/* 0720 */ 0x00fffdf8L, +/* 0724 */ 0x88880000L, /* call load_state */ +/* 0728 */ 0x00ffff64L, +/* 072c */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 0730 */ 0x00000678L, +/* 0734 */ 0x00000034L, +/* 0738 */ 0x72340000L, /* move scratcha0 to sfbr */ +/* 073c */ 0x00000000L, +/* 0740 */ 0x808c0002L, /* jump start, if STATE_ISSUE */ +/* 0744 */ 0x00fff8c0L, +/* 0748 */ 0xc0000004L, /* move memory 4, next, dsa */ +/* 074c */ 0x0000068cL, +/* 0750 */ 0x00000010L, +/* 0754 */ 0x80880000L, /* jump issue_check_loop */ +/* 0758 */ 0x00ffff88L, +/* 075c */ 0xc0000004L, /* move memory 4, sync, scratcha */ +/* 0760 */ 0x00000688L, +/* 0764 */ 0x00000034L, +/* 0768 */ 0x72340000L, /* move scratcha0 to sfbr */ +/* 076c */ 0x00000000L, +/* 0770 */ 0x6a030000L, /* move sfbr to scntl3 */ +/* 0774 */ 0x00000000L, +/* 0778 */ 0x72350000L, /* move scratcha1 to sfbr */ +/* 077c */ 0x00000000L, +/* 0780 */ 0x6a050000L, /* move sfbr to sxfer */ +/* 0784 */ 0x00000000L, +/* 0788 */ 0x90080000L, /* return */ +/* 078c */ 0x00000000L, +}; + +#define NA_SCRIPT_SIZE 484 + +struct na_patch na_patches[] = { + { 0x0006, 5 }, /* 00000018 */ + { 0x000b, 4 }, /* 0000002c */ + { 0x0013, 4 }, /* 0000004c */ + { 0x0017, 1 }, /* 0000005c */ + { 0x0018, 2 }, /* 00000060 */ + { 0x001a, 1 }, /* 00000068 */ + { 0x001b, 2 }, /* 0000006c */ + { 0x0021, 1 }, /* 00000084 */ + { 0x002b, 2 }, /* 000000ac */ + { 0x002c, 1 }, /* 000000b0 */ + { 0x0030, 2 }, /* 000000c0 */ + { 0x0031, 1 }, /* 000000c4 */ + { 0x0037, 2 }, /* 000000dc */ + { 0x0038, 1 }, /* 000000e0 */ + { 0x003a, 2 }, /* 000000e8 */ + { 0x003b, 1 }, /* 000000ec */ + { 0x0043, 4 }, /* 0000010c */ + { 0x0047, 2 }, /* 0000011c */ + { 0x0048, 1 }, /* 00000120 */ + { 0x004e, 1 }, /* 00000138 */ + { 0x004f, 2 }, /* 0000013c */ + { 0x0051, 1 }, /* 00000144 */ + { 0x0052, 2 }, /* 00000148 */ + { 0x0058, 1 }, /* 00000160 */ + { 0x0059, 2 }, /* 00000164 */ + { 0x005b, 1 }, /* 0000016c */ + { 0x0065, 2 }, /* 00000194 */ + { 0x0066, 1 }, /* 00000198 */ + { 0x006a, 2 }, /* 000001a8 */ + { 0x006b, 1 }, /* 000001ac */ + { 0x0073, 4 }, /* 000001cc */ + { 0x0077, 2 }, /* 000001dc */ + { 0x0078, 1 }, /* 000001e0 */ + { 0x007e, 4 }, /* 000001f8 */ + { 0x0082, 2 }, /* 00000208 */ + { 0x0098, 1 }, /* 00000260 */ + { 0x0099, 2 }, /* 00000264 */ + { 0x009f, 2 }, /* 0000027c */ + { 0x00a0, 1 }, /* 00000280 */ + { 0x00b6, 2 }, /* 000002d8 */ + { 0x00c2, 2 }, /* 00000308 */ + { 0x00ca, 2 }, /* 00000328 */ + { 0x00d0, 2 }, /* 00000340 */ + { 0x00d6, 2 }, /* 00000358 */ + { 0x00e6, 2 }, /* 00000398 */ + { 0x00ec, 2 }, /* 000003b0 */ + { 0x0102, 2 }, /* 00000408 */ + { 0x010e, 2 }, /* 00000438 */ + { 0x011c, 4 }, /* 00000470 */ + { 0x0126, 2 }, /* 00000498 */ + { 0x0134, 1 }, /* 000004d0 */ + { 0x0135, 2 }, /* 000004d4 */ + { 0x0139, 2 }, /* 000004e4 */ + { 0x013a, 1 }, /* 000004e8 */ + { 0x014e, 2 }, /* 00000538 */ + { 0x0152, 4 }, /* 00000548 */ + { 0x0153, 2 }, /* 0000054c */ + { 0x0167, 1 }, /* 0000059c */ + { 0x0168, 2 }, /* 000005a0 */ + { 0x016d, 3 }, /* 000005b4 */ + { 0x0170, 1 }, /* 000005c0 */ + { 0x0171, 1 }, /* 000005c4 */ + { 0x0175, 1 }, /* 000005d4 */ + { 0x0176, 1 }, /* 000005d8 */ + { 0x017c, 1 }, /* 000005f0 */ + { 0x017d, 2 }, /* 000005f4 */ + { 0x01a5, 2 }, /* 00000694 */ + { 0x01a6, 1 }, /* 00000698 */ + { 0x01a9, 1 }, /* 000006a4 */ + { 0x01ad, 2 }, /* 000006b4 */ + { 0x01ae, 1 }, /* 000006b8 */ + { 0x01b0, 1 }, /* 000006c0 */ + { 0x01b7, 4 }, /* 000006dc */ + { 0x01b8, 2 }, /* 000006e0 */ + { 0x01cc, 1 }, /* 00000730 */ + { 0x01cd, 2 }, /* 00000734 */ + { 0x01d3, 1 }, /* 0000074c */ + { 0x01d4, 2 }, /* 00000750 */ + { 0x01d8, 1 }, /* 00000760 */ + { 0x01d9, 2 }, /* 00000764 */ +}; +#define NA_PATCHES 80 + +enum na_external { + X_scsi_id_buf, + X_msg_out_buf, + X_cmd_buf, + X_data_buf, + X_status_buf, + X_msgin_buf, + X_dsa_0, + X_dsa_1, + X_dsa_head, + X_ssid_mask, +}; + +enum { + E_issue_check_next = 1864, + E_issue_check_1 = 1828, + E_issue_check_loop = 1764, + E_save_state_smc0 = 1724, + E_load_state_smc0 = 1692, + E_dsa_load_end = 1680, + E_sync = 1672, + E_dsa_save_end = 1664, + E_dsa_copy = 1656, + E_id_out_mismatch_recover = 1536, + E_next = 1676, + E_reload_sync = 1568, + E_find_dsa_smc2 = 1508, + E_lun = 1668, + E_find_dsa_smc1 = 1480, + E_targ = 1664, + E_find_dsa_next = 1516, + E_load_state = 1680, + E_find_dsa_1 = 1424, + E_find_dsa_loop = 1360, + E_find_dsa = 1348, + E_sigp_set = 1744, + E_reselected = 1316, + E_wsr_check = 1268, + E_response_msg_in = 1172, + E_response_repeat = 1132, + E_response = 1108, + E_reject = 988, + E_wdtr = 964, + E_sdtr = 876, + E_ext_done = 988, + E_ext_1 = 756, + E_ext_2 = 900, + E_ext_3 = 788, + E_issue_check = 1752, + E_extended = 708, + E_ignore_wide = 1060, + E_msg_in_skip = 692, + E_disconnected = 1204, + E_msg_in_not_reject = 532, + E_rejected = 668, + E_msg_in_phase = 516, + E_status_phase = 500, + E_data_out_mismatch = 464, + E_data_out_block_mismatch = 368, + E_data_out_normal = 440, + E_data_out_block_loop = 332, + E_data_out_phase = 308, + E_post_data_to_decisions = 1632, + E_data_in_mismatch = 272, + E_data_mismatch_recover = 228, + E_data_block_mismatch_recover = 216, + E_save_state = 1712, + E_data_in_block_mismatch = 136, + E_data_in_normal = 248, + E_data_in_block_loop = 112, + E_dmaaddr = 1660, + E_state = 1656, + E_data_in_phase = 88, + E_cmd_out_mismatch = 80, + E_cmd_phase = 64, + E_to_decisions = 1584, + E_id_out_mismatch = 48, + E_start1 = 40, + E_reselected_on_select = 1292, + E_load_sync = 1884, + E_start = 8, + E_wait_for_reselection = 1308, + E_idle = 0, +}; +#define A_dsa_save_len 8 +#define A_dsa_load_len 24 +#define A_BSIZE 512 +#define A_MSG_REJECT 7 +#define A_X_MSG_WDTR 3 +#define A_X_MSG_SDTR 1 +#define A_X_MSG 1 +#define A_MSG_IGNORE_WIDE_RESIDUE 35 +#define A_MSG_RESTORE_POINTERS 3 +#define A_MSG_SAVE_DATA_POINTER 2 +#define A_MSG_DISCONNECT 4 +#define A_MSG_IDENTIFY 128 +#define A_RESULT_OK 0 +#define A_STATE_DONE 4 +#define A_STATE_DISCONNECTED 3 +#define A_STATE_ISSUE 2 +#define A_STATE_ALLOCATED 1 +#define A_STATE_FREE 0 +#define A_SIR_NOTIFY_RESELECTED_ON_SELECT 117 +#define A_SIR_NOTIFY_LOAD_SYNC 116 +#define A_SIR_NOTIFY_WSR 115 +#define A_SIR_NOTIFY_BLOCK_DATA_IN 114 +#define A_SIR_NOTIFY_DATA_OUT 113 +#define A_SIR_NOTIFY_DATA_IN 112 +#define A_SIR_NOTIFY_COMMAND 111 +#define A_SIR_NOTIFY_DUMP_NEXT_CODE 110 +#define A_SIR_NOTIFY_ISSUE_CHECK 109 +#define A_SIR_NOTIFY_WAIT_RESELECT 108 +#define A_SIR_NOTIFY_ISSUE 107 +#define A_SIR_NOTIFY_SIGP 106 +#define A_SIR_NOTIFY_DUMP2 105 +#define A_SIR_NOTIFY_DUMP 104 +#define A_SIR_NOTIFY_STATUS 103 +#define A_SIR_NOTIFY_MSG_IN 102 +#define A_SIR_NOTIFY_RESELECT 101 +#define A_SIR_NOTIFY_DISC 100 +#define A_SIR_MSG_IGNORE_WIDE_RESIDUE 16 +#define A_SIR_MSG_WDTR 15 +#define A_SIR_EV_PHASE_SWITCH_AFTER_ID 14 +#define A_error_sigp_set 13 +#define A_SIR_EV_RESPONSE_OK 12 +#define A_SIR_MSG_SDTR 11 +#define A_SIR_MSG_REJECT 10 +#define A_error_too_little_data 9 +#define A_error_too_much_data 8 +#define A_error_not_identify_after_reselect 7 +#define A_SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT 6 +#define A_error_weird_message 5 +#define A_error_unexpected_phase 4 +#define A_error_reselected 3 +#define A_error_disconnected 2 +#define A_error_not_cmd_complete 1 +#define A_SIR_MSG_IO_COMPLETE 0 diff --git a/os/pc/sd53c8xx.n b/os/pc/sd53c8xx.n new file mode 100644 index 00000000..d9dc2fa1 --- /dev/null +++ b/os/pc/sd53c8xx.n @@ -0,0 +1,448 @@ +// NCR 53c8xx driver for Plan 9 +// Nigel Roles (nigel@9fs.org) +// +// Microcode +// +// 27/5/02 Fixed problems with transfers >= 256 * 512 +// +// 13/3/01 Fixed microcode to support targets > 7 +// + +extern scsi_id_buf +extern msg_out_buf +extern cmd_buf +extern data_buf +extern status_buf +extern msgin_buf +extern dsa_0 +extern dsa_1 +extern dsa_head +extern ssid_mask + +SIR_MSG_IO_COMPLETE = 0 +error_not_cmd_complete = 1 +error_disconnected = 2 +error_reselected = 3 +error_unexpected_phase = 4 +error_weird_message = 5 +SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT = 6 +error_not_identify_after_reselect = 7 +error_too_much_data = 8 +error_too_little_data = 9 +SIR_MSG_REJECT = 10 +SIR_MSG_SDTR = 11 +SIR_EV_RESPONSE_OK = 12 +error_sigp_set = 13 +SIR_EV_PHASE_SWITCH_AFTER_ID = 14 +SIR_MSG_WDTR = 15 +SIR_MSG_IGNORE_WIDE_RESIDUE = 16 +SIR_NOTIFY_DISC = 100 +SIR_NOTIFY_RESELECT = 101 +SIR_NOTIFY_MSG_IN = 102 +SIR_NOTIFY_STATUS = 103 +SIR_NOTIFY_DUMP = 104 +SIR_NOTIFY_DUMP2 = 105 +SIR_NOTIFY_SIGP = 106 +SIR_NOTIFY_ISSUE = 107 +SIR_NOTIFY_WAIT_RESELECT = 108 +SIR_NOTIFY_ISSUE_CHECK = 109 +SIR_NOTIFY_DUMP_NEXT_CODE = 110 +SIR_NOTIFY_COMMAND = 111 +SIR_NOTIFY_DATA_IN = 112 +SIR_NOTIFY_DATA_OUT = 113 +SIR_NOTIFY_BLOCK_DATA_IN = 114 +SIR_NOTIFY_WSR = 115 +SIR_NOTIFY_LOAD_SYNC = 116 +SIR_NOTIFY_RESELECTED_ON_SELECT = 117 + +STATE_FREE = 0 +STATE_ALLOCATED = 1 +STATE_ISSUE = 2 +STATE_DISCONNECTED = 3 +STATE_DONE = 4 + +RESULT_OK = 0 + +MSG_IDENTIFY = 0x80 +MSG_DISCONNECT = 0x04 +MSG_SAVE_DATA_POINTER = 0x02 +MSG_RESTORE_POINTERS = 0x03 +MSG_IGNORE_WIDE_RESIDUE = 0x23 +X_MSG = 0x01 +X_MSG_SDTR = 0x01 +X_MSG_WDTR = 0x03 +MSG_REJECT = 0x07 + +BSIZE = 512 +//BSIZE=4096 + +idle: + jump wait_for_reselection +start: + call load_sync +// move 13 to ctest0 +// int SIR_NOTIFY_ISSUE + clear target + select atn from scsi_id_buf, reselected_on_select // do I need to clear ATN here? + jump start1, when msg_in +start1: +// move 14 to ctest0 + move from msg_out_buf, when msg_out +id_out_mismatch: + jump start1, when msg_out // repeat on parity grounds + jump to_decisions, when not cmd +cmd_phase: +// int SIR_NOTIFY_COMMAND + clear atn + move from cmd_buf, when cmd +cmd_out_mismatch: + jump to_decisions, when not data_in +data_in_phase: + move memory 4, state, scratcha + move memory 4, dmaaddr, scratchb +// int SIR_NOTIFY_DATA_IN +data_in_block_loop: + move scratcha2 to sfbr + jump data_in_normal, if 0 +// int SIR_NOTIFY_BLOCK_DATA_IN + move BSIZE, ptr dmaaddr, when data_in // transfer BSIZE bytes +data_in_block_mismatch: + move scratchb1 + BSIZE / 256 to scratchb1 // add BSIZE to scratchb + move scratchb2 + 0 to scratchb2 with carry + move scratchb3 + 0 to scratchb3 with carry + move scratcha2 + 255 to scratcha2 // sub one from block count + move memory 4, scratchb, dmaaddr // save latest dmaddr + jump data_in_block_loop, when data_in + move memory 4, scratcha, state // save latest state + call save_state + jump to_decisions +data_block_mismatch_recover: + move memory 4, scratchb, dmaaddr // save latest dmaddr +data_mismatch_recover: + move memory 4, scratcha, state // save latest state + jump to_decisions // no need to save + // as interrupt routine + // did this +data_in_normal: + move scratcha3 to sfbr + int error_too_much_data, if not 0 + move from data_buf, when data_in +data_in_mismatch: + move 2 to scratcha3 + move memory 4, scratcha, state + call save_state + jump post_data_to_decisions +data_out_phase: +// int SIR_NOTIFY_DATA_OUT + move memory 4, state, scratcha + move memory 4, dmaaddr, scratchb +data_out_block_loop: + move scratcha2 to sfbr + jump data_out_normal, if 0 + move memory 4, dmaaddr, scratchb + move BSIZE, ptr dmaaddr, when data_out // transfer BSIZE bytes +data_out_block_mismatch: + move scratchb1 + BSIZE / 256 to scratchb1 // add BSIZE to scratchb + move scratchb2 + 0 to scratchb2 with carry + move scratchb3 + 0 to scratchb3 with carry + move scratcha2 + 255 to scratcha2 // sub one from block count + move memory 4, scratchb, dmaaddr // save latest dmaddr + jump data_out_block_loop, when data_out + move memory 4, scratcha, state // save latest state + jump to_decisions +data_out_normal: + move scratcha3 to sfbr + int error_too_little_data, if not 0 + move from data_buf, when data_out +data_out_mismatch: + move 2 to scratcha3 + move memory 4, scratcha, state + call save_state + jump post_data_to_decisions +status_phase: + move from status_buf, when status +// int SIR_NOTIFY_STATUS + int error_unexpected_phase, when not msg_in +msg_in_phase: + move 1, scratcha, when msg_in +// int SIR_NOTIFY_MSG_IN + jump rejected, if MSG_REJECT +msg_in_not_reject: + jump disconnected, if MSG_DISCONNECT + jump msg_in_skip, if MSG_SAVE_DATA_POINTER + jump msg_in_skip, if MSG_RESTORE_POINTERS + jump ignore_wide, if MSG_IGNORE_WIDE_RESIDUE + jump extended, if X_MSG + int error_not_cmd_complete, if not 0 + move scntl2&0x7e to scntl2 // take care not to clear WSR + clear ack + wait disconnect + // update state + move memory 4, state, scratcha + move STATE_DONE to scratcha0 + move RESULT_OK to scratcha1 + move memory 4, scratcha, state + call save_state +// int SIR_MSG_IO_COMPLETE + intfly 0 + jump issue_check + +rejected: + int SIR_MSG_REJECT + clear ack + jump to_decisions +msg_in_skip: + clear ack + jump to_decisions + +extended: + clear ack + int error_unexpected_phase, when not msg_in + move 1, scratcha1, when msg_in + jump ext_3, if 3 + jump ext_2, if 2 + int error_weird_message, if not 1 +ext_1: + clear ack + int error_unexpected_phase, when not msg_in + move 1, scratcha1, when msg_in + jump ext_done + +ext_3: clear ack + int error_unexpected_phase, when not msg_in + move 1, scratcha1, when msg_in + clear ack + int error_unexpected_phase, when not msg_in + move 1, scratcha2, when msg_in + clear ack + int error_unexpected_phase, when not msg_in + move 1, scratcha3, when msg_in + move scratcha1 to sfbr + jump ext_done, if not X_MSG_SDTR + +// the target sent SDTR - leave ACK asserted and signal kernel +// kernel will either restart at reject, or continue +sdtr: int SIR_MSG_SDTR + clear ack + jump to_decisions + +ext_2: clear ack + int error_unexpected_phase, when not msg_in + move 1, scratcha1, when msg_in + clear ack + int error_unexpected_phase, when not msg_in + move 1, scratcha2, when msg_in + move scratcha1 to sfbr + jump ext_done, if not X_MSG_WDTR + +wdtr: int SIR_MSG_WDTR + clear ack + jump to_decisions + +ext_done: +// ought to check message here, but instead reject all +// NB ATN set +reject: + set atn // get target's ATN + clear ack // finish ACK + move MSG_REJECT to scratcha // prepare message + int error_unexpected_phase, when not msg_out// didn't get ATN + clear atn // last byte coming + move 1, scratcha, when msg_out // send byte + clear ack // finish ACK + jump reject, when msg_out // parity error + jump to_decisions + +ignore_wide: + clear ack + int error_unexpected_phase, when not msg_in + move 1, scratcha1, when msg_in + int SIR_MSG_IGNORE_WIDE_RESIDUE + clear ack + jump to_decisions + +// sends a response to a message +response: + set atn + clear ack + int error_unexpected_phase, when not msg_out +response_repeat: + move from msg_out_buf, when msg_out + jump response_repeat, when msg_out // repeat on parity grounds +// now look for response +// msg_in could be a REJECT +// anything other message is something else so signal kernel first + jump response_msg_in, when msg_in + int SIR_EV_RESPONSE_OK // not a MSG_IN so OK + jump to_decisions + +response_msg_in: + move 1, scratcha, when msg_in + jump rejected, if MSG_REJECT // go and generate rej interrupt + int SIR_EV_RESPONSE_OK // not a REJECT so OK + jump msg_in_not_reject // try others + +disconnected: +// move 5 to ctest0 + move scntl2&0x7e to scntl2 // don't clear WSR + clear ack + wait disconnect + // UPDATE state to disconnected + move memory 4, state, scratcha + move STATE_DISCONNECTED to scratcha0 + move memory 4, scratcha, state + call save_state +wsr_check: + move scntl2&0x01 to sfbr + int SIR_NOTIFY_WSR, if not 0 +// int SIR_NOTIFY_DISC + jump issue_check + +reselected_on_select: + int SIR_NOTIFY_RESELECTED_ON_SELECT + jump reselected + +wait_for_reselection: +// move 11 to ctest0 +// int SIR_NOTIFY_WAIT_RESELECT + wait reselect sigp_set +reselected: +// move 12 to ctest0 + clear target + int SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT, when not msg_in + move 1, scratchb, when msg_in + int error_not_identify_after_reselect, if not MSG_IDENTIFY and mask 0x1f +// int SIR_NOTIFY_RESELECT + // now locate the right DSA - note do not clear ACK, so target doesn't start + // synchronous transfer until we are ready +find_dsa: +// move 6 to ctest0 + move memory 4, dsa_head, dsa +find_dsa_loop: +// move 7 to ctest0 + move dsa0 to sfbr + jump find_dsa_1, if not 0 + move dsa1 to sfbr + jump find_dsa_1, if not 0 + move dsa2 to sfbr + jump find_dsa_1, if not 0 + move dsa3 to sfbr + int error_reselected, if 0 // couldn't match dsa (panic) +find_dsa_1: +// move 8 to ctest0 + // load state from DSA into dsa_copy + call load_state + move memory 4, state, scratcha // get dsastate in scratcha + move scratcha0 to sfbr // and state variable in sfbr + jump find_dsa_next, if not STATE_DISCONNECTED // wrong state + move ssid & ssid_mask to sfbr // get target ID + move memory 1, targ, find_dsa_smc1 // forge target comparison instruction +find_dsa_smc1: + jump find_dsa_next, if not 255 // jump if not matched + move memory 1, lun, find_dsa_smc2 // forge lun comparison instruction + move scratchb0 to sfbr // recover IDENTIFY message +find_dsa_smc2: + jump reload_sync, if 255 and mask ~7 // off we jolly well go +find_dsa_next: + move memory 4, next, dsa // find next + jump find_dsa_loop + +// id_out terminated early +// most likely the message wasn't recognised +// clear ATN and accept the message in +id_out_mismatch_recover: + clear atn + jump msg_in_phase, when msg_in + int SIR_MSG_REJECT + jump to_decisions + +// Reload synchronous registers after a reconnect. If the transfer is a synchronous read, then +// as soon as we clear ACK, the target will switch to data_in and start blasting data into the +// fifo. We need to be executing the 'jump when data_in' instruction before the target stops REQing +// since it is the REQ which latches the 'when'. The target will do 16 REQs before stopping, so +// we have 16 bytes (160uS) plus delays to do this after clearing ACK. Once the jump is executing, +// the target will wait, so as much debugging as you like can happen in data_in_phase, just don't +// stick any delays between 'clear ack' and 'jump data_in_phase, when data_in'. + +reload_sync: + call load_sync + clear ack +to_decisions: + jump data_in_phase, when data_in + jump cmd_phase, if cmd + jump data_out_phase, if data_out + jump status_phase, if status + jump msg_in_phase, if msg_in + int error_unexpected_phase +post_data_to_decisions: + jump status_phase, when status + jump msg_in_phase, if msg_in + int error_unexpected_phase + +// +// MULTI_TARGET +// +// following must mirror top of dsa structure +// the first section is loaded and saved, the +// second section loaded only +dsa_copy: +state: defw 0 // a0 is state, a1 result, a2 dma block count +dmaaddr: defw 0 // dma address for block moves +dsa_save_end: +targ: defw 0 // lsb is target +lun: defw 0 // lsb is lun +sync: defw 0 // lsb is scntl3, sxfer +next: defw 0 +dsa_load_end: +dsa_load_len = dsa_load_end - dsa_copy +dsa_save_len = dsa_save_end - dsa_copy + +load_state: + // load state from DSA into dsa_copy +// move 9 to ctest0 + move memory 4, dsa, load_state_smc0 + 4 +load_state_smc0: + move memory dsa_load_len, 0, dsa_copy +// move 20 to ctest0 + return +save_state: + move memory 4, dsa, save_state_smc0 + 8 +save_state_smc0: + move memory dsa_save_len, dsa_copy, 0 + return + +sigp_set: +// int SIR_NOTIFY_SIGP + move ctest2 to sfbr // clear SIGP +issue_check: +// int SIR_NOTIFY_ISSUE_CHECK +// move 1 to ctest0 + move memory 4, dsa_head, dsa +issue_check_loop: +// move 2 to ctest0 + move dsa0 to sfbr + jump issue_check_1, if not 0 + move dsa1 to sfbr + jump issue_check_1, if not 0 + move dsa2 to sfbr + jump issue_check_1, if not 0 + move dsa3 to sfbr + jump wait_for_reselection, if 0 // nothing to do +issue_check_1: +// move 3 to ctest0 + call load_state + move memory 4, state, scratcha // get dsastate in scratcha + move scratcha0 to sfbr // and state variable in sfbr + jump start, if STATE_ISSUE // right state +issue_check_next: +// move 4 to ctest0 + move memory 4, next, dsa // find next + jump issue_check_loop +load_sync: + move memory 4, sync, scratcha // load the sync stuff + move scratcha0 to sfbr // assuming load_state has been called + move sfbr to scntl3 + move scratcha1 to sfbr + move sfbr to sxfer +// int SIR_NOTIFY_LOAD_SYNC + return diff --git a/os/pc/sdata.c b/os/pc/sdata.c new file mode 100644 index 00000000..1e438986 --- /dev/null +++ b/os/pc/sdata.c @@ -0,0 +1,2206 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +#include "../port/sd.h" + +extern SDifc sdataifc; + +enum { + DbgCONFIG = 0x0001, /* detected drive config info */ + DbgIDENTIFY = 0x0002, /* detected drive identify info */ + DbgSTATE = 0x0004, /* dump state on panic */ + DbgPROBE = 0x0008, /* trace device probing */ + DbgDEBUG = 0x0080, /* the current problem... */ + DbgINL = 0x0100, /* That Inil20+ message we hate */ + Dbg48BIT = 0x0200, /* 48-bit LBA */ + DbgBsy = 0x0400, /* interrupt but Bsy (shared IRQ) */ +}; +#define DEBUG (DbgDEBUG|DbgSTATE) + +enum { /* I/O ports */ + Data = 0, + Error = 1, /* (read) */ + Features = 1, /* (write) */ + Count = 2, /* sector count<7-0>, sector count<15-8> */ + Ir = 2, /* interrupt reason (PACKET) */ + Sector = 3, /* sector number */ + Lbalo = 3, /* LBA<7-0>, LBA<31-24> */ + Cyllo = 4, /* cylinder low */ + Bytelo = 4, /* byte count low (PACKET) */ + Lbamid = 4, /* LBA<15-8>, LBA<39-32> */ + Cylhi = 5, /* cylinder high */ + Bytehi = 5, /* byte count hi (PACKET) */ + Lbahi = 5, /* LBA<23-16>, LBA<47-40> */ + Dh = 6, /* Device/Head, LBA<32-14> */ + Status = 7, /* (read) */ + Command = 7, /* (write) */ + + As = 2, /* Alternate Status (read) */ + Dc = 2, /* Device Control (write) */ +}; + +enum { /* Error */ + Med = 0x01, /* Media error */ + Ili = 0x01, /* command set specific (PACKET) */ + Nm = 0x02, /* No Media */ + Eom = 0x02, /* command set specific (PACKET) */ + Abrt = 0x04, /* Aborted command */ + Mcr = 0x08, /* Media Change Request */ + Idnf = 0x10, /* no user-accessible address */ + Mc = 0x20, /* Media Change */ + Unc = 0x40, /* Uncorrectable data error */ + Wp = 0x40, /* Write Protect */ + Icrc = 0x80, /* Interface CRC error */ +}; + +enum { /* Features */ + Dma = 0x01, /* data transfer via DMA (PACKET) */ + Ovl = 0x02, /* command overlapped (PACKET) */ +}; + +enum { /* Interrupt Reason */ + Cd = 0x01, /* Command/Data */ + Io = 0x02, /* I/O direction */ + Rel = 0x04, /* Bus Release */ +}; + +enum { /* Device/Head */ + Dev0 = 0xA0, /* Master */ + Dev1 = 0xB0, /* Slave */ + Lba = 0x40, /* LBA mode */ +}; + +enum { /* internal flags */ + Lba48 = 0x1, /* LBA48 mode */ + Lba48always = 0x2, /* ... */ +}; + +enum { /* Status, Alternate Status */ + Err = 0x01, /* Error */ + Chk = 0x01, /* Check error (PACKET) */ + Drq = 0x08, /* Data Request */ + Dsc = 0x10, /* Device Seek Complete */ + Serv = 0x10, /* Service */ + Df = 0x20, /* Device Fault */ + Dmrd = 0x20, /* DMA ready (PACKET) */ + Drdy = 0x40, /* Device Ready */ + Bsy = 0x80, /* Busy */ +}; + +enum { /* Command */ + Cnop = 0x00, /* NOP */ + Cdr = 0x08, /* Device Reset */ + Crs = 0x20, /* Read Sectors */ + Crs48 = 0x24, /* Read Sectors Ext */ + Crd48 = 0x25, /* Read w/ DMA Ext */ + Crdq48 = 0x26, /* Read w/ DMA Queued Ext */ + Crsm48 = 0x29, /* Read Multiple Ext */ + Cws = 0x30, /* Write Sectors */ + Cws48 = 0x34, /* Write Sectors Ext */ + Cwd48 = 0x35, /* Write w/ DMA Ext */ + Cwdq48 = 0x36, /* Write w/ DMA Queued Ext */ + Cwsm48 = 0x39, /* Write Multiple Ext */ + Cedd = 0x90, /* Execute Device Diagnostics */ + Cpkt = 0xA0, /* Packet */ + Cidpkt = 0xA1, /* Identify Packet Device */ + Crsm = 0xC4, /* Read Multiple */ + Cwsm = 0xC5, /* Write Multiple */ + Csm = 0xC6, /* Set Multiple */ + Crdq = 0xC7, /* Read DMA queued */ + Crd = 0xC8, /* Read DMA */ + Cwd = 0xCA, /* Write DMA */ + Cwdq = 0xCC, /* Write DMA queued */ + Cstandby = 0xE2, /* Standby */ + Cid = 0xEC, /* Identify Device */ + Csf = 0xEF, /* Set Features */ +}; + +enum { /* Device Control */ + Nien = 0x02, /* (not) Interrupt Enable */ + Srst = 0x04, /* Software Reset */ + Hob = 0x80, /* High Order Bit [sic] */ +}; + +enum { /* PCI Configuration Registers */ + Bmiba = 0x20, /* Bus Master Interface Base Address */ + Idetim = 0x40, /* IE Timing */ + Sidetim = 0x44, /* Slave IE Timing */ + Udmactl = 0x48, /* Ultra DMA/33 Control */ + Udmatim = 0x4A, /* Ultra DMA/33 Timing */ +}; + +enum { /* Bus Master IDE I/O Ports */ + Bmicx = 0, /* Command */ + Bmisx = 2, /* Status */ + Bmidtpx = 4, /* Descriptor Table Pointer */ +}; + +enum { /* Bmicx */ + Ssbm = 0x01, /* Start/Stop Bus Master */ + Rwcon = 0x08, /* Read/Write Control */ +}; + +enum { /* Bmisx */ + Bmidea = 0x01, /* Bus Master IDE Active */ + Idedmae = 0x02, /* IDE DMA Error (R/WC) */ + Ideints = 0x04, /* IDE Interrupt Status (R/WC) */ + Dma0cap = 0x20, /* Drive 0 DMA Capable */ + Dma1cap = 0x40, /* Drive 0 DMA Capable */ +}; +enum { /* Physical Region Descriptor */ + PrdEOT = 0x80000000, /* Bus Master IDE Active */ +}; + +enum { /* offsets into the identify info. */ + Iconfig = 0, /* general configuration */ + Ilcyl = 1, /* logical cylinders */ + Ilhead = 3, /* logical heads */ + Ilsec = 6, /* logical sectors per logical track */ + Iserial = 10, /* serial number */ + Ifirmware = 23, /* firmware revision */ + Imodel = 27, /* model number */ + Imaxrwm = 47, /* max. read/write multiple sectors */ + Icapabilities = 49, /* capabilities */ + Istandby = 50, /* device specific standby timer */ + Ipiomode = 51, /* PIO data transfer mode number */ + Ivalid = 53, + Iccyl = 54, /* cylinders if (valid&0x01) */ + Ichead = 55, /* heads if (valid&0x01) */ + Icsec = 56, /* sectors if (valid&0x01) */ + Iccap = 57, /* capacity if (valid&0x01) */ + Irwm = 59, /* read/write multiple */ + Ilba = 60, /* LBA size */ + Imwdma = 63, /* multiword DMA mode */ + Iapiomode = 64, /* advanced PIO modes supported */ + Iminmwdma = 65, /* min. multiword DMA cycle time */ + Irecmwdma = 66, /* rec. multiword DMA cycle time */ + Iminpio = 67, /* min. PIO cycle w/o flow control */ + Iminiordy = 68, /* min. PIO cycle with IORDY */ + Ipcktbr = 71, /* time from PACKET to bus release */ + Iserbsy = 72, /* time from SERVICE to !Bsy */ + Iqdepth = 75, /* max. queue depth */ + Imajor = 80, /* major version number */ + Iminor = 81, /* minor version number */ + Icsfs = 82, /* command set/feature supported */ + Icsfe = 85, /* command set/feature enabled */ + Iudma = 88, /* ultra DMA mode */ + Ierase = 89, /* time for security erase */ + Ieerase = 90, /* time for enhanced security erase */ + Ipower = 91, /* current advanced power management */ + Ilba48 = 100, /* 48-bit LBA size (64 bits in 100-103) */ + Irmsn = 127, /* removable status notification */ + Isecstat = 128, /* security status */ + Icfapwr = 160, /* CFA power mode */ + Imediaserial = 176, /* current media serial number */ + Icksum = 255, /* checksum */ +}; + +enum { /* bit masks for config identify info */ + Mpktsz = 0x0003, /* packet command size */ + Mincomplete = 0x0004, /* incomplete information */ + Mdrq = 0x0060, /* DRQ type */ + Mrmdev = 0x0080, /* device is removable */ + Mtype = 0x1F00, /* device type */ + Mproto = 0x8000, /* command protocol */ +}; + +enum { /* bit masks for capabilities identify info */ + Mdma = 0x0100, /* DMA supported */ + Mlba = 0x0200, /* LBA supported */ + Mnoiordy = 0x0400, /* IORDY may be disabled */ + Miordy = 0x0800, /* IORDY supported */ + Msoftrst = 0x1000, /* needs soft reset when Bsy */ + Mstdby = 0x2000, /* standby supported */ + Mqueueing = 0x4000, /* queueing overlap supported */ + Midma = 0x8000, /* interleaved DMA supported */ +}; + +enum { /* bit masks for supported/enabled features */ + Msmart = 0x0001, + Msecurity = 0x0002, + Mrmmedia = 0x0004, + Mpwrmgmt = 0x0008, + Mpkt = 0x0010, + Mwcache = 0x0020, + Mlookahead = 0x0040, + Mrelirq = 0x0080, + Msvcirq = 0x0100, + Mreset = 0x0200, + Mprotected = 0x0400, + Mwbuf = 0x1000, + Mrbuf = 0x2000, + Mnop = 0x4000, + Mmicrocode = 0x0001, + Mqueued = 0x0002, + Mcfa = 0x0004, + Mapm = 0x0008, + Mnotify = 0x0010, + Mstandby = 0x0020, + Mspinup = 0x0040, + Mmaxsec = 0x0100, + Mautoacoustic = 0x0200, + Maddr48 = 0x0400, + Mdevconfov = 0x0800, + Mflush = 0x1000, + Mflush48 = 0x2000, + Msmarterror = 0x0001, + Msmartselftest = 0x0002, + Mmserial = 0x0004, + Mmpassthru = 0x0008, + Mlogging = 0x0020, +}; + +typedef struct Ctlr Ctlr; +typedef struct Drive Drive; + +typedef struct Prd { + ulong pa; /* Physical Base Address */ + int count; +} Prd; + +enum { + Nprd = SDmaxio/(64*1024)+2, +}; + +typedef struct Ctlr { + int cmdport; + int ctlport; + int irq; + int tbdf; + int bmiba; /* bus master interface base address */ + + Pcidev* pcidev; + void (*ienable)(Ctlr*); + void (*idisable)(Ctlr*); + SDev* sdev; + + Drive* drive[2]; + + Prd* prdt; /* physical region descriptor table */ + void* prdtbase; + + QLock; /* current command */ + Drive* curdrive; + int command; /* last command issued (debugging) */ + Rendez; + int done; + + Lock; /* register access */ +} Ctlr; + +typedef struct Drive { + Ctlr* ctlr; + + int dev; + ushort info[256]; + int c; /* cylinder */ + int h; /* head */ + int s; /* sector */ + vlong sectors; /* total */ + int secsize; /* sector size */ + + int dma; /* DMA R/W possible */ + int dmactl; + int rwm; /* read/write multiple possible */ + int rwmctl; + + int pkt; /* PACKET device, length of pktcmd */ + uchar pktcmd[16]; + int pktdma; /* this PACKET command using dma */ + + uchar sense[18]; + uchar inquiry[48]; + + QLock; /* drive access */ + int command; /* current command */ + int write; + uchar* data; + int dlen; + uchar* limit; + int count; /* sectors */ + int block; /* R/W bytes per block */ + int status; + int error; + int flags; /* internal flags */ +} Drive; + +static void +pc87415ienable(Ctlr* ctlr) +{ + Pcidev *p; + int x; + + p = ctlr->pcidev; + if(p == nil) + return; + + x = pcicfgr32(p, 0x40); + if(ctlr->cmdport == p->mem[0].bar) + x &= ~0x00000100; + else + x &= ~0x00000200; + pcicfgw32(p, 0x40, x); +} + +static void +atadumpstate(Drive* drive, uchar* cmd, vlong lba, int count) +{ + Prd *prd; + Pcidev *p; + Ctlr *ctlr; + int i, bmiba; + + if(!(DEBUG & DbgSTATE)){ + USED(drive, cmd, lba, count); + return; + } + + ctlr = drive->ctlr; + print("command %2.2uX\n", ctlr->command); + print("data %8.8p limit %8.8p dlen %d status %uX error %uX\n", + drive->data, drive->limit, drive->dlen, + drive->status, drive->error); + if(cmd != nil){ + print("lba %d -> %lld, count %d -> %d (%d)\n", + (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5], lba, + (cmd[7]<<8)|cmd[8], count, drive->count); + } + if(!(inb(ctlr->ctlport+As) & Bsy)){ + for(i = 1; i < 7; i++) + print(" 0x%2.2uX", inb(ctlr->cmdport+i)); + print(" 0x%2.2uX\n", inb(ctlr->ctlport+As)); + } + if(drive->command == Cwd || drive->command == Crd){ + bmiba = ctlr->bmiba; + prd = ctlr->prdt; + print("bmicx %2.2uX bmisx %2.2uX prdt %8.8p\n", + inb(bmiba+Bmicx), inb(bmiba+Bmisx), prd); + for(;;){ + print("pa 0x%8.8luX count %8.8uX\n", + prd->pa, prd->count); + if(prd->count & PrdEOT) + break; + prd++; + } + } + if(ctlr->pcidev && ctlr->pcidev->vid == 0x8086){ + p = ctlr->pcidev; + print("0x40: %4.4uX 0x42: %4.4uX", + pcicfgr16(p, 0x40), pcicfgr16(p, 0x42)); + print("0x48: %2.2uX\n", pcicfgr8(p, 0x48)); + print("0x4A: %4.4uX\n", pcicfgr16(p, 0x4A)); + } +} + +static int +atadebug(int cmdport, int ctlport, char* fmt, ...) +{ + int i, n; + va_list arg; + char buf[PRINTSIZE]; + + if(!(DEBUG & DbgPROBE)){ + USED(cmdport, ctlport, fmt); + return 0; + } + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + + if(cmdport){ + if(buf[n-1] == '\n') + n--; + n += snprint(buf+n, PRINTSIZE-n, " ataregs 0x%uX:", + cmdport); + for(i = Features; i < Command; i++) + n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX", + inb(cmdport+i)); + if(ctlport) + n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX", + inb(ctlport+As)); + n += snprint(buf+n, PRINTSIZE-n, "\n"); + } + putstrn(buf, n); + + return n; +} + +static int +ataready(int cmdport, int ctlport, int dev, int reset, int ready, int micro) +{ + int as; + + atadebug(cmdport, ctlport, "ataready: dev %uX reset %uX ready %uX", + dev, reset, ready); + + for(;;){ + /* + * Wait for the controller to become not busy and + * possibly for a status bit to become true (usually + * Drdy). Must change to the appropriate device + * register set if necessary before testing for ready. + * Always run through the loop at least once so it + * can be used as a test for !Bsy. + */ + as = inb(ctlport+As); + if(as & reset){ + /* nothing to do */ + } + else if(dev){ + outb(cmdport+Dh, dev); + dev = 0; + } + else if(ready == 0 || (as & ready)){ + atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as); + return as; + } + + if(micro-- <= 0){ + atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as); + break; + } + microdelay(1); + } + atadebug(cmdport, ctlport, "ataready: timeout"); + + return -1; +} + +/* +static int +atacsf(Drive* drive, vlong csf, int supported) +{ + ushort *info; + int cmdset, i, x; + + if(supported) + info = &drive->info[Icsfs]; + else + info = &drive->info[Icsfe]; + + for(i = 0; i < 3; i++){ + x = (csf>>(16*i)) & 0xFFFF; + if(x == 0) + continue; + cmdset = info[i]; + if(cmdset == 0 || cmdset == 0xFFFF) + return 0; + return cmdset & x; + } + + return 0; +} +*/ + +static int +atadone(void* arg) +{ + return ((Ctlr*)arg)->done; +} + +static int +atarwmmode(Drive* drive, int cmdport, int ctlport, int dev) +{ + int as, maxrwm, rwm; + + maxrwm = (drive->info[Imaxrwm] & 0xFF); + if(maxrwm == 0) + return 0; + + /* + * Sometimes drives come up with the current count set + * to 0; if so, set a suitable value, otherwise believe + * the value in Irwm if the 0x100 bit is set. + */ + if(drive->info[Irwm] & 0x100) + rwm = (drive->info[Irwm] & 0xFF); + else + rwm = 0; + if(rwm == 0) + rwm = maxrwm; + if(rwm > 16) + rwm = 16; + if(ataready(cmdport, ctlport, dev, Bsy|Drq, Drdy, 102*1000) < 0) + return 0; + outb(cmdport+Count, rwm); + outb(cmdport+Command, Csm); + microdelay(1); + as = ataready(cmdport, ctlport, 0, Bsy, Drdy|Df|Err, 1000); + inb(cmdport+Status); + if(as < 0 || (as & (Df|Err))) + return 0; + + drive->rwm = rwm; + + return rwm; +} + +static int +atadmamode(Drive* drive) +{ + int dma; + + /* + * Check if any DMA mode enabled. + * Assumes the BIOS has picked and enabled the best. + * This is completely passive at the moment, no attempt is + * made to ensure the hardware is correctly set up. + */ + dma = drive->info[Imwdma] & 0x0707; + drive->dma = (dma>>8) & dma; + if(drive->dma == 0 && (drive->info[Ivalid] & 0x04)){ + dma = drive->info[Iudma] & 0x7F7F; + drive->dma = (dma>>8) & dma; + if(drive->dma) + drive->dma |= 'U'<<16; + } + + return dma; +} + +static int +ataidentify(int cmdport, int ctlport, int dev, int pkt, void* info) +{ + int as, command, drdy; + + if(pkt){ + command = Cidpkt; + drdy = 0; + } + else{ + command = Cid; + drdy = Drdy; + } + as = ataready(cmdport, ctlport, dev, Bsy|Drq, drdy, 103*1000); + if(as < 0) + return as; + outb(cmdport+Command, command); + microdelay(1); + + as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 400*1000); + if(as < 0) + return -1; + if(as & Err) + return as; + + memset(info, 0, 512); + inss(cmdport+Data, info, 256); + inb(cmdport+Status); + + if(DEBUG & DbgIDENTIFY){ + int i; + ushort *sp; + + sp = (ushort*)info; + for(i = 0; i < 256; i++){ + if(i && (i%16) == 0) + print("\n"); + print(" %4.4uX", *sp); + sp++; + } + print("\n"); + } + + return 0; +} + +static Drive* +atadrive(int cmdport, int ctlport, int dev) +{ + Drive *drive; + int as, i, pkt; + uchar buf[512], *p; + ushort iconfig, *sp; + + atadebug(0, 0, "identify: port 0x%uX dev 0x%2.2uX\n", cmdport, dev); + pkt = 1; +retry: + as = ataidentify(cmdport, ctlport, dev, pkt, buf); + if(as < 0) + return nil; + if(as & Err){ + if(pkt == 0) + return nil; + pkt = 0; + goto retry; + } + + if((drive = malloc(sizeof(Drive))) == nil) + return nil; + drive->dev = dev; + memmove(drive->info, buf, sizeof(drive->info)); + drive->sense[0] = 0x70; + drive->sense[7] = sizeof(drive->sense)-7; + + drive->inquiry[2] = 2; + drive->inquiry[3] = 2; + drive->inquiry[4] = sizeof(drive->inquiry)-4; + p = &drive->inquiry[8]; + sp = &drive->info[Imodel]; + for(i = 0; i < 20; i++){ + *p++ = *sp>>8; + *p++ = *sp++; + } + + drive->secsize = 512; + + /* + * Beware the CompactFlash Association feature set. + * Now, why this value in Iconfig just walks all over the bit + * definitions used in the other parts of the ATA/ATAPI standards + * is a mystery and a sign of true stupidity on someone's part. + * Anyway, the standard says if this value is 0x848A then it's + * CompactFlash and it's NOT a packet device. + */ + iconfig = drive->info[Iconfig]; + if(iconfig != 0x848A && (iconfig & 0xC000) == 0x8000){ + if(iconfig & 0x01) + drive->pkt = 16; + else + drive->pkt = 12; + } + else{ + if(drive->info[Ivalid] & 0x0001){ + drive->c = drive->info[Iccyl]; + drive->h = drive->info[Ichead]; + drive->s = drive->info[Icsec]; + }else{ + drive->c = drive->info[Ilcyl]; + drive->h = drive->info[Ilhead]; + drive->s = drive->info[Ilsec]; + } + if(drive->info[Icapabilities] & Mlba){ + if(drive->info[Icsfs+1] & Maddr48){ + drive->sectors = drive->info[Ilba48] + | (drive->info[Ilba48+1]<<16) + | ((vlong)drive->info[Ilba48+2]<<32); + drive->flags |= Lba48; + }else{ + drive->sectors = (drive->info[Ilba+1]<<16) + |drive->info[Ilba]; + } + drive->dev |= Lba; + }else + drive->sectors = drive->c*drive->h*drive->s; + atarwmmode(drive, cmdport, ctlport, dev); + } + atadmamode(drive); + + if(DEBUG & DbgCONFIG){ + print("dev %2.2uX port %uX config %4.4uX capabilities %4.4uX", + dev, cmdport, iconfig, drive->info[Icapabilities]); + print(" mwdma %4.4uX", drive->info[Imwdma]); + if(drive->info[Ivalid] & 0x04) + print(" udma %4.4uX", drive->info[Iudma]); + print(" dma %8.8uX rwm %ud\n", drive->dma, drive->rwm); + if(drive->flags&Lba48) + print("\tLLBA sectors %lld\n", drive->sectors); + } + + return drive; +} + +static void +atasrst(int ctlport) +{ + /* + * Srst is a big stick and may cause problems if further + * commands are tried before the drives become ready again. + * Also, there will be problems here if overlapped commands + * are ever supported. + */ + microdelay(5); + outb(ctlport+Dc, Srst); + microdelay(5); + outb(ctlport+Dc, 0); + microdelay(2*1000); +} + +static SDev* +ataprobe(int cmdport, int ctlport, int irq) +{ + Ctlr* ctlr; + SDev *sdev; + Drive *drive; + int dev, error, rhi, rlo; + + if(ioalloc(cmdport, 8, 0, "atacmd") < 0) { + print("ataprobe: Cannot allocate %X\n", cmdport); + return nil; + } + if(ioalloc(ctlport+As, 1, 0, "atactl") < 0){ + print("ataprobe: Cannot allocate %X\n", ctlport + As); + iofree(cmdport); + return nil; + } + + /* + * Try to detect a floating bus. + * Bsy should be cleared. If not, see if the cylinder registers + * are read/write capable. + * If the master fails, try the slave to catch slave-only + * configurations. + * There's no need to restore the tested registers as they will + * be reset on any detected drives by the Cedd command. + * All this indicates is that there is at least one drive on the + * controller; when the non-existent drive is selected in a + * single-drive configuration the registers of the existing drive + * are often seen, only command execution fails. + */ + dev = Dev0; + if(inb(ctlport+As) & Bsy){ + outb(cmdport+Dh, dev); + microdelay(1); +trydev1: + atadebug(cmdport, ctlport, "ataprobe bsy"); + outb(cmdport+Cyllo, 0xAA); + outb(cmdport+Cylhi, 0x55); + outb(cmdport+Sector, 0xFF); + rlo = inb(cmdport+Cyllo); + rhi = inb(cmdport+Cylhi); + if(rlo != 0xAA && (rlo == 0xFF || rhi != 0x55)){ + if(dev == Dev1){ +release: + iofree(cmdport); + iofree(ctlport+As); + return nil; + } + dev = Dev1; + if(ataready(cmdport, ctlport, dev, Bsy, 0, 20*1000) < 0) + goto trydev1; + } + } + + /* + * Disable interrupts on any detected controllers. + */ + outb(ctlport+Dc, Nien); +tryedd1: + if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 105*1000) < 0){ + /* + * There's something there, but it didn't come up clean, + * so try hitting it with a big stick. The timing here is + * wrong but this is a last-ditch effort and it sometimes + * gets some marginal hardware back online. + */ + atasrst(ctlport); + if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 106*1000) < 0) + goto release; + } + + /* + * Can only get here if controller is not busy. + * If there are drives Bsy will be set within 400nS, + * must wait 2mS before testing Status. + * Wait for the command to complete (6 seconds max). + */ + outb(cmdport+Command, Cedd); + delay(2); + if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 6*1000*1000) < 0) + goto release; + + /* + * If bit 0 of the error register is set then the selected drive + * exists. This is enough to detect single-drive configurations. + * However, if the master exists there is no way short of executing + * a command to determine if a slave is present. + * It appears possible to get here testing Dev0 although it doesn't + * exist and the EDD won't take, so try again with Dev1. + */ + error = inb(cmdport+Error); + atadebug(cmdport, ctlport, "ataprobe: dev %uX", dev); + if((error & ~0x80) != 0x01){ + if(dev == Dev1) + goto release; + dev = Dev1; + goto tryedd1; + } + + /* + * At least one drive is known to exist, try to + * identify it. If that fails, don't bother checking + * any further. + * If the one drive found is Dev0 and the EDD command + * didn't indicate Dev1 doesn't exist, check for it. + */ + if((drive = atadrive(cmdport, ctlport, dev)) == nil) + goto release; + if((ctlr = malloc(sizeof(Ctlr))) == nil){ + free(drive); + goto release; + } + memset(ctlr, 0, sizeof(Ctlr)); + if((sdev = malloc(sizeof(SDev))) == nil){ + free(ctlr); + free(drive); + goto release; + } + memset(sdev, 0, sizeof(SDev)); + drive->ctlr = ctlr; + if(dev == Dev0){ + ctlr->drive[0] = drive; + if(!(error & 0x80)){ + /* + * Always leave Dh pointing to a valid drive, + * otherwise a subsequent call to ataready on + * this controller may try to test a bogus Status. + * Ataprobe is the only place possibly invalid + * drives should be selected. + */ + drive = atadrive(cmdport, ctlport, Dev1); + if(drive != nil){ + drive->ctlr = ctlr; + ctlr->drive[1] = drive; + } + else{ + outb(cmdport+Dh, Dev0); + microdelay(1); + } + } + } + else + ctlr->drive[1] = drive; + + ctlr->cmdport = cmdport; + ctlr->ctlport = ctlport; + ctlr->irq = irq; + ctlr->tbdf = BUSUNKNOWN; + ctlr->command = Cedd; /* debugging */ + + sdev->ifc = &sdataifc; + sdev->ctlr = ctlr; + sdev->nunit = 2; + ctlr->sdev = sdev; + + return sdev; +} + +static void +ataclear(SDev *sdev) +{ + Ctlr* ctlr; + + ctlr = sdev->ctlr; + iofree(ctlr->cmdport); + iofree(ctlr->ctlport + As); + + if (ctlr->drive[0]) + free(ctlr->drive[0]); + if (ctlr->drive[1]) + free(ctlr->drive[1]); + if (sdev->name) + free(sdev->name); + if (sdev->unitflg) + free(sdev->unitflg); + if (sdev->unit) + free(sdev->unit); + free(ctlr); + free(sdev); +} + +static char * +atastat(SDev *sdev, char *p, char *e) +{ + Ctlr *ctlr = sdev->ctlr; + + return seprint(p, e, "%s ata port %X ctl %X irq %d\n", + sdev->name, ctlr->cmdport, ctlr->ctlport, ctlr->irq); +} + +static SDev* +ataprobew(DevConf *cf) +{ + if (cf->nports != 2) + error(Ebadarg); + + return ataprobe(cf->ports[0].port, cf->ports[1].port, cf->intnum); +} + +static int +atasetsense(Drive* drive, int status, int key, int asc, int ascq) +{ + drive->sense[2] = key; + drive->sense[12] = asc; + drive->sense[13] = ascq; + + return status; +} + +static int +atastandby(Drive* drive, int period) +{ + Ctlr* ctlr; + int cmdport, done; + + ctlr = drive->ctlr; + drive->command = Cstandby; + qlock(ctlr); + + cmdport = ctlr->cmdport; + ilock(ctlr); + outb(cmdport+Count, period); + outb(cmdport+Dh, drive->dev); + ctlr->done = 0; + ctlr->curdrive = drive; + ctlr->command = Cstandby; /* debugging */ + outb(cmdport+Command, Cstandby); + iunlock(ctlr); + + while(waserror()) + ; + tsleep(ctlr, atadone, ctlr, 30*1000); + poperror(); + + done = ctlr->done; + qunlock(ctlr); + + if(!done || (drive->status & Err)) + return atasetsense(drive, SDcheck, 4, 8, drive->error); + return SDok; +} + +static int +atamodesense(Drive* drive, uchar* cmd) +{ + int len; + + /* + * Fake a vendor-specific request with page code 0, + * return the drive info. + */ + if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F) + return atasetsense(drive, SDcheck, 0x05, 0x24, 0); + len = (cmd[7]<<8)|cmd[8]; + if(len == 0) + return SDok; + if(len < 8+sizeof(drive->info)) + return atasetsense(drive, SDcheck, 0x05, 0x1A, 0); + if(drive->data == nil || drive->dlen < len) + return atasetsense(drive, SDcheck, 0x05, 0x20, 1); + memset(drive->data, 0, 8); + drive->data[0] = sizeof(drive->info)>>8; + drive->data[1] = sizeof(drive->info); + memmove(drive->data+8, drive->info, sizeof(drive->info)); + drive->data += 8+sizeof(drive->info); + + return SDok; +} + +static void +atanop(Drive* drive, int subcommand) +{ + Ctlr* ctlr; + int as, cmdport, ctlport, timeo; + + /* + * Attempt to abort a command by using NOP. + * In response, the drive is supposed to set Abrt + * in the Error register, set (Drdy|Err) in Status + * and clear Bsy when done. However, some drives + * (e.g. ATAPI Zip) just go Bsy then clear Status + * when done, hence the timeout loop only on Bsy + * and the forced setting of drive->error. + */ + ctlr = drive->ctlr; + cmdport = ctlr->cmdport; + outb(cmdport+Features, subcommand); + outb(cmdport+Dh, drive->dev); + ctlr->command = Cnop; /* debugging */ + outb(cmdport+Command, Cnop); + + microdelay(1); + ctlport = ctlr->ctlport; + for(timeo = 0; timeo < 1000; timeo++){ + as = inb(ctlport+As); + if(!(as & Bsy)) + break; + microdelay(1); + } + drive->error |= Abrt; +} + +static void +ataabort(Drive* drive, int dolock) +{ + /* + * If NOP is available (packet commands) use it otherwise + * must try a software reset. + */ + if(dolock) + ilock(drive->ctlr); + if(drive->info[Icsfs] & Mnop) + atanop(drive, 0); + else{ + atasrst(drive->ctlr->ctlport); + drive->error |= Abrt; + } + if(dolock) + iunlock(drive->ctlr); +} + +static int +atadmasetup(Drive* drive, int len) +{ + Prd *prd; + ulong pa; + Ctlr *ctlr; + int bmiba, bmisx, count; + + pa = PCIWADDR(drive->data); + if(pa & 0x03) + return -1; + ctlr = drive->ctlr; + prd = ctlr->prdt; + + /* + * Sometimes drives identify themselves as being DMA capable + * although they are not on a busmastering controller. + */ + if(prd == nil){ + drive->dmactl = 0; + print("disabling dma: not on a busmastering controller\n"); + return -1; + } + + for(;;){ + prd->pa = pa; + count = 64*1024 - (pa & 0xFFFF); + if(count >= len){ + prd->count = PrdEOT|(len & 0xFFFF); + break; + } + prd->count = count; + len -= count; + pa += count; + prd++; + } + + bmiba = ctlr->bmiba; + outl(bmiba+Bmidtpx, PCIWADDR(ctlr->prdt)); + if(drive->write) + outb(ctlr->bmiba+Bmicx, 0); + else + outb(ctlr->bmiba+Bmicx, Rwcon); + bmisx = inb(bmiba+Bmisx); + outb(bmiba+Bmisx, bmisx|Ideints|Idedmae); + + return 0; +} + +static void +atadmastart(Ctlr* ctlr, int write) +{ + if(write) + outb(ctlr->bmiba+Bmicx, Ssbm); + else + outb(ctlr->bmiba+Bmicx, Rwcon|Ssbm); +} + +static int +atadmastop(Ctlr* ctlr) +{ + int bmiba; + + bmiba = ctlr->bmiba; + outb(bmiba+Bmicx, inb(bmiba+Bmicx) & ~Ssbm); + + return inb(bmiba+Bmisx); +} + +static void +atadmainterrupt(Drive* drive, int count) +{ + Ctlr* ctlr; + int bmiba, bmisx; + + ctlr = drive->ctlr; + bmiba = ctlr->bmiba; + bmisx = inb(bmiba+Bmisx); + switch(bmisx & (Ideints|Idedmae|Bmidea)){ + case Bmidea: + /* + * Data transfer still in progress, nothing to do + * (this should never happen). + */ + return; + + case Ideints: + case Ideints|Bmidea: + /* + * Normal termination, tidy up. + */ + drive->data += count; + break; + + default: + /* + * What's left are error conditions (memory transfer + * problem) and the device is not done but the PRD is + * exhausted. For both cases must somehow tell the + * drive to abort. + */ + ataabort(drive, 0); + break; + } + atadmastop(ctlr); + ctlr->done = 1; +} + +static void +atapktinterrupt(Drive* drive) +{ + Ctlr* ctlr; + int cmdport, len; + + ctlr = drive->ctlr; + cmdport = ctlr->cmdport; + switch(inb(cmdport+Ir) & (/*Rel|*/Io|Cd)){ + case Cd: + outss(cmdport+Data, drive->pktcmd, drive->pkt/2); + break; + + case 0: + len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo); + if(drive->data+len > drive->limit){ + atanop(drive, 0); + break; + } + outss(cmdport+Data, drive->data, len/2); + drive->data += len; + break; + + case Io: + len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo); + if(drive->data+len > drive->limit){ + atanop(drive, 0); + break; + } + inss(cmdport+Data, drive->data, len/2); + drive->data += len; + break; + + case Io|Cd: + if(drive->pktdma) + atadmainterrupt(drive, drive->dlen); + else + ctlr->done = 1; + break; + } +} + +static int +atapktio(Drive* drive, uchar* cmd, int clen) +{ + Ctlr *ctlr; + int as, cmdport, ctlport, len, r, timeo; + + if(cmd[0] == 0x5A && (cmd[2] & 0x3F) == 0) + return atamodesense(drive, cmd); + + r = SDok; + + drive->command = Cpkt; + memmove(drive->pktcmd, cmd, clen); + memset(drive->pktcmd+clen, 0, drive->pkt-clen); + drive->limit = drive->data+drive->dlen; + + ctlr = drive->ctlr; + cmdport = ctlr->cmdport; + ctlport = ctlr->ctlport; + + qlock(ctlr); + + if(ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 107*1000) < 0){ + qunlock(ctlr); + return -1; + } + + ilock(ctlr); + if(drive->dlen && drive->dmactl && !atadmasetup(drive, drive->dlen)) + drive->pktdma = Dma; + else + drive->pktdma = 0; + + outb(cmdport+Features, drive->pktdma); + outb(cmdport+Count, 0); + outb(cmdport+Sector, 0); + len = 16*drive->secsize; + outb(cmdport+Bytelo, len); + outb(cmdport+Bytehi, len>>8); + outb(cmdport+Dh, drive->dev); + ctlr->done = 0; + ctlr->curdrive = drive; + ctlr->command = Cpkt; /* debugging */ + if(drive->pktdma) + atadmastart(ctlr, drive->write); + outb(cmdport+Command, Cpkt); + + if((drive->info[Iconfig] & Mdrq) != 0x0020){ + microdelay(1); + as = ataready(cmdport, ctlport, 0, Bsy, Drq|Chk, 4*1000); + if(as < 0) + r = SDtimeout; + else if(as & Chk) + r = SDcheck; + else + atapktinterrupt(drive); + } + iunlock(ctlr); + + while(waserror()) + ; + if(!drive->pktdma) + sleep(ctlr, atadone, ctlr); + else for(timeo = 0; !ctlr->done; timeo++){ + tsleep(ctlr, atadone, ctlr, 1000); + if(ctlr->done) + break; + ilock(ctlr); + atadmainterrupt(drive, 0); + if(!drive->error && timeo > 10){ + ataabort(drive, 0); + atadmastop(ctlr); + drive->dmactl = 0; + drive->error |= Abrt; + } + if(drive->error){ + drive->status |= Chk; + ctlr->curdrive = nil; + } + iunlock(ctlr); + } + poperror(); + + qunlock(ctlr); + + if(drive->status & Chk) + r = SDcheck; + + return r; +} + +static uchar cmd48[256] = { + [Crs] Crs48, + [Crd] Crd48, + [Crdq] Crdq48, + [Crsm] Crsm48, + [Cws] Cws48, + [Cwd] Cwd48, + [Cwdq] Cwdq48, + [Cwsm] Cwsm48, +}; + +static int +atageniostart(Drive* drive, vlong lba) +{ + Ctlr *ctlr; + uchar cmd; + int as, c, cmdport, ctlport, h, len, s, use48; + + use48 = 0; + if((drive->flags&Lba48always) || (lba>>28) || drive->count > 256){ + if(!(drive->flags & Lba48)) + return -1; + use48 = 1; + c = h = s = 0; + }else if(drive->dev & Lba){ + c = (lba>>8) & 0xFFFF; + h = (lba>>24) & 0x0F; + s = lba & 0xFF; + }else{ + c = lba/(drive->s*drive->h); + h = ((lba/drive->s) % drive->h); + s = (lba % drive->s) + 1; + } + + ctlr = drive->ctlr; + cmdport = ctlr->cmdport; + ctlport = ctlr->ctlport; + if(ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 101*1000) < 0) + return -1; + + ilock(ctlr); + if(drive->dmactl && !atadmasetup(drive, drive->count*drive->secsize)){ + if(drive->write) + drive->command = Cwd; + else + drive->command = Crd; + } + else if(drive->rwmctl){ + drive->block = drive->rwm*drive->secsize; + if(drive->write) + drive->command = Cwsm; + else + drive->command = Crsm; + } + else{ + drive->block = drive->secsize; + if(drive->write) + drive->command = Cws; + else + drive->command = Crs; + } + drive->limit = drive->data + drive->count*drive->secsize; + cmd = drive->command; + if(use48){ + outb(cmdport+Count, (drive->count>>8) & 0xFF); + outb(cmdport+Count, drive->count & 0XFF); + outb(cmdport+Lbalo, (lba>>24) & 0xFF); + outb(cmdport+Lbalo, lba & 0xFF); + outb(cmdport+Lbamid, (lba>>32) & 0xFF); + outb(cmdport+Lbamid, (lba>>8) & 0xFF); + outb(cmdport+Lbahi, (lba>>40) & 0xFF); + outb(cmdport+Lbahi, (lba>>16) & 0xFF); + outb(cmdport+Dh, drive->dev|Lba); + cmd = cmd48[cmd]; + + if(DEBUG & Dbg48BIT) + print("using 48-bit commands\n"); + }else{ + outb(cmdport+Count, drive->count); + outb(cmdport+Sector, s); + outb(cmdport+Cyllo, c); + outb(cmdport+Cylhi, c>>8); + outb(cmdport+Dh, drive->dev|h); + } + ctlr->done = 0; + ctlr->curdrive = drive; + ctlr->command = drive->command; /* debugging */ + outb(cmdport+Command, cmd); + + switch(drive->command){ + case Cws: + case Cwsm: + microdelay(1); + as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 1000); + if(as < 0 || (as & Err)){ + iunlock(ctlr); + return -1; + } + len = drive->block; + if(drive->data+len > drive->limit) + len = drive->limit-drive->data; + outss(cmdport+Data, drive->data, len/2); + break; + + case Crd: + case Cwd: + atadmastart(ctlr, drive->write); + break; + } + iunlock(ctlr); + + return 0; +} + +static int +atagenioretry(Drive* drive) +{ + if(drive->dmactl){ + drive->dmactl = 0; + print("atagenioretry: disabling dma\n"); + } + else if(drive->rwmctl) + drive->rwmctl = 0; + else + return atasetsense(drive, SDcheck, 4, 8, drive->error); + + return SDretry; +} + +static int +atagenio(Drive* drive, uchar* cmd, int) +{ + uchar *p; + Ctlr *ctlr; + int count, max; + vlong lba, len; + + /* + * Map SCSI commands into ATA commands for discs. + * Fail any command with a LUN except INQUIRY which + * will return 'logical unit not supported'. + */ + if((cmd[1]>>5) && cmd[0] != 0x12) + return atasetsense(drive, SDcheck, 0x05, 0x25, 0); + + switch(cmd[0]){ + default: + return atasetsense(drive, SDcheck, 0x05, 0x20, 0); + + case 0x00: /* test unit ready */ + return SDok; + + case 0x03: /* request sense */ + if(cmd[4] < sizeof(drive->sense)) + len = cmd[4]; + else + len = sizeof(drive->sense); + if(drive->data && drive->dlen >= len){ + memmove(drive->data, drive->sense, len); + drive->data += len; + } + return SDok; + + case 0x12: /* inquiry */ + if(cmd[4] < sizeof(drive->inquiry)) + len = cmd[4]; + else + len = sizeof(drive->inquiry); + if(drive->data && drive->dlen >= len){ + memmove(drive->data, drive->inquiry, len); + drive->data += len; + } + return SDok; + + case 0x1B: /* start/stop unit */ + /* + * NOP for now, can use the power management feature + * set later. + */ + return SDok; + + case 0x25: /* read capacity */ + if((cmd[1] & 0x01) || cmd[2] || cmd[3]) + return atasetsense(drive, SDcheck, 0x05, 0x24, 0); + if(drive->data == nil || drive->dlen < 8) + return atasetsense(drive, SDcheck, 0x05, 0x20, 1); + /* + * Read capacity returns the LBA of the last sector. + */ + len = drive->sectors-1; + p = drive->data; + *p++ = len>>24; + *p++ = len>>16; + *p++ = len>>8; + *p++ = len; + len = drive->secsize; + *p++ = len>>24; + *p++ = len>>16; + *p++ = len>>8; + *p = len; + drive->data += 8; + return SDok; + + case 0x9E: /* long read capacity */ + if((cmd[1] & 0x01) || cmd[2] || cmd[3]) + return atasetsense(drive, SDcheck, 0x05, 0x24, 0); + if(drive->data == nil || drive->dlen < 8) + return atasetsense(drive, SDcheck, 0x05, 0x20, 1); + /* + * Read capacity returns the LBA of the last sector. + */ + len = drive->sectors-1; + p = drive->data; + *p++ = len>>56; + *p++ = len>>48; + *p++ = len>>40; + *p++ = len>>32; + *p++ = len>>24; + *p++ = len>>16; + *p++ = len>>8; + *p++ = len; + len = drive->secsize; + *p++ = len>>24; + *p++ = len>>16; + *p++ = len>>8; + *p = len; + drive->data += 8; + return SDok; + + case 0x28: /* read */ + case 0x2A: /* write */ + break; + + case 0x5A: + return atamodesense(drive, cmd); + } + + ctlr = drive->ctlr; + lba = (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5]; + count = (cmd[7]<<8)|cmd[8]; + if(drive->data == nil) + return SDok; + if(drive->dlen < count*drive->secsize) + count = drive->dlen/drive->secsize; + qlock(ctlr); + while(count){ + max = (drive->flags&Lba48) ? 65536 : 256; + if(count > max) + drive->count = max; + else + drive->count = count; + if(atageniostart(drive, lba)){ + ilock(ctlr); + atanop(drive, 0); + iunlock(ctlr); + qunlock(ctlr); + return atagenioretry(drive); + } + + while(waserror()) + ; + tsleep(ctlr, atadone, ctlr, 30*1000); + poperror(); + if(!ctlr->done){ + /* + * What should the above timeout be? In + * standby and sleep modes it could take as + * long as 30 seconds for a drive to respond. + * Very hard to get out of this cleanly. + */ + atadumpstate(drive, cmd, lba, count); + ataabort(drive, 1); + qunlock(ctlr); + return atagenioretry(drive); + } + + if(drive->status & Err){ + qunlock(ctlr); + return atasetsense(drive, SDcheck, 4, 8, drive->error); + } + count -= drive->count; + lba += drive->count; + } + qunlock(ctlr); + + return SDok; +} + +static int +atario(SDreq* r) +{ + Ctlr *ctlr; + Drive *drive; + SDunit *unit; + uchar cmd10[10], *cmdp, *p; + int clen, reqstatus, status; + + unit = r->unit; + if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil){ + r->status = SDtimeout; + return SDtimeout; + } + drive = ctlr->drive[unit->subno]; + + /* + * Most SCSI commands can be passed unchanged except for + * the padding on the end. The few which require munging + * are not used internally. Mode select/sense(6) could be + * converted to the 10-byte form but it's not worth the + * effort. Read/write(6) are easy. + */ + switch(r->cmd[0]){ + case 0x08: /* read */ + case 0x0A: /* write */ + cmdp = cmd10; + memset(cmdp, 0, sizeof(cmd10)); + cmdp[0] = r->cmd[0]|0x20; + cmdp[1] = r->cmd[1] & 0xE0; + cmdp[5] = r->cmd[3]; + cmdp[4] = r->cmd[2]; + cmdp[3] = r->cmd[1] & 0x0F; + cmdp[8] = r->cmd[4]; + clen = sizeof(cmd10); + break; + + default: + cmdp = r->cmd; + clen = r->clen; + break; + } + + qlock(drive); +retry: + drive->write = r->write; + drive->data = r->data; + drive->dlen = r->dlen; + + drive->status = 0; + drive->error = 0; + if(drive->pkt) + status = atapktio(drive, cmdp, clen); + else + status = atagenio(drive, cmdp, clen); + if(status == SDretry){ + if(DbgDEBUG) + print("%s: retry: dma %8.8uX rwm %4.4uX\n", + unit->name, drive->dmactl, drive->rwmctl); + goto retry; + } + if(status == SDok){ + atasetsense(drive, SDok, 0, 0, 0); + if(drive->data){ + p = r->data; + r->rlen = drive->data - p; + } + else + r->rlen = 0; + } + else if(status == SDcheck && !(r->flags & SDnosense)){ + drive->write = 0; + memset(cmd10, 0, sizeof(cmd10)); + cmd10[0] = 0x03; + cmd10[1] = r->lun<<5; + cmd10[4] = sizeof(r->sense)-1; + drive->data = r->sense; + drive->dlen = sizeof(r->sense)-1; + drive->status = 0; + drive->error = 0; + if(drive->pkt) + reqstatus = atapktio(drive, cmd10, 6); + else + reqstatus = atagenio(drive, cmd10, 6); + if(reqstatus == SDok){ + r->flags |= SDvalidsense; + atasetsense(drive, SDok, 0, 0, 0); + } + } + qunlock(drive); + r->status = status; + if(status != SDok) + return status; + + /* + * Fix up any results. + * Many ATAPI CD-ROMs ignore the LUN field completely and + * return valid INQUIRY data. Patch the response to indicate + * 'logical unit not supported' if the LUN is non-zero. + */ + switch(cmdp[0]){ + case 0x12: /* inquiry */ + if((p = r->data) == nil) + break; + if((cmdp[1]>>5) && (!drive->pkt || (p[0] & 0x1F) == 0x05)) + p[0] = 0x7F; + /*FALLTHROUGH*/ + default: + break; + } + + return SDok; +} + +static void +atainterrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Drive *drive; + int cmdport, len, status; + + ctlr = arg; + + ilock(ctlr); + if(inb(ctlr->ctlport+As) & Bsy){ + iunlock(ctlr); + if(DEBUG & DbgBsy) + print("IBsy+"); + return; + } + cmdport = ctlr->cmdport; + status = inb(cmdport+Status); + if((drive = ctlr->curdrive) == nil){ + iunlock(ctlr); + if((DEBUG & DbgINL) && ctlr->command != Cedd) + print("Inil%2.2uX+", ctlr->command); + return; + } + + if(status & Err) + drive->error = inb(cmdport+Error); + else switch(drive->command){ + default: + drive->error = Abrt; + break; + + case Crs: + case Crsm: + if(!(status & Drq)){ + drive->error = Abrt; + break; + } + len = drive->block; + if(drive->data+len > drive->limit) + len = drive->limit-drive->data; + inss(cmdport+Data, drive->data, len/2); + drive->data += len; + if(drive->data >= drive->limit) + ctlr->done = 1; + break; + + case Cws: + case Cwsm: + len = drive->block; + if(drive->data+len > drive->limit) + len = drive->limit-drive->data; + drive->data += len; + if(drive->data >= drive->limit){ + ctlr->done = 1; + break; + } + if(!(status & Drq)){ + drive->error = Abrt; + break; + } + len = drive->block; + if(drive->data+len > drive->limit) + len = drive->limit-drive->data; + outss(cmdport+Data, drive->data, len/2); + break; + + case Cpkt: + atapktinterrupt(drive); + break; + + case Crd: + case Cwd: + atadmainterrupt(drive, drive->count*drive->secsize); + break; + + case Cstandby: + ctlr->done = 1; + break; + } + iunlock(ctlr); + + if(drive->error){ + status |= Err; + ctlr->done = 1; + } + + if(ctlr->done){ + ctlr->curdrive = nil; + drive->status = status; + wakeup(ctlr); + } +} + +static SDev* +atapnp(void) +{ + Ctlr *ctlr; + Pcidev *p; + int channel, ispc87415, pi, r; + SDev *legacy[2], *sdev, *head, *tail; + + legacy[0] = legacy[1] = head = tail = nil; + if(sdev = ataprobe(0x1F0, 0x3F4, IrqATA0)){ + head = tail = sdev; + legacy[0] = sdev; + } + if(sdev = ataprobe(0x170, 0x374, IrqATA1)){ + if(head != nil) + tail->next = sdev; + else + head = sdev; + tail = sdev; + legacy[1] = sdev; + } + + p = nil; + while(p = pcimatch(p, 0, 0)){ + /* + * Look for devices with the correct class and sub-class + * code and known device and vendor ID; add native-mode + * channels to the list to be probed, save info for the + * compatibility mode channels. + * Note that the legacy devices should not be considered + * PCI devices by the interrupt controller. + * For both native and legacy, save info for busmastering + * if capable. + * Promise Ultra ATA/66 (PDC20262) appears to + * 1) give a sub-class of 'other mass storage controller' + * instead of 'IDE controller', regardless of whether it's + * the only controller or not; + * 2) put 0 in the programming interface byte (probably + * as a consequence of 1) above). + * Sub-class code 0x04 is 'RAID controller', e.g. VIA VT8237. + */ + if(p->ccrb != 0x01) + continue; + if(p->ccru != 0x01 && p->ccru != 0x04 && p->ccru != 0x80) + continue; + pi = p->ccrp; + ispc87415 = 0; + + switch((p->did<<16)|p->vid){ + default: + continue; + + case (0x0002<<16)|0x100B: /* NS PC87415 */ + /* + * Disable interrupts on both channels until + * after they are probed for drives. + * This must be called before interrupts are + * enabled because the IRQ may be shared. + */ + ispc87415 = 1; + pcicfgw32(p, 0x40, 0x00000300); + break; + case (0x1000<<16)|0x1042: /* PC-Tech RZ1000 */ + /* + * Turn off prefetch. Overkill, but cheap. + */ + r = pcicfgr32(p, 0x40); + r &= ~0x2000; + pcicfgw32(p, 0x40, r); + break; + case (0x4D38<<16)|0x105A: /* Promise PDC20262 */ + case (0x4D30<<16)|0x105A: /* Promise PDC202xx */ + case (0x4D68<<16)|0x105A: /* Promise PDC20268 */ + case (0x3373<<16)|0x105A: /* Promise 20378 RAID */ + case (0x3149<<16)|0x1106: /* VIA VT8237 SATA/RAID */ + pi = 0x85; + break; + case (0x0004<<16)|0x1103: /* HighPoint HPT-370 */ + pi = 0x85; + /* + * Turn off fast interrupt prediction. + */ + if((r = pcicfgr8(p, 0x51)) & 0x80) + pcicfgw8(p, 0x51, r & ~0x80); + if((r = pcicfgr8(p, 0x55)) & 0x80) + pcicfgw8(p, 0x55, r & ~0x80); + break; + case (0x0640<<16)|0x1095: /* CMD 640B */ + /* + * Bugfix code here... + */ + break; + case (0x7441<<16)|0x1022: /* AMD 768 */ + /* + * Set: + * 0x41 prefetch, postwrite; + * 0x43 FIFO configuration 1/2 and 1/2; + * 0x44 status register read retry; + * 0x46 DMA read and end of sector flush. + */ + r = pcicfgr8(p, 0x41); + pcicfgw8(p, 0x41, r|0xF0); + r = pcicfgr8(p, 0x43); + pcicfgw8(p, 0x43, (r & 0x90)|0x2A); + r = pcicfgr8(p, 0x44); + pcicfgw8(p, 0x44, r|0x08); + r = pcicfgr8(p, 0x46); + pcicfgw8(p, 0x46, (r & 0x0C)|0xF0); + break; + case (0x0646<<16)|0x1095: /* CMD 646 */ + case (0x0571<<16)|0x1106: /* VIA 82C686 */ + case (0x0211<<16)|0x1166: /* ServerWorks IB6566 */ + case (0x1230<<16)|0x8086: /* 82371FB (PIIX) */ + case (0x7010<<16)|0x8086: /* 82371SB (PIIX3) */ + case (0x7111<<16)|0x8086: /* 82371[AE]B (PIIX4[E]) */ + case (0x2411<<16)|0x8086: /* 82801AA (ICH) */ + case (0x2421<<16)|0x8086: /* 82801AB (ICH0) */ + case (0x244A<<16)|0x8086: /* 82801BA (ICH2, Mobile) */ + case (0x244B<<16)|0x8086: /* 82801BA (ICH2, High-End) */ + case (0x248A<<16)|0x8086: /* 82801CA (ICH3, Mobile) */ + case (0x248B<<16)|0x8086: /* 82801CA (ICH3, High-End) */ + case (0x24CA<<16)|0x8086: /* 82801DBM (ICH4, Mobile) */ + case (0x24CB<<16)|0x8086: /* 82801DB (ICH4, High-End) */ + case (0x24DB<<16)|0x8086: /* 82801EB (ICH5) */ + break; + } + + for(channel = 0; channel < 2; channel++){ + if(pi & (1<<(2*channel))){ + sdev = ataprobe(p->mem[0+2*channel].bar & ~0x01, + p->mem[1+2*channel].bar & ~0x01, + p->intl); + if(sdev == nil) + continue; + + ctlr = sdev->ctlr; + if(ispc87415) { + ctlr->ienable = pc87415ienable; + print("pc87415disable: not yet implemented\n"); + } + + if(head != nil) + tail->next = sdev; + else + head = sdev; + tail = sdev; + ctlr->tbdf = p->tbdf; + } + else if((sdev = legacy[channel]) == nil) + continue; + else + ctlr = sdev->ctlr; + + ctlr->pcidev = p; + if(!(pi & 0x80)) + continue; + ctlr->bmiba = (p->mem[4].bar & ~0x01) + channel*8; + } + } + +if(0){ + int port; + ISAConf isa; + + /* + * Hack for PCMCIA drives. + * This will be tidied once we figure out how the whole + * removeable device thing is going to work. + */ + memset(&isa, 0, sizeof(isa)); + isa.port = 0x180; /* change this for your machine */ + isa.irq = 11; /* change this for your machine */ + + port = isa.port+0x0C; + channel = pcmspecial("MK2001MPL", &isa); + if(channel == -1) + channel = pcmspecial("SunDisk", &isa); + if(channel == -1){ + isa.irq = 10; + channel = pcmspecial("CF", &isa); + } + if(channel == -1){ + isa.irq = 10; + channel = pcmspecial("OLYMPUS", &isa); + } + if(channel == -1){ + port = isa.port+0x204; + channel = pcmspecial("ATA/ATAPI", &isa); + } + if(channel >= 0 && (sdev = ataprobe(isa.port, port, isa.irq)) != nil){ + if(head != nil) + tail->next = sdev; + else + head = sdev; + } +} + return head; +} + +static SDev* +atalegacy(int port, int irq) +{ + return ataprobe(port, port+0x204, irq); +} + +static SDev* +ataid(SDev* sdev) +{ + int i; + Ctlr *ctlr; + char name[32]; + + /* + * Legacy controllers are always 'C' and 'D' and if + * they exist and have drives will be first in the list. + * If there are no active legacy controllers, native + * controllers start at 'C'. + */ + if(sdev == nil) + return nil; + ctlr = sdev->ctlr; + if(ctlr->cmdport == 0x1F0 || ctlr->cmdport == 0x170) + i = 2; + else + i = 0; + while(sdev){ + if(sdev->ifc == &sdataifc){ + ctlr = sdev->ctlr; + if(ctlr->cmdport == 0x1F0) + sdev->idno = 'C'; + else if(ctlr->cmdport == 0x170) + sdev->idno = 'D'; + else{ + sdev->idno = 'C'+i; + i++; + } + snprint(name, sizeof(name), "sd%c", sdev->idno); + kstrdup(&sdev->name, name); + } + sdev = sdev->next; + } + + return nil; +} + +static int +ataenable(SDev* sdev) +{ + Ctlr *ctlr; + char name[32]; + + ctlr = sdev->ctlr; + + if(ctlr->bmiba){ +#define ALIGN (4 * 1024) + if(ctlr->pcidev != nil) + pcisetbme(ctlr->pcidev); + // ctlr->prdt = xspanalloc(Nprd*sizeof(Prd), 4, 4*1024); + ctlr->prdtbase = xalloc(Nprd * sizeof(Prd) + ALIGN); + ctlr->prdt = (Prd *)(((ulong)ctlr->prdtbase + ALIGN) & ~(ALIGN - 1)); + } + snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name); + intrenable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name); + outb(ctlr->ctlport+Dc, 0); + if(ctlr->ienable) + ctlr->ienable(ctlr); + + return 1; +} + +static int +atadisable(SDev *sdev) +{ + Ctlr *ctlr; + char name[32]; + + ctlr = sdev->ctlr; + outb(ctlr->ctlport+Dc, Nien); /* disable interrupts */ + if (ctlr->idisable) + ctlr->idisable(ctlr); + snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name); + intrdisable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name); + if (ctlr->bmiba) { + if (ctlr->pcidev) + pciclrbme(ctlr->pcidev); + xfree(ctlr->prdtbase); + } + return 0; +} + +static int +atarctl(SDunit* unit, char* p, int l) +{ + int n; + Ctlr *ctlr; + Drive *drive; + + if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil) + return 0; + drive = ctlr->drive[unit->subno]; + + qlock(drive); + n = snprint(p, l, "config %4.4uX capabilities %4.4uX", + drive->info[Iconfig], drive->info[Icapabilities]); + if(drive->dma) + n += snprint(p+n, l-n, " dma %8.8uX dmactl %8.8uX", + drive->dma, drive->dmactl); + if(drive->rwm) + n += snprint(p+n, l-n, " rwm %ud rwmctl %ud", + drive->rwm, drive->rwmctl); + if(drive->flags&Lba48) + n += snprint(p+n, l-n, " lba48always %s", + (drive->flags&Lba48always) ? "on" : "off"); + n += snprint(p+n, l-n, "\n"); + if(drive->sectors){ + n += snprint(p+n, l-n, "geometry %lld %d", + drive->sectors, drive->secsize); + if(drive->pkt == 0) + n += snprint(p+n, l-n, " %d %d %d", + drive->c, drive->h, drive->s); + n += snprint(p+n, l-n, "\n"); + } + qunlock(drive); + + return n; +} + +static int +atawctl(SDunit* unit, Cmdbuf* cb) +{ + int period; + Ctlr *ctlr; + Drive *drive; + + if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil) + return 0; + drive = ctlr->drive[unit->subno]; + + qlock(drive); + if(waserror()){ + qunlock(drive); + nexterror(); + } + + /* + * Dma and rwm control is passive at the moment, + * i.e. it is assumed that the hardware is set up + * correctly already either by the BIOS or when + * the drive was initially identified. + */ + if(strcmp(cb->f[0], "dma") == 0){ + if(cb->nf != 2 || drive->dma == 0) + error(Ebadctl); + if(strcmp(cb->f[1], "on") == 0) + drive->dmactl = drive->dma; + else if(strcmp(cb->f[1], "off") == 0) + drive->dmactl = 0; + else + error(Ebadctl); + } + else if(strcmp(cb->f[0], "rwm") == 0){ + if(cb->nf != 2 || drive->rwm == 0) + error(Ebadctl); + if(strcmp(cb->f[1], "on") == 0) + drive->rwmctl = drive->rwm; + else if(strcmp(cb->f[1], "off") == 0) + drive->rwmctl = 0; + else + error(Ebadctl); + } + else if(strcmp(cb->f[0], "standby") == 0){ + switch(cb->nf){ + default: + error(Ebadctl); + case 2: + period = strtol(cb->f[1], 0, 0); + if(period && (period < 30 || period > 240*5)) + error(Ebadctl); + period /= 5; + break; + } + if(atastandby(drive, period) != SDok) + error(Ebadctl); + } + else if(strcmp(cb->f[0], "lba48always") == 0){ + if(cb->nf != 2 || !(drive->flags&Lba48)) + error(Ebadctl); + if(strcmp(cb->f[1], "on") == 0) + drive->flags |= Lba48always; + else if(strcmp(cb->f[1], "off") == 0) + drive->flags &= ~Lba48always; + else + error(Ebadctl); + } + else + error(Ebadctl); + qunlock(drive); + poperror(); + + return 0; +} + +SDifc sdataifc = { + "ata", /* name */ + + atapnp, /* pnp */ + atalegacy, /* legacy */ + ataid, /* id */ + ataenable, /* enable */ + atadisable, /* disable */ + + scsiverify, /* verify */ + scsionline, /* online */ + atario, /* rio */ + atarctl, /* rctl */ + atawctl, /* wctl */ + + scsibio, /* bio */ + ataprobew, /* probe */ + ataclear, /* clear */ + atastat, /* stat */ +}; diff --git a/os/pc/sdmylex.c b/os/pc/sdmylex.c new file mode 100644 index 00000000..cb132991 --- /dev/null +++ b/os/pc/sdmylex.c @@ -0,0 +1,1249 @@ +/* + * Mylex MultiMaster (Buslogic BT-*) SCSI Host Adapter + * in both 24-bit and 32-bit mode. + * 24-bit mode works for Adaptec AHA-154xx series too. + * + * To do: + * allocate more Ccb's as needed, up to NMbox-1; + * add nmbox and nccb to Ctlr struct for the above; + * 64-bit LUN/explicit wide support necessary? + * + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +#include "../port/sd.h" + +#define K2BPA(va, tbdf) PADDR(va) +#define BPA2K(pa, tbdf) KADDR(pa) + +extern SDifc sdmylexifc; + +enum { /* registers */ + Rcontrol = 0x00, /* WO: control register */ + Rstatus = 0x00, /* RO: status register */ + Rcpr = 0x01, /* WO: command/parameter register */ + Rdatain = 0x01, /* RO: data-in register */ + Rinterrupt = 0x02, /* RO: interrupt register */ +}; + +enum { /* Rcontrol */ + Rsbus = 0x10, /* SCSI Bus Reset */ + Rint = 0x20, /* Interrupt Reset */ + Rsoft = 0x40, /* Soft Reset */ + Rhard = 0x80, /* Hard Reset */ +}; + +enum { /* Rstatus */ + Cmdinv = 0x01, /* Command Invalid */ + Dirrdy = 0x04, /* Data In Register Ready */ + Cprbsy = 0x08, /* Command/Parameter Register Busy */ + Hardy = 0x10, /* Host Adapter Ready */ + Inreq = 0x20, /* Initialisation Required */ + Dfail = 0x40, /* Diagnostic Failure */ + Dact = 0x80, /* Diagnostic Active */ +}; + +enum { /* Rcpr */ + Cinitialise = 0x01, /* Initialise Mailbox */ + Cstart = 0x02, /* Start Mailbox Command */ + Cinquiry = 0x04, /* Adapter Inquiry */ + Ceombri = 0x05, /* Enable OMBR Interrupt */ + Cinquire = 0x0B, /* Inquire Configuration */ + Cextbios = 0x28, /* AHA-1542: extended BIOS info. */ + Cmbienable = 0x29, /* AHA-1542: Mailbox interface enable */ + Ciem = 0x81, /* Initialise Extended Mailbox */ + Ciesi = 0x8D, /* Inquire Extended Setup Information */ + Cerrm = 0x8F, /* Enable strict round-robin mode */ + Cwide = 0x96, /* Wide CCB */ +}; + +enum { /* Rinterrupt */ + Imbl = 0x01, /* Incoming Mailbox Loaded */ + Mbor = 0x02, /* Mailbox Out Ready */ + Cmdc = 0x04, /* Command Complete */ + Rsts = 0x08, /* SCSI Reset State */ + Intv = 0x80, /* Interrupt Valid */ +}; + +typedef struct Mbox24 Mbox24; +struct Mbox24 { + uchar code; /* action/completion code */ + uchar ccb[3]; /* CCB pointer (MSB, ..., LSB) */ +}; + +typedef struct Mbox32 Mbox32; +struct Mbox32 { + uchar ccb[4]; /* CCB pointer (LSB, ..., MSB) */ + uchar btstat; /* BT-7[45]7[SD] status */ + uchar sdstat; /* SCSI device status */ + uchar pad; + uchar code; /* action/completion code */ +}; + +enum { /* mailbox commands */ + Mbfree = 0x00, /* Mailbox not in use */ + + Mbostart = 0x01, /* Start a mailbox command */ + Mboabort = 0x02, /* Abort a mailbox command */ + + Mbiok = 0x01, /* CCB completed without error */ + Mbiabort = 0x02, /* CCB aborted at request of host */ + Mbinx = 0x03, /* Aborted CCB not found */ + Mbierror = 0x04, /* CCB completed with error */ +}; + +typedef struct Ccb24 Ccb24; +typedef struct Ccb32 Ccb32; +typedef union Ccb Ccb; + +typedef struct Ccb24 { + uchar opcode; /* Operation code */ + uchar datadir; /* Data direction control */ + uchar cdblen; /* Length of CDB */ + uchar senselen; /* Length of sense area */ + uchar datalen[3]; /* Data length (MSB, ..., LSB) */ + uchar dataptr[3]; /* Data pointer (MSB, ..., LSB) */ + uchar linkptr[3]; /* Link pointer (MSB, ..., LSB) */ + uchar linkid; /* command linking identifier */ + uchar btstat; /* BT-* adapter status */ + uchar sdstat; /* SCSI device status */ + uchar reserved[2]; /* */ + uchar cs[12+0xFF]; /* Command descriptor block + Sense */ + + void* data; /* buffer if address > 24-bits */ + + Rendez; + int done; /* command completed */ + + Ccb* ccb; /* link on free list */ +} Ccb24; + + +typedef struct Ccb32 { + uchar opcode; /* Operation code */ + uchar datadir; /* Data direction control */ + uchar cdblen; /* Length of CDB */ + uchar senselen; /* Length of sense area */ + uchar datalen[4]; /* Data length (LSB, ..., MSB) */ + uchar dataptr[4]; /* Data pointer (LSB, ..., MSB) */ + uchar reserved[2]; + uchar btstat; /* BT-* adapter status */ + uchar sdstat; /* SCSI device status */ + uchar targetid; /* Target ID */ + uchar luntag; /* LUN & tag */ + uchar cdb[12]; /* Command descriptor block */ + uchar ccbctl; /* CCB control */ + uchar linkid; /* command linking identifier */ + uchar linkptr[4]; /* Link pointer (LSB, ..., MSB) */ + uchar senseptr[4]; /* Sense pointer (LSB, ..., MSB) */ + uchar sense[0xFF]; /* Sense bytes */ + + Rendez; + int done; /* command completed */ + + Ccb* ccb; /* link on free list */ +} Ccb32; + +typedef union Ccb { + Ccb24; + Ccb32; +} Ccb; + +enum { /* opcode */ + OInitiator = 0x00, /* initiator CCB */ + Ordl = 0x03, /* initiator CCB with + * residual data length returned + */ +}; + +enum { /* datadir */ + CCBdatain = 0x08, /* inbound, length is checked */ + CCBdataout = 0x10, /* outbound, length is checked */ +}; + +enum { /* btstat */ + Eok = 0x00, /* normal completion with no errors */ +}; + +enum { /* luntag */ + TagEnable = 0x20, /* Tag enable */ + SQTag = 0x00, /* Simple Queue Tag */ + HQTag = 0x40, /* Head of Queue Tag */ + OQTag = 0x80, /* Ordered Queue Tag */ +}; + +enum { /* CCB control */ + NoDisc = 0x08, /* No disconnect */ + NoUnd = 0x10, /* No underrrun error report */ + NoData = 0x20, /* No data transfer */ + NoStat = 0x40, /* No CCB status if zero */ + NoIntr = 0x80, /* No Interrupts */ +}; + +typedef struct Ctlr Ctlr; +struct Ctlr { + int port; /* I/O port */ + int id; /* adapter SCSI id */ + int bus; /* 24 or 32 -bit */ + int irq; + int wide; + Pcidev* pcidev; + SDev* sdev; + int spurious; + + Lock issuelock; + + Lock ccblock; + QLock ccbq; + Rendez ccbr; + + Lock mboxlock; + void* mb; /* mailbox out + mailbox in */ + int mbox; /* current mailbox out index into mb */ + int mbix; /* current mailbox in index into mb */ + + Lock cachelock; + Ccb* ccb; /* list of free Ccb's */ + Ccb** cache; /* last completed Ccb */ +}; + +/* + * The number of mailboxes should be a multiple of 8 (4 for Mbox32) + * to ensure the boundary between the out and in mailboxes doesn't + * straddle a cache-line boundary. + * The number of Ccb's should be less than the number of mailboxes to + * ensure no queueing is necessary on mailbox allocation. + */ +enum { + NMbox = 8*8, /* number of Mbox's */ + NCcb = NMbox-1, /* number of Ccb's */ +}; + +#define PADDR24(a, n) ((PADDR(a)+(n)) <= (1<<24)) + +static void +ccbfree(Ctlr* ctlr, Ccb* ccb) +{ + lock(&ctlr->ccblock); + if(ctlr->bus == 24) + ((Ccb24*)ccb)->ccb = ctlr->ccb; + else + ((Ccb32*)ccb)->ccb = ctlr->ccb; + if(ctlr->ccb == nil) + wakeup(&ctlr->ccbr); + ctlr->ccb = ccb; + unlock(&ctlr->ccblock); +} + +static int +ccbavailable(void* a) +{ + return ((Ctlr*)a)->ccb != nil; +} + +static Ccb* +ccballoc(Ctlr* ctlr) +{ + Ccb *ccb; + + for(;;){ + lock(&ctlr->ccblock); + if((ccb = ctlr->ccb) != nil){ + if(ctlr->bus == 24) + ctlr->ccb = ((Ccb24*)ccb)->ccb; + else + ctlr->ccb = ((Ccb32*)ccb)->ccb; + unlock(&ctlr->ccblock); + break; + } + + unlock(&ctlr->ccblock); + qlock(&ctlr->ccbq); + if(waserror()){ + qunlock(&ctlr->ccbq); + continue; + } + sleep(&ctlr->ccbr, ccbavailable, ctlr); + qunlock(&ctlr->ccbq); + poperror(); + } + + return ccb; +} + +static int +done24(void* arg) +{ + return ((Ccb24*)arg)->done; +} + +static int +mylex24rio(SDreq* r) +{ + ulong p; + Ctlr *ctlr; + Ccb24 *ccb; + Mbox24 *mb; + uchar *data, lun, *sense; + int d, n, btstat, sdstat, target; + + ctlr = r->unit->dev->ctlr; + target = r->unit->subno; + lun = (r->cmd[1]>>5) & 0x07; + + /* + * Ctlr->cache holds the last completed Ccb for this target if it + * returned 'check condition'. + * If this command is a request-sense and there is valid sense data + * from the last completed Ccb, return it immediately. + */ + lock(&ctlr->cachelock); + if((ccb = ctlr->cache[target]) != nil){ + ctlr->cache[target] = nil; + if(r->cmd[0] == 0x03 + && ccb->sdstat == SDcheck && lun == ((ccb->cs[1]>>5) & 0x07)){ + unlock(&ctlr->cachelock); + if(r->dlen){ + sense = &ccb->cs[ccb->cdblen]; + n = 8+sense[7]; + if(n > r->dlen) + n = r->dlen; + memmove(r->data, sense, n); + r->rlen = n; + } + ccbfree(ctlr, (Ccb*)ccb); + return SDok; + } + } + unlock(&ctlr->cachelock); + if(ccb == nil) + ccb = ccballoc(ctlr); + + /* + * Check if the transfer is to memory above the 24-bit limit the + * controller can address. If it is, try to allocate a temporary + * buffer as a staging area. + */ + n = r->dlen; + if(n && !PADDR24(r->data, n)){ + data = mallocz(n, 0); + if(data == nil || !PADDR24(data, n)){ + if(data != nil){ + free(data); + ccb->data = nil; + } + ccbfree(ctlr, (Ccb*)ccb); + return SDmalloc; + } + if(r->write) + memmove(data, r->data, n); + ccb->data = r->data; + } + else + data = r->data; + + /* + * Fill in the ccb. + */ + ccb->opcode = Ordl; + + ccb->datadir = (target<<5)|lun; + if(n == 0) + ccb->datadir |= CCBdataout|CCBdatain; + else if(!r->write) + ccb->datadir |= CCBdatain; + else + ccb->datadir |= CCBdataout; + + ccb->cdblen = r->clen; + ccb->senselen = 0xFF; + + ccb->datalen[0] = n>>16; + ccb->datalen[1] = n>>8; + ccb->datalen[2] = n; + p = PADDR(data); + ccb->dataptr[0] = p>>16; + ccb->dataptr[1] = p>>8; + ccb->dataptr[2] = p; + + ccb->linkptr[0] = ccb->linkptr[1] = ccb->linkptr[2] = 0; + ccb->linkid = 0; + ccb->btstat = ccb->sdstat = 0; + ccb->reserved[0] = ccb->reserved[1] = 0; + + memmove(ccb->cs, r->cmd, r->clen); + + /* + * There's one more mbox than there there is + * ccb so there is always one free. + */ + lock(&ctlr->mboxlock); + mb = ctlr->mb; + mb += ctlr->mbox; + p = PADDR(ccb); + mb->ccb[0] = p>>16; + mb->ccb[1] = p>>8; + mb->ccb[2] = p; + mb->code = Mbostart; + ctlr->mbox++; + if(ctlr->mbox >= NMbox) + ctlr->mbox = 0; + + /* + * This command does not require Hardy + * and doesn't generate a Cmdc interrupt. + */ + ccb->done = 0; + outb(ctlr->port+Rcpr, Cstart); + unlock(&ctlr->mboxlock); + + /* + * Wait for the request to complete and return the status. + * Since the buffer is not reference counted cannot return + * until the DMA is done writing into the buffer so the caller + * cannot free the buffer prematurely. + */ + while(waserror()) + ; + sleep(ccb, done24, ccb); + poperror(); + + /* + * Save the status and patch up the number of + * bytes actually transferred. + * There's a firmware bug on some 956C controllers + * which causes the return count from a successful + * READ CAPACITY not be updated, so fix it here. + */ + sdstat = ccb->sdstat; + btstat = ccb->btstat; + + d = ccb->datalen[0]<<16; + d |= ccb->datalen[1]<<8; + d |= ccb->datalen[2]; + if(ccb->cs[0] == 0x25 && sdstat == SDok) + d = 0; + n -= d; + r->rlen = n; + + /* + * Tidy things up if a staging area was used for the data, + */ + if(ccb->data != nil){ + if(sdstat == SDok && btstat == 0 && !r->write) + memmove(ccb->data, data, n); + free(data); + ccb->data = nil; + } + + /* + * If there was a check-condition, save the + * ccb for a possible request-sense command. + */ + if(sdstat == SDcheck){ + if(r->flags & SDnosense){ + lock(&ctlr->cachelock); + if(ctlr->cache[target]) + ccbfree(ctlr, ctlr->cache[target]); + ctlr->cache[target] = (Ccb*)ccb; + unlock(&ctlr->cachelock); + return SDcheck; + } + sense = &ccb->cs[ccb->cdblen]; + n = 8+sense[7]; + if(n > sizeof(r->sense)-1) + n = sizeof(r->sense)-1; + memmove(r->sense, sense, n); + r->flags |= SDvalidsense; + } + ccbfree(ctlr, (Ccb*)ccb); + + if(btstat){ + if(btstat == 0x11) + return SDtimeout; + return SDeio; + } + return sdstat; +} + +static void +mylex24interrupt(Ureg*, void* arg) +{ + ulong pa; + Ctlr *ctlr; + Ccb24 *ccb; + Mbox24 *mb, *mbox; + int port, rinterrupt, rstatus; + + ctlr = arg; + port = ctlr->port; + + /* + * Save and clear the interrupt(s). The only + * interrupts expected are Cmdc, which is ignored, + * and Imbl which means something completed. + * There's one spurious interrupt left over from + * initialisation, ignore it. + */ + rinterrupt = inb(port+Rinterrupt); + rstatus = inb(port+Rstatus); + outb(port+Rcontrol, Rint); + if((rinterrupt & ~(Cmdc|Imbl)) != Intv && ctlr->spurious++) + print("%s: interrupt 0x%2.2ux\n", + ctlr->sdev->name, rinterrupt); + if((rinterrupt & Cmdc) && (rstatus & Cmdinv)) + print("%s: command invalid\n", ctlr->sdev->name); + + /* + * Look for something in the mail. + * If there is, save the status, free the mailbox + * and wakeup whoever. + */ + mb = ctlr->mb; + for(mbox = &mb[ctlr->mbix]; mbox->code; mbox = &mb[ctlr->mbix]){ + pa = (mbox->ccb[0]<<16)|(mbox->ccb[1]<<8)|mbox->ccb[2]; + ccb = BPA2K(pa, BUSUNKNOWN); + mbox->code = 0; + ccb->done = 1; + wakeup(ccb); + + ctlr->mbix++; + if(ctlr->mbix >= NMbox+NMbox) + ctlr->mbix = NMbox; + } +} + +static int +done32(void* arg) +{ + return ((Ccb32*)arg)->done; +} + +static int +mylex32rio(SDreq* r) +{ + ulong p; + uchar lun; + Ctlr *ctlr; + Ccb32 *ccb; + Mbox32 *mb; + int d, n, btstat, sdstat, target; + + ctlr = r->unit->dev->ctlr; + target = r->unit->subno; + lun = (r->cmd[1]>>5) & 0x07; + + /* + * Ctlr->cache holds the last completed Ccb for this target if it + * returned 'check condition'. + * If this command is a request-sense and there is valid sense data + * from the last completed Ccb, return it immediately. + */ + lock(&ctlr->cachelock); + if((ccb = ctlr->cache[target]) != nil){ + ctlr->cache[target] = nil; + if(r->cmd[0] == 0x03 + && ccb->sdstat == SDcheck && lun == (ccb->luntag & 0x07)){ + unlock(&ctlr->cachelock); + if(r->dlen){ + n = 8+ccb->sense[7]; + if(n > r->dlen) + n = r->dlen; + memmove(r->data, ccb->sense, n); + r->rlen = n; + } + ccbfree(ctlr, (Ccb*)ccb); + return SDok; + } + } + unlock(&ctlr->cachelock); + if(ccb == nil) + ccb = ccballoc(ctlr); + + /* + * Fill in the ccb. + */ + ccb->opcode = Ordl; + + n = r->dlen; + if(n == 0) + ccb->datadir = CCBdataout|CCBdatain; + else if(!r->write) + ccb->datadir = CCBdatain; + else + ccb->datadir = CCBdataout; + + ccb->cdblen = r->clen; + + ccb->datalen[0] = n; + ccb->datalen[1] = n>>8; + ccb->datalen[2] = n>>16; + ccb->datalen[3] = n>>24; + p = PADDR(r->data); + ccb->dataptr[0] = p; + ccb->dataptr[1] = p>>8; + ccb->dataptr[2] = p>>16; + ccb->dataptr[3] = p>>24; + + ccb->targetid = target; + ccb->luntag = lun; + if(r->unit->inquiry[7] & 0x02) + ccb->luntag |= SQTag|TagEnable; + memmove(ccb->cdb, r->cmd, r->clen); + ccb->btstat = ccb->sdstat = 0; + ccb->ccbctl = 0; + + /* + * There's one more mbox than there there is + * ccb so there is always one free. + */ + lock(&ctlr->mboxlock); + mb = ctlr->mb; + mb += ctlr->mbox; + p = PADDR(ccb); + mb->ccb[0] = p; + mb->ccb[1] = p>>8; + mb->ccb[2] = p>>16; + mb->ccb[3] = p>>24; + mb->code = Mbostart; + ctlr->mbox++; + if(ctlr->mbox >= NMbox) + ctlr->mbox = 0; + + /* + * This command does not require Hardy + * and doesn't generate a Cmdc interrupt. + */ + ccb->done = 0; + outb(ctlr->port+Rcpr, Cstart); + unlock(&ctlr->mboxlock); + + /* + * Wait for the request to complete and return the status. + * Since the buffer is not reference counted cannot return + * until the DMA is done writing into the buffer so the caller + * cannot free the buffer prematurely. + */ + while(waserror()) + ; + sleep(ccb, done32, ccb); + poperror(); + + /* + * Save the status and patch up the number of + * bytes actually transferred. + * There's a firmware bug on some 956C controllers + * which causes the return count from a successful + * READ CAPACITY not to be updated, so fix it here. + */ + sdstat = ccb->sdstat; + btstat = ccb->btstat; + + d = ccb->datalen[0]; + d |= (ccb->datalen[1]<<8); + d |= (ccb->datalen[2]<<16); + d |= (ccb->datalen[3]<<24); + if(ccb->cdb[0] == 0x25 && sdstat == SDok) + d = 0; + n -= d; + r->rlen = n; + + /* + * If there was a check-condition, save the + * ccb for a possible request-sense command. + */ + if(sdstat == SDcheck){ + if(r->flags & SDnosense){ + lock(&ctlr->cachelock); + if(ctlr->cache[target]) + ccbfree(ctlr, ctlr->cache[target]); + ctlr->cache[target] = (Ccb*)ccb; + unlock(&ctlr->cachelock); + return SDcheck; + } + n = 8+ccb->sense[7]; + if(n > sizeof(r->sense)-1) + n = sizeof(r->sense)-1; + memmove(r->sense, ccb->sense, n); + r->flags |= SDvalidsense; + } + ccbfree(ctlr, (Ccb*)ccb); + + if(btstat){ + if(btstat == 0x11) + return SDtimeout; + return SDeio; + } + return sdstat; +} + +static void +mylex32interrupt(Ureg*, void* arg) +{ + ulong pa; + Ctlr *ctlr; + Ccb32 *ccb; + Mbox32 *mb, *mbox; + int port, rinterrupt, rstatus; + + ctlr = arg; + port = ctlr->port; + + /* + * Save and clear the interrupt(s). The only + * interrupts expected are Cmdc, which is ignored, + * and Imbl which means something completed. + * There's one spurious interrupt left over from + * initialisation, ignore it. + */ + rinterrupt = inb(port+Rinterrupt); + rstatus = inb(port+Rstatus); + outb(port+Rcontrol, Rint); + if((rinterrupt & ~(Cmdc|Imbl)) != Intv && ctlr->spurious++) + print("%s: interrupt 0x%2.2ux\n", + ctlr->sdev->name, rinterrupt); + if((rinterrupt & Cmdc) && (rstatus & Cmdinv)) + print("%s: command invalid\n", ctlr->sdev->name); + + /* + * Look for something in the mail. + * If there is, free the mailbox and wakeup whoever. + */ + mb = ctlr->mb; + for(mbox = &mb[ctlr->mbix]; mbox->code; mbox = &mb[ctlr->mbix]){ + pa = (mbox->ccb[3]<<24) + |(mbox->ccb[2]<<16) + |(mbox->ccb[1]<<8) + |mbox->ccb[0]; + if(ctlr->pcidev) + ccb = BPA2K(pa, ctlr->pcidev->tbdf); + else + ccb = BPA2K(pa, BUSUNKNOWN); + mbox->code = 0; + ccb->done = 1; + wakeup(ccb); + + ctlr->mbix++; + if(ctlr->mbix >= NMbox+NMbox) + ctlr->mbix = NMbox; + } +} + +static int +mylexrio(SDreq* r) +{ + int subno; + Ctlr *ctlr; + + subno = r->unit->subno; + ctlr = r->unit->dev->ctlr; + if(subno == ctlr->id || (!ctlr->wide && subno >= 8)) + r->status = SDtimeout; + else if(ctlr->bus == 24) + r->status = mylex24rio(r); + else + r->status = mylex32rio(r); + return r->status; +} + +/* + * Issue a command to a controller. The command and its length is + * contained in cmd and cmdlen. If any data is to be + * returned, datalen should be non-zero, and the returned data + * will be placed in data. + * If Cmdc is set, bail out, the invalid command will be handled + * when the interrupt is processed. + */ +static void +issueio(int port, uchar* cmd, int cmdlen, uchar* data, int datalen) +{ + int len; + + if(cmd[0] != Cstart && cmd[0] != Ceombri){ + while(!(inb(port+Rstatus) & Hardy)) + ; + } + outb(port+Rcpr, cmd[0]); + + len = 1; + while(len < cmdlen){ + if(!(inb(port+Rstatus) & Cprbsy)){ + outb(port+Rcpr, cmd[len]); + len++; + } + if(inb(port+Rinterrupt) & Cmdc) + return; + } + + if(datalen){ + len = 0; + while(len < datalen){ + if(inb(port+Rstatus) & Dirrdy){ + data[len] = inb(port+Rdatain); + len++; + } + if(inb(port+Rinterrupt) & Cmdc) + return; + } + } +} + +/* + * Issue a command to a controller, wait for it to complete then + * try to reset the interrupt. Should only be called at initialisation. + */ +static int +issue(Ctlr* ctlr, uchar* cmd, int cmdlen, uchar* data, int datalen) +{ + int port; + uchar rinterrupt, rstatus; + static Lock mylexissuelock; + + port = ctlr->port; + + ilock(&ctlr->issuelock); + issueio(port, cmd, cmdlen, data, datalen); + + while(!((rinterrupt = inb(port+Rinterrupt)) & Cmdc)) + ; + + rstatus = inb(port+Rstatus); + outb(port+Rcontrol, Rint); + iunlock(&ctlr->issuelock); + + if((rinterrupt & Cmdc) && (rstatus & Cmdinv)) + return 0; + return 1; +} + +static SDev* +mylexprobe(int port, int irq) +{ + SDev *sdev; + Ctlr *ctlr; + uchar cmd[6], data[256]; + int clen, dlen, timeo; + + if(ioalloc(port, 0x3, 0, "mylex") < 0) + return nil; + ctlr = nil; + sdev = nil; + /* + * Attempt to hard-reset the board and reset + * the SCSI bus. If the board state doesn't settle to + * idle with mailbox initialisation required, either + * it isn't a compatible board or it's broken. + * If the controller has SCAM set this can take a while. + */ + if(getconf("*noscsireset") != nil) + outb(port+Rcontrol, Rhard); + else + outb(port+Rcontrol, Rhard|Rsbus); + for(timeo = 0; timeo < 100; timeo++){ + if(inb(port+Rstatus) == (Inreq|Hardy)) + break; + delay(100); + } + if(inb(port+Rstatus) != (Inreq|Hardy)){ +buggery: + if(ctlr != nil) + free(ctlr); + if (sdev != nil) + free(sdev); + iofree(port); + return nil; + } + + if((ctlr = malloc(sizeof(Ctlr))) == nil) + goto buggery; + ctlr->port = port; + ctlr->irq = irq; + ctlr->bus = 24; + ctlr->wide = 0; + + /* + * Try to determine if this is a 32-bit MultiMaster controller + * by attempting to obtain the extended inquiry information; + * this command is not implemented on Adaptec 154xx + * controllers. If successful, the first byte of the returned + * data is the host adapter bus type, 'E' for 32-bit EISA, + * PCI and VLB buses. + */ + cmd[0] = Ciesi; + cmd[1] = 4; + clen = 2; + dlen = 256; + if(issue(ctlr, cmd, clen, data, dlen)){ + if(data[0] == 'E') + ctlr->bus = 32; + ctlr->wide = data[0x0D] & 0x01; + } + else{ + /* + * Inconceivable though it may seem, a hard controller reset + * is necessary here to clear out the command queue. Every + * board seems to lock-up in a different way if you give an + * invalid command and then try to clear out the + * command/parameter and/or data-in register. + * Soft reset doesn't do the job either. Fortunately no + * serious initialisation has been done yet so there's nothing + * to tidy up. + */ + outb(port+Rcontrol, Rhard); + for(timeo = 0; timeo < 100; timeo++){ + if(inb(port+Rstatus) == (Inreq|Hardy)) + break; + delay(100); + } + if(inb(port+Rstatus) != (Inreq|Hardy)) + goto buggery; + } + + /* + * If the BIOS is enabled on the AHA-1542C/CF and BIOS options for + * support of drives > 1Gb, dynamic scanning of the SCSI bus or more + * than 2 drives under DOS 5.0 are enabled, the BIOS disables + * accepting Cmbinit to protect against running with drivers which + * don't support those options. In order to unlock the interface it + * is necessary to read a lock-code using Cextbios and write it back + * using Cmbienable; the lock-code is non-zero. + */ + cmd[0] = Cinquiry; + clen = 1; + dlen = 4; + if(issue(ctlr, cmd, clen, data, dlen) == 0) + goto buggery; + if(data[0] >= 0x43){ + cmd[0] = Cextbios; + clen = 1; + dlen = 2; + if(issue(ctlr, cmd, clen, data, dlen) == 0) + goto buggery; + + /* + * Lock-code returned in data[1]. If it's non-zero write + * it back along with bit 0 of byte 0 cleared to enable + * mailbox initialisation. + */ + if(data[1]){ + cmd[0] = Cmbienable; + cmd[1] = 0; + cmd[2] = data[1]; + clen = 3; + if(issue(ctlr, cmd, clen, 0, 0) == 0) + goto buggery; + } + } + + /* + * Get the id, DMA and IRQ info from the board. This will + * cause an interrupt which will hopefully not cause any + * trouble because the interrupt number isn't known yet. + * This is necessary as the DMA won't be set up if the + * board has the BIOS disabled. + * + * If the IRQ is already known, this must be a 32-bit PCI + * or EISA card, in which case the returned DMA and IRQ can + * be ignored. + */ + cmd[0] = Cinquire; + clen = 1; + dlen = 3; + if(issue(ctlr, cmd, clen, data, dlen) == 0) + goto buggery; + + ctlr->id = data[2] & 0x07; + if(ctlr->irq < 0){ + switch(data[0]){ /* DMA Arbitration Priority */ + case 0x80: /* Channel 7 */ + outb(0xD6, 0xC3); + outb(0xD4, 0x03); + break; + case 0x40: /* Channel 6 */ + outb(0xD6, 0xC2); + outb(0xD4, 0x02); + break; + case 0x20: /* Channel 5 */ + outb(0xD6, 0xC1); + outb(0xD4, 0x01); + break; + case 0x01: /* Channel 0 */ + outb(0x0B, 0xC0); + outb(0x0A, 0x00); + break; + default: + if(ctlr->bus == 24) + goto buggery; + break; + } + + switch(data[1]){ /* Interrupt Channel */ + case 0x40: + ctlr->irq = 15; + break; + case 0x20: + ctlr->irq = 14; + break; + case 0x08: + ctlr->irq = 12; + break; + case 0x04: + ctlr->irq = 11; + break; + case 0x02: + ctlr->irq = 10; + break; + case 0x01: + ctlr->irq = 9; + break; + default: + goto buggery; + } + } + + if((sdev = malloc(sizeof(SDev))) == nil) + goto buggery; + sdev->ifc = &sdmylexifc; + sdev->ctlr = ctlr; + ctlr->sdev = sdev; + if(!ctlr->wide) + sdev->nunit = 8; + else + sdev->nunit = 16; + + return sdev; +} + +static int mylexport[8] = { + 0x330, 0x334, 0x230, 0x234, 0x130, 0x134, 0x000, 0x000, +}; + +static SDev* +mylexpnp(void) +{ + Pcidev *p; + Ctlr *ctlr; + ISAConf isa; + int cfg, ctlrno, i, x; + SDev *sdev, *head, *tail; + + p = nil; + head = tail = nil; + while(p = pcimatch(p, 0x104B, 0)){ + if((sdev = mylexprobe(p->mem[0].bar & ~0x01, p->intl)) == nil) + continue; + + ctlr = sdev->ctlr; + ctlr->pcidev = p; + + if(head != nil) + tail->next = sdev; + else + head = sdev; + tail = sdev; + } + + if(strncmp(KADDR(0xFFFD9), "EISA", 4) == 0){ + for(cfg = 0x1000; cfg < MaxEISA*0x1000; cfg += 0x1000){ + x = 0; + for(i = 0; i < 4; i++) + x |= inb(cfg+CfgEISA+i)<<(i*8); + if(x != 0x0142B30A && x != 0x0242B30A) + continue; + + x = inb(cfg+0xC8C); + if((sdev = mylexprobe(mylexport[x & 0x07], -1)) == nil) + continue; + + if(head != nil) + tail->next = sdev; + else + head = sdev; + tail = sdev; + } + } + + for(ctlrno = 0; ctlrno < 4; ctlrno++){ + memset(&isa, 0, sizeof(isa)); + if(!isaconfig("scsi", ctlrno, &isa)) + continue; + if(strcmp(isa.type, "aha1542")) + continue; + if((sdev = mylexprobe(isa.port, -1)) == nil) + continue; + + if(head != nil) + tail->next = sdev; + else + head = sdev; + tail = sdev; + } + + return head; +} + +static SDev* +mylexid(SDev* sdev) +{ + return scsiid(sdev, &sdmylexifc); +} + +static int +mylex24enable(Ctlr* ctlr) +{ + ulong p; + Ccb24 *ccb, *ccbp; + uchar cmd[6], *v; + int len; + + len = (sizeof(Mbox24)*NMbox*2)+(sizeof(Ccb24)*NCcb); + v = xspanalloc(len, 32, 0); + + if(!PADDR24(ctlr, sizeof(Ctlr)) || !PADDR24(v, len)) + return 0; + + ctlr->mb = v; + v += sizeof(Mbox24)*NMbox*2; + + ccb = (Ccb24*)v; + for(ccbp = ccb; ccbp < &ccb[NCcb]; ccbp++){ + ccbp->ccb = ctlr->ccb; + ctlr->ccb = (Ccb*)ccbp; + } + + /* + * Initialise the software controller and + * set the board scanning the mailboxes. + */ + ctlr->mbix = NMbox; + + cmd[0] = Cinitialise; + cmd[1] = NMbox; + p = K2BPA(ctlr->mb, BUSUNKNOWN); + cmd[2] = p>>16; + cmd[3] = p>>8; + cmd[4] = p; + + return issue(ctlr, cmd, 5, 0, 0); +} + +static int +mylex32enable(Ctlr* ctlr) +{ + ulong p; + Ccb32 *ccb, *ccbp; + uchar cmd[6], *v; + + v = xspanalloc((sizeof(Mbox32)*NMbox*2)+(sizeof(Ccb32)*NCcb), 32, 0); + + ctlr->mb = v; + v += sizeof(Mbox32)*NMbox*2; + + ccb = (Ccb32*)v; + for(ccbp = ccb; ccbp < &ccb[NCcb]; ccbp++){ + /* + * Fill in some stuff that doesn't change. + */ + ccbp->senselen = sizeof(ccbp->sense); + p = PADDR(ccbp->sense); + ccbp->senseptr[0] = p; + ccbp->senseptr[1] = p>>8; + ccbp->senseptr[2] = p>>16; + ccbp->senseptr[3] = p>>24; + + ccbp->ccb = ctlr->ccb; + ctlr->ccb = (Ccb*)ccbp; + } + + /* + * Attempt wide mode setup. + */ + if(ctlr->wide){ + cmd[0] = Cwide; + cmd[1] = 1; + if(!issue(ctlr, cmd, 2, 0, 0)) + ctlr->wide = 0; + } + + /* + * Initialise the software controller and + * set the board scanning the mailboxes. + */ + ctlr->mbix = NMbox; + + cmd[0] = Ciem; + cmd[1] = NMbox; + if(ctlr->pcidev) + p = K2BPA(ctlr->mb, ctlr->tbdf); + else + p = K2BPA(ctlr->mb, BUSUNKNOWN); + cmd[2] = p; + cmd[3] = p>>8; + cmd[4] = p>>16; + cmd[5] = p>>24; + + return issue(ctlr, cmd, 6, 0, 0); +} + +static int +mylexenable(SDev* sdev) +{ + int tbdf; + Ctlr *ctlr; + void (*interrupt)(Ureg*, void*); + char name[32]; + + ctlr = sdev->ctlr; + if(ctlr->cache == nil){ + if((ctlr->cache = malloc(sdev->nunit*sizeof(Ccb*))) == nil) + return 0; + } + + tbdf = BUSUNKNOWN; + if(ctlr->bus == 32){ + if(ctlr->pcidev){ + tbdf = ctlr->pcidev->tbdf; + pcisetbme(ctlr->pcidev); + } + if(!mylex32enable(ctlr)) + return 0; + interrupt = mylex32interrupt; + } + else if(mylex24enable(ctlr)) + interrupt = mylex24interrupt; + else + return 0; + + snprint(name, sizeof(name), "sd%c (%s)", sdev->idno, sdev->ifc->name); + intrenable(ctlr->irq, interrupt, ctlr, tbdf, name); + + return 1; +} + +SDifc sdmylexifc = { + "mylex", /* name */ + + mylexpnp, /* pnp */ + nil, /* legacy */ + mylexid, /* id */ + mylexenable, /* enable */ + nil, /* disable */ + + scsiverify, /* verify */ + scsionline, /* online */ + mylexrio, /* rio */ + nil, /* rctl */ + nil, /* wctl */ + + scsibio, /* bio */ + nil, /* probe */ + nil, /* clear */ + nil, /* stat */ +}; diff --git a/os/pc/sdscsi.c b/os/pc/sdscsi.c new file mode 100644 index 00000000..0340564c --- /dev/null +++ b/os/pc/sdscsi.c @@ -0,0 +1,394 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +#include "../port/sd.h" + +static int +scsitest(SDreq* r) +{ + r->write = 0; + memset(r->cmd, 0, sizeof(r->cmd)); + r->cmd[1] = r->lun<<5; + r->clen = 6; + r->data = nil; + r->dlen = 0; + r->flags = 0; + + r->status = ~0; + + return r->unit->dev->ifc->rio(r); +} + +int +scsiverify(SDunit* unit) +{ + SDreq *r; + int i, status; + uchar *inquiry; + + if((r = malloc(sizeof(SDreq))) == nil) + return 0; + if((inquiry = sdmalloc(sizeof(unit->inquiry))) == nil){ + free(r); + return 0; + } + r->unit = unit; + r->lun = 0; /* ??? */ + + memset(unit->inquiry, 0, sizeof(unit->inquiry)); + r->write = 0; + r->cmd[0] = 0x12; + r->cmd[1] = r->lun<<5; + r->cmd[4] = sizeof(unit->inquiry)-1; + r->clen = 6; + r->data = inquiry; + r->dlen = sizeof(unit->inquiry)-1; + r->flags = 0; + + r->status = ~0; + if(unit->dev->ifc->rio(r) != SDok){ + free(r); + return 0; + } + memmove(unit->inquiry, inquiry, r->dlen); + free(inquiry); + + SET(status); + for(i = 0; i < 3; i++){ + while((status = scsitest(r)) == SDbusy) + ; + if(status == SDok || status != SDcheck) + break; + if(!(r->flags & SDvalidsense)) + break; + if((r->sense[2] & 0x0F) != 0x02) + continue; + + /* + * Unit is 'not ready'. + * If it is in the process of becoming ready or needs + * an initialising command, set status so it will be spun-up + * below. + * If there's no medium, that's OK too, but don't + * try to spin it up. + */ + if(r->sense[12] == 0x04){ + if(r->sense[13] == 0x02 || r->sense[13] == 0x01){ + status = SDok; + break; + } + } + if(r->sense[12] == 0x3A) + break; + } + + if(status == SDok){ + /* + * Try to ensure a direct-access device is spinning. + * Don't wait for completion, ignore the result. + */ + if((unit->inquiry[0] & 0x1F) == 0){ + memset(r->cmd, 0, sizeof(r->cmd)); + r->write = 0; + r->cmd[0] = 0x1B; + r->cmd[1] = (r->lun<<5)|0x01; + r->cmd[4] = 1; + r->clen = 6; + r->data = nil; + r->dlen = 0; + r->flags = 0; + + r->status = ~0; + unit->dev->ifc->rio(r); + } + } + free(r); + + if(status == SDok || status == SDcheck) + return 1; + return 0; +} + +static int +scsirio(SDreq* r) +{ + /* + * Perform an I/O request, returning + * -1 failure + * 0 ok + * 1 no medium present + * 2 retry + * The contents of r may be altered so the + * caller should re-initialise if necesary. + */ + r->status = ~0; + switch(r->unit->dev->ifc->rio(r)){ + default: + return -1; + case SDcheck: + if(!(r->flags & SDvalidsense)) + return -1; + switch(r->sense[2] & 0x0F){ + case 0x00: /* no sense */ + case 0x01: /* recovered error */ + return 2; + case 0x06: /* check condition */ + /* + * 0x28 - not ready to ready transition, + * medium may have changed. + * 0x29 - power on or some type of reset. + */ + if(r->sense[12] == 0x28 && r->sense[13] == 0) + return 2; + if(r->sense[12] == 0x29) + return 2; + return -1; + case 0x02: /* not ready */ + /* + * If no medium present, bail out. + * If unit is becoming ready, rather than not + * not ready, wait a little then poke it again. */ + if(r->sense[12] == 0x3A) + return 1; + if(r->sense[12] != 0x04 || r->sense[13] != 0x01) + return -1; + + while(waserror()) + ; + tsleep(&up->sleep, return0, 0, 500); + poperror(); + scsitest(r); + return 2; + default: + return -1; + } + return -1; + case SDok: + return 0; + } + return -1; +} + +int +scsionline(SDunit* unit) +{ + SDreq *r; + uchar *p; + int ok, retries; + + if((r = malloc(sizeof(SDreq))) == nil) + return 0; + if((p = sdmalloc(8)) == nil){ + free(r); + return 0; + } + + ok = 0; + + r->unit = unit; + r->lun = 0; /* ??? */ + for(retries = 0; retries < 10; retries++){ + /* + * Read-capacity is mandatory for DA, WORM, CD-ROM and + * MO. It may return 'not ready' if type DA is not + * spun up, type MO or type CD-ROM are not loaded or just + * plain slow getting their act together after a reset. + */ + r->write = 0; + memset(r->cmd, 0, sizeof(r->cmd)); + r->cmd[0] = 0x25; + r->cmd[1] = r->lun<<5; + r->clen = 10; + r->data = p; + r->dlen = 8; + r->flags = 0; + + r->status = ~0; + switch(scsirio(r)){ + default: + break; + case 0: + unit->sectors = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]; + if(unit->sectors == 0) + continue; + /* + * Read-capacity returns the LBA of the last sector, + * therefore the number of sectors must be incremented. + */ + unit->sectors++; + unit->secsize = (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7]; + + /* + * Some ATAPI CD readers lie about the block size. + * Since we don't read audio via this interface + * it's okay to always fudge this. + */ + if(unit->secsize == 2352) + unit->secsize = 2048; + ok = 1; + break; + case 1: + ok = 1; + break; + case 2: + continue; + } + break; + } + free(p); + free(r); + + if(ok) + return ok+retries; + else + return 0; +} + +int +scsiexec(SDunit* unit, int write, uchar* cmd, int clen, void* data, int* dlen) +{ + SDreq *r; + int status; + + if((r = malloc(sizeof(SDreq))) == nil) + return SDmalloc; + r->unit = unit; + r->lun = cmd[1]>>5; /* ??? */ + r->write = write; + memmove(r->cmd, cmd, clen); + r->clen = clen; + r->data = data; + if(dlen) + r->dlen = *dlen; + r->flags = 0; + + r->status = ~0; + + /* + * Call the device-specific I/O routine. + * There should be no calls to 'error()' below this + * which percolate back up. + */ + switch(status = unit->dev->ifc->rio(r)){ + case SDok: + if(dlen) + *dlen = r->rlen; + /*FALLTHROUGH*/ + case SDcheck: + /*FALLTHROUGH*/ + default: + /* + * It's more complicated than this. There are conditions + * which are 'ok' but for which the returned status code + * is not 'SDok'. + * Also, not all conditions require a reqsense, might + * need to do a reqsense here and make it available to the + * caller somehow. + * + * Mañana. + */ + break; + } + sdfree(r); + + return status; +} + +long +scsibio(SDunit* unit, int lun, int write, void* data, long nb, long bno) +{ + SDreq *r; + long rlen; + + if((r = malloc(sizeof(SDreq))) == nil) + error(Enomem); + r->unit = unit; + r->lun = lun; +again: + r->write = write; + if(write == 0) + r->cmd[0] = 0x28; + else + r->cmd[0] = 0x2A; + r->cmd[1] = (lun<<5); + r->cmd[2] = bno>>24; + r->cmd[3] = bno>>16; + r->cmd[4] = bno>>8; + r->cmd[5] = bno; + r->cmd[6] = 0; + r->cmd[7] = nb>>8; + r->cmd[8] = nb; + r->cmd[9] = 0; + r->clen = 10; + r->data = data; + r->dlen = nb*unit->secsize; + r->flags = 0; + + r->status = ~0; + switch(scsirio(r)){ + default: + rlen = -1; + break; + case 0: + rlen = r->rlen; + break; + case 2: + rlen = -1; + if(!(r->flags & SDvalidsense)) + break; + switch(r->sense[2] & 0x0F){ + default: + break; + case 0x06: /* check condition */ + /* + * Check for a removeable media change. + * If so, mark it by zapping the geometry info + * to force an online request. + */ + if(r->sense[12] != 0x28 || r->sense[13] != 0) + break; + if(unit->inquiry[1] & 0x80) + unit->sectors = 0; + break; + case 0x02: /* not ready */ + /* + * If unit is becoming ready, + * rather than not not ready, try again. + */ + if(r->sense[12] == 0x04 && r->sense[13] == 0x01) + goto again; + break; + } + break; + } + free(r); + + return rlen; +} + +SDev* +scsiid(SDev* sdev, SDifc* ifc) +{ + char name[32]; + static char idno[16] = "0123456789abcdef"; + static char *p = idno; + + while(sdev){ + if(sdev->ifc == ifc){ + sdev->idno = *p++; + snprint(name, sizeof(name), "sd%c", sdev->idno); + kstrdup(&sdev->name, name); + if(p >= &idno[sizeof(idno)]) + break; + } + sdev = sdev->next; + } + + return nil; +} diff --git a/os/pc/trap.c b/os/pc/trap.c new file mode 100644 index 00000000..465c9bab --- /dev/null +++ b/os/pc/trap.c @@ -0,0 +1,571 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +int (*breakhandler)(Ureg *ur, Proc*); + +static void debugbpt(Ureg*, void*); +static void fault386(Ureg*, void*); +static void doublefault(Ureg*, void*); +static void unexpected(Ureg*, void*); +static void _dumpstack(Ureg*); + +static Lock vctllock; +static Vctl *vctl[256]; + +enum +{ + Ntimevec = 20 /* number of time buckets for each intr */ +}; +ulong intrtimes[256][Ntimevec]; + +void +intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name) +{ + int vno; + Vctl *v; + + if(f == nil){ + print("intrenable: nil handler for %d, tbdf 0x%uX for %s\n", + irq, tbdf, name); + return; + } + + v = xalloc(sizeof(Vctl)); + v->isintr = 1; + v->irq = irq; + v->tbdf = tbdf; + v->f = f; + v->a = a; + strncpy(v->name, name, KNAMELEN-1); + v->name[KNAMELEN-1] = 0; + + ilock(&vctllock); + vno = arch->intrenable(v); + if(vno == -1){ + iunlock(&vctllock); + print("intrenable: couldn't enable irq %d, tbdf 0x%uX for %s\n", + irq, tbdf, v->name); + xfree(v); + return; + } + if(vctl[vno]){ + if(vctl[vno]->isr != v->isr || vctl[vno]->eoi != v->eoi) + panic("intrenable: handler: %s %s %luX %luX %luX %luX\n", + vctl[vno]->name, v->name, + vctl[vno]->isr, v->isr, vctl[vno]->eoi, v->eoi); + v->next = vctl[vno]; + } + vctl[vno] = v; + iunlock(&vctllock); +} + +int +intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name) +{ + Vctl **pv, *v; + int vno; + + /* + * For now, none of this will work with the APIC code, + * there is no mapping between irq and vector as the IRQ + * is pretty meaningless. + */ + if(arch->intrvecno == nil) + return -1; + vno = arch->intrvecno(irq); + ilock(&vctllock); + pv = &vctl[vno]; + while (*pv && + ((*pv)->irq != irq || (*pv)->tbdf != tbdf || (*pv)->f != f || (*pv)->a != a || + strcmp((*pv)->name, name))) + pv = &((*pv)->next); + assert(*pv); + + v = *pv; + *pv = (*pv)->next; /* Link out the entry */ + + if(vctl[vno] == nil && arch->intrdisable != nil) + arch->intrdisable(irq); + iunlock(&vctllock); + xfree(v); + return 0; +} + +static long +irqallocread(Chan*, void *vbuf, long n, vlong offset) +{ + char *buf, *p, str[2*(11+1)+KNAMELEN+1+1]; + int m, vno; + long oldn; + Vctl *v; + + if(n < 0 || offset < 0) + error(Ebadarg); + + oldn = n; + buf = vbuf; + for(vno=0; vnonext){ + m = snprint(str, sizeof str, "%11d %11d %.*s\n", vno, v->irq, KNAMELEN, v->name); + if(m <= offset) /* if do not want this, skip entry */ + offset -= m; + else{ + /* skip offset bytes */ + m -= offset; + p = str+offset; + offset = 0; + + /* write at most max(n,m) bytes */ + if(m > n) + m = n; + memmove(buf, p, m); + n -= m; + buf += m; + + if(n == 0) + return oldn; + } + } + } + return oldn - n; +} + +void +trapenable(int vno, void (*f)(Ureg*, void*), void* a, char *name) +{ + Vctl *v; + + if(vno < 0 || vno >= VectorPIC) + panic("trapenable: vno %d\n", vno); + v = xalloc(sizeof(Vctl)); + v->tbdf = BUSUNKNOWN; + v->f = f; + v->a = a; + strncpy(v->name, name, KNAMELEN); + v->name[KNAMELEN-1] = 0; + + lock(&vctllock); + if(vctl[vno]) + v->next = vctl[vno]->next; + vctl[vno] = v; + unlock(&vctllock); +} + +static void +nmienable(void) +{ + int x; + + /* + * Hack: should be locked with NVRAM access. + */ + outb(0x70, 0x80); /* NMI latch clear */ + outb(0x70, 0); + + x = inb(0x61) & 0x07; /* Enable NMI */ + outb(0x61, 0x08|x); + outb(0x61, x); +} + +void +trapinit(void) +{ + int d1, v; + ulong vaddr; + Segdesc *idt; + + idt = (Segdesc*)IDTADDR; + vaddr = (ulong)vectortable; + for(v = 0; v < 256; v++){ + d1 = (vaddr & 0xFFFF0000)|SEGP; + switch(v){ + + case VectorBPT: + d1 |= SEGPL(3)|SEGIG; + break; + + case VectorSYSCALL: + d1 |= SEGPL(3)|SEGIG; + break; + + default: + d1 |= SEGPL(0)|SEGIG; + break; + } + idt[v].d0 = (vaddr & 0xFFFF)|(KESEL<<16); + idt[v].d1 = d1; + vaddr += 6; + } + + /* + * Special traps. + * Syscall() is called directly without going through trap(). + */ + trapenable(VectorBPT, debugbpt, 0, "debugpt"); + trapenable(VectorPF, fault386, 0, "fault386"); + trapenable(Vector2F, doublefault, 0, "doublefault"); + trapenable(Vector15, unexpected, 0, "unexpected"); + + nmienable(); + + addarchfile("irqalloc", 0444, irqallocread, nil); +} + +static char* excname[32] = { + "divide error", + "debug exception", + "nonmaskable interrupt", + "breakpoint", + "overflow", + "bounds check", + "invalid opcode", + "coprocessor not available", + "double fault", + "coprocessor segment overrun", + "invalid TSS", + "segment not present", + "stack exception", + "general protection violation", + "page fault", + "15 (reserved)", + "coprocessor error", + "alignment check", + "machine check", + "19 (reserved)", + "20 (reserved)", + "21 (reserved)", + "22 (reserved)", + "23 (reserved)", + "24 (reserved)", + "25 (reserved)", + "26 (reserved)", + "27 (reserved)", + "28 (reserved)", + "29 (reserved)", + "30 (reserved)", + "31 (reserved)", +}; + +/* + * keep histogram of interrupt service times + */ +void +intrtime(Mach*, int vno) +{ + USED(vno); +} + +/* + * All traps come here. It is slower to have all traps call trap() + * rather than directly vectoring the handler. However, this avoids a + * lot of code duplication and possible bugs. The only exception is + * VectorSYSCALL. + * Trap is called with interrupts disabled via interrupt-gates. + */ +void +trap(Ureg* ureg) +{ + int i, vno; + char buf[ERRMAX]; + Vctl *ctl, *v; + Mach *mach; + + vno = ureg->trap; + if(ctl = vctl[vno]){ + if(ctl->isintr){ + m->intr++; + if(vno >= VectorPIC && vno != VectorSYSCALL) + m->lastintr = ctl->irq; + } + + if(ctl->isr) + ctl->isr(vno); + for(v = ctl; v != nil; v = v->next){ + if(v->f) + v->f(ureg, v->a); + } + if(ctl->eoi) + ctl->eoi(vno); + + if(ctl->isintr){ + if(up && ctl->irq != IrqTIMER && ctl->irq != IrqCLOCK) + preemption(0); + } + } + else if(vno <= nelem(excname) && up->type == Interp){ + spllo(); + sprint(buf, "sys: trap: %s", excname[vno]); + error(buf); + } + else if(vno >= VectorPIC && vno != VectorSYSCALL){ + /* + * An unknown interrupt. + * Check for a default IRQ7. This can happen when + * the IRQ input goes away before the acknowledge. + * In this case, a 'default IRQ7' is generated, but + * the corresponding bit in the ISR isn't set. + * In fact, just ignore all such interrupts. + */ + + /* call all interrupt routines, just in case */ + for(i = VectorPIC; i <= MaxIrqLAPIC; i++){ + ctl = vctl[i]; + if(ctl == nil) + continue; + if(!ctl->isintr) + continue; + for(v = ctl; v != nil; v = v->next){ + if(v->f) + v->f(ureg, v->a); + } + /* should we do this? */ + if(ctl->eoi) + ctl->eoi(i); + } + + /* clear the interrupt */ + i8259isr(vno); + + if(0)print("cpu%d: spurious interrupt %d, last %d", + m->machno, vno, m->lastintr); + if(0)if(conf.nmach > 1){ + for(i = 0; i < 32; i++){ + if(!(active.machs & (1<machno == mach->machno) + continue; + print(" cpu%d: last %d", + mach->machno, mach->lastintr); + } + print("\n"); + } + m->spuriousintr++; + return; + } + else{ + if(vno == VectorNMI){ + nmienable(); + if(m->machno != 0){ + print("cpu%d: PC %8.8luX\n", + m->machno, ureg->pc); + for(;;); + } + } + dumpregs(ureg); + if(vno < nelem(excname)) + panic("%s", excname[vno]); + panic("unknown trap/intr: %d\n", vno); + } + + /* delaysched set because we held a lock or because our quantum ended */ + if(up && up->delaysched){ + sched(); + splhi(); + } +} + +/* + * dump registers + */ +void +dumpregs2(Ureg* ureg) +{ + if(up) + print("cpu%d: registers for %s %lud\n", + m->machno, up->text, up->pid); + else + print("cpu%d: registers for kernel\n", m->machno); + print("FLAGS=%luX TRAP=%luX ECODE=%luX PC=%luX", + ureg->flags, ureg->trap, ureg->ecode, ureg->pc); + print(" SS=%4.4luX USP=%luX\n", ureg->ss & 0xFFFF, ureg->usp); + print(" AX %8.8luX BX %8.8luX CX %8.8luX DX %8.8luX\n", + ureg->ax, ureg->bx, ureg->cx, ureg->dx); + print(" SI %8.8luX DI %8.8luX BP %8.8luX\n", + ureg->si, ureg->di, ureg->bp); + print(" CS %4.4luX DS %4.4luX ES %4.4luX FS %4.4luX GS %4.4luX\n", + ureg->cs & 0xFFFF, ureg->ds & 0xFFFF, ureg->es & 0xFFFF, + ureg->fs & 0xFFFF, ureg->gs & 0xFFFF); +} + +void +dumpregs(Ureg* ureg) +{ + extern ulong etext; + vlong mca, mct; + + dumpregs2(ureg); + + /* + * Processor control registers. + * If machine check exception, time stamp counter, page size extensions + * or enhanced virtual 8086 mode extensions are supported, there is a + * CR4. If there is a CR4 and machine check extensions, read the machine + * check address and machine check type registers if RDMSR supported. + */ + print(" CR0 %8.8lux CR2 %8.8lux CR3 %8.8lux", + getcr0(), getcr2(), getcr3()); + if(m->cpuiddx & 0x9A){ + print(" CR4 %8.8lux", getcr4()); + if((m->cpuiddx & 0xA0) == 0xA0){ + rdmsr(0x00, &mca); + rdmsr(0x01, &mct); + print("\n MCA %8.8llux MCT %8.8llux", mca, mct); + } + } + print("\n ur %lux up %lux\n", ureg, up); +} + +/* + * Fill in enough of Ureg to get a stack trace, and call a function. + * Used by debugging interface rdb. + */ +void +callwithureg(void (*fn)(Ureg*)) +{ + Ureg ureg; + ureg.pc = getcallerpc(&fn); + ureg.sp = (ulong)&fn; + fn(&ureg); +} + +static void +_dumpstack(Ureg *ureg) +{ + ulong l, v, i, estack; + extern ulong etext; + + print("ktrace /kernel/path %.8lux %.8lux\n", ureg->pc, ureg->sp); + i = 0; + if(up + && (ulong)&l >= (ulong)up->kstack + && (ulong)&l <= (ulong)up->kstack+KSTACK) + estack = (ulong)up->kstack+KSTACK; + else if((ulong)&l >= (ulong)m->stack + && (ulong)&l <= (ulong)m+BY2PG) + estack = (ulong)m+MACHSIZE; + else + return; + + for(l=(ulong)&l; lpc--; + sprint(buf, "sys: breakpoint"); + error(buf); +} + +static void +doublefault(Ureg*, void*) +{ + panic("double fault"); +} + +static void +unexpected(Ureg* ureg, void*) +{ + print("unexpected trap %lud; ignoring\n", ureg->trap); +} + +static void +fault386(Ureg* ureg, void*) +{ + ulong addr; + int read, user; + char buf[ERRMAX]; + + addr = getcr2(); + user = (ureg->cs & 0xFFFF) == UESEL; + if(!user && mmukmapsync(addr)) + return; + read = !(ureg->ecode & 2); + spllo(); + snprint(buf, sizeof(buf), "trap: fault %s pc=0x%lux addr=0x%lux", + read ? "read" : "write", ureg->pc, addr); + if(up->type == Interp) + disfault(ureg, buf); + dumpregs(ureg); + panic("fault: %s\n", buf); +} + + + + + +static void +linkproc(void) +{ + spllo(); + up->kpfun(up->arg); + pexit("kproc dying", 0); +} + +void +kprocchild(Proc* p, void (*func)(void*), void* arg) +{ + /* + * gotolabel() needs a word on the stack in + * which to place the return PC used to jump + * to linkproc(). + */ + p->sched.pc = (ulong)linkproc; + p->sched.sp = (ulong)p->kstack+KSTACK-BY2WD; + + p->kpfun = func; + p->arg = arg; +} + + + +ulong +dbgpc(Proc *p) +{ + Ureg *ureg; + + ureg = p->dbgreg; + if(ureg == 0) + return 0; + + return ureg->pc; +} diff --git a/os/pc/tv.h b/os/pc/tv.h new file mode 100644 index 00000000..181563b8 --- /dev/null +++ b/os/pc/tv.h @@ -0,0 +1,15 @@ +static ushort +swab16(ushort u) { + return u; +} + +#define ScreenWidth 640 /* screen width */ +#define ScreenHeight 480 /* screen height */ +#define XCorrection 9 /* correction for x axes (trial and error) */ +#define YCorrection 32 /* correction for y axes (trial and error) */ +#define HSync 1 +#define VSync 1 + +#define AudioChip TEA6320T /* new board has TEA6320T */ + +#define EISA(a) (a) diff --git a/os/pc/uarti8250.c b/os/pc/uarti8250.c new file mode 100644 index 00000000..2d6c0da2 --- /dev/null +++ b/os/pc/uarti8250.c @@ -0,0 +1,740 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/uart.h" + +/* + * 8250 UART and compatibles. + */ +enum { + Uart0 = 0x3F8, /* COM1 */ + Uart0IRQ = 4, + Uart1 = 0x2F8, /* COM2 */ + Uart1IRQ = 3, + + UartFREQ = 1843200, +}; + +enum { /* I/O ports */ + Rbr = 0, /* Receiver Buffer (RO) */ + Thr = 0, /* Transmitter Holding (WO) */ + Ier = 1, /* Interrupt Enable */ + Iir = 2, /* Interrupt Identification (RO) */ + Fcr = 2, /* FIFO Control (WO) */ + Lcr = 3, /* Line Control */ + Mcr = 4, /* Modem Control */ + Lsr = 5, /* Line Status */ + Msr = 6, /* Modem Status */ + Scr = 7, /* Scratch Pad */ + Dll = 0, /* Divisor Latch LSB */ + Dlm = 1, /* Divisor Latch MSB */ +}; + +enum { /* Ier */ + Erda = 0x01, /* Enable Received Data Available */ + Ethre = 0x02, /* Enable Thr Empty */ + Erls = 0x04, /* Enable Receiver Line Status */ + Ems = 0x08, /* Enable Modem Status */ +}; + +enum { /* Iir */ + Ims = 0x00, /* Ms interrupt */ + Ip = 0x01, /* Interrupt Pending (not) */ + Ithre = 0x02, /* Thr Empty */ + Irda = 0x04, /* Received Data Available */ + Irls = 0x06, /* Receiver Line Status */ + Ictoi = 0x0C, /* Character Time-out Indication */ + IirMASK = 0x3F, + Ifena = 0xC0, /* FIFOs enabled */ +}; + +enum { /* Fcr */ + FIFOena = 0x01, /* FIFO enable */ + FIFOrclr = 0x02, /* clear Rx FIFO */ + FIFOtclr = 0x04, /* clear Tx FIFO */ + FIFO1 = 0x00, /* Rx FIFO trigger level 1 byte */ + FIFO4 = 0x40, /* 4 bytes */ + FIFO8 = 0x80, /* 8 bytes */ + FIFO14 = 0xC0, /* 14 bytes */ +}; + +enum { /* Lcr */ + Wls5 = 0x00, /* Word Length Select 5 bits/byte */ + Wls6 = 0x01, /* 6 bits/byte */ + Wls7 = 0x02, /* 7 bits/byte */ + Wls8 = 0x03, /* 8 bits/byte */ + WlsMASK = 0x03, + Stb = 0x04, /* 2 stop bits */ + Pen = 0x08, /* Parity Enable */ + Eps = 0x10, /* Even Parity Select */ + Stp = 0x20, /* Stick Parity */ + Brk = 0x40, /* Break */ + Dlab = 0x80, /* Divisor Latch Access Bit */ +}; + +enum { /* Mcr */ + Dtr = 0x01, /* Data Terminal Ready */ + Rts = 0x02, /* Ready To Send */ + Out1 = 0x04, /* no longer in use */ + Ie = 0x08, /* IRQ Enable */ + Dm = 0x10, /* Diagnostic Mode loopback */ +}; + +enum { /* Lsr */ + Dr = 0x01, /* Data Ready */ + Oe = 0x02, /* Overrun Error */ + Pe = 0x04, /* Parity Error */ + Fe = 0x08, /* Framing Error */ + Bi = 0x10, /* Break Interrupt */ + Thre = 0x20, /* Thr Empty */ + Temt = 0x40, /* Tramsmitter Empty */ + FIFOerr = 0x80, /* error in receiver FIFO */ +}; + +enum { /* Msr */ + Dcts = 0x01, /* Delta Cts */ + Ddsr = 0x02, /* Delta Dsr */ + Teri = 0x04, /* Trailing Edge of Ri */ + Ddcd = 0x08, /* Delta Dcd */ + Cts = 0x10, /* Clear To Send */ + Dsr = 0x20, /* Data Set Ready */ + Ri = 0x40, /* Ring Indicator */ + Dcd = 0x80, /* Data Set Ready */ +}; + +typedef struct Ctlr { + int io; + int irq; + int tbdf; + int iena; + + uchar sticky[8]; + + Lock; + int hasfifo; + int checkfifo; + int fena; +} Ctlr; + +extern PhysUart i8250physuart; + +static Ctlr i8250ctlr[2] = { +{ .io = Uart0, + .irq = Uart0IRQ, + .tbdf = BUSUNKNOWN, }, + +{ .io = Uart1, + .irq = Uart1IRQ, + .tbdf = BUSUNKNOWN, }, +}; + +static Uart i8250uart[2] = { +{ .regs = &i8250ctlr[0], + .name = "COM1", + .freq = UartFREQ, + .phys = &i8250physuart, + .special= 0, + .next = &i8250uart[1], }, + +{ .regs = &i8250ctlr[1], + .name = "COM2", + .freq = UartFREQ, + .phys = &i8250physuart, + .special= 0, + .next = nil, }, +}; + +#define csr8r(c, r) inb((c)->io+(r)) +#define csr8w(c, r, v) outb((c)->io+(r), (c)->sticky[(r)]|(v)) + +static long +i8250status(Uart* uart, void* buf, long n, long offset) +{ + char *p; + Ctlr *ctlr; + uchar ier, lcr, mcr, msr; + + ctlr = uart->regs; + p = malloc(READSTR); + mcr = ctlr->sticky[Mcr]; + msr = csr8r(ctlr, Msr); + ier = ctlr->sticky[Ier]; + lcr = ctlr->sticky[Lcr]; + snprint(p, READSTR, + "b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n" + "dev(%d) type(%d) framing(%d) overruns(%d) " + "berr(%d) serr(%d)%s%s%s%s\n", + + uart->baud, + uart->hup_dcd, + (msr & Dsr) != 0, + uart->hup_dsr, + (lcr & WlsMASK) + 5, + (ier & Ems) != 0, + (lcr & Pen) ? ((lcr & Eps) ? 'e': 'o'): 'n', + (mcr & Rts) != 0, + (lcr & Stb) ? 2: 1, + ctlr->fena, + + uart->dev, + uart->type, + uart->ferr, + uart->oerr, + uart->berr, + uart->serr, + (msr & Cts) ? " cts": "", + (msr & Dsr) ? " dsr": "", + (msr & Dcd) ? " dcd": "", + (msr & Ri) ? " ring": "" + ); + n = readstr(offset, buf, n, p); + free(p); + + return n; +} + +static void +i8250fifo(Uart* uart, int level) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + if(ctlr->hasfifo == 0) + return; + + /* + * Changing the FIFOena bit in Fcr flushes data + * from both receive and transmit FIFOs; there's + * no easy way to guarantee not losing data on + * the receive side, but it's possible to wait until + * the transmitter is really empty. + */ + ilock(ctlr); + while(!(csr8r(ctlr, Lsr) & Temt)) + ; + + /* + * Set the trigger level, default is the max. + * value. + * Some UARTs require FIFOena to be set before + * other bits can take effect, so set it twice. + */ + ctlr->fena = level; + switch(level){ + case 0: + break; + case 1: + level = FIFO1|FIFOena; + break; + case 4: + level = FIFO4|FIFOena; + break; + case 8: + level = FIFO8|FIFOena; + break; + default: + level = FIFO14|FIFOena; + break; + } + csr8w(ctlr, Fcr, level); + csr8w(ctlr, Fcr, level); + iunlock(ctlr); +} + +static void +i8250dtr(Uart* uart, int on) +{ + Ctlr *ctlr; + + /* + * Toggle DTR. + */ + ctlr = uart->regs; + if(on) + ctlr->sticky[Mcr] |= Dtr; + else + ctlr->sticky[Mcr] &= ~Dtr; + csr8w(ctlr, Mcr, 0); +} + +static void +i8250rts(Uart* uart, int on) +{ + Ctlr *ctlr; + + /* + * Toggle RTS. + */ + ctlr = uart->regs; + if(on) + ctlr->sticky[Mcr] |= Rts; + else + ctlr->sticky[Mcr] &= ~Rts; + csr8w(ctlr, Mcr, 0); +} + +static void +i8250modemctl(Uart* uart, int on) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + ilock(&uart->tlock); + if(on){ + ctlr->sticky[Ier] |= Ems; + csr8w(ctlr, Ier, ctlr->sticky[Ier]); + uart->modem = 1; + uart->cts = csr8r(ctlr, Msr) & Cts; + } + else{ + ctlr->sticky[Ier] &= ~Ems; + csr8w(ctlr, Ier, ctlr->sticky[Ier]); + uart->modem = 0; + uart->cts = 1; + } + iunlock(&uart->tlock); + + /* modem needs fifo */ + (*uart->phys->fifo)(uart, on); +} + +static int +i8250parity(Uart* uart, int parity) +{ + int lcr; + Ctlr *ctlr; + + ctlr = uart->regs; + lcr = ctlr->sticky[Lcr] & ~(Eps|Pen); + + switch(parity){ + case 'e': + lcr |= Eps|Pen; + break; + case 'o': + lcr |= Pen; + break; + case 'n': + break; + default: + return -1; + } + ctlr->sticky[Lcr] = lcr; + csr8w(ctlr, Lcr, 0); + + uart->parity = parity; + + return 0; +} + +static int +i8250stop(Uart* uart, int stop) +{ + int lcr; + Ctlr *ctlr; + + ctlr = uart->regs; + lcr = ctlr->sticky[Lcr] & ~Stb; + + switch(stop){ + case 1: + break; + case 2: + lcr |= Stb; + break; + default: + return -1; + } + ctlr->sticky[Lcr] = lcr; + csr8w(ctlr, Lcr, 0); + + uart->stop = stop; + + return 0; +} + +static int +i8250bits(Uart* uart, int bits) +{ + int lcr; + Ctlr *ctlr; + + ctlr = uart->regs; + lcr = ctlr->sticky[Lcr] & ~WlsMASK; + + switch(bits){ + case 5: + lcr |= Wls5; + break; + case 6: + lcr |= Wls6; + break; + case 7: + lcr |= Wls7; + break; + case 8: + lcr |= Wls8; + break; + default: + return -1; + } + ctlr->sticky[Lcr] = lcr; + csr8w(ctlr, Lcr, 0); + + uart->bits = bits; + + return 0; +} + +static int +i8250baud(Uart* uart, int baud) +{ + ulong bgc; + Ctlr *ctlr; + + /* + * Set the Baud rate by calculating and setting the Baud rate + * Generator Constant. This will work with fairly non-standard + * Baud rates. + */ + if(uart->freq == 0 || baud <= 0) + return -1; + bgc = (uart->freq+8*baud-1)/(16*baud); + + ctlr = uart->regs; + csr8w(ctlr, Lcr, Dlab); + outb(ctlr->io+Dlm, bgc>>8); + outb(ctlr->io+Dll, bgc); + csr8w(ctlr, Lcr, 0); + + uart->baud = baud; + + return 0; +} + +static void +i8250break(Uart* uart, int ms) +{ + Ctlr *ctlr; + + /* + * Send a break. + */ + if(ms <= 0) + ms = 200; + + ctlr = uart->regs; + csr8w(ctlr, Lcr, Brk); + tsleep(&up->sleep, return0, 0, ms); + csr8w(ctlr, Lcr, 0); +} + +static void +i8250kick(Uart* uart) +{ + int i; + Ctlr *ctlr; + + if(uart->cts == 0 || uart->blocked) + return; + + /* + * 128 here is an arbitrary limit to make sure + * we don't stay in this loop too long. If the + * chip's output queue is longer than 128, too + * bad -- presotto + */ + ctlr = uart->regs; + for(i = 0; i < 128; i++){ + if(!(csr8r(ctlr, Lsr) & Thre)) + break; + if(uart->op >= uart->oe && uartstageoutput(uart) == 0) + break; + outb(ctlr->io+Thr, *(uart->op++)); + } +} + +static void +i8250interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Uart *uart; + int iir, lsr, old, r; + + uart = arg; + + ctlr = uart->regs; + for(iir = csr8r(ctlr, Iir); !(iir & Ip); iir = csr8r(ctlr, Iir)){ + switch(iir & IirMASK){ + case Ims: /* Ms interrupt */ + r = csr8r(ctlr, Msr); + if(r & Dcts){ + ilock(&uart->tlock); + old = uart->cts; + uart->cts = r & Cts; + if(old == 0 && uart->cts) + uart->ctsbackoff = 2; + iunlock(&uart->tlock); + } + if(r & Ddsr){ + old = r & Dsr; + if(uart->hup_dsr && uart->dsr && !old) + uart->dohup = 1; + uart->dsr = old; + } + if(r & Ddcd){ + old = r & Dcd; + if(uart->hup_dcd && uart->dcd && !old) + uart->dohup = 1; + uart->dcd = old; + } + break; + case Ithre: /* Thr Empty */ + uartkick(uart); + break; + case Irda: /* Received Data Available */ + case Irls: /* Receiver Line Status */ + case Ictoi: /* Character Time-out Indication */ + /* + * Consume any received data. + * If the received byte came in with a break, + * parity or framing error, throw it away; + * overrun is an indication that something has + * already been tossed. + */ + while((lsr = csr8r(ctlr, Lsr)) & Dr){ + if(lsr & (FIFOerr|Oe)) + uart->oerr++; + if(lsr & Pe) + uart->perr++; + if(lsr & Fe) + uart->ferr++; + r = csr8r(ctlr, Rbr); + if(!(lsr & (Bi|Fe|Pe))) + uartrecv(uart, r); + } + break; + + default: + iprint("weird uart interrupt 0x%2.2uX\n", iir); + break; + } + } +} + +static void +i8250disable(Uart* uart) +{ + Ctlr *ctlr; + + /* + * Turn off DTR and RTS, disable interrupts and fifos. + */ + (*uart->phys->dtr)(uart, 0); + (*uart->phys->rts)(uart, 0); + (*uart->phys->fifo)(uart, 0); + + ctlr = uart->regs; + ctlr->sticky[Ier] = 0; + csr8w(ctlr, Ier, ctlr->sticky[Ier]); + + if(ctlr->iena != 0){ + if(intrdisable(ctlr->irq, i8250interrupt, uart, ctlr->tbdf, uart->name) == 0) + ctlr->iena = 0; + } +} + +static void +i8250enable(Uart* uart, int ie) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + + /* + * Check if there is a FIFO. + * Changing the FIFOena bit in Fcr flushes data + * from both receive and transmit FIFOs; there's + * no easy way to guarantee not losing data on + * the receive side, but it's possible to wait until + * the transmitter is really empty. + * Also, reading the Iir outwith i8250interrupt() + * can be dangerous, but this should only happen + * once, before interrupts are enabled. + */ + ilock(ctlr); + if(!ctlr->checkfifo){ + /* + * Wait until the transmitter is really empty. + */ + while(!(csr8r(ctlr, Lsr) & Temt)) + ; + csr8w(ctlr, Fcr, FIFOena); + if(csr8r(ctlr, Iir) & Ifena) + ctlr->hasfifo = 1; + csr8w(ctlr, Fcr, 0); + ctlr->checkfifo = 1; + } + iunlock(ctlr); + + /* + * Enable interrupts and turn on DTR and RTS. + * Be careful if this is called to set up a polled serial line + * early on not to try to enable interrupts as interrupt- + * -enabling mechanisms might not be set up yet. + */ + if(ie){ + if(ctlr->iena == 0){ + intrenable(ctlr->irq, i8250interrupt, uart, ctlr->tbdf, uart->name); + ctlr->iena = 1; + } + ctlr->sticky[Ier] = Ethre|Erda; + ctlr->sticky[Mcr] |= Ie; + } + else{ + ctlr->sticky[Ier] = 0; + ctlr->sticky[Mcr] = 0; + } + csr8w(ctlr, Ier, ctlr->sticky[Ier]); + csr8w(ctlr, Mcr, ctlr->sticky[Mcr]); + + (*uart->phys->dtr)(uart, 1); + (*uart->phys->rts)(uart, 1); + + /* + * During startup, the i8259 interrupt controller is reset. + * This may result in a lost interrupt from the i8250 uart. + * The i8250 thinks the interrupt is still outstanding and does not + * generate any further interrupts. The workaround is to call the + * interrupt handler to clear any pending interrupt events. + * Note: this must be done after setting Ier. + */ + if(ie) + i8250interrupt(nil, uart); +} + +void* +i8250alloc(int io, int irq, int tbdf) +{ + Ctlr *ctlr; + + if((ctlr = malloc(sizeof(Ctlr))) != nil){ + ctlr->io = io; + ctlr->irq = irq; + ctlr->tbdf = tbdf; + } + + return ctlr; +} + +static Uart* +i8250pnp(void) +{ + return i8250uart; +} + +static int +i8250getc(Uart *uart) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + while(!(csr8r(ctlr, Lsr)&Dr)) + delay(1); + return csr8r(ctlr, Rbr); +} + +static void +i8250putc(Uart *uart, int c) +{ + int i; + Ctlr *ctlr; + + ctlr = uart->regs; + for(i = 0; !(csr8r(ctlr, Lsr)&Thre) && i < 128; i++) + delay(1); + outb(ctlr->io+Thr, c); + for(i = 0; !(csr8r(ctlr, Lsr)&Thre) && i < 128; i++) + delay(1); +} + +PhysUart i8250physuart = { + .name = "i8250", + .pnp = i8250pnp, + .enable = i8250enable, + .disable = i8250disable, + .kick = i8250kick, + .dobreak = i8250break, + .baud = i8250baud, + .bits = i8250bits, + .stop = i8250stop, + .parity = i8250parity, + .modemctl = i8250modemctl, + .rts = i8250rts, + .dtr = i8250dtr, + .status = i8250status, + .fifo = i8250fifo, + .getc = i8250getc, + .putc = i8250putc, +}; + +void +i8250console(void) +{ + Uart *uart; + int n; + char *cmd, *p; + + if((p = getconf("console")) == nil) + return; + n = strtoul(p, &cmd, 0); + if(p == cmd) + return; + switch(n){ + default: + return; + case 0: + uart = &i8250uart[0]; + break; + case 1: + uart = &i8250uart[1]; + break; + } + + (*uart->phys->enable)(uart, 0); + uartctl(uart, "b9600 l8 pn s1"); + if(*cmd != '\0') + uartctl(uart, cmd); + + consuart = uart; + uart->console = 1; +} + +void +i8250mouse(char* which, int (*putc)(Queue*, int), int setb1200) +{ + char *p; + int port; + + port = strtol(which, &p, 0); + if(p == which || port < 0 || port > 1) + error(Ebadarg); + uartmouse(&i8250uart[port], putc, setb1200); +} + +void +i8250setmouseputc(char* which, int (*putc)(Queue*, int)) +{ + char *p; + int port; + + port = strtol(which, &p, 0); + if(p == which || port < 0 || port > 1) + error(Ebadarg); + uartsetmouseputc(&i8250uart[port], putc); + +} diff --git a/os/pc/uartisa.c b/os/pc/uartisa.c new file mode 100644 index 00000000..de4a8dcc --- /dev/null +++ b/os/pc/uartisa.c @@ -0,0 +1,98 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/uart.h" + +extern PhysUart i8250physuart; +extern PhysUart isaphysuart; +extern void* i8250alloc(int, int, int); + +static Uart* +uartisa(int ctlrno, ISAConf* isa) +{ + int io; + void *ctlr; + Uart *uart; + char buf[64]; + + io = isa->port; + snprint(buf, sizeof(buf), "%s%d", isaphysuart.name, ctlrno); + if(ioalloc(io, 8, 0, buf) < 0){ + print("uartisa: I/O 0x%uX in use\n", io); + return nil; + } + + uart = malloc(sizeof(Uart)); + ctlr = i8250alloc(io, isa->irq, BUSUNKNOWN); + if(ctlr == nil){ + iofree(io); + free(uart); + return nil; + } + + uart->regs = ctlr; + snprint(buf, sizeof(buf), "COM%d", ctlrno+1); + kstrdup(&uart->name, buf); + uart->freq = isa->freq; + uart->phys = &i8250physuart; + + return uart; +} + +static Uart* +uartisapnp(void) +{ + int ctlrno; + ISAConf isa; + Uart *head, *tail, *uart; + + /* + * Look for up to 4 discrete UARTs on the ISA bus. + * All suitable devices are configured to simply point + * to the generic i8250 driver. + */ + head = tail = nil; + for(ctlrno = 2; ctlrno < 6; ctlrno++){ + memset(&isa, 0, sizeof(isa)); + if(!isaconfig("uart", ctlrno, &isa)) + continue; + if(strcmp(isa.type, "isa") != 0) + continue; + if(isa.port == 0 || isa.irq == 0) + continue; + if(isa.freq == 0) + isa.freq = 1843200; + uart = uartisa(ctlrno, &isa); + if(uart == nil) + continue; + if(head != nil) + tail->next = uart; + else + head = uart; + tail = uart; + } + + return head; +} + +PhysUart isaphysuart = { + .name = "UartISA", + .pnp = uartisapnp, + .enable = nil, + .disable = nil, + .kick = nil, + .dobreak = nil, + .baud = nil, + .bits = nil, + .stop = nil, + .parity = nil, + .modemctl = nil, + .rts = nil, + .dtr = nil, + .status = nil, + .fifo = nil, +}; diff --git a/os/pc/uartpci.c b/os/pc/uartpci.c new file mode 100644 index 00000000..e84592b0 --- /dev/null +++ b/os/pc/uartpci.c @@ -0,0 +1,137 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/uart.h" + +extern PhysUart i8250physuart; +extern PhysUart pciphysuart; +extern void* i8250alloc(int, int, int); + +static Uart* +uartpci(int ctlrno, Pcidev* p, int barno, int n, int freq, char* name) +{ + int i, io; + void *ctlr; + char buf[64]; + Uart *head, *uart; + + io = p->mem[barno].bar & ~0x01; + snprint(buf, sizeof(buf), "%s%d", pciphysuart.name, ctlrno); + if(ioalloc(io, p->mem[barno].size, 0, buf) < 0){ + print("uartpci: I/O 0x%uX in use\n", io); + return nil; + } + + head = uart = malloc(sizeof(Uart)*n); + + for(i = 0; i < n; i++){ + ctlr = i8250alloc(io, p->intl, p->tbdf); + io += 8; + if(ctlr == nil) + continue; + + uart->regs = ctlr; + snprint(buf, sizeof(buf), "%s.%8.8uX", name, p->tbdf); + kstrdup(&uart->name, buf); + uart->freq = freq; + uart->phys = &i8250physuart; + if(uart != head) + (uart-1)->next = uart; + uart++; + } + + return head; +} + +static Uart* +uartpcipnp(void) +{ + Pcidev *p; + char *name; + int ctlrno, n, subid; + Uart *head, *tail, *uart; + + /* + * Loop through all PCI devices looking for simple serial + * controllers (ccrb == 0x07) and configure the ones which + * are familiar. All suitable devices are configured to + * simply point to the generic i8250 driver. + */ + head = tail = nil; + ctlrno = 0; + for(p = pcimatch(nil, 0, 0); p != nil; p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x07 || p->ccru != 0) + continue; + + switch((p->did<<16)|p->vid){ + default: + continue; + case (0x9050<<16)|0x10B5: /* Perle PCI-Fast4 series */ + case (0x9030<<16)|0x10B5: /* Perle Ultraport series */ + /* + * These devices consists of a PLX bridge (the above + * PCI VID+DID) behind which are some 16C654 UARTs. + * Must check the subsystem VID and DID for correct + * match. + */ + subid = pcicfgr16(p, PciSVID); + subid |= pcicfgr16(p, PciSID)<<16; + switch(subid){ + default: + continue; + case (0x0011<<16)|0x12E0: /* Perle PCI-Fast16 */ + n = 16; + name = "PCI-Fast16"; + break; + case (0x0021<<16)|0x12E0: /* Perle PCI-Fast8 */ + n = 8; + name = "PCI-Fast8"; + break; + case (0x0031<<16)|0x12E0: /* Perle PCI-Fast4 */ + n = 4; + name = "PCI-Fast4"; + break; + case (0x0021<<16)|0x155F: /* Perle Ultraport8 */ + n = 8; + name = "Ultraport8"; /* 16C754 UARTs */ + break; + } + uart = uartpci(ctlrno, p, 2, n, 7372800, name); + if(uart == nil) + continue; + break; + } + + if(head != nil) + tail->next = uart; + else + head = uart; + for(tail = uart; tail->next != nil; tail = tail->next) + ; + ctlrno++; + } + + return head; +} + +PhysUart pciphysuart = { + .name = "UartPCI", + .pnp = uartpcipnp, + .enable = nil, + .disable = nil, + .kick = nil, + .dobreak = nil, + .baud = nil, + .bits = nil, + .stop = nil, + .parity = nil, + .modemctl = nil, + .rts = nil, + .dtr = nil, + .status = nil, + .fifo = nil, +}; diff --git a/os/pc/usb.h b/os/pc/usb.h new file mode 100644 index 00000000..32da4d51 --- /dev/null +++ b/os/pc/usb.h @@ -0,0 +1,160 @@ +typedef struct Ctlr Ctlr; +typedef struct Endpt Endpt; +typedef struct Udev Udev; +typedef struct Usbhost Usbhost; + +enum +{ + MaxUsb = 10, /* max number of USB Host Controller Interfaces (Usbhost*) */ + MaxUsbDev = 32, /* max number of attached USB devices, including root hub (Udev*) */ + + /* + * USB packet definitions... + */ + TokIN = 0x69, + TokOUT = 0xE1, + TokSETUP = 0x2D, + + /* request type */ + RH2D = 0<<7, + RD2H = 1<<7, + Rstandard = 0<<5, + Rclass = 1<<5, + Rvendor = 2<<5, + Rdevice = 0, + Rinterface = 1, + Rendpt = 2, + Rother = 3, +}; + +#define Class(csp) ((csp)&0xff) +#define Subclass(csp) (((csp)>>8)&0xff) +#define Proto(csp) (((csp)>>16)&0xff) +#define CSP(c, s, p) ((c) | ((s)<<8) | ((p)<<16)) + +/* + * device endpoint + */ +struct Endpt +{ + Ref; + Lock; + int x; /* index in Udev.ep */ + int id; /* hardware endpoint address */ + int maxpkt; /* maximum packet size (from endpoint descriptor) */ + int data01; /* 0=DATA0, 1=DATA1 */ + uchar eof; + ulong csp; + uchar mode; /* OREAD, OWRITE, ORDWR */ + uchar nbuf; /* number of buffers allowed */ + uchar iso; + uchar debug; + uchar active; /* listed for examination by interrupts */ + int setin; + /* ISO related: */ + int hz; + int remain; /* for packet size calculations */ + int samplesz; + int sched; /* schedule index; -1 if undefined or aperiodic */ + int pollms; /* polling interval in msec */ + int psize; /* (remaining) size of this packet */ + int off; /* offset into packet */ + /* Real-time iso stuff */ + ulong foffset; /* file offset (to detect seeks) */ + ulong poffset; /* offset of next packet to be queued */ + ulong toffset; /* offset associated with time */ + vlong time; /* timeassociated with offset */ + int buffered; /* bytes captured but unread, or written but unsent */ + /* end ISO stuff */ + + Udev* dev; /* owning device */ + + ulong nbytes; + ulong nblocks; + + void *private; + + // all the rest could (should?) move to the driver private structure; except perhaps err + QLock rlock; + Rendez rr; + Queue* rq; + QLock wlock; + Rendez wr; + Queue* wq; + + int ntd; + char* err; // needs to be global for unstall; fix? + + Endpt* activef; /* active endpoint list */ +}; + +/* device parameters */ +enum +{ + /* Udev.state */ + Disabled = 0, + Attached, + Enabled, + Assigned, + Configured, + + /* Udev.class */ + Noclass = 0, + Hubclass = 9, +}; + +/* + * active USB device + */ +struct Udev +{ + Ref; + Lock; + Usbhost *uh; + int x; /* index in usbdev[] */ + int busy; + int state; + int id; + uchar port; /* port number on connecting hub */ + ulong csp; + ushort vid; /* vendor id */ + ushort did; /* product id */ + int ls; + int npt; + Endpt* ep[16]; /* active end points */ + Udev* ports; /* active ports, if hub */ + Udev* next; /* next device on this hub */ +}; + +/* + * One of these per active Host Controller Interface (HCI) + */ +struct Usbhost +{ + ISAConf; /* hardware info */ + int tbdf; /* type+busno+devno+funcno */ + + QLock; /* protects namespace state */ + int idgen; /* version number to distinguish new connections */ + Udev* dev[MaxUsbDev]; /* device endpoints managed by this HCI */ + + void (*init)(Usbhost*); + void (*interrupt)(Ureg*, void*); + + void (*portinfo)(Usbhost*, char*, char*); + void (*portreset)(Usbhost*, int); + void (*portenable)(Usbhost*, int, int); + + void (*epalloc)(Usbhost*, Endpt*); + void (*epfree)(Usbhost*, Endpt*); + void (*epopen)(Usbhost*, Endpt*); + void (*epclose)(Usbhost*, Endpt*); + void (*epmode)(Usbhost*, Endpt*); + + long (*read)(Usbhost*, Endpt*, void*, long, vlong); + long (*write)(Usbhost*, Endpt*, void*, long, vlong, int); + + void *ctlr; +}; + +extern void addusbtype(char*, int(*)(Usbhost*)); diff --git a/os/pc/usbuhci.c b/os/pc/usbuhci.c new file mode 100644 index 00000000..4d65726c --- /dev/null +++ b/os/pc/usbuhci.c @@ -0,0 +1,1538 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "usb.h" + +#define XPRINT if(debug)print + +static int Chatty = 0; +static int debug = 0; + +static char Estalled[] = "usb endpoint stalled"; + +/* + * UHCI interface registers and bits + */ +enum +{ + /* i/o space */ + Cmd = 0, + Status = 2, + Usbintr = 4, + Frnum = 6, + Flbaseadd = 8, + SOFMod = 0xC, + Portsc0 = 0x10, + Portsc1 = 0x12, + + /* port status */ + Suspend = 1<<12, + PortReset = 1<<9, + SlowDevice = 1<<8, + ResumeDetect = 1<<6, + PortChange = 1<<3, /* write 1 to clear */ + PortEnable = 1<<2, + StatusChange = 1<<1, /* write 1 to clear */ + DevicePresent = 1<<0, + + NFRAME = 1024, + FRAMESIZE= NFRAME*sizeof(ulong), /* fixed by hardware; aligned to same */ + + Vf = 1<<2, /* TD only */ + IsQH = 1<<1, + Terminate = 1<<0, + + /* TD.status */ + SPD = 1<<29, + ErrLimit0 = 0<<27, + ErrLimit1 = 1<<27, + ErrLimit2 = 2<<27, + ErrLimit3 = 3<<27, + LowSpeed = 1<<26, + IsoSelect = 1<<25, + IOC = 1<<24, + Active = 1<<23, + Stalled = 1<<22, + DataBufferErr = 1<<21, + Babbling = 1<<20, + NAKed = 1<<19, + CRCorTimeout = 1<<18, + BitstuffErr = 1<<17, + AnyError = (Stalled | DataBufferErr | Babbling | NAKed | CRCorTimeout | BitstuffErr), + + /* TD.dev */ + IsDATA1 = 1<<19, + + /* TD.flags (software) */ + CancelTD= 1<<0, + IsoClean= 1<<2, +}; + +static struct +{ + int bit; + char *name; +} +portstatus[] = +{ + { Suspend, "suspend", }, + { PortReset, "reset", }, + { SlowDevice, "lowspeed", }, + { ResumeDetect, "resume", }, + { PortChange, "portchange", }, + { PortEnable, "enable", }, + { StatusChange, "statuschange", }, + { DevicePresent, "present", }, +}; + +typedef struct Ctlr Ctlr; +typedef struct Endptx Endptx; +typedef struct QH QH; +typedef struct TD TD; + +/* + * software structures + */ +struct Ctlr +{ + Lock; /* protects state shared with interrupt (eg, free list) */ + Ctlr* next; + Pcidev* pcidev; + int active; + + int io; + ulong* frames; /* frame list */ + ulong* frameld; /* real time load on each of the frame list entries */ + QLock resetl; /* lock controller during USB reset */ + + TD* tdpool; + TD* freetd; + QH* qhpool; + QH* freeqh; + + QH* ctlq; /* queue for control i/o */ + QH* bwsop; /* empty bandwidth sop (to PIIX4 errata specifications) */ + QH* bulkq; /* queue for bulk i/o (points back to bandwidth sop) */ + QH* recvq; /* receive queues for bulk i/o */ + + Udev* ports[2]; + + struct { + Lock; + Endpt* f; + } activends; + + long usbints; /* debugging */ + long framenumber; + long frameptr; + long usbbogus; +}; + +#define IN(x) ins(ctlr->io+(x)) +#define OUT(x, v) outs(ctlr->io+(x), (v)) + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +struct Endptx +{ + QH* epq; /* queue of TDs for this endpoint */ + + /* ISO related: */ + void* tdalloc; + void* bpalloc; + uchar* bp0; /* first block in array */ + TD * td0; /* first td in array */ + TD * etd; /* pointer into circular list of TDs for isochronous ept */ + TD * xtd; /* next td to be cleaned */ +}; + +/* + * UHCI hardware structures, aligned on 16-byte boundary + */ +struct TD +{ + ulong link; + ulong status; /* controller r/w */ + ulong dev; + ulong buffer; + + /* software */ + ulong flags; + union{ + Block* bp; /* non-iso */ + ulong offset; /* iso */ + }; + Endpt* ep; + TD* next; +}; +#define TFOL(p) ((TD*)KADDR((ulong)(p) & ~(0xF|PCIWINDOW))) + +struct QH +{ + ulong head; + ulong entries; /* address of next TD or QH to process (updated by controller) */ + + /* software */ + QH* hlink; + TD* first; + QH* next; /* free list */ + TD* last; + ulong _d1; /* fillers */ + ulong _d2; +}; +#define QFOL(p) ((QH*)KADDR((ulong)(p) & ~(0xF|PCIWINDOW))) + +static TD * +alloctd(Ctlr *ctlr) +{ + TD *t; + + ilock(ctlr); + t = ctlr->freetd; + if(t == nil) + panic("alloctd"); /* TO DO */ + ctlr->freetd = t->next; + t->next = nil; + iunlock(ctlr); + t->ep = nil; + t->bp = nil; + t->status = 0; + t->link = Terminate; + t->buffer = 0; + t->flags = 0; + return t; +} + +static void +freetd(Ctlr *ctlr, TD *t) +{ + t->ep = nil; + if(t->bp) + freeb(t->bp); + t->bp = nil; + ilock(ctlr); + t->buffer = 0xdeadbeef; + t->next = ctlr->freetd; + ctlr->freetd = t; + iunlock(ctlr); +} + +static void +dumpdata(Block *b, int n) +{ + int i; + + XPRINT("\tb %8.8lux[%d]: ", (ulong)b->rp, n); + if(n > 16) + n = 16; + for(i=0; irp[i]); + XPRINT("\n"); +} + +static void +dumptd(TD *t, int follow) +{ + int i, n; + char buf[20], *s; + TD *t0; + + t0 = t; + while(t){ + i = t->dev & 0xFF; + if(i == TokOUT || i == TokSETUP) + n = ((t->dev>>21) + 1) & 0x7FF; + else if((t->status & Active) == 0) + n = (t->status + 1) & 0x7FF; + else + n = 0; + s = buf; + if(t->status & Active) + *s++ = 'A'; + if(t->status & Stalled) + *s++ = 'S'; + if(t->status & DataBufferErr) + *s++ = 'D'; + if(t->status & Babbling) + *s++ = 'B'; + if(t->status & NAKed) + *s++ = 'N'; + if(t->status & CRCorTimeout) + *s++ = 'T'; + if(t->status & BitstuffErr) + *s++ = 'b'; + if(t->status & LowSpeed) + *s++ = 'L'; + *s = 0; + XPRINT("td %8.8lux: ", t); + XPRINT("l=%8.8lux s=%8.8lux d=%8.8lux b=%8.8lux %8.8lux f=%8.8lux\n", + t->link, t->status, t->dev, t->buffer, t->bp?(ulong)t->bp->rp:0, t->flags); + XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n", + buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1); + if(debug && t->bp && (t->flags & CancelTD) == 0) + dumpdata(t->bp, n); + if(!follow || t->link & Terminate || t->link & IsQH) + break; + t = TFOL(t->link); + if(t == t0) + break; /* looped */ + } +} + +static TD * +alloctde(Ctlr *ctlr, Endpt *e, int pid, int n) +{ + TD *t; + int tog, id; + + t = alloctd(ctlr); + id = (e->x<<7)|(e->dev->x&0x7F); + tog = 0; + if(e->data01 && pid != TokSETUP) + tog = IsDATA1; + t->ep = e; + t->status = ErrLimit3 | Active | IOC; /* or put IOC only on last? */ + if(e->dev->ls) + t->status |= LowSpeed; + t->dev = ((n-1)<<21) | ((id&0x7FF)<<8) | pid | tog; + return t; +} + +static QH * +allocqh(Ctlr *ctlr) +{ + QH *qh; + + ilock(ctlr); + qh = ctlr->freeqh; + if(qh == nil) + panic("allocqh"); /* TO DO */ + ctlr->freeqh = qh->next; + qh->next = nil; + iunlock(ctlr); + qh->head = Terminate; + qh->entries = Terminate; + qh->hlink = nil; + qh->first = nil; + qh->last = nil; + return qh; +} + +static void +freeqh(Ctlr *ctlr, QH *qh) +{ + ilock(ctlr); + qh->next = ctlr->freeqh; + ctlr->freeqh = qh; + iunlock(ctlr); +} + +static void +dumpqh(QH *q) +{ + int i; + QH *q0; + + q0 = q; + for(i = 0; q != nil && i < 10; i++){ + XPRINT("qh %8.8lux: %8.8lux %8.8lux\n", q, q->head, q->entries); + if((q->entries & Terminate) == 0) + dumptd(TFOL(q->entries), 1); + if(q->head & Terminate) + break; + if((q->head & IsQH) == 0){ + XPRINT("head:"); + dumptd(TFOL(q->head), 1); + break; + } + q = QFOL(q->head); + if(q == q0) + break; /* looped */ + } +} + +static void +queuetd(Ctlr *ctlr, QH *q, TD *t, int vf, char *why) +{ + TD *lt; + + for(lt = t; lt->next != nil; lt = lt->next) + lt->link = PCIWADDR(lt->next) | vf; + lt->link = Terminate; + ilock(ctlr); + XPRINT("queuetd %s: t=%p lt=%p q=%p first=%p last=%p entries=%.8lux\n", + why, t, lt, q, q->first, q->last, q->entries); + if(q->first != nil){ + q->last->link = PCIWADDR(t) | vf; + q->last->next = t; + }else{ + q->first = t; + q->entries = PCIWADDR(t); + } + q->last = lt; + XPRINT(" t=%p q=%p first=%p last=%p entries=%.8lux\n", + t, q, q->first, q->last, q->entries); + dumpqh(q); + iunlock(ctlr); +} + +static void +cleantd(Ctlr *ctlr, TD *t, int discard) +{ + Block *b; + int n, err; + + XPRINT("cleanTD: %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); + if(t->ep != nil && t->ep->debug) + dumptd(t, 0); + if(t->status & Active) + panic("cleantd Active"); + err = t->status & (AnyError&~NAKed); + /* TO DO: on t->status&AnyError, q->entries will not have advanced */ + if (err) { + XPRINT("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); +// print("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); + } + switch(t->dev&0xFF){ + case TokIN: + if(discard || (t->flags & CancelTD) || t->ep == nil || t->ep->x!=0&&err){ + if(t->ep != nil){ + if(err != 0) + t->ep->err = err==Stalled? Estalled: Eio; + wakeup(&t->ep->rr); /* in case anyone cares */ + } + break; + } + b = t->bp; + n = (t->status + 1) & 0x7FF; + if(n > b->lim - b->wp) + n = 0; + b->wp += n; + if(Chatty) + dumpdata(b, n); + t->bp = nil; + t->ep->nbytes += n; + t->ep->nblocks++; + qpass(t->ep->rq, b); /* TO DO: flow control */ + wakeup(&t->ep->rr); /* TO DO */ + break; + case TokSETUP: + XPRINT("cleanTD: TokSETUP %lux\n", &t->ep); + /* don't really need to wakeup: subsequent IN or OUT gives status */ + if(t->ep != nil) { + wakeup(&t->ep->wr); /* TO DO */ + XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr); + } + break; + case TokOUT: + /* TO DO: mark it done somewhere */ + XPRINT("cleanTD: TokOut %lux\n", &t->ep); + if(t->ep != nil){ + if(t->bp){ + n = BLEN(t->bp); + t->ep->nbytes += n; + t->ep->nblocks++; + } + if(t->ep->x!=0 && err != 0) + t->ep->err = err==Stalled? Estalled: Eio; + if(--t->ep->ntd < 0) + panic("cleantd ntd"); + wakeup(&t->ep->wr); /* TO DO */ + XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr); + } + break; + } + freetd(ctlr, t); +} + +static void +cleanq(Ctlr *ctlr, QH *q, int discard, int vf) +{ + TD *t, *tp; + + ilock(ctlr); + tp = nil; + for(t = q->first; t != nil;){ + XPRINT("cleanq: %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer, t->flags, t->next); + if(t->status & Active){ + if(t->status & NAKed){ + t->status = (t->status & ~NAKed) | IOC; /* ensure interrupt next frame */ + tp = t; + t = t->next; + continue; + } + if(t->flags & CancelTD){ + XPRINT("cancelTD: %8.8lux\n", (ulong)t); + t->status = (t->status & ~Active) | IOC; /* ensure interrupt next frame */ + tp = t; + t = t->next; + continue; + } + tp = t; + t = t->next; + continue; + } + t->status &= ~IOC; + if (tp == nil) { + q->first = t->next; + if(q->first != nil) + q->entries = PCIWADDR(q->first); + else + q->entries = Terminate; + } else { + tp->next = t->next; + if (t->next != nil) + tp->link = PCIWADDR(t->next) | vf; + else + tp->link = Terminate; + } + if (q->last == t) + q->last = tp; + iunlock(ctlr); + cleantd(ctlr, t, discard); + ilock(ctlr); + if (tp) + t = tp->next; + else + t = q->first; + XPRINT("t = %8.8lux\n", t); + dumpqh(q); + } + if(q->first && q->entries != PCIWADDR(q->first)){ + ctlr->usbbogus++; + q->entries = PCIWADDR(q->first); + } + iunlock(ctlr); +} + +static void +canceltds(Ctlr *ctlr, QH *q, Endpt *e) +{ + TD *t; + + if(q != nil){ + ilock(ctlr); + for(t = q->first; t != nil; t = t->next) + if(t->ep == e) + t->flags |= CancelTD; + iunlock(ctlr); + XPRINT("cancel:\n"); + dumpqh(q); + } +} + +static void +eptcancel(Ctlr *ctlr, Endpt *e) +{ + Endptx *x; + + if(e == nil) + return; + x = e->private; + canceltds(ctlr, x->epq, e); + canceltds(ctlr, ctlr->ctlq, e); + canceltds(ctlr, ctlr->bulkq, e); +} + +static void +eptactivate(Ctlr *ctlr, Endpt *e) +{ + ilock(&ctlr->activends); + if(e->active == 0){ + XPRINT("activate 0x%p\n", e); + e->active = 1; + e->activef = ctlr->activends.f; + ctlr->activends.f = e; + } + iunlock(&ctlr->activends); +} + +static void +eptdeactivate(Ctlr *ctlr, Endpt *e) +{ + Endpt **l; + + /* could be O(1) but not worth it yet */ + ilock(&ctlr->activends); + if(e->active){ + e->active = 0; + XPRINT("deactivate 0x%p\n", e); + for(l = &ctlr->activends.f; *l != e; l = &(*l)->activef) + if(*l == nil){ + iunlock(&ctlr->activends); + panic("usb eptdeactivate"); + } + *l = e->activef; + } + iunlock(&ctlr->activends); +} + +static void +queueqh(Ctlr *ctlr, QH *qh) +{ + QH *q; + + // See if it's already queued + for (q = ctlr->recvq->next; q; q = q->hlink) + if (q == qh) + return; + if ((qh->hlink = ctlr->recvq->next) == nil) + qh->head = Terminate; + else + qh->head = PCIWADDR(ctlr->recvq->next) | IsQH; + ctlr->recvq->next = qh; + ctlr->recvq->entries = PCIWADDR(qh) | IsQH; +} + +static QH* +qxmit(Ctlr *ctlr, Endpt *e, Block *b, int pid) +{ + TD *t; + int n, vf; + QH *qh; + Endptx *x; + + x = e->private; + if(b != nil){ + n = BLEN(b); + t = alloctde(ctlr, e, pid, n); + t->bp = b; + t->buffer = PCIWADDR(b->rp); + }else + t = alloctde(ctlr, e, pid, 0); + ilock(ctlr); + e->ntd++; + iunlock(ctlr); + if(e->debug) pprint("QTD: %8.8lux n=%ld\n", t, b?BLEN(b): 0); + vf = 0; + if(e->x == 0){ + qh = ctlr->ctlq; + vf = 0; + }else if((qh = x->epq) == nil || e->mode != OWRITE){ + qh = ctlr->bulkq; + vf = Vf; + } + queuetd(ctlr, qh, t, vf, "qxmit"); + return qh; +} + +static QH* +qrcv(Ctlr *ctlr, Endpt *e) +{ + TD *t; + Block *b; + QH *qh; + int vf; + Endptx *x; + + x = e->private; + t = alloctde(ctlr, e, TokIN, e->maxpkt); + b = allocb(e->maxpkt); + t->bp = b; + t->buffer = PCIWADDR(b->wp); + vf = 0; + if(e->x == 0){ + qh = ctlr->ctlq; + }else if((qh = x->epq) == nil || e->mode != OREAD){ + qh = ctlr->bulkq; + vf = Vf; + } + queuetd(ctlr, qh, t, vf, "qrcv"); + return qh; +} + +static int +usbsched(Ctlr *ctlr, int pollms, ulong load) +{ + int i, d, q; + ulong best, worst; + + best = 1000000; + q = -1; + for (d = 0; d < pollms; d++){ + worst = 0; + for (i = d; i < NFRAME; i++){ + if (ctlr->frameld[i] + load > worst) + worst = ctlr->frameld[i] + load; + } + if (worst < best){ + best = worst; + q = d; + } + } + return q; +} + +static int +schedendpt(Ctlr *ctlr, Endpt *e) +{ + TD *td; + Endptx *x; + uchar *bp; + int i, id, ix, size, frnum; + + if(!e->iso || e->sched >= 0) + return 0; + + if (e->active){ + return -1; + } + e->off = 0; + e->sched = usbsched(ctlr, e->pollms, e->maxpkt); + if(e->sched < 0) + return -1; + + x = e->private; + if (x->tdalloc || x->bpalloc) + panic("usb: tdalloc/bpalloc"); + x->tdalloc = mallocz(0x10 + NFRAME*sizeof(TD), 1); + x->bpalloc = mallocz(0x10 + e->maxpkt*NFRAME/e->pollms, 1); + x->td0 = (TD*)(((ulong)x->tdalloc + 0xf) & ~0xf); + x->bp0 = (uchar *)(((ulong)x->bpalloc + 0xf) & ~0xf); + frnum = (IN(Frnum) + 1) & 0x3ff; + frnum = (frnum & ~(e->pollms - 1)) + e->sched; + x->xtd = &x->td0[(frnum+8)&0x3ff]; /* Next td to finish */ + x->etd = nil; + e->remain = 0; + e->nbytes = 0; + td = x->td0; + for(i = e->sched; i < NFRAME; i += e->pollms){ + bp = x->bp0 + e->maxpkt*i/e->pollms; + td->buffer = PCIWADDR(bp); + td->ep = e; + td->next = &td[1]; + ctlr->frameld[i] += e->maxpkt; + td++; + } + td[-1].next = x->td0; + for(i = e->sched; i < NFRAME; i += e->pollms){ + ix = (frnum+i) & 0x3ff; + td = &x->td0[ix]; + + id = (e->x<<7)|(e->dev->x&0x7F); + if (e->mode == OREAD) + /* enable receive on this entry */ + td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | TokIN; + else{ + size = (e->hz + e->remain)*e->pollms/1000; + e->remain = (e->hz + e->remain)*e->pollms%1000; + size *= e->samplesz; + td->dev = ((size-1)<<21) | ((id&0x7FF)<<8) | TokOUT; + } + td->status = ErrLimit1 | Active | IsoSelect | IOC; + td->link = ctlr->frames[ix]; + td->flags |= IsoClean; + ctlr->frames[ix] = PCIWADDR(td); + } + return 0; +} + +static void +unschedendpt(Ctlr *ctlr, Endpt *e) +{ + int q; + TD *td; + Endptx *x; + ulong *addr; + + if(!e->iso || e->sched < 0) + return; + + x = e->private; + if (x->tdalloc == nil) + panic("tdalloc"); + for (q = e->sched; q < NFRAME; q += e->pollms){ + td = x->td0++; + addr = &ctlr->frames[q]; + while(*addr != PADDR(td)) { + if(*addr & IsQH) + panic("usb: TD expected"); + addr = &TFOL(*addr)->link; + } + *addr = td->link; + ctlr->frameld[q] -= e->maxpkt; + } + free(x->tdalloc); + free(x->bpalloc); + x->tdalloc = nil; + x->bpalloc = nil; + x->etd = nil; + x->td0 = nil; + e->sched = -1; +} + +static void +epalloc(Usbhost *uh, Endpt *e) +{ + Endptx *x; + + x = malloc(sizeof(Endptx)); + e->private = x; + x->epq = allocqh(uh->ctlr); + if(x->epq == nil) + panic("devendptx"); +} + +static void +epfree(Usbhost *uh, Endpt *e) +{ + Ctlr *ctlr; + Endptx *x; + + ctlr = uh->ctlr; + x = e->private; + if(x->epq != nil) + freeqh(ctlr, x->epq); +} + +static void +epopen(Usbhost *uh, Endpt *e) +{ + Ctlr *ctlr; + + ctlr = uh->ctlr; + if(e->iso && e->active) + error("already open"); + if(schedendpt(ctlr, e) < 0){ + if(e->active) + error("cannot schedule USB endpoint, active"); + else + error("cannot schedule USB endpoint"); + } + eptactivate(ctlr, e); +} + +static void +epclose(Usbhost *uh, Endpt *e) +{ + Ctlr *ctlr; + + ctlr = uh->ctlr; + eptdeactivate(ctlr, e); + unschedendpt(ctlr, e); +} + +static void +epmode(Usbhost *uh, Endpt *e) +{ + Ctlr *ctlr; + Endptx *x; + + ctlr = uh->ctlr; + x = e->private; + if(e->iso) { + if(x->epq != nil) { + freeqh(ctlr, x->epq); + x->epq = nil; + } + } + else { + /* Each bulk device gets a queue head hanging off the + * bulk queue head + */ + if(x->epq == nil) { + x->epq = allocqh(ctlr); + if(x->epq == nil) + panic("epbulk: allocqh"); + } + queueqh(ctlr, x->epq); + } +} + +static int ioport[] = {-1, Portsc0, Portsc1}; + +static void +portreset(Usbhost *uh, int port) +{ + int i, p; + Ctlr *ctlr; + + ctlr = uh->ctlr; + if(port != 1 && port != 2) + error(Ebadarg); + + /* should check that device not being configured on other port? */ + p = ioport[port]; + qlock(&ctlr->resetl); + if(waserror()){ + qunlock(&ctlr->resetl); + nexterror(); + } + XPRINT("r: %x\n", IN(p)); + ilock(ctlr); + OUT(p, PortReset); + delay(12); /* BUG */ + XPRINT("r2: %x\n", IN(p)); + OUT(p, IN(p) & ~PortReset); + XPRINT("r3: %x\n", IN(p)); + OUT(p, IN(p) | PortEnable); + microdelay(64); + for(i=0; i<1000 && (IN(p) & PortEnable) == 0; i++) + ; + XPRINT("r': %x %d\n", IN(p), i); + OUT(p, (IN(p) & ~PortReset)|PortEnable); + iunlock(ctlr); + poperror(); + qunlock(&ctlr->resetl); +} + +static void +portenable(Usbhost *uh, int port, int on) +{ + int w, p; + Ctlr *ctlr; + + ctlr = uh->ctlr; + if(port != 1 && port != 2) + error(Ebadarg); + + /* should check that device not being configured on other port? */ + p = ioport[port]; + qlock(&ctlr->resetl); + if(waserror()){ + qunlock(&ctlr->resetl); + nexterror(); + } + ilock(ctlr); + w = IN(p); + if(on) + w |= PortEnable; + else + w &= ~PortEnable; + OUT(p, w); + microdelay(64); + iunlock(ctlr); + XPRINT("e: %x\n", IN(p)); + poperror(); + qunlock(&ctlr->resetl); +} + +static void +portinfo(Usbhost *uh, char *s, char *se) +{ + int x, i, j; + Ctlr *ctlr; + + ctlr = uh->ctlr; + for(i = 1; i <= 2; i++) { + ilock(ctlr); + x = IN(ioport[i]); + if((x & (PortChange|StatusChange)) != 0) + OUT(ioport[i], x); + iunlock(ctlr); + s = seprint(s, se, "%d %ux", i, x); + for(j = 0; j < nelem(portstatus); j++) { + if((x & portstatus[j].bit) != 0) + s = seprint(s, se, " %s", portstatus[j].name); + } + s = seprint(s, se, "\n"); + } +} + +static void +cleaniso(Endpt *e, int frnum) +{ + TD *td; + int id, n, i; + Endptx *x; + uchar *bp; + + x = e->private; + td = x->xtd; + if (td->status & Active) + return; + id = (e->x<<7)|(e->dev->x&0x7F); + do { + if (td->status & AnyError) + XPRINT("usbisoerror 0x%lux\n", td->status); + n = (td->status + 1) & 0x3ff; + e->nbytes += n; + if ((td->flags & IsoClean) == 0) + e->nblocks++; + if (e->mode == OREAD){ + e->buffered += n; + e->poffset += (td->status + 1) & 0x3ff; + td->offset = e->poffset; + td->dev = ((e->maxpkt -1)<<21) | ((id&0x7FF)<<8) | TokIN; + e->toffset = td->offset; + }else{ + if ((td->flags & IsoClean) == 0){ + e->buffered -= n; + if (e->buffered < 0){ +// print("e->buffered %d?\n", e->buffered); + e->buffered = 0; + } + } + e->toffset = td->offset; + n = (e->hz + e->remain)*e->pollms/1000; + e->remain = (e->hz + e->remain)*e->pollms%1000; + n *= e->samplesz; + td->dev = ((n -1)<<21) | ((id&0x7FF)<<8) | TokOUT; + td->offset = e->poffset; + e->poffset += n; + } + td = td->next; + if (x->xtd == td){ + XPRINT("@"); + break; + } + } while ((td->status & Active) == 0); + e->time = todget(nil); + x->xtd = td; + for (n = 2; n < 4; n++){ + i = ((frnum + n)&0x3ff); + td = x->td0 + i; + bp = x->bp0 + e->maxpkt*i/e->pollms; + if (td->status & Active) + continue; + + if (e->mode == OWRITE){ + if (td == x->etd) { + XPRINT("*"); + memset(bp+e->off, 0, e->maxpkt-e->off); + if (e->off == 0) + td->flags |= IsoClean; + else + e->buffered += (((td->dev>>21) +1) & 0x3ff) - e->off; + x->etd = nil; + }else if ((td->flags & IsoClean) == 0){ + XPRINT("-"); + memset(bp, 0, e->maxpkt); + td->flags |= IsoClean; + } + } else { + /* Unread bytes are now lost */ + e->buffered -= (td->status + 1) & 0x3ff; + } + td->status = ErrLimit1 | Active | IsoSelect | IOC; + } + wakeup(&e->wr); +} + +static void +interrupt(Ureg*, void *a) +{ + QH *q; + Ctlr *ctlr; + Endpt *e; + Endptx *x; + int s, frnum; + Usbhost *uh; + + uh = a; + ctlr = uh->ctlr; + s = IN(Status); + ctlr->frameptr = inl(ctlr->io+Flbaseadd); + ctlr->framenumber = IN(Frnum) & 0x3ff; + OUT(Status, s); + if ((s & 0x1f) == 0) + return; + ctlr->usbints++; + frnum = IN(Frnum) & 0x3ff; + if (s & 0x1a) { + XPRINT("cmd #%x sofmod #%x\n", IN(Cmd), inb(ctlr->io+SOFMod)); + XPRINT("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1)); + } + + ilock(&ctlr->activends); + for(e = ctlr->activends.f; e != nil; e = e->activef) { + x = e->private; + if(!e->iso && x->epq != nil) { + XPRINT("cleanq(ctlr, x->epq, 0, 0)\n"); + cleanq(ctlr, x->epq, 0, 0); + } + if(e->iso) { + XPRINT("cleaniso(e)\n"); + cleaniso(e, frnum); + } + } + iunlock(&ctlr->activends); + XPRINT("cleanq(ctlr, ctlr->ctlq, 0, 0)\n"); + cleanq(ctlr, ctlr->ctlq, 0, 0); + XPRINT("cleanq(ctlr, ctlr->bulkq, 0, Vf)\n"); + cleanq(ctlr, ctlr->bulkq, 0, Vf); + XPRINT("clean recvq\n"); + for (q = ctlr->recvq->next; q; q = q->hlink) { + XPRINT("cleanq(ctlr, q, 0, Vf)\n"); + cleanq(ctlr, q, 0, Vf); + } +} + +static int +eptinput(void *arg) +{ + Endpt *e; + + e = arg; + return e->eof || e->err || qcanread(e->rq); +} + +static int +isoreadyx(Endptx *x) +{ + return x->etd == nil || (x->etd != x->xtd && (x->etd->status & Active) == 0); +} + +static int +isoready(void *arg) +{ + int ret; + Ctlr *ctlr; + Endpt *e; + Endptx *x; + + e = arg; + ctlr = e->dev->uh->ctlr; + x = e->private; + ilock(&ctlr->activends); + ret = isoreadyx(x); + iunlock(&ctlr->activends); + return ret; +} + +static long +isoio(Ctlr *ctlr, Endpt *e, void *a, long n, ulong offset, int w) +{ + TD *td; + Endptx *x; + int i, frnum; + uchar *p, *q, *bp; + volatile int isolock; + + x = e->private; + qlock(&e->rlock); + isolock = 0; + if(waserror()){ + if (isolock){ + isolock = 0; + iunlock(&ctlr->activends); + } + qunlock(&e->rlock); + eptcancel(ctlr, e); + nexterror(); + } + p = a; + if (offset != 0 && offset != e->foffset){ + iprint("offset %lud, foffset %lud\n", offset, e->foffset); + /* Seek to a specific position */ + frnum = (IN(Frnum) + 8) & 0x3ff; + td = x->td0 +frnum; + if (offset < td->offset) + error("ancient history"); + while (offset > e->toffset){ + tsleep(&e->wr, return0, 0, 500); + } + while (offset >= td->offset + ((w?(td->dev >> 21):td->status) + 1) & 0x7ff){ + td = td->next; + if (td == x->xtd) + iprint("trouble\n"); + } + ilock(&ctlr->activends); + isolock = 1; + e->off = td->offset - offset; + if (e->off >= e->maxpkt){ + iprint("I can't program: %d\n", e->off); + e->off = 0; + } + x->etd = td; + e->foffset = offset; + } + do { + if (isolock == 0){ + ilock(&ctlr->activends); + isolock = 1; + } + td = x->etd; + if (td == nil || e->off == 0){ + if (td == nil){ + XPRINT("0"); + if (w){ + frnum = (IN(Frnum) + 1) & 0x3ff; + td = x->td0 + frnum; + while(td->status & Active) + td = td->next; + }else{ + frnum = (IN(Frnum) - 4) & 0x3ff; + td = x->td0 + frnum; + while(td->next != x->xtd) + td = td->next; + } + x->etd = td; + e->off = 0; + }else{ + /* New td, make sure it's ready */ + while (isoreadyx(x) == 0){ + isolock = 0; + iunlock(&ctlr->activends); + sleep(&e->wr, isoready, e); + ilock(&ctlr->activends); + isolock = 1; + } + if (x->etd == nil){ + XPRINT("!"); + continue; + } + } + if (w) + e->psize = ((td->dev >> 21) + 1) & 0x7ff; + else + e->psize = (x->etd->status + 1) & 0x7ff; + if(e->psize > e->maxpkt) + panic("packet size > maximum"); + } + if((i = n) >= e->psize) + i = e->psize; + if (w) + e->buffered += i; + else{ + e->buffered -= i; + if (e->buffered < 0) + e->buffered = 0; + } + isolock = 0; + iunlock(&ctlr->activends); + td->flags &= ~IsoClean; + bp = x->bp0 + (td - x->td0) * e->maxpkt / e->pollms; + q = bp + e->off; + if (w){ + memmove(q, p, i); + }else{ + memmove(p, q, i); + } + p += i; + n -= i; + e->off += i; + e->psize -= i; + if (e->psize){ + if (n != 0) + panic("usb iso: can't happen"); + break; + } + if(w) + td->offset = offset + (p-(uchar*)a) - (((td->dev >> 21) + 1) & 0x7ff); + td->status = ErrLimit3 | Active | IsoSelect | IOC; + x->etd = td->next; + e->off = 0; + } while(n > 0); + n = p-(uchar*)a; + e->foffset += n; + poperror(); + if (isolock) + iunlock(&ctlr->activends); + qunlock(&e->rlock); + return n; +} + +static long +read(Usbhost *uh, Endpt *e, void *a, long n, vlong offset) +{ + long l, i; + Block *b; + Ctlr *ctlr; + uchar *p; + + ctlr = uh->ctlr; + if(e->iso) + return isoio(ctlr, e, a, n, (ulong)offset, 0); + + XPRINT("qlock(%p)\n", &e->rlock); + qlock(&e->rlock); + XPRINT("got qlock(%p)\n", &e->rlock); + if(waserror()){ + qunlock(&e->rlock); + eptcancel(ctlr, e); + nexterror(); + } + p = a; + do { + if(e->eof) { + XPRINT("e->eof\n"); + break; + } + if(e->err) + error(e->err); + qrcv(ctlr, e); + if(!e->iso) + e->data01 ^= 1; + sleep(&e->rr, eptinput, e); + if(e->err) + error(e->err); + b = qget(e->rq); /* TO DO */ + if(b == nil) { + XPRINT("b == nil\n"); + break; + } + if(waserror()){ + freeb(b); + nexterror(); + } + l = BLEN(b); + if((i = l) > n) + i = n; + if(i > 0){ + memmove(p, b->rp, i); + p += i; + } + poperror(); + freeb(b); + n -= i; + if (l != e->maxpkt) + break; + } while (n > 0); + poperror(); + qunlock(&e->rlock); + return p-(uchar*)a; +} + +static int +qisempty(void *arg) +{ + return ((QH*)arg)->entries & Terminate; +} + +static long +write(Usbhost *uh, Endpt *e, void *a, long n, vlong offset, int tok) +{ + int i, j; + QH *qh; + Block *b; + Ctlr *ctlr; + uchar *p; + + ctlr = uh->ctlr; + if(e->iso) + return isoio(ctlr, e, a, n, (ulong)offset, 1); + + p = a; + qlock(&e->wlock); + if(waserror()){ + qunlock(&e->wlock); + eptcancel(ctlr, e); + nexterror(); + } + do { + if(e->err) + error(e->err); + if((i = n) >= e->maxpkt) + i = e->maxpkt; + b = allocb(i); + if(waserror()){ + freeb(b); + nexterror(); + } + XPRINT("out [%d]", i); + for (j = 0; j < i; j++) XPRINT(" %.2x", p[j]); + XPRINT("\n"); + memmove(b->wp, p, i); + b->wp += i; + p += i; + n -= i; + poperror(); + qh = qxmit(ctlr, e, b, tok); + tok = TokOUT; + e->data01 ^= 1; + if(e->ntd >= e->nbuf) { +XPRINT("qh %s: q=%p first=%p last=%p entries=%.8lux\n", + "writeusb sleep", qh, qh->first, qh->last, qh->entries); + XPRINT("write: sleep %lux\n", &e->wr); + sleep(&e->wr, qisempty, qh); + XPRINT("write: awake\n"); + } + } while(n > 0); + poperror(); + qunlock(&e->wlock); + return p-(uchar*)a; +} + +static void +init(Usbhost* uh) +{ + Ctlr *ctlr; + + ctlr = uh->ctlr; + ilock(ctlr); + outl(ctlr->io+Flbaseadd, PCIWADDR(ctlr->frames)); + OUT(Frnum, 0); + OUT(Usbintr, 0xF); /* enable all interrupts */ + XPRINT("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(ctlr->io+SOFMod)); + XPRINT("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1)); + if((IN(Cmd)&1)==0) + OUT(Cmd, 1); /* run */ +// pprint("at: c=%x s=%x c0=%x\n", IN(Cmd), IN(Status), IN(Portsc0)); + iunlock(ctlr); +} + +static void +scanpci(void) +{ + int io; + Ctlr *ctlr; + Pcidev *p; + static int already = 0; + + if(already) + return; + already = 1; + p = nil; + while(p = pcimatch(p, 0, 0)) { + /* + * Find UHCI controllers. Class = 12 (serial controller), + * Sub-class = 3 (USB) and Programming Interface = 0. + */ + if(p->ccrb != 0x0C || p->ccru != 0x03 || p->ccrp != 0x00) + continue; + io = p->mem[4].bar & ~0x0F; + if(io == 0) { + print("usbuhci: failed to map registers\n"); + continue; + } + if(ioalloc(io, p->mem[4].size, 0, "usbuhci") < 0){ + print("usbuhci: port %d in use\n", io); + continue; + } + if(p->intl == 0xFF || p->intl == 0) { + print("usbuhci: no irq assigned for port %d\n", io); + continue; + } + + XPRINT("usbuhci: %x/%x port 0x%ux size 0x%x irq %d\n", + p->vid, p->did, io, p->mem[4].size, p->intl); + + ctlr = malloc(sizeof(Ctlr)); + ctlr->pcidev = p; + ctlr->io = io; + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + +static int +reset(Usbhost *uh) +{ + int i; + TD *t; + ulong io; + Ctlr *ctlr; + Pcidev *p; + + scanpci(); + + /* + * Any adapter matches if no uh->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(uh->port == 0 || uh->port == ctlr->io){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + io = ctlr->io; + p = ctlr->pcidev; + + uh->ctlr = ctlr; + uh->port = io; + uh->irq = p->intl; + uh->tbdf = p->tbdf; + + XPRINT("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n", + IN(Cmd), IN(Status), IN(Usbintr), inb(io+Frnum)); + XPRINT("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n", + IN(Flbaseadd), inb(io+SOFMod), IN(Portsc0), IN(Portsc1)); + + OUT(Cmd, 0); /* stop */ + while((IN(Status) & (1<<5)) == 0) /* wait for halt */ + ; + OUT(Status, 0xFF); /* clear pending interrupts */ + pcicfgw16(p, 0xc0, 0x2000); /* legacy support register: turn off lunacy mode */ + + if(0){ + i = inb(io+SOFMod); + OUT(Cmd, 4); /* global reset */ + delay(15); + OUT(Cmd, 0); /* end reset */ + delay(4); + outb(io+SOFMod, i); + } + + ctlr->tdpool = xspanalloc(128*sizeof(TD), 16, 0); + for(i=128; --i>=0;){ + ctlr->tdpool[i].next = ctlr->freetd; + ctlr->freetd = &ctlr->tdpool[i]; + } + ctlr->qhpool = xspanalloc(64*sizeof(QH), 16, 0); + for(i=64; --i>=0;){ + ctlr->qhpool[i].next = ctlr->freeqh; + ctlr->freeqh = &ctlr->qhpool[i]; + } + + /* + * the last entries of the periodic (interrupt & isochronous) scheduling TD entries + * points to the control queue and the bandwidth sop for bulk traffic. + * this is looped following the instructions in PIIX4 errata 29773804.pdf: + * a QH links to a looped but inactive TD as its sole entry, + * with its head entry leading on to the bulk traffic, the last QH of which + * links back to the empty QH. + */ + ctlr->ctlq = allocqh(ctlr); + ctlr->bwsop = allocqh(ctlr); + ctlr->bulkq = allocqh(ctlr); + ctlr->recvq = allocqh(ctlr); + t = alloctd(ctlr); /* inactive TD, looped */ + t->link = PCIWADDR(t); + ctlr->bwsop->entries = PCIWADDR(t); + + ctlr->ctlq->head = PCIWADDR(ctlr->bulkq) | IsQH; + ctlr->bulkq->head = PCIWADDR(ctlr->recvq) | IsQH; + ctlr->recvq->head = PCIWADDR(ctlr->bwsop) | IsQH; + if (1) /* don't use loop back */ + ctlr->bwsop->head = Terminate; + else /* set up loop back */ + ctlr->bwsop->head = PCIWADDR(ctlr->bwsop) | IsQH; + + ctlr->frames = xspanalloc(FRAMESIZE, FRAMESIZE, 0); + ctlr->frameld = xallocz(FRAMESIZE, 1); + for (i = 0; i < NFRAME; i++) + ctlr->frames[i] = PCIWADDR(ctlr->ctlq) | IsQH; + + /* + * Linkage to the generic USB driver. + */ + uh->init = init; + uh->interrupt = interrupt; + + uh->portinfo = portinfo; + uh->portreset = portreset; + uh->portenable = portenable; + + uh->epalloc = epalloc; + uh->epfree = epfree; + uh->epopen = epopen; + uh->epclose = epclose; + uh->epmode = epmode; + + uh->read = read; + uh->write = write; + + return 0; +} + +void +usbuhcilink(void) +{ + addusbtype("uhci", reset); +} diff --git a/os/pc/vga.c b/os/pc/vga.c new file mode 100644 index 00000000..0d6c7743 --- /dev/null +++ b/os/pc/vga.c @@ -0,0 +1,241 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +static Memimage* back; +static Memimage *conscol; + +static Point curpos; +static Rectangle window; +static int *xp; +static int xbuf[256]; +static Lock vgascreenlock; +int drawdebug; + +void +vgaimageinit(ulong chan) +{ + if(back == nil){ + back = allocmemimage(Rect(0,0,1,1), chan); /* RSC BUG */ + if(back == nil) + panic("back alloc"); /* RSC BUG */ + back->flags |= Frepl; + back->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF); + memfillcolor(back, DBlack); + } + + if(conscol == nil){ + conscol = allocmemimage(Rect(0,0,1,1), chan); /* RSC BUG */ + if(conscol == nil) + panic("conscol alloc"); /* RSC BUG */ + conscol->flags |= Frepl; + conscol->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF); + memfillcolor(conscol, DWhite); + } +} + +static void +vgascroll(VGAscr* scr) +{ + int h, o; + Point p; + Rectangle r; + + h = scr->memdefont->height; + o = 8*h; + r = Rpt(window.min, Pt(window.max.x, window.max.y-o)); + p = Pt(window.min.x, window.min.y+o); + memimagedraw(scr->gscreen, r, scr->gscreen, p, nil, p, S); + r = Rpt(Pt(window.min.x, window.max.y-o), window.max); + memimagedraw(scr->gscreen, r, back, ZP, nil, ZP, S); + + curpos.y -= o; +} + +static void +vgascreenputc(VGAscr* scr, char* buf, Rectangle *flushr) +{ + Point p; + int h, w, pos; + Rectangle r; + +// drawdebug = 1; + if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)]) + xp = xbuf; + + h = scr->memdefont->height; + switch(buf[0]){ + + case '\n': + if(curpos.y+h >= window.max.y){ + vgascroll(scr); + *flushr = window; + } + curpos.y += h; + vgascreenputc(scr, "\r", flushr); + break; + + case '\r': + xp = xbuf; + curpos.x = window.min.x; + break; + + case '\t': + p = memsubfontwidth(scr->memdefont, " "); + w = p.x; + if(curpos.x >= window.max.x-4*w) + vgascreenputc(scr, "\n", flushr); + + pos = (curpos.x-window.min.x)/w; + pos = 4-(pos%4); + *xp++ = curpos.x; + r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y + h); + memimagedraw(scr->gscreen, r, back, back->r.min, nil, back->r.min, S); + curpos.x += pos*w; + break; + + case '\b': + if(xp <= xbuf) + break; + xp--; + r = Rect(*xp, curpos.y, curpos.x, curpos.y+h); + memimagedraw(scr->gscreen, r, back, back->r.min, nil, ZP, S); + combinerect(flushr, r); + curpos.x = *xp; + break; + + case '\0': + break; + + default: + p = memsubfontwidth(scr->memdefont, buf); + w = p.x; + + if(curpos.x >= window.max.x-w) + vgascreenputc(scr, "\n", flushr); + + *xp++ = curpos.x; + r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y+h); + memimagedraw(scr->gscreen, r, back, back->r.min, nil, back->r.min, S); + memimagestring(scr->gscreen, curpos, conscol, ZP, scr->memdefont, buf); + combinerect(flushr, r); + curpos.x += w; + } +// drawdebug = 0; +} + +static void +vgascreenputs(char* s, int n) +{ + int i; + Rune r; + char buf[4]; + VGAscr *scr; + Rectangle flushr; + + scr = &vgascreen[0]; + + if(!islo()){ + /* + * Don't deadlock trying to + * print in an interrupt. + */ + if(!canlock(&vgascreenlock)) + return; + } + else + lock(&vgascreenlock); + + flushr = Rect(10000, 10000, -10000, -10000); + + while(n > 0){ + i = chartorune(&r, s); + if(i == 0){ + s++; + --n; + continue; + } + memmove(buf, s, i); + buf[i] = 0; + n -= i; + s += i; + vgascreenputc(scr, buf, &flushr); + } + flushmemscreen(flushr); + + unlock(&vgascreenlock); +} + +void +vgascreenwin(VGAscr* scr) +{ + int h, w; + + h = scr->memdefont->height; + w = scr->memdefont->info[' '].width; + + window = insetrect(scr->gscreen->r, 48); + window.max.x = window.min.x+((window.max.x-window.min.x)/w)*w; + window.max.y = window.min.y+((window.max.y-window.min.y)/h)*h; + curpos = window.min; + + screenputs = vgascreenputs; +} + +/* + * Supposedly this is the way to turn DPMS + * monitors off using just the VGA registers. + * Unfortunately, it seems to mess up the video mode + * on the cards I've tried. + */ +void +vgablank(VGAscr*, int blank) +{ + uchar seq1, crtc17; + + if(blank) { + seq1 = 0x00; + crtc17 = 0x80; + } else { + seq1 = 0x20; + crtc17 = 0x00; + } + + outs(Seqx, 0x0100); /* synchronous reset */ + seq1 |= vgaxi(Seqx, 1) & ~0x20; + vgaxo(Seqx, 1, seq1); + crtc17 |= vgaxi(Crtx, 0x17) & ~0x80; + delay(10); + vgaxo(Crtx, 0x17, crtc17); + outs(Crtx, 0x0300); /* end synchronous reset */ +} + +void +cornerstring(char *s) +{ + int h, w; + VGAscr *scr; + Rectangle r; + Point p; + + scr = &vgascreen[0]; + if(scr->aperture == 0 || screenputs != vgascreenputs) + return; + p = memsubfontwidth(scr->memdefont, s); + w = p.x; + h = scr->memdefont->height; + + r = Rect(0, 0, w, h); + memimagedraw(scr->gscreen, r, back, back->r.min, nil, back->r.min, S); + memimagestring(scr->gscreen, r.min, conscol, ZP, scr->memdefont, s); +// flushmemscreen(r); +} diff --git a/os/pc/vga.h b/os/pc/vga.h new file mode 100644 index 00000000..685a8ee5 --- /dev/null +++ b/os/pc/vga.h @@ -0,0 +1,75 @@ +/* + * Generic VGA registers. + */ +enum { + MiscW = 0x03C2, /* Miscellaneous Output (W) */ + MiscR = 0x03CC, /* Miscellaneous Output (R) */ + Status0 = 0x03C2, /* Input status 0 (R) */ + Status1 = 0x03DA, /* Input Status 1 (R) */ + FeatureR = 0x03CA, /* Feature Control (R) */ + FeatureW = 0x03DA, /* Feature Control (W) */ + + Seqx = 0x03C4, /* Sequencer Index, Data at Seqx+1 */ + Crtx = 0x03D4, /* CRT Controller Index, Data at Crtx+1 */ + Grx = 0x03CE, /* Graphics Controller Index, Data at Grx+1 */ + Attrx = 0x03C0, /* Attribute Controller Index and Data */ + + PaddrW = 0x03C8, /* Palette Address Register, write */ + Pdata = 0x03C9, /* Palette Data Register */ + Pixmask = 0x03C6, /* Pixel Mask Register */ + PaddrR = 0x03C7, /* Palette Address Register, read */ + Pstatus = 0x03C7, /* DAC Status (RO) */ + + Pcolours = 256, /* Palette */ + Pred = 0, + Pgreen = 1, + Pblue = 2, + + Pblack = 0x00, + Pwhite = 0xFF, +}; + +#define vgai(port) inb(port) +#define vgao(port, data) outb(port, data) + +extern int vgaxi(long, uchar); +extern int vgaxo(long, uchar, uchar); + +typedef struct Cursor Cursor; +struct Cursor +{ + Point offset; + uchar clr[2*16]; + uchar set[2*16]; +}; + +/* + * First pass at tidying this up... + */ +typedef struct Mode { + int x; + int y; + int d; + + ulong aperture; /* this is a physical address */ + int apsize; + int apshift; +} Mode; + +/* + * Definitions of known VGA controllers. + */ +typedef struct Vgac Vgac; +struct Vgac { + char* name; + void (*page)(int); + void (*init)(Mode*); + int (*ident)(void); + void (*enable)(void); + void (*disable)(void); + void (*move)(int, int); + void (*load)(Cursor*); + Vgac* link; +}; + +extern void addvgaclink(Vgac*); diff --git a/os/pc/vga3dfx.c b/os/pc/vga3dfx.c new file mode 100644 index 00000000..cce27185 --- /dev/null +++ b/os/pc/vga3dfx.c @@ -0,0 +1,258 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +typedef struct { + int vidProcCfg; + int hwCurPatAddr; + int hwCurLoc; + int hwCurC0; + int hwCurC1; +} Cursor3dfx; + +enum { + dramInit0 = 0x18, + dramInit1 = 0x1C, + + hwCur = 0x5C, +}; + +static ulong +tdfxlinear(VGAscr* scr, int* size, int* align) +{ + Pcidev *p; + int oapsize, wasupamem; + ulong aperture, oaperture; + + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + aperture = 0; + if(p = pcimatch(nil, 0x121A, 0)){ + switch(p->did){ + case 0x0003: /* Banshee */ + case 0x0005: /* Avenger (a.k.a. Voodoo3) */ + case 0x0009: /* Voodoo5 */ + aperture = p->mem[1].bar & ~0x0F; + *size = p->mem[1].size; + break; + default: + break; + } + } + + if(wasupamem){ + if(oaperture == aperture) + return oaperture; + upafree(oaperture, oapsize); + } + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)){ + aperture = oaperture; + scr->isupamem = 1; + } + else + scr->isupamem = 0; + } + else + scr->isupamem = 1; + + return aperture; +} + +static void +tdfxenable(VGAscr* scr) +{ + Pcidev *p; + ulong aperture; + int align, i, *mmio, size; + + /* + * Only once, can't be disabled for now. + * scr->io holds the physical address of + * the MMIO registers. + */ + if(scr->io) + return; + if(p = pcimatch(nil, 0x121A, 0)){ + switch(p->did){ + case 0x0003: /* Banshee */ + case 0x0005: /* Avenger (a.k.a. Voodoo3) */ + break; + default: + return; + } + } + else + return; + scr->io = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0); + if(scr->io == 0) + return; + + addvgaseg("3dfxmmio", (ulong)scr->io, p->mem[0].size); + + size = p->mem[1].size; + align = 0; + aperture = tdfxlinear(scr, &size, &align); + if(aperture){ + scr->aperture = aperture; + scr->apsize = size; + addvgaseg("3dfxscreen", aperture, size); + } + + /* + * Find a place for the cursor data in display memory. + * If SDRAM then there's 16MB memory else it's SGRAM + * and can count it based on the power-on straps - + * chip size can be 8Mb or 16Mb, and there can be 4 or + * 8 of them. + * Use the last 1KB of the framebuffer. + */ + mmio = KADDR(scr->io + dramInit0); + if(*(mmio+1) & 0x40000000) + i = 16*1024*1024; + else{ + if(*mmio & 0x08000000) + i = 16*1024*1024/8; + else + i = 8*1024*1024/8; + if(*mmio & 0x04000000) + i *= 8; + else + i *= 4; + } + scr->storage = i - 1024; +} + +static void +tdfxcurdisable(VGAscr* scr) +{ + Cursor3dfx *cursor3dfx; + + if(scr->io == 0) + return; + cursor3dfx = KADDR(scr->io+hwCur); + cursor3dfx->vidProcCfg &= ~0x08000000; +} + +static void +tdfxcurload(VGAscr* scr, Cursor* curs) +{ + int y; + uchar *p; + Cursor3dfx *cursor3dfx; + + if(scr->io == 0) + return; + cursor3dfx = KADDR(scr->io+hwCur); + + /* + * Disable the cursor then load the new image in + * the top-left of the 64x64 array. + * The cursor data is stored in memory as 128-bit + * words consisting of plane 0 in the least significant 64-bits + * and plane 1 in the most significant. + * The X11 cursor truth table is: + * p0 p1 colour + * 0 0 transparent + * 0 1 transparent + * 1 0 hwCurC0 + * 1 1 hwCurC1 + * Unused portions of the image have been initialised to be + * transparent. + */ + cursor3dfx->vidProcCfg &= ~0x08000000; + p = KADDR(scr->aperture + scr->storage); + for(y = 0; y < 16; y++){ + *p++ = curs->clr[2*y]|curs->set[2*y]; + *p++ = curs->clr[2*y+1]|curs->set[2*y+1]; + p += 6; + *p++ = curs->set[2*y]; + *p++ = curs->set[2*y+1]; + p += 6; + } + + /* + * Save the cursor hotpoint and enable the cursor. + * The 0,0 cursor point is bottom-right. + */ + scr->offset.x = 63+curs->offset.x; + scr->offset.y = 63+curs->offset.y; + cursor3dfx->vidProcCfg |= 0x08000000; +} + +static int +tdfxcurmove(VGAscr* scr, Point p) +{ + Cursor3dfx *cursor3dfx; + + if(scr->io == 0) + return 1; + cursor3dfx = KADDR(scr->io+hwCur); + + cursor3dfx->hwCurLoc = ((p.y+scr->offset.y)<<16)|(p.x+scr->offset.x); + + return 0; +} + +static void +tdfxcurenable(VGAscr* scr) +{ + Cursor3dfx *cursor3dfx; + + tdfxenable(scr); + if(scr->io == 0) + return; + cursor3dfx = KADDR(scr->io+hwCur); + + /* + * Cursor colours. + */ + cursor3dfx->hwCurC0 = 0xFFFFFFFF; + cursor3dfx->hwCurC1 = 0x00000000; + + /* + * Initialise the 64x64 cursor to be transparent (X11 mode). + */ + cursor3dfx->hwCurPatAddr = scr->storage; + memset(KADDR(scr->aperture + scr->storage), 0, 64*16); + + /* + * Load, locate and enable the 64x64 cursor in X11 mode. + */ + tdfxcurload(scr, &arrow); + tdfxcurmove(scr, ZP); + cursor3dfx->vidProcCfg |= 0x08000002; +} + +VGAdev vga3dfxdev = { + "3dfx", + + tdfxenable, + nil, + nil, + tdfxlinear, +}; + +VGAcur vga3dfxcur = { + "3dfxhwgc", + + tdfxcurenable, + tdfxcurdisable, + tdfxcurload, + tdfxcurmove, +}; diff --git a/os/pc/vgaark2000pv.c b/os/pc/vgaark2000pv.c new file mode 100644 index 00000000..d42eccc3 --- /dev/null +++ b/os/pc/vgaark2000pv.c @@ -0,0 +1,190 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +static int +ark2000pvpageset(VGAscr*, int page) +{ + uchar seq15; + + seq15 = vgaxi(Seqx, 0x15); + vgaxo(Seqx, 0x15, page); + vgaxo(Seqx, 0x16, page); + + return seq15; +} + +static void +ark2000pvpage(VGAscr* scr, int page) +{ + lock(&scr->devlock); + ark2000pvpageset(scr, page); + unlock(&scr->devlock); +} + +static void +ark2000pvdisable(VGAscr*) +{ + uchar seq20; + + seq20 = vgaxi(Seqx, 0x20) & ~0x08; + vgaxo(Seqx, 0x20, seq20); +} + +static void +ark2000pvenable(VGAscr* scr) +{ + uchar seq20; + ulong storage; + + /* + * Disable the cursor then configure for X-Windows style, + * 32x32 and 4/8-bit colour depth. + * Set cursor colours for 4/8-bit. + */ + seq20 = vgaxi(Seqx, 0x20) & ~0x1F; + vgaxo(Seqx, 0x20, seq20); + seq20 |= 0x18; + + vgaxo(Seqx, 0x26, 0x00); + vgaxo(Seqx, 0x27, 0x00); + vgaxo(Seqx, 0x28, 0x00); + vgaxo(Seqx, 0x29, 0xFF); + vgaxo(Seqx, 0x2A, 0xFF); + vgaxo(Seqx, 0x2B, 0xFF); + + /* + * Cursor storage is a 256 byte or 1Kb block located in the last + * 16Kb of video memory. Crt25 is the index of which block. + */ + storage = (vgaxi(Seqx, 0x10)>>6) & 0x03; + storage = (1024*1024)<storage = storage; + vgaxo(Seqx, 0x25, 0x3F); + + /* + * Enable the cursor. + */ + vgaxo(Seqx, 0x20, seq20); +} + +static void +ark2000pvload(VGAscr* scr, Cursor* curs) +{ + uchar *p, seq10; + int opage, x, y; + + /* + * Is linear addressing turned on? This will determine + * how we access the cursor storage. + */ + seq10 = vgaxi(Seqx, 0x10); + opage = 0; + p = KADDR(scr->aperture); + if(!(seq10 & 0x10)){ + lock(&scr->devlock); + opage = ark2000pvpageset(scr, scr->storage>>16); + p += (scr->storage & 0xFFFF); + } + else + p += scr->storage; + + /* + * The cursor is set in X11 mode which gives the following + * truth table: + * and xor colour + * 0 0 underlying pixel colour + * 0 1 underlying pixel colour + * 1 0 background colour + * 1 1 foreground colour + * Put the cursor into the top-left of the 32x32 array. + * The manual doesn't say what the data layout in memory is - + * this worked out by trial and error. + */ + for(y = 0; y < 32; y++){ + for(x = 0; x < 32/8; x++){ + if(x < 16/8 && y < 16){ + *p++ = curs->clr[2*y + x]|curs->set[2*y + x]; + *p++ = curs->set[2*y + x]; + } + else { + *p++ = 0x00; + *p++ = 0x00; + } + } + } + + if(!(seq10 & 0x10)){ + ark2000pvpageset(scr, opage); + unlock(&scr->devlock); + } + + /* + * Save the cursor hotpoint. + */ + scr->offset = curs->offset; +} + +static int +ark2000pvmove(VGAscr* scr, Point p) +{ + int x, xo, y, yo; + + /* + * Mustn't position the cursor offscreen even partially, + * or it might disappear. Therefore, if x or y is -ve, adjust the + * cursor origins instead. + */ + if((x = p.x+scr->offset.x) < 0){ + xo = -x; + x = 0; + } + else + xo = 0; + if((y = p.y+scr->offset.y) < 0){ + yo = -y; + y = 0; + } + else + yo = 0; + + /* + * Load the new values. + */ + vgaxo(Seqx, 0x2C, xo); + vgaxo(Seqx, 0x2D, yo); + vgaxo(Seqx, 0x21, (x>>8) & 0x0F); + vgaxo(Seqx, 0x22, x & 0xFF); + vgaxo(Seqx, 0x23, (y>>8) & 0x0F); + vgaxo(Seqx, 0x24, y & 0xFF); + + return 0; +} + +VGAdev vgaark2000pvdev = { + "ark2000pv", + + 0, + 0, + ark2000pvpage, + 0, +}; + +VGAcur vgaark2000pvcur = { + "ark2000pvhwgc", + + ark2000pvenable, + ark2000pvdisable, + ark2000pvload, + ark2000pvmove, +}; diff --git a/os/pc/vgabt485.c b/os/pc/vgabt485.c new file mode 100644 index 00000000..1c155e6d --- /dev/null +++ b/os/pc/vgabt485.c @@ -0,0 +1,245 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +/* + * Hardware graphics cursor support for + * Brooktree Bt485 Monolithic True-Color RAMDAC. + * Assumes hooked up to an S3 86C928. + * + * BUGS: + * 64x64x2 cursor always used; + * no support for interlaced mode. + */ +enum { + AddrW = 0x00, /* Address register; palette/cursor RAM write */ + Palette = 0x01, /* 6/8-bit color palette data */ + Pmask = 0x02, /* Pixel mask register */ + AddrR = 0x03, /* Address register; palette/cursor RAM read */ + ColorW = 0x04, /* Address register; cursor/overscan color write */ + Color = 0x05, /* Cursor/overscan color data */ + Cmd0 = 0x06, /* Command register 0 */ + ColorR = 0x07, /* Address register; cursor/overscan color read */ + Cmd1 = 0x08, /* Command register 1 */ + Cmd2 = 0x09, /* Command register 2 */ + Status = 0x0A, /* Status */ + Cmd3 = 0x1A, /* Command register 3 */ + Cram = 0x0B, /* Cursor RAM array data */ + Cxlr = 0x0C, /* Cursor x-low register */ + Cxhr = 0x0D, /* Cursor x-high register */ + Cylr = 0x0E, /* Cursor y-low register */ + Cyhr = 0x0F, /* Cursor y-high register */ + + Nreg = 0x10, +}; + +/* + * Lower 2-bits of indirect DAC register + * addressing. + */ +static ushort dacxreg[4] = { + PaddrW, Pdata, Pixmask, PaddrR +}; + +static uchar +bt485io(uchar reg) +{ + uchar crt55, cr0; + + crt55 = vgaxi(Crtx, 0x55) & 0xFC; + if((reg & 0x0F) == Status){ + /* + * 1,2: Set indirect addressing for Status or + * Cmd3 - set bit7 of Cr0. + */ + vgaxo(Crtx, 0x55, crt55|((Cmd0>>2) & 0x03)); + cr0 = vgai(dacxreg[Cmd0 & 0x03])|0x80; + vgao(dacxreg[Cmd0 & 0x03], cr0); + + /* + * 3,4: Set the index into the Write register, + * index == 0x00 for Status, 0x01 for Cmd3. + */ + vgaxo(Crtx, 0x55, crt55|((AddrW>>2) & 0x03)); + vgao(dacxreg[AddrW & 0x03], (reg == Status) ? 0x00: 0x01); + + /* + * 5,6: Get the contents of the appropriate + * register at 0x0A. + */ + } + + return crt55; +} + +static uchar +bt485i(uchar reg) +{ + uchar crt55, r; + + crt55 = bt485io(reg); + vgaxo(Crtx, 0x55, crt55|((reg>>2) & 0x03)); + r = vgai(dacxreg[reg & 0x03]); + vgaxo(Crtx, 0x55, crt55); + + return r; +} + +static void +bt485o(uchar reg, uchar data) +{ + uchar crt55; + + crt55 = bt485io(reg); + vgaxo(Crtx, 0x55, crt55|((reg>>2) & 0x03)); + vgao(dacxreg[reg & 0x03], data); + vgaxo(Crtx, 0x55, crt55); +} + +static void +bt485disable(VGAscr*) +{ + uchar r; + + /* + * Disable + * cursor mode 3; + * cursor control enable for Bt485 DAC; + * the hardware cursor external operation mode. + */ + r = bt485i(Cmd2) & ~0x03; + bt485o(Cmd2, r); + + r = vgaxi(Crtx, 0x45) & ~0x20; + vgaxo(Crtx, 0x45, r); + + r = vgaxi(Crtx, 0x55) & ~0x20; + vgaxo(Crtx, 0x55, r); +} + +static void +bt485enable(VGAscr*) +{ + uchar r; + + /* + * Turn cursor off. + */ + r = bt485i(Cmd2) & 0xFC; + bt485o(Cmd2, r); + + /* + * Overscan colour, + * cursor colour 1 (white), + * cursor colour 2, 3 (black). + */ + bt485o(ColorW, 0x00); + bt485o(Color, Pwhite); bt485o(Color, Pwhite); bt485o(Color, Pwhite); + + bt485o(Color, Pwhite); bt485o(Color, Pwhite); bt485o(Color, Pwhite); + + bt485o(Color, Pblack); bt485o(Color, Pblack); bt485o(Color, Pblack); + bt485o(Color, Pblack); bt485o(Color, Pblack); bt485o(Color, Pblack); + + /* + * Finally, enable + * the hardware cursor external operation mode; + * cursor control enable for Bt485 DAC. + * The #9GXE cards seem to need the 86C928 Bt485 support + * enabled in order to work at all in enhanced mode. + */ + + r = vgaxi(Crtx, 0x55)|0x20; + vgaxo(Crtx, 0x55, r); + + r = vgaxi(Crtx, 0x45)|0x20; + vgaxo(Crtx, 0x45, r); +} + +static void +bt485load(VGAscr* scr, Cursor* curs) +{ + uchar r; + int x, y; + + /* + * Turn cursor off; + * put cursor into 64x64x2 mode and clear MSBs of address; + * clear LSBs of address; + */ + r = bt485i(Cmd2) & 0xFC; + bt485o(Cmd2, r); + + r = (bt485i(Cmd3) & 0xFC)|0x04; + bt485o(Cmd3, r); + + bt485o(AddrW, 0x00); + + /* + * Now load the cursor RAM array, both planes. + * The cursor is 16x16, the array 64x64; put + * the cursor in the top left. The 0,0 cursor + * point is bottom-right, so positioning will + * have to take that into account. + */ + for(y = 0; y < 64; y++){ + for(x = 0; x < 64/8; x++){ + if(x < 16/8 && y < 16) + bt485o(Cram, curs->clr[x+y*2]); + else + bt485o(Cram, 0x00); + } + } + for(y = 0; y < 64; y++){ + for(x = 0; x < 64/8; x++){ + if(x < 16/8 && y < 16) + bt485o(Cram, curs->set[x+y*2]); + else + bt485o(Cram, 0x00); + } + } + + /* + * Initialise the cursor hot-point + * and enable the cursor. + */ + scr->offset.x = 64+curs->offset.x; + scr->offset.y = 64+curs->offset.y; + + r = (bt485i(Cmd2) & 0xFC)|0x01; + bt485o(Cmd2, r); +} + +static int +bt485move(VGAscr* scr, Point p) +{ + int x, y; + + x = p.x+scr->offset.x; + y = p.y+scr->offset.y; + + bt485o(Cxlr, x & 0xFF); + bt485o(Cxhr, (x>>8) & 0x0F); + bt485o(Cylr, y & 0xFF); + bt485o(Cyhr, (y>>8) & 0x0F); + + return 0; +} + +VGAcur vgabt485cur = { + "bt485hwgc", + + bt485enable, + bt485disable, + bt485load, + bt485move, +}; diff --git a/os/pc/vgaclgd542x.c b/os/pc/vgaclgd542x.c new file mode 100644 index 00000000..6dd95e9e --- /dev/null +++ b/os/pc/vgaclgd542x.c @@ -0,0 +1,291 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +static int +clgd542xpageset(VGAscr*, int page) +{ + uchar gr09; + int opage; + + if(vgaxi(Seqx, 0x07) & 0xF0) + page = 0; + gr09 = vgaxi(Grx, 0x09); + if(vgaxi(Grx, 0x0B) & 0x20){ + vgaxo(Grx, 0x09, page<<2); + opage = gr09>>2; + } + else{ + vgaxo(Grx, 0x09, page<<4); + opage = gr09>>4; + } + + return opage; +} + +static void +clgd542xpage(VGAscr* scr, int page) +{ + lock(&scr->devlock); + clgd542xpageset(scr, page); + unlock(&scr->devlock); +} + +static ulong +clgd542xlinear(VGAscr* scr, int* size, int* align) +{ + ulong aperture, oaperture; + int oapsize, wasupamem; + Pcidev *p; + + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + if(wasupamem) + upafree(oaperture, oapsize); + scr->isupamem = 0; + + if(p = pcimatch(nil, 0x1013, 0)){ + aperture = p->mem[0].bar & ~0x0F; + *size = p->mem[0].size; + } + else + aperture = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)) + scr->isupamem = 1; + } + else + scr->isupamem = 1; + + return aperture; +} + +static void +clgd542xdisable(VGAscr*) +{ + uchar sr12; + + sr12 = vgaxi(Seqx, 0x12); + vgaxo(Seqx, 0x12, sr12 & ~0x01); +} + +static void +clgd542xenable(VGAscr* scr) +{ + uchar sr12; + int mem, x; + + /* + * Disable the cursor. + */ + sr12 = vgaxi(Seqx, 0x12); + vgaxo(Seqx, 0x12, sr12 & ~0x01); + + /* + * Cursor colours. + * Can't call setcolor here as cursor is already locked. + */ + vgaxo(Seqx, 0x12, sr12|0x02); + vgao(PaddrW, 0x00); + vgao(Pdata, Pwhite); + vgao(Pdata, Pwhite); + vgao(Pdata, Pwhite); + vgao(PaddrW, 0x0F); + vgao(Pdata, Pblack); + vgao(Pdata, Pblack); + vgao(Pdata, Pblack); + vgaxo(Seqx, 0x12, sr12); + + mem = 0; + switch(vgaxi(Crtx, 0x27) & ~0x03){ + + case 0x88: /* CL-GD5420 */ + case 0x8C: /* CL-GD5422 */ + case 0x94: /* CL-GD5424 */ + case 0x80: /* CL-GD5425 */ + case 0x90: /* CL-GD5426 */ + case 0x98: /* CL-GD5427 */ + case 0x9C: /* CL-GD5429 */ + /* + * The BIOS leaves the memory size in Seq0A, bits 4 and 3. + * See Technical Reference Manual Appendix E1, Section 1.3.2. + * + * The storage area for the 64x64 cursors is the last 16Kb of + * display memory. + */ + mem = (vgaxi(Seqx, 0x0A)>>3) & 0x03; + break; + + case 0xA0: /* CL-GD5430 */ + case 0xA8: /* CL-GD5434 */ + case 0xAC: /* CL-GD5436 */ + case 0xB8: /* CL-GD5446 */ + case 0x30: /* CL-GD7543 */ + /* + * Attempt to intuit the memory size from the DRAM control + * register. Minimum is 512KB. + * If DRAM bank switching is on then there's double. + */ + x = vgaxi(Seqx, 0x0F); + mem = (x>>3) & 0x03; + if(x & 0x80) + mem++; + break; + + default: /* uh, ah dunno */ + break; + } + scr->storage = ((256<aperture); + if(!(seq07 & 0xF0)){ + lock(&scr->devlock); + opage = clgd542xpageset(scr, scr->storage>>16); + p += (scr->storage & 0xFFFF); + } + else + p += scr->storage; + p += index*1024; + + for(y = yo; y < 16; y++){ + p0 = scr->set[2*y]; + p1 = scr->set[2*y+1]; + if(xo){ + p0 = (p0<>(8-xo)); + p1 <<= xo; + } + *p++ = p0; + *p++ = p1; + + for(x = 16; x < 64; x += 8) + *p++ = 0x00; + + p0 = scr->clr[2*y]|scr->set[2*y]; + p1 = scr->clr[2*y+1]|scr->set[2*y+1]; + if(xo){ + p0 = (p0<>(8-xo)); + p1 <<= xo; + } + *p++ = p0; + *p++ = p1; + + for(x = 16; x < 64; x += 8) + *p++ = 0x00; + } + while(y < 64+yo){ + for(x = 0; x < 64; x += 8){ + *p++ = 0x00; + *p++ = 0x00; + } + y++; + } + + if(!(seq07 & 0xF0)){ + clgd542xpageset(scr, opage); + unlock(&scr->devlock); + } +} + +static void +clgd542xload(VGAscr* scr, Cursor* curs) +{ + uchar sr12; + + /* + * Disable the cursor. + */ + sr12 = vgaxi(Seqx, 0x12); + vgaxo(Seqx, 0x12, sr12 & ~0x01); + + memmove(&scr->Cursor, curs, sizeof(Cursor)); + clgd542xinitcursor(scr, 0, 0, 0); + + /* + * Enable the cursor. + */ + vgaxo(Seqx, 0x13, 0); + vgaxo(Seqx, 0x12, sr12|0x05); +} + +static int +clgd542xmove(VGAscr* scr, Point p) +{ + int index, x, xo, y, yo; + + index = 0; + if((x = p.x+scr->offset.x) < 0){ + xo = -x; + x = 0; + } + else + xo = 0; + if((y = p.y+scr->offset.y) < 0){ + yo = -y; + y = 0; + } + else + yo = 0; + + if(xo || yo){ + clgd542xinitcursor(scr, xo, yo, 1); + index = 1; + } + vgaxo(Seqx, 0x13, index<<2); + + vgaxo(Seqx, 0x10|((x & 0x07)<<5), (x>>3) & 0xFF); + vgaxo(Seqx, 0x11|((y & 0x07)<<5), (y>>3) & 0xFF); + + return 0; +} + +VGAdev vgaclgd542xdev = { + "clgd542x", + + 0, + 0, + clgd542xpage, + clgd542xlinear, +}; + +VGAcur vgaclgd542xcur = { + "clgd542xhwgc", + + clgd542xenable, + clgd542xdisable, + clgd542xload, + clgd542xmove, +}; diff --git a/os/pc/vgaclgd546x.c b/os/pc/vgaclgd546x.c new file mode 100644 index 00000000..62bcd82a --- /dev/null +++ b/os/pc/vgaclgd546x.c @@ -0,0 +1,277 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +typedef struct Cursor546x Cursor546x; +struct Cursor546x { + ushort x; + ushort y; + ushort preset; + ushort enable; + ushort addr; +}; + +enum { + PaletteState = 0xB0, + CursorMMIO = 0xE0, +}; + +static ulong +clgd546xlinear(VGAscr* scr, int* size, int* align) +{ + ulong aperture, oaperture; + int oapsize, wasupamem; + Pcidev *p; + + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + aperture = 0; + if(p = pcimatch(nil, 0x1013, 0)){ + switch(p->did){ + case 0xD0: + case 0xD4: + case 0xD6: + aperture = p->mem[0].bar & ~0x0F; + *size = p->mem[0].size; + break; + default: + break; + } + } + + if(wasupamem){ + if(oaperture == aperture) + return oaperture; + upafree(oaperture, oapsize); + } + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)){ + aperture = oaperture; + scr->isupamem = 1; + } + else + scr->isupamem = 0; + } + else + scr->isupamem = 1; + + return aperture; +} +static void +clgd546xenable(VGAscr* scr) +{ + Pcidev *p; + int size, align; + ulong aperture; + + /* + * Only once, can't be disabled for now. + * scr->io holds the virtual address of + * the MMIO registers. + */ + if(scr->io) + return; + if(p = pcimatch(nil, 0x1013, 0)){ + switch(p->did){ + case 0xD0: + case 0xD4: + case 0xD6: + break; + default: + return; + } + } + else + return; + scr->io = upamalloc(p->mem[1].bar & ~0x0F, p->mem[1].size, 0); + if(scr->io == 0) + return; + addvgaseg("clgd546xmmio", scr->io, p->mem[1].size); + + scr->io = (ulong)KADDR(scr->io); + + size = p->mem[0].size; + align = 0; + aperture = clgd546xlinear(scr, &size, &align); + if(aperture) { + scr->aperture = aperture; + scr->apsize = size; + addvgaseg("clgd546xscreen", aperture, size); + } +} + +static void +clgd546xcurdisable(VGAscr* scr) +{ + Cursor546x *cursor546x; + + if(scr->io == 0) + return; + cursor546x = (Cursor546x*)(scr->io+CursorMMIO); + cursor546x->enable = 0; +} + +static void +clgd546xcurload(VGAscr* scr, Cursor* curs) +{ + int c, i, m, y; + uchar *p; + Cursor546x *cursor546x; + + if(scr->io == 0) + return; + cursor546x = (Cursor546x*)(scr->io+CursorMMIO); + + /* + * Disable the cursor then change only the bits + * that need it. + */ + cursor546x->enable = 0; + p = (uchar*)(scr->aperture + scr->storage); + for(y = 0; y < 16; y++){ + c = curs->set[2*y]; + m = 0; + for(i = 0; i < 8; i++){ + if(c & (1<<(7-i))) + m |= 1<set[2*y + 1]; + m = 0; + for(i = 0; i < 8; i++){ + if(c & (1<<(7-i))) + m |= 1<set[2*y]|curs->clr[2*y]; + m = 0; + for(i = 0; i < 8; i++){ + if(c & (1<<(7-i))) + m |= 1<set[2*y + 1]|curs->clr[2*y + 1]; + m = 0; + for(i = 0; i < 8; i++){ + if(c & (1<<(7-i))) + m |= 1<offset = curs->offset; + cursor546x->enable = 1; +} + +static int +clgd546xcurmove(VGAscr* scr, Point p) +{ + int x, xo, y, yo; + Cursor546x *cursor546x; + + if(scr->io == 0) + return 1; + cursor546x = (Cursor546x*)(scr->io+CursorMMIO); + + if((x = p.x+scr->offset.x) < 0){ + xo = -x; + x = 0; + } + else + xo = 0; + if((y = p.y+scr->offset.y) < 0){ + yo = -y; + y = 0; + } + else + yo = 0; + + cursor546x->preset = (xo<<8)|yo; + cursor546x->x = x; + cursor546x->y = y; + + return 0; +} + +static void +clgd546xcurenable(VGAscr* scr) +{ + uchar *p; + Cursor546x *cursor546x; + + clgd546xenable(scr); + if(scr->io == 0) + return; + cursor546x = (Cursor546x*)(scr->io+CursorMMIO); + + /* + * Cursor colours. + * Can't call setcolor here as cursor is already locked. + */ + p = (uchar*)(scr->io+PaletteState); + *p |= 0x08; + vgao(PaddrW, 0x00); + vgao(Pdata, Pwhite); + vgao(Pdata, Pwhite); + vgao(Pdata, Pwhite); + vgao(PaddrW, 0x0F); + vgao(Pdata, Pblack); + vgao(Pdata, Pblack); + vgao(Pdata, Pblack); + *p &= ~0x08; + + /* + * Find a place for the cursor data in display memory. + * 2 cursor images might be needed, 1KB each so use the last + * 2KB of the framebuffer and initialise them to be + * transparent. + */ + scr->storage = ((vgaxi(Seqx, 0x14) & 0x07)+1)*1024*1022; + cursor546x->addr = (scr->storage>>10)<<2; + memset((uchar*)(scr->aperture + scr->storage), 0, 2*64*16); + + /* + * Load, locate and enable the 64x64 cursor. + */ + clgd546xcurload(scr, &arrow); + clgd546xcurmove(scr, ZP); + cursor546x->enable = 1; +} + +VGAdev vgaclgd546xdev = { + "clgd546x", + + clgd546xenable, + nil, + nil, + clgd546xlinear, +}; + +VGAcur vgaclgd546xcur = { + "clgd546xhwgc", + + clgd546xcurenable, + clgd546xcurdisable, + clgd546xcurload, + clgd546xcurmove, +}; diff --git a/os/pc/vgact65545.c b/os/pc/vgact65545.c new file mode 100644 index 00000000..9a1467ac --- /dev/null +++ b/os/pc/vgact65545.c @@ -0,0 +1,149 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +static void +ct65545page(VGAscr*, int page) +{ + outb(0x3D6, 0x10); + outb(0x3D7, page<<6); +} + +static void +ct65545disable(VGAscr*) +{ + outl(0xA3D0, 0); +} + +static void +ct65545enable(VGAscr* scr) +{ + ulong storage; + + /* + * Find a place for the cursor data in display memory. + * Must be on a 1024-byte boundary. + */ + storage = ROUND(scr->gscreen->width*BY2WD*scr->gscreen->r.max.y, 1024); + outl(0xB3D0, storage); + scr->storage = storage; + + /* + * Set the colours. + * Enable the cursor. + */ + outl(0xA7D0, 0xFFFF0000); + outl(0xA3D0, 2); +} + +static void +ct65545initcursor(VGAscr* scr, int xo, int yo, int index) +{ + uchar *mem; + uint and, clr, set, xor; + int i, x, y; + + mem = KADDR(scr->aperture); + mem += scr->storage + index*1024; + + for(y = yo; y < 16; y++){ + clr = (scr->clr[2*y]<<8)|scr->clr[2*y+1]; + set = (scr->set[2*y]<<8)|scr->set[2*y+1]; + if(xo){ + clr <<= xo; + set <<= xo; + } + + and = 0; + xor = 0; + for(i = 0; i < 16; i++){ + if(set & (1<>8; + *mem++ = xor>>8; + *mem++ = and; + *mem++ = xor; + + for(x = 16; x < 64; x += 8){ + *mem++ = 0xFF; + *mem++ = 0x00; + } + } + while(y < 64+yo){ + for(x = 0; x < 64; x += 8){ + *mem++ = 0xFF; + *mem++ = 0x00; + } + y++; + } +} + +static void +ct65545load(VGAscr* scr, Cursor* curs) +{ + memmove(&scr->Cursor, curs, sizeof(Cursor)); + ct65545initcursor(scr, 0, 0, 0); +} + +static int +ct65545move(VGAscr* scr, Point p) +{ + int index, x, xo, y, yo; + + index = 0; + if((x = p.x+scr->offset.x) < 0){ + xo = -x; + x = 0; + } + else + xo = 0; + if((y = p.y+scr->offset.y) < 0){ + yo = -y; + y = 0; + } + else + yo = 0; + + if(xo || yo){ + ct65545initcursor(scr, xo, yo, 1); + index = 1; + } + outl(0xB3D0, scr->storage + index*1024); + + outl(0xAFD0, (y<<16)|x); + + return 0; +} + +VGAdev vgact65545dev = { + "ct65540", /* BUG: really 65545 */ + + 0, + 0, + ct65545page, + 0, +}; + +VGAcur vgact65545cur = { + "ct65545hwgc", + + ct65545enable, + ct65545disable, + ct65545load, + ct65545move, +}; diff --git a/os/pc/vgacyber938x.c b/os/pc/vgacyber938x.c new file mode 100644 index 00000000..7c86679c --- /dev/null +++ b/os/pc/vgacyber938x.c @@ -0,0 +1,225 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +enum { + CursorON = 0xC8, + CursorOFF = 0x00, +}; + +static int +cyber938xpageset(VGAscr*, int page) +{ + int opage; + + opage = inb(0x3D8); + + outb(0x3D8, page); + outb(0x3D9, page); + + return opage; +} + +static void +cyber938xpage(VGAscr* scr, int page) +{ + lock(&scr->devlock); + cyber938xpageset(scr, page); + unlock(&scr->devlock); +} + +static ulong +cyber938xlinear(VGAscr* scr, int* size, int* align) +{ + ulong aperture, oaperture; + int oapsize, wasupamem; + int osize; + Pcidev *p; + + osize = *size; + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + if(wasupamem) + upafree(oaperture, oapsize); + scr->isupamem = 0; + scr->mmio = 0; + + if(p = pcimatch(nil, 0x1023, 0)){ + aperture = p->mem[0].bar & ~0x0F; + *size = p->mem[0].size; + /* + * Heuristic to detect the MMIO space. We're flying blind + * here, with only the XFree86 source to guide us. + */ + if(p->mem[1].size == 0x20000) + scr->mmio = (ulong*)(p->mem[1].bar & ~0x0F); + } + else + aperture = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)) + scr->isupamem = 1; + } + else + scr->isupamem = 1; + + if(aperture) + addvgaseg("cyber938xscreen", aperture, osize); + if(scr->mmio) + addvgaseg("cyber938xmmio", (ulong)scr->mmio, 0x20000); + + return aperture; +} + +static void +cyber938xcurdisable(VGAscr*) +{ + vgaxo(Crtx, 0x50, CursorOFF); +} + +static void +cyber938xcurload(VGAscr* scr, Cursor* curs) +{ + uchar *p; + int islinear, opage, y; + + cyber938xcurdisable(scr); + + opage = 0; + p = KADDR(scr->aperture); + islinear = vgaxi(Crtx, 0x21) & 0x20; + if(!islinear){ + lock(&scr->devlock); + opage = cyber938xpageset(scr, scr->storage>>16); + p += (scr->storage & 0xFFFF); + } + else + p += scr->storage; + + for(y = 0; y < 16; y++){ + *p++ = curs->set[2*y]|curs->clr[2*y]; + *p++ = curs->set[2*y + 1]|curs->clr[2*y + 1]; + *p++ = 0x00; + *p++ = 0x00; + *p++ = curs->set[2*y]; + *p++ = curs->set[2*y + 1]; + *p++ = 0x00; + *p++ = 0x00; + } + memset(p, 0, (32-y)*8); + + if(!islinear){ + cyber938xpageset(scr, opage); + unlock(&scr->devlock); + } + + /* + * Save the cursor hotpoint and enable the cursor. + */ + scr->offset = curs->offset; + vgaxo(Crtx, 0x50, CursorON); +} + +static int +cyber938xcurmove(VGAscr* scr, Point p) +{ + int x, xo, y, yo; + + /* + * Mustn't position the cursor offscreen even partially, + * or it might disappear. Therefore, if x or y is -ve, adjust the + * cursor origins instead. + */ + if((x = p.x+scr->offset.x) < 0){ + xo = -x; + x = 0; + } + else + xo = 0; + if((y = p.y+scr->offset.y) < 0){ + yo = -y; + y = 0; + } + else + yo = 0; + + /* + * Load the new values. + */ + vgaxo(Crtx, 0x46, xo); + vgaxo(Crtx, 0x47, yo); + vgaxo(Crtx, 0x40, x & 0xFF); + vgaxo(Crtx, 0x41, (x>>8) & 0xFF); + vgaxo(Crtx, 0x42, y & 0xFF); + vgaxo(Crtx, 0x43, (y>>8) & 0xFF); + + return 0; +} + +static void +cyber938xcurenable(VGAscr* scr) +{ + int i; + ulong storage; + + cyber938xcurdisable(scr); + + /* + * Cursor colours. + */ + for(i = 0x48; i < 0x4C; i++) + vgaxo(Crtx, i, 0x00); + for(i = 0x4C; i < 0x50; i++) + vgaxo(Crtx, i, 0xFF); + + /* + * Find a place for the cursor data in display memory. + */ + storage = ((scr->gscreen->width*BY2WD*scr->gscreen->r.max.y+1023)/1024); + vgaxo(Crtx, 0x44, storage & 0xFF); + vgaxo(Crtx, 0x45, (storage>>8) & 0xFF); + storage *= 1024; + scr->storage = storage; + + /* + * Load, locate and enable the 32x32 cursor. + * (64x64 is bit 0, X11 format is bit 6 and cursor + * enable is bit 7). Bit 3 needs to be set on 9382 + * chips otherwise even the white bits are black. + */ + cyber938xcurload(scr, &arrow); + cyber938xcurmove(scr, ZP); + vgaxo(Crtx, 0x50, CursorON); +} + +VGAdev vgacyber938xdev = { + "cyber938x", + + nil, /* enable */ + nil, /* disable */ + cyber938xpage, /* page */ + cyber938xlinear, /* linear */ + nil, /* drawinit */ +}; + +VGAcur vgacyber938xcur = { + "cyber938xhwgc", + + cyber938xcurenable, /* enable */ + cyber938xcurdisable, /* disable */ + cyber938xcurload, /* load */ + cyber938xcurmove, /* move */ +}; diff --git a/os/pc/vgaet4000.c b/os/pc/vgaet4000.c new file mode 100644 index 00000000..f3ede446 --- /dev/null +++ b/os/pc/vgaet4000.c @@ -0,0 +1,270 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +static void +setet4000page(int page) +{ + uchar p; + + p = page & 0x0F; + p |= p<<4; + outb(0x3CD, p); + + p = (page & 0x30); + p |= p>>4; + outb(0x3CB, p); +} + +static void +et4000page(VGAscr *scr, int page) +{ + lock(&scr->devlock); + setet4000page(page); + unlock(&scr->devlock); +} + +static void +et4000disable(VGAscr*) +{ + uchar imaF7; + + outb(0x217A, 0xF7); + imaF7 = inb(0x217B) & ~0x80; + outb(0x217B, imaF7); +} + +static void +et4000enable(VGAscr *scr) +{ + uchar imaF7; + + et4000disable(scr); + + /* + * Configure CRTCB for Sprite, 64x64, + * CRTC pixel overlay. + */ + outb(0x217A, 0xEF); + outb(0x217B, 0x02); + + /* + * Cursor goes in the top left corner + * of the Sprite area, so the horizontal and + * vertical presets are 0. + */ + outb(0x217A, 0xE2); + outb(0x217B, 0x00); + outb(0x217A, 0xE3); + outb(0x217B, 0x00); + + outb(0x217A, 0xE6); + outb(0x217B, 0x00); + outb(0x217A, 0xE7); + outb(0x217B, 0x00); + + /* + * Find a place for the cursor data in display memory. + * Must be on a "doubleword" boundary, but put it on a + * 1024-byte boundary so that there's no danger of it + * crossing a page. + */ + scr->storage = (scr->gscreen->width*BY2WD*scr->gscreen->r.max.y+1023)/1024; + scr->storage *= 1024/4; + outb(0x217A, 0xE8); + outb(0x217B, scr->storage & 0xFF); + outb(0x217A, 0xE9); + outb(0x217B, (scr->storage>>8) & 0xFF); + outb(0x217A, 0xEA); + outb(0x217B, (scr->storage>>16) & 0x0F); + scr->storage *= 4; + + /* + * Row offset in "quadwords". Must be 2 for Sprite. + * Bag the pixel-panning. + * Colour depth, must be 2 for Sprite. + */ + outb(0x217A, 0xEB); + outb(0x217B, 0x02); + outb(0x217A, 0xEC); + outb(0x217B, 0x00); + + outb(0x217A, 0xED); + outb(0x217B, 0x00); + + outb(0x217A, 0xEE); +// if(vgascreen.ldepth == 3) + outb(0x217B, 0x01); +// else +// outb(0x217B, 0x00); + + /* + * Enable the CRTCB/Sprite. + */ + outb(0x217A, 0xF7); + imaF7 = inb(0x217B); + outb(0x217B, 0x80|imaF7); +} + +static void +et4000load(VGAscr *scr, Cursor *c) +{ + uchar p0, p1, *mem; + int i, x, y; + ushort p; + uchar clr[2*16], set[2*16]; + + /* + * Lock the display memory so we can update the + * cursor bitmap if necessary. + */ + lock(&scr->devlock); + + /* + * Disable the cursor. + * Set the display page (do we need to restore + * the current contents when done?) and the + * pointer to the two planes. What if this crosses + * into a new page? + */ + et4000disable(scr); + + setet4000page(scr->storage>>16); + mem = (uchar*)KADDR(scr->aperture) + (scr->storage & 0xFFFF); + + /* + * Initialise the 64x64 cursor RAM array. There are 2 planes, + * p0 and p1. Data is written 4 pixels per byte, with p1 the + * MS bit of each pixel. + * The cursor mode gives the following truth table: + * p1 p0 colour + * 0 0 Sprite Colour 0 (defined as 0x00) + * 0 1 Sprite Colour 1 (defined as 0xFF) + * 1 0 Transparent (allow CRTC pixel pass through) + * 1 1 Invert (allow CRTC pixel invert through) + * Put the cursor into the top-left of the 64x64 array. + * + * This is almost certainly wrong, since it has not + * been updated for the 3rd edition color values. + */ + memmove(clr, c->clr, sizeof(clr)); +// pixreverse(clr, sizeof(clr), 0); + memmove(set, c->set, sizeof(set)); +// pixreverse(set, sizeof(set), 0); + for(y = 0; y < 64; y++){ + for(x = 0; x < 64/8; x++){ + if(x < 16/8 && y < 16){ + p0 = clr[x+y*2]; + p1 = set[x+y*2]; + + p = 0x0000; + for(i = 0; i < 8; i++){ + if(p1 & (1<<(7-i))){ + /* nothing to do */ + } + else if(p0 & (1<<(7-i))) + p |= 0x01<<(2*i); + else + p |= 0x02<<(2*i); + } + *mem++ = p & 0xFF; + *mem++ = (p>>8) & 0xFF; + } + else { + *mem++ = 0xAA; + *mem++ = 0xAA; + } + } + } + + /* + * enable the cursor. + */ + outb(0x217A, 0xF7); + p = inb(0x217B)|0x80; + outb(0x217B, p); + + unlock(&scr->devlock); +} + +static int +et4000move(VGAscr *scr, Point p) +{ + int x, xo, y, yo; + + if(canlock(&scr->devlock) == 0) + return 1; + + /* + * Mustn't position the cursor offscreen even partially, + * or it disappears. Therefore, if x or y is -ve, adjust the + * cursor presets instead. + */ + if((x = p.x+scr->offset.x) < 0){ + xo = -x; + x = 0; + } + else + xo = 0; + if((y = p.y+scr->offset.y) < 0){ + yo = -y; + y = 0; + } + else + yo = 0; + + /* + * The cursor image is jerky if we don't do this. + * The cursor information is probably fetched from + * display memory during the horizontal blank active + * time and it doesn't like it if the coordinates + * are changed underneath. + */ + while((vgai(Status1) & 0x08) == 0) + ; + + outb(0x217A, 0xE2); + outb(0x217B, xo); + + outb(0x217A, 0xE6); + outb(0x217B, yo); + + outb(0x217A, 0xE1); + outb(0x217B, (x>>8) & 0xFF); + outb(0x217A, 0xE0); + outb(0x217B, x & 0xFF); + outb(0x217A, 0xE5); + outb(0x217B, (y>>8) & 0xFF); + outb(0x217A, 0xE4); + outb(0x217B, y & 0xFF); + + unlock(&scr->devlock); + return 0; +} + +VGAcur vgaet4000cur = { + "et4000hwgc", + + et4000enable, + et4000disable, + et4000load, + et4000move, +}; + +VGAdev vgaet4000dev = { + "et4000", + + 0, + 0, + et4000page, + 0 +}; diff --git a/os/pc/vgahiqvideo.c b/os/pc/vgahiqvideo.c new file mode 100644 index 00000000..6ae02b04 --- /dev/null +++ b/os/pc/vgahiqvideo.c @@ -0,0 +1,274 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +enum { + Xrx = 0x3D6, /* Configuration Extensions Index */ +}; + +static uchar +hiqvideoxi(long port, uchar index) +{ + uchar data; + + outb(port, index); + data = inb(port+1); + + return data; +} + +static void +hiqvideoxo(long port, uchar index, uchar data) +{ + outb(port, index); + outb(port+1, data); +} + +static ulong +hiqvideolinear(VGAscr* scr, int* size, int* align) +{ + ulong aperture, oaperture; + int oapsize, wasupamem; + Pcidev *p; + + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + aperture = 0; + if(p = pcimatch(nil, 0x102C, 0)){ + switch(p->did){ + case 0x00C0: /* 69000 HiQVideo */ + case 0x00E0: /* 65550 HiQV32 */ + case 0x00E4: /* 65554 HiQV32 */ + case 0x00E5: /* 65555 HiQV32 */ + aperture = p->mem[0].bar & ~0x0F; + *size = p->mem[0].size; + break; + default: + break; + } + } + + if(wasupamem){ + if(oaperture == aperture) + return oaperture; + upafree(oaperture, oapsize); + } + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)){ + aperture = oaperture; + scr->isupamem = 1; + } + else + scr->isupamem = 0; + } + else + scr->isupamem = 1; + + return aperture; +} + +static void +hiqvideoenable(VGAscr* scr) +{ + Pcidev *p; + int align, size, vmsize; + ulong aperture; + + /* + * Only once, can't be disabled for now. + */ + if(scr->io) + return; + if(p = pcimatch(nil, 0x102C, 0)){ + switch(p->did){ + case 0x00C0: /* 69000 HiQVideo */ + vmsize = 2*1024*1024; + break; + case 0x00E0: /* 65550 HiQV32 */ + case 0x00E4: /* 65554 HiQV32 */ + case 0x00E5: /* 65555 HiQV32 */ + switch((hiqvideoxi(Xrx, 0x43)>>1) & 0x03){ + default: + case 0: + vmsize = 1*1024*1024; + break; + case 1: + vmsize = 2*1024*1024; + break; + } + break; + default: + return; + } + } + else + return; + + size = p->mem[0].size; + align = 0; + aperture = hiqvideolinear(scr, &size, &align); + if(aperture) { + scr->aperture = aperture; + scr->apsize = size; + addvgaseg("hiqvideoscreen", aperture, size); + } + + /* + * Find a place for the cursor data in display memory. + * Must be on a 4096-byte boundary. + * scr->io holds the physical address of the cursor + * storage area in the framebuffer region. + */ + scr->storage = vmsize-4096; + scr->io = scr->aperture+scr->storage; +} + +static void +hiqvideocurdisable(VGAscr*) +{ + hiqvideoxo(Xrx, 0xA0, 0x10); +} + +static void +hiqvideocurload(VGAscr* scr, Cursor* curs) +{ + uchar *p; + int x, y; + + /* + * Disable the cursor. + */ + hiqvideocurdisable(scr); + + if(scr->io == 0) + return; + p = KADDR(scr->io); + + for(y = 0; y < 16; y += 2){ + *p++ = ~(curs->clr[2*y]|curs->set[2*y]); + *p++ = ~(curs->clr[2*y+1]|curs->set[2*y+1]); + *p++ = 0xFF; + *p++ = 0xFF; + *p++ = ~(curs->clr[2*y+2]|curs->set[2*y+2]); + *p++ = ~(curs->clr[2*y+3]|curs->set[2*y+3]); + *p++ = 0xFF; + *p++ = 0xFF; + *p++ = curs->set[2*y]; + *p++ = curs->set[2*y+1]; + *p++ = 0x00; + *p++ = 0x00; + *p++ = curs->set[2*y+2]; + *p++ = curs->set[2*y+3]; + *p++ = 0x00; + *p++ = 0x00; + } + while(y < 32){ + for(x = 0; x < 64; x += 8) + *p++ = 0xFF; + for(x = 0; x < 64; x += 8) + *p++ = 0x00; + y += 2; + } + + /* + * Save the cursor hotpoint and enable the cursor. + */ + scr->offset = curs->offset; + hiqvideoxo(Xrx, 0xA0, 0x11); +} + +static int +hiqvideocurmove(VGAscr* scr, Point p) +{ + int x, y; + + if(scr->io == 0) + return 1; + + if((x = p.x+scr->offset.x) < 0) + x = 0x8000|(-x & 0x07FF); + if((y = p.y+scr->offset.y) < 0) + y = 0x8000|(-y & 0x07FF); + + hiqvideoxo(Xrx, 0xA4, x & 0xFF); + hiqvideoxo(Xrx, 0xA5, (x>>8) & 0xFF); + hiqvideoxo(Xrx, 0xA6, y & 0xFF); + hiqvideoxo(Xrx, 0xA7, (y>>8) & 0xFF); + + return 0; +} + +static void +hiqvideocurenable(VGAscr* scr) +{ + uchar xr80; + + hiqvideoenable(scr); + if(scr->io == 0) + return; + + /* + * Disable the cursor. + */ + hiqvideocurdisable(scr); + + /* + * Cursor colours. + * Can't call setcolor here as cursor is already locked. + * When done make sure the cursor enable in Xr80 is set. + */ + xr80 = hiqvideoxi(Xrx, 0x80); + hiqvideoxo(Xrx, 0x80, xr80|0x01); + vgao(PaddrW, 0x04); + vgao(Pdata, Pwhite); + vgao(Pdata, Pwhite); + vgao(Pdata, Pwhite); + vgao(Pdata, Pblack); + vgao(Pdata, Pblack); + vgao(Pdata, Pblack); + hiqvideoxo(Xrx, 0x80, xr80|0x10); + + hiqvideoxo(Xrx, 0xA2, (scr->storage>>12)<<4); + hiqvideoxo(Xrx, 0xA3, (scr->storage>>16) & 0x3F); + + /* + * Load, locate and enable the 32x32 cursor. + * Cursor enable in Xr80 better be set already. + */ + hiqvideocurload(scr, &arrow); + hiqvideocurmove(scr, ZP); + hiqvideoxo(Xrx, 0xA0, 0x11); +} + +VGAdev vgahiqvideodev = { + "hiqvideo", + + hiqvideoenable, /* enable */ + nil, /* disable */ + nil, /* page */ + hiqvideolinear, /* linear */ +}; + +VGAcur vgahiqvideocur = { + "hiqvideohwgc", + + hiqvideocurenable, /* enable */ + hiqvideocurdisable, /* disable */ + hiqvideocurload, /* load */ + hiqvideocurmove, /* move */ +}; diff --git a/os/pc/vgai81x.c b/os/pc/vgai81x.c new file mode 100644 index 00000000..ac840fe6 --- /dev/null +++ b/os/pc/vgai81x.c @@ -0,0 +1,282 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +typedef struct +{ + ushort ctl; + ushort pad; + ulong base; + ulong pos; +} CursorI81x; + +enum { + Fbsize = 8*MB, + + hwCur = 0x70080, +}; + +static Pcidev * +i81xpcimatch(void) +{ + Pcidev *p; + + p = nil; + while((p = pcimatch(p, 0x8086, 0)) != nil){ + switch(p->did){ + default: + continue; + case 0x7121: + case 0x7123: + case 0x7125: + case 0x1102: + case 0x1112: + case 0x1132: + case 0x3577: /* IBM R31 uses intel 830M chipset */ + return p; + } + } + return nil; +} + +static ulong +i81xlinear(VGAscr* scr, int* size, int* align) +{ + Pcidev *p; + int oapsize, wasupamem; + ulong aperture, oaperture, fbuf, fbend, *rp; + + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + aperture = 0; + p = i81xpcimatch(); + if(p != nil) { + aperture = p->mem[0].bar & ~0x0F; + *size = p->mem[0].size; + if(*size > Fbsize) + *size = Fbsize; + } + + if(wasupamem){ + if(oaperture == aperture) + return oaperture; + upafree(oaperture, oapsize); + } + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)){ + aperture = oaperture; + scr->isupamem = 1; + } + else + scr->isupamem = 0; + } + else + scr->isupamem = 1; + + /* allocate space for frame buffer, populate page table */ + if(oapsize == 0) { + fbuf = PADDR(xspanalloc(*size, BY2PG, 0)); + fbend = PGROUND(fbuf+*size); + rp = KADDR(scr->io+0x10000); + while(fbuf < fbend) { + *rp++ = fbuf | (1<<0); + fbuf += BY2PG; + } + } + return aperture; +} + +static void +i81xenable(VGAscr* scr) +{ + Pcidev *p; + int align, size; + Mach *mach0; + ulong aperture, pgtbl, *rp, cursor, *pte; + + /* + * Only once, can't be disabled for now. + * scr->io holds the physical address of + * the MMIO registers. + */ + if(scr->io) + return; + p = i81xpcimatch(); + if(p == nil) + return; + scr->io = upamalloc(p->mem[1].bar & ~0x0F, p->mem[1].size, 0); + if(scr->io == 0) + return; + + /* allocate page table */ + pgtbl = PADDR(xspanalloc(64*1024, BY2PG, 0)); + rp = KADDR(scr->io+0x2020); + *rp = pgtbl | 1; + + addvgaseg("i81xmmio", (ulong)scr->io, p->mem[0].size); + + size = p->mem[0].size; + align = 0; + aperture = i81xlinear(scr, &size, &align); + if(aperture){ + scr->aperture = aperture; + scr->apsize = size; + addvgaseg("i81xscreen", aperture, size); + } + + /* + * allocate space for the cursor data in system memory. + * must be uncached. + */ + cursor = (ulong)xspanalloc(BY2PG, BY2PG, 0); + mach0 = MACHP(0); + pte = mmuwalk(mach0->pdb, cursor, 2, 0); + if(pte == nil) + panic("i81x cursor"); + *pte |= PTEUNCACHED; + scr->storage = PADDR(cursor); +} + +static void +i81xcurdisable(VGAscr* scr) +{ + CursorI81x *hwcurs; + + if(scr->io == 0) + return; + hwcurs = KADDR(scr->io+hwCur); + hwcurs->ctl = (1<<4); +} + +static void +i81xcurload(VGAscr* scr, Cursor* curs) +{ + int y; + uchar *p; + CursorI81x *hwcurs; + + if(scr->io == 0) + return; + hwcurs = KADDR(scr->io+hwCur); + + /* + * Disable the cursor then load the new image in + * the top-left of the 32x32 array. + * Unused portions of the image have been initialised to be + * transparent. + */ + hwcurs->ctl = (1<<4); + p = KADDR(scr->storage); + for(y = 0; y < 16; y += 2) { + *p++ = ~(curs->clr[2*y]|curs->set[2*y]); + *p++ = ~(curs->clr[2*y+1]|curs->set[2*y+1]); + p += 2; + *p++ = ~(curs->clr[2*y+2]|curs->set[2*y+2]); + *p++ = ~(curs->clr[2*y+3]|curs->set[2*y+3]); + p += 2; + *p++ = curs->set[2*y]; + *p++ = curs->set[2*y+1]; + p += 2; + *p++ = curs->set[2*y+2]; + *p++ = curs->set[2*y+3]; + p += 2; + } + + /* + * Save the cursor hotpoint and enable the cursor. + * The 0,0 cursor point is top-left. + */ + scr->offset.x = curs->offset.x; + scr->offset.y = curs->offset.y; + hwcurs->ctl = (1<<4)|1; +} + +static int +i81xcurmove(VGAscr* scr, Point p) +{ + int x, y; + ulong pos; + CursorI81x *hwcurs; + + if(scr->io == 0) + return 1; + hwcurs = KADDR(scr->io+hwCur); + + x = p.x+scr->offset.x; + y = p.y+scr->offset.y; + pos = 0; + if(x < 0) { + pos |= (1<<15); + x = -x; + } + if(y < 0) { + pos |= (1<<31); + y = -y; + } + pos |= ((y&0x7ff)<<16)|(x&0x7ff); + hwcurs->pos = pos; + + return 0; +} + +static void +i81xcurenable(VGAscr* scr) +{ + int i; + uchar *p; + CursorI81x *hwcurs; + + i81xenable(scr); + if(scr->io == 0) + return; + hwcurs = KADDR(scr->io+hwCur); + + /* + * Initialise the 32x32 cursor to be transparent in 2bpp mode. + */ + hwcurs->base = scr->storage; + p = KADDR(scr->storage); + for(i = 0; i < 32/2; i++) { + memset(p, 0xff, 8); + memset(p+8, 0, 8); + p += 16; + } + /* + * Load, locate and enable the 32x32 cursor in 2bpp mode. + */ + i81xcurload(scr, &arrow); + i81xcurmove(scr, ZP); +} + +VGAdev vgai81xdev = { + "i81x", + + i81xenable, + nil, + nil, + i81xlinear, +}; + +VGAcur vgai81xcur = { + "i81xhwgc", + + i81xcurenable, + i81xcurdisable, + i81xcurload, + i81xcurmove, +}; diff --git a/os/pc/vgamach64xx.c b/os/pc/vgamach64xx.c new file mode 100644 index 00000000..322c449c --- /dev/null +++ b/os/pc/vgamach64xx.c @@ -0,0 +1,1250 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +char Eunsupportedformat[] = "unsupported video format"; +char Enotconfigured[] = "device not configured"; + +#define SCALE_ZERO_EXTEND 0x0 +#define SCALE_DYNAMIC 0x1 +#define SCALE_RED_TEMP_6500K 0x0 +#define SCALE_RED_TEMP_9800K 0x2 +#define SCALE_HORZ_BLEND 0x0 +#define SCALE_HORZ_REP 0x4 +#define SCALE_VERT_BLEND 0x0 +#define SCALE_VERT_REP 0x8 +#define SCALE_BANDWIDTH_NORMAL 0x0 +#define SCALE_BANDWIDTH_EXCEEDED 0x4000000 +#define SCALE_BANDWIDTH_RESET 0x4000000 +#define SCALE_CLK_ACTIVITY 0x0 +#define SCALE_CLK_CONTINUOUS 0x20000000 +#define OVERLAY_DISABLE 0x0 +#define OVERLAY_ENABLE 0x40000000 +#define SCALE_DISABLE 0x0 +#define SCALE_ENABLE 0x80000000 + +#define SCALER_FRAME_READ_MODE_FULL 0x0 +#define SCALER_BUF_MODE_SINGLE 0x0 +#define SCALER_BUF_MODE_DOUBLE 0x40000 +#define SCALER_BUF_NEXT_0 0x0 +#define SCALER_BUF_NEXT_1 0x80000 +#define SCALER_BUF_STATUS_0 0x0 +#define SCALER_BUF_STATUS_1 0x100000 + +#define OVERLAY_MIX_G_CMP 0x0 +#define OVERLAY_MIX_ALWAYS_G 0x100 +#define OVERLAY_MIX_ALWAYS_V 0x200 +#define OVERLAY_MIX_NOT_G 0x300 +#define OVERLAY_MIX_NOT_V 0x400 +#define OVERLAY_MIX_G_XOR_V 0x500 +#define OVERLAY_MIX_NOT_G_XOR_V 0x600 +#define OVERLAY_MIX_V_CMP 0x700 +#define OVERLAY_MIX_NOT_G_OR_NOT_V 0x800 +#define OVERLAY_MIX_G_OR_NOT_V 0x900 +#define OVERLAY_MIX_NOT_G_OR_V 0xA00 +#define OVERLAY_MIX_G_OR_V 0xB00 +#define OVERLAY_MIX_G_AND_V 0xC00 +#define OVERLAY_MIX_NOT_G_AND_V 0xD00 +#define OVERLAY_MIX_G_AND_NOT_V 0xE00 +#define OVERLAY_MIX_NOT_G_AND_NOT_V 0xF00 +#define OVERLAY_EXCLUSIVE_NORMAL 0x0 +#define OVERLAY_EXCLUSIVE_V_ONLY 0x80000000 + +#define VIDEO_IN_8BPP 0x2 +#define VIDEO_IN_16BPP 0x4 +#define VIDEO_IN_32BPP 0x6 +#define VIDEO_IN_VYUY422 0xB /*16 bpp */ +#define VIDEO_IN_YVYU422 0xC /* 16 bpp */ +#define SCALE_IN_15BPP 0x30000 /* aRGB 1555 */ +#define SCALE_IN_16BPP 0x40000 /* RGB 565 */ +#define SCALE_IN_32BPP 0x60000 /* aRGB 8888 */ +#define SCALE_IN_YUV9 0x90000 /* planar */ +#define SCALE_IN_YUV12 0xA0000 /* planar */ +#define SCALE_IN_VYUY422 0xB0000 /* 16 bpp */ +#define SCALE_IN_YVYU422 0xC0000 /* 16 bpp */ +#define HOST_YUV_APERTURE_UPPER 0x0 +#define HOST_YUV_APERTURE_LOWER 0x20000000 +#define HOST_MEM_MODE_Y 0x40000000 +#define HOST_MEM_MODE_U 0x80000000 +#define HOST_MEM_MODE_V 0xC0000000 +#define HOST_MEM_MODE_NORMAL HOST_YUV_APERTURE_UPPER + +static Chan *ovl_chan; /* Channel of controlling process */ +static int ovl_width; /* Width of input overlay buffer */ +static int ovl_height; /* Height of input overlay buffer */ +static int ovl_format; /* Overlay format */ +static ulong ovl_fib; /* Frame in bytes */ + +enum { + VTGTB1S1 = 0x01, // Asic description for VTB1S1 and GTB1S1. + VT4GTIIC = 0x3A, // asic descr for VT4 and RAGE IIC + GTB1U1 = 0x19, // Asic description for GTB1U1. + GTB1S2 = 0x41, // Asic description for GTB1S2. + GTB2U1 = 0x1A, + GTB2U2 = 0x5A, + GTB2U3 = 0x9A, + GTIIIC1U1 = 0x1B, // 3D RAGE PRO asic descrp. + GTIIIC1U2 = 0x5B, // 3D RAGE PRO asic descrp. + GTIIIC2U1 = 0x1C, // 3D RAGE PRO asic descrp. + GTIIIC2U2 = 0x5C, // 3D RAGE PRO asic descrp. + GTIIIC2U3 = 0x7C, // 3D RAGE PRO asic descrp. + GTBC = 0x3A, // 3D RAGE IIC asic descrp. + LTPRO = 0x9C, // 3D RAGE LT PRO +}; + +/* + * ATI Mach64(CT|ET|G*|V*|L*). + */ +typedef struct Mach64types Mach64types; +struct Mach64types { + ushort m64_id; /* Chip ID */ + int m64_vtgt; /* Is this a VT or GT chipset? */ + ulong m64_ovlclock; /* Max. overlay clock frequency */ + int m64_pro; /* Is this a PRO? */ +}; + +static ulong mach64refclock; +static Mach64types *mach64type; +static int mach64revb; /* Revision B or greater? */ +static ulong mach64overlay; /* Overlay buffer */ + +static Mach64types mach64s[] = { + ('C'<<8)|'T', 0, 1350000, /*?*/ 0, /* 4354: CT */ + ('E'<<8)|'T', 0, 1350000, /*?*/ 0, /* 4554: ET */ + ('G'<<8)|'B', 1, 1250000, 1, /* 4742: 264GT PRO */ + ('G'<<8)|'D', 1, 1250000, 1, /* 4744: 264GT PRO */ + ('G'<<8)|'I', 1, 1250000, 1, /* 4749: 264GT PRO */ + ('G'<<8)|'M', 0, 1350000, 0, /* 474D: Rage XL */ + ('G'<<8)|'P', 1, 1250000, 1, /* 4750: 264GT PRO */ + ('G'<<8)|'Q', 1, 1250000, 1, /* 4751: 264GT PRO */ + ('G'<<8)|'R', 1, 1250000, 1, /* 4752: */ + ('G'<<8)|'T', 1, 800000, 0, /* 4754: 264GT[B] */ + ('G'<<8)|'U', 1, 1000000, 0, /* 4755: 264GT DVD */ + ('G'<<8)|'V', 1, 1000000, 0, /* 4756: Rage2C */ + ('G'<<8)|'Z', 1, 1000000, 0, /* 475A: Rage2C */ + ('V'<<8)|'T', 1, 800000, 0, /* 5654: 264VT/GT/VTB */ + ('V'<<8)|'U', 1, 800000, 0, /* 5655: 264VT3 */ + ('V'<<8)|'V', 1, 1000000, 0, /* 5656: 264VT4 */ + ('L'<<8)|'B', 0, 1350000, 1, /* 4C42: Rage LTPro AGP */ + ('L'<<8)|'I', 0, 1350000, 0, /* 4C49: Rage LTPro AGP */ + ('L'<<8)|'M', 0, 1350000, 0, /* 4C4D: Rage Mobility */ + ('L'<<8)|'P', 0, 1350000, 1, /* 4C50: 264LT PRO */ +}; + + +static int hwfill(VGAscr*, Rectangle, ulong); +static int hwscroll(VGAscr*, Rectangle, Rectangle); +static void initengine(VGAscr*); + +static Pcidev* +mach64xxpci(void) +{ + Pcidev *p; + int i; + + if((p = pcimatch(nil, 0x1002, 0)) == nil) + return nil; + + for (i = 0; i != nelem(mach64s); i++) + if (mach64s[i].m64_id == p->did) { + mach64type = &mach64s[i]; + return p; + } + return nil; +} + +static void +mach64xxenable(VGAscr* scr) +{ + Pcidev *p; + + /* + * Only once, can't be disabled for now. + */ + if(scr->io) + return; + if(p = mach64xxpci()){ + scr->id = p->did; + + /* + * The CT doesn't always have the I/O base address + * in the PCI base registers. There is a way to find + * it via the vendor-specific PCI config space but + * this will do for now. + */ + scr->io = p->mem[1].bar & ~0x03; + + if(scr->io == 0) + scr->io = 0x2EC; + } +} + +static ulong +mach64xxlinear(VGAscr* scr, int* size, int* align) +{ + ulong aperture, osize, oaperture; + int i, oapsize, wasupamem; + Pcidev *p; + + osize = *size; + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + if(p = mach64xxpci()){ + for(i=0; imem); i++){ + if(p->mem[i].size >= *size + && ((p->mem[i].bar & ~0x0F) & (*align-1)) == 0) + break; + } + if(i >= nelem(p->mem)){ + print("vgamach64xx: aperture not found\n"); + return 0; + } + aperture = p->mem[i].bar & ~0x0F; + *size = p->mem[i].size; + } + else + aperture = 0; + + if(wasupamem) + upafree(oaperture, oapsize); + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)) + scr->isupamem = 1; + } + else + scr->isupamem = 1; + + scr->mmio = KADDR(aperture+osize-0x400); + if(oaperture && oaperture != aperture) + print("warning (BUG): redefinition of aperture does not change mach64mmio segment\n"); + addvgaseg("mach64mmio", aperture+osize-BY2PG, BY2PG); + addvgaseg("mach64screen", aperture, osize); + + return aperture; +} + +enum { + CrtcOffPitch = 0x05, + CrtcGenCtl = 0x07, + CurClr0 = 0x0B, /* I/O Select */ + CurClr1 = 0x0C, + CurOffset = 0x0D, + CurHVposn = 0x0E, + CurHVoff = 0x0F, + BusCntl = 0x13, + GenTestCntl = 0x19, + + CrtcHsyncDis = 0x04, + CrtcVsyncDis = 0x08, + + ContextMask = 0x100, /* not accessible via I/O */ + FifoStat, + GuiStat, + DpFrgdClr, + DpBkgdClr, + DpWriteMask, + DpMix, + DpPixWidth, + DpSrc, + ClrCmpCntl, + GuiTrajCntl, + ScLeftRight, + ScTopBottom, + DstOffPitch, + DstYX, + DstHeightWidth, + DstCntl, + DstHeight, + DstBresErr, + DstBresInc, + DstBresDec, + SrcCntl, + SrcHeight1Width1, + SrcHeight2Width2, + SrcYX, + SrcWidth1, + SrcYXstart, + HostCntl, + PatReg0, + PatReg1, + PatCntl, + ScBottom, + ScLeft, + ScRight, + ScTop, + ClrCmpClr, + ClrCmpMask, + DpChainMask, + SrcOffPitch, + LcdIndex, + LcdData, + ClockCntl, + OverlayScaleCntl, + ConfigChipId, + Buf0Pitch, + ScalerBuf0Pitch, + CaptureConfig, + OverlayKeyCntl, + ScalerColourCntl, + ScalerHCoef0, + ScalerHCoef1, + ScalerHCoef2, + ScalerHCoef3, + ScalerHCoef4, + VideoFormat, + Buf0Offset, + ScalerBuf0Offset, + CrtcGenCntl, + OverlayScaleInc, + OverlayYX, + OverlayYXEnd, + ScalerHeightWidth, + HTotalDisp, + VTotalDisp, +}; + +enum { + LCD_ConfigPanel = 0, + LCD_GenCtrl, + LCD_DstnCntl, + LCD_HfbPitchAddr, + LCD_HorzStretch, + LCD_VertStretch, + LCD_ExtVertStretch, + LCD_LtGio, + LCD_PowerMngmnt, + LCD_ZvgPio, + Nlcd, +}; + +#define Bank1 (-0x100) /* 1KB */ + +static int mmoffset[] = { + [HTotalDisp] 0x00, + [VTotalDisp] 0x02, + [CrtcOffPitch] 0x05, + [CrtcGenCntl] 0x07, + [CurClr0] 0x18, + [CurClr1] 0x19, + [CurOffset] 0x1A, + [CurHVposn] 0x1B, + [CurHVoff] 0x1C, + [ClockCntl] 0x24, + [BusCntl] 0x28, + [LcdIndex] 0x29, + [LcdData] 0x2A, + [GenTestCntl] 0x34, + [ConfigChipId] 0x38, + [DstOffPitch] 0x40, + [DstYX] 0x43, + [DstHeight] 0x45, + [DstHeightWidth] 0x46, + [DstBresErr] 0x49, + [DstBresInc] 0x4A, + [DstBresDec] 0x4B, + [DstCntl] 0x4C, + [SrcOffPitch] 0x60, + [SrcYX] 0x63, + [SrcWidth1] 0x64, + [SrcYXstart] 0x69, + [SrcHeight1Width1] 0x66, + [SrcHeight2Width2] 0x6C, + [SrcCntl] 0x6D, + [HostCntl] 0x90, + [PatReg0] 0xA0, + [PatReg1] 0xA1, + [PatCntl] 0xA2, + [ScLeft] 0xA8, + [ScRight] 0xA9, + [ScLeftRight] 0xAA, + [ScTop] 0xAB, + [ScBottom] 0xAC, + [ScTopBottom] 0xAD, + [DpBkgdClr] 0xB0, + [DpFrgdClr] 0xB1, + [DpWriteMask] 0xB2, + [DpChainMask] 0xB3, + [DpPixWidth] 0xB4, + [DpMix] 0xB5, + [DpSrc] 0xB6, + [ClrCmpClr] 0xC0, + [ClrCmpMask] 0xC1, + [ClrCmpCntl] 0xC2, + [FifoStat] 0xC4, + [ContextMask] 0xC8, + [GuiTrajCntl] 0xCC, + [GuiStat] 0xCE, + + /* Bank1 */ + [OverlayYX] Bank1 + 0x00, + [OverlayYXEnd] Bank1 + 0x01, + [OverlayKeyCntl] Bank1 + 0x06, + [OverlayScaleInc] Bank1 + 0x08, + [OverlayScaleCntl] Bank1 + 0x09, + [ScalerHeightWidth] Bank1 + 0x0A, + [ScalerBuf0Offset] Bank1 + 0x0D, + [ScalerBuf0Pitch] Bank1 + 0x0F, + [VideoFormat] Bank1 + 0x12, + [CaptureConfig] Bank1 + 0x14, + [Buf0Offset] Bank1 + 0x20, + [Buf0Pitch] Bank1 + 0x23, + [ScalerColourCntl] Bank1 + 0x54, + [ScalerHCoef0] Bank1 + 0x55, + [ScalerHCoef1] Bank1 + 0x56, + [ScalerHCoef2] Bank1 + 0x57, + [ScalerHCoef3] Bank1 + 0x58, + [ScalerHCoef4] Bank1 + 0x59, +}; + +static ulong +ior32(VGAscr* scr, int r) +{ + if(scr->io == 0x2EC || scr->io == 0x1C8) + return inl((r<<10)+scr->io); + if(r >= 0x100 && scr->mmio != nil) + return scr->mmio[mmoffset[r]]; + return inl((mmoffset[r]<<2)+scr->io); +} + +static void +iow32(VGAscr* scr, int r, ulong l) +{ + if(scr->io == 0x2EC || scr->io == 0x1C8) + outl(((r)<<10)+scr->io, l); + else if(r >= 0x100 && scr->mmio != nil) + scr->mmio[mmoffset[r]] = l; + else + outl((mmoffset[r]<<2)+scr->io, l); +} + +static ulong +lcdr32(VGAscr *scr, ulong r) +{ + ulong or; + + or = ior32(scr, LcdIndex); + iow32(scr, LcdIndex, (or&~0x0F) | (r&0x0F)); + return ior32(scr, LcdData); +} + +static void +lcdw32(VGAscr *scr, ulong r, ulong v) +{ + ulong or; + + or = ior32(scr, LcdIndex); + iow32(scr, LcdIndex, (or&~0x0F) | (r&0x0F)); + iow32(scr, LcdData, v); +} + +static void +mach64xxcurdisable(VGAscr* scr) +{ + ulong r; + + r = ior32(scr, GenTestCntl); + iow32(scr, GenTestCntl, r & ~0x80); +} + +static void +mach64xxcurload(VGAscr* scr, Cursor* curs) +{ + uchar *p; + int i, y; + ulong c, s, m, r; + + /* + * Disable the cursor. + */ + r = ior32(scr, GenTestCntl); + iow32(scr, GenTestCntl, r & ~0x80); + + p = KADDR(scr->aperture); + p += scr->storage; + + /* + * Initialise the 64x64 cursor RAM array. + * The cursor mode gives the following truth table: + * p1 p0 colour + * 0 0 Cursor Colour 0 + * 0 1 Cursor Colour 1 + * 1 0 Transparent + * 1 1 Complement + * Put the cursor into the top-right of the 64x64 array. + */ + for(y = 0; y < 16; y++){ + for(i = 0; i < (64-16)/8; i++){ + *p++ = 0xAA; + *p++ = 0xAA; + } + + c = (curs->clr[2*y]<<8)|curs->clr[y*2 + 1]; + s = (curs->set[2*y]<<8)|curs->set[y*2 + 1]; + + m = 0x00000000; + for(i = 0; i < 16; i++){ + if(s & (1<<(15-i))) + m |= 0x01<<(2*i); + else if(c & (1<<(15-i))){ + /* nothing to do */ + } + else + m |= 0x02<<(2*i); + } + *p++ = m; + *p++ = m>>8; + *p++ = m>>16; + *p++ = m>>24; + } + memset(p, 0xAA, (64-16)*16); + + /* + * Set the cursor hotpoint and enable the cursor. + */ + scr->offset = curs->offset; + iow32(scr, GenTestCntl, 0x80|r); +} + +static int +ptalmostinrect(Point p, Rectangle r) +{ + return p.x>=r.min.x && p.x<=r.max.x && + p.y>=r.min.y && p.y<=r.max.y; +} + +/* + * If necessary, translate the rectangle physr + * some multiple of [dx dy] so that it includes p. + * Return 1 if the rectangle changed. + */ +static int +screenpan(Point p, Rectangle *physr, int dx, int dy) +{ + int d; + + if(ptalmostinrect(p, *physr)) + return 0; + + if(p.y < physr->min.y){ + d = physr->min.y - (p.y&~(dy-1)); + physr->min.y -= d; + physr->max.y -= d; + } + if(p.y > physr->max.y){ + d = ((p.y+dy-1)&~(dy-1)) - physr->max.y; + physr->min.y += d; + physr->max.y += d; + } + + if(p.x < physr->min.x){ + d = physr->min.x - (p.x&~(dx-1)); + physr->min.x -= d; + physr->max.x -= d; + } + if(p.x > physr->max.x){ + d = ((p.x+dx-1)&~(dx-1)) - physr->max.x; + physr->min.x += d; + physr->max.x += d; + } + return 1; +} + +static int +mach64xxcurmove(VGAscr* scr, Point p) +{ + int x, xo, y, yo; + int dx; + ulong off, pitch; + + /* + * If the point we want to display is outside the current + * screen rectangle, pan the screen to display it. + * + * We have to move in 64-bit chunks. + */ + if(scr->gscreen->depth == 24) + dx = (64*3)/24; + else + dx = 64 / scr->gscreen->depth; + + if(panning && screenpan(p, &physgscreenr, dx, 1)){ + off = (physgscreenr.min.y*Dx(scr->gscreen->r)+physgscreenr.min.x)/dx; + pitch = Dx(scr->gscreen->r)/8; + iow32(scr, CrtcOffPitch, (pitch<<22)|off); + } + + p.x -= physgscreenr.min.x; + p.y -= physgscreenr.min.y; + + /* + * Mustn't position the cursor offscreen even partially, + * or it disappears. Therefore, if x or y is -ve, adjust the + * cursor presets instead. If y is negative also have to + * adjust the starting offset. + */ + if((x = p.x+scr->offset.x) < 0){ + xo = x; + x = 0; + } + else + xo = 0; + if((y = p.y+scr->offset.y) < 0){ + yo = y; + y = 0; + } + else + yo = 0; + + iow32(scr, CurHVoff, ((64-16-yo)<<16)|(64-16-xo)); + iow32(scr, CurOffset, scr->storage/8 + (-yo*2)); + iow32(scr, CurHVposn, (y<<16)|x); + + return 0; +} + +static void +mach64xxcurenable(VGAscr* scr) +{ + ulong r, storage; + + mach64xxenable(scr); + if(scr->io == 0) + return; + + r = ior32(scr, GenTestCntl); + iow32(scr, GenTestCntl, r & ~0x80); + + iow32(scr, CurClr0, (Pwhite<<24)|(Pwhite<<16)|(Pwhite<<8)|Pwhite); + iow32(scr, CurClr1, (Pblack<<24)|(Pblack<<16)|(Pblack<<8)|Pblack); + + /* + * Find a place for the cursor data in display memory. + * Must be 64-bit aligned. + */ + storage = (scr->gscreen->width*BY2WD*scr->gscreen->r.max.y+7)/8; + iow32(scr, CurOffset, storage); + scr->storage = storage*8; + + /* + * Cursor goes in the top right corner of the 64x64 array + * so the horizontal and vertical presets are 64-16. + */ + iow32(scr, CurHVposn, (0<<16)|0); + iow32(scr, CurHVoff, ((64-16)<<16)|(64-16)); + + /* + * Load, locate and enable the 64x64 cursor. + */ + mach64xxcurload(scr, &arrow); + mach64xxcurmove(scr, ZP); + iow32(scr, GenTestCntl, 0x80|r); +} + +static void +waitforfifo(VGAscr *scr, int entries) +{ + int x; + + x = 0; + while((ior32(scr, FifoStat)&0xFF) > (0x8000>>entries) && x++ < 1000000) + ; + if(x >= 1000000) + iprint("fifo %d stat %.8lux %.8lux scrio %.8lux mmio %p scr %p pc %luX\n", entries, ior32(scr, FifoStat), scr->mmio[mmoffset[FifoStat]], scr->io, scr->mmio, scr, getcallerpc(&scr)); +} + +static void +waitforidle(VGAscr *scr) +{ + int x; + + waitforfifo(scr, 16); + x = 0; + while((ior32(scr, GuiStat)&1) && x++ < 1000000) + ; + if(x >= 1000000) + iprint("idle stat %.8lux %.8lux scrio %.8lux mmio %p scr %p pc %luX\n", ior32(scr, GuiStat), scr->mmio[mmoffset[GuiStat]], scr->io, scr->mmio, scr, getcallerpc(&scr)); +} + +static void +resetengine(VGAscr *scr) +{ + ulong x; + x = ior32(scr, GenTestCntl); + iow32(scr, GenTestCntl, x&~0x100); + iow32(scr, GenTestCntl, x|0x100); + iow32(scr, BusCntl, ior32(scr, BusCntl)|0x00A00000); +} + +static void +init_overlayclock(VGAscr *scr) +{ + uchar *cc, save, pll_ref_div, pll_vclk_cntl, vclk_post_div, + vclk_fb_div, ecp_div; + int i; + ulong dotclock; + + /* Taken from GLX */ + /* Get monitor dotclock, check for Overlay Scaler clock limit */ + cc = (uchar *)&scr->mmio[mmoffset[ClockCntl]]; + save = cc[1]; i = cc[0] & 3; + cc[1] = 2<<2; pll_ref_div = cc[2]; + cc[1] = 5<<2; pll_vclk_cntl = cc[2]; + cc[1] = 6<<2; vclk_post_div = (cc[2]>>(i+i)) & 3; + cc[1] = (7+i)<<2; vclk_fb_div = cc[2]; + + dotclock = 2 * mach64refclock * vclk_fb_div / + (pll_ref_div * (1 << vclk_post_div)); + /* ecp_div: 0=dotclock, 1=dotclock/2, 2=dotclock/4 */ + ecp_div = dotclock / mach64type->m64_ovlclock; + if (ecp_div>2) ecp_div = 2; + + /* Force a scaler clock factor of 1 if refclock * + * is unknown (VCLK_SRC not PLLVCLK) */ + if ((pll_vclk_cntl & 0x03) != 0x03) + ecp_div = 0; + if ((pll_vclk_cntl & 0x30) != ecp_div<<4) { + cc[1] = (5<<2)|2; + cc[2] = (pll_vclk_cntl&0xCF) | (ecp_div<<4); + } + + /* Restore PLL Register Index */ + cc[1] = save; +} + +static void +initengine(VGAscr *scr) +{ + ulong pitch; + uchar *bios; + ushort table; + + pitch = Dx(scr->gscreen->r)/8; + if(scr->gscreen->depth == 24) + pitch *= 3; + + resetengine(scr); + waitforfifo(scr, 14); + iow32(scr, ContextMask, ~0); + iow32(scr, DstOffPitch, pitch<<22); + iow32(scr, DstYX, 0); + iow32(scr, DstHeight, 0); + iow32(scr, DstBresErr, 0); + iow32(scr, DstBresInc, 0); + iow32(scr, DstBresDec, 0); + iow32(scr, DstCntl, 0x23); + iow32(scr, SrcOffPitch, pitch<<22); + iow32(scr, SrcYX, 0); + iow32(scr, SrcHeight1Width1, 1); + iow32(scr, SrcYXstart, 0); + iow32(scr, SrcHeight2Width2, 1); + iow32(scr, SrcCntl, 0x01); + + waitforfifo(scr, 13); + iow32(scr, HostCntl, 0); + iow32(scr, PatReg0, 0); + iow32(scr, PatReg1, 0); + iow32(scr, PatCntl, 0); + iow32(scr, ScLeft, 0); + iow32(scr, ScTop, 0); + iow32(scr, ScBottom, 0xFFFF); + iow32(scr, ScRight, 0xFFFF); + iow32(scr, DpBkgdClr, 0); + iow32(scr, DpFrgdClr, ~0); + iow32(scr, DpWriteMask, ~0); + iow32(scr, DpMix, 0x70003); + iow32(scr, DpSrc, 0x00010100); + + waitforfifo(scr, 3); + iow32(scr, ClrCmpClr, 0); + iow32(scr, ClrCmpMask, ~0); + iow32(scr, ClrCmpCntl, 0); + + waitforfifo(scr, 2); + switch(scr->gscreen->depth){ + case 8: + case 24: /* [sic] */ + iow32(scr, DpPixWidth, 0x00020202); + iow32(scr, DpChainMask, 0x8080); + break; + case 16: + iow32(scr, DpPixWidth, 0x00040404); + iow32(scr, DpChainMask, 0x8410); + break; + case 32: + iow32(scr, DpPixWidth, 0x00060606); + iow32(scr, DpChainMask, 0x8080); + break; + } + + /* Get the base freq from the BIOS */ + bios = KADDR(0xC000); + table = *(ushort *)(bios + 0x48); + table = *(ushort *)(bios + table + 0x10); + switch (*(ushort *)(bios + table + 0x08)) { + case 2700: + mach64refclock = 270000; + break; + case 2863: + case 2864: + mach64refclock = 286363; + break; + case 2950: + mach64refclock = 294989; + break; + case 1432: + default: + mach64refclock = 143181; + break ; + } + + /* Figure out which revision this chip is */ + switch ((scr->mmio[mmoffset[ConfigChipId]] >> 24) & 0xFF) { + case VTGTB1S1: + case GTB1U1: + case GTB1S2: + case GTB2U1: + case GTB2U2: + case GTB2U3: + case GTBC: + case GTIIIC1U1: + case GTIIIC1U2: + case GTIIIC2U1: + case GTIIIC2U2: + case GTIIIC2U3: + case LTPRO: + mach64revb = 1; + break; + default: + mach64revb = 0; + break; + } + + waitforidle(scr); +} + +static int +mach64hwfill(VGAscr *scr, Rectangle r, ulong sval) +{ + ulong pitch; + ulong ctl; + +if(drawdebug) + iprint("hwfill %R val %lux...\n", r, sval); + + /* shouldn't happen */ + if(scr->io == 0x2EC || scr->io == 0x1C8 || scr->io == 0) + return 0; + + pitch = Dx(scr->gscreen->r)/8; + ctl = 1|2; /* left-to-right, top-to-bottom */ + if(scr->gscreen->depth == 24){ + r.min.x *= 3; + r.max.x *= 3; + pitch *= 3; + ctl |= (1<<7)|(((r.min.x/4)%6)<<8); + } + + waitforfifo(scr, 11); + iow32(scr, DpFrgdClr, sval); + iow32(scr, DpWriteMask, 0xFFFFFFFF); + iow32(scr, DpMix, 0x00070003); + iow32(scr, DpSrc, 0x00000111); + iow32(scr, ClrCmpCntl, 0x00000000); + iow32(scr, ScLeftRight, 0x1FFF0000); + iow32(scr, ScTopBottom, 0x1FFF0000); + iow32(scr, DstOffPitch, pitch<<22); + iow32(scr, DstCntl, ctl); + iow32(scr, DstYX, (r.min.x<<16)|r.min.y); + iow32(scr, DstHeightWidth, (Dx(r)<<16)|Dy(r)); + + waitforidle(scr); + return 1; +} + +static int +mach64hwscroll(VGAscr *scr, Rectangle r, Rectangle sr) +{ + ulong pitch; + Point dp, sp; + ulong ctl; + int dx, dy; + + dx = Dx(r); + dy = Dy(r); + pitch = Dx(scr->gscreen->r)/8; + if(scr->gscreen->depth == 24){ + dx *= 3; + pitch *= 3; + r.min.x *= 3; + sr.min.x *= 3; + } + + ctl = 0; + if(r.min.x <= sr.min.x){ + ctl |= 1; + dp.x = r.min.x; + sp.x = sr.min.x; + }else{ + dp.x = r.min.x+dx-1; + sp.x = sr.min.x+dx-1; + } + + if(r.min.y <= sr.min.y){ + ctl |= 2; + dp.y = r.min.y; + sp.y = sr.min.y; + }else{ + dp.y = r.min.y+dy-1; + sp.y = sr.min.y+dy-1; + } + + if(scr->gscreen->depth == 24) + ctl |= (1<<7)|(((dp.x/4)%6)<<8); + + waitforfifo(scr, 6); + iow32(scr, ScLeftRight, 0x1FFF0000); + iow32(scr, ScTopBottom, 0x1FFF0000); + iow32(scr, DpWriteMask, 0xFFFFFFFF); + iow32(scr, DpMix, 0x00070003); + iow32(scr, DpSrc, 0x00000300); + iow32(scr, ClrCmpCntl, 0x00000000); + + waitforfifo(scr, 8); + iow32(scr, SrcOffPitch, pitch<<22); + iow32(scr, SrcCntl, 0x00000000); + iow32(scr, SrcYX, (sp.x<<16)|sp.y); + iow32(scr, SrcWidth1, dx); + iow32(scr, DstOffPitch, pitch<<22); + iow32(scr, DstCntl, ctl); + + iow32(scr, DstYX, (dp.x<<16)|dp.y); + iow32(scr, DstHeightWidth, (dx<<16)|dy); + + waitforidle(scr); + + return 1; +} + +/* + * This should work, but doesn't. + * It messes up the screen timings for some reason. + */ +static void +mach64blank(VGAscr *scr, int blank) +{ + ulong ctl; + + ctl = ior32(scr, CrtcGenCtl) & ~(CrtcHsyncDis|CrtcVsyncDis); + if(blank) + ctl |= CrtcHsyncDis|CrtcVsyncDis; + iow32(scr, CrtcGenCtl, ctl); +} + +/* + * We squirrel away whether the LCD and/or CRT were + * on when we were called to blank the screen, and + * restore the old state. If we are called to blank the + * screen when it is already blank, we don't update the state. + * Such a call sequence should not happen, though. + * + * We could try forcing the chip into power management + * mode instead, but I'm not sure how that would interact + * with screen updates going on while the screen is blanked. + */ +static void +mach64lcdblank(VGAscr *scr, int blank) +{ + static int crtlcd; + ulong x; + + if(blank) { + x = lcdr32(scr, LCD_GenCtrl); + if(x & 3) { + crtlcd = x & 3; + lcdw32(scr, LCD_GenCtrl, x&~3); + } + } else { + if(crtlcd == 0) + crtlcd = 2; /* lcd only */ + x = lcdr32(scr, LCD_GenCtrl); + lcdw32(scr, LCD_GenCtrl, x | crtlcd); + } +} + +static void +mach64xxdrawinit(VGAscr *scr) +{ + if(scr->io > 0x2FF){ + initengine(scr); + scr->fill = mach64hwfill; + scr->scroll = mach64hwscroll; + } +/* scr->blank = mach64blank; */ + switch(scr->id){ + default: + break; + case ('L'<<8)|'B': /* 4C42: Rage 3D LTPro */ + case ('L'<<8)|'I': /* 4C49: Rage 3D LTPro */ + case ('L'<<8)|'M': /* 4C4D: Rage Mobility */ + case ('L'<<8)|'P': /* 4C50: Rage 3D LTPro */ + scr->blank = mach64lcdblank; + hwblank = 1; + break; + } +} + +static void +ovl_configure(VGAscr *scr, Chan *c, char **field) +{ + int w, h; + char *format; + + w = (int)strtol(field[1], nil, 0); + h = (int)strtol(field[2], nil, 0); + format = field[3]; + + if (c != ovl_chan) + error(Einuse); + if (strcmp(format, "YUYV")) + error(Eunsupportedformat); + + ovl_width = w; + ovl_height = h; + ovl_fib = w * h * sizeof(ushort); + + waitforidle(scr); + scr->mmio[mmoffset[BusCntl]] |= 0x08000000; /* Enable regblock 1 */ + scr->mmio[mmoffset[OverlayScaleCntl]] = + SCALE_ZERO_EXTEND|SCALE_RED_TEMP_6500K| + SCALE_HORZ_BLEND|SCALE_VERT_BLEND; + scr->mmio[mmoffset[!mach64revb? Buf0Pitch: ScalerBuf0Pitch]] = w; + scr->mmio[mmoffset[CaptureConfig]] = + SCALER_FRAME_READ_MODE_FULL| + SCALER_BUF_MODE_SINGLE| + SCALER_BUF_NEXT_0; + scr->mmio[mmoffset[OverlayKeyCntl]] = !mach64revb? + OVERLAY_MIX_ALWAYS_V|(OVERLAY_EXCLUSIVE_NORMAL << 28): + 0x011; + + if (mach64type->m64_pro) { + waitforfifo(scr, 6); + + /* set the scaler co-efficient registers */ + scr->mmio[mmoffset[ScalerColourCntl]] = + (0x00) | (0x10 << 8) | (0x10 << 16); + scr->mmio[mmoffset[ScalerHCoef0]] = + (0x00) | (0x20 << 8); + scr->mmio[mmoffset[ScalerHCoef1]] = + (0x0D) | (0x20 << 8) | (0x06 << 16) | (0x0D << 24); + scr->mmio[mmoffset[ScalerHCoef2]] = + (0x0D) | (0x1C << 8) | (0x0A << 16) | (0x0D << 24); + scr->mmio[mmoffset[ScalerHCoef3]] = + (0x0C) | (0x1A << 8) | (0x0E << 16) | (0x0C << 24); + scr->mmio[mmoffset[ScalerHCoef4]] = + (0x0C) | (0x14 << 8) | (0x14 << 16) | (0x0C << 24); + } + + waitforfifo(scr, 3); + scr->mmio[mmoffset[VideoFormat]] = SCALE_IN_YVYU422 | + (!mach64revb? 0xC: 0); + + if (mach64overlay == 0) + mach64overlay = scr->storage + 64 * 64 * sizeof(uchar); + scr->mmio[mmoffset[!mach64revb? Buf0Offset: ScalerBuf0Offset]] = + mach64overlay; +} + +static void +ovl_enable(VGAscr *scr, Chan *c, char **field) +{ + int x, y, w, h; + long h_inc, v_inc; + + x = (int)strtol(field[1], nil, 0); + y = (int)strtol(field[2], nil, 0); + w = (int)strtol(field[3], nil, 0); + h = (int)strtol(field[4], nil, 0); + + if (x < 0 || x + w > physgscreenr.max.x || + y < 0 || y + h > physgscreenr.max.y) + error(Ebadarg); + + if (c != ovl_chan) + error(Einuse); + if (scr->mmio[mmoffset[CrtcGenCntl]] & 1) { /* double scan enable */ + y *= 2; + h *= 2; + } + + waitforfifo(scr, 2); + scr->mmio[mmoffset[OverlayYX]] = + ((x & 0xFFFF) << 16) | (y & 0xFFFF); + scr->mmio[mmoffset[OverlayYXEnd]] = + (((x + w) & 0xFFFF) << 16) | ((y + h) & 0xFFFF); + + h_inc = (ovl_width << 12) / (w >> 1); /* ??? */ + v_inc = (ovl_height << 12) / h; + waitforfifo(scr, 2); + scr->mmio[mmoffset[OverlayScaleInc]] = + ((h_inc & 0xFFFF) << 16) | (v_inc & 0xFFFF); + scr->mmio[mmoffset[ScalerHeightWidth]] = + ((ovl_width & 0xFFFF) << 16) | (ovl_height & 0xFFFF); + waitforidle(scr); + scr->mmio[mmoffset[OverlayScaleCntl]] |= + (SCALE_ENABLE|OVERLAY_ENABLE); +} + +static void +ovl_status(VGAscr *scr, Chan *, char **field) +{ + pprint("%s: %s %.4uX, VT/GT %s, PRO %s, ovlclock %d, rev B %s, refclock %ld\n", + scr->dev->name, field[0], mach64type->m64_id, + mach64type->m64_vtgt? "yes": "no", + mach64type->m64_pro? "yes": "no", + mach64type->m64_ovlclock, + mach64revb? "yes": "no", + mach64refclock); + pprint("%s: storage @%.8luX, aperture @%8.ulX, ovl buf @%.8ulX\n", + scr->dev->name, scr->storage, scr->aperture, + mach64overlay); +} + +static void +ovl_openctl(VGAscr *, Chan *c, char **) +{ + if (ovl_chan) + error(Einuse); + ovl_chan = c; +} + +static void +ovl_closectl(VGAscr *scr, Chan *c, char **) +{ + if (c != ovl_chan) return; + + waitforidle(scr); + scr->mmio[mmoffset[OverlayScaleCntl]] &= + ~(SCALE_ENABLE|OVERLAY_ENABLE); + ovl_chan = nil; + ovl_width = ovl_height = ovl_fib = 0; +} + +enum +{ + CMclosectl, + CMconfigure, + CMenable, + CMopenctl, + CMstatus, +}; + +static void (*ovl_cmds[])(VGAscr *, Chan *, char **) = +{ + [CMclosectl] ovl_closectl, + [CMconfigure] ovl_configure, + [CMenable] ovl_enable, + [CMopenctl] ovl_openctl, + [CMstatus] ovl_status, +}; + +static Cmdtab mach64xxcmd[] = +{ + CMclosectl, "closectl", 1, + CMconfigure, "configure", 4, + CMenable, "enable", 5, + CMopenctl, "openctl", 1, + CMstatus, "status", 1, +}; + +static void +mach64xxovlctl(VGAscr *scr, Chan *c, void *a, int n) +{ + Cmdbuf *cb; + Cmdtab *ct; + + if(!mach64type->m64_vtgt) + error(Enodev); + + if(!scr->overlayinit){ + scr->overlayinit = 1; + init_overlayclock(scr); + } + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + + ct = lookupcmd(cb, mach64xxcmd, nelem(mach64xxcmd)); + + ovl_cmds[ct->index](scr, c, cb->f); + + poperror(); + free(cb); +} + +static int +mach64xxovlwrite(VGAscr *scr, void *a, int len, vlong offs) +{ + uchar *src; + int _len; + + if (ovl_chan == nil) return len; /* Acts as a /dev/null */ + + /* Calculate the destination address */ + _len = len; + src = (uchar *)a; + while (len > 0) { + ulong _offs; + int nb; + + _offs = (ulong)(offs % ovl_fib); + nb = (_offs + len > ovl_fib)? ovl_fib - _offs: len; + memmove((uchar *)KADDR(scr->aperture + mach64overlay + _offs), + src, nb); + offs += nb; + src += nb; + len -= nb; + } + return _len; +} + +VGAdev vgamach64xxdev = { + "mach64xx", + + mach64xxenable, /* enable */ + 0, /* disable */ + 0, /* page */ + mach64xxlinear, /* linear */ + mach64xxdrawinit, /* drawinit */ + 0, + mach64xxovlctl, /* overlay control */ + mach64xxovlwrite, /* write the overlay */ +}; + +VGAcur vgamach64xxcur = { + "mach64xxhwgc", + + mach64xxcurenable, /* enable */ + mach64xxcurdisable, /* disable */ + mach64xxcurload, /* load */ + mach64xxcurmove, /* move */ + + 1 /* doespanning */ +}; + diff --git a/os/pc/vgamga2164w.c b/os/pc/vgamga2164w.c new file mode 100644 index 00000000..6332236a --- /dev/null +++ b/os/pc/vgamga2164w.c @@ -0,0 +1,289 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +/* + * Matrox Millennium and Matrox Millennium II. + * Matrox MGA-2064W, MGA-2164W 3D graphics accelerators. + * Texas Instruments Tvp3026 RAMDAC. + */ + +enum { + /* pci chip manufacturer */ + MATROX = 0x102B, + + /* pci chip device ids */ + MGA2064 = 0x0519, + MGA2164 = 0x051B, + MGA2164AGP = 0x051F +}; + +static Pcidev* +mgapcimatch(void) +{ + Pcidev *p; + + p = pcimatch(nil, MATROX, MGA2164AGP); + if(p == nil) { + p = pcimatch(nil, MATROX, MGA2164); + if(p == nil) + p = pcimatch(nil, MATROX, MGA2064); + } + return p; +} + +static ulong +mga2164wlinear(VGAscr* scr, int* size, int* align) +{ + ulong aperture, oaperture; + int oapsize, wasupamem; + Pcidev *p; + + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + if(p = mgapcimatch()){ + aperture = p->mem[p->did==MGA2064? 1 : 0].bar & ~0x0F; + *size = (p->did==MGA2064? 8 :16)*1024*1024; + } + else + aperture = 0; + + if(wasupamem) { + if(oaperture == aperture) + return oaperture; + upafree(oaperture, oapsize); + } + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)) { + aperture = oaperture; + scr->isupamem = 1; + } + else + scr->isupamem = 0; + } + else + scr->isupamem = 1; + + return aperture; +} + +static void +mga2164wenable(VGAscr* scr) +{ + Pcidev *p; + int size, align, immio; + ulong aperture; + + /* + * Only once, can't be disabled for now. + * scr->io holds the virtual address of + * the MMIO registers. + */ + if(scr->io) + return; + + p = mgapcimatch(); + if(p == nil) + return; + + immio = p->did==MGA2064? 0 : 1; + scr->io = upamalloc(p->mem[immio].bar & ~0x0F, p->mem[immio].size, 0); + if(scr->io == 0) + return; + addvgaseg("mga2164wmmio", scr->io, p->mem[immio].size); + + scr->io = (ulong)KADDR(scr->io); + + /* need to map frame buffer here too, so vga can find memory size */ + size = (p->did==MGA2064? 8 :16)*1024*1024; + align = 0; + aperture = mga2164wlinear(scr, &size, &align); + if(aperture) { + scr->aperture = aperture; + scr->apsize = size; + addvgaseg("mga2164wscreen", aperture, size); + } +} + +enum { + Index = 0x00, /* Index */ + Data = 0x0A, /* Data */ + + CaddrW = 0x04, /* Colour Write Address */ + Cdata = 0x05, /* Colour Data */ + + Cctl = 0x09, /* Direct Cursor Control */ + Cram = 0x0B, /* Cursor Ram Data */ + Cxlsb = 0x0C, /* Cursor X LSB */ + Cxmsb = 0x0D, /* Cursor X MSB */ + Cylsb = 0x0E, /* Cursor Y LSB */ + Cymsb = 0x0F, /* Cursor Y MSB */ + + Icctl = 0x06, /* Indirect Cursor Control */ +}; + +static void +tvp3026disable(VGAscr* scr) +{ + uchar *tvp3026; + + if(scr->io == 0) + return; + tvp3026 = KADDR(scr->io+0x3C00); + + /* + * Make sure cursor is off + * and direct control enabled. + */ + *(tvp3026+Index) = Icctl; + *(tvp3026+Data) = 0x90; + *(tvp3026+Cctl) = 0x00; +} + +static void +tvp3026load(VGAscr* scr, Cursor* curs) +{ + int x, y; + uchar *tvp3026; + + if(scr->io == 0) + return; + tvp3026 = KADDR(scr->io+0x3C00); + + /* + * Make sure cursor is off by initialising the cursor + * control to defaults. + * Write to the indirect control register to make sure + * direct register is enabled and upper 2 bits of cursor + * RAM address are 0. + * Put 0 in index register for lower 8 bits of cursor RAM address. + */ + tvp3026disable(scr); + *(tvp3026+Index) = 0; + + /* + * Initialise the 64x64 cursor RAM array. There are 2 planes, + * p0 and p1. Data is written 8 pixels per byte, with p0 in the + * first 512 bytes of the array and p1 in the second. + * The cursor is set in 3-colour mode which gives the following + * truth table: + * p1 p0 colour + * 0 0 transparent + * 0 1 cursor colour 0 + * 1 0 cursor colour 1 + * 1 1 cursor colour 2 + * Put the cursor into the top-left of the 64x64 array. + * The 0,0 cursor point is bottom-right, so positioning will + * have to take that into account. + */ + for(y = 0; y < 64; y++){ + for(x = 0; x < 64/8; x++){ + if(x < 16/8 && y < 16) + *(tvp3026+Cram) = curs->clr[x+y*2]; + else + *(tvp3026+Cram) = 0x00; + } + } + for(y = 0; y < 64; y++){ + for(x = 0; x < 64/8; x++){ + if(x < 16/8 && y < 16) + *(tvp3026+Cram) = curs->set[x+y*2]; + else + *(tvp3026+Cram) = 0x00; + } + } + + /* + * Initialise the cursor hotpoint + * and enable the cursor in 3-colour mode. + */ + scr->offset.x = 64+curs->offset.x; + scr->offset.y = 64+curs->offset.y; + *(tvp3026+Cctl) = 0x01; +} + +static int +tvp3026move(VGAscr* scr, Point p) +{ + int x, y; + uchar *tvp3026; + + if(scr->io == 0) + return 1; + tvp3026 = KADDR(scr->io+0x3C00); + + x = p.x+scr->offset.x; + y = p.y+scr->offset.y; + + *(tvp3026+Cxlsb) = x & 0xFF; + *(tvp3026+Cxmsb) = (x>>8) & 0x0F; + *(tvp3026+Cylsb) = y & 0xFF; + *(tvp3026+Cymsb) = (y>>8) & 0x0F; + + return 0; +} + +static void +tvp3026enable(VGAscr* scr) +{ + int i; + uchar *tvp3026; + + if(scr->io == 0) + return; + tvp3026 = KADDR(scr->io+0x3C00); + + tvp3026disable(scr); + + /* + * Overscan colour, + * cursor colour 1 (white), + * cursor colour 2, 3 (black). + */ + *(tvp3026+CaddrW) = 0x00; + for(i = 0; i < 6; i++) + *(tvp3026+Cdata) = Pwhite; + for(i = 0; i < 6; i++) + *(tvp3026+Cdata) = Pblack; + + /* + * Load, locate and enable the + * 64x64 cursor in 3-colour mode. + */ + tvp3026load(scr, &arrow); + tvp3026move(scr, ZP); + *(tvp3026+Cctl) = 0x01; +} + +VGAdev vgamga2164wdev = { + "mga2164w", + + mga2164wenable, /* enable */ + 0, /* disable */ + 0, /* page */ + mga2164wlinear, /* linear */ +}; + +VGAcur vgamga2164wcur = { + "mga2164whwgc", + + tvp3026enable, + tvp3026disable, + tvp3026load, + tvp3026move, +}; diff --git a/os/pc/vgamga4xx.c b/os/pc/vgamga4xx.c new file mode 100644 index 00000000..7ddeebf1 --- /dev/null +++ b/os/pc/vgamga4xx.c @@ -0,0 +1,603 @@ + +/* + * Matrox G200, G400 and G450. + * see /sys/src/cmd/aux/vga/mga4xx.c + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +enum { + MATROX = 0x102B, + MGA4xx = 0x0525, + MGA200 = 0x0521, + + Kilo = 1024, + Meg = 1024*1024, + + FCOL = 0x1c24, + FXRIGHT = 0x1cac, + FXLEFT = 0x1ca8, + YDST = 0x1c90, + YLEN = 0x1c5c, + DWGCTL = 0x1c00, + DWG_TRAP = 0x04, + DWG_BITBLT = 0x08, + DWG_ILOAD = 0x09, + DWG_LINEAR = 0x0080, + DWG_SOLID = 0x0800, + DWG_ARZERO = 0x1000, + DWG_SGNZERO = 0x2000, + DWG_SHIFTZERO = 0x4000, + DWG_REPLACE = 0x000C0000, + DWG_REPLACE2 = (DWG_REPLACE | 0x40), + DWG_XOR = 0x00060010, + DWG_BFCOL = 0x04000000, + DWG_BMONOWF = 0x08000000, + DWG_TRANSC = 0x40000000, + SRCORG = 0x2cb4, + PITCH = 0x1c8c, + DSTORG = 0x2cb8, + PLNWRT = 0x1c1c, + ZORG = 0x1c0c, + MACCESS = 0x1c04, + STATUS = 0x1e14, + FXBNDRY = 0x1C84, + CXBNDRY = 0x1C80, + YTOP = 0x1C98, + YBOT = 0x1C9C, + YDSTLEN = 0x1C88, + AR0 = 0x1C60, + AR1 = 0x1C64, + AR2 = 0x1C68, + AR3 = 0x1C6C, + AR4 = 0x1C70, + AR5 = 0x1C74, + SGN = 0x1C58, + SGN_SCANLEFT = 1, + SGN_SCANRIGHT = 0, + SGN_SDY_POSITIVE = 0, + SGN_SDY_NEGATIVE = 4, + + GO = 0x0100, + FIFOSTATUS = 0x1E10, + CACHEFLUSH = 0x1FFF, + + CRTCEXTIDX = 0x1FDE, /* CRTC Extension Index */ + CRTCEXTDATA = 0x1FDF, /* CRTC Extension Data */ + + FILL_OPERAND = 0x800c7804, +}; + +static Pcidev* +mgapcimatch(void) +{ + Pcidev* p; + + p = pcimatch(nil, MATROX, MGA4xx); + if (p == nil) + p = pcimatch(nil, MATROX, MGA200); + return p; +} + +static ulong +mga4xxlinear(VGAscr* scr, int* size, int* align) +{ + ulong aperture, oaperture; + int oapsize, wasupamem; + Pcidev * p; + + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + if(p = mgapcimatch()){ + aperture = p->mem[0].bar & ~0x0F; + if(p->did == MGA4xx) + *size = 32*Meg; + else + *size = 8*Meg; + } + else + aperture = 0; + + if(wasupamem) { + if(oaperture == aperture) + return oaperture; + upafree(oaperture, oapsize); + } + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)) { + aperture = oaperture; + scr->isupamem = 1; + } + else + scr->isupamem = 0; + } + else + scr->isupamem = 1; + + return aperture; +} + +static void +mgawrite8(VGAscr* scr, int index, uchar val) +{ + ((uchar*)scr->io)[index] = val; +} + +static uchar +mgaread8(VGAscr* scr, int index) +{ + return ((uchar*)scr->io)[index]; +} + +static uchar +crtcextset(VGAscr* scr, int index, uchar set, uchar clr) +{ + uchar tmp; + + mgawrite8(scr, CRTCEXTIDX, index); + tmp = mgaread8(scr, CRTCEXTDATA); + mgawrite8(scr, CRTCEXTIDX, index); + mgawrite8(scr, CRTCEXTDATA, (tmp & ~clr) | set); + + return tmp; +} + +static void +mga4xxenable(VGAscr* scr) +{ + Pcidev * pci; + int size, align; + ulong aperture; + int i, n, k; + uchar * p; + uchar x[16]; + uchar crtcext3; + + /* + * Only once, can't be disabled for now. + * scr->io holds the virtual address of + * the MMIO registers. + */ + if(scr->io) + return; + + pci = mgapcimatch(); + if(pci == nil) + return; + + scr->io = upamalloc(pci->mem[1].bar & ~0x0F, 16*1024, 0); + if(scr->io == 0) + return; + + addvgaseg("mga4xxmmio", scr->io, pci->mem[1].size); + + scr->io = (ulong)KADDR(scr->io); + + /* need to map frame buffer here too, so vga can find memory size */ + size = 8*Meg; + align = 0; + aperture = mga4xxlinear(scr, &size, &align); + if(aperture) { + scr->aperture = aperture; + addvgaseg("mga4xxscreen", aperture, size); + + /* Find out how much memory is here, some multiple of 2 Meg */ + + /* First Set MGA Mode ... */ + crtcext3 = crtcextset(scr, 3, 0x80, 0x00); + + p = (uchar*)aperture; + n = (size / Meg) / 2; + for (i = 0; i < n; i++) { + k = (2*i+1)*Meg; + p[k] = 0; + p[k] = i+1; + *((uchar*)(scr->io + CACHEFLUSH)) = 0; + x[i] = p[k]; + } + for(i = 1; i < n; i++) + if(x[i] != i+1) + break; + scr->apsize = 2*i*Meg; + + crtcextset(scr, 3, crtcext3, 0xff); + } +} + +enum { + Index = 0x00, /* Index */ + Data = 0x0A, /* Data */ + + Cxlsb = 0x0C, /* Cursor X LSB */ + Cxmsb = 0x0D, /* Cursor X MSB */ + Cylsb = 0x0E, /* Cursor Y LSB */ + Cymsb = 0x0F, /* Cursor Y MSB */ + + Icuradrl = 0x04, /* Cursor Base Address Low */ + Icuradrh = 0x05, /* Cursor Base Address High */ + Icctl = 0x06, /* Indirect Cursor Control */ +}; + +static void +dac4xxdisable(VGAscr* scr) +{ + uchar * dac4xx; + + if(scr->io == 0) + return; + + dac4xx = KADDR(scr->io+0x3C00); + + *(dac4xx+Index) = Icctl; + *(dac4xx+Data) = 0x00; +} + +static void +dac4xxload(VGAscr* scr, Cursor* curs) +{ + int y; + uchar * p; + uchar * dac4xx; + + if(scr->io == 0) + return; + + dac4xx = KADDR(scr->io+0x3C00); + + dac4xxdisable(scr); + + p = KADDR(scr->storage); + for(y = 0; y < 64; y++){ + *p++ = 0; *p++ = 0; *p++ = 0; + *p++ = 0; *p++ = 0; *p++ = 0; + if(y <16){ + *p++ = curs->set[1+y*2]|curs->clr[1+2*y]; + *p++ = curs->set[y*2]|curs->clr[2*y]; + } else{ + *p++ = 0; *p++ = 0; + } + + *p++ = 0; *p++ = 0; *p++ = 0; + *p++ = 0; *p++ = 0; *p++ = 0; + if(y <16){ + *p++ = curs->set[1+y*2]; + *p++ = curs->set[y*2]; + } else{ + *p++ = 0; *p++ = 0; + } + } + scr->offset.x = 64 + curs->offset.x; + scr->offset.y = 64 + curs->offset.y; + + *(dac4xx+Index) = Icctl; + *(dac4xx+Data) = 0x03; +} + +static int +dac4xxmove(VGAscr* scr, Point p) +{ + int x, y; + uchar * dac4xx; + + if(scr->io == 0) + return 1; + + dac4xx = KADDR(scr->io + 0x3C00); + + x = p.x + scr->offset.x; + y = p.y + scr->offset.y; + + *(dac4xx+Cxlsb) = x & 0xFF; + *(dac4xx+Cxmsb) = (x>>8) & 0x0F; + + *(dac4xx+Cylsb) = y & 0xFF; + *(dac4xx+Cymsb) = (y>>8) & 0x0F; + + return 0; +} + +static void +dac4xxenable(VGAscr* scr) +{ + uchar * dac4xx; + ulong storage; + + if(scr->io == 0) + return; + dac4xx = KADDR(scr->io+0x3C00); + + dac4xxdisable(scr); + + storage = (scr->apsize - 4096) & ~0x3ff; + + *(dac4xx+Index) = Icuradrl; + *(dac4xx+Data) = 0xff & (storage >> 10); + *(dac4xx+Index) = Icuradrh; + *(dac4xx+Data) = 0xff & (storage >> 18); + + scr->storage = (ulong) KADDR((ulong)scr->aperture + (ulong)storage); + + /* Show X11-Like Cursor */ + *(dac4xx+Index) = Icctl; + *(dac4xx+Data) = 0x03; + + /* Cursor Color 0 : White */ + *(dac4xx+Index) = 0x08; + *(dac4xx+Data) = 0xff; + *(dac4xx+Index) = 0x09; + *(dac4xx+Data) = 0xff; + *(dac4xx+Index) = 0x0a; + *(dac4xx+Data) = 0xff; + + /* Cursor Color 1 : Black */ + *(dac4xx+Index) = 0x0c; + *(dac4xx+Data) = 0x00; + *(dac4xx+Index) = 0x0d; + *(dac4xx+Data) = 0x00; + *(dac4xx+Index) = 0x0e; + *(dac4xx+Data) = 0x00; + + /* Cursor Color 2 : Red */ + *(dac4xx+Index) = 0x10; + *(dac4xx+Data) = 0xff; + *(dac4xx+Index) = 0x11; + *(dac4xx+Data) = 0x00; + *(dac4xx+Index) = 0x12; + *(dac4xx+Data) = 0x00; + + /* + * Load, locate and enable the + * 64x64 cursor in X11 mode. + */ + dac4xxload(scr, &arrow); + dac4xxmove(scr, ZP); +} + +static void +mga4xxblank(VGAscr* scr, int blank) +{ + char * cp; + uchar * mga; + uchar seq1, crtcext1; + + /* blank = 0 -> turn screen on */ + /* blank = 1 -> turn screen off */ + + if(scr->io == 0) + return; + mga = KADDR(scr->io); + + if (blank == 0) { + seq1 = 0x00; + crtcext1 = 0x00; + } else { + seq1 = 0x20; + crtcext1 = 0x10; /* Default value ... : standby */ + cp = getconf("*dpms"); + if (cp) { + if (cistrcmp(cp, "standby") == 0) { + crtcext1 = 0x10; + } else if (cistrcmp(cp, "suspend") == 0) { + crtcext1 = 0x20; + } else if (cistrcmp(cp, "off") == 0) { + crtcext1 = 0x30; + } + } + } + + *(mga + 0x1fc4) = 1; + seq1 |= *(mga + 0x1fc5) & ~0x20; + *(mga + 0x1fc5) = seq1; + + *(mga + 0x1fde) = 1; + crtcext1 |= *(mga + 0x1fdf) & ~0x30; + *(mga + 0x1fdf) = crtcext1; +} + +static void +mgawrite32(uchar * mga, ulong reg, ulong val) +{ + ulong * l; + + l = (ulong *)(&mga[reg]); + l[0] = val; +} + +static ulong +mgaread32(uchar * mga, ulong reg) +{ + return *((ulong *)(&mga[reg])); +} + +static int +mga4xxfill(VGAscr* scr, Rectangle r, ulong color) +{ + uchar * mga; + + /* Constant Shaded Trapezoids / Rectangle Fills */ + if(scr->io == 0) + return 0; + mga = KADDR(scr->io); + + mgawrite32(mga, DWGCTL, 0); + mgawrite32(mga, FCOL, color); + mgawrite32(mga, FXRIGHT, r.max.x); + mgawrite32(mga, FXLEFT, r.min.x); + mgawrite32(mga, YDST, r.min.y); + mgawrite32(mga, YLEN, Dy(r)); + mgawrite32(mga, DWGCTL + GO, FILL_OPERAND); + + while (mgaread32(mga, STATUS) & 0x00010000) + ; + return 1; +} + +#define mga_fifo(n) do {} while ((mgaread32(mga, FIFOSTATUS) & 0xFF) < (n)) + +static int +mga4xxscroll(VGAscr* scr, Rectangle r_dst, Rectangle r_src) +{ + uchar * mga; + ulong pitch, y; + ulong width, height, start, end, scandir; + int ydir; + + /* Two-operand Bitblts */ + if(scr->io == 0) + return 0; + + mga = KADDR(scr->io); + + pitch = Dx(scr->gscreen->r); + + mgawrite32(mga, DWGCTL, 0); + + scandir = 0; + + height = abs(Dy(r_src)); + width = abs(Dx(r_src)); + + assert(height == abs(Dy(r_dst))); + assert(width == abs(Dx(r_dst))); + + if ((r_src.min.y == r_dst.min.y) && (r_src.min.x == r_dst.min.x)) + { + if (0) + print("move x,y to x,y !\n"); + return 1; + } + + ydir = 1; + if (r_dst.min.y > r_src.min.y) + { + if (0) + print("ydir = -1\n"); + ydir = -1; + scandir |= 4; // Blit UP + } + + if (r_dst.min.x > r_src.min.x) + { + if (0) + print("xdir = -1\n"); + scandir |= 1; // Blit Left + } + + mga_fifo(4); + if (scandir) + { + mgawrite32(mga, DWGCTL, DWG_BITBLT | DWG_SHIFTZERO | + DWG_SGNZERO | DWG_BFCOL | DWG_REPLACE); + mgawrite32(mga, SGN, scandir); + } else + { + mgawrite32(mga, DWGCTL, DWG_BITBLT | DWG_SHIFTZERO | + DWG_BFCOL | DWG_REPLACE); + } + mgawrite32(mga, AR5, ydir * pitch); + + width--; + start = end = r_src.min.x + (r_src.min.y * pitch); + if ((scandir & 1) == 1) + { + start += width; + } else + { + end += width; + } + + y = r_dst.min.y; + if ((scandir & 4) == 4) + { + start += (height - 1) * pitch; + end += (height - 1) * pitch; + y += (height - 1); + } + + mga_fifo(4); + mgawrite32(mga, AR0, end); + mgawrite32(mga, AR3, start); + mgawrite32(mga, FXBNDRY, ((r_dst.min.x+width)<<16) | r_dst.min.x); + mgawrite32(mga, YDSTLEN + GO, (y << 16) | height); + + if (1) + { + while (mgaread32(mga, STATUS) & 0x00010000) + ; + } + + return 1; +} + +static void +mga4xxdrawinit(VGAscr* scr) +{ + uchar * mga; + Pcidev* p; + + p = pcimatch(nil, MATROX, MGA4xx); + if (p == nil) + return ; + + if(scr->io == 0) + return; + mga = KADDR(scr->io); + + mgawrite32(mga, SRCORG, 0); + mgawrite32(mga, DSTORG, 0); + mgawrite32(mga, ZORG, 0); + mgawrite32(mga, PLNWRT, ~0); + mgawrite32(mga, FCOL, 0xffff0000); + mgawrite32(mga, CXBNDRY, 0xFFFF0000); + mgawrite32(mga, YTOP, 0); + mgawrite32(mga, YBOT, 0x01FFFFFF); + mgawrite32(mga, PITCH, Dx(scr->gscreen->r) & ((1 << 13) - 1)); + switch(scr->gscreen->depth) { + case 8: + mgawrite32(mga, MACCESS, 0); + break; + case 32: + mgawrite32(mga, MACCESS, 2); + break; + default: + return; /* depth not supported ! */ + } + scr->fill = mga4xxfill; + scr->scroll = mga4xxscroll; + scr->blank = mga4xxblank; +} + +VGAdev vgamga4xxdev = { + "mga4xx", + + mga4xxenable, /* enable */ + 0, /* disable */ + 0, /* page */ + mga4xxlinear, /* linear */ + mga4xxdrawinit, +}; + +VGAcur vgamga4xxcur = { + "mga4xxhwgc", + dac4xxenable, + dac4xxdisable, + dac4xxload, + dac4xxmove, +}; diff --git a/os/pc/vganeomagic.c b/os/pc/vganeomagic.c new file mode 100644 index 00000000..ba25a581 --- /dev/null +++ b/os/pc/vganeomagic.c @@ -0,0 +1,541 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +typedef struct CursorNM CursorNM; +struct CursorNM { + int enable; + int x; + int y; + int colour1; + int colour2; + int addr; +}; + +static ulong +neomagiclinear(VGAscr* scr, int* size, int* align) +{ + ulong aperture, oaperture; + int oapsize, wasupamem; + Pcidev *p; + + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + aperture = 0; + if(p = pcimatch(nil, 0x10C8, 0)){ + switch(p->did){ + case 0x0004: /* MagicGraph 128XD */ + case 0x0005: /* MagicMedia 256AV */ + case 0x0006: /* MagicMedia 256ZX */ + aperture = p->mem[0].bar & ~0x0F; + *size = p->mem[0].size; + break; + default: + break; + } + } + + if(wasupamem){ + if(oaperture == aperture) + return oaperture; + upafree(oaperture, oapsize); + } + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)){ + aperture = oaperture; + scr->isupamem = 1; + } + else + scr->isupamem = 0; + } + else + scr->isupamem = 1; + + return aperture; +} + +static void +neomagicenable(VGAscr* scr) +{ + Pcidev *p; + int align, curoff, size, vmsize; + ulong aperture; + + /* + * Only once, can't be disabled for now. + * scr->io holds the physical address of the cursor registers + * in the MMIO space. This may need to change for older chips + * which have the MMIO space offset in the framebuffer region. + */ + if(scr->io) + return; + if(p = pcimatch(nil, 0x10C8, 0)){ + switch(p->did){ + case 0x0004: /* MagicGraph 128XD */ + curoff = 0x100; + vmsize = 2048*1024; + break; + case 0x0005: /* MagicMedia 256AV */ + curoff = 0x1000; + vmsize = 2560*1024; + break; + case 0x0006: /* MagicMedia 256ZX */ + curoff = 0x1000; + vmsize = 4096*1024; + break; + default: + return; + } + } + else + return; + scr->io = upamalloc(p->mem[1].bar & ~0x0F, p->mem[1].size, 0); + if(scr->io == 0) + return; + addvgaseg("neomagicmmio", scr->io, p->mem[1].size); + scr->mmio = KADDR(scr->io); + + /* + * Find a place for the cursor data in display memory. + * 2 cursor images might be needed, 1KB each so use the + * last 2KB of the framebuffer. + */ + scr->storage = vmsize-2*1024; + scr->io += curoff; + + size = p->mem[0].size; + align = 0; + aperture = neomagiclinear(scr, &size, &align); + if(aperture) { + scr->aperture = aperture; + scr->apsize = size; + addvgaseg("neomagicscreen", aperture, size); + } +} + +static void +neomagiccurdisable(VGAscr* scr) +{ + CursorNM *cursornm; + + if(scr->io == 0) + return; + cursornm = KADDR(scr->io); + cursornm->enable = 0; +} + +static void +neomagicinitcursor(VGAscr* scr, int xo, int yo, int index) +{ + uchar *p; + uint p0, p1; + int x, y; + + p = KADDR(scr->aperture); + p += scr->storage + index*1024; + + for(y = yo; y < 16; y++){ + p0 = scr->set[2*y]; + p1 = scr->set[2*y+1]; + if(xo){ + p0 = (p0<>(8-xo)); + p1 <<= xo; + } + *p++ = p0; + *p++ = p1; + + for(x = 16; x < 64; x += 8) + *p++ = 0x00; + + p0 = scr->clr[2*y]|scr->set[2*y]; + p1 = scr->clr[2*y+1]|scr->set[2*y+1]; + if(xo){ + p0 = (p0<>(8-xo)); + p1 <<= xo; + } + *p++ = p0; + *p++ = p1; + + for(x = 16; x < 64; x += 8) + *p++ = 0x00; + } + while(y < 64+yo){ + for(x = 0; x < 64; x += 8){ + *p++ = 0x00; + *p++ = 0x00; + } + y++; + } +} + +static void +neomagiccurload(VGAscr* scr, Cursor* curs) +{ + CursorNM *cursornm; + + if(scr->io == 0) + return; + cursornm = KADDR(scr->io); + + cursornm->enable = 0; + memmove(&scr->Cursor, curs, sizeof(Cursor)); + neomagicinitcursor(scr, 0, 0, 0); + cursornm->enable = 1; +} + +static int +neomagiccurmove(VGAscr* scr, Point p) +{ + CursorNM *cursornm; + int addr, index, x, xo, y, yo; + + if(scr->io == 0) + return 1; + cursornm = KADDR(scr->io); + + index = 0; + if((x = p.x+scr->offset.x) < 0){ + xo = -x; + x = 0; + } + else + xo = 0; + if((y = p.y+scr->offset.y) < 0){ + yo = -y; + y = 0; + } + else + yo = 0; + + if(xo || yo){ + index = 1; + neomagicinitcursor(scr, xo, yo, index); + } + addr = ((scr->storage+(1024*index))>>10) & 0xFFF; + addr = ((addr & 0x00F)<<8)|((addr>>4) & 0xFF); + if(cursornm->addr != addr) + cursornm->addr = addr; + + cursornm->x = x; + cursornm->y = y; + + return 0; +} + +static void +neomagiccurenable(VGAscr* scr) +{ + CursorNM *cursornm; + + neomagicenable(scr); + if(scr->io == 0) + return; + cursornm = KADDR(scr->io); + cursornm->enable = 0; + + /* + * Cursor colours. + */ + cursornm->colour1 = (Pblack<<16)|(Pblack<<8)|Pblack; + cursornm->colour2 = (Pwhite<<16)|(Pwhite<<8)|Pwhite; + + /* + * Load, locate and enable the 64x64 cursor. + */ + neomagiccurload(scr, &arrow); + neomagiccurmove(scr, ZP); + cursornm->enable = 1; +} + +static int neomagicbltflags; + +/* registers */ +enum { + BltStat = 0, + BltCntl = 1, + XPColor = 2, + FGColor = 3, + BGColor = 4, + Pitch = 5, + ClipLT = 6, + ClipRB = 7, + SrcBitOff = 8, + SrcStartOff = 9, + + DstStartOff = 11, + XYExt = 12, + + PageCntl = 20, + PageBase, + PostBase, + PostPtr, + DataPtr, +}; + +/* flags */ +enum { + NEO_BS0_BLT_BUSY = 0x00000001, + NEO_BS0_FIFO_AVAIL = 0x00000002, + NEO_BS0_FIFO_PEND = 0x00000004, + + NEO_BC0_DST_Y_DEC = 0x00000001, + NEO_BC0_X_DEC = 0x00000002, + NEO_BC0_SRC_TRANS = 0x00000004, + NEO_BC0_SRC_IS_FG = 0x00000008, + NEO_BC0_SRC_Y_DEC = 0x00000010, + NEO_BC0_FILL_PAT = 0x00000020, + NEO_BC0_SRC_MONO = 0x00000040, + NEO_BC0_SYS_TO_VID = 0x00000080, + + NEO_BC1_DEPTH8 = 0x00000100, + NEO_BC1_DEPTH16 = 0x00000200, + NEO_BC1_DEPTH24 = 0x00000300, + NEO_BC1_X_320 = 0x00000400, + NEO_BC1_X_640 = 0x00000800, + NEO_BC1_X_800 = 0x00000c00, + NEO_BC1_X_1024 = 0x00001000, + NEO_BC1_X_1152 = 0x00001400, + NEO_BC1_X_1280 = 0x00001800, + NEO_BC1_X_1600 = 0x00001c00, + NEO_BC1_DST_TRANS = 0x00002000, + NEO_BC1_MSTR_BLT = 0x00004000, + NEO_BC1_FILTER_Z = 0x00008000, + + NEO_BC2_WR_TR_DST = 0x00800000, + + NEO_BC3_SRC_XY_ADDR = 0x01000000, + NEO_BC3_DST_XY_ADDR = 0x02000000, + NEO_BC3_CLIP_ON = 0x04000000, + NEO_BC3_FIFO_EN = 0x08000000, + NEO_BC3_BLT_ON_ADDR = 0x10000000, + NEO_BC3_SKIP_MAPPING = 0x80000000, + + NEO_MODE1_DEPTH8 = 0x0100, + NEO_MODE1_DEPTH16 = 0x0200, + NEO_MODE1_DEPTH24 = 0x0300, + NEO_MODE1_X_320 = 0x0400, + NEO_MODE1_X_640 = 0x0800, + NEO_MODE1_X_800 = 0x0c00, + NEO_MODE1_X_1024 = 0x1000, + NEO_MODE1_X_1152 = 0x1400, + NEO_MODE1_X_1280 = 0x1800, + NEO_MODE1_X_1600 = 0x1c00, + NEO_MODE1_BLT_ON_ADDR = 0x2000, +}; + +/* Raster Operations */ +enum { + GXclear = 0x000000, /* 0x0000 */ + GXand = 0x080000, /* 0x1000 */ + GXandReverse = 0x040000, /* 0x0100 */ + GXcopy = 0x0c0000, /* 0x1100 */ + GXandInvert = 0x020000, /* 0x0010 */ + GXnoop = 0x0a0000, /* 0x1010 */ + GXxor = 0x060000, /* 0x0110 */ + GXor = 0x0e0000, /* 0x1110 */ + GXnor = 0x010000, /* 0x0001 */ + GXequiv = 0x090000, /* 0x1001 */ + GXinvert = 0x050000, /* 0x0101 */ + GXorReverse = 0x0d0000, /* 0x1101 */ + GXcopyInvert = 0x030000, /* 0x0011 */ + GXorInverted = 0x0b0000, /* 0x1011 */ + GXnand = 0x070000, /* 0x0111 */ + GXset = 0x0f0000, /* 0x1111 */ +}; + +static void +waitforidle(VGAscr *scr) +{ + ulong *mmio; + long x; + + mmio = scr->mmio; + x = 0; + while((mmio[BltStat] & NEO_BS0_BLT_BUSY) && x++ < 1000000) + ; + //if(x >= 1000000) + // iprint("idle stat %lud scrmmio %.8lux scr %p pc %luX\n", mmio[BltStat], scr->mmio, scr, getcallerpc(&scr)); +} + +static void +waitforfifo(VGAscr *scr, int entries) +{ + ulong *mmio; + long x; + + mmio = scr->mmio; + x = 0; + while(((mmio[BltStat]>>8) < entries) && x++ < 1000000) + ; + //if(x >= 1000000) + // iprint("fifo stat %d scrmmio %.8lux scr %p pc %luX\n", mmio[BltStat]>>8, scr->mmio, scr, getcallerpc(&scr)); + /* DirectFB says the above doesn't work. if so... */ + /* waitforidle(scr); */ +} + +static int +neomagichwfill(VGAscr *scr, Rectangle r, ulong sval) +{ + ulong *mmio; + + mmio = scr->mmio; + + waitforfifo(scr, 1); + mmio[FGColor] = sval; + waitforfifo(scr, 3); + mmio[BltCntl] = neomagicbltflags + | NEO_BC3_FIFO_EN + | NEO_BC0_SRC_IS_FG + | NEO_BC3_SKIP_MAPPING + | GXcopy; + mmio[DstStartOff] = scr->aperture + + r.min.y*scr->gscreen->width*BY2WD + + r.min.x*scr->gscreen->depth/BI2BY; + mmio[XYExt] = (Dy(r) << 16) | (Dx(r) & 0xffff); + waitforidle(scr); + return 1; +} + +static int +neomagichwscroll(VGAscr *scr, Rectangle r, Rectangle sr) +{ + ulong *mmio; + int pitch, pixel; + + mmio = scr->mmio; + + pitch = scr->gscreen->width*BY2WD; + pixel = scr->gscreen->depth/BI2BY; + + waitforfifo(scr, 4); + if (r.min.y < sr.min.y || (r.min.y == sr.min.y && r.min.x < sr.min.x)) { + /* start from upper-left */ + mmio[BltCntl] = neomagicbltflags + | NEO_BC3_FIFO_EN + | NEO_BC3_SKIP_MAPPING + | GXcopy; + mmio[SrcStartOff] = scr->aperture + + sr.min.y*pitch + sr.min.x*pixel; + mmio[DstStartOff] = scr->aperture + + r.min.y*pitch + r.min.x*pixel; + } else { + /* start from lower-right */ + mmio[BltCntl] = neomagicbltflags + | NEO_BC0_X_DEC + | NEO_BC0_DST_Y_DEC + | NEO_BC0_SRC_Y_DEC + | NEO_BC3_FIFO_EN + | NEO_BC3_SKIP_MAPPING + | GXcopy; + mmio[SrcStartOff] = scr->aperture + + (sr.max.y-1)*pitch + (sr.max.x-1)*pixel; + mmio[DstStartOff] = scr->aperture + + (r.max.y-1)*pitch + (r.max.x-1)*pixel; + } + mmio[XYExt] = (Dy(r) << 16) | (Dx(r) & 0xffff); + waitforidle(scr); + return 1; +} + +static void +neomagicdrawinit(VGAscr *scr) +{ + ulong *mmio; + uint bltmode, pitch; + + mmio = scr->mmio; + + pitch = scr->gscreen->width*BY2WD; + + neomagicbltflags = bltmode = 0; + + switch(scr->gscreen->depth) { + case 8: + bltmode |= NEO_MODE1_DEPTH8; + neomagicbltflags |= NEO_BC1_DEPTH8; + break; + case 16: + bltmode |= NEO_MODE1_DEPTH16; + neomagicbltflags |= NEO_BC1_DEPTH16; + break; + case 24: /* I can't get it to work, and XFree86 doesn't either. */ + default: /* give up */ + return; + } + + switch(Dx(scr->gscreen->r)) { + case 320: + bltmode |= NEO_MODE1_X_320; + neomagicbltflags |= NEO_BC1_X_320; + break; + case 640: + bltmode |= NEO_MODE1_X_640; + neomagicbltflags |= NEO_BC1_X_640; + break; + case 800: + bltmode |= NEO_MODE1_X_800; + neomagicbltflags |= NEO_BC1_X_800; + break; + case 1024: + bltmode |= NEO_MODE1_X_1024; + neomagicbltflags |= NEO_BC1_X_1024; + break; + case 1152: + bltmode |= NEO_MODE1_X_1152; + neomagicbltflags |= NEO_BC1_X_1152; + break; + case 1280: + bltmode |= NEO_MODE1_X_1280; + neomagicbltflags |= NEO_BC1_X_1280; + break; + case 1600: + bltmode |= NEO_MODE1_X_1600; + neomagicbltflags |= NEO_BC1_X_1600; + break; + default: + /* don't worry about it */ + break; + } + + waitforidle(scr); + mmio[BltStat] = bltmode << 16; + mmio[Pitch] = (pitch << 16) | (pitch & 0xffff); + + scr->fill = neomagichwfill; + scr->scroll = neomagichwscroll; +} + +VGAdev vganeomagicdev = { + "neomagic", + + neomagicenable, + nil, + nil, + neomagiclinear, + neomagicdrawinit, +}; + +VGAcur vganeomagiccur = { + "neomagichwgc", + + neomagiccurenable, + neomagiccurdisable, + neomagiccurload, + neomagiccurmove, +}; + diff --git a/os/pc/vganvidia.c b/os/pc/vganvidia.c new file mode 100644 index 00000000..e057ef1f --- /dev/null +++ b/os/pc/vganvidia.c @@ -0,0 +1,373 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +enum { + Pramin = 0x00710000, + Pramdac = 0x00680000, + Fifo = 0x00800000, + Pgraph = 0x00400000 +}; + +enum { + hwCurPos = Pramdac + 0x0300, + hwCurImage = Pramin + (0x00010000 - 0x0800), +}; + +/* Nvidia is good about backwards compatibility -- any did >= 0x20 is fine */ +static Pcidev* +nvidiapci(void) +{ + Pcidev *p; + + p = nil; + while((p = pcimatch(p, 0x10DE, 0)) != nil){ + if(p->did >= 0x20 && p->ccrb == 3) /* video card */ + return p; + } + return nil; +} + +static ulong +nvidialinear(VGAscr* scr, int* size, int* align) +{ + Pcidev *p; + int oapsize, wasupamem; + ulong aperture, oaperture; + + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + aperture = 0; + if(p = nvidiapci()){ + aperture = p->mem[1].bar & ~0x0F; + *size = p->mem[1].size; + } + + if(wasupamem){ + if(oaperture == aperture) + return oaperture; + upafree(oaperture, oapsize); + } + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)){ + aperture = oaperture; + scr->isupamem = 1; + } + else + scr->isupamem = 0; + } + else + scr->isupamem = 1; + + return aperture; +} + +static void +nvidiaenable(VGAscr* scr) +{ + Pcidev *p; + ulong aperture; + int align, size; + + /* + * Only once, can't be disabled for now. + * scr->io holds the physical address of + * the MMIO registers. + */ + if(scr->io) + return; + p = nvidiapci(); + if(p == nil) + return; + scr->id = p->did; + + scr->io = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0); + if(scr->io == 0) + return; + addvgaseg("nvidiammio", scr->io, p->mem[0].size); + + size = p->mem[1].size; + align = 0; + aperture = nvidialinear(scr, &size, &align); + if(aperture){ + scr->aperture = aperture; + scr->apsize = size; + addvgaseg("nvidiascreen", aperture, size); + } +} + +static void +nvidiacurdisable(VGAscr* scr) +{ + if(scr->io == 0) + return; + + vgaxo(Crtx, 0x31, vgaxi(Crtx, 0x31) & ~0x01); +} + +static void +nvidiacurload(VGAscr* scr, Cursor* curs) +{ + ulong* p; + int i,j; + ushort c,s; + ulong tmp; + + if(scr->io == 0) + return; + + vgaxo(Crtx, 0x31, vgaxi(Crtx, 0x31) & ~0x01); + + p = KADDR(scr->io + hwCurImage); + + for(i=0; i<16; i++) { + switch(scr->id){ + default: + c = (curs->clr[2 * i] << 8) | curs->clr[2 * i+1]; + s = (curs->set[2 * i] << 8) | curs->set[2 * i+1]; + break; + case 0x171: /* for Geforece4 MX bug, K.Okamoto */ + case 0x181: + c = (curs->clr[2 * i+1] << 8) | curs->clr[2 * i]; + s = (curs->set[2 * i+1] << 8) | curs->set[2 * i]; + break; + } + tmp = 0; + for (j=0; j<16; j++){ + if(s&0x8000) + tmp |= 0x80000000; + else if(c&0x8000) + tmp |= 0xFFFF0000; + if (j&0x1){ + *p++ = tmp; + tmp = 0; + } else { + tmp>>=16; + } + c<<=1; + s<<=1; + } + for (j=0; j<8; j++) + *p++ = 0; + } + for (i=0; i<256; i++) + *p++ = 0; + + scr->offset = curs->offset; + vgaxo(Crtx, 0x31, vgaxi(Crtx, 0x31) | 0x01); + + return; +} + +static int +nvidiacurmove(VGAscr* scr, Point p) +{ + ulong* cursorpos; + + if(scr->io == 0) + return 1; + + cursorpos = KADDR(scr->io + hwCurPos); + *cursorpos = ((p.y+scr->offset.y)<<16)|((p.x+scr->offset.x) & 0xFFFF); + + return 0; +} + +static void +nvidiacurenable(VGAscr* scr) +{ + nvidiaenable(scr); + if(scr->io == 0) + return; + + vgaxo(Crtx, 0x1F, 0x57); + + nvidiacurload(scr, &arrow); + nvidiacurmove(scr, ZP); + + vgaxo(Crtx, 0x31, vgaxi(Crtx, 0x31) | 0x01); +} + +enum { + RopFifo = 0x00000000, + ClipFifo = 0x00002000, + PattFifo = 0x00004000, + BltFifo = 0x00008000, + BitmapFifo = 0x0000A000, +}; + +enum { + RopRop3 = RopFifo + 0x300, + + ClipTopLeft = ClipFifo + 0x300, + ClipWidthHeight = ClipFifo + 0x304, + + PattShape = PattFifo + 0x0308, + PattColor0 = PattFifo + 0x0310, + PattColor1 = PattFifo + 0x0314, + PattMonochrome0 = PattFifo + 0x0318, + PattMonochrome1 = PattFifo + 0x031C, + + BltTopLeftSrc = BltFifo + 0x0300, + BltTopLeftDst = BltFifo + 0x0304, + BltWidthHeight = BltFifo + 0x0308, + + BitmapColor1A = BitmapFifo + 0x03FC, + BitmapURect0TopLeft = BitmapFifo + 0x0400, + BitmapURect0WidthHeight = BitmapFifo + 0x0404, +}; + +static void +waitforidle(VGAscr *scr) +{ + ulong* pgraph; + int x; + + pgraph = KADDR(scr->io + Pgraph); + + x = 0; + while(pgraph[0x00000700/4] & 0x01 && x++ < 1000000) + ; + + if(x >= 1000000) + iprint("idle stat %lud scrio %.8lux scr %p pc %luX\n", *pgraph, scr->io, scr, getcallerpc(&scr)); +} + +static void +waitforfifo(VGAscr *scr, int fifo, int entries) +{ + ushort* fifofree; + int x; + + x = 0; + fifofree = KADDR(scr->io + Fifo + fifo + 0x10); + + while(((*fifofree >> 2) < entries) && x++ < 1000000) + ; + + if(x >= 1000000) + iprint("fifo stat %d scrio %.8lux scr %p pc %luX\n", *fifofree, scr->io, scr, getcallerpc(&scr)); +} + +static int +nvidiahwfill(VGAscr *scr, Rectangle r, ulong sval) +{ + ulong* fifo; + + fifo = KADDR(scr->io + Fifo); + + waitforfifo(scr, BitmapFifo, 1); + + fifo[BitmapColor1A/4] = sval; + + waitforfifo(scr, BitmapFifo, 2); + + fifo[BitmapURect0TopLeft/4] = (r.min.x << 16) | r.min.y; + fifo[BitmapURect0WidthHeight/4] = (Dx(r) << 16) | Dy(r); + + waitforidle(scr); + + return 1; +} + +static int +nvidiahwscroll(VGAscr *scr, Rectangle r, Rectangle sr) +{ + ulong* fifo; + + fifo = KADDR(scr->io + Fifo); + + waitforfifo(scr, BltFifo, 3); + + fifo[BltTopLeftSrc/4] = (sr.min.y << 16) | sr.min.x; + fifo[BltTopLeftDst/4] = (r.min.y << 16) | r.min.x; + fifo[BltWidthHeight/4] = (Dy(r) << 16) | Dx(r); + + waitforidle(scr); + + return 1; +} + +void +nvidiablank(VGAscr*, int blank) +{ + uchar seq1, crtc1A; + + seq1 = vgaxi(Seqx, 1) & ~0x20; + crtc1A = vgaxi(Crtx, 0x1A) & ~0xC0; + + if(blank){ + seq1 |= 0x20; +// crtc1A |= 0xC0; + crtc1A |= 0x80; + } + + vgaxo(Seqx, 1, seq1); + vgaxo(Crtx, 0x1A, crtc1A); +} + +static void +nvidiadrawinit(VGAscr *scr) +{ + ulong* fifo; + + fifo = KADDR(scr->io + Fifo); + + waitforfifo(scr, ClipFifo, 2); + + fifo[ClipTopLeft/4] = 0x0; + fifo[ClipWidthHeight/4] = 0x80008000; + + waitforfifo(scr, PattFifo, 5); + + fifo[PattShape/4] = 0; + fifo[PattColor0/4] = 0xffffffff; + fifo[PattColor1/4] = 0xffffffff; + fifo[PattMonochrome0/4] = 0xffffffff; + fifo[PattMonochrome1/4] = 0xffffffff; + + waitforfifo(scr, RopFifo, 1); + + fifo[RopRop3/4] = 0xCC; + + waitforidle(scr); + + scr->blank = nvidiablank; + hwblank = 1; + scr->fill = nvidiahwfill; + scr->scroll = nvidiahwscroll; +} + +VGAdev vganvidiadev = { + "nvidia", + + nvidiaenable, + nil, + nil, + nvidialinear, + nvidiadrawinit, +}; + +VGAcur vganvidiacur = { + "nvidiahwgc", + + nvidiacurenable, + nvidiacurdisable, + nvidiacurload, + nvidiacurmove, +}; diff --git a/os/pc/vgargb524.c b/os/pc/vgargb524.c new file mode 100644 index 00000000..89c2129d --- /dev/null +++ b/os/pc/vgargb524.c @@ -0,0 +1,236 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +/* + * IBM RGB524. + * 170/220MHz High Performance Palette DAC. + * + * Assumes hooked up to an S3 Vision96[48]. + */ +enum { + IndexLo = 0x00, + IndexHi = 0x01, + Data = 0x02, + IndexCtl = 0x03, +}; + +enum { /* index registers */ + CursorCtl = 0x30, + CursorXLo = 0x31, + CursorXHi = 0x32, + CursorYLo = 0x33, + CursorYHi = 0x34, + CursorHotX = 0x35, + CursorHotY = 0x36, + + CursorR1 = 0x40, + CursorG1 = 0x41, + CursorB1 = 0x42, + CursorR2 = 0x43, + CursorG2 = 0x44, + CursorB2 = 0x45, + CursorR3 = 0x46, + CursorG3 = 0x47, + CursorB3 = 0x48, + + CursorArray = 0x100, +}; + +/* + * Lower 2-bits of indirect DAC register + * addressing. + */ +static ushort dacxreg[4] = { + PaddrW, Pdata, Pixmask, PaddrR +}; + +static uchar +rgb524setrs2(void) +{ + uchar rs2; + + rs2 = vgaxi(Crtx, 0x55); + vgaxo(Crtx, 0x55, (rs2 & 0xFC)|0x01); + + return rs2; +} + +static void +rgb524xo(int index, uchar data) +{ + vgao(dacxreg[IndexLo], index & 0xFF); + vgao(dacxreg[IndexHi], (index>>8) & 0xFF); + vgao(dacxreg[Data], data); +} + +static void +rgb524disable(VGAscr*) +{ + uchar rs2; + + rs2 = rgb524setrs2(); + rgb524xo(CursorCtl, 0x00); + vgaxo(Crtx, 0x55, rs2); +} + +static void +rgb524enable(VGAscr*) +{ + uchar rs2; + + rs2 = rgb524setrs2(); + + /* + * Make sure cursor is off by initialising the cursor + * control to defaults. + */ + rgb524xo(CursorCtl, 0x00); + + /* + * Cursor colour 1 (white), + * cursor colour 2 (black). + */ + rgb524xo(CursorR1, Pwhite); rgb524xo(CursorG1, Pwhite); rgb524xo(CursorB1, Pwhite); + rgb524xo(CursorR2, Pblack); rgb524xo(CursorG2, Pblack); rgb524xo(CursorB2, Pblack); + + /* + * Enable the cursor, 32x32, mode 2. + */ + rgb524xo(CursorCtl, 0x23); + + vgaxo(Crtx, 0x55, rs2); +} + +static void +rgb524load(VGAscr*, Cursor* curs) +{ + uchar p, p0, p1, rs2; + int x, y; + + rs2 = rgb524setrs2(); + + /* + * Make sure cursor is off by initialising the cursor + * control to defaults. + */ + rgb524xo(CursorCtl, 0x00); + + /* + * Set auto-increment mode for index-register addressing + * and initialise the cursor array index. + */ + vgao(dacxreg[IndexCtl], 0x01); + vgao(dacxreg[IndexLo], CursorArray & 0xFF); + vgao(dacxreg[IndexHi], (CursorArray>>8) & 0xFF); + + /* + * Initialise the 32x32 cursor RAM array. There are 2 planes, + * p0 and p1. Data is written 4 pixels per byte, with p1 the + * MS bit of each pixel. + * The cursor is set in X-Windows mode which gives the following + * truth table: + * p1 p0 colour + * 0 0 underlying pixel colour + * 0 1 underlying pixel colour + * 1 0 cursor colour 1 + * 1 1 cursor colour 2 + * Put the cursor into the top-left of the 32x32 array. + */ + for(y = 0; y < 32; y++){ + for(x = 0; x < 32/8; x++){ + if(x < 16/8 && y < 16){ + p0 = curs->clr[x+y*2]; + p1 = curs->set[x+y*2]; + + p = 0x00; + if(p1 & 0x80) + p |= 0xC0; + else if(p0 & 0x80) + p |= 0x80; + if(p1 & 0x40) + p |= 0x30; + else if(p0 & 0x40) + p |= 0x20; + if(p1 & 0x20) + p |= 0x0C; + else if(p0 & 0x20) + p |= 0x08; + if(p1 & 0x10) + p |= 0x03; + else if(p0 & 0x10) + p |= 0x02; + vgao(dacxreg[Data], p); + + p = 0x00; + if(p1 & 0x08) + p |= 0xC0; + else if(p0 & 0x08) + p |= 0x80; + if(p1 & 0x04) + p |= 0x30; + else if(p0 & 0x04) + p |= 0x20; + if(p1 & 0x02) + p |= 0x0C; + else if(p0 & 0x02) + p |= 0x08; + if(p1 & 0x01) + p |= 0x03; + else if(p0 & 0x01) + p |= 0x02; + vgao(dacxreg[Data], p); + } + else{ + vgao(dacxreg[Data], 0x00); + vgao(dacxreg[Data], 0x00); + } + } + } + + /* + * Initialise the cursor hotpoint, + * enable the cursor and restore state. + */ + rgb524xo(CursorHotX, -curs->offset.x); + rgb524xo(CursorHotY, -curs->offset.y); + + rgb524xo(CursorCtl, 0x23); + + vgaxo(Crtx, 0x55, rs2); +} + +static int +rgb524move(VGAscr*, Point p) +{ + uchar rs2; + + rs2 = rgb524setrs2(); + + rgb524xo(CursorXLo, p.x & 0xFF); + rgb524xo(CursorXHi, (p.x>>8) & 0x0F); + rgb524xo(CursorYLo, p.y & 0xFF); + rgb524xo(CursorYHi, (p.y>>8) & 0x0F); + + vgaxo(Crtx, 0x55, rs2); + + return 0; +} + +VGAcur vgargb524cur = { + "rgb524hwgc", + + rgb524enable, + rgb524disable, + rgb524load, + rgb524move, +}; diff --git a/os/pc/vgas3.c b/os/pc/vgas3.c new file mode 100644 index 00000000..b23657e6 --- /dev/null +++ b/os/pc/vgas3.c @@ -0,0 +1,620 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +enum { + PCIS3 = 0x5333, /* PCI VID */ + + SAVAGE3D = 0x8A20, /* PCI DID */ + SAVAGE3DMV = 0x8A21, + SAVAGE4 = 0x8A22, + PROSAVAGEP = 0x8A25, + PROSAVAGEK = 0x8A26, + PROSAVAGE8 = 0x8D04, + SAVAGEMXMV = 0x8C10, + SAVAGEMX = 0x8C11, + SAVAGEIXMV = 0x8C12, + SAVAGEIX = 0x8C13, + SUPERSAVAGEIXC16 = 0x8C2E, + SAVAGE2000 = 0x9102, + + VIRGE = 0x5631, + VIRGEGX2 = 0x8A10, + VIRGEDXGX = 0x8A01, + VIRGEVX = 0x883D, + VIRGEMX = 0x8C01, + VIRGEMXP = 0x8C03, + + VIRTUALPC2004 = 0x8810, + AURORA64VPLUS = 0x8812, +}; + +static int +s3pageset(VGAscr* scr, int page) +{ + uchar crt35, crt51; + int opage; + + crt35 = vgaxi(Crtx, 0x35); + if(scr->gscreen->depth >= 8){ + /* + * The S3 registers need to be unlocked for this. + * Let's hope they are already: + * vgaxo(Crtx, 0x38, 0x48); + * vgaxo(Crtx, 0x39, 0xA0); + * + * The page is 6 bits, the lower 4 bits in Crt35<3:0>, + * the upper 2 in Crt51<3:2>. + */ + vgaxo(Crtx, 0x35, page & 0x0F); + crt51 = vgaxi(Crtx, 0x51); + vgaxo(Crtx, 0x51, (crt51 & ~0x0C)|((page & 0x30)>>2)); + opage = ((crt51 & 0x0C)<<2)|(crt35 & 0x0F); + } + else{ + vgaxo(Crtx, 0x35, (page<<2) & 0x0C); + opage = (crt35>>2) & 0x03; + } + + return opage; +} + +static void +s3page(VGAscr* scr, int page) +{ + int id; + + id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E); + switch(id){ + + case VIRGEGX2: + break; + + default: + lock(&scr->devlock); + s3pageset(scr, page); + unlock(&scr->devlock); + break; + } +} + +static ulong +s3linear(VGAscr* scr, int* size, int* align) +{ + char *mmioname; + ulong aperture, oaperture, mmiobase, mmiosize; + int i, id, j, osize, oapsize, wasupamem; + Pcidev *p; + + osize = *size; + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + mmiosize = 0; + mmiobase = 0; + mmioname = nil; + + /* + * S3 makes cards other than display controllers, so + * look for the first S3 display controller (device class 3) + * and not one of their sound cards. + */ + p = nil; + while(p = pcimatch(p, PCIS3, 0)){ + if(p->ccrb == 0x03) + break; + } + if(p != nil){ + for(i=0; imem); i++){ + if(p->mem[i].size >= *size + && ((p->mem[i].bar & ~0x0F) & (*align-1)) == 0) + break; + } + if(i >= nelem(p->mem)){ + print("vgas3: aperture not found\n"); + return 0; + } + aperture = p->mem[i].bar & ~0x0F; + *size = p->mem[i].size; + + id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E); + switch(id){ /* find mmio */ + case SAVAGE4: + case PROSAVAGEP: + case PROSAVAGEK: + case PROSAVAGE8: + case SUPERSAVAGEIXC16: + /* + * We could assume that the MMIO registers + * will be in the screen segment and just use + * that, but PCI software is allowed to move them + * if it feels like it, so we look for an aperture of + * the right size; only the first 512k actually means + * anything. The S3 engineers overestimated how + * much space they would need in the first design. + */ + for(j=0; jmem); j++){ + if(i == j) + continue; + if(p->mem[j].size==512*1024 || p->mem[j].size==16*1024*1024){ + mmiobase = p->mem[j].bar & ~0x0F; + mmiosize = 512*1024; + scr->mmio = (ulong*)upamalloc(mmiobase, mmiosize, 0); + mmioname = "savagemmio"; + break; + } + } + if(mmiosize == 0){ + print("savage4: mmio not found\n"); + return 0; + } + } + }else + aperture = 0; + + if(wasupamem) + upafree(oaperture, oapsize); + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)) + scr->isupamem = 1; + } + else + scr->isupamem = 1; + + if(oaperture && oaperture != aperture) + print("warning (BUG): redefinition of aperture does not change s3screen segment\n"); + addvgaseg("s3screen", aperture, osize); + + if(mmiosize) + addvgaseg(mmioname, mmiobase, mmiosize); + + return aperture; +} + +static void +s3vsyncactive(void) +{ + /* + * Hardware cursor information is fetched from display memory + * during the horizontal blank active time. The 80x chips may hang + * if the cursor is turned on or off during this period. + */ + while((vgai(Status1) & 0x08) == 0) + ; +} + +static void +s3disable(VGAscr*) +{ + uchar crt45; + + /* + * Turn cursor off. + */ + crt45 = vgaxi(Crtx, 0x45) & 0xFE; + s3vsyncactive(); + vgaxo(Crtx, 0x45, crt45); +} + +static void +s3load(VGAscr* scr, Cursor* curs) +{ + uchar *p; + int id, dolock, opage, x, y; + + /* + * Disable the cursor and + * set the pointer to the two planes. + */ + s3disable(scr); + + opage = 0; + p = KADDR(scr->aperture); + id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E); + switch(id){ + + case VIRTUALPC2004: + case VIRGE: + case VIRGEDXGX: + case VIRGEGX2: + case VIRGEVX: + case SAVAGEMXMV: + case SAVAGEIXMV: + case SAVAGE4: + case PROSAVAGEP: + case PROSAVAGEK: + case PROSAVAGE8: + case SUPERSAVAGEIXC16: + dolock = 0; + p += scr->storage; + break; + + default: + dolock = 1; + lock(&scr->devlock); + opage = s3pageset(scr, scr->storage>>16); + p += (scr->storage & 0xFFFF); + break; + } + + /* + * The cursor is set in Microsoft Windows format (the ViRGE/GX2 doesn't + * support the X11 format) which gives the following truth table: + * and xor colour + * 0 0 background colour + * 0 1 foreground colour + * 1 0 current screen pixel + * 1 1 NOT current screen pixel + * Put the cursor into the top-left of the 64x64 array. + * + * The cursor pattern in memory is interleaved words of + * AND and XOR patterns. + */ + for(y = 0; y < 64; y++){ + for(x = 0; x < 64/8; x += 2){ + if(x < 16/8 && y < 16){ + *p++ = ~(curs->clr[2*y + x]|curs->set[2*y + x]); + *p++ = ~(curs->clr[2*y + x+1]|curs->set[2*y + x+1]); + *p++ = curs->set[2*y + x]; + *p++ = curs->set[2*y + x+1]; + } + else { + *p++ = 0xFF; + *p++ = 0xFF; + *p++ = 0x00; + *p++ = 0x00; + } + } + } + + if(dolock){ + s3pageset(scr, opage); + unlock(&scr->devlock); + } + + /* + * Save the cursor hotpoint and enable the cursor. + */ + scr->offset = curs->offset; + s3vsyncactive(); + vgaxo(Crtx, 0x45, 0x01); +} + +static int +s3move(VGAscr* scr, Point p) +{ + int x, xo, y, yo; + + /* + * Mustn't position the cursor offscreen even partially, + * or it disappears. Therefore, if x or y is -ve, adjust the + * cursor offset instead. + * There seems to be a bug in that if the offset is 1, the + * cursor doesn't disappear off the left edge properly, so + * round it up to be even. + */ + if((x = p.x+scr->offset.x) < 0){ + xo = -x; + xo = ((xo+1)/2)*2; + x = 0; + } + else + xo = 0; + if((y = p.y+scr->offset.y) < 0){ + yo = -y; + y = 0; + } + else + yo = 0; + + vgaxo(Crtx, 0x46, (x>>8) & 0x07); + vgaxo(Crtx, 0x47, x & 0xFF); + vgaxo(Crtx, 0x49, y & 0xFF); + vgaxo(Crtx, 0x4E, xo); + vgaxo(Crtx, 0x4F, yo); + vgaxo(Crtx, 0x48, (y>>8) & 0x07); + + return 0; +} + +static void +s3enable(VGAscr* scr) +{ + int i; + ulong storage; + + s3disable(scr); + + /* + * Cursor colours. Set both the CR0[EF] and the colour + * stack in case we are using a 16-bit RAMDAC. + */ + vgaxo(Crtx, 0x0E, Pwhite); + vgaxo(Crtx, 0x0F, Pblack); + vgaxi(Crtx, 0x45); + + for(i = 0; i < 3; i++) + vgaxo(Crtx, 0x4A, Pblack); + vgaxi(Crtx, 0x45); + for(i = 0; i < 3; i++) + vgaxo(Crtx, 0x4B, Pwhite); + + /* + * Find a place for the cursor data in display memory. + * Must be on a 1024-byte boundary. + */ + storage = (scr->gscreen->width*BY2WD*scr->gscreen->r.max.y+1023)/1024; + vgaxo(Crtx, 0x4C, storage>>8); + vgaxo(Crtx, 0x4D, storage & 0xFF); + storage *= 1024; + scr->storage = storage; + + /* + * Load, locate and enable the cursor + * in Microsoft Windows format. + */ + s3load(scr, &arrow); + s3move(scr, ZP); + vgaxo(Crtx, 0x55, vgaxi(Crtx, 0x55) & ~0x10); + s3vsyncactive(); + vgaxo(Crtx, 0x45, 0x01); +} + +/* + * The manual gives byte offsets, but we want ulong offsets, hence /4. + */ +enum { + SrcBase = 0xA4D4/4, + DstBase = 0xA4D8/4, + Stride = 0xA4E4/4, + FgrdData = 0xA4F4/4, + WidthHeight = 0xA504/4, + SrcXY = 0xA508/4, + DestXY = 0xA50C/4, + Command = 0xA500/4, + SubStat = 0x8504/4, + FifoStat = 0x850C/4, +}; + +/* + * Wait for writes to VGA memory via linear aperture to flush. + */ +enum {Maxloop = 1<<24}; +struct { + ulong linear; + ulong fifo; + ulong idle; + ulong lineartimeout; + ulong fifotimeout; + ulong idletimeout; +} waitcount; + +static void +waitforlinearfifo(VGAscr *scr) +{ + ulong *mmio; + long x; + static ulong nwaitforlinearfifo; + ulong mask, val; + + switch(scr->id){ + default: + panic("unknown scr->id in s3 waitforlinearfifo"); + case 0x8A01: /* ViRGE/[DG]X. XFree86 says no waiting necessary */ + return; + case 0x5631: /* ViRGE */ + case 0x883D: /* ViRGE/VX */ + mask = 0x0F<<6; + val = 0x08<<6; + break; + case 0x8A10: /* ViRGE/GX2 */ + mask = 0x1F<<6; + val = 0x10<<6; + break; + } + mmio = scr->mmio; + x = 0; + while((mmio[FifoStat]&mask) != val && x++ < Maxloop) + waitcount.linear++; + if(x >= Maxloop) + waitcount.lineartimeout++; +} + +static void +waitforfifo(VGAscr *scr, int entries) +{ + ulong *mmio; + long x; + static ulong nwaitforfifo; + + mmio = scr->mmio; + x = 0; + while((mmio[SubStat]&0x1F00) < ((entries+2)<<8) && x++ < Maxloop) + waitcount.fifo++; + if(x >= Maxloop) + waitcount.fifotimeout++; +} + +static void +waitforidle(VGAscr *scr) +{ + ulong *mmio; + long x; + + mmio = scr->mmio; + x = 0; + while((mmio[SubStat]&0x3F00) != 0x3000 && x++ < Maxloop) + waitcount.idle++; + if(x >= Maxloop) + waitcount.idletimeout++; +} + +static int +hwscroll(VGAscr *scr, Rectangle r, Rectangle sr) +{ + enum { Bitbltop = 0xCC }; /* copy source */ + ulong *mmio; + ulong cmd, stride; + Point dp, sp; + int did, d; + + d = scr->gscreen->depth; + did = (d-8)/8; + cmd = 0x00000020|(Bitbltop<<17)|(did<<2); + stride = Dx(scr->gscreen->r)*d/8; + + if(r.min.x <= sr.min.x){ + cmd |= 1<<25; + dp.x = r.min.x; + sp.x = sr.min.x; + }else{ + dp.x = r.max.x-1; + sp.x = sr.max.x-1; + } + + if(r.min.y <= sr.min.y){ + cmd |= 1<<26; + dp.y = r.min.y; + sp.y = sr.min.y; + }else{ + dp.y = r.max.y-1; + sp.y = sr.max.y-1; + } + + mmio = scr->mmio; + waitforlinearfifo(scr); + waitforfifo(scr, 7); + mmio[SrcBase] = scr->aperture; + mmio[DstBase] = scr->aperture; + mmio[Stride] = (stride<<16)|stride; + mmio[WidthHeight] = ((Dx(r)-1)<<16)|Dy(r); + mmio[SrcXY] = (sp.x<<16)|sp.y; + mmio[DestXY] = (dp.x<<16)|dp.y; + mmio[Command] = cmd; + waitforidle(scr); + return 1; +} + +static int +hwfill(VGAscr *scr, Rectangle r, ulong sval) +{ + enum { Bitbltop = 0xCC }; /* copy source */ + ulong *mmio; + ulong cmd, stride; + int did, d; + + d = scr->gscreen->depth; + did = (d-8)/8; + cmd = 0x16000120|(Bitbltop<<17)|(did<<2); + stride = Dx(scr->gscreen->r)*d/8; + mmio = scr->mmio; + waitforlinearfifo(scr); + waitforfifo(scr, 8); + mmio[SrcBase] = scr->aperture; + mmio[DstBase] = scr->aperture; + mmio[DstBase] = scr->aperture; + mmio[Stride] = (stride<<16)|stride; + mmio[FgrdData] = sval; + mmio[WidthHeight] = ((Dx(r)-1)<<16)|Dy(r); + mmio[DestXY] = (r.min.x<<16)|r.min.y; + mmio[Command] = cmd; + waitforidle(scr); + return 1; +} + +enum { + CursorSyncCtl = 0x0D, /* in Seqx */ + VsyncHi = 0x80, + VsyncLo = 0x40, + HsyncHi = 0x20, + HsyncLo = 0x10, +}; + +static void +s3blank(VGAscr*, int blank) +{ + uchar x; + + x = vgaxi(Seqx, CursorSyncCtl); + x &= ~0xF0; + if(blank) + x |= VsyncLo | HsyncLo; + vgaxo(Seqx, CursorSyncCtl, x); +} + +static void +s3drawinit(VGAscr *scr) +{ + extern void savageinit(VGAscr*); /* vgasavage.c */ + ulong id; + + id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E); + scr->id = id; + + /* + * It's highly likely that other ViRGEs will work without + * change to the driver, with the exception of the size of + * the linear aperture memory write FIFO. Since we don't + * know that size, I'm not turning them on. See waitforlinearfifo + * above. + */ + scr->blank = s3blank; + /* hwblank = 1; not known to work well */ + + switch(id){ + case VIRGE: + case VIRGEVX: + case VIRGEGX2: + scr->mmio = (ulong*)(scr->aperture+0x1000000); + scr->fill = hwfill; + scr->scroll = hwscroll; + break; + case SAVAGEMXMV: + case SAVAGEIXMV: + scr->mmio = (ulong*)(scr->aperture+0x1000000); + savageinit(scr); + break; + case SUPERSAVAGEIXC16: + case SAVAGE4: + case PROSAVAGEP: + case PROSAVAGE8: + case PROSAVAGEK: + /* scr->mmio is set by s3linear */ + savageinit(scr); + break; + } +} + +VGAdev vgas3dev = { + "s3", + + 0, + 0, + s3page, + s3linear, + s3drawinit, +}; + +VGAcur vgas3cur = { + "s3hwgc", + + s3enable, + s3disable, + s3load, + s3move, +}; + diff --git a/os/pc/vgasavage.c b/os/pc/vgasavage.c new file mode 100644 index 00000000..263b688b --- /dev/null +++ b/os/pc/vgasavage.c @@ -0,0 +1,571 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +enum { + PCIS3 = 0x5333, /* PCI VID */ + + SAVAGE3D = 0x8A20, /* PCI DID */ + SAVAGE3DMV = 0x8A21, + SAVAGE4 = 0x8A22, + PROSAVAGEP = 0x8A25, + PROSAVAGEK = 0x8A26, + PROSAVAGE8 = 0x8D04, + SAVAGEMXMV = 0x8C10, + SAVAGEMX = 0x8C11, + SAVAGEIXMV = 0x8C12, + SAVAGEIX = 0x8C13, + SUPERSAVAGEIXC16 = 0x8C2E, + SAVAGE2000 = 0x9102, + + VIRGE = 0x5631, + VIRGEGX2 = 0x8A10, + VIRGEDXGX = 0x8A01, + VIRGEVX = 0x883D, + VIRGEMX = 0x8C01, + VIRGEMXP = 0x8C03, + + AURORA64VPLUS = 0x8812, +}; + +/* + * Savage4 et al. acceleration. + * + * This is based only on the Savage4 documentation. + * It is expected to work on other Savage cards as well, + * but has not been tried. + * + * There are five ways to access the 2D graphics engine registers: + * - Old MMIO non-packed format + * - Old MMIO packed format + * - New MMIO non-packed format + * - New MMIO packed format + * - Burst Command Interface (BCI) + * + * Of these, the manual hints that the first three are deprecated, + * and it does not document any of those three well enough to use. + * + * I have tried for many hours with no success to understand the BCI + * interface well enough to use it. It is not well documented, and the + * XFree86 driver seems to completely contradict what little documentation + * there is. + * + * This leaves the packed new MMIO. + * The manual contradicts itself here, claming that the registers + * start at 0x2008100 as well as at 0x0008100 from the base of the + * mmio segment. Since the segment is only 512k, we assume that + * the latter is the correct offset. + * + * According to the manual, only 16-bit reads of the 2D registers + * are supported: 32-bit reads will return garbage in the upper word. + * 32-bit writes must be enabled explicitly. + * + * 32-bit reads of the status registers seem just fine. + */ + +/* 2D graphics engine registers for Savage4; others appear to be mostly the same */ +enum { + SubsystemStatus = 0x8504, /* Subsystem Status: read only */ + /* read only: whether we get interrupts on various events */ + VsyncInt = 1<<0, /* vertical sync */ + GeBusyInt = 1<<1, /* 2D graphics engine busy */ + BfifoFullInt = 1<<2, /* BIU FIFO full */ + BfifoEmptyInt = 1<<3, /* BIU FIFO empty */ + CfifoFullInt = 1<<4, /* command FIFO full */ + CfifoEmptyInt = 1<<5, /* command FIFO empty */ + BciInt = 1<<6, /* BCI */ + LpbInt = 1<<7, /* LPB */ + CbHiInt = 1<<16, /* COB upper threshold */ + CbLoInt = 1<<17, /* COB lower threshold */ + + SubsystemCtl = 0x8504, /* Subsystem Control: write only */ + /* clear interrupts for various events */ + VsyncClr = 1<<0, + GeBusyClr = 1<<1, + BfifoFullClr = 1<<2, + BfifoEmptyClr = 1<<3, + CfifoFullClr = 1<<4, + CfifoEmptyClr = 1<<5, + BciClr = 1<<6, + LpbClr = 1<<7, + CbHiClr = 1<<16, + CbLoClr = 1<<17, + + /* enable interrupts for various events */ + VsyncEna = 1<<8, + Busy2DEna = 1<<9, + BfifoFullEna = 1<<10, + BfifoEmptyEna = 1<<11, + CfifoFullEna = 1<<12, + CfifoEmptyEna = 1<<13, + SubsysBciEna = 1<<14, + CbHiEna = 1<<24, + CbLoEna = 1<<25, + + /* 2D graphics engine software reset */ + GeSoftReset = 1<<15, + + FifoStatus = 0x8508, /* FIFO status: read only */ + CwbEmpty = 1<<0, /* command write buffer empty */ + CrbEmpty = 1<<1, /* command read buffer empty */ + CobEmpty = 1<<2, /* command overflow buffer empty */ + CfifoEmpty = 1<<3, /* command FIFO empty */ + CwbFull = 1<<8, /* command write buffer full */ + CrbFull = 1<<9, /* command read buffer full */ + CobFull = 1<<10, /* command overflow buffer full */ + CfifoFull = 1<<11, /* command FIFO full */ + + AdvFunCtl = 0x850C, /* Advanced Function Control: read/write */ + GeEna = 1<<0, /* enable 2D/3D engine */ + /* + * according to the manual, BigPixel should be + * set when bpp >= 8 (bpp != 4), and then CR50_5-4 are + * used to figure out bpp example. however, it does bad things + * to the screen in 8bpp mode. + */ + BigPixel = 1<<2, /* 8 or more bpp enhanced mode */ + LaEna = 1<<3, /* linear addressing ena: or'ed with CR58_4 */ + Mclk_2 = 0<<8, /* 2D engine clock divide: MCLK/2 */ + Mclk_4 = 1<<8, /* " MCLK/4 */ + Mclk = 2<<8, /* " MCLK */ + /* Mclk = 3<<8, /* " MCLK */ + Ic33mhz = 1<<16, /* Internal clock 33 MHz (instead of 66) */ + + WakeupReg = 0x8510, /* Wakeup: read/write */ + WakeupBit = 1<<0, /* wake up: or'ed with 3C3_0 */ + + SourceY = 0x8100, /* UL corner of bitblt source */ + SourceX = 0x8102, /* " */ + RectY = 0x8100, /* UL corner of rectangle fill */ + RectX = 0x8102, /* " */ + DestY = 0x8108, /* UL corner of bitblt dest */ + DestX = 0x810A, /* " */ + Height = 0x8148, /* bitblt, image xfer rectangle height */ + Width = 0x814A, /* bitblt, image xfer rectangle width */ + + StartY = 0x8100, /* Line draw: first point*/ + StartX = 0x8102, /* " */ + /* + * For line draws, the following must be programmed: + * axial step constant = 2*min(|dx|,|dy|) + * diagonal step constant = 2*[min(|dx|,|dy|) - max(|dx|,|dy|)] + * error term = 2*min(|dx|,|dy|) - max(|dx|,|dy| - 1 + * [sic] when start X < end X + * error term = 2*min(|dx|,|dy|) - max(|dx|,|dy| + * [sic] when start X >= end X + */ + AxialStep = 0x8108, + DiagonalStep = 0x810A, + LineError = 0x8110, + MinorLength = 0x8148, /* pixel count along minor axis */ + MajorLength = 0x814A, /* pixel count along major axis */ + + DrawCmd = 0x8118, /* Drawing Command: write only */ + CmdMagic = 0<<1, + AcrossPlane = 1<<1, /* across the plane mode */ + LastPixelOff = 1<<2, /* last pixel of line or vector draw not drawn */ + Radial = 1<<3, /* enable radial direction (else axial) */ + DoDraw = 1<<4, /* draw pixels (else only move current pos) */ + + DrawRight = 1<<5, /* axial drawing direction: left to right */ + /* DrawLeft = 0<<5, */ + MajorY = 1<<6, + /* MajorX = 0<<6, */ + DrawDown = 1<<7, + /* DrawUp = 0<<7, */ + Degree0 = 0<<5, /* drawing direction when Radial */ + Degree45 = 1<<5, + /* ... */ + Degree315 = 7<<5, + + UseCPUData = 1<<8, + + /* image write bus transfer width */ + Bus8 = 0<<9, + Bus16 = 1<<9, + /* + * in Bus32 mode, doubleword bits beyond the image rect width are + * discarded. each line starts on a new doubleword. + * Bus32AP is intended for across-the-plane mode and + * rounds to byte boundaries instead. + */ + Bus32 = 2<<9, + Bus32AP = 3<<9, + + CmdNop = 0<<13, /* nop */ + CmdLine = 1<<13, /* draw line */ + CmdFill = 2<<13, /* fill rectangle */ + CmdBitblt = 6<<13, /* bitblt */ + CmdPatblt = 7<<13, /* 8x8 pattern blt */ + + SrcGBD = 0<<16, + SrcPBD = 1<<16, + SrcSBD = 2<<16, + + DstGBD = 0<<18, + DstPBD = 1<<18, + DstSBD = 2<<18, + + /* color sources, controls */ + BgColor = 0x8120, /* Background Color: read/write */ + FgColor = 0x8124, /* Foreground Color: read/write */ + BitplaneWmask = 0x8128, /* Bitplane Write Mask: read/write */ + BitplaneRmask = 0x812C, /* Bitplane Read Mask: read/write */ + CmpColor = 0x8130, /* Color Compare: read/write */ + BgMix = 0x8134, + FgMix = 0x8136, + MixNew = 7, + SrcBg = 0<<5, + SrcFg = 1<<5, + SrcCPU = 2<<5, + SrcDisp = 3<<5, + + /* clipping rectangle */ + TopScissors = 0x8138, /* Top Scissors: write only */ + LeftScissors = 0x813A, /* Left Scissors: write only */ + BottomScissors = 0x813C, /* Bottom Scissors: write only */ + RightScissors = 0x813E, /* Right Scissors: write only */ + + /* + * Registers with Magic were indirectly accessed in older modes. + * It is not clear whether the Magic is necessary. + * In the older modes, writes to these registers were pipelined, + * so that you had to issue an engine command and wait for engine + * idle before reading a write back. It is not clear if this is + * still the case either. + */ + PixCtl = 0x8140, /* Pixel Control: write only */ + PixMagic = 0xA<<12, + PixMixFg = 0<<6, /* foreground mix register always */ + PixMixCPU = 2<<6, /* CPU data determines mix register */ + PixMixDisp = 3<<6, /* display data determines mix register */ + + MfMisc2Ctl = 0x8142, /* Multifunction Control Misc. 2: write only */ + MfMisc2Magic = 0xD<<12, + DstShift = 0, /* 3 bits: destination base address in MB */ + SrcShift = 4, /* 3 bits: source base address in MB */ + WaitFifoEmpty = 2<<8, /* wait for write FIFO empty between draws */ + + MfMiscCtl = 0x8144, /* Multifunction Control Misc: write only */ + MfMiscMagic = 0xE<<12, + UseHighBits = 1<<4, /* select upper 16 bits for 32-bit reg access */ + ClipInvert = 1<<5, /* only touch pixels outside clip rectangle */ + SkipSame = 0<<6, /* ignore pixels with color CmpColor */ + SkipDifferent = 1<<7, /* ignore pixels not color CmpColor */ + CmpEna = 1<<8, /* enable color compare */ + W32Ena = 1<<9, /* enable 32-bit register write */ + ClipDis = 1<<11, /* disable clipping */ + + /* + * The bitmap descriptor 1 registers contain the starting + * address of the bitmap (in bytes). + * The bitmap descriptor 2 registesr contain stride (in pixels) + * in the lower 16 bits, depth (in bits) in the next 8 bits, + * and whether block write is disabled. + */ + GBD1 = 0x8168, /* Global Bitmap Descriptor 1: read/write */ + GBD2 = 0x816C, /* Global Bitmap Descriptor 2: read/write */ + /* GBD2-only bits */ + BDS64 = 1<<0, /* bitmap descriptor size 64 bits */ + GBDBciEna = 1<<3, /* BCI enable */ + /* generic BD2 bits */ + BlockWriteDis = 1<<28, + StrideShift = 0, + DepthShift = 16, + + PBD1 = 0x8170, /* Primary Bitmap Descriptor: read/write */ + PBD2 = 0x8174, + SBD1 = 0x8178, /* Secondary Bitmap Descriptor: read/write */ + SBD2 = 0x817C, +}; + +/* mastered data transfer registers */ + +/* configuration/status registers */ +enum { + XStatus0 = 0x48C00, /* Status Word 0: read only */ + /* rev. A silicon differs from rev. B; use AltStatus0 */ + CBEMaskA = 0x1FFFF, /* filled command buffer entries */ + CBEShiftA = 0, + BciIdleA = 1<<17, /* BCI idle */ + Ge3IdleA = 1<<18, /* 3D engine idle */ + Ge2IdleA = 1<<19, /* 2D engine idle */ + McpIdleA = 1<<20, /* motion compensation processor idle */ + MeIdleA = 1<<22, /* master engine idle */ + PfPendA = 1<<23, /* page flip pending */ + + CBEMaskB = 0x1FFFFF, + CBEShiftB = 0, + BciIdleB = 1<<25, + Ge3IdleB = 1<<26, + Ge2IdleB = 1<<27, + McpIdleB = 1<<28, + MeIdleB = 1<<30, + PfPendB = 1<<31, + + AltStatus0 = 0x48C60, /* Alternate Status Word 0: read only */ + CBEMask = 0x1FFFF, + CBEShift = 0, + /* the Savage4 manual says bits 17..23 for these, like Status0 */ + /* empirically, they are bits 21..26 */ + BciIdle = 1<<21, + Ge3Idle = 1<<22, + Ge2Idle = 1<<23, + McpIdle = 1<<24, + MeIdle = 1<<25, + PfPend = 1<<26, + + XStatus1 = 0x48C04, /* Status Word 1: read only */ + /* contains event tag 1, event tag 0, both 16 bits */ + + XStatus2 = 0x48C08, /* Status Word 2: read only */ + ScanMask = 0x3FF, /* current scan line */ + ScanShift = 0, + VRTMask = 0x7F100, /* vert retrace count */ + VRTShift = 11, + + CbThresh = 0x48C10, /* Command Buffer Thresholds: read/write */ + CobOff = 0x48C14, /* Command Overflow Buffer: read/write */ + + CobPtr = 0x48C18, /* Command Overflow Buffer Pointers: read/write */ + CobEna = 1<<2, /* command overflow buffer enable */ + CobBciEna = 1<<3, /* BCI function enable */ + CbeMask = 0xFFFF8000, /* no. of entries in command buffer */ + CbeShift = 15, + + AltStatus1 = 0x48C64, /* Alternate Status Word 1: read onnly */ + /* contains current texture surface tag, vertex buffer tag */ + +}; + +struct { + ulong idletimeout; + ulong tostatw[16]; +} savagestats; + +enum { + Maxloop = 1<<20 +}; + +static void +savagewaitidle(VGAscr *scr) +{ + long x; + ulong *statw, mask, goal; + + switch(scr->id){ + case SAVAGE4: + case PROSAVAGEP: + case PROSAVAGEK: + case PROSAVAGE8: + /* wait for engine idle and FIFO empty */ + statw = (ulong*)((uchar*)scr->mmio+AltStatus0); + mask = CBEMask | Ge2Idle; + goal = Ge2Idle; + break; + /* case SAVAGEMXMV: ? */ + /* case SAVAGEMX: ? */ + /* case SAVAGEIX: ? */ + case SUPERSAVAGEIXC16: + case SAVAGEIXMV: + case SAVAGEMXMV: + /* wait for engine idle and FIFO empty */ + statw = (ulong*)((uchar*)scr->mmio+XStatus0); + mask = CBEMaskA | Ge2IdleA; + goal = Ge2IdleA; + break; + default: + /* + * best we can do: can't print or we'll call ourselves. + * savageinit is supposed to not let this happen. + */ + return; + } + + for(x=0; xmmio; + + *(ulong*)(mmio+FgColor) = sval; + *(ulong*)(mmio+BgColor) = sval; + *(ulong*)(mmio+BgMix) = SrcFg|MixNew; + *(ulong*)(mmio+FgMix) = SrcFg|MixNew; + *(ushort*)(mmio+RectY) = r.min.y; + *(ushort*)(mmio+RectX) = r.min.x; + *(ushort*)(mmio+Width) = Dx(r)-1; + *(ushort*)(mmio+Height) = Dy(r)-1; + *(ulong*)(mmio+DrawCmd) = CmdMagic | DoDraw | CmdFill | DrawRight | DrawDown; + savagewaitidle(scr); + return 1; +} + +static int +savagescroll(VGAscr *scr, Rectangle r, Rectangle sr) +{ + uchar *mmio; + ulong cmd; + Point dp, sp; + + cmd = CmdMagic | DoDraw | CmdBitblt | SrcPBD | DstGBD; + + if(r.min.x <= sr.min.x){ + cmd |= DrawRight; + dp.x = r.min.x; + sp.x = sr.min.x; + }else{ + dp.x = r.max.x-1; + sp.x = sr.max.x-1; + } + + if(r.min.y <= sr.min.y){ + cmd |= DrawDown; + dp.y = r.min.y; + sp.y = sr.min.y; + }else{ + dp.y = r.max.y-1; + sp.y = sr.max.y-1; + } + + mmio = (uchar*)scr->mmio; + + *(ushort*)(mmio+SourceX) = sp.x; + *(ushort*)(mmio+SourceY) = sp.y; + *(ushort*)(mmio+DestX) = dp.x; + *(ushort*)(mmio+DestY) = dp.y; + *(ushort*)(mmio+Width) = Dx(r)-1; + *(ushort*)(mmio+Height) = Dy(r)-1; + *(ulong*)(mmio+BgMix) = SrcDisp|MixNew; + *(ulong*)(mmio+FgMix) = SrcDisp|MixNew; + *(ulong*)(mmio+DrawCmd) = cmd; + savagewaitidle(scr); + return 1; +} + +static void +savageblank(VGAscr*, int blank) +{ + uchar seqD; + + /* + * Will handle DPMS to monitor + */ + vgaxo(Seqx, 8, vgaxi(Seqx,8)|0x06); + seqD = vgaxi(Seqx, 0xD); + seqD &= 0x03; + if(blank) + seqD |= 0x50; + vgaxo(Seqx, 0xD, seqD); + + /* + * Will handle LCD + */ + if(blank) + vgaxo(Seqx, 0x31, vgaxi(Seqx, 0x31) & ~0x10); + else + vgaxo(Seqx, 0x31, vgaxi(Seqx, 0x31) | 0x10); +} + + +void +savageinit(VGAscr *scr) +{ + uchar *mmio; + ulong bd; + + /* if you add chip IDs here be sure to update savagewaitidle */ + switch(scr->id){ + case SAVAGE4: + case PROSAVAGEP: + case PROSAVAGEK: + case PROSAVAGE8: + case SAVAGEIXMV: + case SUPERSAVAGEIXC16: + case SAVAGEMXMV: + break; + default: + print("unknown savage %.4lux\n", scr->id); + return; + } + + mmio = (uchar*)scr->mmio; + if(mmio == nil) { + print("savageinit: no mmio\n"); + return; + } + + /* 2D graphics engine software reset */ + *(ushort*)(mmio+SubsystemCtl) = GeSoftReset; + delay(2); + *(ushort*)(mmio+SubsystemCtl) = 0; + savagewaitidle(scr); + + /* disable BCI as much as possible */ + *(ushort*)(mmio+CobPtr) &= ~CobBciEna; + *(ushort*)(mmio+GBD2) &= ~GBDBciEna; + savagewaitidle(scr); + + /* enable 32-bit writes, disable clipping */ + *(ushort*)(mmio+MfMiscCtl) = MfMiscMagic|W32Ena|ClipDis; + savagewaitidle(scr); + + /* enable all read, write planes */ + *(ulong*)(mmio+BitplaneRmask) = ~0; + *(ulong*)(mmio+BitplaneWmask) = ~0; + savagewaitidle(scr); + + /* turn on linear access, 2D engine */ + *(ulong*)(mmio+AdvFunCtl) |= GeEna|LaEna; + savagewaitidle(scr); + + /* set bitmap descriptors */ + bd = (scr->gscreen->depth<gscreen->r)<>16; + savagewaitidle(scr); + + scr->fill = savagefill; + scr->scroll = savagescroll; + scr->blank = savageblank; + hwblank = 0; +} diff --git a/os/pc/vgat2r4.c b/os/pc/vgat2r4.c new file mode 100644 index 00000000..b8003b87 --- /dev/null +++ b/os/pc/vgat2r4.c @@ -0,0 +1,586 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +/* + * #9 Ticket to Ride IV. + */ +enum { + IndexLo = 0x10/4, + IndexHi = 0x14/4, + Data = 0x18/4, + IndexCtl = 0x1C/4, + + Zoom = 0x54/4, +}; + +enum { /* index registers */ + CursorSyncCtl = 0x03, + HsyncHi = 0x01, + HsyncLo = 0x02, + VsyncHi = 0x04, + VsyncLo = 0x08, + + CursorCtl = 0x30, + CursorXLo = 0x31, + CursorXHi = 0x32, + CursorYLo = 0x33, + CursorYHi = 0x34, + CursorHotX = 0x35, + CursorHotY = 0x36, + + CursorR1 = 0x40, + CursorG1 = 0x41, + CursorB1 = 0x42, + CursorR2 = 0x43, + CursorG2 = 0x44, + CursorB2 = 0x45, + CursorR3 = 0x46, + CursorG3 = 0x47, + CursorB3 = 0x48, + + CursorArray = 0x100, + + CursorMode32x32 = 0x23, + CursorMode64x64 = 0x27, + CursorMode = CursorMode32x32, +}; + +static ulong +t2r4linear(VGAscr* scr, int* size, int* align) +{ + ulong aperture, oaperture; + int oapsize, wasupamem; + Pcidev *p; + + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + aperture = 0; + if(p = pcimatch(nil, 0x105D, 0)){ + switch(p->did){ + case 0x5348: + aperture = p->mem[0].bar & ~0x0F; + *size = p->mem[0].size; + break; + default: + break; + } + } + + if(wasupamem){ + if(oaperture == aperture) + return oaperture; + upafree(oaperture, oapsize); + } + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)){ + aperture = oaperture; + scr->isupamem = 1; + } + else + scr->isupamem = 0; + } + else + scr->isupamem = 1; + + return aperture; +} + +static void +t2r4enable(VGAscr* scr) +{ + Pcidev *p; + int size, align; + ulong aperture, mmio; + + /* + * Only once, can't be disabled for now. + * scr->mmio holds the virtual address of + * the MMIO registers. + */ + if(scr->mmio) + return; + if(p = pcimatch(nil, 0x105D, 0)){ + switch(p->did){ + case 0x5348: + break; + default: + return; + } + } + else + return; + mmio = upamalloc(p->mem[4].bar & ~0x0F, p->mem[4].size, 0); + if(mmio == 0) + return; + addvgaseg("t2r4mmio", mmio, p->mem[4].size); + + scr->mmio = KADDR(mmio); + + size = p->mem[0].size; + align = 0; + aperture = t2r4linear(scr, &size, &align); + if(aperture){ + scr->aperture = aperture; + scr->apsize = size; + addvgaseg("t2r4screen", aperture, size); + } +} + +static uchar +t2r4xi(VGAscr* scr, int index) +{ + ulong *mmio; + + mmio = scr->mmio; + mmio[IndexLo] = index & 0xFF; + mmio[IndexHi] = (index>>8) & 0xFF; + + return mmio[Data]; +} + +static void +t2r4xo(VGAscr* scr, int index, uchar data) +{ + ulong *mmio; + + mmio = scr->mmio; + mmio[IndexLo] = index & 0xFF; + mmio[IndexHi] = (index>>8) & 0xFF; + + mmio[Data] = data; +} + +static void +t2r4curdisable(VGAscr* scr) +{ + if(scr->mmio == 0) + return; + t2r4xo(scr, CursorCtl, 0x00); +} + +static void +t2r4curload(VGAscr* scr, Cursor* curs) +{ + uchar *data; + int size, x, y, zoom; + ulong clr, *mmio, pixels, set; + + mmio = scr->mmio; + if(mmio == 0) + return; + + /* + * Make sure cursor is off by initialising the cursor + * control to defaults. + */ + t2r4xo(scr, CursorCtl, 0x00); + + /* + * Set auto-increment mode for index-register addressing + * and initialise the cursor array index. + */ + mmio[IndexCtl] = 0x01; + mmio[IndexLo] = CursorArray & 0xFF; + mmio[IndexHi] = (CursorArray>>8) & 0xFF; + + /* + * Initialise the cursor RAM array. There are 2 planes, + * p0 and p1. Data is written 4 pixels per byte, with p1 the + * MS bit of each pixel. + * The cursor is set in X-Windows mode which gives the following + * truth table: + * p1 p0 colour + * 0 0 underlying pixel colour + * 0 1 underlying pixel colour + * 1 0 cursor colour 1 + * 1 1 cursor colour 2 + * Put the cursor into the top-left of the array. + * + * Although this looks a lot like the IBM RGB524 cursor, the + * scanlines appear to be twice as long as they should be and + * some of the other features are missing. + */ + if(mmio[Zoom] & 0x0F) + zoom = 32; + else + zoom = 16; + data = (uchar*)&mmio[Data]; + for(y = 0; y < zoom; y++){ + clr = (curs->clr[2*y]<<8)|curs->clr[y*2 + 1]; + set = (curs->set[2*y]<<8)|curs->set[y*2 + 1]; + pixels = 0; + for(x = 0; x < 16; x++){ + if(set & (1<>24; + *data = pixels>>16; + *data = pixels>>8; + *data = pixels; + + *data = 0x00; + *data = 0x00; + *data = 0x00; + *data = 0x00; + + if(CursorMode == CursorMode32x32 && zoom == 16) + continue; + *data = pixels>>24; + *data = pixels>>16; + *data = pixels>>8; + *data = pixels; + + *data = 0x00; + *data = 0x00; + *data = 0x00; + *data = 0x00; + } + if(CursorMode == CursorMode32x32) + size = 32; + else + size = 64; + while(y < size){ + for(x = 0; x < size/8; x++){ + *data = 0x00; + *data = 0x00; + } + y++; + } + mmio[IndexCtl] = 0x00; + + /* + * Initialise the hotpoint and enable the cursor. + */ + t2r4xo(scr, CursorHotX, -curs->offset.x); + zoom = (scr->mmio[Zoom] & 0x0F)+1; + t2r4xo(scr, CursorHotY, -curs->offset.y*zoom); + + t2r4xo(scr, CursorCtl, CursorMode); +} + +static int +t2r4curmove(VGAscr* scr, Point p) +{ + int y, zoom; + + if(scr->mmio == 0) + return 1; + + t2r4xo(scr, CursorXLo, p.x & 0xFF); + t2r4xo(scr, CursorXHi, (p.x>>8) & 0x0F); + + zoom = (scr->mmio[Zoom] & 0x0F)+1; + y = p.y*zoom; + t2r4xo(scr, CursorYLo, y & 0xFF); + t2r4xo(scr, CursorYHi, (y>>8) & 0x0F); + + return 0; +} + +static void +t2r4curenable(VGAscr* scr) +{ + t2r4enable(scr); + if(scr->mmio == 0) + return; + + /* + * Make sure cursor is off by initialising the cursor + * control to defaults. + */ + t2r4xo(scr, CursorCtl, 0x00); + + /* + * Cursor colour 1 (white), + * cursor colour 2 (black). + */ + t2r4xo(scr, CursorR1, Pwhite); + t2r4xo(scr, CursorG1, Pwhite); + t2r4xo(scr, CursorB1, Pwhite); + + t2r4xo(scr, CursorR2, Pblack); + t2r4xo(scr, CursorG2, Pblack); + t2r4xo(scr, CursorB2, Pblack); + + /* + * Load, locate and enable the cursor, 64x64, mode 2. + */ + t2r4curload(scr, &arrow); + t2r4curmove(scr, ZP); + t2r4xo(scr, CursorCtl, CursorMode); +} + +enum { + Flow = 0x08/4, + Busy = 0x0C/4, + BufCtl = 0x20/4, + DeSorg = 0x28/4, + DeDorg = 0x2C/4, + DeSptch = 0x40/4, + DeDptch = 0x44/4, + CmdOpc = 0x50/4, + CmdRop = 0x54/4, + CmdStyle = 0x58/4, + CmdPatrn = 0x5C/4, + CmdClp = 0x60/4, + CmdPf = 0x64/4, + Fore = 0x68/4, + Back = 0x6C/4, + Mask = 0x70/4, + DeKey = 0x74/4, + Lpat = 0x78/4, + Pctrl = 0x7C/4, + Clptl = 0x80/4, + Clpbr = 0x84/4, + XY0 = 0x88/4, + XY1 = 0x8C/4, + XY2 = 0x90/4, + XY3 = 0x94/4, + XY4 = 0x98/4, + Alpha = 0x128/4, + ACtl = 0x16C/4, + + RBaseD = 0x4000/4, +}; + +/* wait until pipeline ready for new command */ +static void +waitforfifo(VGAscr *scr) +{ + int x; + ulong *d; + x = 0; + + d = scr->mmio + RBaseD; + while((d[Busy]&1) && x++ < 1000000) + ; + if(x >= 1000000) /* shouldn't happen */ + iprint("busy %8lux\n", d[Busy]); +} + +/* wait until command has finished executing */ +static void +waitforcmd(VGAscr *scr) +{ + int x; + ulong *d; + x = 0; + + d = scr->mmio + RBaseD; + while((d[Flow]&0x1B) && x++ < 1000000) + ; + if(x >= 1000000) /* shouldn't happen */ + iprint("flow %8lux\n", d[Flow]); +} + +/* wait until memory controller not busy (i.e. wait for writes to flush) */ +static void +waitformem(VGAscr *scr) +{ + int x; + ulong *d; + x = 0; + + d = scr->mmio + RBaseD; + while((d[Flow]&2)&& x++ < 1000000) + ; + if(x >= 1000000) /* shouldn't happen */ + iprint("mem %8lux\n", d[Busy]); +} + +static int +t2r4hwscroll(VGAscr *scr, Rectangle r, Rectangle sr) +{ + int ctl; + Point dp, sp; + ulong *d; + int depth; + + if(r.min.y == sr.min.y){ /* a purely horizontal scroll */ + depth = scr->gscreen->depth; + switch(depth){ + case 32: + /* + * Using the SGI flat panels with the Ticket-to-Ride IV, horizontal + * 32-bit scrolls don't work perfectly on rectangles of width <= 24. + * we bail on a bigger bound for padding. + */ + if(Dx(r) < 32) + return 0; + break; + case 16: + /* + * Using the SGI flat panels with the Ticket-to-Ride IV, horizontal + * 16-bit scrolls don't work perfectly on rectangles of width <= 96. + * we bail on a bigger bound for padding. + */ + if(Dx(r) < 104) + return 0; + break; + } + } + waitformem(scr); + waitforfifo(scr); + d = scr->mmio + RBaseD; + ctl = 0; + if(r.min.x <= sr.min.x){ + dp.x = r.min.x; + sp.x = sr.min.x; + }else{ + ctl |= 2; + dp.x = r.max.x-1; + sp.x = sr.max.x-1; + } + + if(r.min.y < sr.min.y){ + dp.y = r.min.y; + sp.y = sr.min.y; + }else{ + ctl |= 1; + dp.y = r.max.y-1; + sp.y = sr.max.y-1; + } + + d[CmdOpc] = 0x1; /* bitblt */ + d[CmdRop] = 0xC; /* copy source */ + d[CmdStyle] = 0; + d[CmdPatrn] = 0; + d[Fore] = 0; + d[Back] = 0; + + /* writing XY1 executes cmd */ + d[XY3] = ctl; + d[XY0] = (sp.x<<16)|sp.y; + d[XY2] = (Dx(r)<<16)|Dy(r); + d[XY4] = 0; + d[XY1] = (dp.x<<16)|dp.y; + waitforcmd(scr); + + return 1; +} + +static int +t2r4hwfill(VGAscr *scr, Rectangle r, ulong sval) +{ + ulong *d; + + d = scr->mmio + RBaseD; + + waitformem(scr); + waitforfifo(scr); + d[CmdOpc] = 0x1; /* bitblt */ + d[CmdRop] = 0xC; /* copy source */ + d[CmdStyle] = 1; /* use source from Fore register */ + d[CmdPatrn] = 0; /* no stipple */ + d[Fore] = sval; + d[Back] = sval; + + /* writing XY1 executes cmd */ + d[XY3] = 0; + d[XY0] = (r.min.x<<16)|r.min.y; + d[XY2] = (Dx(r)<<16)|Dy(r); + d[XY4] = 0; + d[XY1] = (r.min.x<<16)|r.min.y; + waitforcmd(scr); + + return 1; +} + +static void +t2r4blank(VGAscr *scr, int blank) +{ + uchar x; + + x = t2r4xi(scr, CursorSyncCtl); + x &= ~0x0F; + if(blank) + x |= HsyncLo | VsyncLo; + t2r4xo(scr, CursorSyncCtl, x); +} + +static void +t2r4drawinit(VGAscr *scr) +{ + ulong pitch; + int depth; + int fmt; + ulong *d; + + pitch = Dx(scr->gscreen->r); + depth = scr->gscreen->depth; + + switch(scr->gscreen->chan){ + case RGB16: + fmt = 3; + break; + case XRGB32: + fmt = 2; + break; + case RGB15: + fmt = 1; + break; + default: + scr->fill = nil; + scr->scroll = nil; + return; + } + + d = scr->mmio + RBaseD; + + d[BufCtl] = fmt<<24; + d[DeSorg] = 0; + d[DeDorg] = 0; + d[DeSptch] = (pitch*depth)/8; + d[DeDptch] = (pitch*depth)/8; + d[CmdClp] = 0; /* 2 = inside rectangle */ + d[Mask] = ~0; + d[DeKey] = 0; + d[Clptl] = 0; + d[Clpbr] = 0xFFF0FFF0; + d[Alpha] = 0; + d[ACtl] = 0; + + scr->fill = t2r4hwfill; + scr->scroll = t2r4hwscroll; + scr->blank = t2r4blank; + hwblank = 1; +} + +VGAdev vgat2r4dev = { + "t2r4", + + t2r4enable, + nil, + nil, + t2r4linear, + t2r4drawinit, +}; + +VGAcur vgat2r4cur = { + "t2r4hwgc", + + t2r4curenable, + t2r4curdisable, + t2r4curload, + t2r4curmove, +}; + diff --git a/os/pc/vgatvp3020.c b/os/pc/vgatvp3020.c new file mode 100644 index 00000000..08871d4a --- /dev/null +++ b/os/pc/vgatvp3020.c @@ -0,0 +1,216 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +/* + * TVP3020 Viewpoint Video Interface Pallette. + * Assumes hooked up to an S3 86C928. + */ +enum { + Index = 0x06, /* Index register */ + Data = 0x07, /* Data register */ +}; + +/* + * Lower 2-bits of indirect DAC register + * addressing. + */ +static ushort dacxreg[4] = { + PaddrW, Pdata, Pixmask, PaddrR +}; + +static uchar +tvp3020io(uchar reg, uchar data) +{ + uchar crt55; + + crt55 = vgaxi(Crtx, 0x55) & 0xFC; + vgaxo(Crtx, 0x55, crt55|((reg>>2) & 0x03)); + vgao(dacxreg[reg & 0x03], data); + + return crt55; +} + +static void +tvp3020xo(uchar index, uchar data) +{ + uchar crt55; + + crt55 = tvp3020io(Index, index); + vgao(dacxreg[Data & 0x03], data); + vgaxo(Crtx, 0x55, crt55); +} + +static void +tvp3020disable(VGAscr*) +{ + uchar r; + + /* + * Disable + * cursor; + * cursor control enable for Bt485 DAC (!); + * the hardware cursor external operation mode. + */ + tvp3020xo(0x06, 0x10); /* Cursor Control Register */ + + r = vgaxi(Crtx, 0x45) & ~0x20; + vgaxo(Crtx, 0x45, r); + + r = vgaxi(Crtx, 0x55) & ~0x20; + vgaxo(Crtx, 0x55, r); +} + +static void +tvp3020enable(VGAscr*) +{ + uchar r; + + /* + * Make sure cursor is off by initialising the cursor + * control to defaults + X-Windows cursor mode. + */ + tvp3020xo(0x06, 0x10); /* Cursor Control Register */ + + /* + * Overscan colour, + * cursor colour 1 (white), + * cursor colour 2 (black). + */ + tvp3020xo(0x20, Pwhite); tvp3020xo(0x21, Pwhite); tvp3020xo(0x22, Pwhite); + tvp3020xo(0x23, Pwhite); tvp3020xo(0x24, Pwhite); tvp3020xo(0x25, Pwhite); + tvp3020xo(0x26, Pblack); tvp3020xo(0x27, Pblack); tvp3020xo(0x28, Pblack); + + /* + * Finally, enable + * the hardware cursor external operation mode; + * cursor control enable for Bt485 DAC (!). + */ + r = vgaxi(Crtx, 0x55)|0x20; + vgaxo(Crtx, 0x55, r); + + r = vgaxi(Crtx, 0x45)|0x20; + vgaxo(Crtx, 0x45, r); +} + +static void +tvp3020load(VGAscr*, Cursor* curs) +{ + uchar p, p0, p1; + int x, y; + + /* + * Make sure cursor is off by initialising the cursor + * control to defaults + X-Windows cursor mode. + */ + tvp3020xo(0x06, 0x10); /* Cursor Control Register */ + + /* + * Initialise the cursor RAM LS and MS address + * (LS must be first). + */ + tvp3020xo(0x08, 0x00); /* Cursor RAM LS Address */ + tvp3020xo(0x09, 0x00); /* Cursor RAM MS Address */ + + /* + * Initialise the 64x64 cursor RAM array. There are 2 planes, + * p0 and p1. Data is written 4 pixels per byte, with p1 the + * MS bit of each pixel. + * The cursor is set in X-Windows mode which gives the following + * truth table: + * p1 p0 colour + * 0 0 underlying pixel colour + * 0 1 underlying pixel colour + * 1 0 cursor colour 1 + * 1 1 cursor colour 2 + * Put the cursor into the top-left of the 64x64 array. + */ + for(y = 0; y < 64; y++){ + for(x = 0; x < 64/8; x++){ + if(x < 16/8 && y < 16){ + p0 = curs->clr[x+y*2]; + p1 = curs->set[x+y*2]; + + p = 0x00; + if(p1 & 0x10) + p |= 0x03; + else if(p0 & 0x10) + p |= 0x02; + if(p1 & 0x20) + p |= 0x0C; + else if(p0 & 0x20) + p |= 0x08; + if(p1 & 0x40) + p |= 0x30; + else if(p0 & 0x40) + p |= 0x20; + if(p1 & 0x80) + p |= 0xC0; + else if(p0 & 0x80) + p |= 0x80; + tvp3020xo(0x0A, p); /* Cursor RAM Data */ + + p = 0x00; + if(p1 & 0x01) + p |= 0x03; + else if(p0 & 0x01) + p |= 0x02; + if(p1 & 0x02) + p |= 0x0C; + else if(p0 & 0x02) + p |= 0x08; + if(p1 & 0x04) + p |= 0x30; + else if(p0 & 0x04) + p |= 0x20; + if(p1 & 0x08) + p |= 0xC0; + else if(p0 & 0x08) + p |= 0x80; + tvp3020xo(0x0A, p); /* Cursor RAM Data */ + } + else{ + tvp3020xo(0x0A, 0x00); /* Cursor RAM Data */ + tvp3020xo(0x0A, 0x00); + } + } + } + + /* + * Initialise the cursor hotpoint + * and enable the cursor. + */ + tvp3020xo(0x04, -curs->offset.x); /* Sprite Origin X */ + tvp3020xo(0x05, -curs->offset.y); /* Sprite Origin Y */ + + tvp3020xo(0x06, 0x40|0x10); /* Cursor Control Register */ +} + +static int +tvp3020move(VGAscr*, Point p) +{ + tvp3020xo(0x00, p.x & 0xFF); /* Cursor Position X LSB */ + tvp3020xo(0x01, (p.x>>8) & 0x0F); /* Cursor Position X MSB */ + tvp3020xo(0x02, p.y & 0xFF); /* Cursor Position Y LSB */ + tvp3020xo(0x03, (p.y>>8) & 0x0F); /* Cursor Position Y MSB */ + + return 0; +} + +VGAcur vgatvp3020cur = { + "tvp3020hwgc", + + tvp3020enable, + tvp3020disable, + tvp3020load, + tvp3020move, +}; diff --git a/os/pc/vgatvp3026.c b/os/pc/vgatvp3026.c new file mode 100644 index 00000000..1b97d1bf --- /dev/null +++ b/os/pc/vgatvp3026.c @@ -0,0 +1,189 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +/* + * TVP3026 Viewpoint Video Interface Pallette. + * Assumes hooked up to an S3 Vision968. + */ +enum { + Index = 0x00, /* Index */ + Data = 0x0A, /* Data */ + + CaddrW = 0x04, /* Colour Write Address */ + Cdata = 0x05, /* Colour Data */ + + Cctl = 0x09, /* Direct Cursor Control */ + Cram = 0x0B, /* Cursor Ram Data */ + Cxlsb = 0x0C, /* Cursor X LSB */ + Cxmsb = 0x0D, /* Cursor X MSB */ + Cylsb = 0x0E, /* Cursor Y LSB */ + Cymsb = 0x0F, /* Cursor Y MSB */ + + Icctl = 0x06, /* Indirect Cursor Control */ +}; + +/* + * Lower 2-bits of indirect DAC register + * addressing. + */ +static ushort dacxreg[4] = { + PaddrW, Pdata, Pixmask, PaddrR +}; + +static uchar +tvp3026io(uchar reg, uchar data) +{ + uchar crt55; + + crt55 = vgaxi(Crtx, 0x55) & 0xFC; + vgaxo(Crtx, 0x55, crt55|((reg>>2) & 0x03)); + vgao(dacxreg[reg & 0x03], data); + + return crt55; +} + +static void +tvp3026o(uchar reg, uchar data) +{ + uchar crt55; + + crt55 = tvp3026io(reg, data); + vgaxo(Crtx, 0x55, crt55); +} + +void +tvp3026xo(uchar index, uchar data) +{ + uchar crt55; + + crt55 = tvp3026io(Index, index); + vgaxo(Crtx, 0x55, crt55|((Data>>2) & 0x03)); + vgao(dacxreg[Data & 0x03], data); + vgaxo(Crtx, 0x55, crt55); +} + +static void +tvp3026disable(VGAscr*) +{ + tvp3026xo(Icctl, 0x90); + tvp3026o(Cctl, 0x00); +} + +static void +tvp3026enable(VGAscr*) +{ + /* + * Make sure cursor is off and direct control enabled. + */ + tvp3026xo(Icctl, 0x90); + tvp3026o(Cctl, 0x00); + + /* + * Overscan colour, + * cursor colour 1 (white), + * cursor colour 2, 3 (black). + */ + tvp3026o(CaddrW, 0x00); + tvp3026o(Cdata, Pwhite); tvp3026o(Cdata, Pwhite); tvp3026o(Cdata, Pwhite); + tvp3026o(Cdata, Pwhite); tvp3026o(Cdata, Pwhite); tvp3026o(Cdata, Pwhite); + tvp3026o(Cdata, Pblack); tvp3026o(Cdata, Pblack); tvp3026o(Cdata, Pblack); + tvp3026o(Cdata, Pblack); tvp3026o(Cdata, Pblack); tvp3026o(Cdata, Pblack); + + /* + * Enable the cursor in 3-colour mode. + */ + tvp3026o(Cctl, 0x01); +} + +static void +tvp3026load(VGAscr* scr, Cursor* curs) +{ + int x, y; + + /* + * Make sure cursor is off by initialising the cursor + * control to defaults. + * Write to the indirect control register to make sure + * direct register is enabled and upper 2 bits of cursor + * RAM address are 0. + * The LSBs of the cursor RAM address are in PaddrW. + */ + tvp3026xo(Icctl, 0x90); + tvp3026o(Cctl, 0x00); + vgao(PaddrW, 0x00); + + /* + * Initialise the 64x64 cursor RAM array. There are 2 planes, + * p0 and p1. Data is written 8 pixels per byte, with p0 in the + * first 512 bytes of the array and p1 in the second. + * The cursor is set in 3-colour mode which gives the following + * truth table: + * p1 p0 colour + * 0 0 transparent + * 0 1 cursor colour 0 + * 1 0 cursor colour 1 + * 1 1 cursor colour 2 + * Put the cursor into the top-left of the 64x64 array. + * The 0,0 cursor point is bottom-right, so positioning will + * have to take that into account. + */ + for(y = 0; y < 64; y++){ + for(x = 0; x < 64/8; x++){ + if(x < 16/8 && y < 16) + tvp3026o(Cram, curs->clr[x+y*2]); + else + tvp3026o(Cram, 0x00); + } + } + for(y = 0; y < 64; y++){ + for(x = 0; x < 64/8; x++){ + if(x < 16/8 && y < 16) + tvp3026o(Cram, curs->set[x+y*2]); + else + tvp3026o(Cram, 0x00); + } + } + + /* + * Initialise the cursor hotpoint + * and enable the cursor in 3-colour mode. + */ + scr->offset.x = 64+curs->offset.x; + scr->offset.y = 64+curs->offset.y; + tvp3026o(Cctl, 0x01); +} + +static int +tvp3026move(VGAscr* scr, Point p) +{ + int x, y; + + x = p.x+scr->offset.x; + y = p.y+scr->offset.y; + + tvp3026o(Cxlsb, x & 0xFF); + tvp3026o(Cxmsb, (x>>8) & 0x0F); + tvp3026o(Cylsb, y & 0xFF); + tvp3026o(Cymsb, (y>>8) & 0x0F); + + return 0; +} + +VGAcur vgatvp3026cur = { + "tvp3026hwgc", + + tvp3026enable, + tvp3026disable, + tvp3026load, + tvp3026move, +}; diff --git a/os/pc/vgavmware.c b/os/pc/vgavmware.c new file mode 100644 index 00000000..4cf09e48 --- /dev/null +++ b/os/pc/vgavmware.c @@ -0,0 +1,386 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +enum { + PCIVMWARE = 0x15AD, /* PCI VID */ + + VMWARE1 = 0x0710, /* PCI DID */ + VMWARE2 = 0x0405, +}; + +enum { + Rid = 0, + Renable, + Rwidth, + Rheight, + Rmaxwidth, + + Rmaxheight, + Rdepth, + Rbpp, + Rpseudocolor, + Rrmask, + + Rgmask, + Rbmask, + Rbpl, + Rfbstart, + Rfboffset, + + Rfbmaxsize, + Rfbsize, + Rcap, + Rmemstart, + Rmemsize, + + Rconfigdone, + Rsync, + Rbusy, + Rguestid, + Rcursorid, + + Rcursorx, + Rcursory, + Rcursoron, + Nreg, + + Crectfill = 1<<0, + Crectcopy = 1<<1, + Crectpatfill = 1<<2, + Coffscreen = 1<<3, + Crasterop = 1<<4, + Ccursor = 1<<5, + Ccursorbypass = 1<<6, + Ccursorbypass2 = 1<<7, + C8bitemulation = 1<<8, + Calphacursor = 1<<9, + + FifoMin = 0, + FifoMax = 1, + FifoNextCmd = 2, + FifoStop = 3, + FifoUser = 4, + + Xupdate = 1, + Xrectfill = 2, + Xrectcopy = 3, + Xdefinebitmap = 4, + Xdefinebitmapscanline = 5, + Xdefinepixmap = 6, + Xdefinepixmapscanline = 7, + Xrectbitmapfill = 8, + Xrectpixmapfill = 9, + Xrectbitmapcopy = 10, + Xrectpixmapcopy = 11, + Xfreeobject = 12, + Xrectropfill = 13, + Xrectropcopy = 14, + Xrectropbitmapfill = 15, + Xrectroppixmapfill = 16, + Xrectropbitmapcopy = 17, + Xrectroppixmapcopy = 18, + Xdefinecursor = 19, + Xdisplaycursor = 20, + Xmovecursor = 21, + Xdefinealphacursor = 22, + Xcmdmax = 23, + + CursorOnHide = 0, + CursorOnShow = 1, + CursorOnRemoveFromFb = 2, + CursorOnRestoreToFb = 3, + + Rpalette = 1024, +}; + +typedef struct Vmware Vmware; +struct Vmware { + ulong fb; + + ulong ra; + ulong rd; + + ulong r[Nreg]; + ulong *mmio; + ulong mmiosize; + + char chan[32]; + int depth; +}; + +Vmware xvm; +Vmware *vm=&xvm; + +static ulong +vmrd(Vmware *vm, int i) +{ + outl(vm->ra, i); + return inl(vm->rd); +} + +static void +vmwr(Vmware *vm, int i, ulong v) +{ + outl(vm->ra, i); + outl(vm->rd, v); +} + +static void +vmwait(Vmware *vm) +{ + vmwr(vm, Rsync, 1); + while(vmrd(vm, Rbusy)) + ; +} + +static ulong +vmwarelinear(VGAscr* scr, int* size, int* align) +{ + char err[64]; + ulong aperture, oaperture; + int osize, oapsize, wasupamem; + Pcidev *p; + + osize = *size; + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + p = pcimatch(nil, PCIVMWARE, 0); + if(p == nil) + error("no vmware card found"); + + switch(p->did){ + default: + snprint(err, sizeof err, "unknown vmware id %.4ux", p->did); + error(err); + + case VMWARE1: + vm->ra = 0x4560; + vm->rd = 0x4560+4; + break; + + case VMWARE2: + vm->ra = p->mem[0].bar&~3; + vm->rd = vm->ra + 1; + } + + aperture = (ulong)(vmrd(vm, Rfbstart)); + *size = vmrd(vm, Rfbsize); + + if(wasupamem) + upafree(oaperture, oapsize); + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)) + scr->isupamem = 1; + }else + scr->isupamem = 1; + + if(oaperture && aperture != oaperture) + print("warning (BUG): redefinition of aperture does not change vmwarescreen segment\n"); + addvgaseg("vmwarescreen", aperture, osize); + + return aperture; +} + +static void +vmfifowr(Vmware *vm, ulong v) +{ + ulong *mm; + + mm = vm->mmio; + if(mm == nil){ + iprint("!"); + return; + } + + if(mm[FifoNextCmd]+sizeof(ulong) == mm[FifoStop] + || (mm[FifoNextCmd]+sizeof(ulong) == mm[FifoMax] + && mm[FifoStop] == mm[FifoMin])) + vmwait(vm); + + mm[mm[FifoNextCmd]/sizeof(ulong)] = v; + + /* must do this way so mm[FifoNextCmd] is never mm[FifoMax] */ + v = mm[FifoNextCmd] + sizeof(ulong); + if(v == mm[FifoMax]) + v = mm[FifoMin]; + mm[FifoNextCmd] = v; +} + +static void +vmwareflush(VGAscr*, Rectangle r) +{ + if(vm->mmio == nil) + return; + + vmfifowr(vm, Xupdate); + vmfifowr(vm, r.min.x); + vmfifowr(vm, r.min.y); + vmfifowr(vm, r.max.x-r.min.x); + vmfifowr(vm, r.max.y-r.min.y); + vmwait(vm); +} + +static void +vmwareload(VGAscr*, Cursor *c) +{ + int i; + ulong clr, set; + ulong and[16]; + ulong xor[16]; + + if(vm->mmio == nil) + return; + vmfifowr(vm, Xdefinecursor); + vmfifowr(vm, 1); /* cursor id */ + vmfifowr(vm, -c->offset.x); + vmfifowr(vm, -c->offset.y); + + vmfifowr(vm, 16); /* width */ + vmfifowr(vm, 16); /* height */ + vmfifowr(vm, 1); /* depth for and mask */ + vmfifowr(vm, 1); /* depth for xor mask */ + + for(i=0; i<16; i++){ + clr = (c->clr[i*2+1]<<8) | c->clr[i*2]; + set = (c->set[i*2+1]<<8) | c->set[i*2]; + and[i] = ~(clr|set); /* clr and set pixels => black */ + xor[i] = clr&~set; /* clr pixels => white */ + } + for(i=0; i<16; i++) + vmfifowr(vm, and[i]); + for(i=0; i<16; i++) + vmfifowr(vm, xor[i]); + + vmwait(vm); +} + +static int +vmwaremove(VGAscr*, Point p) +{ + vmwr(vm, Rcursorid, 1); + vmwr(vm, Rcursorx, p.x); + vmwr(vm, Rcursory, p.y); + vmwr(vm, Rcursoron, CursorOnShow); + return 0; +} + +static void +vmwaredisable(VGAscr*) +{ + vmwr(vm, Rcursorid, 1); + vmwr(vm, Rcursoron, CursorOnHide); +} + +static void +vmwareenable(VGAscr*) +{ + vmwr(vm, Rcursorid, 1); + vmwr(vm, Rcursoron, CursorOnShow); +} + +static void +vmwareblank(int) +{ +} + +static int +vmwarescroll(VGAscr*, Rectangle r, Rectangle sr) +{ + if(vm->mmio == nil) + return 0; + vmfifowr(vm, Xrectcopy); + vmfifowr(vm, sr.min.x); + vmfifowr(vm, sr.min.y); + vmfifowr(vm, r.min.x); + vmfifowr(vm, r.min.y); + vmfifowr(vm, Dx(r)); + vmfifowr(vm, Dy(r)); + vmwait(vm); + return 1; +} + +static int +vmwarefill(VGAscr*, Rectangle r, ulong sval) +{ + if(vm->mmio == nil) + return 0; + vmfifowr(vm, Xrectfill); + vmfifowr(vm, sval); + vmfifowr(vm, r.min.x); + vmfifowr(vm, r.min.y); + vmfifowr(vm, r.max.x-r.min.x); + vmfifowr(vm, r.max.y-r.min.y); + vmwait(vm); + return 1; +} + +static void +vmwaredrawinit(VGAscr *scr) +{ + ulong offset; + ulong mmiobase, mmiosize; + + if(scr->mmio==nil){ + mmiobase = vmrd(vm, Rmemstart); + if(mmiobase == 0) + return; + mmiosize = vmrd(vm, Rmemsize); + scr->mmio = KADDR(upamalloc(mmiobase, mmiosize, 0)); + vm->mmio = scr->mmio; + vm->mmiosize = mmiosize; + if(scr->mmio == nil) + return; + addvgaseg("vmwaremmio", mmiobase, mmiosize); + } + + scr->mmio[FifoMin] = 4*sizeof(ulong); + scr->mmio[FifoMax] = vm->mmiosize; + scr->mmio[FifoNextCmd] = 4*sizeof(ulong); + scr->mmio[FifoStop] = 4*sizeof(ulong); + vmwr(vm, Rconfigdone, 1); + + scr->scroll = vmwarescroll; + scr->fill = vmwarefill; + + offset = vmrd(vm, Rfboffset); + scr->gscreendata->bdata += offset; +} + +VGAdev vgavmwaredev = { + "vmware", + + 0, + 0, + 0, + vmwarelinear, + vmwaredrawinit, + 0, + 0, + 0, + vmwareflush, +}; + +VGAcur vgavmwarecur = { + "vmwarehwgc", + + vmwareenable, + vmwaredisable, + vmwareload, + vmwaremove, +}; diff --git a/os/pc/vgax.c b/os/pc/vgax.c new file mode 100644 index 00000000..c765508e --- /dev/null +++ b/os/pc/vgax.c @@ -0,0 +1,102 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +static Lock vgaxlock; /* access to index registers */ + +int +vgaxi(long port, uchar index) +{ + uchar data; + + ilock(&vgaxlock); + switch(port){ + + case Seqx: + case Crtx: + case Grx: + outb(port, index); + data = inb(port+1); + break; + + case Attrx: + /* + * Allow processor access to the colour + * palette registers. Writes to Attrx must + * be preceded by a read from Status1 to + * initialise the register to point to the + * index register and not the data register. + * Processor access is allowed by turning + * off bit 0x20. + */ + inb(Status1); + if(index < 0x10){ + outb(Attrx, index); + data = inb(Attrx+1); + inb(Status1); + outb(Attrx, 0x20|index); + } + else{ + outb(Attrx, 0x20|index); + data = inb(Attrx+1); + } + break; + + default: + iunlock(&vgaxlock); + return -1; + } + iunlock(&vgaxlock); + + return data & 0xFF; +} + +int +vgaxo(long port, uchar index, uchar data) +{ + ilock(&vgaxlock); + switch(port){ + + case Seqx: + case Crtx: + case Grx: + /* + * We could use an outport here, but some chips + * (e.g. 86C928) have trouble with that for some + * registers. + */ + outb(port, index); + outb(port+1, data); + break; + + case Attrx: + inb(Status1); + if(index < 0x10){ + outb(Attrx, index); + outb(Attrx, data); + inb(Status1); + outb(Attrx, 0x20|index); + } + else{ + outb(Attrx, 0x20|index); + outb(Attrx, data); + } + break; + + default: + iunlock(&vgaxlock); + return -1; + } + iunlock(&vgaxlock); + + return 0; +} diff --git a/os/pc/wavelan.c b/os/pc/wavelan.c new file mode 100644 index 00000000..0916f460 --- /dev/null +++ b/os/pc/wavelan.c @@ -0,0 +1,1268 @@ +/* + Lucent Wavelan IEEE 802.11 pcmcia. + There is almost no documentation for the card. + the driver is done using both the FreeBSD, Linux and + original Plan 9 drivers as `documentation'. + + Has been used with the card plugged in during all up time. + no cards removals/insertions yet. + + For known BUGS see the comments below. Besides, + the driver keeps interrupts disabled for just too + long. When it gets robust, locks should be revisited. + + BUGS: check endian, alignment and mem/io issues; + multicast; + receive watchdog interrupts. + TODO: automatic power management; + improve locking. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" +#include "etherif.h" +#include "wavelan.h" + +enum +{ + MSperTick= 50, /* ms between ticks of kproc */ +}; + +/* + * When we're using a PCI device and memory-mapped I/O, + * the registers are spaced out as though each takes 32 bits, + * even though they are only 16-bit registers. Thus, + * ctlr->mmb[reg] is the right way to access register reg, + * even though a priori you'd expect to use ctlr->mmb[reg/2]. + */ +void +csr_outs(Ctlr *ctlr, int reg, ushort arg) +{ + if(ctlr->mmb) + ctlr->mmb[reg] = arg; + else + outs(ctlr->iob+reg, arg); +} + +ushort +csr_ins(Ctlr *ctlr, int reg) +{ + if(ctlr->mmb) + return ctlr->mmb[reg]; + else + return ins(ctlr->iob+reg); +} + +static void +csr_ack(Ctlr *ctlr, int ev) +{ + csr_outs(ctlr, WR_EvAck, ev); +} + +static void +csr_inss(Ctlr *ctlr, int reg, void *dat, int ndat) +{ + ushort *rp, *wp; + + if(ctlr->mmb){ + rp = &ctlr->mmb[reg]; + wp = dat; + while(ndat-- > 0) + *wp++ = *rp; + }else + inss(ctlr->iob+reg, dat, ndat); +} + +static void +csr_outss(Ctlr *ctlr, int reg, void *dat, int ndat) +{ + ushort *rp, *wp; + + if(ctlr->mmb){ + rp = dat; + wp = &ctlr->mmb[reg]; + while(ndat-- > 0) + *wp = *rp++; + }else + outss(ctlr->iob+reg, dat, ndat); +} + +// w_... routines do not ilock the Ctlr and should +// be called locked. + +void +w_intdis(Ctlr* ctlr) +{ + csr_outs(ctlr, WR_IntEna, 0); + csr_ack(ctlr, 0xffff); +} + +static void +w_intena(Ctlr* ctlr) +{ + csr_outs(ctlr, WR_IntEna, WEvs); +} + +int +w_cmd(Ctlr *ctlr, ushort cmd, ushort arg) +{ + int i, rc; + + for(i=0; ictlrno, cmd, csr_ins(ctlr, WR_Cmd)); + return -1; + } + + csr_outs(ctlr, WR_Parm0, arg); + csr_outs(ctlr, WR_Cmd, cmd); + + for(i=0; ictlrno, cmd, i); + if(i == IniTmOut){ + print("#l%d: execing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_EvSts)); + return -1; + } + } + rc = csr_ins(ctlr, WR_Sts); + csr_ack(ctlr, WCmdEv); + + if((rc&WCmdMsk) != (cmd&WCmdMsk)){ + print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc); + return -1; + } + if(rc&WResSts){ + /* + * Don't print; this happens on every WCmdAccWr for some reason. + */ + if(0) print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc); + return -1; + } + return 0; +} + +static int +w_seek(Ctlr* ctlr, ushort id, ushort offset, int chan) +{ + int i, rc; + static ushort sel[] = { WR_Sel0, WR_Sel1 }; + static ushort off[] = { WR_Off0, WR_Off1 }; + + if(chan != 0 && chan != 1) + panic("wavelan: bad chan\n"); + csr_outs(ctlr, sel[chan], id); + csr_outs(ctlr, off[chan], offset); + for (i=0; itype)){ + DEBUG("wavelan: access read failed\n"); + return -1; + } + if(w_seek(ctlr,ltv->type,0,1)){ + DEBUG("wavelan: seek failed\n"); + return -1; + } + len = csr_ins(ctlr, WR_Data1); + if(len > ltv->len) + return -1; + ltv->len = len; + if((code=csr_ins(ctlr, WR_Data1)) != ltv->type){ + USED(code); + DEBUG("wavelan: type %x != code %x\n",ltv->type,code); + return -1; + } + if(ltv->len > 0) + csr_inss(ctlr, WR_Data1, <v->val, ltv->len-1); + + return 0; +} + +static void +w_outltv(Ctlr* ctlr, Wltv* ltv) +{ + if(w_seek(ctlr,ltv->type, 0, 1)) + return; + csr_outss(ctlr, WR_Data1, ltv, ltv->len+1); + w_cmd(ctlr, WCmdAccWr, ltv->type); +} + +void +ltv_outs(Ctlr* ctlr, int type, ushort val) +{ + Wltv ltv; + + ltv.len = 2; + ltv.type = type; + ltv.val = val; + w_outltv(ctlr, <v); +} + +int +ltv_ins(Ctlr* ctlr, int type) +{ + Wltv ltv; + + ltv.len = 2; + ltv.type = type; + ltv.val = 0; + if(w_inltv(ctlr, <v)) + return -1; + return ltv.val; +} + +static void +ltv_outstr(Ctlr* ctlr, int type, char* val) +{ + Wltv ltv; + int len; + + len = strlen(val); + if(len > sizeof(ltv.s)) + len = sizeof(ltv.s); + memset(<v, 0, sizeof(ltv)); + ltv.len = (sizeof(ltv.type)+sizeof(ltv.slen)+sizeof(ltv.s))/2; + ltv.type = type; + +// This should be ltv.slen = len; according to Axel Belinfante + ltv.slen = len; + + strncpy(ltv.s, val, len); + w_outltv(ctlr, <v); +} + +static char Unkname[] = "who knows"; +static char Nilname[] = "card does not tell"; + +static char* +ltv_inname(Ctlr* ctlr, int type) +{ + static Wltv ltv; + int len; + + memset(<v,0,sizeof(ltv)); + ltv.len = WNameLen/2+2; + ltv.type = type; + if(w_inltv(ctlr, <v)) + return Unkname; + len = ltv.slen; + if(len == 0 || ltv.s[0] == 0) + return Nilname; + if(len >= sizeof ltv.s) + len = sizeof ltv.s - 1; + ltv.s[len] = '\0'; + return ltv.s; +} + +static int +w_read(Ctlr* ctlr, int type, int off, void* buf, ulong len) +{ + if(w_seek(ctlr, type, off, 1)){ + DEBUG("wavelan: w_read: seek failed"); + return 0; + } + csr_inss(ctlr, WR_Data1, buf, len/2); + + return len; +} + +static int +w_write(Ctlr* ctlr, int type, int off, void* buf, ulong len) +{ + int tries; + + for (tries=0; tries < WTmOut; tries++){ + if(w_seek(ctlr, type, off, 0)){ + DEBUG("wavelan: w_write: seek failed\n"); + return 0; + } + + csr_outss(ctlr, WR_Data0, buf, len/2); + + csr_outs(ctlr, WR_Data0, 0xdead); + csr_outs(ctlr, WR_Data0, 0xbeef); + if(w_seek(ctlr, type, off + len, 0)){ + DEBUG("wavelan: write seek failed\n"); + return 0; + } + if(csr_ins(ctlr, WR_Data0) == 0xdead) + if(csr_ins(ctlr, WR_Data0) == 0xbeef) + return len; + DEBUG("wavelan: Hermes bug byte.\n"); + return 0; + } + DEBUG("wavelan: tx timeout\n"); + return 0; +} + +static int +w_alloc(Ctlr* ctlr, int len) +{ + int rc; + int i,j; + + if(w_cmd(ctlr, WCmdMalloc, len)==0) + for (i = 0; ictlr; + + if(!ctlr) + return -1; + + w_intdis(ctlr); + w_cmd(ctlr, WCmdDis, 0); + w_intdis(ctlr); + if(w_cmd(ctlr, WCmdIni, 0)) + return -1; + w_intdis(ctlr); + + ltv_outs(ctlr, WTyp_Tick, 8); + ltv_outs(ctlr, WTyp_MaxLen, ctlr->maxlen); + ltv_outs(ctlr, WTyp_Ptype, ctlr->ptype); + ltv_outs(ctlr, WTyp_CreateIBSS, ctlr->createibss); + ltv_outs(ctlr, WTyp_RtsThres, ctlr->rtsthres); + ltv_outs(ctlr, WTyp_TxRate, ctlr->txrate); + ltv_outs(ctlr, WTyp_ApDens, ctlr->apdensity); + ltv_outs(ctlr, WTyp_PMWait, ctlr->pmwait); + ltv_outs(ctlr, WTyp_PM, ctlr->pmena); + if(*ctlr->netname) + ltv_outstr(ctlr, WTyp_NetName, ctlr->netname); + if(*ctlr->wantname) + ltv_outstr(ctlr, WTyp_WantName, ctlr->wantname); + ltv_outs(ctlr, WTyp_Chan, ctlr->chan); + if(*ctlr->nodename) + ltv_outstr(ctlr, WTyp_NodeName, ctlr->nodename); + ltv.len = 4; + ltv.type = WTyp_Mac; + memmove(ltv.addr, ether->ea, Eaddrlen); + w_outltv(ctlr, <v); + + ltv_outs(ctlr, WTyp_Prom, (ether->prom?1:0)); + + if(ctlr->hascrypt && ctlr->crypt){ + ltv_outs(ctlr, WTyp_Crypt, ctlr->crypt); + ltv_outs(ctlr, WTyp_TxKey, ctlr->txkey); + w_outltv(ctlr, &ctlr->keys); + ltv_outs(ctlr, WTyp_XClear, ctlr->xclear); + } + + // BUG: set multicast addresses + + if(w_cmd(ctlr, WCmdEna, 0)){ + DEBUG("wavelan: Enable failed"); + return -1; + } + ctlr->txdid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8); + ctlr->txmid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8); + if(ctlr->txdid == -1 || ctlr->txmid == -1) + DEBUG("wavelan: alloc failed"); + ctlr->txbusy = 0; + w_intena(ctlr); + return 0; +} + +static void +w_rxdone(Ether* ether) +{ + Ctlr* ctlr = (Ctlr*) ether->ctlr; + int len, sp; + WFrame f; + Block* bp=0; + Etherpkt* ep; + + sp = csr_ins(ctlr, WR_RXId); + len = w_read(ctlr, sp, 0, &f, sizeof(f)); + if(len == 0){ + DEBUG("wavelan: read frame error\n"); + goto rxerror; + } + if(f.sts&WF_Err){ + goto rxerror; + } + switch(f.sts){ + case WF_1042: + case WF_Tunnel: + case WF_WMP: + len = f.dlen + WSnapHdrLen; + bp = iallocb(ETHERHDRSIZE + len + 2); + if(!bp) + goto rxerror; + ep = (Etherpkt*) bp->wp; + memmove(ep->d, f.addr1, Eaddrlen); + memmove(ep->s, f.addr2, Eaddrlen); + memmove(ep->type,&f.type,2); + bp->wp += ETHERHDRSIZE; + if(w_read(ctlr, sp, WF_802_11_Off, bp->wp, len+2) == 0){ + DEBUG("wavelan: read 802.11 error\n"); + goto rxerror; + } + bp->wp = bp->rp+(ETHERHDRSIZE+f.dlen); + break; + default: + len = ETHERHDRSIZE + f.dlen + 2; + bp = iallocb(len); + if(!bp) + goto rxerror; + if(w_read(ctlr, sp, WF_802_3_Off, bp->wp, len) == 0){ + DEBUG("wavelan: read 800.3 error\n"); + goto rxerror; + } + bp->wp += len; + } + + ctlr->nrx++; + etheriq(ether,bp,1); + ctlr->signal = ((ctlr->signal*15)+((f.qinfo>>8) & 0xFF))/16; + ctlr->noise = ((ctlr->noise*15)+(f.qinfo & 0xFF))/16; + return; + +rxerror: + freeb(bp); + ctlr->nrxerr++; +} + +static void +w_txstart(Ether* ether) +{ + Etherpkt *pkt; + Ctlr *ctlr; + Block *bp; + int len, off; + + if((ctlr = ether->ctlr) == nil || (ctlr->state & (Attached|Power)) != (Attached|Power) || ctlr->txbusy) + return; + + if((bp = qget(ether->oq)) == nil) + return; + pkt = (Etherpkt*)bp->rp; + + // + // If the packet header type field is > 1500 it is an IP or + // ARP datagram, otherwise it is an 802.3 packet. See RFC1042. + // + memset(&ctlr->txf, 0, sizeof(ctlr->txf)); + if(((pkt->type[0]<<8)|pkt->type[1]) > 1500){ + ctlr->txf.framectl = WF_Data; + memmove(ctlr->txf.addr1, pkt->d, Eaddrlen); + memmove(ctlr->txf.addr2, pkt->s, Eaddrlen); + memmove(ctlr->txf.dstaddr, pkt->d, Eaddrlen); + memmove(ctlr->txf.srcaddr, pkt->s, Eaddrlen); + memmove(&ctlr->txf.type, pkt->type, 2); + bp->rp += ETHERHDRSIZE; + len = BLEN(bp); + off = WF_802_11_Off; + ctlr->txf.dlen = len+ETHERHDRSIZE-WSnapHdrLen; + hnputs((uchar*)&ctlr->txf.dat[0], WSnap0); + hnputs((uchar*)&ctlr->txf.dat[1], WSnap1); + hnputs((uchar*)&ctlr->txf.len, len+ETHERHDRSIZE-WSnapHdrLen); + } + else{ + len = BLEN(bp); + off = WF_802_3_Off; + ctlr->txf.dlen = len; + } + w_write(ctlr, ctlr->txdid, 0, &ctlr->txf, sizeof(ctlr->txf)); + w_write(ctlr, ctlr->txdid, off, bp->rp, len+2); + + if(w_cmd(ctlr, WCmdReclaim|WCmdTx, ctlr->txdid)){ + DEBUG("wavelan: transmit failed\n"); + ctlr->ntxerr++; + } + else{ + ctlr->txbusy = 1; + ctlr->txtmout = 2; + } + freeb(bp); +} + +static void +w_txdone(Ctlr* ctlr, int sts) +{ + ctlr->txbusy = 0; + ctlr->txtmout = 0; + if(sts & WTxErrEv) + ctlr->ntxerr++; + else + ctlr->ntx++; +} + +/* save the stats info in the ctlr struct */ +static void +w_stats(Ctlr* ctlr, int len) +{ + int i, rc; + ulong* p = (ulong*)&ctlr->WStats; + ulong* pend = (ulong*)&ctlr->end; + + for (i = 0; i < len && p < pend; i++){ + rc = csr_ins(ctlr, WR_Data1); + if(rc > 0xf000) + rc = ~rc & 0xffff; + p[i] += rc; + } +} + +/* send the base station scan info to any readers */ +static void +w_scaninfo(Ether* ether, Ctlr *ctlr, int len) +{ + int i, j; + Netfile **ep, *f, **fp; + Block *bp; + WScan *wsp; + ushort *scanbuf; + + scanbuf = malloc(len*2); + if(scanbuf == nil) + return; + + for (i = 0; i < len ; i++) + scanbuf[i] = csr_ins(ctlr, WR_Data1); + + /* calculate number of samples */ + len /= 25; + if(len == 0) + goto out; + + i = ether->scan; + ep = ðer->f[Ntypes]; + for(fp = ether->f; fp < ep && i > 0; fp++){ + f = *fp; + if(f == nil || f->scan == 0) + continue; + + bp = iallocb(100*len); + if(bp == nil) + break; + for(j = 0; j < len; j++){ + wsp = (WScan*)(&scanbuf[j*25]); + if(wsp->ssid_len > 32) + wsp->ssid_len = 32; + bp->wp = (uchar*)seprint((char*)bp->wp, (char*)bp->lim, + "ssid=%.*s;bssid=%E;signal=%d;noise=%d;chan=%d%s\n", + wsp->ssid_len, wsp->ssid, wsp->bssid, wsp->signal, + wsp->noise, wsp->chan, (wsp->capinfo&(1<<4))?";wep":""); + } + qpass(f->in, bp); + i--; + } +out: + free(scanbuf); +} + +static int +w_info(Ether *ether, Ctlr* ctlr) +{ + int sp; + Wltv ltv; + + sp = csr_ins(ctlr, WR_InfoId); + ltv.len = ltv.type = 0; + w_read(ctlr, sp, 0, <v, 4); + ltv.len--; + switch(ltv.type){ + case WTyp_Stats: + w_stats(ctlr, ltv.len); + return 0; + case WTyp_Scan: + w_scaninfo(ether, ctlr, ltv.len); + return 0; + } + return -1; +} + +/* set scanning interval */ +static void +w_scanbs(void *a, uint secs) +{ + Ether *ether = a; + Ctlr* ctlr = (Ctlr*) ether->ctlr; + + ctlr->scanticks = secs*(1000/MSperTick); +} + +static void +w_intr(Ether *ether) +{ + int rc, txid; + Ctlr* ctlr = (Ctlr*) ether->ctlr; + + if((ctlr->state & Power) == 0) + return; + + if((ctlr->state & Attached) == 0){ + csr_ack(ctlr, 0xffff); + csr_outs(ctlr, WR_IntEna, 0); + return; + } + + rc = csr_ins(ctlr, WR_EvSts); + csr_ack(ctlr, ~WEvs); // Not interested in them + if(rc & WRXEv){ + w_rxdone(ether); + csr_ack(ctlr, WRXEv); + } + if(rc & WTXEv){ + w_txdone(ctlr, rc); + csr_ack(ctlr, WTXEv); + } + if(rc & WAllocEv){ + ctlr->nalloc++; + txid = csr_ins(ctlr, WR_Alloc); + csr_ack(ctlr, WAllocEv); + if(txid == ctlr->txdid){ + if((rc & WTXEv) == 0) + w_txdone(ctlr, rc); + } + } + if(rc & WInfoEv){ + ctlr->ninfo++; + w_info(ether, ctlr); + csr_ack(ctlr, WInfoEv); + } + if(rc & WTxErrEv){ + w_txdone(ctlr, rc); + csr_ack(ctlr, WTxErrEv); + } + if(rc & WIDropEv){ + ctlr->nidrop++; + csr_ack(ctlr, WIDropEv); + } + w_txstart(ether); +} + +// Watcher to ensure that the card still works properly and +// to request WStats updates once a minute. +// BUG: it runs much more often, see the comment below. + +static void +w_timer(void* arg) +{ + Ether* ether = (Ether*) arg; + Ctlr* ctlr = (Ctlr*)ether->ctlr; + + ctlr->timerproc = up; + for(;;){ + tsleep(&up->sleep, return0, 0, MSperTick); + ctlr = (Ctlr*)ether->ctlr; + if(ctlr == 0) + break; + if((ctlr->state & (Attached|Power)) != (Attached|Power)) + continue; + ctlr->ticks++; + + ilock(ctlr); + + // Seems that the card gets frames BUT does + // not send the interrupt; this is a problem because + // I suspect it runs out of receive buffers and + // stops receiving until a transmit watchdog + // reenables the card. + // The problem is serious because it leads to + // poor rtts. + // This can be seen clearly by commenting out + // the next if and doing a ping: it will stop + // receiving (although the icmp replies are being + // issued from the remote) after a few seconds. + // Of course this `bug' could be because I'm reading + // the card frames in the wrong way; due to the + // lack of documentation I cannot know. + + if(csr_ins(ctlr, WR_EvSts)&WEvs){ + ctlr->tickintr++; + w_intr(ether); + } + + if((ctlr->ticks % 10) == 0) { + if(ctlr->txtmout && --ctlr->txtmout == 0){ + ctlr->nwatchdogs++; + w_txdone(ctlr, WTxErrEv); + if(w_enable(ether)){ + DEBUG("wavelan: wdog enable failed\n"); + } + w_txstart(ether); + } + if((ctlr->ticks % 120) == 0) + if(ctlr->txbusy == 0) + w_cmd(ctlr, WCmdEnquire, WTyp_Stats); + if(ctlr->scanticks > 0) + if((ctlr->ticks % ctlr->scanticks) == 0) + if(ctlr->txbusy == 0) + w_cmd(ctlr, WCmdEnquire, WTyp_Scan); + } + iunlock(ctlr); + } + pexit("terminated", 0); +} + +void +w_multicast(void*, uchar*, int) +{ + // BUG: to be added. +} + +void +w_attach(Ether* ether) +{ + Ctlr* ctlr; + char name[64]; + int rc; + + if(ether->ctlr == 0) + return; + + snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno); + ctlr = (Ctlr*) ether->ctlr; + if((ctlr->state & Attached) == 0){ + ilock(ctlr); + rc = w_enable(ether); + iunlock(ctlr); + if(rc == 0){ + ctlr->state |= Attached; + kproc(name, w_timer, ether, 0); + } else + print("#l%d: enable failed\n",ether->ctlrno); + } +} + +void +w_detach(Ether* ether) +{ + Ctlr* ctlr; + char name[64]; + + if(ether->ctlr == nil) + return; + + snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno); + ctlr = (Ctlr*) ether->ctlr; + if(ctlr->state & Attached){ + ilock(ctlr); + w_intdis(ctlr); + if(ctlr->timerproc){ + if(!postnote(ctlr->timerproc, 1, "kill", 0)) + print("timerproc note not posted\n"); + print("w_detach, killing 0x%p\n", ctlr->timerproc); + } + ctlr->state &= ~Attached; + iunlock(ctlr); + } + ether->ctlr = nil; +} + +void +w_power(Ether* ether, int on) +{ + Ctlr *ctlr; + + ctlr = (Ctlr*) ether->ctlr; + ilock(ctlr); +iprint("w_power %d\n", on); + if(on){ + if((ctlr->state & Power) == 0){ + if (wavelanreset(ether, ctlr) < 0){ + iprint("w_power: reset failed\n"); + iunlock(ctlr); + w_detach(ether); + free(ctlr); + return; + } + if(ctlr->state & Attached) + w_enable(ether); + ctlr->state |= Power; + } + }else{ + if(ctlr->state & Power){ + if(ctlr->state & Attached) + w_intdis(ctlr); + ctlr->state &= ~Power; + } + } + iunlock(ctlr); +} + +#define PRINTSTAT(fmt,val) l += snprint(p+l, READSTR-l, (fmt), (val)) +#define PRINTSTR(fmt) l += snprint(p+l, READSTR-l, (fmt)) + +long +w_ifstat(Ether* ether, void* a, long n, ulong offset) +{ + Ctlr *ctlr = (Ctlr*) ether->ctlr; + char *k, *p; + int i, l, txid; + + ether->oerrs = ctlr->ntxerr; + ether->crcs = ctlr->nrxfcserr; + ether->frames = 0; + ether->buffs = ctlr->nrxdropnobuf; + ether->overflows = 0; + + // + // Offset must be zero or there's a possibility the + // new data won't match the previous read. + // + if(n == 0 || offset != 0) + return 0; + + p = malloc(READSTR); + l = 0; + + PRINTSTAT("Signal: %d\n", ctlr->signal-149); + PRINTSTAT("Noise: %d\n", ctlr->noise-149); + PRINTSTAT("SNR: %ud\n", ctlr->signal-ctlr->noise); + PRINTSTAT("Interrupts: %lud\n", ctlr->nints); + PRINTSTAT("Double Interrupts: %lud\n", ctlr->ndoubleint); + PRINTSTAT("TxPackets: %lud\n", ctlr->ntx); + PRINTSTAT("RxPackets: %lud\n", ctlr->nrx); + PRINTSTAT("TxErrors: %lud\n", ctlr->ntxerr); + PRINTSTAT("RxErrors: %lud\n", ctlr->nrxerr); + PRINTSTAT("TxRequests: %lud\n", ctlr->ntxrq); + PRINTSTAT("AllocEvs: %lud\n", ctlr->nalloc); + PRINTSTAT("InfoEvs: %lud\n", ctlr->ninfo); + PRINTSTAT("InfoDrop: %lud\n", ctlr->nidrop); + PRINTSTAT("WatchDogs: %lud\n", ctlr->nwatchdogs); + PRINTSTAT("Ticks: %ud\n", ctlr->ticks); + PRINTSTAT("TickIntr: %ud\n", ctlr->tickintr); + k = ((ctlr->state & Attached) ? "attached" : "not attached"); + PRINTSTAT("Card %s", k); + k = ((ctlr->state & Power) ? "on" : "off"); + PRINTSTAT(", power %s", k); + k = ((ctlr->txbusy)? ", txbusy" : ""); + PRINTSTAT("%s\n", k); + + if(ctlr->hascrypt){ + PRINTSTR("Keys: "); + for (i = 0; i < WNKeys; i++){ + if(ctlr->keys.keys[i].len == 0) + PRINTSTR("none "); + else if(SEEKEYS == 0) + PRINTSTR("set "); + else + PRINTSTAT("%s ", ctlr->keys.keys[i].dat); + } + PRINTSTR("\n"); + } + + // real card stats + ilock(ctlr); + PRINTSTR("\nCard stats: \n"); + PRINTSTAT("Status: %ux\n", csr_ins(ctlr, WR_Sts)); + PRINTSTAT("Event status: %ux\n", csr_ins(ctlr, WR_EvSts)); + i = ltv_ins(ctlr, WTyp_Ptype); + PRINTSTAT("Port type: %d\n", i); + PRINTSTAT("Transmit rate: %d\n", ltv_ins(ctlr, WTyp_TxRate)); + PRINTSTAT("Current Transmit rate: %d\n", + ltv_ins(ctlr, WTyp_CurTxRate)); + PRINTSTAT("Channel: %d\n", ltv_ins(ctlr, WTyp_Chan)); + PRINTSTAT("AP density: %d\n", ltv_ins(ctlr, WTyp_ApDens)); + PRINTSTAT("Promiscuous mode: %d\n", ltv_ins(ctlr, WTyp_Prom)); + if(i == WPTypeAdHoc) + PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName)); + else { + Wltv ltv; + PRINTSTAT("Current name: %s\n", ltv_inname(ctlr, WTyp_CurName)); + ltv.type = WTyp_BaseID; + ltv.len = 4; + if(w_inltv(ctlr, <v)) + print("#l%d: unable to read base station mac addr\n", ether->ctlrno); + l += snprint(p+l, READSTR-l, "Base station: %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n", + ltv.addr[0], ltv.addr[1], ltv.addr[2], ltv.addr[3], ltv.addr[4], ltv.addr[5]); + } + PRINTSTAT("Net name: %s\n", ltv_inname(ctlr, WTyp_WantName)); + PRINTSTAT("Node name: %s\n", ltv_inname(ctlr, WTyp_NodeName)); + if(ltv_ins(ctlr, WTyp_HasCrypt) == 0) + PRINTSTR("WEP: not supported\n"); + else { + if(ltv_ins(ctlr, WTyp_Crypt) == 0) + PRINTSTR("WEP: disabled\n"); + else{ + PRINTSTR("WEP: enabled\n"); + k = ((ctlr->xclear)? "excluded": "included"); + PRINTSTAT("Clear packets: %s\n", k); + txid = ltv_ins(ctlr, WTyp_TxKey); + PRINTSTAT("Transmit key id: %d\n", txid); + } + } + iunlock(ctlr); + + PRINTSTAT("ntxuframes: %lud\n", ctlr->ntxuframes); + PRINTSTAT("ntxmframes: %lud\n", ctlr->ntxmframes); + PRINTSTAT("ntxfrags: %lud\n", ctlr->ntxfrags); + PRINTSTAT("ntxubytes: %lud\n", ctlr->ntxubytes); + PRINTSTAT("ntxmbytes: %lud\n", ctlr->ntxmbytes); + PRINTSTAT("ntxdeferred: %lud\n", ctlr->ntxdeferred); + PRINTSTAT("ntxsretries: %lud\n", ctlr->ntxsretries); + PRINTSTAT("ntxmultiretries: %lud\n", ctlr->ntxmultiretries); + PRINTSTAT("ntxretrylimit: %lud\n", ctlr->ntxretrylimit); + PRINTSTAT("ntxdiscards: %lud\n", ctlr->ntxdiscards); + PRINTSTAT("nrxuframes: %lud\n", ctlr->nrxuframes); + PRINTSTAT("nrxmframes: %lud\n", ctlr->nrxmframes); + PRINTSTAT("nrxfrags: %lud\n", ctlr->nrxfrags); + PRINTSTAT("nrxubytes: %lud\n", ctlr->nrxubytes); + PRINTSTAT("nrxmbytes: %lud\n", ctlr->nrxmbytes); + PRINTSTAT("nrxfcserr: %lud\n", ctlr->nrxfcserr); + PRINTSTAT("nrxdropnobuf: %lud\n", ctlr->nrxdropnobuf); + PRINTSTAT("nrxdropnosa: %lud\n", ctlr->nrxdropnosa); + PRINTSTAT("nrxcantdecrypt: %lud\n", ctlr->nrxcantdecrypt); + PRINTSTAT("nrxmsgfrag: %lud\n", ctlr->nrxmsgfrag); + PRINTSTAT("nrxmsgbadfrag: %lud\n", ctlr->nrxmsgbadfrag); + USED(l); + n = readstr(offset, a, n, p); + free(p); + return n; +} +#undef PRINTSTR +#undef PRINTSTAT + +static int +parsekey(WKey* key, char* a) +{ + int i, k, len, n; + char buf[WMaxKeyLen]; + + len = strlen(a); + if(len == WMinKeyLen || len == WMaxKeyLen){ + memset(key->dat, 0, sizeof(key->dat)); + memmove(key->dat, a, len); + key->len = len; + + return 0; + } + else if(len == WMinKeyLen*2 || len == WMaxKeyLen*2){ + k = 0; + for(i = 0; i < len; i++){ + if(*a >= '0' && *a <= '9') + n = *a++ - '0'; + else if(*a >= 'a' && *a <= 'f') + n = *a++ - 'a' + 10; + else if(*a >= 'A' && *a <= 'F') + n = *a++ - 'A' + 10; + else + return -1; + + if(i & 1){ + buf[k] |= n; + k++; + } + else + buf[k] = n<<4; + } + + memset(key->dat, 0, sizeof(key->dat)); + memmove(key->dat, buf, k); + key->len = k; + + return 0; + } + + return -1; +} + +int +w_option(Ctlr* ctlr, char* buf, long n) +{ + char *p; + int i, r; + Cmdbuf *cb; + + r = 0; + + cb = parsecmd(buf, n); + if(cb->nf < 2) + r = -1; + else if(cistrcmp(cb->f[0], "essid") == 0){ + if(cistrcmp(cb->f[1],"default") == 0) + p = ""; + else + p = cb->f[1]; + if(ctlr->ptype == WPTypeAdHoc){ + memset(ctlr->netname, 0, sizeof(ctlr->netname)); + strncpy(ctlr->netname, p, WNameLen); + } + else{ + memset(ctlr->wantname, 0, sizeof(ctlr->wantname)); + strncpy(ctlr->wantname, p, WNameLen); + } + } + else if(cistrcmp(cb->f[0], "station") == 0){ + memset(ctlr->nodename, 0, sizeof(ctlr->nodename)); + strncpy(ctlr->nodename, cb->f[1], WNameLen); + } + else if(cistrcmp(cb->f[0], "channel") == 0){ + if((i = atoi(cb->f[1])) >= 1 && i <= 16) + ctlr->chan = i; + else + r = -1; + } + else if(cistrcmp(cb->f[0], "mode") == 0){ + if(cistrcmp(cb->f[1], "managed") == 0) + ctlr->ptype = WPTypeManaged; + else if(cistrcmp(cb->f[1], "wds") == 0) + ctlr->ptype = WPTypeWDS; + else if(cistrcmp(cb->f[1], "adhoc") == 0) + ctlr->ptype = WPTypeAdHoc; + else if((i = atoi(cb->f[1])) >= 0 && i <= 3) + ctlr->ptype = i; + else + r = -1; + } + else if(cistrcmp(cb->f[0], "ibss") == 0){ + if(cistrcmp(cb->f[1], "on") == 0) + ctlr->createibss = 1; + else + ctlr->createibss = 0; + } + else if(cistrcmp(cb->f[0], "crypt") == 0){ + if(cistrcmp(cb->f[1], "off") == 0) + ctlr->crypt = 0; + else if(cistrcmp(cb->f[1], "on") == 0 && ctlr->hascrypt) + ctlr->crypt = 1; + else + r = -1; + } + else if(cistrcmp(cb->f[0], "clear") == 0){ + if(cistrcmp(cb->f[1], "on") == 0) + ctlr->xclear = 0; + else if(cistrcmp(cb->f[1], "off") == 0 && ctlr->hascrypt) + ctlr->xclear = 1; + else + r = -1; + } + else if(cistrncmp(cb->f[0], "key", 3) == 0){ + if((i = atoi(cb->f[0]+3)) >= 1 && i <= WNKeys){ + ctlr->txkey = i-1; + if(parsekey(&ctlr->keys.keys[ctlr->txkey], cb->f[1])) + r = -1; + } + else + r = -1; + } + else if(cistrcmp(cb->f[0], "txkey") == 0){ + if((i = atoi(cb->f[1])) >= 1 && i <= WNKeys) + ctlr->txkey = i-1; + else + r = -1; + } + else if(cistrcmp(cb->f[0], "pm") == 0){ + if(cistrcmp(cb->f[1], "off") == 0) + ctlr->pmena = 0; + else if(cistrcmp(cb->f[1], "on") == 0){ + ctlr->pmena = 1; + if(cb->nf == 3){ + i = atoi(cb->f[2]); + // check range here? what are the units? + ctlr->pmwait = i; + } + } + else + r = -1; + } + else + r = -2; + free(cb); + + return r; +} + +long +w_ctl(Ether* ether, void* buf, long n) +{ + Ctlr *ctlr; + + if((ctlr = ether->ctlr) == nil) + error(Enonexist); + if((ctlr->state & Attached) == 0) + error(Eshutdown); + + ilock(ctlr); + if(w_option(ctlr, buf, n)){ + iunlock(ctlr); + error(Ebadctl); + } + if(ctlr->txbusy) + w_txdone(ctlr, WTxErrEv); + w_enable(ether); + w_txstart(ether); + iunlock(ctlr); + + return n; +} + +void +w_transmit(Ether* ether) +{ + Ctlr* ctlr = ether->ctlr; + + if(ctlr == 0) + return; + + ilock(ctlr); + ctlr->ntxrq++; + w_txstart(ether); + iunlock(ctlr); +} + +void +w_promiscuous(void* arg, int on) +{ + Ether* ether = (Ether*)arg; + Ctlr* ctlr = ether->ctlr; + + if(ctlr == nil) + error("card not found"); + if((ctlr->state & Attached) == 0) + error("card not attached"); + ilock(ctlr); + ltv_outs(ctlr, WTyp_Prom, (on?1:0)); + iunlock(ctlr); +} + +void +w_interrupt(Ureg* ,void* arg) +{ + Ether* ether = (Ether*) arg; + Ctlr* ctlr = (Ctlr*) ether->ctlr; + + if(ctlr == 0) + return; + ilock(ctlr); + ctlr->nints++; + w_intr(ether); + iunlock(ctlr); +} + +int +wavelanreset(Ether* ether, Ctlr *ctlr) +{ + Wltv ltv; + + iprint("wavelanreset, iob 0x%ux\n", ctlr->iob); + w_intdis(ctlr); + if(w_cmd(ctlr,WCmdIni,0)){ + iprint("#l%d: init failed\n", ether->ctlrno); + return -1; + } + w_intdis(ctlr); + ltv_outs(ctlr, WTyp_Tick, 8); + + ctlr->chan = 0; + ctlr->ptype = WDfltPType; + ctlr->txkey = 0; + ctlr->createibss = 0; + ctlr->keys.len = sizeof(WKey)*WNKeys/2 + 1; + ctlr->keys.type = WTyp_Keys; + if(ctlr->hascrypt = ltv_ins(ctlr, WTyp_HasCrypt)) + ctlr->crypt = 1; + *ctlr->netname = *ctlr->wantname = 0; + strcpy(ctlr->nodename, "Plan 9 STA"); + + ctlr->netname[WNameLen-1] = 0; + ctlr->wantname[WNameLen-1] = 0; + ctlr->nodename[WNameLen-1] =0; + + ltv.type = WTyp_Mac; + ltv.len = 4; + if(w_inltv(ctlr, <v)){ + iprint("#l%d: unable to read mac addr\n", + ether->ctlrno); + return -1; + } + memmove(ether->ea, ltv.addr, Eaddrlen); + + if(ctlr->chan == 0) + ctlr->chan = ltv_ins(ctlr, WTyp_Chan); + ctlr->apdensity = WDfltApDens; + ctlr->rtsthres = WDfltRtsThres; + ctlr->txrate = WDfltTxRate; + ctlr->maxlen = WMaxLen; + ctlr->pmena = 0; + ctlr->pmwait = 100; + ctlr->signal = 1; + ctlr->noise = 1; + ctlr->state |= Power; + + // free old Ctlr struct if resetting after suspend + if(ether->ctlr && ether->ctlr != ctlr) + free(ether->ctlr); + + // link to ether + ether->ctlr = ctlr; + ether->mbps = 10; + ether->attach = w_attach; + ether->detach = w_detach; + ether->interrupt = w_interrupt; + ether->transmit = w_transmit; + ether->ifstat = w_ifstat; + ether->ctl = w_ctl; + ether->power = w_power; + ether->promiscuous = w_promiscuous; + ether->multicast = w_multicast; + ether->scanbs = w_scanbs; + ether->arg = ether; + + DEBUG("#l%d: irq %d port %lx type %s", + ether->ctlrno, ether->irq, ether->port, ether->type); + DEBUG(" %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux\n", + ether->ea[0], ether->ea[1], ether->ea[2], + ether->ea[3], ether->ea[4], ether->ea[5]); + + return 0; +} + +char* wavenames[] = { + "WaveLAN/IEEE", + "TrueMobile 1150", + "Instant Wireless ; Network PC CARD", + "Instant Wireless Network PC Card", + "Avaya Wireless PC Card", + "AirLancer MC-11", + "INTERSIL;HFA384x/IEEE;Version 01.02;", + nil, +}; diff --git a/os/pc/wavelan.h b/os/pc/wavelan.h new file mode 100644 index 00000000..7f76149e --- /dev/null +++ b/os/pc/wavelan.h @@ -0,0 +1,327 @@ +#define DEBUG if(1){}else print + +#define SEEKEYS 0 + +// Lucent's Length-Type-Value records to talk to the wavelan. +// most operational parameters are read/set using this. +enum +{ + WTyp_Stats = 0xf100, + WTyp_Scan = 0xf101, + WTyp_Link = 0xf200, + WTyp_Ptype = 0xfc00, + WTyp_Mac = 0xfc01, + WTyp_WantName = 0xfc02, + WTyp_Chan = 0xfc03, + WTyp_NetName = 0xfc04, + WTyp_ApDens = 0xfc06, + WTyp_MaxLen = 0xfc07, + WTyp_PM = 0xfc09, + WTyp_PMWait = 0xfc0c, + WTyp_NodeName = 0xfc0e, + WTyp_Crypt = 0xfc20, + WTyp_XClear = 0xfc22, + WTyp_CreateIBSS = 0xfc81, + WTyp_RtsThres = 0xfc83, + WTyp_TxRate = 0xfc84, + WTx1Mbps = 0x0, + WTx2Mbps = 0x1, + WTxAuto = 0x3, + WTyp_Prom = 0xfc85, + WTyp_Keys = 0xfcb0, + WTyp_TxKey = 0xfcb1, + WTyp_StationID = 0xfd20, + WTyp_CurName = 0xfd41, + WTyp_BaseID = 0xfd42, // ID of the currently connected-to base station + WTyp_CurTxRate = 0xfd44, // Current TX rate + WTyp_HasCrypt = 0xfd4f, + WTyp_Tick = 0xfce0, +}; + +// Controller +enum +{ + WDfltIRQ = 3, // default irq + WDfltIOB = 0x180, // default IO base + + WIOLen = 0x40, // Hermes IO length + + WTmOut = 65536, // Cmd time out + + WPTypeManaged = 1, + WPTypeWDS = 2, + WPTypeAdHoc = 3, + WDfltPType = WPTypeManaged, + + WDfltApDens = 1, + WDfltRtsThres = 2347, // == disabled + WDfltTxRate = WTxAuto, // 2Mbps + + WMaxLen = 2304, + WNameLen = 32, + + WNKeys = 4, + WKeyLen = 14, + WMinKeyLen = 5, + WMaxKeyLen = 13, + + // Wavelan hermes registers + WR_Cmd = 0x00, + WCmdIni = 0x0000, + WCmdEna = 0x0001, + WCmdDis = 0x0002, + WCmdTx = 0x000b, + WCmdMalloc = 0x000a, + WCmdEnquire = 0x0011, + WCmdMsk = 0x003f, + WCmdAccRd = 0x0021, + WCmdReclaim = 0x0100, + WCmdAccWr = 0x0121, + WCmdBusy = 0x8000, + WR_Parm0 = 0x02, + WR_Parm1 = 0x04, + WR_Parm2 = 0x06, + WR_Sts = 0x08, + WR_InfoId = 0x10, + WR_Sel0 = 0x18, + WR_Sel1 = 0x1a, + WR_Off0 = 0x1c, + WR_Off1 = 0x1e, + WBusyOff = 0x8000, + WErrOff = 0x4000, + WResSts = 0x7f00, + WR_RXId = 0x20, + WR_Alloc = 0x22, + WR_EvSts = 0x30, + WR_IntEna = 0x32, + WCmdEv = 0x0010, + WRXEv = 0x0001, + WTXEv = 0x0002, + WTxErrEv = 0x0004, + WAllocEv = 0x0008, + WInfoEv = 0x0080, + WIDropEv = 0x2000, + WTickEv = 0x8000, + WEvs = WRXEv|WTXEv|WAllocEv|WInfoEv|WIDropEv, + + WR_EvAck = 0x34, + WR_Data0 = 0x36, + WR_Data1 = 0x38, + + WR_PciCor = 0x26, + WR_PciHcr = 0x2E, + + // Frame stuff + + WF_Err = 0x0003, + WF_1042 = 0x2000, + WF_Tunnel = 0x4000, + WF_WMP = 0x6000, + + WF_Data = 0x0008, + + WSnapK1 = 0xaa, + WSnapK2 = 0x00, + WSnapCtlr = 0x03, + WSnap0 = (WSnapK1|(WSnapK1<<8)), + WSnap1 = (WSnapK2|(WSnapCtlr<<8)), + WSnapHdrLen = 6, + + WF_802_11_Off = 0x44, + WF_802_3_Off = 0x2e, + +}; + +typedef struct Ctlr Ctlr; +typedef struct Wltv Wltv; +typedef struct WFrame WFrame; +typedef struct Stats Stats; +typedef struct WStats WStats; +typedef struct WScan WScan; +typedef struct WKey WKey; + +struct WStats +{ + ulong ntxuframes; // unicast frames + ulong ntxmframes; // multicast frames + ulong ntxfrags; // fragments + ulong ntxubytes; // unicast bytes + ulong ntxmbytes; // multicast bytes + ulong ntxdeferred; // deferred transmits + ulong ntxsretries; // single retries + ulong ntxmultiretries; // multiple retries + ulong ntxretrylimit; + ulong ntxdiscards; + ulong nrxuframes; // unicast frames + ulong nrxmframes; // multicast frames + ulong nrxfrags; // fragments + ulong nrxubytes; // unicast bytes + ulong nrxmbytes; // multicast bytes + ulong nrxfcserr; + ulong nrxdropnobuf; + ulong nrxdropnosa; + ulong nrxcantdecrypt; + ulong nrxmsgfrag; + ulong nrxmsgbadfrag; + ulong end; +}; + +struct WScan +{ + ushort chan; /* dss channel */ + ushort noise; /* average noise in the air */ + ushort signal; /* signal strength */ + uchar bssid[Eaddrlen]; /* MAC address of the ap */ + ushort interval; /* beacon transmit interval */ + ushort capinfo; /* capability bits (0-ess, 1-ibss, 4-privacy [wep]) */ + ushort ssid_len; /* ssid length */ + char ssid[WNameLen]; /* ssid (ap name) */ +}; + +struct WFrame +{ + ushort sts; + ushort rsvd0; + ushort rsvd1; + ushort qinfo; + ushort rsvd2; + ushort rsvd3; + ushort txctl; + ushort framectl; + ushort id; + uchar addr1[Eaddrlen]; + uchar addr2[Eaddrlen]; + uchar addr3[Eaddrlen]; + ushort seqctl; + uchar addr4[Eaddrlen]; + ushort dlen; + uchar dstaddr[Eaddrlen]; + uchar srcaddr[Eaddrlen]; + ushort len; + ushort dat[3]; + ushort type; +}; + +struct WKey +{ + ushort len; + char dat[WKeyLen]; +}; + +struct Wltv +{ + ushort len; + ushort type; + union + { + struct { + ushort val; + ushort pad; + }; + struct { + uchar addr[8]; + }; + struct { + ushort slen; + char s[WNameLen]; + }; + struct { + char name[WNameLen]; + }; + struct { + WKey keys[WNKeys]; + }; + }; +}; + +// What the driver thinks. Not what the card thinks. +struct Stats +{ + ulong nints; + ulong ndoubleint; + ulong nrx; + ulong ntx; + ulong ntxrq; + ulong nrxerr; + ulong ntxerr; + ulong nalloc; // allocation (reclaim) events + ulong ninfo; + ulong nidrop; + ulong nwatchdogs; // transmit time outs, actually + int ticks; + int tickintr; + int signal; + int noise; +}; + +enum { + Attached = 0x01, + Power = 0x02, +}; + +struct Ctlr +{ + Lock; + + int state; // Attached | Power + int slot; + int iob; + int createibss; + int ptype; + int apdensity; + int rtsthres; + int txbusy; + int txrate; + int txdid; + int txmid; + int txtmout; + int maxlen; + int chan; + int pmena; + int pmwait; + + Proc *timerproc; + int scanticks; + + char netname[WNameLen]; + char wantname[WNameLen]; + char nodename[WNameLen]; + WFrame txf; + uchar txbuf[1536]; + + int hascrypt; // card has encryption + int crypt; // encryption off/on + int txkey; // transmit key + Wltv keys; // default keys + int xclear; // exclude clear packets off/on + + int ctlrno; + + ushort *mmb; + /* for PCI-based devices */ + Ctlr *next; + int active; + Pcidev *pcidev; + + Stats; + WStats; +}; + +extern char* wavenames[]; + +void csr_outs(Ctlr*, int, ushort); +ushort csr_ins(Ctlr*, int); +void w_intdis(Ctlr*); +int w_cmd(Ctlr *, ushort, ushort); +void ltv_outs(Ctlr*, int, ushort); +int ltv_ins(Ctlr*, int); +int w_option(Ctlr*, char*, long); +int w_inltv(Ctlr*, Wltv*); +void w_attach(Ether*); +void w_interrupt(Ureg*,void*); +void w_transmit(Ether*); +long w_ifstat(Ether*, void*, long, ulong); +long w_ctl(Ether*, void*, long); +void w_promiscuous(void*, int); +void w_multicast(void*, uchar*, int); +int wavelanreset(Ether*, Ctlr*); diff --git a/os/pc/x86break.c b/os/pc/x86break.c new file mode 100644 index 00000000..db31fe4f --- /dev/null +++ b/os/pc/x86break.c @@ -0,0 +1,138 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +// +// from trap.c +// + +uchar BREAK = 0xcc; +static ulong skipflags; +extern int (*breakhandler)(Ureg *ur, Proc*); +static Bkpt *skip; +int breakmatch(BkptCond *cond, Ureg *ur, Proc *p); +void breaknotify(Bkpt *b, Proc *p); +void breakrestore(Bkpt *b); +Bkpt* breakclear(int id); + +void +skiphandler(Ureg *ur, void*) +{ + if (skip == 0) + panic("single step outside of skip"); + + breakrestore( skip ); + skip = 0; + ur->flags = skipflags; + if (up != 0) + up->state = Running; +} + +void +machbreakinit(void) +{ + breakhandler = breakhit; + trapenable(VectorDBG, skiphandler, nil, "bkpt.skip"); +} + +Instr +machinstr(ulong addr) +{ + if (addr < KTZERO) + error(Ebadarg); + return *(uchar*)addr; +} + +void +machbreakset(ulong addr) +{ + if (addr < KTZERO) + error(Ebadarg); + *(uchar*)addr = BREAK; +} + +void +machbreakclear(ulong addr, Instr i) +{ + if (addr < KTZERO) + error(Ebadarg); + *(uchar*)addr = i; +} + +// +// Called from the exception handler when a breakpoint instruction has been +// hit. This cannot not be called unless at least one breakpoint with this +// address is in the list of breakpoints. (All breakpoint notifications must +// previously have been set via setbreak()) +// +// foreach breakpoint in list +// if breakpoint matches conditions +// notify the break handler +// if no breakpoints matched the conditions +// pick a random breakpoint set to this address +// +// set a breakpoint at the next instruction to be executed, +// and pass the current breakpoint to the "skiphandler" +// +// clear the current breakpoint +// +// Tell the scheduler to stop scheduling, so the caller is +// guaranteed to execute the instruction, followed by the +// added breakpoint. +// +// + +extern Bkpt *breakpoints; + + +int +breakhit(Ureg *ur, Proc *p) +{ + Bkpt *b; + int nmatched; + + ur->pc--; + + nmatched = 0; + for(b = breakpoints; b != nil; b = b->next) { + if(breakmatch(b->conditions, ur, p)) { + breaknotify(b, p); + ++nmatched; + } + } + + if (nmatched) + return 1; + + if (skip != nil) + panic("x86break: non-nil skip in breakhit\n"); + + for(b = breakpoints; b != (Bkpt*) nil; b = b->next) { + if(b->addr == ur->pc) { + if(breakclear(b->id) == 0) + panic("breakhit: breakclear() failed"); + + skip = b; + skipflags = ur->flags; + if (p != 0) + p->state = Stopped; /* this should disable scheduling */ + + if (ur->flags & (1 << 9)) { /* mask all interrupts */ + ur->flags &= ~(1<<9); + } + ur->flags |= (1 << 8); + } + } + return 1; +} + +int +isvalid_va(void*) +{ + return 1; +} diff --git a/os/pc/zoran.h b/os/pc/zoran.h new file mode 100644 index 00000000..b54305ef --- /dev/null +++ b/os/pc/zoran.h @@ -0,0 +1,907 @@ +static uchar +zrmpeg1[] = { +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x66, 0x05, +0x00, 0x08, 0x00, 0x80, 0x00, 0x08, 0x5a, 0x00, +0x00, 0x08, 0x58, 0x01, 0x00, 0x08, 0x6e, 0x00, +0x00, 0x08, 0x6c, 0x00, 0x00, 0x03, 0x81, 0x03, +0x00, 0x08, 0x9a, 0x00, 0x00, 0x08, 0xfc, 0x3f, +0x00, 0x0d, 0x02, 0x97, 0x00, 0x06, 0x01, 0xfa, +0x00, 0x0c, 0xa2, 0xd1, 0x00, 0x08, 0x6a, 0x00, +0x00, 0x06, 0x01, 0x00, 0x00, 0x0c, 0xa0, 0x6b, +0x00, 0x06, 0x01, 0xb3, 0x00, 0x0c, 0xa8, 0x09, +0x00, 0x0c, 0xd8, 0x12, 0x00, 0x08, 0x5c, 0x01, +0x00, 0x08, 0x02, 0xf0, 0x00, 0x08, 0xe4, 0x33, +0x00, 0x08, 0x0c, 0x0c, 0x00, 0x08, 0x04, 0x08, +0x00, 0x00, 0xe3, 0x80, 0x00, 0x04, 0x85, 0x90, +0x00, 0x08, 0x0c, 0x0c, 0x00, 0x00, 0xe3, 0x80, +0x00, 0x04, 0x85, 0xb1, 0x00, 0x08, 0x0c, 0x08, +0x00, 0x04, 0x8d, 0x08, 0x00, 0x06, 0x81, 0x08, +0x00, 0x0c, 0xa8, 0x22, 0x00, 0x08, 0x64, 0xff, +0x00, 0x01, 0x01, 0x07, 0x00, 0x08, 0x02, 0x00, +0x00, 0x06, 0x01, 0x02, 0x00, 0x0c, 0x90, 0x27, +0x00, 0x08, 0x02, 0x01, 0x00, 0x06, 0x6b, 0x00, +0x00, 0x08, 0xde, 0x01, 0x00, 0x0c, 0xa8, 0x09, +0x00, 0x08, 0x0c, 0x0f, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x0c, 0x10, 0x00, 0x06, 0x8d, 0x01, +0x00, 0x0c, 0xa0, 0x3a, 0x00, 0x08, 0x0a, 0x00, +0x00, 0x08, 0x60, 0x00, 0x00, 0x08, 0x0c, 0x08, +0x00, 0x04, 0x9d, 0x08, 0x00, 0x08, 0x8b, 0x41, +0x00, 0x02, 0x50, 0x05, 0x00, 0x06, 0x0b, 0x40, +0x00, 0x0c, 0xa8, 0x31, 0x00, 0x06, 0x6b, 0x00, +0x00, 0x0c, 0xa8, 0x3f, 0x00, 0x0d, 0x00, 0x3b, +0x00, 0x08, 0x60, 0x01, 0x00, 0x08, 0x0a, 0x40, +0x00, 0x08, 0x0c, 0x01, 0x00, 0x06, 0x0d, 0x00, +0x00, 0x0c, 0x98, 0x49, 0x00, 0x08, 0x0a, 0x40, +0x00, 0x08, 0x0c, 0x08, 0x00, 0x04, 0x9d, 0x08, +0x00, 0x08, 0x8b, 0x41, 0x00, 0x02, 0x50, 0x05, +0x00, 0x06, 0x0b, 0x80, 0x00, 0x0c, 0xa8, 0x40, +0x00, 0x06, 0x6b, 0x00, 0x00, 0x0c, 0xa8, 0x16, +0x00, 0x0d, 0x00, 0x4e, 0x00, 0x08, 0x02, 0x10, +0x00, 0x08, 0x8a, 0x41, 0x00, 0x02, 0x50, 0x05, +0x00, 0x06, 0x0b, 0x80, 0x00, 0x0c, 0xa8, 0x4a, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x97, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x95, +0x00, 0x06, 0x01, 0xb8, 0x00, 0x0c, 0xa9, 0xff, +0x00, 0x06, 0x6f, 0x00, 0x00, 0x0c, 0xa0, 0x57, +0x00, 0x08, 0x58, 0x00, 0x00, 0x08, 0x6e, 0x01, +0x00, 0x08, 0x0c, 0x10, 0x00, 0x08, 0x0c, 0x09, +0x00, 0x08, 0x0c, 0x01, 0x00, 0x08, 0x80, 0x06, +0x00, 0x08, 0x0c, 0x01, 0x00, 0x04, 0x9d, 0x0f, +0x00, 0x03, 0x12, 0x6c, 0x00, 0x08, 0xd8, 0x01, +0x00, 0x08, 0x6c, 0x00, 0x00, 0x06, 0x01, 0x00, +0x00, 0x0c, 0x98, 0x64, 0x00, 0x08, 0x58, 0x00, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x97, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x95, +0x00, 0x0d, 0x01, 0xf7, 0x00, 0x06, 0x5d, 0x01, +0x00, 0x0c, 0xa0, 0x6c, 0x00, 0x0c, 0xd8, 0x6b, +0x00, 0x09, 0x20, 0xe5, 0x00, 0x09, 0x62, 0xe6, +0x00, 0x09, 0x5e, 0xe3, 0x00, 0x09, 0x60, 0xc2, +0x00, 0x08, 0x5c, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x02, 0x00, 0x2d, 0x00, 0x0c, 0xa0, 0x75, +0x00, 0x08, 0xda, 0x00, 0x00, 0x0c, 0xd0, 0x76, +0x00, 0x08, 0x50, 0x00, 0x00, 0x08, 0x23, 0xff, +0x00, 0x08, 0x24, 0x00, 0x00, 0x08, 0x0c, 0x0a, +0x00, 0x08, 0xe6, 0x32, 0x00, 0x04, 0x8d, 0x06, +0x00, 0x08, 0xe8, 0x00, 0x00, 0x08, 0x0c, 0x03, +0x00, 0x04, 0x8d, 0x0d, 0x00, 0x08, 0xaa, 0x00, +0x00, 0x08, 0x0c, 0x10, 0x00, 0x06, 0x2b, 0x01, +0x00, 0x0c, 0xa8, 0x8c, 0x00, 0x06, 0x6d, 0x00, +0x00, 0x0c, 0xa8, 0x8a, 0x00, 0x08, 0x80, 0x2c, +0x00, 0x01, 0x01, 0x01, 0x00, 0x08, 0xd8, 0x00, +0x00, 0x08, 0x6c, 0x01, 0x00, 0x0d, 0x00, 0xab, +0x00, 0x08, 0x58, 0x00, 0x00, 0x0d, 0x00, 0xab, +0x00, 0x06, 0x2b, 0x02, 0x00, 0x0c, 0xa8, 0x97, +0x00, 0x06, 0x6d, 0x00, 0x00, 0x0c, 0xa0, 0x99, +0x00, 0x08, 0x58, 0x00, 0x00, 0x0d, 0x00, 0x9a, +0x00, 0x08, 0x02, 0x03, 0x00, 0x06, 0x5b, 0x01, +0x00, 0x0c, 0xa8, 0xb0, 0x00, 0x08, 0x02, 0x01, +0x00, 0x0d, 0x00, 0xb0, 0x00, 0x06, 0xd9, 0x01, +0x00, 0x0c, 0xa0, 0x9a, 0x00, 0x08, 0x58, 0x03, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0xab, +0x00, 0x08, 0xd4, 0x02, 0x00, 0x08, 0xae, 0x00, +0x00, 0x08, 0xac, 0x03, 0x00, 0x01, 0xc1, 0x9e, +0x00, 0x02, 0xc1, 0x9f, 0x00, 0x08, 0x80, 0x00, +0x00, 0x06, 0x2b, 0x03, 0x00, 0x0c, 0xa8, 0xab, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0xab, +0x00, 0x08, 0xd6, 0x02, 0x00, 0x08, 0xb2, 0x00, +0x00, 0x08, 0xb0, 0x03, 0x00, 0x01, 0xc1, 0xa0, +0x00, 0x02, 0xc1, 0xa1, 0x00, 0x08, 0xfc, 0x3f, +0x00, 0x0d, 0x02, 0x97, 0x00, 0x08, 0x82, 0x15, +0x00, 0x06, 0xd9, 0x02, 0x00, 0x0c, 0xa8, 0x92, +0x00, 0x09, 0x02, 0xe7, 0x00, 0x08, 0xfc, 0x3f, +0x00, 0x0d, 0x02, 0x95, 0x00, 0x08, 0x88, 0x0d, +0x00, 0x08, 0x90, 0x04, 0x00, 0x08, 0x92, 0x04, +0x00, 0x08, 0x94, 0x04, 0x00, 0x08, 0x45, 0xfe, +0x00, 0x08, 0x34, 0x00, 0x00, 0x08, 0x36, 0x00, +0x00, 0x08, 0x38, 0x00, 0x00, 0x08, 0x3a, 0x00, +0x00, 0x08, 0x0c, 0x05, 0x00, 0x04, 0x8d, 0x0b, +0x00, 0x09, 0x00, 0xc0, 0x00, 0x08, 0x46, 0x01, +0x00, 0x08, 0x0c, 0x01, 0x00, 0x08, 0x80, 0x28, +0x00, 0x08, 0x0c, 0x1c, 0x00, 0x08, 0x0e, 0x01, +0x00, 0x08, 0xce, 0x00, 0x00, 0x06, 0x0f, 0x22, +0x00, 0x0c, 0xa0, 0xc2, 0x00, 0x0c, 0x82, 0x7c, +0x00, 0x00, 0x80, 0xa2, 0x00, 0x06, 0x47, 0x00, +0x00, 0x08, 0xd0, 0x00, 0x00, 0x0c, 0xaa, 0x77, +0x00, 0x08, 0x4c, 0x01, 0x00, 0x0d, 0x02, 0x85, +0x00, 0x06, 0x5b, 0x00, 0x00, 0x0c, 0xa2, 0xbe, +0x00, 0x08, 0x52, 0x00, 0x00, 0x08, 0x4c, 0x00, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0xd5, +0x00, 0x09, 0x52, 0xe0, 0x00, 0x06, 0x2b, 0x01, +0x00, 0x0c, 0xa0, 0xf0, 0x00, 0x08, 0x0c, 0x1c, +0x00, 0x06, 0x2b, 0x03, 0x00, 0x0c, 0xa1, 0x01, +0x00, 0x0c, 0x91, 0xff, 0x00, 0x08, 0x0e, 0x02, +0x00, 0x08, 0x80, 0x00, 0x00, 0x06, 0x8f, 0x01, +0x00, 0x0c, 0xa8, 0xf8, 0x00, 0x08, 0xa6, 0x07, +0x00, 0x08, 0x52, 0x20, 0x00, 0x08, 0x80, 0x00, +0x00, 0x06, 0xa7, 0x08, 0x00, 0x0c, 0xa9, 0x0c, +0x00, 0x08, 0x34, 0x00, 0x00, 0x08, 0x36, 0x00, +0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, +0x00, 0x08, 0x8a, 0x2a, 0x00, 0x08, 0x48, 0x03, +0x00, 0x08, 0x46, 0x06, 0x00, 0x08, 0xfc, 0x3f, +0x00, 0x0d, 0x02, 0x23, 0x00, 0x06, 0x4d, 0x00, +0x00, 0x0c, 0xa9, 0x66, 0x00, 0x0d, 0x01, 0x0c, +0x00, 0x08, 0x0c, 0x01, 0x00, 0x06, 0x0d, 0x00, +0x00, 0x0c, 0x80, 0xf6, 0x00, 0x08, 0x0c, 0x01, +0x00, 0x08, 0x26, 0x11, 0x00, 0x0d, 0x01, 0x0e, +0x00, 0x08, 0x26, 0x01, 0x00, 0x0d, 0x01, 0x0e, +0x00, 0x06, 0xd9, 0x02, 0x00, 0x0c, 0xa1, 0x0c, +0x00, 0x08, 0xa6, 0x07, 0x00, 0x08, 0x52, 0x00, +0x00, 0x08, 0x46, 0x06, 0x00, 0x08, 0x48, 0x03, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x23, +0x00, 0x0d, 0x01, 0x0c, 0x00, 0x08, 0x0e, 0x03, +0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x8f, 0x01, +0x00, 0x0c, 0xa8, 0xf8, 0x00, 0x06, 0x8f, 0x04, +0x00, 0x0c, 0xa1, 0x08, 0x00, 0x03, 0x01, 0x04, +0x00, 0x06, 0x8f, 0x08, 0x00, 0x0c, 0xa1, 0x0b, +0x00, 0x03, 0x01, 0x20, 0x00, 0x08, 0xd2, 0x00, +0x00, 0x08, 0xa6, 0x07, 0x00, 0x08, 0x80, 0x00, +0x00, 0x06, 0xa7, 0x10, 0x00, 0x0c, 0xa1, 0x13, +0x00, 0x08, 0x0c, 0x05, 0x00, 0x04, 0x8d, 0x0b, +0x00, 0x09, 0x00, 0xc0, 0x00, 0x06, 0xa7, 0x08, +0x00, 0x0c, 0xa1, 0x2f, 0x00, 0x08, 0x88, 0x17, +0x00, 0x08, 0x8a, 0x1e, 0x00, 0x08, 0x84, 0x1f, +0x00, 0x08, 0x80, 0x16, 0x00, 0x08, 0x86, 0x1a, +0x00, 0x08, 0xc8, 0x02, 0x00, 0x08, 0xc6, 0x03, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x00, +0x00, 0x08, 0x84, 0x1b, 0x00, 0x08, 0x80, 0x16, +0x00, 0x08, 0xb4, 0x01, 0x00, 0x08, 0xc6, 0x02, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x00, +0x00, 0x08, 0xb6, 0x01, 0x00, 0x08, 0x80, 0x00, +0x00, 0x06, 0xa7, 0x08, 0x00, 0x0c, 0xa1, 0x2f, +0x00, 0x08, 0x82, 0x1b, 0x00, 0x08, 0x80, 0x1a, +0x00, 0x08, 0x8a, 0x2a, 0x00, 0x08, 0x46, 0x06, +0x00, 0x08, 0x48, 0x03, 0x00, 0x08, 0xfc, 0x3f, +0x00, 0x0d, 0x02, 0x23, 0x00, 0x06, 0xa7, 0x04, +0x00, 0x0c, 0xa1, 0x4e, 0x00, 0x06, 0x4d, 0x00, +0x00, 0x0c, 0xa9, 0x44, 0x00, 0x08, 0x80, 0x18, +0x00, 0x08, 0x88, 0x19, 0x00, 0x08, 0x8a, 0x20, +0x00, 0x08, 0x84, 0x21, 0x00, 0x08, 0x86, 0x1c, +0x00, 0x08, 0xc8, 0x02, 0x00, 0x08, 0xc6, 0x03, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x00, +0x00, 0x08, 0xb8, 0x01, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x84, 0x1d, 0x00, 0x08, 0x80, 0x18, +0x00, 0x08, 0xc6, 0x02, 0x00, 0x08, 0xfc, 0x3f, +0x00, 0x0d, 0x02, 0x00, 0x00, 0x08, 0xba, 0x01, +0x00, 0x0c, 0xc9, 0x44, 0x00, 0x08, 0x82, 0x1d, +0x00, 0x08, 0x80, 0x1c, 0x00, 0x08, 0x8a, 0x2b, +0x00, 0x08, 0x46, 0x06, 0x00, 0x08, 0x48, 0x00, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x23, +0x00, 0x06, 0x4d, 0x00, 0x00, 0x0c, 0xa9, 0x66, +0x00, 0x06, 0x4d, 0x00, 0x00, 0x0c, 0xa9, 0x58, +0x00, 0x06, 0xa7, 0x02, 0x00, 0x08, 0x28, 0x00, +0x00, 0x0c, 0xa1, 0x58, 0x00, 0x08, 0x0c, 0x1c, +0x00, 0x08, 0x0e, 0x04, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0xa8, 0x07, 0x00, 0x08, 0x80, 0x00, +0x00, 0x06, 0xd9, 0x02, 0x00, 0x0c, 0xa1, 0x5c, +0x00, 0x08, 0x00, 0x00, 0x00, 0x0d, 0x01, 0x5e, +0x00, 0x08, 0x80, 0x13, 0x00, 0x01, 0x01, 0x01, +0x00, 0x09, 0x00, 0xc1, 0x00, 0x06, 0xa7, 0x01, +0x00, 0x0c, 0xa1, 0x66, 0x00, 0x08, 0x28, 0x3f, +0x00, 0x08, 0x34, 0x00, 0x00, 0x08, 0x36, 0x00, +0x00, 0x08, 0x38, 0x00, 0x00, 0x08, 0x3a, 0x00, +0x00, 0x08, 0x46, 0x06, 0x00, 0x0c, 0xd9, 0x69, +0x00, 0x0d, 0x01, 0xff, 0x00, 0x0c, 0xd1, 0x67, +0x00, 0x0c, 0xc9, 0x6b, 0x00, 0x08, 0x00, 0x01, +0x00, 0x01, 0x80, 0x23, 0x00, 0x06, 0xa7, 0x01, +0x00, 0x08, 0xc6, 0x00, 0x00, 0x0c, 0xa1, 0xa5, +0x00, 0x08, 0x0c, 0x1c, 0x00, 0x06, 0x47, 0x02, +0x00, 0x0c, 0x81, 0x75, 0x00, 0x08, 0x0e, 0x06, +0x00, 0x0d, 0x01, 0x76, 0x00, 0x08, 0x0e, 0x07, +0x00, 0x08, 0x08, 0x00, 0x00, 0x06, 0x0f, 0x00, +0x00, 0x0c, 0xa1, 0x85, 0x00, 0x08, 0xfa, 0x07, +0x00, 0x08, 0x8c, 0x3d, 0x00, 0x05, 0x4f, 0x10, +0x00, 0x04, 0xe9, 0x84, 0x00, 0x01, 0x90, 0x07, +0x00, 0x03, 0xa2, 0x81, 0x00, 0x06, 0x88, 0x02, +0x00, 0x0c, 0xa9, 0x85, 0x00, 0x08, 0x05, 0xff, +0x00, 0x03, 0xaf, 0x82, 0x00, 0x02, 0x10, 0x04, +0x00, 0x03, 0x15, 0x84, 0x00, 0x03, 0xc9, 0x03, +0x00, 0x06, 0x47, 0x04, 0x00, 0x0c, 0x91, 0x96, +0x00, 0x06, 0x47, 0x02, 0x00, 0x0c, 0x81, 0x96, +0x00, 0x00, 0xc8, 0x48, 0x00, 0x0d, 0x01, 0x9f, +0x00, 0x06, 0x47, 0x04, 0x00, 0x0c, 0x91, 0x8a, +0x00, 0x06, 0x47, 0x01, 0x00, 0x0c, 0xa1, 0x93, +0x00, 0x00, 0xc8, 0x4a, 0x00, 0x08, 0x94, 0x04, +0x00, 0x0d, 0x01, 0xa0, 0x00, 0x00, 0xc8, 0x49, +0x00, 0x08, 0x92, 0x04, 0x00, 0x0d, 0x01, 0xa0, +0x00, 0x08, 0x84, 0x28, 0x00, 0x05, 0x14, 0x22, +0x00, 0x06, 0x03, 0x01, 0x00, 0x0c, 0xa1, 0x8c, +0x00, 0x00, 0xc8, 0x4d, 0x00, 0x06, 0x47, 0x01, +0x00, 0x0c, 0xa1, 0x94, 0x00, 0x06, 0x47, 0x00, +0x00, 0x0c, 0xa1, 0x91, 0x00, 0x08, 0x90, 0x04, +0x00, 0x08, 0x0a, 0x80, 0x00, 0x06, 0xd9, 0x02, +0x00, 0x0c, 0xa9, 0xcc, 0x00, 0x08, 0x8b, 0x44, +0x00, 0x0d, 0x01, 0xbc, 0x00, 0x06, 0x47, 0x00, +0x00, 0x0c, 0xa1, 0xaf, 0x00, 0x06, 0xd3, 0x20, +0x00, 0x0c, 0xa1, 0xaf, 0x00, 0x08, 0x80, 0x1a, +0x00, 0x08, 0x82, 0x1b, 0x00, 0x08, 0x8a, 0x2a, +0x00, 0x08, 0x48, 0x03, 0x00, 0x08, 0xfc, 0x3f, +0x00, 0x0d, 0x02, 0x23, 0x00, 0x06, 0x4d, 0x00, +0x00, 0x0c, 0xa9, 0xd5, 0x00, 0x08, 0x80, 0x23, +0x00, 0x03, 0x80, 0x81, 0x00, 0x06, 0x80, 0x14, +0x00, 0x0c, 0xa1, 0xd5, 0x00, 0x08, 0x0c, 0x1c, +0x00, 0x08, 0x0e, 0x08, 0x00, 0x08, 0x0a, 0x80, +0x00, 0x00, 0xda, 0x3c, 0x00, 0x06, 0xd9, 0x02, +0x00, 0x0c, 0xa9, 0xd1, 0x00, 0x08, 0x8b, 0x47, +0x00, 0x08, 0x7a, 0x1c, 0x00, 0x08, 0x8c, 0x3d, +0x00, 0x08, 0x0e, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x0c, 0xb1, 0xd5, 0x00, 0x00, 0xda, 0x3c, +0x00, 0x02, 0x50, 0x05, 0x00, 0x08, 0x8b, 0xc7, +0x00, 0x0c, 0xc1, 0xc0, 0x00, 0x08, 0x52, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x09, 0x52, 0xe0, +0x00, 0x0c, 0xb1, 0xd5, 0x00, 0x08, 0x8c, 0x3d, +0x00, 0x0c, 0xc1, 0xc8, 0x00, 0x0d, 0x01, 0xc8, +0x00, 0x06, 0x47, 0x00, 0x00, 0x0c, 0xa1, 0xd1, +0x00, 0x08, 0x48, 0x03, 0x00, 0x08, 0xfc, 0x3f, +0x00, 0x0d, 0x02, 0x23, 0x00, 0x08, 0x7a, 0x1c, +0x00, 0x08, 0x8c, 0x3d, 0x00, 0x08, 0x0e, 0x00, +0x00, 0x0d, 0x01, 0xc8, 0x00, 0x06, 0x47, 0x00, +0x00, 0x0c, 0xa1, 0xdf, 0x00, 0x06, 0xd3, 0x04, +0x00, 0x0c, 0xa1, 0xdf, 0x00, 0x08, 0x80, 0x1c, +0x00, 0x08, 0x82, 0x1d, 0x00, 0x08, 0x8a, 0x2b, +0x00, 0x08, 0x48, 0x00, 0x00, 0x08, 0xfc, 0x3f, +0x00, 0x0d, 0x02, 0x23, 0x00, 0x08, 0x82, 0x29, +0x00, 0x01, 0x13, 0x24, 0x00, 0x06, 0x47, 0x00, +0x00, 0x08, 0xd2, 0x01, 0x00, 0x0c, 0xa9, 0x67, +0x00, 0x06, 0x4d, 0x00, 0x00, 0x0c, 0xaa, 0x8d, +0x00, 0x06, 0xa7, 0x01, 0x00, 0x0c, 0xa1, 0xea, +0x00, 0x08, 0x80, 0x28, 0x00, 0x08, 0xc4, 0x00, +0x00, 0x08, 0x0c, 0x1c, 0x00, 0x08, 0x0e, 0x01, +0x00, 0x08, 0x80, 0x28, 0x00, 0x06, 0x0f, 0x24, +0x00, 0x0c, 0xa1, 0xf1, 0x00, 0x08, 0xce, 0x00, +0x00, 0x0d, 0x00, 0xc5, 0x00, 0x08, 0xfc, 0x3f, +0x00, 0x0d, 0x02, 0x97, 0x00, 0x06, 0x01, 0x01, +0x00, 0x0c, 0x81, 0xf7, 0x00, 0x06, 0x01, 0xaf, +0x00, 0x0c, 0x88, 0xb3, 0x00, 0x06, 0x01, 0x00, +0x00, 0x0c, 0xa0, 0x69, 0x00, 0x06, 0x01, 0xb8, +0x00, 0x0c, 0xa0, 0x54, 0x00, 0x06, 0x01, 0xb3, +0x00, 0x0c, 0xa0, 0x12, 0x00, 0x06, 0x01, 0xb7, +0x00, 0x0c, 0xa2, 0xbe, 0x00, 0x0d, 0x00, 0x09, +0x00, 0x08, 0x0c, 0x1c, 0x00, 0x08, 0x0e, 0x05, +0x00, 0x08, 0x02, 0x00, 0x00, 0x08, 0x86, 0x07, +0x00, 0x03, 0x88, 0x81, 0x00, 0x06, 0x01, 0x01, +0x00, 0x0c, 0xa2, 0x0f, 0x00, 0x06, 0x07, 0x00, +0x00, 0x0c, 0xa2, 0x0f, 0x00, 0x08, 0xfa, 0x04, +0x00, 0x08, 0x8c, 0x3d, 0x00, 0x05, 0x29, 0x10, +0x00, 0x04, 0xe5, 0x82, 0x00, 0x05, 0x10, 0x81, +0x00, 0x05, 0x23, 0x81, 0x00, 0x08, 0x04, 0x00, +0x00, 0x03, 0xb9, 0x83, 0x00, 0x06, 0x07, 0x00, +0x00, 0x0c, 0xa2, 0x1b, 0x00, 0x03, 0xa1, 0x05, +0x00, 0x06, 0x07, 0x00, 0x00, 0x0c, 0x92, 0x19, +0x00, 0x00, 0x97, 0x83, 0x00, 0x00, 0xa7, 0x82, +0x00, 0x0d, 0x02, 0x1b, 0x00, 0x05, 0x17, 0x83, +0x00, 0x05, 0x27, 0x82, 0x00, 0x00, 0x96, 0x23, +0x00, 0x06, 0x02, 0x05, 0x00, 0x0c, 0x92, 0x21, +0x00, 0x06, 0x48, 0x01, 0x00, 0x0c, 0x92, 0x21, +0x00, 0x08, 0xfe, 0x3e, 0x00, 0x00, 0x94, 0x23, +0x00, 0x08, 0xfe, 0x3e, 0x00, 0x06, 0xd9, 0x02, +0x00, 0x0c, 0xa2, 0x27, 0x00, 0x08, 0x00, 0x00, +0x00, 0x08, 0x02, 0x00, 0x00, 0x06, 0x0b, 0x01, +0x00, 0x0c, 0xaa, 0x2b, 0x00, 0x03, 0x81, 0x01, +0x00, 0x03, 0x93, 0x01, 0x00, 0x06, 0x47, 0x02, +0x00, 0x0c, 0x92, 0x41, 0x00, 0x06, 0x01, 0x00, +0x00, 0x0c, 0x9a, 0x30, 0x00, 0x00, 0x81, 0x01, +0x00, 0x04, 0x21, 0x01, 0x00, 0x04, 0x05, 0x01, +0x00, 0x03, 0xc1, 0x01, 0x00, 0x05, 0x45, 0x82, +0x00, 0x06, 0x03, 0x00, 0x00, 0x0c, 0x9a, 0x37, +0x00, 0x00, 0x93, 0x01, 0x00, 0x04, 0x33, 0x01, +0x00, 0x04, 0x17, 0x01, 0x00, 0x03, 0xc3, 0x01, +0x00, 0x05, 0x47, 0x83, 0x00, 0x08, 0x0a, 0x03, +0x00, 0x03, 0xca, 0x11, 0x00, 0x00, 0xc1, 0x80, +0x00, 0x03, 0xca, 0x12, 0x00, 0x00, 0xc3, 0x81, +0x00, 0x0d, 0x02, 0x4a, 0x00, 0x01, 0x21, 0x01, +0x00, 0x04, 0x01, 0x01, 0x00, 0x01, 0x33, 0x01, +0x00, 0x04, 0x13, 0x01, 0x00, 0x08, 0x0a, 0x04, +0x00, 0x03, 0xca, 0x11, 0x00, 0x00, 0xc1, 0x80, +0x00, 0x03, 0xca, 0x12, 0x00, 0x00, 0xc3, 0x81, +0x00, 0x08, 0x88, 0x29, 0x00, 0x03, 0xa4, 0x64, +0x00, 0x03, 0x45, 0x84, 0x00, 0x02, 0x50, 0x24, +0x00, 0x03, 0xbb, 0x83, 0x00, 0x03, 0x47, 0x84, +0x00, 0x01, 0x28, 0xa4, 0x00, 0x06, 0x05, 0x24, +0x00, 0x0c, 0xaa, 0x58, 0x00, 0x06, 0x49, 0x00, +0x00, 0x0c, 0xaa, 0x58, 0x00, 0x0c, 0xda, 0x57, +0x00, 0x0d, 0x01, 0xff, 0x00, 0x0c, 0xca, 0x55, +0x00, 0x06, 0xd9, 0x02, 0x00, 0x0c, 0xa2, 0x5f, +0x00, 0x06, 0x6d, 0x00, 0x00, 0x0c, 0xa2, 0x5e, +0x00, 0x08, 0x08, 0x04, 0x00, 0x0d, 0x02, 0x5f, +0x00, 0x08, 0x08, 0x20, 0x00, 0x08, 0xd2, 0x04, +0x00, 0x09, 0x08, 0xe0, 0x00, 0x01, 0xc0, 0x23, +0x00, 0x03, 0xc9, 0x01, 0x00, 0x08, 0x0a, 0x26, +0x00, 0x03, 0xdb, 0x04, 0x00, 0x03, 0x5b, 0x08, +0x00, 0x00, 0xd9, 0x84, 0x00, 0x08, 0xfe, 0x04, +0x00, 0x0d, 0x02, 0x72, 0x00, 0x08, 0x80, 0x00, +0x00, 0x0d, 0x02, 0x72, 0x00, 0x08, 0x80, 0x00, +0x00, 0x00, 0x81, 0x08, 0x00, 0x0d, 0x02, 0x75, +0x00, 0x00, 0x93, 0x08, 0x00, 0x0d, 0x02, 0x72, +0x00, 0x00, 0x81, 0x08, 0x00, 0x08, 0x80, 0x00, +0x00, 0x09, 0x00, 0xe1, 0x00, 0x09, 0x02, 0xe2, +0x00, 0x08, 0xfe, 0x3e, 0x00, 0x00, 0x93, 0x08, +0x00, 0x0d, 0x02, 0x72, 0x00, 0x08, 0x0c, 0x1c, +0x00, 0x08, 0x0e, 0x01, 0x00, 0x08, 0x80, 0x00, +0x00, 0x06, 0x0f, 0x23, 0x00, 0x0c, 0xa0, 0xc8, +0x00, 0x08, 0x82, 0x28, 0x00, 0x08, 0xce, 0x01, +0x00, 0x00, 0xf3, 0x80, 0x00, 0x08, 0xd0, 0x00, +0x00, 0x06, 0x0f, 0x01, 0x00, 0x0c, 0xa0, 0xce, +0x00, 0x06, 0x47, 0x00, 0x00, 0x0c, 0xa8, 0xce, +0x00, 0x08, 0x4c, 0x02, 0x00, 0x08, 0x00, 0x00, +0x00, 0x09, 0x00, 0xc1, 0x00, 0x08, 0xfc, 0x3f, +0x00, 0x0d, 0x02, 0xd5, 0x00, 0x06, 0x2b, 0x02, +0x00, 0x0c, 0xa9, 0x26, 0x00, 0x08, 0x26, 0x00, +0x00, 0x0d, 0x00, 0xe0, 0x00, 0x02, 0x00, 0x27, +0x00, 0x02, 0x10, 0x00, 0x00, 0x06, 0x50, 0x01, +0x00, 0x08, 0xce, 0x00, 0x00, 0x0c, 0x92, 0x85, +0x00, 0x06, 0x4d, 0x01, 0x00, 0x0c, 0xa2, 0x77, +0x00, 0x0d, 0x00, 0xce, 0x00, 0x06, 0x01, 0xb2, +0x00, 0x0c, 0xaa, 0xa7, 0x00, 0x08, 0x0d, 0x08, +0x00, 0x06, 0x0d, 0x00, 0x00, 0x0c, 0xaa, 0x97, +0x00, 0x08, 0x0c, 0x08, 0x00, 0x04, 0x8d, 0x08, +0x00, 0x0c, 0xaa, 0x97, 0x00, 0x08, 0x0c, 0x10, +0x00, 0x06, 0x0d, 0x00, 0x00, 0x0c, 0xa2, 0x9d, +0x00, 0x06, 0x0d, 0x01, 0x00, 0x0c, 0xa2, 0xa8, +0x00, 0x04, 0x8d, 0x08, 0x00, 0x06, 0x01, 0x01, +0x00, 0x0c, 0xaa, 0x97, 0x00, 0x03, 0x8d, 0x08, +0x00, 0x04, 0x81, 0x08, 0x00, 0x08, 0xfe, 0x3e, +0x00, 0x08, 0x0c, 0x08, 0x00, 0x04, 0x8d, 0x08, +0x00, 0x08, 0xfe, 0x3e, 0x00, 0x08, 0x0c, 0x01, +0x00, 0x04, 0xad, 0x0f, 0x00, 0x08, 0x0c, 0x03, +0x00, 0x04, 0x8d, 0x0d, 0x00, 0x06, 0x67, 0xff, +0x00, 0x0c, 0xa2, 0xba, 0x00, 0x08, 0x82, 0x33, +0x00, 0x05, 0x92, 0x34, 0x00, 0x01, 0x13, 0x1f, +0x00, 0x02, 0x10, 0x01, 0x00, 0x00, 0x83, 0x80, +0x00, 0x06, 0x01, 0x07, 0x00, 0x0c, 0x8a, 0xba, +0x00, 0x05, 0x00, 0x87, 0x00, 0x0d, 0x02, 0xb6, +0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0xb0, 0x81, +0x00, 0x03, 0xc7, 0x04, 0x00, 0x08, 0xfe, 0x3e, +0x00, 0x06, 0x5b, 0x01, 0x00, 0x0c, 0xa2, 0xc2, +0x00, 0x09, 0x00, 0xe8, 0x00, 0x0d, 0x00, 0x01, +0x00, 0x0c, 0xda, 0xc2, 0x00, 0x08, 0x5a, 0x00, +0x00, 0x08, 0x23, 0xff, 0x00, 0x08, 0x24, 0x00, +0x00, 0x08, 0x4e, 0x00, 0x00, 0x08, 0x2a, 0x02, +0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x2a, 0xe7, +0x00, 0x08, 0x82, 0x10, 0x00, 0x00, 0x80, 0x71, +0x00, 0x01, 0x90, 0x01, 0x00, 0x0c, 0xaa, 0xcb, +0x00, 0x02, 0x01, 0xa8, 0x00, 0x08, 0x4c, 0x02, +0x00, 0x0d, 0x02, 0x85, 0x00, 0x08, 0xe4, 0x33, +0x00, 0x08, 0x60, 0x00, 0x00, 0x08, 0x6a, 0x01, +0x00, 0x0d, 0x00, 0x2f, 0x00, 0x02, 0x00, 0x11, +0x00, 0x06, 0x20, 0x00, 0x00, 0x08, 0xa2, 0x00, +0x00, 0x0c, 0x92, 0xdd, 0x00, 0x02, 0x00, 0x12, +0x00, 0x08, 0x22, 0x00, 0x00, 0x08, 0xa4, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0xfe, 0x3e, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x0a, 0x0d, +}; +static uchar +zrmpeg2[] = { +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0xc1, 0x81, +0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x2e, 0x01, +0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x47, 0x17, +0x00, 0x03, 0x2e, 0x02, 0x00, 0x02, 0xee, 0x00, +0x00, 0x03, 0x74, 0x17, 0x00, 0x02, 0x9c, 0x02, +0x00, 0x03, 0x88, 0x0c, 0x00, 0x03, 0x46, 0x81, +0x00, 0x03, 0x50, 0x07, 0x00, 0x02, 0x80, 0x80, +0x00, 0x03, 0x88, 0x13, 0x00, 0x03, 0x43, 0x81, +0x00, 0x02, 0x9c, 0x01, 0x00, 0x03, 0x88, 0x14, +0x00, 0x03, 0x48, 0x80, 0x00, 0x03, 0x40, 0x80, +0x00, 0x02, 0x9c, 0x80, 0x00, 0x03, 0x88, 0x17, +0x00, 0x03, 0x44, 0x81, 0x00, 0x02, 0x9c, 0x04, +0x00, 0x03, 0x88, 0x1a, 0x00, 0x03, 0x45, 0x81, +0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x50, 0x17, +0x00, 0x00, 0x5c, 0x8f, 0x00, 0x02, 0x84, 0x01, +0x00, 0x03, 0x88, 0x20, 0x00, 0x03, 0x49, 0x81, +0x00, 0x02, 0xee, 0x00, 0x00, 0x00, 0x5d, 0x0f, +0x00, 0x00, 0xc9, 0x04, 0x00, 0x03, 0x4d, 0x12, +0x00, 0x00, 0xa4, 0x2d, 0x00, 0x02, 0xe8, 0x00, +0x00, 0x02, 0x80, 0x01, 0x00, 0x03, 0x8a, 0x29, +0x00, 0x02, 0xe8, 0x01, 0x00, 0x03, 0x4d, 0x14, +0x00, 0x00, 0x41, 0x10, 0x00, 0x01, 0x49, 0x02, +0x00, 0x00, 0xab, 0x6d, 0x00, 0x00, 0x41, 0x20, +0x00, 0x01, 0x49, 0x02, 0x00, 0x00, 0xaa, 0x0d, +0x00, 0x02, 0x9c, 0x01, 0x00, 0x03, 0x8a, 0x33, +0x00, 0x00, 0x92, 0x10, 0x00, 0x03, 0x4d, 0x14, +0x00, 0x00, 0x5d, 0x10, 0x00, 0x00, 0xc9, 0x02, +0x00, 0x00, 0xab, 0x6d, 0x00, 0x00, 0x5d, 0x20, +0x00, 0x00, 0xc9, 0x02, 0x00, 0x00, 0xaa, 0x0d, +0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x28, 0x5f, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x42, 0x17, +0x00, 0x03, 0x2e, 0x20, 0x00, 0x01, 0xdc, 0x01, +0x00, 0x03, 0xa6, 0x43, 0x00, 0x03, 0x4d, 0x17, +0x00, 0x00, 0x20, 0x0d, 0x00, 0x03, 0x20, 0x17, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x2e, 0x21, +0x00, 0x00, 0x3c, 0x02, 0x00, 0x03, 0x20, 0x24, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x75, 0x17, +0x00, 0x03, 0x2e, 0x68, 0x00, 0x02, 0xef, 0x00, +0x00, 0x03, 0x43, 0x17, 0x00, 0x02, 0xef, 0x00, +0x00, 0x03, 0x44, 0x17, 0x00, 0x02, 0xef, 0x00, +0x00, 0x03, 0x45, 0x17, 0x00, 0x00, 0x1c, 0x01, +0x00, 0x03, 0x46, 0x10, 0x00, 0x00, 0x20, 0x02, +0x00, 0x03, 0x47, 0x10, 0x00, 0x00, 0x22, 0x23, +0x00, 0x00, 0x20, 0x02, 0x00, 0x03, 0x49, 0x10, +0x00, 0x00, 0x23, 0xa3, 0x00, 0x03, 0x50, 0x35, +0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0xe0, 0x04, +0x00, 0x03, 0xa6, 0x5e, 0x00, 0x01, 0xe0, 0x02, +0x00, 0x03, 0x4f, 0x10, 0x00, 0x00, 0x20, 0x02, +0x00, 0x03, 0x58, 0x10, 0x00, 0x03, 0x50, 0x35, +0x00, 0x01, 0x40, 0x01, 0x00, 0x00, 0x00, 0x01, +0x00, 0x00, 0x20, 0x83, 0x00, 0x03, 0x59, 0x11, +0x00, 0x01, 0xe0, 0x84, 0x00, 0x03, 0x5a, 0x11, +0x00, 0x02, 0xe2, 0x01, 0x00, 0x00, 0x26, 0x83, +0x00, 0x00, 0x36, 0xa5, 0x00, 0x03, 0x14, 0x69, +0x00, 0x03, 0x10, 0x6a, 0x00, 0x03, 0xa6, 0x6f, +0x00, 0x03, 0x1c, 0x6a, 0x00, 0x03, 0x1e, 0x6b, +0x00, 0x03, 0xa6, 0x72, 0x00, 0x03, 0x30, 0x6b, +0x00, 0x03, 0x2a, 0x6c, 0x00, 0x03, 0xa8, 0x77, +0x00, 0x03, 0x0e, 0x6c, 0x00, 0x03, 0xa6, 0x77, +0x00, 0x03, 0x12, 0x6c, 0x00, 0x03, 0x32, 0x6d, +0x00, 0x03, 0x34, 0x6e, 0x00, 0x03, 0xa8, 0x7c, +0x00, 0x03, 0x0c, 0x6d, 0x00, 0x03, 0x2a, 0x6e, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x42, 0x17, +0x00, 0x03, 0x2e, 0x22, 0x00, 0x00, 0x1c, 0x01, +0x00, 0x01, 0x40, 0x01, 0x00, 0x00, 0xc0, 0x01, +0x00, 0x03, 0x20, 0x18, 0x00, 0x02, 0xef, 0x00, +0x00, 0x03, 0x73, 0x17, 0x00, 0x03, 0x2e, 0x23, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x76, 0x17, +0x00, 0x03, 0x2e, 0x60, 0x00, 0x01, 0x5f, 0x01, +0x00, 0x02, 0x9c, 0x01, 0x00, 0x03, 0x88, 0x8d, +0x00, 0x03, 0x41, 0x81, 0x00, 0x02, 0xef, 0x00, +0x00, 0x03, 0x43, 0x17, 0x00, 0x03, 0x2e, 0x61, +0x00, 0x02, 0x9c, 0x01, 0x00, 0x03, 0x88, 0x94, +0x00, 0x03, 0x42, 0x81, 0x00, 0x03, 0x47, 0x80, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x44, 0x17, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x45, 0x17, +0x00, 0x03, 0x4d, 0x33, 0x00, 0x01, 0xff, 0x2d, +0x00, 0x03, 0x73, 0x0c, 0x00, 0x03, 0x5b, 0x1f, +0x00, 0x03, 0x8f, 0x94, 0x00, 0x03, 0x4d, 0x36, +0x00, 0x03, 0x50, 0x05, 0x00, 0x00, 0x20, 0x02, +0x00, 0x03, 0x51, 0x03, 0x00, 0x00, 0x24, 0x84, +0x00, 0x03, 0xa6, 0xa7, 0x00, 0x00, 0xc0, 0x01, +0x00, 0x01, 0xa3, 0x6d, 0x00, 0x01, 0xa7, 0x6d, +0x00, 0x03, 0x8e, 0xab, 0x00, 0x01, 0xa0, 0x0d, +0x00, 0x00, 0xc0, 0x01, 0x00, 0x03, 0x4d, 0x11, +0x00, 0x01, 0xe3, 0x6d, 0x00, 0x03, 0x72, 0x0d, +0x00, 0x00, 0x1c, 0x81, 0x00, 0x03, 0x41, 0x11, +0x00, 0x00, 0x3d, 0xa2, 0x00, 0x02, 0xee, 0x00, +0x00, 0x00, 0x1f, 0x81, 0x00, 0x03, 0x2e, 0x6f, +0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x4c, 0x17, +0x00, 0x03, 0x51, 0x03, 0x00, 0x01, 0x45, 0x01, +0x00, 0x00, 0x28, 0x86, 0x00, 0x03, 0x47, 0x11, +0x00, 0x00, 0x06, 0x01, 0x00, 0x03, 0x51, 0x06, +0x00, 0x00, 0xc4, 0x81, 0x00, 0x03, 0x48, 0x11, +0x00, 0x00, 0x26, 0x63, 0x00, 0x00, 0x08, 0x81, +0x00, 0x03, 0x4e, 0x11, 0x00, 0x00, 0x28, 0x05, +0x00, 0x03, 0x4f, 0x10, 0x00, 0x00, 0x00, 0x01, +0x00, 0x03, 0x71, 0x10, 0x00, 0x03, 0x50, 0x05, +0x00, 0x00, 0xc1, 0x81, 0x00, 0x03, 0x4b, 0x13, +0x00, 0x00, 0x2c, 0x03, 0x00, 0x03, 0x58, 0x10, +0x00, 0x03, 0x50, 0x36, 0x00, 0x01, 0x41, 0x81, +0x00, 0x03, 0x4d, 0x13, 0x00, 0x03, 0x52, 0x04, +0x00, 0x01, 0x48, 0x81, 0x00, 0x01, 0xa5, 0x0d, +0x00, 0x01, 0xca, 0x81, 0x00, 0x03, 0x53, 0x32, +0x00, 0x00, 0x2d, 0x08, 0x00, 0x00, 0x29, 0x03, +0x00, 0x01, 0xc9, 0x01, 0x00, 0x03, 0x56, 0x12, +0x00, 0x03, 0x52, 0x31, 0x00, 0x00, 0x2b, 0x62, +0x00, 0x01, 0x4d, 0x81, 0x00, 0x00, 0x2d, 0x8d, +0x00, 0x03, 0x5a, 0x13, 0x00, 0x01, 0xcd, 0x81, +0x00, 0x03, 0x59, 0x13, 0x00, 0x01, 0xc0, 0x01, +0x00, 0x03, 0x4d, 0x11, 0x00, 0x01, 0xe2, 0xad, +0x00, 0x03, 0x18, 0x62, 0x00, 0x03, 0x1c, 0x63, +0x00, 0x03, 0x1e, 0x64, 0x00, 0x03, 0x0e, 0x65, +0x00, 0x03, 0x32, 0x66, 0x00, 0x03, 0xa6, 0xed, +0x00, 0x03, 0xa8, 0xf2, 0x00, 0x03, 0xa3, 0x03, +0x00, 0x03, 0xab, 0x09, 0x00, 0x03, 0x0a, 0x62, +0x00, 0x03, 0x50, 0x31, 0x00, 0x03, 0x20, 0x64, +0x00, 0x03, 0x28, 0x65, 0x00, 0x03, 0x34, 0x66, +0x00, 0x03, 0x8f, 0x0c, 0x00, 0x03, 0xa9, 0x0c, +0x00, 0x03, 0xaa, 0xfb, 0x00, 0x03, 0xa2, 0xfe, +0x00, 0x03, 0x02, 0x62, 0x00, 0x03, 0x8f, 0x0c, +0x00, 0x03, 0xa2, 0xf6, 0x00, 0x03, 0xa5, 0x0c, +0x00, 0x03, 0x2a, 0x66, 0x00, 0x03, 0x8f, 0x0c, +0x00, 0x03, 0x30, 0x63, 0x00, 0x03, 0x12, 0x64, +0x00, 0x03, 0x2c, 0x65, 0x00, 0x03, 0x2a, 0x66, +0x00, 0x03, 0x8f, 0x0c, 0x00, 0x03, 0x02, 0x62, +0x00, 0x03, 0x0c, 0x65, 0x00, 0x03, 0x8f, 0x0c, +0x00, 0x03, 0x0a, 0x62, 0x00, 0x03, 0x30, 0x64, +0x00, 0x03, 0x12, 0x65, 0x00, 0x03, 0x2c, 0x66, +0x00, 0x03, 0x8f, 0x0c, 0x00, 0x03, 0x0a, 0x62, +0x00, 0x03, 0x30, 0x63, 0x00, 0x03, 0x12, 0x64, +0x00, 0x03, 0x2c, 0x65, 0x00, 0x03, 0x16, 0x66, +0x00, 0x03, 0x8f, 0x0c, 0x00, 0x03, 0x02, 0x62, +0x00, 0x03, 0x0c, 0x65, 0x00, 0x03, 0x34, 0x66, +0x00, 0x03, 0x14, 0x67, 0x00, 0x03, 0xa9, 0x0f, +0x00, 0x03, 0x10, 0x67, 0x00, 0x03, 0x4c, 0x1f, +0x00, 0x03, 0x8f, 0x86, 0x00, 0x02, 0xda, 0x44, +0x00, 0x02, 0xe2, 0x01, 0x00, 0x02, 0xe0, 0x04, +0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x4d, 0x57, +0x00, 0x00, 0x27, 0x6d, 0x00, 0x01, 0xc0, 0x01, +0x00, 0x03, 0x8b, 0x14, 0x00, 0x02, 0xe0, 0x04, +0x00, 0x02, 0xef, 0x00, 0x00, 0x01, 0x5d, 0x0a, +0x00, 0x03, 0x4d, 0x52, 0x00, 0x00, 0x27, 0x6d, +0x00, 0x03, 0x4d, 0x57, 0x00, 0x00, 0x27, 0x6d, +0x00, 0x01, 0xc0, 0x01, 0x00, 0x03, 0x8b, 0x1a, +0x00, 0x02, 0xe0, 0x06, 0x00, 0x02, 0xee, 0x00, +0x00, 0x03, 0x4d, 0x57, 0x00, 0x00, 0x27, 0x6d, +0x00, 0x01, 0xc0, 0x01, 0x00, 0x03, 0x8b, 0x23, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x2e, 0x56, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x2e, 0x57, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x71, 0x17, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x72, 0x17, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x77, 0x17, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x41, 0x17, +0x00, 0x03, 0xa1, 0x36, 0x00, 0x01, 0x5f, 0x82, +0x00, 0x02, 0xf1, 0x75, 0x00, 0x03, 0x59, 0x17, +0x00, 0x03, 0x5b, 0x1f, 0x00, 0x03, 0x8f, 0xc5, +0x00, 0x00, 0xc4, 0x89, 0x00, 0x01, 0x40, 0x07, +0x00, 0x03, 0x4d, 0x10, 0x00, 0x00, 0xa4, 0x0d, +0x00, 0x03, 0x73, 0x10, 0x00, 0x02, 0xee, 0x00, +0x00, 0x03, 0x50, 0x34, 0x00, 0x03, 0x20, 0x03, +0x00, 0x02, 0xe4, 0x00, 0x00, 0x03, 0x53, 0x00, +0x00, 0x03, 0xb3, 0x48, 0x00, 0x01, 0x4d, 0x84, +0x00, 0x02, 0x8c, 0x01, 0x00, 0x03, 0x89, 0x50, +0x00, 0x00, 0x4c, 0x0e, 0x00, 0x01, 0x40, 0x01, +0x00, 0x02, 0x40, 0x05, 0x00, 0x03, 0x81, 0x50, +0x00, 0x02, 0xe4, 0x01, 0x00, 0x02, 0x40, 0x03, +0x00, 0x03, 0x81, 0x50, 0x00, 0x02, 0xe4, 0x02, +0x00, 0x03, 0x75, 0x12, 0x00, 0x02, 0xe0, 0x00, +0x00, 0x03, 0x76, 0x10, 0x00, 0x02, 0x48, 0x02, +0x00, 0x03, 0x85, 0x70, 0x00, 0x03, 0x59, 0x37, +0x00, 0x03, 0x50, 0x33, 0x00, 0x01, 0x40, 0x02, +0x00, 0x03, 0x58, 0x10, 0x00, 0x03, 0x5b, 0x1f, +0x00, 0x03, 0x8f, 0xc5, 0x00, 0x00, 0xc4, 0x81, +0x00, 0x01, 0x40, 0x0f, 0x00, 0x03, 0x4d, 0x10, +0x00, 0x00, 0xa4, 0x0d, 0x00, 0x00, 0x23, 0xaf, +0x00, 0x03, 0x59, 0x31, 0x00, 0x03, 0xb3, 0x63, +0x00, 0x03, 0x59, 0x32, 0x00, 0x03, 0x4c, 0x1f, +0x00, 0x03, 0x8f, 0x7c, 0x00, 0x03, 0x72, 0x10, +0x00, 0x03, 0x4b, 0x0e, 0x00, 0x02, 0x60, 0x0e, +0x00, 0x03, 0x85, 0x6a, 0x00, 0x03, 0x4b, 0x10, +0x00, 0x03, 0x50, 0x0e, 0x00, 0x01, 0xa0, 0x0b, +0x00, 0x03, 0x76, 0x10, 0x00, 0x03, 0x51, 0x32, +0x00, 0x01, 0xa4, 0x8b, 0x00, 0x03, 0x72, 0x11, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x50, 0x10, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x4a, 0x17, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x4b, 0x17, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x4b, 0x17, +0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x45, 0x17, +0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x8f, 0x7a, +0x00, 0x03, 0x50, 0x33, 0x00, 0x01, 0x40, 0x02, +0x00, 0x03, 0x58, 0x10, 0x00, 0x03, 0x5b, 0x1f, +0x00, 0x03, 0x8f, 0xc5, 0x00, 0x00, 0xc4, 0x81, +0x00, 0x01, 0x40, 0x0f, 0x00, 0x03, 0x4d, 0x10, +0x00, 0x03, 0x5f, 0x0c, 0x00, 0x00, 0xa4, 0x0d, +0x00, 0x03, 0x58, 0x35, 0x00, 0x03, 0x59, 0x36, +0x00, 0x03, 0x5b, 0x1f, 0x00, 0x03, 0x8f, 0xc5, +0x00, 0x00, 0xc4, 0x89, 0x00, 0x01, 0x40, 0x07, +0x00, 0x03, 0x4d, 0x10, 0x00, 0x00, 0xa4, 0x0d, +0x00, 0x03, 0xa1, 0x90, 0x00, 0x01, 0x40, 0x02, +0x00, 0x03, 0xb1, 0x92, 0x00, 0x00, 0xc0, 0x02, +0x00, 0x03, 0x5f, 0x0c, 0x00, 0x03, 0x4f, 0x10, +0x00, 0x03, 0xa7, 0x96, 0x00, 0x02, 0xec, 0x0f, +0x00, 0x03, 0x4c, 0x33, 0x00, 0x00, 0x3b, 0x0c, +0x00, 0x03, 0x87, 0x9a, 0x00, 0x02, 0xec, 0x00, +0x00, 0x03, 0x4d, 0x16, 0x00, 0x00, 0xd8, 0x01, +0x00, 0x00, 0x22, 0x03, 0x00, 0x03, 0x52, 0x03, +0x00, 0x01, 0x49, 0x01, 0x00, 0x00, 0x2a, 0x8d, +0x00, 0x03, 0x50, 0x15, 0x00, 0x03, 0xa9, 0xbf, +0x00, 0x03, 0xa7, 0xb5, 0x00, 0x03, 0xab, 0xb0, +0x00, 0x03, 0xad, 0xab, 0x00, 0x03, 0xa3, 0xa8, +0x00, 0x03, 0xa5, 0xc2, 0x00, 0x03, 0x8f, 0xc3, +0x00, 0x03, 0x50, 0x14, 0x00, 0x03, 0xa5, 0xc3, +0x00, 0x03, 0x8f, 0xc2, 0x00, 0x03, 0xa3, 0xae, +0x00, 0x03, 0xaf, 0xc3, 0x00, 0x03, 0x8f, 0xc2, +0x00, 0x03, 0x50, 0x14, 0x00, 0x03, 0x8f, 0xc2, +0x00, 0x03, 0x50, 0x16, 0x00, 0x03, 0xa3, 0xb3, +0x00, 0x03, 0x8f, 0xc3, 0x00, 0x00, 0xc0, 0x01, +0x00, 0x03, 0x8f, 0xc2, 0x00, 0x03, 0xab, 0xbc, +0x00, 0x03, 0xa3, 0xb9, 0x00, 0x03, 0xaf, 0xc3, +0x00, 0x03, 0x8f, 0xc2, 0x00, 0x03, 0x50, 0x14, +0x00, 0x03, 0xaf, 0xc3, 0x00, 0x03, 0x8f, 0xc2, +0x00, 0x03, 0x50, 0x16, 0x00, 0x01, 0xc0, 0x01, +0x00, 0x03, 0x8f, 0xc3, 0x00, 0x03, 0xa7, 0xc1, +0x00, 0x03, 0x8f, 0xa4, 0x00, 0x03, 0xaf, 0xc3, +0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x5f, 0x1b, +0x00, 0x03, 0x20, 0x70, 0x00, 0x02, 0xe4, 0x01, +0x00, 0x02, 0xe0, 0x01, 0x00, 0x00, 0xc0, 0x0f, +0x00, 0x03, 0x5a, 0x10, 0x00, 0x02, 0xe0, 0x00, +0x00, 0x02, 0xe2, 0x00, 0x00, 0x02, 0xa8, 0x19, +0x00, 0x03, 0x89, 0xce, 0x00, 0x00, 0x24, 0x98, +0x00, 0x01, 0x40, 0x01, 0x00, 0x02, 0x84, 0x01, +0x00, 0x03, 0x89, 0xd2, 0x00, 0x00, 0xa0, 0x1a, +0x00, 0x01, 0x44, 0x81, 0x00, 0x00, 0xc9, 0x01, +0x00, 0x03, 0x8b, 0xcb, 0x00, 0x03, 0x5f, 0x1b, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x0a, 0x0d, +}; +static uchar +zrmpeg3s[] = { +0x00, 0x03, 0x8e, 0x00, 0x00, 0x03, 0xc0, 0x0a, +0x00, 0x03, 0x50, 0x0b, 0x00, 0x01, 0x40, 0x08, +0x00, 0x02, 0x80, 0x02, 0x00, 0x03, 0x8a, 0x07, +0x00, 0x02, 0xca, 0x00, 0x00, 0x02, 0x80, 0x01, +0x00, 0x03, 0x8a, 0x0a, 0x00, 0x02, 0xd6, 0xe0, +0x00, 0x02, 0xce, 0x00, 0x00, 0x03, 0x4c, 0x80, +0x00, 0x02, 0xde, 0x04, 0x00, 0x02, 0xc2, 0x00, +0x00, 0x02, 0xd2, 0x00, 0x00, 0x02, 0xf8, 0x00, +0x00, 0x03, 0x5d, 0x10, 0x00, 0x03, 0x5b, 0x1f, +0x00, 0x03, 0x8f, 0x8c, 0x00, 0x03, 0x88, 0x1d, +0x00, 0x02, 0x5c, 0xfa, 0x00, 0x03, 0x8a, 0x11, +0x00, 0x02, 0xe8, 0x46, 0x00, 0x02, 0xf8, 0x00, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x5d, 0x17, +0x00, 0x01, 0xd2, 0x01, 0x00, 0x03, 0x8a, 0x18, +0x00, 0x03, 0x8e, 0x11, 0x00, 0x02, 0xee, 0x00, +0x00, 0x03, 0x5b, 0x1f, 0x00, 0x03, 0x8f, 0xd2, +0x00, 0x03, 0xa0, 0x22, 0x00, 0x03, 0x46, 0x17, +0x00, 0x03, 0x5b, 0x1f, 0x00, 0x03, 0x8f, 0x8c, +0x00, 0x02, 0x5c, 0xbb, 0x00, 0x03, 0x8a, 0x29, +0x00, 0x03, 0x5b, 0x1f, 0x00, 0x03, 0x8f, 0x8c, +0x00, 0x03, 0x88, 0x1d, 0x00, 0x02, 0x5c, 0xb9, +0x00, 0x03, 0x89, 0x00, 0x00, 0x02, 0x5c, 0xbc, +0x00, 0x03, 0x84, 0x26, 0x00, 0x03, 0x50, 0x17, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x54, 0x17, +0x00, 0x02, 0xee, 0x00, 0x00, 0x01, 0xd2, 0x01, +0x00, 0x02, 0x5c, 0xff, 0x00, 0x03, 0x88, 0x30, +0x00, 0x01, 0x5d, 0x06, 0x00, 0x02, 0x48, 0x01, +0x00, 0x03, 0x8a, 0x3a, 0x00, 0x02, 0xee, 0x00, +0x00, 0x01, 0xd2, 0x02, 0x00, 0x02, 0xee, 0x00, +0x00, 0x01, 0x5d, 0x04, 0x00, 0x03, 0x88, 0x46, +0x00, 0x03, 0x5b, 0x1f, 0x00, 0x03, 0x8f, 0xd2, +0x00, 0x03, 0x44, 0x17, 0x00, 0x01, 0xd2, 0x04, +0x00, 0x02, 0x48, 0x03, 0x00, 0x03, 0x8a, 0x46, +0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x5b, 0x1f, +0x00, 0x03, 0x8f, 0xd2, 0x00, 0x01, 0xd2, 0x05, +0x00, 0x02, 0x50, 0x05, 0x00, 0x03, 0x86, 0x26, +0x00, 0x02, 0xe2, 0xff, 0x00, 0x00, 0x67, 0x6b, +0x00, 0x02, 0x60, 0x0d, 0x00, 0x03, 0x85, 0xfa, +0x00, 0x03, 0xb2, 0xab, 0x00, 0x02, 0x88, 0x02, +0x00, 0x03, 0x89, 0x16, 0x00, 0x03, 0x42, 0x17, +0x00, 0x03, 0x8e, 0xa0, 0x00, 0x02, 0x88, 0x02, +0x00, 0x03, 0x89, 0x16, 0x00, 0x03, 0x55, 0x00, +0x00, 0x02, 0x94, 0x01, 0x00, 0x03, 0x88, 0x5a, +0x00, 0x01, 0x57, 0x01, 0x00, 0x00, 0x5b, 0x07, +0x00, 0x03, 0xb5, 0x16, 0x00, 0x03, 0x8e, 0x5f, +0x00, 0x02, 0x94, 0x10, 0x00, 0x03, 0x89, 0x16, +0x00, 0x01, 0x57, 0x05, 0x00, 0x00, 0x5b, 0x07, +0x00, 0x03, 0xb7, 0x16, 0x00, 0x01, 0x41, 0x05, +0x00, 0x02, 0x48, 0x06, 0x00, 0x03, 0x8b, 0x16, +0x00, 0x02, 0x58, 0x00, 0x00, 0x03, 0x8b, 0x16, +0x00, 0x03, 0x55, 0x00, 0x00, 0x02, 0x94, 0x01, +0x00, 0x03, 0x8a, 0x69, 0x00, 0x03, 0x4b, 0x81, +0x00, 0x03, 0x8e, 0x6a, 0x00, 0x03, 0x4a, 0x81, +0x00, 0x03, 0x45, 0x10, 0x00, 0x03, 0xa0, 0x6e, +0x00, 0x03, 0x40, 0x81, 0x00, 0x03, 0x22, 0x16, +0x00, 0x03, 0x50, 0x35, 0x00, 0x02, 0x40, 0x01, +0x00, 0x03, 0x80, 0x75, 0x00, 0x03, 0x84, 0x7d, +0x00, 0x02, 0xe0, 0x00, 0x00, 0x02, 0xe2, 0x00, +0x00, 0x03, 0x8e, 0x79, 0x00, 0x03, 0x4d, 0x32, +0x00, 0x03, 0x50, 0x04, 0x00, 0x03, 0x4c, 0x1f, +0x00, 0x03, 0x8f, 0x83, 0x00, 0x03, 0x20, 0x10, +0x00, 0x03, 0x22, 0x11, 0x00, 0x03, 0x20, 0x12, +0x00, 0x03, 0x22, 0x13, 0x00, 0x03, 0xb4, 0x80, +0x00, 0x03, 0xb6, 0x80, 0x00, 0x03, 0x8e, 0x51, +0x00, 0x03, 0x55, 0x1c, 0x00, 0x02, 0xf8, 0x01, +0x00, 0x03, 0xb4, 0x84, 0x00, 0x02, 0xf8, 0x02, +0x00, 0x03, 0x51, 0x07, 0x00, 0x03, 0x5d, 0x11, +0x00, 0x01, 0xd2, 0x02, 0x00, 0x03, 0xa6, 0x90, +0x00, 0x03, 0x43, 0x81, 0x00, 0x02, 0xe2, 0xff, +0x00, 0x00, 0xc4, 0x88, 0x00, 0x00, 0x67, 0x65, +0x00, 0x02, 0xee, 0x00, 0x00, 0x00, 0xbc, 0x8d, +0x00, 0x03, 0x5d, 0x11, 0x00, 0x01, 0xd2, 0x01, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x5d, 0x17, +0x00, 0x01, 0xd2, 0x02, 0x00, 0x02, 0x50, 0x01, +0x00, 0x03, 0x80, 0x90, 0x00, 0x02, 0xef, 0x00, +0x00, 0x03, 0x47, 0x17, 0x00, 0x02, 0x90, 0x01, +0x00, 0x03, 0x88, 0x26, 0x00, 0x02, 0xee, 0x00, +0x00, 0x00, 0xdd, 0x08, 0x00, 0x02, 0xe0, 0xff, +0x00, 0x00, 0x61, 0x65, 0x00, 0x00, 0xa9, 0x65, +0x00, 0x03, 0x43, 0x80, 0x00, 0x03, 0x8e, 0x26, +0x00, 0x03, 0x48, 0x81, 0x00, 0x03, 0x49, 0x81, +0x00, 0x03, 0xa0, 0xa5, 0x00, 0x03, 0x40, 0x81, +0x00, 0x03, 0x22, 0x16, 0x00, 0x03, 0x4d, 0x36, +0x00, 0x03, 0x50, 0x02, 0x00, 0x03, 0x4c, 0x1f, +0x00, 0x03, 0x8f, 0x83, 0x00, 0x03, 0x20, 0x14, +0x00, 0x03, 0x22, 0x15, 0x00, 0x03, 0x50, 0x0e, +0x00, 0x02, 0xf8, 0x00, 0x00, 0x03, 0x51, 0x07, +0x00, 0x03, 0x5d, 0x11, 0x00, 0x03, 0x45, 0x80, +0x00, 0x03, 0xa5, 0x1d, 0x00, 0x01, 0xd2, 0x02, +0x00, 0x03, 0xa2, 0xbc, 0x00, 0x03, 0x41, 0x81, +0x00, 0x02, 0xe2, 0xff, 0x00, 0x00, 0xc4, 0x88, +0x00, 0x00, 0x67, 0x60, 0x00, 0x02, 0xee, 0x00, +0x00, 0x00, 0xbf, 0x8d, 0x00, 0x00, 0x12, 0x01, +0x00, 0x03, 0x8e, 0xbd, 0x00, 0x03, 0x50, 0x17, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0xa8, 0xc7, +0x00, 0x02, 0x5d, 0xe0, 0x00, 0x03, 0x89, 0x98, +0x00, 0x02, 0x5c, 0x00, 0x00, 0x03, 0x8a, 0xc7, +0x00, 0x02, 0xec, 0x01, 0x00, 0x01, 0xbb, 0xef, +0x00, 0x03, 0x8a, 0xcd, 0x00, 0x02, 0xde, 0x01, +0x00, 0x03, 0x8e, 0xcf, 0x00, 0x02, 0xde, 0x04, +0x00, 0x02, 0x5c, 0x01, 0x00, 0x03, 0x89, 0x1d, +0x00, 0x01, 0x5f, 0x08, 0x00, 0x02, 0x58, 0x01, +0x00, 0x03, 0x89, 0x70, 0x00, 0x03, 0xa8, 0xcf, +0x00, 0x03, 0x5d, 0x17, 0x00, 0x01, 0xd2, 0x02, +0x00, 0x02, 0x50, 0x01, 0x00, 0x03, 0x80, 0xbb, +0x00, 0x03, 0x51, 0x17, 0x00, 0x02, 0xef, 0x00, +0x00, 0x03, 0x45, 0x81, 0x00, 0x03, 0x42, 0x80, +0x00, 0x02, 0xce, 0x00, 0x00, 0x03, 0x4e, 0x17, +0x00, 0x03, 0xa8, 0xda, 0x00, 0x03, 0x47, 0x17, +0x00, 0x02, 0xd8, 0x26, 0x00, 0x02, 0x90, 0x01, +0x00, 0x03, 0x88, 0xde, 0x00, 0x02, 0xd8, 0xf9, +0x00, 0x03, 0x4f, 0x81, 0x00, 0x02, 0x5c, 0x01, +0x00, 0x03, 0x88, 0xea, 0x00, 0x01, 0x5e, 0x88, +0x00, 0x02, 0x54, 0x01, 0x00, 0x03, 0x88, 0xe5, +0x00, 0x03, 0x5f, 0x0c, 0x00, 0x02, 0xda, 0xff, +0x00, 0x00, 0x7f, 0x0d, 0x00, 0x02, 0x44, 0x00, +0x00, 0x03, 0x89, 0x2d, 0x00, 0x03, 0x5f, 0x0c, +0x00, 0x02, 0x84, 0xff, 0x00, 0x03, 0x8a, 0xf1, +0x00, 0x02, 0x90, 0x01, 0x00, 0x03, 0x8a, 0xf4, +0x00, 0x03, 0x42, 0x81, 0x00, 0x02, 0xce, 0x00, +0x00, 0x02, 0xdc, 0x00, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x5f, 0x0c, 0x00, 0x03, 0x50, 0x10, +0x00, 0x02, 0xee, 0x00, 0x00, 0x00, 0xdd, 0x08, +0x00, 0x03, 0x56, 0x17, 0x00, 0x02, 0xd8, 0xfb, +0x00, 0x03, 0x8f, 0x2d, 0x00, 0x02, 0xee, 0x00, +0x00, 0x00, 0xdd, 0x08, 0x00, 0x02, 0xe0, 0xff, +0x00, 0x00, 0x60, 0x20, 0x00, 0x00, 0xa8, 0x20, +0x00, 0x03, 0x41, 0x80, 0x00, 0x03, 0x8e, 0x26, +0x00, 0x03, 0xb9, 0x0a, 0x00, 0x03, 0x50, 0x1e, +0x00, 0x02, 0x40, 0x00, 0x00, 0x03, 0x89, 0x06, +0x00, 0x02, 0x40, 0x90, 0x00, 0x03, 0x8b, 0x08, +0x00, 0x02, 0xe0, 0x10, 0x00, 0x03, 0x20, 0xc0, +0x00, 0x02, 0xe0, 0x80, 0x00, 0x03, 0x20, 0xc0, +0x00, 0x03, 0x50, 0x07, 0x00, 0x03, 0xa7, 0x0e, +0x00, 0x03, 0x50, 0x05, 0x00, 0x03, 0x8f, 0x0f, +0x00, 0x03, 0x50, 0x05, 0x00, 0x02, 0xf8, 0x00, +0x00, 0x03, 0x5d, 0x10, 0x00, 0x03, 0x4c, 0x1f, +0x00, 0x03, 0x8f, 0x7c, 0x00, 0x02, 0xe0, 0xf0, +0x00, 0x03, 0x20, 0xc0, 0x00, 0x03, 0x8e, 0x00, +0x00, 0x02, 0x50, 0x00, 0x00, 0x03, 0x88, 0x26, +0x00, 0x01, 0xd2, 0x02, 0x00, 0x02, 0xef, 0x00, +0x00, 0x01, 0xd2, 0x02, 0x00, 0x03, 0x85, 0x19, +0x00, 0x03, 0x8e, 0x26, 0x00, 0x03, 0x4f, 0x80, +0x00, 0x03, 0x56, 0x10, 0x00, 0x03, 0xbf, 0x21, +0x00, 0x00, 0xdb, 0x08, 0x00, 0x02, 0x58, 0x00, +0x00, 0x03, 0x8a, 0xcd, 0x00, 0x02, 0xd8, 0xcd, +0x00, 0x03, 0xbf, 0x2b, 0x00, 0x01, 0xd2, 0x02, +0x00, 0x02, 0x50, 0x01, 0x00, 0x03, 0x87, 0x72, +0x00, 0x02, 0xef, 0x00, 0x00, 0x01, 0x5f, 0x08, +0x00, 0x03, 0x8f, 0x2d, 0x00, 0x00, 0xdf, 0x08, +0x00, 0x01, 0x5b, 0x08, 0x00, 0x02, 0x58, 0xb7, +0x00, 0x03, 0x89, 0x6b, 0x00, 0x02, 0x58, 0xb8, +0x00, 0x03, 0x89, 0x5e, 0x00, 0x02, 0xea, 0x00, +0x00, 0x02, 0x74, 0x0a, 0x00, 0x03, 0x89, 0x63, +0x00, 0x02, 0x58, 0xb3, 0x00, 0x03, 0x89, 0x5f, +0x00, 0x02, 0x58, 0xb8, 0x00, 0x03, 0x89, 0x5f, +0x00, 0x02, 0x58, 0x00, 0x00, 0x03, 0x8b, 0x63, +0x00, 0x02, 0xec, 0x01, 0x00, 0x00, 0x3a, 0x69, +0x00, 0x03, 0xa9, 0x5f, 0x00, 0x02, 0x50, 0x01, +0x00, 0x03, 0x87, 0x63, 0x00, 0x03, 0x56, 0x0a, +0x00, 0x02, 0x78, 0x09, 0x00, 0x03, 0x83, 0x63, +0x00, 0x03, 0xbf, 0x49, 0x00, 0x00, 0xdf, 0x88, +0x00, 0x03, 0x4d, 0x17, 0x00, 0x02, 0xee, 0x00, +0x00, 0x00, 0xbf, 0x8d, 0x00, 0x01, 0xd2, 0x01, +0x00, 0x03, 0x8f, 0x4b, 0x00, 0x02, 0xef, 0x00, +0x00, 0x01, 0xd2, 0x02, 0x00, 0x00, 0x5f, 0x38, +0x00, 0x02, 0x58, 0x18, 0x00, 0x03, 0x8b, 0x59, +0x00, 0x03, 0x44, 0x81, 0x00, 0x03, 0x56, 0x0a, +0x00, 0x01, 0xba, 0x69, 0x00, 0x00, 0x38, 0x61, +0x00, 0x03, 0x56, 0x01, 0x00, 0x01, 0xbb, 0x0b, +0x00, 0x02, 0x78, 0x0a, 0x00, 0x03, 0x81, 0x58, +0x00, 0x02, 0xc2, 0x00, 0x00, 0x01, 0xba, 0x69, +0x00, 0x03, 0x8f, 0x68, 0x00, 0x02, 0xec, 0x00, +0x00, 0x03, 0x5d, 0x16, 0x00, 0x02, 0xed, 0x00, +0x00, 0x03, 0x5d, 0x16, 0x00, 0x03, 0x8f, 0x68, +0x00, 0x03, 0xaf, 0x75, 0x00, 0x03, 0x44, 0x80, +0x00, 0x02, 0xec, 0x00, 0x00, 0x03, 0x5d, 0x16, +0x00, 0x03, 0x47, 0x0e, 0x00, 0x03, 0xbf, 0x68, +0x00, 0x03, 0xa9, 0x68, 0x00, 0x03, 0xab, 0x68, +0x00, 0x02, 0xec, 0x01, 0x00, 0x03, 0x5d, 0x16, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x5f, 0x0c, +0x00, 0x03, 0x50, 0x10, 0x00, 0x02, 0xec, 0xcd, +0x00, 0x02, 0x78, 0x0c, 0x00, 0x03, 0x8b, 0x76, +0x00, 0x02, 0xd8, 0xcf, 0x00, 0x03, 0x8f, 0x76, +0x00, 0x03, 0x4f, 0x81, 0x00, 0x03, 0x8f, 0x1e, +0x00, 0x02, 0xe2, 0x01, 0x00, 0x02, 0xd8, 0xd4, +0x00, 0x03, 0x8f, 0x28, 0x00, 0x02, 0xd9, 0x00, +0x00, 0x03, 0x4c, 0x81, 0x00, 0x02, 0xce, 0x00, +0x00, 0x02, 0xe6, 0x00, 0x00, 0x03, 0x5d, 0x13, +0x00, 0x02, 0xe7, 0xb7, 0x00, 0x03, 0x5d, 0x13, +0x00, 0x02, 0xec, 0x0a, 0x00, 0x02, 0xe6, 0x00, +0x00, 0x03, 0x5d, 0x13, 0x00, 0x01, 0xdb, 0x01, +0x00, 0x03, 0x8b, 0x7e, 0x00, 0x03, 0x5f, 0x0c, +0x00, 0x03, 0x50, 0x10, 0x00, 0x01, 0xe0, 0x06, +0x00, 0x01, 0x40, 0x01, 0x00, 0x03, 0x58, 0x10, +0x00, 0x03, 0x59, 0x33, 0x00, 0x03, 0x5b, 0x1f, +0x00, 0x03, 0x8f, 0xda, 0x00, 0x00, 0x24, 0x0d, +0x00, 0x03, 0x5f, 0x0c, 0x00, 0x01, 0x40, 0x88, +0x00, 0x02, 0xe0, 0x00, 0x00, 0x02, 0xee, 0x00, +0x00, 0x02, 0x5c, 0x01, 0x00, 0x03, 0x89, 0x93, +0x00, 0x03, 0x81, 0x8c, 0x00, 0x00, 0x00, 0x01, +0x00, 0x03, 0x8f, 0x8d, 0x00, 0x02, 0x40, 0x02, +0x00, 0x03, 0x85, 0x8c, 0x00, 0x02, 0xee, 0x00, +0x00, 0x03, 0x5f, 0x1b, 0x00, 0x02, 0x5c, 0xba, +0x00, 0x02, 0x50, 0x0d, 0x00, 0x03, 0x84, 0xc0, +0x00, 0x02, 0xea, 0x02, 0x00, 0x02, 0xe2, 0x3c, +0x00, 0x02, 0x50, 0x0d, 0x00, 0x03, 0x83, 0xa0, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x8e, 0xc0, +0x00, 0x00, 0x16, 0x81, 0x00, 0x02, 0xee, 0x00, +0x00, 0x02, 0xc4, 0xff, 0x00, 0x00, 0x64, 0xa2, +0x00, 0x02, 0x7c, 0x02, 0x00, 0x03, 0x8b, 0xb0, +0x00, 0x00, 0x45, 0x07, 0x00, 0x01, 0x44, 0x83, +0x00, 0x00, 0xc9, 0x08, 0x00, 0x03, 0x42, 0x12, +0x00, 0x00, 0xa4, 0x82, 0x00, 0x02, 0x54, 0x0b, +0x00, 0x03, 0x85, 0xa0, 0x00, 0x02, 0xea, 0x00, +0x00, 0x01, 0xd2, 0x0b, 0x00, 0x03, 0x8f, 0x9c, +0x00, 0x03, 0x43, 0x15, 0x00, 0x01, 0xf2, 0x03, +0x00, 0x02, 0xe2, 0x07, 0x00, 0x00, 0xc4, 0x88, +0x00, 0x00, 0x04, 0x81, 0x00, 0x00, 0xc5, 0x08, +0x00, 0x02, 0x54, 0x02, 0x00, 0x03, 0x81, 0xc3, +0x00, 0x03, 0x85, 0xbd, 0x00, 0x03, 0x42, 0x12, +0x00, 0x00, 0xbf, 0x82, 0x00, 0x00, 0x12, 0x02, +0x00, 0x03, 0x8e, 0xc0, 0x00, 0x00, 0xdd, 0x08, +0x00, 0x03, 0x43, 0x12, 0x00, 0x02, 0xee, 0x00, +0x00, 0x00, 0xbf, 0x83, 0x00, 0x00, 0x12, 0x01, +0x00, 0x03, 0x8e, 0xc0, 0x00, 0x03, 0x43, 0x12, +0x00, 0x03, 0x4f, 0x80, 0x00, 0x00, 0x45, 0x07, +0x00, 0x01, 0x44, 0x83, 0x00, 0x00, 0xc9, 0x08, +0x00, 0x03, 0x42, 0x12, 0x00, 0x00, 0xa4, 0x82, +0x00, 0x03, 0xbf, 0xb5, 0x00, 0x00, 0xc5, 0x08, +0x00, 0x01, 0x49, 0x08, 0x00, 0x00, 0xa9, 0x03, +0x00, 0x03, 0x5d, 0x12, 0x00, 0x01, 0xd6, 0x82, +0x00, 0x03, 0x4f, 0x81, 0x00, 0x03, 0x8f, 0xc5, +0x00, 0x02, 0xef, 0x00, 0x00, 0x00, 0x5f, 0x82, +0x00, 0x00, 0xdf, 0x8e, 0x00, 0x03, 0x5a, 0x17, +0x00, 0x02, 0xef, 0x00, 0x00, 0x01, 0x5f, 0x81, +0x00, 0x03, 0x5f, 0x1b, 0x00, 0x00, 0xbf, 0x9a, +0x00, 0x02, 0xe4, 0x01, 0x00, 0x02, 0xe0, 0x01, +0x00, 0x00, 0xc0, 0x0f, 0x00, 0x03, 0x5a, 0x10, +0x00, 0x02, 0xe0, 0x00, 0x00, 0x02, 0xe2, 0x00, +0x00, 0x02, 0xa8, 0x19, 0x00, 0x03, 0x89, 0xe3, +0x00, 0x00, 0x24, 0x98, 0x00, 0x01, 0x40, 0x01, +0x00, 0x02, 0x84, 0x01, 0x00, 0x03, 0x89, 0xe7, +0x00, 0x00, 0xa0, 0x1a, 0x00, 0x01, 0x44, 0x81, +0x00, 0x00, 0xc9, 0x01, 0x00, 0x03, 0x8b, 0xe0, +0x00, 0x03, 0x5f, 0x1b, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x3c, 0xc0, 0x00, 0x03, 0x70, 0x10, +0x00, 0x03, 0x50, 0x1e, 0x00, 0x02, 0x40, 0xe0, +0x00, 0x03, 0x8b, 0xf6, 0x00, 0x03, 0x47, 0x81, +0x00, 0x01, 0x40, 0x04, 0x00, 0x03, 0x88, 0x01, +0x00, 0x03, 0x60, 0x80, 0x00, 0x03, 0x50, 0x30, +0x00, 0x02, 0xea, 0xff, 0x00, 0x00, 0x77, 0x65, +0x00, 0x03, 0x88, 0x51, 0x00, 0x02, 0x60, 0x0d, +0x00, 0x03, 0x88, 0x7d, 0x00, 0x03, 0x8f, 0x16, +0x0a, 0x0d, +}; diff --git a/os/port/NOTICE b/os/port/NOTICE new file mode 100644 index 00000000..3b325e40 --- /dev/null +++ b/os/port/NOTICE @@ -0,0 +1,18 @@ +The following files are subject to the Lucent Public License 1.02: + +devbridge.c +devds.c +devdup.c +devloopback.c +devpci.c (portions) +devpnp.c +devsd.c +devuart.c +edf.c +edf.h +ethermii.c +ethermii.h +log.c +mul64fract.c +rdb.c +sd.h diff --git a/os/port/alarm.c b/os/port/alarm.c new file mode 100644 index 00000000..4f7662c3 --- /dev/null +++ b/os/port/alarm.c @@ -0,0 +1,41 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +Talarm talarm; + +/* + * called every clock tick + */ +void +checkalarms(void) +{ + Proc *p; + ulong now; + + now = MACHP(0)->ticks; + + if(talarm.list == 0 || canlock(&talarm) == 0) + return; + + for(;;) { + p = talarm.list; + if(p == 0) + break; + + if(p->twhen == 0) { + talarm.list = p->tlink; + p->trend = 0; + continue; + } + if(now < p->twhen) + break; + wakeup(p->trend); + talarm.list = p->tlink; + p->trend = 0; + } + + unlock(&talarm); +} diff --git a/os/port/alloc.c b/os/port/alloc.c new file mode 100644 index 00000000..810560a4 --- /dev/null +++ b/os/port/alloc.c @@ -0,0 +1,963 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "interp.h" + +#define left u.s.bhl +#define right u.s.bhr +#define fwd u.s.bhf +#define prev u.s.bhv +#define parent u.s.bhp + +#define RESERVED 256*1024 + +struct Pool +{ + char* name; + int pnum; + ulong maxsize; + int quanta; + int chunk; + ulong ressize; + ulong cursize; + ulong arenasize; + ulong hw; + Lock l; + Bhdr* root; + Bhdr* chain; + ulong nalloc; + ulong nfree; + int nbrk; + int lastfree; + int warn; + void (*move)(void*, void*); +}; + +static +struct +{ + int n; + Pool pool[MAXPOOL]; + // Lock l; +} table = { + 3, + { + { "main", 0, 4*1024*1024, 31, 128*1024, 15*256*1024 }, + { "heap", 1, 16*1024*1024, 31, 128*1024, 15*1024*1024 }, + { "image", 2, 8*1024*1024, 31, 300*1024, 15*512*1024 }, + } +}; + +Pool* mainmem = &table.pool[0]; +Pool* heapmem = &table.pool[1]; +Pool* imagmem = &table.pool[2]; + +static void _auditmemloc(char *, void *); +void (*auditmemloc)(char *, void *) = _auditmemloc; +static void _poolfault(void *, char *, ulong); +void (*poolfault)(void *, char *, ulong) = _poolfault; + +/* non tracing + * +enum { + Npadlong = 0, + MallocOffset = 0, + ReallocOffset = 0 +}; + * + */ + +/* tracing */ +enum { + Npadlong = 2, + MallocOffset = 0, + ReallocOffset = 1 +}; + +int +memusehigh(void) +{ + return mainmem->cursize > mainmem->ressize || + heapmem->cursize > heapmem->ressize || + imagmem->cursize > imagmem->ressize; +} + +void +poolimmutable(void *v) +{ + Bhdr *b; + + D2B(b, v); + b->magic = MAGIC_I; +} + +void +poolmutable(void *v) +{ + Bhdr *b; + + D2B(b, v); + b->magic = MAGIC_A; + ((Heap*)v)->color = mutator; +} + +char* +poolname(Pool *p) +{ + return p->name; +} + +Bhdr* +poolchain(Pool *p) +{ + return p->chain; +} + +void +pooldel(Pool *p, Bhdr *t) +{ + Bhdr *s, *f, *rp, *q; + + if(t->parent == nil && p->root != t) { + t->prev->fwd = t->fwd; + t->fwd->prev = t->prev; + return; + } + + if(t->fwd != t) { + f = t->fwd; + s = t->parent; + f->parent = s; + if(s == nil) + p->root = f; + else { + if(s->left == t) + s->left = f; + else + s->right = f; + } + + rp = t->left; + f->left = rp; + if(rp != nil) + rp->parent = f; + rp = t->right; + f->right = rp; + if(rp != nil) + rp->parent = f; + + t->prev->fwd = t->fwd; + t->fwd->prev = t->prev; + return; + } + + if(t->left == nil) + rp = t->right; + else { + if(t->right == nil) + rp = t->left; + else { + f = t; + rp = t->right; + s = rp->left; + while(s != nil) { + f = rp; + rp = s; + s = rp->left; + } + if(f != t) { + s = rp->right; + f->left = s; + if(s != nil) + s->parent = f; + s = t->right; + rp->right = s; + if(s != nil) + s->parent = rp; + } + s = t->left; + rp->left = s; + s->parent = rp; + } + } + q = t->parent; + if(q == nil) + p->root = rp; + else { + if(t == q->left) + q->left = rp; + else + q->right = rp; + } + if(rp != nil) + rp->parent = q; +} + +void +pooladd(Pool *p, Bhdr *q) +{ + int size; + Bhdr *tp, *t; + + q->magic = MAGIC_F; + + q->left = nil; + q->right = nil; + q->parent = nil; + q->fwd = q; + q->prev = q; + + t = p->root; + if(t == nil) { + p->root = q; + return; + } + + size = q->size; + + tp = nil; + while(t != nil) { + if(size == t->size) { + q->prev = t->prev; + q->prev->fwd = q; + q->fwd = t; + t->prev = q; + return; + } + tp = t; + if(size < t->size) + t = t->left; + else + t = t->right; + } + + q->parent = tp; + if(size < tp->size) + tp->left = q; + else + tp->right = q; +} + +void +poolsummary(void) +{ + int x = 0; + char buf[400]; + + print("\n"); + print(" cursize maxsize hw nalloc nfree nbrk max name\n"); + + x=poolread( buf, sizeof buf - 1, x ); + buf[x] = 0; + putstrn(buf, x); + print("\n"); +} + +void* +poolalloc(Pool *p, ulong asize) +{ + Bhdr *q, *t; + int alloc, ldr, ns, frag; + int osize, size; + Prog *prog; + + if(asize >= 1024*1024*1024) /* for sanity and to avoid overflow */ + return nil; + if(p->cursize > p->ressize && (prog = currun()) != nil && prog->flags&Prestricted) + return nil; + size = asize; + osize = size; + size = (size + BHDRSIZE + p->quanta) & ~(p->quanta); + + ilock(&p->l); + p->nalloc++; + + t = p->root; + q = nil; + while(t) { + if(t->size == size) { + t = t->fwd; + pooldel(p, t); + t->magic = MAGIC_A; + p->cursize += t->size; + if(p->cursize > p->hw) + p->hw = p->cursize; + iunlock(&p->l); + return B2D(t); + } + if(size < t->size) { + q = t; + t = t->left; + } + else + t = t->right; + } + if(q != nil) { + pooldel(p, q); + q->magic = MAGIC_A; + frag = q->size - size; + if(frag < (size>>2) && frag < 0x8000) { + p->cursize += q->size; + if(p->cursize > p->hw) + p->hw = p->cursize; + iunlock(&p->l); + return B2D(q); + } + /* Split */ + ns = q->size - size; + q->size = size; + B2T(q)->hdr = q; + t = B2NB(q); + t->size = ns; + B2T(t)->hdr = t; + pooladd(p, t); + p->cursize += q->size; + if(p->cursize > p->hw) + p->hw = p->cursize; + iunlock(&p->l); + return B2D(q); + } + + ns = p->chunk; + if(size > ns) + ns = size; + ldr = p->quanta+1; + + alloc = ns+ldr+ldr; + p->arenasize += alloc; + if(p->arenasize > p->maxsize) { + p->arenasize -= alloc; + ns = p->maxsize-p->arenasize-ldr-ldr; + ns &= ~p->quanta; + if (ns < size) { + if(poolcompact(p)) { + iunlock(&p->l); + return poolalloc(p, osize); + } + + iunlock(&p->l); + if(p->warn) + return nil; + p->warn = 1; + if (p != mainmem || ns > 512) + print("arena too large: %s size %d cursize %lud arenasize %lud maxsize %lud, alloc = %d\n", p->name, osize, p->cursize, p->arenasize, p->maxsize, alloc); + return nil; + } + alloc = ns+ldr+ldr; + p->arenasize += alloc; + } + + p->nbrk++; + t = xalloc(alloc); + if(t == nil) { + p->nbrk--; + iunlock(&p->l); + return nil; + } + /* Double alignment */ + t = (Bhdr *)(((ulong)t + 7) & ~7); + + /* TBS xmerge */ + if(0 && p->chain != nil && (char*)t-(char*)B2LIMIT(p->chain)-ldr == 0){ + /* can merge chains */ + if(0)print("merging chains %p and %p in %s\n", p->chain, t, p->name); + q = B2LIMIT(p->chain); + q->magic = MAGIC_A; + q->size = alloc; + B2T(q)->hdr = q; + t = B2NB(q); + t->magic = MAGIC_E; + p->chain->csize += alloc; + p->cursize += alloc; + iunlock(&p->l); + poolfree(p, B2D(q)); /* for backward merge */ + return poolalloc(p, osize); + } + + t->magic = MAGIC_E; /* Make a leader */ + t->size = ldr; + t->csize = ns+ldr; + t->clink = p->chain; + p->chain = t; + B2T(t)->hdr = t; + t = B2NB(t); + + t->magic = MAGIC_A; /* Make the block we are going to return */ + t->size = size; + B2T(t)->hdr = t; + q = t; + + ns -= size; /* Free the rest */ + if(ns > 0) { + q = B2NB(t); + q->size = ns; + B2T(q)->hdr = q; + pooladd(p, q); + } + B2NB(q)->magic = MAGIC_E; /* Mark the end of the chunk */ + + p->cursize += t->size; + if(p->cursize > p->hw) + p->hw = p->cursize; + iunlock(&p->l); + return B2D(t); +} + +void +poolfree(Pool *p, void *v) +{ + Bhdr *b, *c; + extern Bhdr *ptr; + + D2B(b, v); + + ilock(&p->l); + p->nfree++; + p->cursize -= b->size; + + c = B2NB(b); + if(c->magic == MAGIC_F) { /* Join forward */ + if(c == ptr) + ptr = b; + pooldel(p, c); + c->magic = 0; + b->size += c->size; + B2T(b)->hdr = b; + } + + c = B2PT(b)->hdr; + if(c->magic == MAGIC_F) { /* Join backward */ + if(b == ptr) + ptr = c; + pooldel(p, c); + b->magic = 0; + c->size += b->size; + b = c; + B2T(b)->hdr = b; + } + + pooladd(p, b); + iunlock(&p->l); +} + +void * +poolrealloc(Pool *p, void *v, ulong size) +{ + Bhdr *b; + void *nv; + int osize; + + if(size >= 1024*1024*1024) /* for sanity and to avoid overflow */ + return nil; + if(size == 0){ + poolfree(p, v); + return nil; + } + SET(osize); + if(v != nil){ + ilock(&p->l); + D2B(b, v); + osize = b->size - BHDRSIZE; + iunlock(&p->l); + if(osize >= size) + return v; + } + nv = poolalloc(p, size); + if(nv != nil && v != nil){ + memmove(nv, v, osize); + poolfree(p, v); + } + return nv; +} + +ulong +poolmsize(Pool *p, void *v) +{ + Bhdr *b; + ulong size; + + if(v == nil) + return 0; + ilock(&p->l); + D2B(b, v); + size = b->size - BHDRSIZE; + iunlock(&p->l); + return size; +} + +static ulong +poolmax(Pool *p) +{ + Bhdr *t; + ulong size; + + ilock(&p->l); + size = p->maxsize - p->cursize; + t = p->root; + if(t != nil) { + while(t->right != nil) + t = t->right; + if(size < t->size) + size = t->size; + } + if(size >= BHDRSIZE) + size -= BHDRSIZE; + iunlock(&p->l); + return size; +} + +int +poolread(char *va, int count, ulong offset) +{ + Pool *p; + int n, i, signed_off; + + n = 0; + signed_off = offset; + for(i = 0; i < table.n; i++) { + p = &table.pool[i]; + n += snprint(va+n, count-n, "%11lud %11lud %11lud %11lud %11lud %11d %11lud %s\n", + p->cursize, + p->maxsize, + p->hw, + p->nalloc, + p->nfree, + p->nbrk, + poolmax(p), + p->name); + + if(signed_off > 0) { + signed_off -= n; + if(signed_off < 0) { + memmove(va, va+n+signed_off, -signed_off); + n = -signed_off; + } + else + n = 0; + } + + } + return n; +} + +void* +malloc(ulong size) +{ + void *v; + + v = poolalloc(mainmem, size+Npadlong*sizeof(ulong)); + if(v != nil){ + if(Npadlong){ + v = (ulong*)v+Npadlong; + setmalloctag(v, getcallerpc(&size)); + setrealloctag(v, 0); + } + memset(v, 0, size); + } + return v; +} + +void* +smalloc(ulong size) +{ + void *v; + + for(;;) { + v = poolalloc(mainmem, size+Npadlong*sizeof(ulong)); + if(v != nil) + break; + tsleep(&up->sleep, return0, 0, 100); + } + if(Npadlong){ + v = (ulong*)v+Npadlong; + setmalloctag(v, getcallerpc(&size)); + setrealloctag(v, 0); + } + memset(v, 0, size); + return v; +} + +void* +mallocz(ulong size, int clr) +{ + void *v; + + v = poolalloc(mainmem, size+Npadlong*sizeof(ulong)); + if(v != nil){ + if(Npadlong){ + v = (ulong*)v+Npadlong; + setmalloctag(v, getcallerpc(&size)); + setrealloctag(v, 0); + } + if(clr) + memset(v, 0, size); + } + return v; +} + +void +free(void *v) +{ + Bhdr *b; + + if(v != nil) { + if(Npadlong) + v = (ulong*)v-Npadlong; + D2B(b, v); + poolfree(mainmem, v); + } +} + +void* +realloc(void *v, ulong size) +{ + void *nv; + + if(size == 0) + return malloc(size); /* temporary change until realloc calls can be checked */ + if(v != nil) + v = (ulong*)v-Npadlong; + if(Npadlong!=0 && size!=0) + size += Npadlong*sizeof(ulong); + nv = poolrealloc(mainmem, v, size); + if(nv != nil) { + nv = (ulong*)nv+Npadlong; + setrealloctag(nv, getcallerpc(&v)); + if(v == nil) + setmalloctag(v, getcallerpc(&v)); + } + return nv; +} + +void +setmalloctag(void *v, ulong pc) +{ + ulong *u; + + USED(v); + USED(pc); + if(Npadlong <= MallocOffset || v == nil) + return; + u = v; + u[-Npadlong+MallocOffset] = pc; +} + +ulong +getmalloctag(void *v) +{ + USED(v); + if(Npadlong <= MallocOffset) + return ~0; + return ((ulong*)v)[-Npadlong+MallocOffset]; +} + +void +setrealloctag(void *v, ulong pc) +{ + ulong *u; + + USED(v); + USED(pc); + if(Npadlong <= ReallocOffset || v == nil) + return; + u = v; + u[-Npadlong+ReallocOffset] = pc; +} + +ulong +getrealloctag(void *v) +{ + USED(v); + if(Npadlong <= ReallocOffset) + return ((ulong*)v)[-Npadlong+ReallocOffset]; + return ~0; +} + +ulong +msize(void *v) +{ + if(v == nil) + return 0; + return poolmsize(mainmem, (ulong*)v-Npadlong)-Npadlong*sizeof(ulong); +} + +void* +calloc(ulong n, ulong szelem) +{ + return malloc(n*szelem); +} + +void +pooldump(Bhdr *b, int d, int c) +{ + Bhdr *t; + + if(b == nil) + return; + + print("%.8lux %.8lux %.8lux %c %4d %lud (f %.8lux p %.8lux)\n", + b, b->left, b->right, c, d, b->size, b->fwd, b->prev); + d++; + for(t = b->fwd; t != b; t = t->fwd) + print("\t%.8lux %.8lux %.8lux\n", t, t->prev, t->fwd); + pooldump(b->left, d, 'l'); + pooldump(b->right, d, 'r'); +} + +void +poolshow(void) +{ + int i; + + for(i = 0; i < table.n; i++) { + print("Arena: %s root=%.8lux\n", table.pool[i].name, table.pool[i].root); + pooldump(table.pool[i].root, 0, 'R'); + } +} + +void +poolsetcompact(Pool *p, void (*move)(void*, void*)) +{ + p->move = move; +} + +int +poolcompact(Pool *pool) +{ + Bhdr *base, *limit, *ptr, *end, *next; + int compacted, nb; + + if(pool->move == nil || pool->lastfree == pool->nfree) + return 0; + + pool->lastfree = pool->nfree; + + base = pool->chain; + ptr = B2NB(base); /* First Block in arena has clink */ + limit = B2LIMIT(base); + compacted = 0; + + pool->root = nil; + end = ptr; + while(base != nil) { + next = B2NB(ptr); + if(ptr->magic == MAGIC_A || ptr->magic == MAGIC_I) { + if(ptr != end) { + memmove(end, ptr, ptr->size); + pool->move(B2D(ptr), B2D(end)); + compacted = 1; + } + end = B2NB(end); + } + if(next >= limit) { + nb = (uchar*)limit - (uchar*)end; + if(nb > 0){ + if(nb < pool->quanta+1) + panic("poolcompact: leftover too small\n"); + end->size = nb; + B2T(end)->hdr = end; + pooladd(pool, end); + } + base = base->clink; + if(base == nil) + break; + ptr = B2NB(base); + end = ptr; /* could do better by copying between chains */ + limit = B2LIMIT(base); + } else + ptr = next; + } + + return compacted; +} + +void +poolsize(Pool *p, int max, int contig) +{ + void *x; + + p->maxsize = max; + if(max == 0) + p->ressize = max; + else if(max < RESERVED) + p->ressize = max; + else + p->ressize = max-RESERVED; + if (contig && max > 0) { + p->chunk = max-1024; + x = poolalloc(p, p->chunk); + if(x == nil) + panic("poolsize: don't have %d bytes\n", p->chunk); + poolfree(p, x); + p->hw = 0; + } +} + +static void +_poolfault(void *v, char *msg, ulong c) +{ + setpanic(); + auditmemloc(msg, v); + panic("%s %lux (from %lux/%lux)", msg, v, getcallerpc(&v), c); +} + +static void +dumpvl(char *msg, ulong *v, int n) +{ + int i, l; + + l = print("%s at %p: ", msg, v); + for(i = 0; i < n; i++) { + if(l >= 60) { + print("\n"); + l = print(" %p: ", v); + } + l += print(" %lux", *v++); + } + print("\n"); + USED(l); +} + +static void +corrupted(char *str, char *msg, Pool *p, Bhdr *b, void *v) +{ + print("%s(%p): pool %s CORRUPT: %s at %p'%lud(magic=%lux)\n", + str, v, p->name, msg, b, b->size, b->magic); + dumpvl("bad Bhdr", (ulong *)((ulong)b & ~3)-4, 10); +} + +static void +_auditmemloc(char *str, void *v) +{ + Pool *p; + Bhdr *bc, *ec, *b, *nb, *fb = nil; + char *fmsg, *msg; + ulong fsz; + + SET(fsz, fmsg); + for (p = &table.pool[0]; p < &table.pool[nelem(table.pool)]; p++) { + ilock(&p->l); + for (bc = p->chain; bc != nil; bc = bc->clink) { + if (bc->magic != MAGIC_E) { + iunlock(&p->l); + corrupted(str, "chain hdr!=MAGIC_E", p, bc, v); + goto nextpool; + } + ec = B2LIMIT(bc); + if (((Bhdr*)v >= bc) && ((Bhdr*)v < ec)) + goto found; + } + iunlock(&p->l); +nextpool: ; + } + print("%s: %lux not in pools\n", str, v); + return; + +found: + for (b = bc; b < ec; b = nb) { + switch(b->magic) { + case MAGIC_F: + msg = "free blk"; + break; + case MAGIC_I: + msg = "immutable block"; + break; + case MAGIC_A: + msg = "block"; + break; + default: + if (b == bc && b->magic == MAGIC_E) { + msg = "pool hdr"; + break; + } + iunlock(&p->l); + corrupted(str, "bad magic", p, b, v); + goto badchunk; + } + if (b->size <= 0 || (b->size & p->quanta)) { + iunlock(&p->l); + corrupted(str, "bad size", p, b, v); + goto badchunk; + } + if (fb != nil) + break; + nb = B2NB(b); + if ((Bhdr*)v < nb) { + fb = b; + fsz = b->size; + fmsg = msg; + } + } + iunlock(&p->l); + if (b >= ec) { + if (b > ec) + corrupted(str, "chain size mismatch", p, b, v); + else if (b->magic != MAGIC_E) + corrupted(str, "chain end!=MAGIC_E", p, b, v); + } +badchunk: + if (fb != nil) { + print("%s: %lux in %s:", str, v, p->name); + if (fb == v) + print(" is %s '%lux\n", fmsg, fsz); + else + print(" in %s at %lux'%lux\n", fmsg, fb, fsz); + dumpvl("area", (ulong *)((ulong)v & ~3)-4, 20); + } +} + +char * +poolaudit(char*(*audit)(int, Bhdr *)) +{ + Pool *p; + Bhdr *bc, *ec, *b; + char *r = nil; + + for (p = &table.pool[0]; p < &table.pool[nelem(table.pool)]; p++) { + ilock(&p->l); + for (bc = p->chain; bc != nil; bc = bc->clink) { + if (bc->magic != MAGIC_E) { + iunlock(&p->l); + return "bad chain hdr"; + } + ec = B2LIMIT(bc); + for (b = bc; b < ec; b = B2NB(b)) { + if (b->size <= 0 || (b->size & p->quanta)) + r = "bad size in bhdr"; + else + switch(b->magic) { + case MAGIC_E: + if (b != bc) { + r = "unexpected MAGIC_E"; + break; + } + case MAGIC_F: + case MAGIC_A: + case MAGIC_I: + r = audit(p->pnum, b); + break; + default: + r = "bad magic"; + } + if (r != nil) { + iunlock(&p->l); + return r; + } + } + if (b != ec || b->magic != MAGIC_E) { + iunlock(&p->l); + return "bad chain ending"; + } + } + iunlock(&p->l); + } + return r; +} + +void +poolinit(void) +{ + debugkey('m', "memory pools", poolsummary, 0); +} diff --git a/os/port/allocb.c b/os/port/allocb.c new file mode 100644 index 00000000..6ab67d60 --- /dev/null +++ b/os/port/allocb.c @@ -0,0 +1,159 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +enum +{ + Hdrspc = 64, /* leave room for high-level headers */ + Bdead = 0x51494F42, /* "QIOB" */ +}; + +struct +{ + Lock; + ulong bytes; +} ialloc; + +/* + * allocate blocks (round data base address to 64 bit boundary). + * if mallocz gives us more than we asked for, leave room at the front + * for header. + */ +Block* +_allocb(int size) +{ + Block *b; + ulong addr; + int n; + + b = mallocz(sizeof(Block)+size+Hdrspc+(BY2V-1), 0); + if(b == nil) + return nil; + + b->next = nil; + b->list = nil; + b->free = nil; + b->flag = 0; + + addr = (ulong)b; + addr = ROUND(addr + sizeof(Block), BY2V); + b->base = (uchar*)addr; + b->lim = ((uchar*)b) + msize(b); + b->rp = b->base; + n = b->lim - b->base - size; + b->rp += n & ~(BY2V-1); + b->wp = b->rp; + + return b; +} + +Block* +allocb(int size) +{ + Block *b; + + if(0 && up == nil) + panic("allocb outside process: %8.8lux", getcallerpc(&size)); + b = _allocb(size); + if(b == 0) + exhausted("Blocks"); + setmalloctag(b, getcallerpc(&size)); + return b; +} + +/* + * interrupt time allocation + */ +Block* +iallocb(int size) +{ + Block *b; + + if(ialloc.bytes > conf.ialloc){ + //print("iallocb: limited %lud/%lud\n", ialloc.bytes, conf.ialloc); + return nil; + } + + b = _allocb(size); + if(b == nil){ + //print("iallocb: no memory %lud/%lud\n", ialloc.bytes, conf.ialloc); + return nil; + } + setmalloctag(b, getcallerpc(&size)); + b->flag = BINTR; + + ilock(&ialloc); + ialloc.bytes += b->lim - b->base; + iunlock(&ialloc); + + return b; +} + +void +freeb(Block *b) +{ + void *dead = (void*)Bdead; + + if(b == nil) + return; + + /* + * drivers which perform non cache coherent DMA manage their own buffer + * pool of uncached buffers and provide their own free routine. + */ + if(b->free) { + b->free(b); + return; + } + if(b->flag & BINTR) { + ilock(&ialloc); + ialloc.bytes -= b->lim - b->base; + iunlock(&ialloc); + } + + /* poison the block in case someone is still holding onto it */ + b->next = dead; + b->rp = dead; + b->wp = dead; + b->lim = dead; + b->base = dead; + + free(b); +} + +void +checkb(Block *b, char *msg) +{ + void *dead = (void*)Bdead; + + if(b == dead) + panic("checkb b %s %lux", msg, b); + if(b->base == dead || b->lim == dead || b->next == dead + || b->rp == dead || b->wp == dead){ + print("checkb: base 0x%8.8luX lim 0x%8.8luX next 0x%8.8luX\n", + b->base, b->lim, b->next); + print("checkb: rp 0x%8.8luX wp 0x%8.8luX\n", b->rp, b->wp); + panic("checkb dead: %s\n", msg); + } + + if(b->base > b->lim) + panic("checkb 0 %s %lux %lux", msg, b->base, b->lim); + if(b->rp < b->base) + panic("checkb 1 %s %lux %lux", msg, b->base, b->rp); + if(b->wp < b->base) + panic("checkb 2 %s %lux %lux", msg, b->base, b->wp); + if(b->rp > b->lim) + panic("checkb 3 %s %lux %lux", msg, b->rp, b->lim); + if(b->wp > b->lim) + panic("checkb 4 %s %lux %lux", msg, b->wp, b->lim); + +} + +void +iallocsummary(void) +{ + print("ialloc %lud/%lud\n", ialloc.bytes, conf.ialloc); +} diff --git a/os/port/chan.c b/os/port/chan.c new file mode 100644 index 00000000..a5d213aa --- /dev/null +++ b/os/port/chan.c @@ -0,0 +1,1431 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +char* +channame(Chan *c) /* DEBUGGING */ +{ + if(c == nil) + return ""; + if(c->name == nil) + return ""; + if(c->name->s == nil) + return ""; + return c->name->s; +} + +enum +{ + CNAMESLOP = 20 +}; + +struct +{ + Lock; + int fid; + Chan *free; + Chan *list; +}chanalloc; + +typedef struct Elemlist Elemlist; + +struct Elemlist +{ + char *name; /* copy of name, so '/' can be overwritten */ + int nelems; + char **elems; + int *off; + int mustbedir; +}; + +#define SEP(c) ((c) == 0 || (c) == '/') +void cleancname(Cname*); + +int +isdotdot(char *p) +{ + return p[0]=='.' && p[1]=='.' && p[2]=='\0'; +} + +int +incref(Ref *r) +{ + int x; + + lock(&r->l); + x = ++r->ref; + unlock(&r->l); + return x; +} + +int +decref(Ref *r) +{ + int x; + + lock(&r->l); + x = --r->ref; + unlock(&r->l); + if(x < 0) + panic("decref, pc=0x%lux", getcallerpc(&r)); + + return x; +} + +/* + * Rather than strncpy, which zeros the rest of the buffer, kstrcpy + * truncates if necessary, always zero terminates, does not zero fill, + * and puts ... at the end of the string if it's too long. Usually used to + * save a string in up->genbuf; + */ +void +kstrcpy(char *s, char *t, int ns) +{ + int nt; + + nt = strlen(t); + if(nt+1 <= ns){ + memmove(s, t, nt+1); + return; + } + /* too long */ + if(ns < 4){ + /* but very short! */ + strncpy(s, t, ns); + return; + } + /* truncate with ... at character boundary (very rare case) */ + memmove(s, t, ns-4); + ns -= 4; + s[ns] = '\0'; + /* look for first byte of UTF-8 sequence by skipping continuation bytes */ + while(ns>0 && (s[--ns]&0xC0)==0x80) + ; + strcpy(s+ns, "..."); +} + +int +emptystr(char *s) +{ + if(s == nil) + return 1; + if(s[0] == '\0') + return 1; + return 0; +} + +/* + * Atomically replace *p with copy of s + */ +void +kstrdup(char **p, char *s) +{ + int n; + char *t, *prev; + + n = strlen(s)+1; + /* if it's a user, we can wait for memory; if not, something's very wrong */ + if(up){ + t = smalloc(n); + setmalloctag(t, getcallerpc(&p)); + }else{ + t = malloc(n); + if(t == nil) + panic("kstrdup: no memory"); + } + memmove(t, s, n); + prev = *p; + *p = t; + free(prev); +} + +void +chandevreset(void) +{ + int i; + + for(i=0; devtab[i] != nil; i++) + devtab[i]->reset(); +} + +void +chandevinit(void) +{ + int i; + + for(i=0; devtab[i] != nil; i++) + devtab[i]->init(); +} + +void +chandevshutdown(void) +{ + int i; + + /* shutdown in reverse order */ + for(i=0; devtab[i] != nil; i++) + ; + for(i--; i >= 0; i--) + devtab[i]->shutdown(); +} + +Chan* +newchan(void) +{ + Chan *c; + + lock(&chanalloc); + c = chanalloc.free; + if(c != 0) + chanalloc.free = c->next; + unlock(&chanalloc); + + if(c == nil) { + c = smalloc(sizeof(Chan)); + lock(&chanalloc); + c->fid = ++chanalloc.fid; + c->link = chanalloc.list; + chanalloc.list = c; + unlock(&chanalloc); + } + + /* if you get an error before associating with a dev, + close calls rootclose, a nop */ + c->type = 0; + c->flag = 0; + c->ref = 1; + c->dev = 0; + c->offset = 0; + c->iounit = 0; + c->umh = 0; + c->uri = 0; + c->dri = 0; + c->aux = 0; + c->mchan = 0; + c->mcp = 0; + c->mux = 0; + c->mqid.path = 0; + c->mqid.vers = 0; + c->mqid.type = 0; + c->name = 0; + return c; +} + +static Ref ncname; + +Cname* +newcname(char *s) +{ + Cname *n; + int i; + + n = smalloc(sizeof(Cname)); + i = strlen(s); + n->len = i; + n->alen = i+CNAMESLOP; + n->s = smalloc(n->alen); + memmove(n->s, s, i+1); + n->ref = 1; + incref(&ncname); + return n; +} + +void +cnameclose(Cname *n) +{ + if(n == nil) + return; + if(decref(n)) + return; + decref(&ncname); + free(n->s); + free(n); +} + +Cname* +addelem(Cname *n, char *s) +{ + int i, a; + char *t; + Cname *new; + + if(s[0]=='.' && s[1]=='\0') + return n; + + if(n->ref > 1){ + /* copy on write */ + new = newcname(n->s); + cnameclose(n); + n = new; + } + + i = strlen(s); + if(n->len+1+i+1 > n->alen){ + a = n->len+1+i+1 + CNAMESLOP; + t = smalloc(a); + memmove(t, n->s, n->len+1); + free(n->s); + n->s = t; + n->alen = a; + } + if(n->len>0 && n->s[n->len-1]!='/' && s[0]!='/') /* don't insert extra slash if one is present */ + n->s[n->len++] = '/'; + memmove(n->s+n->len, s, i+1); + n->len += i; + if(isdotdot(s)) + cleancname(n); + return n; +} + +void +chanfree(Chan *c) +{ + c->flag = CFREE; + + if(c->umh != nil){ + putmhead(c->umh); + c->umh = nil; + } + if(c->umc != nil){ + cclose(c->umc); + c->umc = nil; + } + if(c->mux != nil){ + muxclose(c->mux); + c->mux = nil; + } + if(c->mchan != nil){ + cclose(c->mchan); + c->mchan = nil; + } + + cnameclose(c->name); + + lock(&chanalloc); + c->next = chanalloc.free; + chanalloc.free = c; + unlock(&chanalloc); +} + +void +cclose(Chan *c) +{ + if(c == 0) + return; + + if(c->flag&CFREE) + panic("cclose %lux", getcallerpc(&c)); + + if(decref(c)) + return; + + if(!waserror()){ + devtab[c->type]->close(c); + poperror(); + } + chanfree(c); +} + +/* + * Make sure we have the only copy of c. (Copy on write.) + */ +Chan* +cunique(Chan *c) +{ + Chan *nc; + + if(c->ref != 1) { + nc = cclone(c); + cclose(c); + c = nc; + } + + return c; +} + +int +eqqid(Qid a, Qid b) +{ + return a.path==b.path && a.vers==b.vers; +} + +int +eqchan(Chan *a, Chan *b, int pathonly) +{ + if(a->qid.path != b->qid.path) + return 0; + if(!pathonly && a->qid.vers!=b->qid.vers) + return 0; + if(a->type != b->type) + return 0; + if(a->dev != b->dev) + return 0; + return 1; +} + +int +eqchantdqid(Chan *a, int type, int dev, Qid qid, int pathonly) +{ + if(a->qid.path != qid.path) + return 0; + if(!pathonly && a->qid.vers!=qid.vers) + return 0; + if(a->type != type) + return 0; + if(a->dev != dev) + return 0; + return 1; +} + +Mhead* +newmhead(Chan *from) +{ + Mhead *mh; + + mh = smalloc(sizeof(Mhead)); + mh->ref = 1; + mh->from = from; + incref(from); + +/* + n = from->name->len; + if(n >= sizeof(mh->fromname)) + n = sizeof(mh->fromname)-1; + memmove(mh->fromname, from->name->s, n); + mh->fromname[n] = 0; +*/ + return mh; +} + +int +cmount(Chan *new, Chan *old, int flag, char *spec) +{ + Pgrp *pg; + int order, flg; + Mhead *m, **l, *mh; + Mount *nm, *f, *um, **h; + + if(QTDIR & (old->qid.type^new->qid.type)) + error(Emount); + +if(old->umh) + print("cmount old extra umh\n"); + + order = flag&MORDER; + + if((old->qid.type&QTDIR)==0 && order != MREPL) + error(Emount); + + mh = new->umh; + + /* + * Not allowed to bind when the old directory + * is itself a union. (Maybe it should be allowed, but I don't see + * what the semantics would be.) + * + * We need to check mh->mount->next to tell unions apart from + * simple mount points, so that things like + * mount -c fd /root + * bind -c /root / + * work. The check of mount->mflag catches things like + * mount fd /root + * bind -c /root / + * + * This is far more complicated than it should be, but I don't + * see an easier way at the moment. -rsc + */ + if((flag&MCREATE) && mh && mh->mount + && (mh->mount->next || !(mh->mount->mflag&MCREATE))) + error(Emount); + + pg = up->env->pgrp; + wlock(&pg->ns); + + l = &MOUNTH(pg, old->qid); + for(m = *l; m; m = m->hash) { + if(eqchan(m->from, old, 1)) + break; + l = &m->hash; + } + + if(m == nil) { + /* + * nothing mounted here yet. create a mount + * head and add to the hash table. + */ + m = newmhead(old); + *l = m; + + /* + * if this is a union mount, add the old + * node to the mount chain. + */ + if(order != MREPL) + m->mount = newmount(m, old, 0, 0); + } + wlock(&m->lock); + if(waserror()){ + wunlock(&m->lock); + nexterror(); + } + wunlock(&pg->ns); + + nm = newmount(m, new, flag, spec); + if(mh != nil && mh->mount != nil) { + /* + * copy a union when binding it onto a directory + */ + flg = order; + if(order == MREPL) + flg = MAFTER; + h = &nm->next; + um = mh->mount; + for(um = um->next; um; um = um->next) { + f = newmount(m, um->to, flg, um->spec); + *h = f; + h = &f->next; + } + } + + if(m->mount && order == MREPL) { + mountfree(m->mount); + m->mount = 0; + } + + if(flag & MCREATE) + nm->mflag |= MCREATE; + + if(m->mount && order == MAFTER) { + for(f = m->mount; f->next; f = f->next) + ; + f->next = nm; + } + else { + for(f = nm; f->next; f = f->next) + ; + f->next = m->mount; + m->mount = nm; + } + + wunlock(&m->lock); + poperror(); + return nm->mountid; +} + +void +cunmount(Chan *mnt, Chan *mounted) +{ + Pgrp *pg; + Mhead *m, **l; + Mount *f, **p; + + if(mnt->umh) /* should not happen */ + print("cunmount newp extra umh %p has %p\n", mnt, mnt->umh); + + /* + * It _can_ happen that mounted->umh is non-nil, + * because mounted is the result of namec(Aopen) + * (see sysfile.c:/^sysunmount). + * If we open a union directory, it will have a umh. + * Although surprising, this is okay, since the + * cclose will take care of freeing the umh. + */ + + pg = up->env->pgrp; + wlock(&pg->ns); + + l = &MOUNTH(pg, mnt->qid); + for(m = *l; m; m = m->hash) { + if(eqchan(m->from, mnt, 1)) + break; + l = &m->hash; + } + + if(m == 0) { + wunlock(&pg->ns); + error(Eunmount); + } + + wlock(&m->lock); + if(mounted == 0) { + *l = m->hash; + wunlock(&pg->ns); + mountfree(m->mount); + m->mount = nil; + cclose(m->from); + wunlock(&m->lock); + putmhead(m); + return; + } + + p = &m->mount; + for(f = *p; f; f = f->next) { + /* BUG: Needs to be 2 pass */ + if(eqchan(f->to, mounted, 1) || + (f->to->mchan && eqchan(f->to->mchan, mounted, 1))) { + *p = f->next; + f->next = 0; + mountfree(f); + if(m->mount == nil) { + *l = m->hash; + cclose(m->from); + wunlock(&m->lock); + wunlock(&pg->ns); + putmhead(m); + return; + } + wunlock(&m->lock); + wunlock(&pg->ns); + return; + } + p = &f->next; + } + wunlock(&m->lock); + wunlock(&pg->ns); + error(Eunion); +} + +Chan* +cclone(Chan *c) +{ + Chan *nc; + Walkqid *wq; + + wq = devtab[c->type]->walk(c, nil, nil, 0); + if(wq == nil) + error("clone failed"); + nc = wq->clone; + free(wq); + nc->name = c->name; + if(c->name) + incref(c->name); + return nc; +} + +int +findmount(Chan **cp, Mhead **mp, int type, int dev, Qid qid) +{ + Pgrp *pg; + Mhead *m; + + pg = up->env->pgrp; + rlock(&pg->ns); + for(m = MOUNTH(pg, qid); m; m = m->hash){ + rlock(&m->lock); +if(m->from == nil){ + print("m %p m->from 0\n", m); + runlock(&m->lock); + continue; +} + if(eqchantdqid(m->from, type, dev, qid, 1)) { + runlock(&pg->ns); + if(mp != nil){ + incref(m); + if(*mp != nil) + putmhead(*mp); + *mp = m; + } + if(*cp != nil) + cclose(*cp); + incref(m->mount->to); + *cp = m->mount->to; + runlock(&m->lock); + return 1; + } + runlock(&m->lock); + } + + runlock(&pg->ns); + return 0; +} + +int +domount(Chan **cp, Mhead **mp) +{ + return findmount(cp, mp, (*cp)->type, (*cp)->dev, (*cp)->qid); +} + +Chan* +undomount(Chan *c, Cname *name) +{ + Chan *nc; + Pgrp *pg; + Mount *t; + Mhead **h, **he, *f; + + pg = up->env->pgrp; + rlock(&pg->ns); + if(waserror()) { + runlock(&pg->ns); + nexterror(); + } + + he = &pg->mnthash[MNTHASH]; + for(h = pg->mnthash; h < he; h++) { + for(f = *h; f; f = f->hash) { + if(strcmp(f->from->name->s, name->s) != 0) + continue; + for(t = f->mount; t; t = t->next) { + if(eqchan(c, t->to, 1)) { + /* + * We want to come out on the left hand side of the mount + * point using the element of the union that we entered on. + * To do this, find the element that has a from name of + * c->name->s. + */ + if(strcmp(t->head->from->name->s, name->s) != 0) + continue; + nc = t->head->from; + incref(nc); + cclose(c); + c = nc; + break; + } + } + } + } + poperror(); + runlock(&pg->ns); + return c; +} + +/* + * Either walks all the way or not at all. No partial results in *cp. + * *nerror is the number of names to display in an error message. + */ +static char Edoesnotexist[] = "does not exist"; +int +walk(Chan **cp, char **names, int nnames, int nomount, int *nerror) +{ + int dev, dotdot, i, n, nhave, ntry, type; + Chan *c, *nc; + Cname *cname; + Mount *f; + Mhead *mh, *nmh; + Walkqid *wq; + + c = *cp; + incref(c); + cname = c->name; + incref(cname); + mh = nil; + + /* + * While we haven't gotten all the way down the path: + * 1. step through a mount point, if any + * 2. send a walk request for initial dotdot or initial prefix without dotdot + * 3. move to the first mountpoint along the way. + * 4. repeat. + * + * An invariant is that each time through the loop, c is on the undomount + * side of the mount point, and c's name is cname. + */ + for(nhave=0; nhaveqid.type&QTDIR)==0){ + if(nerror) + *nerror = nhave; + cnameclose(cname); + cclose(c); + strcpy(up->env->errstr, Enotdir); + if(mh != nil) + putmhead(mh); + return -1; + } + ntry = nnames - nhave; + if(ntry > MAXWELEM) + ntry = MAXWELEM; + dotdot = 0; + for(i=0; itype; + dev = c->dev; + + if((wq = devtab[type]->walk(c, nil, names+nhave, ntry)) == nil){ + /* try a union mount, if any */ + if(mh && !nomount){ + /* + * mh->mount == c, so start at mh->mount->next + */ + rlock(&mh->lock); + for(f = mh->mount->next; f; f = f->next) + if((wq = devtab[f->to->type]->walk(f->to, nil, names+nhave, ntry)) != nil) + break; + runlock(&mh->lock); + if(f != nil){ + type = f->to->type; + dev = f->to->dev; + } + } + if(wq == nil){ + cclose(c); + cnameclose(cname); + if(nerror) + *nerror = nhave+1; + if(mh != nil) + putmhead(mh); + return -1; + } + } + + nmh = nil; + if(dotdot) { + assert(wq->nqid == 1); + assert(wq->clone != nil); + + cname = addelem(cname, ".."); + nc = undomount(wq->clone, cname); + n = 1; + } else { + nc = nil; + if(!nomount) + for(i=0; inqid && iqid[i])) + break; + if(nc == nil){ /* no mount points along path */ + if(wq->clone == nil){ + cclose(c); + cnameclose(cname); + if(wq->nqid==0 || (wq->qid[wq->nqid-1].type&QTDIR)){ + if(nerror) + *nerror = nhave+wq->nqid+1; + strcpy(up->env->errstr, Edoesnotexist); + }else{ + if(nerror) + *nerror = nhave+wq->nqid; + strcpy(up->env->errstr, Enotdir); + } + free(wq); + if(mh != nil) + putmhead(mh); + return -1; + } + n = wq->nqid; + nc = wq->clone; + }else{ /* stopped early, at a mount point */ + if(wq->clone != nil){ + cclose(wq->clone); + wq->clone = nil; + } + n = i+1; + } + for(i=0; iumh != nil){ //BUG + print("walk umh\n"); + putmhead(c->umh); + c->umh = nil; + } + + cnameclose(c->name); + c->name = cname; + + cclose(*cp); + *cp = c; + if(nerror) + *nerror = 0; + return 0; +} + +/* + * c is a mounted non-creatable directory. find a creatable one. + */ +Chan* +createdir(Chan *c, Mhead *m) +{ + Chan *nc; + Mount *f; + + rlock(&m->lock); + if(waserror()) { + runlock(&m->lock); + nexterror(); + } + for(f = m->mount; f; f = f->next) { + if(f->mflag&MCREATE) { + nc = cclone(f->to); + runlock(&m->lock); + poperror(); + cclose(c); + return nc; + } + } + error(Enocreate); + return 0; +} + +void +saveregisters(void) +{ +} + +/* + * In place, rewrite name to compress multiple /, eliminate ., and process .. + */ +void +cleancname(Cname *n) +{ + char *p; + + if(n->s[0] == '#'){ + p = strchr(n->s, '/'); + if(p == nil) + return; + cleanname(p); + + /* + * The correct name is #i rather than #i/, + * but the correct name of #/ is #/. + */ + if(strcmp(p, "/")==0 && n->s[1] != '/') + *p = '\0'; + }else + cleanname(n->s); + n->len = strlen(n->s); +} + +static void +growparse(Elemlist *e) +{ + char **new; + int *inew; + enum { Delta = 8 }; + + if(e->nelems % Delta == 0){ + new = smalloc((e->nelems+Delta) * sizeof(char*)); + memmove(new, e->elems, e->nelems*sizeof(char*)); + free(e->elems); + e->elems = new; + inew = smalloc((e->nelems+Delta+1) * sizeof(int)); + memmove(inew, e->off, e->nelems*sizeof(int)); + free(e->off); + e->off = inew; + } +} + +/* + * The name is known to be valid. + * Copy the name so slashes can be overwritten. + * An empty string will set nelem=0. + * A path ending in / or /. or /.//./ etc. will have + * e.mustbedir = 1, so that we correctly + * reject, e.g., "/adm/users/." when /adm/users is a file + * rather than a directory. + */ +static void +parsename(char *name, Elemlist *e) +{ + char *slash; + + kstrdup(&e->name, name); + name = e->name; + e->nelems = 0; + e->elems = nil; + e->off = smalloc(sizeof(int)); + e->off[0] = skipslash(name) - name; + for(;;){ + name = skipslash(name); + if(*name=='\0'){ + e->mustbedir = 1; + break; + } + growparse(e); + + e->elems[e->nelems++] = name; + slash = utfrune(name, '/'); + if(slash == nil){ + e->off[e->nelems] = name+strlen(name) - e->name; + e->mustbedir = 0; + break; + } + e->off[e->nelems] = slash - e->name; + *slash++ = '\0'; + name = slash; + } +} + +void* +memrchr(void *va, int c, long n) +{ + uchar *a, *e; + + a = va; + for(e=a+n-1; e>a; e--) + if(*e == c) + return e; + return nil; +} + +/* + * Turn a name into a channel. + * &name[0] is known to be a valid address. It may be a kernel address. + * + * Opening with amode Aopen, Acreate, or Aremove guarantees + * that the result will be the only reference to that particular fid. + * This is necessary since we might pass the result to + * devtab[]->remove(). + * + * Opening Atodir, Amount, or Aaccess does not guarantee this. + * + * Opening Aaccess can, under certain conditions, return a + * correct Chan* but with an incorrect Cname attached. + * Since the functions that open Aaccess (sysstat, syswstat, sys_stat) + * do not use the Cname*, this avoids an unnecessary clone. + */ +Chan* +namec(char *aname, int amode, int omode, ulong perm) +{ + int n, prefix, len, t, nomount, npath; + Chan *c, *cnew; + Cname *cname; + Elemlist e; + Rune r; + Mhead *m; + char *createerr, tmperrbuf[ERRMAX]; + char *name; + + name = aname; + if(name[0] == '\0') + error("empty file name"); + validname(name, 1); + + /* + * Find the starting off point (the current slash, the root of + * a device tree, or the current dot) as well as the name to + * evaluate starting there. + */ + nomount = 0; + switch(name[0]){ + case '/': + c = up->env->pgrp->slash; + incref(c); + break; + + case '#': + nomount = 1; + up->genbuf[0] = '\0'; + n = 0; + while(*name!='\0' && (*name != '/' || n < 2)){ + if(n >= sizeof(up->genbuf)-1) + error(Efilename); + up->genbuf[n++] = *name++; + } + up->genbuf[n] = '\0'; + n = chartorune(&r, up->genbuf+1)+1; + if(r == 'M') + error(Enoattach); + /* + * the nodevs exceptions are + * | it only gives access to pipes you create + * e this process's environment + * s private file2chan creation space + * D private secure sockets name space + * a private TLS name space + */ + if(up->env->pgrp->nodevs && + (utfrune("|esDa", r) == nil || r == 's' && up->genbuf[n]!='\0')) + error(Enoattach); + t = devno(r, 1); + if(t == -1) + error(Ebadsharp); + c = devtab[t]->attach(up->genbuf+n); + break; + + default: + c = up->env->pgrp->dot; + incref(c); + break; + } + prefix = name - aname; + + e.name = nil; + e.elems = nil; + e.off = nil; + e.nelems = 0; + if(waserror()){ + cclose(c); + free(e.name); + free(e.elems); + free(e.off); +//dumpmount(); + nexterror(); + } + + /* + * Build a list of elements in the path. + */ + parsename(name, &e); + + /* + * On create, .... + */ + if(amode == Acreate){ + /* perm must have DMDIR if last element is / or /. */ + if(e.mustbedir && !(perm&DMDIR)){ + npath = e.nelems; + strcpy(tmperrbuf, "create without DMDIR"); + goto NameError; + } + + /* don't try to walk the last path element just yet. */ + if(e.nelems == 0) + error(Eexist); + e.nelems--; + } + + if(walk(&c, e.elems, e.nelems, nomount, &npath) < 0){ + if(npath < 0 || npath > e.nelems){ + print("namec %s walk error npath=%d\n", aname, npath); + nexterror(); + } + strcpy(tmperrbuf, up->env->errstr); + NameError: + len = prefix+e.off[npath]; + if(len < ERRMAX/3 || (name=memrchr(aname, '/', len))==nil || name==aname) + snprint(up->genbuf, sizeof up->genbuf, "%.*s", len, aname); + else + snprint(up->genbuf, sizeof up->genbuf, "...%.*s", (int)(len-(name-aname)), name); + snprint(up->env->errstr, ERRMAX, "%#q %s", up->genbuf, tmperrbuf); + nexterror(); + } + + if(e.mustbedir && !(c->qid.type&QTDIR)){ + npath = e.nelems; + strcpy(tmperrbuf, "not a directory"); + goto NameError; + } + + if(amode == Aopen && (omode&3) == OEXEC && (c->qid.type&QTDIR)){ + npath = e.nelems; + error("cannot exec directory"); + } + + switch(amode){ + case Aaccess: + if(!nomount) + domount(&c, nil); + break; + + case Abind: + m = nil; + if(!nomount) + domount(&c, &m); + if(c->umh != nil) + putmhead(c->umh); + c->umh = m; + break; + + case Aremove: + case Aopen: + Open: + /* save the name; domount might change c */ + cname = c->name; + incref(cname); + m = nil; + if(!nomount) + domount(&c, &m); + + /* our own copy to open or remove */ + c = cunique(c); + + /* now it's our copy anyway, we can put the name back */ + cnameclose(c->name); + c->name = cname; + + switch(amode){ + case Aremove: + putmhead(m); + break; + + case Aopen: + case Acreate: +if(c->umh != nil){ + print("cunique umh\n"); + putmhead(c->umh); + c->umh = nil; +} + + /* only save the mount head if it's a multiple element union */ + if(m && m->mount && m->mount->next) + c->umh = m; + else + putmhead(m); + + /* save registers else error() in open has wrong value of c saved */ + saveregisters(); + + if(omode == OEXEC) + c->flag &= ~CCACHE; + + c = devtab[c->type]->open(c, omode&~OCEXEC); + + if(omode & OCEXEC) + c->flag |= CCEXEC; + if(omode & ORCLOSE) + c->flag |= CRCLOSE; + break; + } + break; + + case Atodir: + /* + * Directories (e.g. for cd) are left before the mount point, + * so one may mount on / or . and see the effect. + */ + if(!(c->qid.type & QTDIR)) + error(Enotdir); + break; + + case Amount: + /* + * When mounting on an already mounted upon directory, + * one wants subsequent mounts to be attached to the + * original directory, not the replacement. Don't domount. + */ + break; + + case Acreate: + /* + * We've already walked all but the last element. + * If the last exists, try to open it OTRUNC. + * If omode&OEXCL is set, just give up. + */ + e.nelems++; + if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) == 0){ + if(omode&OEXCL) + error(Eexist); + omode |= OTRUNC; + goto Open; + } + + /* + * The semantics of the create(2) system call are that if the + * file exists and can be written, it is to be opened with truncation. + * On the other hand, the create(5) message fails if the file exists. + * If we get two create(2) calls happening simultaneously, + * they might both get here and send create(5) messages, but only + * one of the messages will succeed. To provide the expected create(2) + * semantics, the call with the failed message needs to try the above + * walk again, opening for truncation. This correctly solves the + * create/create race, in the sense that any observable outcome can + * be explained as one happening before the other. + * The create/create race is quite common. For example, it happens + * when two rc subshells simultaneously update the same + * environment variable. + * + * The implementation still admits a create/create/remove race: + * (A) walk to file, fails + * (B) walk to file, fails + * (A) create file, succeeds, returns + * (B) create file, fails + * (A) remove file, succeeds, returns + * (B) walk to file, return failure. + * + * This is hardly as common as the create/create race, and is really + * not too much worse than what might happen if (B) got a hold of a + * file descriptor and then the file was removed -- either way (B) can't do + * anything with the result of the create call. So we don't care about this race. + * + * Applications that care about more fine-grained decision of the races + * can use the OEXCL flag to get at the underlying create(5) semantics; + * by default we provide the common case. + * + * We need to stay behind the mount point in case we + * need to do the first walk again (should the create fail). + * + * We also need to cross the mount point and find the directory + * in the union in which we should be creating. + * + * The channel staying behind is c, the one moving forward is cnew. + */ + m = nil; + cnew = nil; /* is this assignment necessary? */ + if(!waserror()){ /* try create */ + if(!nomount && findmount(&cnew, &m, c->type, c->dev, c->qid)) + cnew = createdir(cnew, m); + else{ + cnew = c; + incref(cnew); + } + + /* + * We need our own copy of the Chan because we're + * about to send a create, which will move it. Once we have + * our own copy, we can fix the name, which might be wrong + * if findmount gave us a new Chan. + */ + cnew = cunique(cnew); + cnameclose(cnew->name); + cnew->name = c->name; + incref(cnew->name); + + devtab[cnew->type]->create(cnew, e.elems[e.nelems-1], omode&~(OEXCL|OCEXEC), perm); + poperror(); + if(omode & OCEXEC) + cnew->flag |= CCEXEC; + if(omode & ORCLOSE) + cnew->flag |= CRCLOSE; + if(m) + putmhead(m); + cclose(c); + c = cnew; + c->name = addelem(c->name, e.elems[e.nelems-1]); + break; + }else{ /* create failed */ + cclose(cnew); + if(m) + putmhead(m); + if(omode & OEXCL) + nexterror(); + /* save error */ + createerr = up->env->errstr; + up->env->errstr = tmperrbuf; + /* note: we depend that walk does not error */ + if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) < 0){ + up->env->errstr = createerr; + error(createerr); /* report true error */ + } + up->env->errstr = createerr; + omode |= OTRUNC; + goto Open; + } + panic("namec: not reached"); + + default: + panic("unknown namec access %d\n", amode); + } + + poperror(); + + /* place final element in genbuf for e.g. exec */ + if(e.nelems > 0) + kstrcpy(up->genbuf, e.elems[e.nelems-1], sizeof up->genbuf); + else + kstrcpy(up->genbuf, ".", sizeof up->genbuf); + free(e.name); + free(e.elems); + free(e.off); + + return c; +} + +/* + * name is valid. skip leading / and ./ as much as possible + */ +char* +skipslash(char *name) +{ + while(name[0]=='/' || (name[0]=='.' && (name[1]==0 || name[1]=='/'))) + name++; + return name; +} + +char isfrog[256]={ + /*NUL*/ 1, 1, 1, 1, 1, 1, 1, 1, + /*BKS*/ 1, 1, 1, 1, 1, 1, 1, 1, + /*DLE*/ 1, 1, 1, 1, 1, 1, 1, 1, + /*CAN*/ 1, 1, 1, 1, 1, 1, 1, 1, + ['/'] 1, + [0x7f] 1, +}; + +/* + * Check that the name + * a) is in valid memory. + * b) is shorter than 2^16 bytes, so it can fit in a 9P string field. + * c) contains no frogs. + * The first byte is known to be addressible by the requester, so the + * routine works for kernel and user memory both. + * The parameter slashok flags whether a slash character is an error + * or a valid character. + */ +void +validname(char *aname, int slashok) +{ + char *ename, *name; + int c; + Rune r; + + name = aname; + ename = memchr(name, 0, (1<<16)); + + if(ename==nil || ename-name>=(1<<16)) + error("name too long"); + + while(*name){ + /* all characters above '~' are ok */ + c = *(uchar*)name; + if(c >= Runeself) + name += chartorune(&r, name); + else{ + if(isfrog[c]) + if(!slashok || c!='/'){ + snprint(up->genbuf, sizeof(up->genbuf), "%s: %q", Ebadchar, aname); + error(up->genbuf); + } + name++; + } + } +} + +void +isdir(Chan *c) +{ + if(c->qid.type & QTDIR) + return; + error(Enotdir); +} + +/* + * This is necessary because there are many + * pointers to the top of a given mount list: + * + * - the mhead in the namespace hash table + * - the mhead in chans returned from findmount: + * used in namec and then by unionread. + * - the mhead in chans returned from createdir: + * used in the open/create race protect, which is gone. + * + * The RWlock in the Mhead protects the mount list it contains. + * The mount list is deleted when we cunmount. + * The RWlock ensures that nothing is using the mount list at that time. + * + * It is okay to replace c->mh with whatever you want as + * long as you are sure you have a unique reference to it. + * + * This comment might belong somewhere else. + */ +void +putmhead(Mhead *m) +{ + if(m && decref(m) == 0){ + m->mount = (Mount*)0xCafeBeef; + free(m); + } +} diff --git a/os/port/cis.c b/os/port/cis.c new file mode 100644 index 00000000..368eb6d6 --- /dev/null +++ b/os/port/cis.c @@ -0,0 +1,539 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +enum{ + Linktarget = 0x13, +}; + +/* + * read and crack the card information structure enough to set + * important parameters like power + */ +/* cis memory walking */ +typedef struct Cisdat { + uchar *cisbase; + int cispos; + int cisskip; + int cislen; +} Cisdat; + +static void tcfig(PCMslot*, Cisdat*, int); +static void tentry(PCMslot*, Cisdat*, int); +static void tvers1(PCMslot*, Cisdat*, int); +static void tlonglnkmfc(PCMslot*, Cisdat*, int); + +static int +readc(Cisdat *cis, uchar *x) +{ + if(cis->cispos >= cis->cislen) + return 0; + *x = cis->cisbase[cis->cisskip*cis->cispos]; + cis->cispos++; + return 1; +} + +static int +xcistuple(int slotno, int tuple, int subtuple, void *v, int nv, int attr) +{ + PCMmap *m; + Cisdat cis; + int i, l; + uchar *p; + uchar type, link, n, c; + int this, subtype; + + m = pcmmap(slotno, 0, 0, attr); + if(m == 0) + return -1; + + cis.cisbase = KADDR(m->isa); + cis.cispos = 0; + cis.cisskip = attr ? 2 : 1; + cis.cislen = m->len; + + /* loop through all the tuples */ + for(i = 0; i < 1000; i++){ + this = cis.cispos; + if(readc(&cis, &type) != 1) + break; + if(type == 0xFF) + break; + if(readc(&cis, &link) != 1) + break; + if(link == 0xFF) + break; + + n = link; + if(link > 1 && subtuple != -1){ + if(readc(&cis, &c) != 1) + break; + subtype = c; + n--; + }else + subtype = -1; + + if(type == tuple && subtype == subtuple){ + p = v; + for(l=0; l= 0) + return n; + return xcistuple(slotno, tuple, subtuple, v, nv, 0); +} + +void +pcmcisread(PCMslot *pp) +{ + int this; + Cisdat cis; + PCMmap *m; + uchar type, link; + + memset(pp->ctab, 0, sizeof(pp->ctab)); + pp->ncfg = 0; + memset(pp->cfg, 0, sizeof(pp->cfg)); + pp->configed = 0; + pp->nctab = 0; + pp->verstr[0] = 0; + + /* + * Read all tuples in attribute space. + */ + m = pcmmap(pp->slotno, 0, 0, 1); + if(m == 0) + return; + + cis.cisbase = KADDR(m->isa); + cis.cispos = 0; + cis.cisskip = 2; + cis.cislen = m->len; + + /* loop through all the tuples */ + for(;;){ + this = cis.cispos; + if(readc(&cis, &type) != 1) + break; + if(type == 0xFF) + break; + if(readc(&cis, &link) != 1) + break; + + switch(type){ + default: + break; + case 6: + tlonglnkmfc(pp, &cis, type); + break; + case 0x15: + tvers1(pp, &cis, type); + break; + case 0x1A: + tcfig(pp, &cis, type); + break; + case 0x1B: + tentry(pp, &cis, type); + break; + } + + if(link == 0xFF) + break; + cis.cispos = this + (2+link); + } + pcmunmap(pp->slotno, m); +} + +static ulong +getlong(Cisdat *cis, int size) +{ + uchar c; + int i; + ulong x; + + x = 0; + for(i = 0; i < size; i++){ + if(readc(cis, &c) != 1) + break; + x |= c<<(i*8); + } + return x; +} + +static void +tcfig(PCMslot *pp, Cisdat *cis, int ) +{ + uchar size, rasize, rmsize; + uchar last; + + if(readc(cis, &size) != 1) + return; + rasize = (size&0x3) + 1; + rmsize = ((size>>2)&0xf) + 1; + if(readc(cis, &last) != 1) + return; + + if(pp->ncfg >= 8){ + print("tcfig: too many configuration registers\n"); + return; + } + + pp->cfg[pp->ncfg].caddr = getlong(cis, rasize); + pp->cfg[pp->ncfg].cpresent = getlong(cis, rmsize); + pp->ncfg++; +} + +static ulong vexp[8] = +{ + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000 +}; +static ulong vmant[16] = +{ + 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 90, +}; + +static ulong +microvolt(Cisdat *cis) +{ + uchar c; + ulong microvolts; + ulong exp; + + if(readc(cis, &c) != 1) + return 0; + exp = vexp[c&0x7]; + microvolts = vmant[(c>>3)&0xf]*exp; + while(c & 0x80){ + if(readc(cis, &c) != 1) + return 0; + switch(c){ + case 0x7d: + break; /* high impedence when sleeping */ + case 0x7e: + case 0x7f: + microvolts = 0; /* no connection */ + break; + default: + exp /= 10; + microvolts += exp*(c&0x7f); + } + } + return microvolts; +} + +static ulong +nanoamps(Cisdat *cis) +{ + uchar c; + ulong nanoamps; + + if(readc(cis, &c) != 1) + return 0; + nanoamps = vexp[c&0x7]*vmant[(c>>3)&0xf]; + while(c & 0x80){ + if(readc(cis, &c) != 1) + return 0; + if(c == 0x7d || c == 0x7e || c == 0x7f) + nanoamps = 0; + } + return nanoamps; +} + +/* + * only nominal voltage (feature 1) is important for config, + * other features must read card to stay in sync. + */ +static ulong +power(Cisdat *cis) +{ + uchar feature; + ulong mv; + + mv = 0; + if(readc(cis, &feature) != 1) + return 0; + if(feature & 1) + mv = microvolt(cis); + if(feature & 2) + microvolt(cis); + if(feature & 4) + microvolt(cis); + if(feature & 8) + nanoamps(cis); + if(feature & 0x10) + nanoamps(cis); + if(feature & 0x20) + nanoamps(cis); + if(feature & 0x40) + nanoamps(cis); + return mv/1000000; +} + +static ulong mantissa[16] = +{ 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, }; + +static ulong exponent[8] = +{ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, }; + +static ulong +ttiming(Cisdat *cis, int scale) +{ + uchar unscaled; + ulong nanosecs; + + if(readc(cis, &unscaled) != 1) + return 0; + nanosecs = (mantissa[(unscaled>>3)&0xf]*exponent[unscaled&7])/10; + nanosecs = nanosecs * vexp[scale]; + return nanosecs; +} + +static void +timing(Cisdat *cis, PCMconftab *ct) +{ + uchar c, i; + + if(readc(cis, &c) != 1) + return; + i = c&0x3; + if(i != 3) + ct->maxwait = ttiming(cis, i); /* max wait */ + i = (c>>2)&0x7; + if(i != 7) + ct->readywait = ttiming(cis, i); /* max ready/busy wait */ + i = (c>>5)&0x7; + if(i != 7) + ct->otherwait = ttiming(cis, i); /* reserved wait */ +} + +static void +iospaces(Cisdat *cis, PCMconftab *ct) +{ + uchar c; + int i, nio; + + ct->nio = 0; + if(readc(cis, &c) != 1) + return; + + ct->bit16 = ((c>>5)&3) >= 2; + if(!(c & 0x80)){ + ct->io[0].start = 0; + ct->io[0].len = 1<<(c&0x1f); + ct->nio = 1; + return; + } + + if(readc(cis, &c) != 1) + return; + + /* + * For each of the range descriptions read the + * start address and the length (value is length-1). + */ + nio = (c&0xf)+1; + for(i = 0; i < nio; i++){ + ct->io[i].start = getlong(cis, (c>>4)&0x3); + ct->io[i].len = getlong(cis, (c>>6)&0x3)+1; + } + ct->nio = nio; +} + +static void +irq(Cisdat *cis, PCMconftab *ct) +{ + uchar c; + + if(readc(cis, &c) != 1) + return; + ct->irqtype = c & 0xe0; + if(c & 0x10) + ct->irqs = getlong(cis, 2); + else + ct->irqs = 1<<(c&0xf); + ct->irqs &= 0xDEB8; /* levels available to card */ +} + +static void +memspace(Cisdat *cis, int asize, int lsize, int host) +{ + ulong haddress, address, len; + + len = getlong(cis, lsize)*256; + address = getlong(cis, asize)*256; + USED(len, address); + if(host){ + haddress = getlong(cis, asize)*256; + USED(haddress); + } +} + +static void +tentry(PCMslot *pp, Cisdat *cis, int ) +{ + uchar c, i, feature; + PCMconftab *ct; + + if(pp->nctab >= nelem(pp->ctab)) + return; + if(readc(cis, &c) != 1) + return; + ct = &pp->ctab[pp->nctab++]; + + /* copy from last default config */ + if(pp->def) + *ct = *pp->def; + + ct->index = c & 0x3f; + + /* is this the new default? */ + if(c & 0x40) + pp->def = ct; + + /* memory wait specified? */ + if(c & 0x80){ + if(readc(cis, &i) != 1) + return; + if(i&0x80) + ct->memwait = 1; + } + + if(readc(cis, &feature) != 1) + return; + switch(feature&0x3){ + case 1: + ct->vpp1 = ct->vpp2 = power(cis); + break; + case 2: + power(cis); + ct->vpp1 = ct->vpp2 = power(cis); + break; + case 3: + power(cis); + ct->vpp1 = power(cis); + ct->vpp2 = power(cis); + break; + default: + break; + } + if(feature&0x4) + timing(cis, ct); + if(feature&0x8) + iospaces(cis, ct); + if(feature&0x10) + irq(cis, ct); + switch((feature>>5)&0x3){ + case 1: + memspace(cis, 0, 2, 0); + break; + case 2: + memspace(cis, 2, 2, 0); + break; + case 3: + if(readc(cis, &c) != 1) + return; + for(i = 0; i <= (c&0x7); i++) + memspace(cis, (c>>5)&0x3, (c>>3)&0x3, c&0x80); + break; + } + pp->configed++; +} + +static void +tvers1(PCMslot *pp, Cisdat *cis, int ) +{ + uchar c, major, minor, last; + int i; + + if(readc(cis, &major) != 1) + return; + if(readc(cis, &minor) != 1) + return; + last = 0; + for(i = 0; i < sizeof(pp->verstr)-1; i++){ + if(readc(cis, &c) != 1) + return; + if(c == 0) + c = ';'; + if(c == '\n') + c = ';'; + if(c == 0xff) + break; + if(c == ';' && last == ';') + continue; + pp->verstr[i] = c; + last = c; + } + pp->verstr[i] = 0; +} + +static void +tlonglnkmfc(PCMslot *pp, Cisdat *cis, int) +{ + int i, npos, opos; + uchar nfn, space, expect, type, this, link; + + readc(cis, &nfn); + for(i = 0; i < nfn; i++){ + readc(cis, &space); + npos = getlong(cis, 4); + opos = cis->cispos; + cis->cispos = npos; + expect = Linktarget; + + while(1){ + this = cis->cispos; + if(readc(cis, &type) != 1) + break; + if(type == 0xFF) + break; + if(readc(cis, &link) != 1) + break; + + if(expect && expect != type){ + print("tlonglnkmfc: expected %X found %X\n", + expect, type); + break; + } + expect = 0; + + switch(type){ + default: + break; + case 0x15: + tvers1(pp, cis, type); + break; + case 0x1A: + tcfig(pp, cis, type); + break; + case 0x1B: + tentry(pp, cis, type); + break; + } + + if(link == 0xFF) + break; + cis->cispos = this + (2+link); + } + cis->cispos = opos; + } +} diff --git a/os/port/dev.c b/os/port/dev.c new file mode 100644 index 00000000..4e91b8d0 --- /dev/null +++ b/os/port/dev.c @@ -0,0 +1,434 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +extern ulong kerndate; + +void +mkqid(Qid *q, vlong path, ulong vers, int type) +{ + q->type = type; + q->vers = vers; + q->path = path; +} + +int +devno(int c, int user) +{ + int i; + + for(i = 0; devtab[i] != nil; i++) { + if(devtab[i]->dc == c) + return i; + } + if(user == 0) + panic("devno %C 0x%ux", c, c); + + return -1; +} + +void +devdir(Chan *c, Qid qid, char *n, vlong length, char *user, long perm, Dir *db) +{ + db->name = n; + if(c->flag&CMSG) + qid.type |= QTMOUNT; + db->qid = qid; + db->type = devtab[c->type]->dc; + db->dev = c->dev; + db->mode = perm; + db->mode |= qid.type << 24; + db->atime = seconds(); + db->mtime = kerndate; + db->length = length; + db->uid = user; + db->gid = eve; + db->muid = user; +} + +/* + * the zeroth element of the table MUST be the directory itself for .. +*/ +int +devgen(Chan *c, char*, Dirtab *tab, int ntab, int i, Dir *dp) +{ + if(tab == 0) + return -1; + if(i != DEVDOTDOT){ + /* skip over the first element, that for . itself */ + i++; + if(i >= ntab) + return -1; + tab += i; + } + devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp); + return 1; +} + +void +devreset(void) +{ +} + +void +devinit(void) +{ +} + +void +devshutdown(void) +{ +} + +Chan* +devattach(int tc, char *spec) +{ + Chan *c; + char *buf; + + c = newchan(); + mkqid(&c->qid, 0, 0, QTDIR); + c->type = devno(tc, 0); + if(spec == nil) + spec = ""; + buf = smalloc(4+strlen(spec)+1); + sprint(buf, "#%C%s", tc, spec); + c->name = newcname(buf); + free(buf); + return c; +} + + +Chan* +devclone(Chan *c) +{ + Chan *nc; + + if(c->flag & COPEN) + panic("clone of open file type %C\n", devtab[c->type]->dc); + + nc = newchan(); + + nc->type = c->type; + nc->dev = c->dev; + nc->mode = c->mode; + nc->qid = c->qid; + nc->offset = c->offset; + nc->umh = nil; + nc->mountid = c->mountid; + nc->aux = c->aux; + nc->mqid = c->mqid; + nc->mcp = c->mcp; + return nc; +} + +Walkqid* +devwalk(Chan *c, Chan *nc, char **name, int nname, Dirtab *tab, int ntab, Devgen *gen) +{ + int i, j, alloc; + Walkqid *wq; + char *n; + Dir dir; + + if(nname > 0) + isdir(c); + + alloc = 0; + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + if(waserror()){ + if(alloc && wq->clone!=nil) + cclose(wq->clone); + free(wq); + return nil; + } + if(nc == nil){ + nc = devclone(c); + nc->type = 0; /* device doesn't know about this channel yet */ + alloc = 1; + } + wq->clone = nc; + + for(j=0; jqid.type&QTDIR)){ + if(j==0) + error(Enotdir); + goto Done; + } + n = name[j]; + if(strcmp(n, ".") == 0){ + Accept: + wq->qid[wq->nqid++] = nc->qid; + continue; + } + if(strcmp(n, "..") == 0){ + (*gen)(nc, nil, tab, ntab, DEVDOTDOT, &dir); + nc->qid = dir.qid; + goto Accept; + } + /* + * Ugly problem: If we're using devgen, make sure we're + * walking the directory itself, represented by the first + * entry in the table, and not trying to step into a sub- + * directory of the table, e.g. /net/net. Devgen itself + * should take care of the problem, but it doesn't have + * the necessary information (that we're doing a walk). + */ + if(gen==devgen && nc->qid.path!=tab[0].qid.path) + goto Notfound; + for(i=0;; i++) { + switch((*gen)(nc, n, tab, ntab, i, &dir)){ + case -1: + Notfound: + if(j == 0) + error(Enonexist); + kstrcpy(up->env->errstr, Enonexist, ERRMAX); + goto Done; + case 0: + continue; + case 1: + if(strcmp(n, dir.name) == 0){ + nc->qid = dir.qid; + goto Accept; + } + continue; + } + } + } + /* + * We processed at least one name, so will return some data. + * If we didn't process all nname entries succesfully, we drop + * the cloned channel and return just the Qids of the walks. + */ +Done: + poperror(); + if(wq->nqid < nname){ + if(alloc) + cclose(wq->clone); + wq->clone = nil; + }else if(wq->clone){ + /* attach cloned channel to same device */ + wq->clone->type = c->type; + } + return wq; +} + +int +devstat(Chan *c, uchar *db, int n, Dirtab *tab, int ntab, Devgen *gen) +{ + int i; + Dir dir; + char *p, *elem; + + for(i=0;; i++) + switch((*gen)(c, nil, tab, ntab, i, &dir)){ + case -1: + if(c->qid.type & QTDIR){ + if(c->name == nil) + elem = "???"; + else if(strcmp(c->name->s, "/") == 0) + elem = "/"; + else + for(elem=p=c->name->s; *p; p++) + if(*p == '/') + elem = p+1; + devdir(c, c->qid, elem, 0, eve, DMDIR|0555, &dir); + n = convD2M(&dir, db, n); + if(n == 0) + error(Ebadarg); + return n; + } + print("%s %s: devstat %C %llux\n", + up->text, up->env->user, + devtab[c->type]->dc, c->qid.path); + + error(Enonexist); + case 0: + break; + case 1: + if(c->qid.path == dir.qid.path) { + if(c->flag&CMSG) + dir.mode |= DMMOUNT; + n = convD2M(&dir, db, n); + if(n == 0) + error(Ebadarg); + return n; + } + break; + } + error(Egreg); /* not reached? */ + return -1; +} + +long +devdirread(Chan *c, char *d, long n, Dirtab *tab, int ntab, Devgen *gen) +{ + long m, dsz; + struct{ + Dir; + char slop[100]; + }dir; + + for(m=0; mdri++) { + switch((*gen)(c, nil, tab, ntab, c->dri, &dir)){ + case -1: + return m; + + case 0: + break; + + case 1: + dsz = convD2M(&dir, (uchar*)d, n-m); + if(dsz <= BIT16SZ){ /* <= not < because this isn't stat; read is stuck */ + if(m == 0) + error(Eshort); + return m; + } + m += dsz; + d += dsz; + break; + } + } + + return m; +} + +/* + * error(Eperm) if open permission not granted for up->env->user. + */ +void +devpermcheck(char *fileuid, ulong perm, int omode) +{ + ulong t; + static int access[] = { 0400, 0200, 0600, 0100 }; + + if(strcmp(up->env->user, fileuid) == 0) + perm <<= 0; + else + if(strcmp(up->env->user, eve) == 0) + perm <<= 3; + else + perm <<= 6; + + t = access[omode&3]; + if((t&perm) != t) + error(Eperm); +} + +Chan* +devopen(Chan *c, int omode, Dirtab *tab, int ntab, Devgen *gen) +{ + int i; + Dir dir; + + for(i=0;; i++) { + switch((*gen)(c, nil, tab, ntab, i, &dir)){ + case -1: + goto Return; + case 0: + break; + case 1: + if(c->qid.path == dir.qid.path) { + devpermcheck(dir.uid, dir.mode, omode); + goto Return; + } + break; + } + } +Return: + c->offset = 0; + if((c->qid.type&QTDIR) && omode!=OREAD) + error(Eperm); + c->mode = openmode(omode); + c->flag |= COPEN; + return c; +} + +void +devcreate(Chan*, char*, int, ulong) +{ + error(Eperm); +} + +Block* +devbread(Chan *c, long n, ulong offset) +{ + Block *bp; + + bp = allocb(n); + if(bp == 0) + error(Enomem); + if(waserror()) { + freeb(bp); + nexterror(); + } + bp->wp += devtab[c->type]->read(c, bp->wp, n, offset); + poperror(); + return bp; +} + +long +devbwrite(Chan *c, Block *bp, ulong offset) +{ + long n; + + if(waserror()) { + freeb(bp); + nexterror(); + } + n = devtab[c->type]->write(c, bp->rp, BLEN(bp), offset); + poperror(); + freeb(bp); + + return n; +} + +void +devremove(Chan*) +{ + error(Eperm); +} + +int +devwstat(Chan*, uchar*, int) +{ + error(Eperm); + return 0; +} + +void +devpower(int) +{ + error(Eperm); +} + +int +devconfig(int, char *, DevConf *) +{ + error(Eperm); + return 0; +} + +/* + * check that the name in a wstat is plausible + */ +void +validwstatname(char *name) +{ + validname(name, 0); + if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) + error(Efilename); +} + +Dev* +devbyname(char *name) +{ + int i; + + for(i = 0; devtab[i] != nil; i++) + if(strcmp(devtab[i]->name, name) == 0) + return devtab[i]; + return nil; +} diff --git a/os/port/devXXX.c b/os/port/devXXX.c new file mode 100644 index 00000000..2c76abcd --- /dev/null +++ b/os/port/devXXX.c @@ -0,0 +1,147 @@ +/* + * template for making a new device + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + + +enum{ + Qdir, + Qdata, +}; + +static +Dirtab XXXtab[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, /* entry for "." must be first if devgen used */ + "data", {Qdata, 0}, 0, 0666, +}; + +static void +XXXreset(void) /* default in dev.c */ +{ +} + +static void +XXXinit(void) /* default in dev.c */ +{ +} + +static Chan* +XXXattach(char* spec) +{ + return devattach('X', spec); +} + +static Walkqid* +XXXwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, XXXtab, nelem(XXXtab), devgen); +} + +static int +XXXstat(Chan* c, uchar *db, int n) +{ + return devstat(c, db, n, XXXtab, nelem(XXXtab), devgen); +} + +static Chan* +XXXopen(Chan* c, int omode) +{ + return devopen(c, omode, XXXtab, nelem(XXXtab), devgen); +} + +static void +XXXcreate(Chan* c, char* name, int omode, ulong perm) /* default in dev.c */ +{ + USED(c, name, omode, perm); + error(Eperm); +} + +static void +XXXremove(Chan* c) /* default in dev.c */ +{ + USED(c); + error(Eperm); +} + +static int +XXXwstat(Chan* c, uchar *dp, int n) /* default in dev.c */ +{ + USED(c, dp); + error(Eperm); + return n; +} + +static void +XXXclose(Chan* c) +{ + USED(c); +} + +static long +XXXread(Chan* c, void* a, long n, vlong offset) +{ + USED(offset); + + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, XXXtab, nelem(XXXtab), devgen); + case Qdata: + break; + default: + n=0; + break; + } + return n; +} + +static Block* +XXXbread(Chan* c, long n, ulong offset) /* default in dev.c */ +{ + return devbread(c, n, offset); +} + +static long +XXXwrite(Chan* c, void* a, long n, vlong offset) +{ + USED(a, offset); + + switch((ulong)c->qid.path){ + case Qdata: + break; + default: + error(Ebadusefd); + } + return n; +} + +static long +XXXbwrite(Chan* c, Block* bp, ulong offset) /* default in dev.c */ +{ + return devbwrite(c, bp, offset); +} + +Dev XXXdevtab = { /* defaults in dev.c */ + 'X', + "XXX", + + XXXreset, /* devreset */ + XXXinit, /* devinit */ + devshutdown, + XXXattach, + XXXwalk, + XXXstat, + XXXopen, + XXXcreate, /* devcreate */ + XXXclose, + XXXread, + XXXbread, /* devbread */ + XXXwrite, + XXXbwrite, /* devbwrite */ + XXXremove, /* devremove */ + XXXwstat, /* devwstat */ +}; diff --git a/os/port/devaudio.c b/os/port/devaudio.c new file mode 100644 index 00000000..b1441e03 --- /dev/null +++ b/os/port/devaudio.c @@ -0,0 +1,947 @@ +/* + * SB 16 driver + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" +#include "audio.h" + +typedef struct AQueue AQueue; +typedef struct Buf Buf; + +enum +{ + Qdir = 0, + Qaudio, + Qvolume, + + Fmono = 1, + Fin = 2, + Fout = 4, + + Aclosed = 0, + Aread, + Awrite, + + Vaudio = 0, + Vsynth, + Vcd, + Vline, + Vmic, + Vspeaker, + Vtreb, + Vbass, + Vspeed, + Nvol, + + Speed = 44100, + Ncmd = 50, /* max volume command words */ +}; + +static +Dirtab audiodir[] = +{ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "audio", {Qaudio}, 0, 0666, + "volume", {Qvolume}, 0, 0666, +}; + +struct Buf +{ + uchar* virt; + ulong phys; + Buf* next; +}; +struct AQueue +{ + Lock; + Buf* first; + Buf* last; +}; +static struct +{ + QLock; + Rendez vous; + int bufinit; /* boolean if buffers allocated */ + int curcount; /* how much data in current buffer */ + int active; /* boolean dma running */ + int intr; /* boolean an interrupt has happened */ + int amode; /* Aclosed/Aread/Awrite for /audio */ + int rivol[Nvol]; /* right/left input/output volumes */ + int livol[Nvol]; + int rovol[Nvol]; + int lovol[Nvol]; + int major; /* SB16 major version number (sb 4) */ + int minor; /* SB16 minor version number */ + + Buf buf[Nbuf]; /* buffers and queues */ + AQueue empty; + AQueue full; + Buf* current; + Buf* filling; +} audio; + +static struct +{ + char* name; + int flag; + int ilval; /* initial values */ + int irval; +} volumes[] = +{ +[Vaudio] "audio", Fout, 50, 50, +[Vsynth] "synth", Fin|Fout, 0, 0, +[Vcd] "cd", Fin|Fout, 0, 0, +[Vline] "line", Fin|Fout, 0, 0, +[Vmic] "mic", Fin|Fout|Fmono, 0, 0, +[Vspeaker] "speaker", Fout|Fmono, 0, 0, + +[Vtreb] "treb", Fout, 50, 50, +[Vbass] "bass", Fout, 50, 50, + +[Vspeed] "speed", Fin|Fout|Fmono, Speed, Speed, + 0 +}; + +static struct +{ + Lock; + int reset; /* io ports to the sound blaster */ + int read; + int write; + int wstatus; + int rstatus; + int mixaddr; + int mixdata; + int clri8; + int clri16; + int clri401; + int dma; +} blaster; + +static void swab(uchar*); + +static char Emajor[] = "soundblaster not responding/wrong version"; +static char Emode[] = "illegal open mode"; +static char Evolume[] = "illegal volume specifier"; + +static int +sbcmd(int val) +{ + int i, s; + + for(i=1<<16; i!=0; i--) { + s = inb(blaster.wstatus); + if((s & 0x80) == 0) { + outb(blaster.write, val); + return 0; + } + } +/* print("#A: sbcmd (#%.2x) timeout\n", val); /**/ + return 1; +} + +static int +sbread(void) +{ + int i, s; + + for(i=1<<16; i!=0; i--) { + s = inb(blaster.rstatus); + if((s & 0x80) != 0) { + return inb(blaster.read); + } + } +/* print("#A: sbread did not respond\n"); /**/ + return 0xbb; +} + +static int +mxcmd(int addr, int val) +{ + + outb(blaster.mixaddr, addr); + outb(blaster.mixdata, val); + return 1; +} + +static int +mxread(int addr) +{ + int s; + + outb(blaster.mixaddr, addr); + s = inb(blaster.mixdata); + return s; +} + +static void +mxcmds(int s, int v) +{ + + if(v > 100) + v = 100; + if(v < 0) + v = 0; + mxcmd(s, (v*255)/100); +} + +static void +mxcmdt(int s, int v) +{ + + if(v > 100) + v = 100; + if(v <= 0) + mxcmd(s, 0); + else + mxcmd(s, 255-100+v); +} + +static void +mxcmdu(int s, int v) +{ + + if(v > 100) + v = 100; + if(v <= 0) + v = 0; + mxcmd(s, 128-50+v); +} + +static void +mxvolume(void) +{ + int *left, *right; + int source; + + if(audio.amode == Aread){ + left = audio.livol; + right = audio.rivol; + }else{ + left = audio.lovol; + right = audio.rovol; + } + + ilock(&blaster); + + mxcmd(0x30, 255); /* left master */ + mxcmd(0x31, 255); /* right master */ + mxcmd(0x3f, 0); /* left igain */ + mxcmd(0x40, 0); /* right igain */ + mxcmd(0x41, 0); /* left ogain */ + mxcmd(0x42, 0); /* right ogain */ + + mxcmds(0x32, left[Vaudio]); + mxcmds(0x33, right[Vaudio]); + + mxcmds(0x34, left[Vsynth]); + mxcmds(0x35, right[Vsynth]); + + mxcmds(0x36, left[Vcd]); + mxcmds(0x37, right[Vcd]); + + mxcmds(0x38, left[Vline]); + mxcmds(0x39, right[Vline]); + + mxcmds(0x3a, left[Vmic]); + mxcmds(0x3b, left[Vspeaker]); + + mxcmdu(0x44, left[Vtreb]); + mxcmdu(0x45, right[Vtreb]); + + mxcmdu(0x46, left[Vbass]); + mxcmdu(0x47, right[Vbass]); + + source = 0; + if(left[Vsynth]) + source |= 1<<6; + if(right[Vsynth]) + source |= 1<<5; + if(left[Vaudio]) + source |= 1<<4; + if(right[Vaudio]) + source |= 1<<3; + if(left[Vcd]) + source |= 1<<2; + if(right[Vcd]) + source |= 1<<1; + if(left[Vmic]) + source |= 1<<0; + if(audio.amode == Aread) + mxcmd(0x3c, 0); /* output switch */ + else + mxcmd(0x3c, source); + mxcmd(0x3d, source); /* input left switch */ + mxcmd(0x3e, source); /* input right switch */ + iunlock(&blaster); +} + +static Buf* +getbuf(AQueue *q) +{ + Buf *b; + + ilock(q); + b = q->first; + if(b) + q->first = b->next; + iunlock(q); + + return b; +} + +static void +putbuf(AQueue *q, Buf *b) +{ + + ilock(q); + b->next = 0; + if(q->first) + q->last->next = b; + else + q->first = b; + q->last = b; + iunlock(q); +} + +/* + * move the dma to the next buffer + */ +static void +contindma(void) +{ + Buf *b; + + if(!audio.active) + goto shutdown; + + b = audio.current; + if(audio.amode == Aread) { + if(b) /* shouldnt happen */ + putbuf(&audio.full, b); + b = getbuf(&audio.empty); + } else { + if(b) /* shouldnt happen */ + putbuf(&audio.empty, b); + b = getbuf(&audio.full); + } + audio.current = b; + if(b == 0) + goto shutdown; + + dmasetup(blaster.dma, b->virt, Bufsize, audio.amode == Aread); + return; + +shutdown: + dmaend(blaster.dma); + sbcmd(0xd9); /* exit at end of count */ + sbcmd(0xd5); /* pause */ + audio.curcount = 0; + audio.active = 0; +} + +/* + * cause sb to get an interrupt per buffer. + * start first dma + */ +static void +startdma(void) +{ + ulong count; + int speed; + + ilock(&blaster); + dmaend(blaster.dma); + if(audio.amode == Aread) { + sbcmd(0x42); /* input sampling rate */ + speed = audio.livol[Vspeed]; + } else { + sbcmd(0x41); /* output sampling rate */ + speed = audio.lovol[Vspeed]; + } + sbcmd(speed>>8); + sbcmd(speed); + + count = (Bufsize >> 1) - 1; + if(audio.amode == Aread) + sbcmd(0xbe); /* A/D, autoinit */ + else + sbcmd(0xb6); /* D/A, autoinit */ + sbcmd(0x30); /* stereo, 16 bit */ + sbcmd(count); + sbcmd(count>>8); + + audio.active = 1; + contindma(); + iunlock(&blaster); +} + +/* + * if audio is stopped, + * start it up again. + */ +static void +pokeaudio(void) +{ + if(!audio.active) + startdma(); +} + +static void +audiosbintr(void) +{ + int stat, dummy; + + stat = mxread(0x82) & 7; /* get irq status */ + if(stat) { + dummy = 0; + if(stat & 2) { + ilock(&blaster); + dummy = inb(blaster.clri16); + contindma(); + iunlock(&blaster); + audio.intr = 1; + wakeup(&audio.vous); + } + if(stat & 1) { + dummy = inb(blaster.clri8); + } + if(stat & 4) { + dummy = inb(blaster.clri401); + } + USED(dummy); + } +} + +static void +pcaudiosbintr(Ureg*, void*) +{ +/* print("#A: audio interrupt\n"); /**/ + audiosbintr(); +} + +static void +audiodmaintr(void) +{ +/* print("#A: dma interrupt\n"); /**/ +} + +static int +anybuf(void*) +{ + return audio.intr; +} + +/* + * wait for some output to get + * empty buffers back. + */ +static void +waitaudio(void) +{ + + audio.intr = 0; + pokeaudio(); + tsleep(&audio.vous, anybuf, 0, 10*1000); + if(audio.intr == 0) { +/* print("#A: audio timeout\n"); /**/ + audio.active = 0; + pokeaudio(); + } +} + +static void +sbbufinit(void) +{ + int i; + void *p; + + for(i=0; i3 υs */ + outb(blaster.reset, 0); + delay(1); + + i = sbread(); + if(i != 0xaa) { + print("#A: no response #%.2x\n", i); + return; + } + + sbcmd(0xe1); /* get version */ + audio.major = sbread(); + audio.minor = sbread(); + + if(audio.major != 4) { + print("#A: model #%.2x #%.2x; not SB 16\n", audio.major, audio.minor); + return; + } + /* + * initialize the mixer + */ + mxcmd(0x00, 0); /* Reset mixer */ + mxvolume(); + + /* + * set up irq/dma chans + */ + mxcmd(0x80, /* irq */ + (sbconf.irq==2)? 1: + (sbconf.irq==5)? 2: + (sbconf.irq==7)? 4: + (sbconf.irq==10)? 8: + 0); + mxcmd(0x81, 1<qid.path) { + default: + error(Eperm); + break; + + case Qvolume: + case Qdir: + break; + + case Qaudio: + amode = Awrite; + if((omode&7) == OREAD) + amode = Aread; + qlock(&audio); + if(audio.amode != Aclosed){ + qunlock(&audio); + error(Einuse); + } + if(audio.bufinit == 0) { + audio.bufinit = 1; + sbbufinit(); + } + audio.amode = amode; + setempty(); + audio.curcount = 0; + qunlock(&audio); + mxvolume(); + break; + } + c = devopen(c, omode, audiodir, nelem(audiodir), devgen); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + + return c; +} + +static void +audioclose(Chan *c) +{ + + switch((ulong)c->qid.path) { + default: + error(Eperm); + break; + + case Qdir: + case Qvolume: + break; + + case Qaudio: + if(c->flag & COPEN) { + qlock(&audio); + audio.amode = Aclosed; + if(waserror()){ + qunlock(&audio); + nexterror(); + } + while(audio.active) + waitaudio(); + setempty(); + poperror(); + qunlock(&audio); + } + break; + } +} + +static long +audioread(Chan *c, void *vp, long n, vlong offset) +{ + int liv, riv, lov, rov; + long m, n0; + char buf[300]; + Buf *b; + int j; + char *a; + + a = vp; + n0 = n; + switch((ulong)c->qid.path) { + default: + error(Eperm); + break; + + case Qdir: + return devdirread(c, a, n, audiodir, nelem(audiodir), devgen); + + case Qaudio: + if(audio.amode != Aread) + error(Emode); + qlock(&audio); + if(waserror()){ + qunlock(&audio); + nexterror(); + } + while(n > 0) { + b = audio.filling; + if(b == 0) { + b = getbuf(&audio.full); + if(b == 0) { + waitaudio(); + continue; + } + audio.filling = b; + swab(b->virt); + audio.curcount = 0; + } + m = Bufsize-audio.curcount; + if(m > n) + m = n; + memmove(a, b->virt+audio.curcount, m); + + audio.curcount += m; + n -= m; + a += m; + if(audio.curcount >= Bufsize) { + audio.filling = 0; + putbuf(&audio.empty, b); + } + } + poperror(); + qunlock(&audio); + break; + + case Qvolume: + j = 0; + buf[0] = 0; + for(m=0; volumes[m].name; m++){ + liv = audio.livol[m]; + riv = audio.rivol[m]; + lov = audio.lovol[m]; + rov = audio.rovol[m]; + j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name); + if((volumes[m].flag & Fmono) || liv==riv && lov==rov){ + if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov) + j += snprint(buf+j, sizeof(buf)-j, " %d", liv); + else{ + if(volumes[m].flag & Fin) + j += snprint(buf+j, sizeof(buf)-j, " in %d", liv); + if(volumes[m].flag & Fout) + j += snprint(buf+j, sizeof(buf)-j, " out %d", lov); + } + }else{ + if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov && riv==rov) + j += snprint(buf+j, sizeof(buf)-j, " left %d right %d", + liv, riv); + else{ + if(volumes[m].flag & Fin) + j += snprint(buf+j, sizeof(buf)-j, " in left %d right %d", + liv, riv); + if(volumes[m].flag & Fout) + j += snprint(buf+j, sizeof(buf)-j, " out left %d right %d", + lov, rov); + } + } + j += snprint(buf+j, sizeof(buf)-j, "\n"); + } + + return readstr(offset, a, n, buf); + } + return n0-n; +} + +static long +audiowrite(Chan *c, void *vp, long n, vlong) +{ + long m, n0; + int i, nf, v, left, right, in, out; + char buf[255], *field[Ncmd]; + Buf *b; + char *a; + + a = vp; + n0 = n; + switch((ulong)c->qid.path) { + default: + error(Eperm); + break; + + case Qvolume: + v = Vaudio; + left = 1; + right = 1; + in = 1; + out = 1; + if(n > sizeof(buf)-1) + n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = '\0'; + + nf = getfields(buf, field, Ncmd, 1, " \t\n"); + for(i = 0; i < nf; i++){ + /* + * a number is volume + */ + if(field[i][0] >= '0' && field[i][0] <= '9') { + m = strtoul(field[i], 0, 10); + if(left && out) + audio.lovol[v] = m; + if(left && in) + audio.livol[v] = m; + if(right && out) + audio.rovol[v] = m; + if(right && in) + audio.rivol[v] = m; + mxvolume(); + goto cont0; + } + + for(m=0; volumes[m].name; m++) { + if(strcmp(field[i], volumes[m].name) == 0) { + v = m; + in = 1; + out = 1; + left = 1; + right = 1; + goto cont0; + } + } + + if(strcmp(field[i], "reset") == 0) { + resetlevel(); + mxvolume(); + goto cont0; + } + if(strcmp(field[i], "in") == 0) { + in = 1; + out = 0; + goto cont0; + } + if(strcmp(field[i], "out") == 0) { + in = 0; + out = 1; + goto cont0; + } + if(strcmp(field[i], "left") == 0) { + left = 1; + right = 0; + goto cont0; + } + if(strcmp(field[i], "right") == 0) { + left = 0; + right = 1; + goto cont0; + } + error(Evolume); + break; + cont0:; + } + break; + + case Qaudio: + if(audio.amode != Awrite) + error(Emode); + qlock(&audio); + if(waserror()){ + qunlock(&audio); + nexterror(); + } + while(n > 0) { + b = audio.filling; + if(b == 0) { + b = getbuf(&audio.empty); + if(b == 0) { + waitaudio(); + continue; + } + audio.filling = b; + audio.curcount = 0; + } + + m = Bufsize-audio.curcount; + if(m > n) + m = n; + memmove(b->virt+audio.curcount, a, m); + + audio.curcount += m; + n -= m; + a += m; + if(audio.curcount >= Bufsize) { + audio.filling = 0; + swab(b->virt); + putbuf(&audio.full, b); + } + } + poperror(); + qunlock(&audio); + break; + } + return n0 - n; +} + +static void +swab(uchar *a) +{ + ulong *p, *ep, b; + + if(!SBswab) + return; + p = (ulong*)a; + ep = p + (Bufsize>>2); + while(p < ep) { + b = *p; + b = (b>>24) | (b<<24) | + ((b&0xff0000) >> 8) | + ((b&0x00ff00) << 8); + *p++ = b; + } +} + +Dev audiodevtab = { + 'A', + "audio", + + devreset, + audioinit, + devshutdown, + audioattach, + audiowalk, + audiostat, + audioopen, + devcreate, + audioclose, + audioread, + devbread, + audiowrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devbench.c b/os/port/devbench.c new file mode 100644 index 00000000..22ab5e34 --- /dev/null +++ b/os/port/devbench.c @@ -0,0 +1,1165 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include +#include "io.h" +#include "../port/error.h" +#include +#include "kernel.h" + +/* Builtin module support */ +#include "bench.h" +#include "benchmod.h" + +typedef enum { None, Calibrate, Base, Op, Intr, Dis, Gc, MS2T, xTest}; +static struct { + int inuse; /* reference count */ + int test; + void* scratch; + char* buf; + int bufsz; + char* wpos; + void (*op)(void); + vlong tickstart; +} bench; + +static void +log(char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + bench.wpos = vseprint(bench.wpos, bench.buf+bench.bufsz, msg, ap); + va_end(ap); +} + +void +elog(char *msg, ...) +{ + va_list ap; + + if(bench.buf == 0) + return; + va_start(ap, msg); + bench.wpos = vseprint(bench.wpos, bench.buf+bench.bufsz, msg, ap); + va_end(ap); +} + +static void +clear(void) +{ + bench.wpos = bench.buf; +} + +static long +rep(void *to, long n, ulong offset) +{ + long left = bench.wpos - bench.buf - offset; + if(left < 0) + left = 0; + if(n > left) + n = left; + memmove(to, bench.buf+offset, n); + return n; +} + +static long +notest(int report, void *va, long n, ulong offset) +{ + USED(report, va, n, offset); + if(report) + return rep(va, n, offset); + return 0; +} + +// Calibration +static long MS2TS = 0; // time stamps per millisec +static long US2TS = 0; // time stamps per microsecond + +static long +cal(int report, void *va, long n, ulong offset) +{ + int tot, i, lim, low, max, pl, mdelay; + ulong t; + if(report) + return rep(va, n, offset); + clear(); + setpri(PriRealtime); + lim = 1000; + low = 64000000; + max = 0; + tot = 0; + mdelay = 1000; + for(i=0; i max) + max = t; + tot += t; + } + MS2TS = tot/lim; + US2TS = MS2TS/1000; + if(va) + log("mdelay=%lud lim=%lud tot=%lud low=%lud max=%lud\n", mdelay, lim, tot, low, max); + setpri(PriNormal); + return n; +} + +/* + * ticks to format string + */ +/*static*/ char * +ts2str(vlong ticks) +{ +#define Nbuf 5 + static char b[Nbuf][40]; + static int n=Nbuf-1; + char *fmt, *unit; + double d; + + if(0){ + print("ticks=%lld MS2TS=%ld\n", ticks, MS2TS); + d = (double)ticks; + print("1:%f\n", d); + d = (double)ticks*1000; + //print("2:%f\n", d); + d = ((double)ticks)/MS2TS; + //print("3:%f\n", d); + } + n = (n+1)%Nbuf; + if(ticks > MS2TS*1000) { + fmt = "%.2f %s"; + unit = "s"; + d = ((double)ticks/MS2TS) * 1000.0; + } else if(ticks > MS2TS) { + fmt = "%.2f %s"; + unit = "ms"; + d = (double)ticks/MS2TS; + } else if(ticks > MS2TS/1000) { + fmt = "%.2f %s"; + unit = "us"; + d = ((double)ticks*1000)/MS2TS; + } else { + fmt = "%.2f %s"; + unit = "ns"; + d = ((double)ticks*1000*1000)/MS2TS; + } + sprint(b[n], fmt, d, unit); + return b[n]; +} + +/* + * ticks to microseconds + */ +static double +ts2us(vlong ticks) +{ + return ((double)ticks*1000)/MS2TS; +} + +/* + * microseconds timestamp + */ +static vlong +bus(int reset) +{ + vlong now; + if(US2TS == 0) + return 0; + if(reset) { + bench.tickstart = archrdtsc(); + return 0; + } + now = archrdtsc(); + return ((now-bench.tickstart))/US2TS; +} + +// Base +static long +base(int report, void *va, long n, ulong offset) +{ + int tot, i, lim, low, max, pl; + ulong t; + char *bm; + + if(report) + return rep(va, n, offset); + clear(); + setpri(PriRealtime); + lim = 1000; + low = 64000000; + max = 0; + tot = 0; + for(i=0; i max) + max = t; + tot += t; + } + bm = ts2str(tot/lim); + log("%d %lud %lud %lud %lud (%s)\n", up->pid, lim, tot, low, max, bm); + setpri(PriNormal); + return n; +} + +// Timeop + +typedef struct Psync Psync; + +enum { + Maxprocs=3, +}; + +struct Psync { + Rendez r; + int flag; + int id; + int awaken; +}; +static Psync timesync[Maxprocs]; +static Ref nactive; +static Ref nbusy; +static RWlock sync; + +static void +nilop(void) +{ +} + +static int +timev(void *a) +{ + return *(int*)a; +} + +static void +timeop0(void *ap) +{ + int tot, i, lim, low, max; + ulong t; + Psync *ps; + char *bm; + + ps = ap; + setpri(PriRealtime); + incref(&nactive); + sleep(&ps->r, timev, &ps->flag); + rlock(&sync); + lim = 1000; + low = 64000000; + max = 0; + tot = 0; + for(i=0; i max) + max = t; + tot += t; + } + bm = ts2str(tot/lim); + log("%d %lud %lud %lud %lud (%s)\n", up->pid, lim, tot, low, max, bm); + runlock(&sync); + pexit("", 0); +} + +static long +timeop(int report, void *va, long n, ulong offset) +{ + int i, np, pl; + + if(report) + return rep(va, n, offset); + clear(); + bench.op = 0; + if(strncmp(va, "nil", 3) == 0) + bench.op = nilop; + else if(strncmp(va, "sched", 5) == 0) + bench.op = sched; + else + return 0; + for(np=1; np<=Maxprocs; np++) { + nactive.ref = 0; + wlock(&sync); + log("%d procs\n", np); + setpri(PriRealtime); + for(i=0; isleep, return0, 0, 20); + for(i=0; ispllo = curct->intr = curct->isave = archrdtsc32(); + else { + curct->spllo = spltbl; + curct->intr = intrtbl; + curct->isave = isavetbl; + } + curct->arrive = archrdtsc32(); + intrwant = 0; + wakeup(&vous); + curct->wakeup = archrdtsc32(); +} + +/* + * sleep calls intrtest with splhi (under lock): + * provoke the interrupt now, so that it is guaranteed + * not to happen until sleep has queued the process, + * forcing wakeup to do something. + */ +static int +intrtest(void*) +{ + ienable = 1; /* enable recording on interrupt */ + curct->sleep = archrdtsc32(); + return intrwant==0; +} + +static long +intrtime(int report, void *va, long n, ulong offset) +{ + Ictr *ic; + long t; + int i; + char *bm; + if(report) + return rep(va, n, offset); + clear(); + + setpri(PriRealtime); + sched(); + curct = counters; + ienable = 0; + addclock0link(intrwake, MS2HZ); + for(i=0; ibase = archrdtsc32(); + sleep(&vous, intrtest, nil); + curct->awake = archrdtsc32(); + sched(); /* just to slow it down between trials */ + } + log("interrupt\n"); + for(i=0; iawake - ic->base; + bm = ts2str(ic->awake - ic->arrive); + ic->awake -= ic->wakeup; + ic->wakeup -= ic->arrive; + ic->arrive -= ic->isave; + ic->isave -= ic->intr; + ic->intr -= ic->spllo; + ic->spllo -= ic->sleep; + ic->sleep -= ic->base; + log("%ld\t%ld\t%ld\t%ld\t%ld\t%ld\t%ld\t%ld (%s)\n", ic->sleep, ic->spllo, ic->intr, ic->isave, ic->arrive, ic->wakeup, ic->awake, t, bm); + } + setpri(PriNormal); + return n; +} + + +/* DIS operation timing */ + +typedef struct { + vlong n; /* count */ + vlong min; + vlong max; + vlong sum; + vlong sumsq; /* sum of squares */ +} Stat; + +static void +stat(enum { Reset, Inc } op, Stat *c, vlong val) +{ + switch(op) { + case Reset: + c->n = 0; + c->sum = 0; + c->sumsq = 0; + c->min = 0; + c->max = 0; + break; + case Inc: + c->n++; + c->sum += val; + c->sumsq += val*val; + break; + } + if(val < c->min || c->n == 1) + c->min = val; + if(val > c->max || c->n == 1) + c->max = val; +} + +static void +statinc(Stat *d, Stat *s) +{ + d->n += s->n; + d->sum += s->sum; + d->sumsq += s->sumsq; + if(s->min < d->min || d->n == s->n) + d->min = s->min; + if(s->max > d->max || d->n == s->n) + d->max = s->max; +} + +enum +{ + HSIZE = 31, + MAXCOUNT = 100000000L, +}; + +typedef struct { + int op; + int pc; + long count; + Stat t; /* combined dec and execution time */ +} Istat; + +typedef struct Mstat Mstat; +struct Mstat { + char* name; + char* path; + int ninst; + Istat* inst; + Inst* base; + Mstat* hash; + Mstat* link; +}; + +struct +{ + Mstat* hash[HSIZE]; + Mstat* list; +} vmstat; + +extern struct /* see ../../interp/tab.h:/keywds/ */ +{ + char* name; + int op; + int terminal; +}keywds[]; + +static char * +opname(int op) +{ + char *name; + + if(op < 0 || op >= MAXDIS) + return "Unknown"; + return keywds[op].name; + if(name == 0) + name = ""; + return name; +} + +static void +mreset(void) +{ + Mstat *mp, *link; + + for(mp=vmstat.list; mp; mp=link) { + link = mp->link; + free(mp->inst); + free(mp); + } + vmstat.list = 0; + memset(vmstat.hash, 0, HSIZE*sizeof(Mstat*)); +} + +static ulong +hash(void *s) +{ + ulong sum = 0; + uchar *a = s; + + while(*a) + sum = (sum << 1) + *a++; + return sum%HSIZE; +} + +static Mstat * +mlookup(Module *mod) +{ + Mstat *m; + ulong h; + + for(m=vmstat.hash[hash(mod->name)]; m; m=m->hash) + if(strcmp(m->name, mod->name) == 0 + && strcmp(m->path, mod->path) == 0) { + return m; + } + + + m = malloc(sizeof(Mstat)); + if(m == 0) + return 0; + kstrdup(&m->name, mod->name); + kstrdup(&m->path, mod->path); + m->ninst = mod->nprog; + m->inst = malloc(m->ninst*sizeof(Istat)); + if(m->path == 0 || m->inst == 0) + return 0; + m->base = mod->prog; + m->link = vmstat.list; + vmstat.list = m; + h = hash(m->name); + m->hash = vmstat.hash[h]; + vmstat.hash[h] = m; + return m; +} + +/* interpreted code Dis timing */ +void +bxec(Prog *p) +{ + int op, pc; + vlong t0, t; + Mstat* ms; + Istat* is; + Module *om; + + R = p->R; + R.MP = R.M->MP; + R.IC = p->quanta; + + if(p->kill != nil) { + char *m; + m = p->kill; + p->kill = nil; + error(m); + } + + if(R.M->compiled) + comvec(); + else { + om = 0; + ms = mlookup(R.M->m); + do { + op = R.PC->op; + pc = R.PC-R.M->prog; + if(om != R.M->m) { + om = R.M->m; + ms = mlookup(R.M->m); + } + + t0 = archrdtsc(); + dec[R.PC->add](); + R.PC++; + optab[op](); + t = archrdtsc(); + if(ms) { + is = &ms->inst[pc]; + if(is->count < MAXCOUNT) { + if(is->count++ == 0) { + is->op = op; + is->pc = pc; + } + stat(Inc, &is->t, t-t0); + } + } + if(op==ISPAWN || op==IMSPAWN) { + Prog *new = delruntail(Pdebug); + new->xec = bxec; + addrun(new); + } + } while(--R.IC != 0); + } + + p->R = R; +} + +/* compiled code Dis timing */ + +static struct { /* compiled code timing */ + int set; + int op, pc; /* Dis opcode and program counter */ + vlong t0, t; /* time-in and time-out */ + vlong base; /* cost of doing the timing */ + Mstat *ms; + Module *om; + int timing; /* between "dis timer start" and stop */ +} C; + +enum { Nop = 0 }; /* opcode value for Dis NOP instruction */ +void +dopostcomp(vlong t) +{ + Istat* is; + + C.t = t; + C.set = 0; + if(C.ms != 0) { + is = &C.ms->inst[C.pc]; + if(C.op == Nop) { /* NOP calibration */ + vlong newbase = C.t - C.t0; + if(C.base == 0 || newbase < C.base) + C.base = newbase; + } + if(is->count < MAXCOUNT) { + if(is->count++ == 0) { + is->op = C.op; + is->pc = C.pc; + } + stat(Inc, &is->t, C.t-C.t0/*-C.base*/); + } + } +} + +void +postcomp(void) +{ + vlong t; + + t = archrdtsc(); + if(C.timing == 0 || C.set == 0) + return; + dopostcomp(t); +} + +void +precomp(void) +{ + vlong t; + + t = archrdtsc(); + if(C.timing == 0) + return; + if(C.set) + dopostcomp(t); + C.pc = *(ulong *)R.m; + C.op = *(ulong *)R.s; + if(C.om != R.M->m) { + C.om = R.M->m; + C.ms = mlookup(R.M->m); + } + C.set = 1; + C.t0 = archrdtsc(); +} + +/* standard deviation */ +static vlong +sdev(Stat *s) +{ + extern double sqrt(double); + vlong var; + var = s->sum; + var *= var/s->n; + var = (s->sumsq - var)/s->n; + return (vlong)sqrt(var); +} + +/* + * Use the sequence: + * 1. "timer startclr" or "timer start", then, + * 2. Any DIS operations, and, + * 3. "timer stop", to stop timing. + * 4. Read the results from the data file after: + * a) "timer report" to get module/pc level results, or + * b) "timer summary" to get opcode level results + */ +static long +distime(int report, void *va, long n, ulong offset) +{ + Prog *p; + Mstat *mp; + Istat *ip, *ep; + + if(report) + return rep(va, n, offset); + clear(); + acquire(); + p = currun(); + if(strncmp(va, "timer startclr", 14) == 0) { + mreset(); + memset(&C, 0, sizeof(C)); + C.timing = 1; + p->xec = bxec; + } else if(strncmp(va, "timer start", 11) == 0) { + p->xec = bxec; + C.timing = 1; + } else if(strncmp(va, "timer stop", 10) == 0) { + p->xec = xec; /* bug: stop all xec threads */ + C.timing = 0; + } else if(strncmp(va, "timer nilop", 11) == 0) { + } else if(strncmp(va, "timer report", 12) == 0) /* by address */ + for(mp=vmstat.list; mp; mp=mp->link) { + ep = mp->inst + mp->ninst; + for(ip=mp->inst; ipcount > 0) { + char *mean = ts2str(ip->t.sum/ip->count); + char *min = ts2str(ip->t.min); + char *max = ts2str(ip->t.max); + char *std = ts2str(sdev(&ip->t)); + log("%s %d %s %ld %s %s %s %s\n", mp->path, ip->pc, opname(ip->op), ip->count, mean, min, max, std); + } + } + else if(strncmp(va, "timer summary", 13) == 0) { /* by opcode */ + static Stat T[MAXDIS]; + int i; + + for(i=0; ilink) { + ep = mp->inst + mp->ninst; + for(ip=mp->inst; ipcount > 0) + statinc(&T[ip->op], &ip->t); + } + for(i=0; in > 0) { + mean = ts2str(t->sum/t->n); + min = ts2str(t->min); + max = ts2str(t->max); + std = ts2str(sdev(t)); + } + log("%d %s %lld %s %s %s %s\n", i, opname(i), t->n, mean, min, max, std); + } + } else + n = 0; + R.IC = 1; + release(); + + return n; +} + +/* + * Garbage collection + */ +static int nidle; + +int +idlegc(void *p) +{ + int done; + Prog *head; + vlong t0, t1, tot; + USED(p); + + head = progn(0); /* isched.head */ + done = gccolor + 3; + tot = 0; + while(gccolor < done && gcruns()) { + if(tready(nil)) + break; + t0 = archrdtsc(); + rungc(head); + t1 = archrdtsc(); + t1 -= t0; + tot += t1; +// log(" %.2f", ts2us(t1)); + } + log(" %.2f", ts2us(tot)); + nidle--; + if(nidle == 0) { + log("\n"); + return 1; + } + return 0; +} + +static long +gctime(int report, void *va, long n, ulong offset) +{ + int i; + vlong t0, t1; + Prog *head; + + if(report) + return rep(va, n, offset); + clear(); + acquire(); + head = progn(0); /* isched.head */ +/* + if(strncmp(va, "idle", 4) == 0) { + nidle = 100; + log("GCIDLE:1l:Observation:n:Time:us"); + atidle(idlegc, 0); + } else if(strncmp(va, "stop", 4) == 0) { + atidledont(idlegc, 0); + } else +*/ + if(strncmp(va, "sched", 5) == 0) { + log("GCSCHED:1l:Observation:n:Time:us"); + for(i=0; i<1000; i++) { + t0 = archrdtsc(); + rungc(head); + t1 = archrdtsc(); + log(" %.2f", ts2us(t1-t0)); + release(); + acquire(); + } + log("\n"); + } else if(strncmp(va, "acquire", 7) == 0) { + log("GCACQUIRE:1l:Observation:n:Time:us"); + for(i=0; i<1000; i++) { + t0 = archrdtsc(); + release(); + acquire(); + head = progn(0); /* isched.head */ + rungc(head); + release(); + acquire(); + t1 = archrdtsc(); + log(" %.2f", ts2us(t1-t0)); + } + log("\n"); + } + + release(); + + return n; +} + + +/* + * Request the number of time stamp ticks per millisecond + */ +static long +ms2ts(int report, void *va, long n, ulong offset) +{ + if(report) + return rep(va, n, offset); + log("%.ld\n", MS2TS); + return n; +} + +/* + * test + */ + +static long +test(int report, void *va, long n, ulong offset) +{ +// vlong v; + double d; + if(report) + return rep(va, n, offset); +// v = 5; +// print("vlong %lld\n", v); +// print("before cast\n"); +// d = (double)v; +// print("after cast\n"); +// print("before assign\n"); + d=100.0; + print("after assign\n"); + print("double %f\n", d); +// log("%lld %f\n", v, d); + return n; +} + +/* + * $Bench builtin support + */ +void +Bench_reset(void *) +{ + bus(1); +} + +void +Bench_microsec(void *fp) +{ + F_Bench_microsec *f; + + f = fp; + *f->ret = bus(0); +} + +void +Bench_disablegc(void *) +{ + gclock(); +} + +void +Bench_enablegc(void *) +{ + gcunlock(); +} + + +#define fdchk(x) ((x) == (Bench_FD*)H ? -1 : (x)->fd) +void +Bench_read(void *fp) +{ + int n; + F_Bench_read *f; + vlong usrelease, uskread, usacquire, ussched; + + f = fp; + n = f->n; + if(f->buf == (Array*)H) { + *f->ret = 0; + return; + } + if(n > f->buf->len) + n = f->buf->len; + + bus(1); + release(); + usrelease = bus(0); + *f->ret = kread(fdchk(f->fd), f->buf->data, n); + uskread = bus(0); + acquire(); + usacquire = bus(0); + sched(); + ussched = bus(0); + log("%lld %lld %lld %lud %lld\n", usrelease, uskread, usacquire, m->ticks, ussched); +} + + +/* + * driver support + */ +long (*Test[])(int report, void *va, long n, ulong offset) = { + [None] notest, + [Calibrate] cal, + [Base] base, + [Op] timeop, + [Intr] intrtime, + [Dis] distime, + [Gc] gctime, + [MS2T] ms2ts, + [xTest] test, +}; + +enum { + Benchdirqid, + Benchdataqid, + Benchctlqid, + Benchusqid, +}; +#define Data 0 +static Dirtab benchtab[]={ + ".", {Benchdirqid,0,QTDIR}, 0, 0555, + "bdata", {Benchdataqid}, 0, 0444, + "bctl", {Benchctlqid}, 0, 0660, + "busec", {Benchusqid}, 0, 0660, +}; + +static void +benchreset(void) +{ + builtinmod("$Bench", Benchmodtab); +} + +static Chan* +benchattach(char *spec) +{ + bench.inuse++; + if(bench.inuse == 1) { + bench.bufsz = 100*READSTR; + bench.buf = xalloc(bench.bufsz); + bench.wpos = bench.buf; + if(bench.buf == 0) + error(Enomem); + bench.test = None; + cal(0, 0, 0, 0); + } + return devattach('x', spec); +} + +void +benchshutdown(void) +{ + bench.inuse--; + if(bench.inuse == 0) + xfree(bench.buf); +} + +static Walkqid* +benchwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, benchtab, nelem(benchtab), devgen); +} + +static Chan* +benchopen(Chan *c, int omode) +{ + if(c->qid.path == Benchdirqid){ + if(omode != OREAD) + error(Eperm); + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static int +benchstat(Chan *c, uchar *dp, int n) +{ + switch((ulong)c->qid.path){ + case Benchdataqid: + benchtab[Data].length = bench.wpos - bench.buf; + } + return devstat(c, dp, n, benchtab, nelem(benchtab), devgen); +} + +static void +benchclose(Chan*) +{ +} + +static long +benchread(Chan *c, void *buf, long n, vlong offset) +{ + vlong us; + char tmp[64]; + + switch((ulong)c->qid.path){ + case Benchdirqid: + return devdirread(c, buf, n, benchtab, nelem(benchtab), devgen); + + case Benchdataqid: + return Test[bench.test](1, buf, n, offset); + + case Benchusqid: + us = archrdtsc(); + us /= US2TS; + snprint(tmp, sizeof(tmp), "%.lld", us); + return readstr(0, buf, n, tmp); + default: + n = 0; + break; + } + return n; +} + +static long +benchwrite(Chan *c, void *buf, long n, vlong offset) +{ + int argn = n; + + switch((ulong)c->qid.path){ + case Benchctlqid: + bench.test = None; + memset((char *)bench.buf, 0, bench.bufsz); + bench.wpos = bench.buf; + if(strncmp(buf, "test", 4) == 0) + bench.test = xTest; + else if(strncmp(buf, "calibrate", 9) == 0) + bench.test = Calibrate; + else if(strncmp(buf, "base", 4) == 0) + bench.test = Base; + else if(strncmp(buf, "intr", 4) == 0) + bench.test = Intr; + else if(strncmp(buf, "op ", 3) == 0) { + bench.test = Op; + buf = (char *)buf + 3; + argn -= 3; + } else if(strncmp(buf, "dis ", 4) == 0) { + bench.test = Dis; + buf = (char *)buf + 4; + argn -= 4; + } else if(strncmp(buf, "gc ", 3) == 0) { + bench.test = Gc; + buf = (char *)buf + 3; + argn -= 3; + } else if(strncmp(buf, "ms2ts", 5) == 0) + bench.test = MS2T; + else + error(Ebadctl); + Test[bench.test](0, buf, argn, offset); + break; + case Benchusqid: + bench.tickstart = archrdtsc(); + break; + default: + error(Ebadusefd); + } + return n; +} + +Dev benchdevtab = { + 'x', + "bench", + + benchreset, + devinit, + benchshutdown, + benchattach, + benchwalk, + benchstat, + benchopen, + devcreate, + benchclose, + benchread, + devbread, + benchwrite, + devbwrite, + devremove, + devwstat, + +}; diff --git a/os/port/devboot.c b/os/port/devboot.c new file mode 100644 index 00000000..66babe38 --- /dev/null +++ b/os/port/devboot.c @@ -0,0 +1,150 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +enum{ + Qdir, + Qboot, + Qmem, + Qkexec, + + Maxkexec = 1536*1024, +}; + +static +Dirtab bootdir[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "boot", {Qboot}, 0, 0220, + "mem", {Qmem}, 0, 0660, + "kexec", {Qkexec}, 0, 0220, +}; + +static Chan* +bootattach(char *spec) +{ + return devattach('B', spec); +} + +static Walkqid* +bootwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, bootdir, nelem(bootdir), devgen); +} + +static int +bootstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, bootdir, nelem(bootdir), devgen); +} + +static Chan* +bootopen(Chan *c, int omode) +{ + if (c->qid.path == Qkexec) { + c->aux = malloc(Maxkexec); + print("kexec buffer: %lux\n", c->aux); + } + return devopen(c, omode, bootdir, nelem(bootdir), devgen); +} + +static void +bootclose(Chan *c) +{ + if(c->qid.path == Qkexec && c->aux != nil){ + print("exec new kernel @%lux\n", (ulong)c->aux); + splhi(); + segflush(c->aux, 64*1024); + gotopc((ulong)c->aux); + } +} + +static long +bootread(Chan *c, void *buf, long n, vlong offset) +{ + switch((ulong)c->qid.path){ + + case Qdir: + return devdirread(c, buf, n, bootdir, nelem(bootdir), devgen); + + case Qmem: + /* kernel memory */ + if(offset>=KZERO && offset KZERO+conf.npage*BY2PG) + n = KZERO+conf.npage*BY2PG - offset; + memmove(buf, (char*)offset, n); + return n; + } + error(Ebadarg); + } + + error(Egreg); + return 0; /* not reached */ +} + +static long +bootwrite(Chan *c, void *buf, long n, vlong offset) +{ + ulong pc; + uchar *p; + + switch((ulong)c->qid.path){ + case Qmem: + /* kernel memory */ + if(offset>=KZERO && offset KZERO+conf.npage*BY2PG) + n = KZERO+conf.npage*BY2PG - offset; + memmove((char*)offset, buf, n); + segflush((void*)offset, n); + return n; + } + error(Ebadarg); + + case Qboot: + p = (uchar*)buf; + pc = (((((p[0]<<8)|p[1])<<8)|p[2])<<8)|p[3]; + if(pc < KZERO || pc >= KZERO+conf.npage*BY2PG) + error(Ebadarg); + splhi(); + segflush((void*)pc, 64*1024); + gotopc(pc); + + case Qkexec: + print("."); + if(c->aux != nil && offset <= Maxkexec){ + if(offset+n > Maxkexec) + n = Maxkexec - offset; + memmove((char*)c->aux+offset, buf, n); + segflush((char*)c->aux+offset, n); + return n; + } + free(c->aux); + c->aux = nil; + error(Ebadarg); + } + error(Ebadarg); + return 0; /* not reached */ +} + +Dev bootdevtab = { + 'B', + "boot", + + devreset, + devinit, + devshutdown, + bootattach, + bootwalk, + bootstat, + bootopen, + devcreate, + bootclose, + bootread, + devbread, + bootwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devbridge.c b/os/port/devbridge.c new file mode 100644 index 00000000..9237728e --- /dev/null +++ b/os/port/devbridge.c @@ -0,0 +1,1206 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/netif.h" +#include "../port/error.h" + +typedef struct Bridge Bridge; +typedef struct Port Port; +typedef struct Centry Centry; +typedef struct Iphdr Iphdr; +typedef struct Tcphdr Tcphdr; + +enum +{ + Qtopdir= 1, /* top level directory */ + + Qbridgedir, /* bridge* directory */ + Qbctl, + Qstats, + Qcache, + Qlog, + + Qportdir, /* directory for a protocol */ + Qpctl, + Qlocal, + Qstatus, + + MaxQ, + + Maxbridge= 4, + Maxport= 128, // power of 2 + CacheHash= 257, // prime + CacheLook= 5, // how many cache entries to examine + CacheSize= (CacheHash+CacheLook-1), + CacheTimeout= 5*60, // timeout for cache entry in seconds + + TcpMssMax = 1300, // max desirable Tcp MSS value + TunnelMtu = 1400, +}; + +static Dirtab bridgedirtab[]={ + "ctl", {Qbctl}, 0, 0666, + "stats", {Qstats}, 0, 0444, + "cache", {Qcache}, 0, 0444, + "log", {Qlog}, 0, 0666, +}; + +static Dirtab portdirtab[]={ + "ctl", {Qpctl}, 0, 0666, + "local", {Qlocal}, 0, 0444, + "status", {Qstatus}, 0, 0444, +}; + +enum { + Logcache= (1<<0), + Logmcast= (1<<1), +}; + +// types of interfaces +enum +{ + Tether, + Ttun, +}; + +static Logflag logflags[] = +{ + { "cache", Logcache, }, + { "multicast", Logmcast, }, + { nil, 0, }, +}; + +static Dirtab *dirtab[MaxQ]; + +#define TYPE(x) (((ulong)(x).path) & 0xff) +#define PORT(x) ((((ulong)(x).path) >> 8)&(Maxport-1)) +#define QID(x, y) (((x)<<8) | (y)) + +struct Centry +{ + uchar d[Eaddrlen]; + int port; + long expire; // entry expires this number of seconds after bootime + long src; + long dst; +}; + +struct Bridge +{ + QLock; + int nport; + Port *port[Maxport]; + Centry cache[CacheSize]; + ulong hit; + ulong miss; + ulong copy; + long delay0; // constant microsecond delay per packet + long delayn; // microsecond delay per byte + int tcpmss; // modify tcpmss value + + Log; +}; + +struct Port +{ + int id; + Bridge *bridge; + int ref; + int closed; + + Chan *data[2]; // channel to data + + int mcast; // send multi cast packets + + Proc *readp; // read proc + + // the following uniquely identifies the port + int type; + char name[KNAMELEN]; + + // owner hash - avoids bind/unbind races + ulong ownhash; + + // various stats + int in; // number of packets read + int inmulti; // multicast or broadcast + int inunknown; // unknown address + int out; // number of packets read + int outmulti; // multicast or broadcast + int outunknown; // unknown address + int outfrag; // fragmented the packet + int nentry; // number of cache entries for this port +}; + +enum { + IP_VER = 0x40, /* Using IP version 4 */ + IP_HLEN = 0x05, /* Header length in characters */ + IP_DF = 0x4000, /* Don't fragment */ + IP_MF = 0x2000, /* More fragments */ + IP_MAX = (32*1024), /* Maximum Internet packet size */ + IP_TCPPROTO = 6, + EOLOPT = 0, + NOOPOPT = 1, + MSSOPT = 2, + MSS_LENGTH = 4, /* Mean segment size */ + SYN = 0x02, /* Pkt. is synchronise */ + IPHDR = 20, /* sizeof(Iphdr) */ +}; + +struct Iphdr +{ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* ip->identification */ + uchar frag[2]; /* Fragment information */ + uchar ttl; /* Time to live */ + uchar proto; /* Protocol */ + uchar cksum[2]; /* Header checksum */ + uchar src[4]; /* IP source */ + uchar dst[4]; /* IP destination */ +}; + +struct Tcphdr +{ + uchar sport[2]; + uchar dport[2]; + uchar seq[4]; + uchar ack[4]; + uchar flag[2]; + uchar win[2]; + uchar cksum[2]; + uchar urg[2]; +}; + +static Bridge bridgetab[Maxbridge]; + +static int m2p[] = { + [OREAD] 4, + [OWRITE] 2, + [ORDWR] 6 +}; + +static int bridgegen(Chan *c, char*, Dirtab*, int, int s, Dir *dp); +static void portbind(Bridge *b, int argc, char *argv[]); +static void portunbind(Bridge *b, int argc, char *argv[]); +static void etherread(void *a); +static char *cachedump(Bridge *b); +static void portfree(Port *port); +static void cacheflushport(Bridge *b, int port); +static void etherwrite(Port *port, Block *bp); + +extern ulong parseip(uchar*, char*); +extern ushort ipcsum(uchar *addr); + +static void +bridgeinit(void) +{ + int i; + Dirtab *dt; + // setup dirtab with non directory entries + for(i=0; iqid)] = dt; + } + for(i=0; iqid)] = dt; + } +} + +static Chan* +bridgeattach(char* spec) +{ + Chan *c; + int dev; + + dev = atoi(spec); + if(dev<0 || dev >= Maxbridge) + error("bad specification"); + + c = devattach('B', spec); + mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR); + c->dev = dev; + + return c; +} + +static Walkqid* +bridgewalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, (Dirtab*)0, 0, bridgegen); +} + +static int +bridgestat(Chan* c, uchar* db, int n) +{ + return devstat(c, db, n, (Dirtab *)0, 0L, bridgegen); +} + +static Chan* +bridgeopen(Chan* c, int omode) +{ + int perm; + Bridge *b; + + omode &= 3; + perm = m2p[omode]; + USED(perm); + + b = bridgetab + c->dev; + USED(b); + + switch(TYPE(c->qid)) { + default: + break; + case Qlog: + logopen(b); + break; + case Qcache: + c->aux = cachedump(b); + break; + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +bridgeclose(Chan* c) +{ + Bridge *b = bridgetab + c->dev; + + switch(TYPE(c->qid)) { + case Qcache: + if(c->flag & COPEN) + free(c->aux); + break; + case Qlog: + if(c->flag & COPEN) + logclose(b); + break; + } +} + +static long +bridgeread(Chan *c, void *a, long n, vlong off) +{ + char buf[256]; + Bridge *b = bridgetab + c->dev; + Port *port; + int i, ingood, outgood; + + USED(off); + switch(TYPE(c->qid)) { + default: + error(Eperm); + case Qtopdir: + case Qbridgedir: + case Qportdir: + return devdirread(c, a, n, 0, 0, bridgegen); + case Qlog: + return logread(b, a, off, n); + case Qstatus: + qlock(b); + port = b->port[PORT(c->qid)]; + if(port == 0) + strcpy(buf, "unbound\n"); + else { + i = 0; + switch(port->type) { + default: panic("bridgeread: unknown port type: %d", port->type); + case Tether: + i += snprint(buf+i, sizeof(buf)-i, "ether %s: ", port->name); + break; + case Ttun: + i += snprint(buf+i, sizeof(buf)-i, "tunnel %s: ", port->name); + break; + } + ingood = port->in-port->inmulti-port->inunknown; + outgood = port->out-port->outmulti-port->outunknown; + i += snprint(buf+i, sizeof(buf)-i, "in=%d(%d:%d:%d) out=%d(%d:%d:%d:%d)\n", + port->in, ingood, port->inmulti, port->inunknown, + port->out, outgood, port->outmulti, port->outunknown, port->outfrag); + USED(i); + } + n = readstr(off, a, n, buf); + qunlock(b); + return n; + case Qbctl: + snprint(buf, sizeof(buf), "%s tcpmss\ndelay %ld %ld\n", b->tcpmss ? "set" : "clear", + b->delay0, b->delayn); + n = readstr(off, a, n, buf); + return n; + case Qcache: + n = readstr(off, a, n, c->aux); + return n; + case Qstats: + snprint(buf, sizeof(buf), "hit=%uld miss=%uld copy=%uld\n", + b->hit, b->miss, b->copy); + n = readstr(off, a, n, buf); + return n; + } +} + +static void +bridgeoption(Bridge *b, char *option, int value) +{ + if(strcmp(option, "tcpmss") == 0) + b->tcpmss = value; + else + error("unknown bridge option"); +} + + +static long +bridgewrite(Chan *c, void *a, long n, vlong off) +{ + Bridge *b = bridgetab + c->dev; + Cmdbuf *cb; + char *arg0; + char *p; + + USED(off); + switch(TYPE(c->qid)) { + default: + error(Eperm); + case Qbctl: + cb = parsecmd(a, n); + qlock(b); + if(waserror()) { + qunlock(b); + free(cb); + nexterror(); + } + if(cb->nf == 0) + error("short write"); + arg0 = cb->f[0]; + if(strcmp(arg0, "bind") == 0) { + portbind(b, cb->nf-1, cb->f+1); + } else if(strcmp(arg0, "unbind") == 0) { + portunbind(b, cb->nf-1, cb->f+1); + } else if(strcmp(arg0, "cacheflush") == 0) { + logb(b, Logcache, "cache flush\n"); + memset(b->cache, 0, CacheSize*sizeof(Centry)); + } else if(strcmp(arg0, "set") == 0) { + if(cb->nf != 2) + error("usage: set option"); + bridgeoption(b, cb->f[1], 1); + } else if(strcmp(arg0, "clear") == 0) { + if(cb->nf != 2) + error("usage: clear option"); + bridgeoption(b, cb->f[1], 0); + } else if(strcmp(arg0, "delay") == 0) { + if(cb->nf != 3) + error("usage: delay delay0 delayn"); + b->delay0 = strtol(cb->f[1], nil, 10); + b->delayn = strtol(cb->f[2], nil, 10); + } else + error("unknown control request"); + poperror(); + qunlock(b); + free(cb); + return n; + case Qlog: + cb = parsecmd(a, n); + p = logctl(b, cb->nf, cb->f, logflags); + free(cb); + if(p != nil) + error(p); + return n; + } +} + +static int +bridgegen(Chan *c, char *, Dirtab*, int, int s, Dir *dp) +{ + Bridge *b = bridgetab + c->dev; + int type = TYPE(c->qid); + Dirtab *dt; + Qid qid; + + if(s == DEVDOTDOT){ + switch(TYPE(c->qid)){ + case Qtopdir: + case Qbridgedir: + snprint(up->genbuf, sizeof(up->genbuf), "#B%ld", c->dev); + mkqid(&qid, Qtopdir, 0, QTDIR); + devdir(c, qid, up->genbuf, 0, eve, 0555, dp); + break; + case Qportdir: + snprint(up->genbuf, sizeof(up->genbuf), "bridge%ld", c->dev); + mkqid(&qid, Qbridgedir, 0, QTDIR); + devdir(c, qid, up->genbuf, 0, eve, 0555, dp); + break; + default: + panic("bridgewalk %llux", c->qid.path); + } + return 1; + } + + switch(type) { + default: + // non directory entries end up here + if(c->qid.type & QTDIR) + panic("bridgegen: unexpected directory"); + if(s != 0) + return -1; + dt = dirtab[TYPE(c->qid)]; + if(dt == nil) + panic("bridgegen: unknown type: %lud", TYPE(c->qid)); + devdir(c, c->qid, dt->name, dt->length, eve, dt->perm, dp); + return 1; + case Qtopdir: + if(s != 0) + return -1; + snprint(up->genbuf, sizeof(up->genbuf), "bridge%ld", c->dev); + mkqid(&qid, QID(0, Qbridgedir), 0, QTDIR); + devdir(c, qid, up->genbuf, 0, eve, 0555, dp); + return 1; + case Qbridgedir: + if(sqid, dt->name, dt->length, eve, dt->perm, dp); + return 1; + } + s -= nelem(bridgedirtab); + if(s >= b->nport) + return -1; + mkqid(&qid, QID(s, Qportdir), 0, QTDIR); + snprint(up->genbuf, sizeof(up->genbuf), "%d", s); + devdir(c, qid, up->genbuf, 0, eve, 0555, dp); + return 1; + case Qportdir: + if(s>=nelem(portdirtab)) + return -1; + dt = portdirtab+s; + mkqid(&qid, QID(PORT(c->qid),TYPE(dt->qid)), 0, QTFILE); + devdir(c, qid, dt->name, dt->length, eve, dt->perm, dp); + return 1; + } +} + +// also in netif.c +static int +parseaddr(uchar *to, char *from, int alen) +{ + char nip[4]; + char *p; + int i; + + p = from; + for(i = 0; i < alen; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +// assumes b is locked +static void +portbind(Bridge *b, int argc, char *argv[]) +{ + Port *port; + char path[8*KNAMELEN]; + char buf[100]; + char *dev, *dev2=nil, *p; + Chan *ctl; + int type=0, i, n; + char *usage = "usage: bind ether|tunnel name ownhash dev [dev2]"; + char name[KNAMELEN]; + ulong ownhash; + + memset(name, 0, KNAMELEN); + if(argc < 4) + error(usage); + if(strcmp(argv[0], "ether") == 0) { + if(argc != 4) + error(usage); + type = Tether; + strncpy(name, argv[1], KNAMELEN); + name[KNAMELEN-1] = 0; +// parseaddr(addr, argv[1], Eaddrlen); + } else if(strcmp(argv[0], "tunnel") == 0) { + if(argc != 5) + error(usage); + type = Ttun; + strncpy(name, argv[1], KNAMELEN); + name[KNAMELEN-1] = 0; +// parseip(addr, argv[1]); + dev2 = argv[4]; + } else + error(usage); + ownhash = atoi(argv[2]); + dev = argv[3]; + for(i=0; inport; i++) { + port = b->port[i]; + if(port != nil) + if(port->type == type) + if(memcmp(port->name, name, KNAMELEN) == 0) + error("port in use"); + } + for(i=0; iport[i] == nil) + break; + if(i == Maxport) + error("no more ports"); + port = smalloc(sizeof(Port)); + port->ref = 1; + port->id = i; + port->ownhash = ownhash; + + if(waserror()) { + portfree(port); + nexterror(); + } + port->type = type; + memmove(port->name, name, KNAMELEN); + switch(port->type) { + default: panic("portbind: unknown port type: %d", type); + case Tether: + snprint(path, sizeof(path), "%s/clone", dev); + ctl = namec(path, Aopen, ORDWR, 0); + if(waserror()) { + cclose(ctl); + nexterror(); + } + // check addr? + + // get directory name + n = devtab[ctl->type]->read(ctl, buf, sizeof(buf), 0); + buf[n] = 0; + for(p = buf; *p == ' '; p++) + ; + snprint(path, sizeof(path), "%s/%lud/data", dev, strtoul(p, 0, 0)); + + // setup connection to be promiscuous + snprint(buf, sizeof(buf), "connect -1"); + devtab[ctl->type]->write(ctl, buf, strlen(buf), 0); + snprint(buf, sizeof(buf), "promiscuous"); + devtab[ctl->type]->write(ctl, buf, strlen(buf), 0); + snprint(buf, sizeof(buf), "bridge"); + devtab[ctl->type]->write(ctl, buf, strlen(buf), 0); + + // open data port + port->data[0] = namec(path, Aopen, ORDWR, 0); + // dup it + incref(port->data[0]); + port->data[1] = port->data[0]; + + poperror(); + cclose(ctl); + + break; + case Ttun: + port->data[0] = namec(dev, Aopen, OREAD, 0); + port->data[1] = namec(dev2, Aopen, OWRITE, 0); + break; + } + + poperror(); + + // commited to binding port + b->port[port->id] = port; + port->bridge = b; + if(b->nport <= port->id) + b->nport = port->id+1; + + // assumes kproc always succeeds + kproc("etherread", etherread, port, 0); // poperror must be next + port->ref++; +} + +// assumes b is locked +static void +portunbind(Bridge *b, int argc, char *argv[]) +{ + Port *port=nil; + int type=0, i; + char *usage = "usage: unbind ether|tunnel addr [ownhash]"; + char name[KNAMELEN]; + ulong ownhash; + + memset(name, 0, KNAMELEN); + if(argc < 2 || argc > 3) + error(usage); + if(strcmp(argv[0], "ether") == 0) { + type = Tether; + strncpy(name, argv[1], KNAMELEN); + name[KNAMELEN-1] = 0; +// parseaddr(addr, argv[1], Eaddrlen); + } else if(strcmp(argv[0], "tunnel") == 0) { + type = Ttun; + strncpy(name, argv[1], KNAMELEN); + name[KNAMELEN-1] = 0; +// parseip(addr, argv[1]); + } else + error(usage); + if(argc == 3) + ownhash = atoi(argv[2]); + else + ownhash = 0; + for(i=0; inport; i++) { + port = b->port[i]; + if(port != nil) + if(port->type == type) + if(memcmp(port->name, name, KNAMELEN) == 0) + break; + } + if(i == b->nport) + error("port not found"); + if(ownhash != 0 && port->ownhash != 0 && ownhash != port->ownhash) + error("bad owner hash"); + + port->closed = 1; + b->port[i] = nil; // port is now unbound + cacheflushport(b, i); + + // try and stop reader + if(port->readp) + postnote(port->readp, 1, "unbind", 0); + portfree(port); +} + +// assumes b is locked +static Centry * +cachelookup(Bridge *b, uchar d[Eaddrlen]) +{ + int i; + uint h; + Centry *p; + long sec; + + // dont cache multicast or broadcast + if(d[0] & 1) + return 0; + + h = 0; + for(i=0; icache + h; + sec = TK2SEC(m->ticks); + for(i=0; id, Eaddrlen) == 0) { + p->dst++; + if(sec >= p->expire) { + logb(b, Logcache, "expired cache entry: %E %d\n", + d, p->port); + return nil; + } + p->expire = sec + CacheTimeout; + return p; + } + } + logb(b, Logcache, "cache miss: %E\n", d); + return nil; +} + +// assumes b is locked +static void +cacheupdate(Bridge *b, uchar d[Eaddrlen], int port) +{ + int i; + uint h; + Centry *p, *pp; + long sec; + + // dont cache multicast or broadcast + if(d[0] & 1) { + logb(b, Logcache, "bad source address: %E\n", d); + return; + } + + h = 0; + for(i=0; icache + h; + pp = p; + sec = p->expire; + + // look for oldest entry + for(i=0; id, d, Eaddrlen) == 0) { + p->expire = TK2SEC(m->ticks) + CacheTimeout; + if(p->port != port) { + logb(b, Logcache, "NIC changed port %d->%d: %E\n", + p->port, port, d); + p->port = port; + } + p->src++; + return; + } + if(p->expire < sec) { + sec = p->expire; + pp = p; + } + } + if(pp->expire != 0) + logb(b, Logcache, "bumping from cache: %E %d\n", pp->d, pp->port); + pp->expire = TK2SEC(m->ticks) + CacheTimeout; + memmove(pp->d, d, Eaddrlen); + pp->port = port; + pp->src = 1; + pp->dst = 0; + logb(b, Logcache, "adding to cache: %E %d\n", pp->d, pp->port); +} + +// assumes b is locked +static void +cacheflushport(Bridge *b, int port) +{ + Centry *ce; + int i; + + ce = b->cache; + for(i=0; iport != port) + continue; + memset(ce, 0, sizeof(Centry)); + } +} + +static char * +cachedump(Bridge *b) +{ + int i, n; + long sec, off; + char *buf, *p, *ep; + Centry *ce; + char c; + + qlock(b); + if(waserror()) { + qunlock(b); + nexterror(); + } + sec = TK2SEC(m->ticks); + n = 0; + for(i=0; icache[i].expire != 0) + n++; + + n *= 51; // change if print format is changed + n += 10; // some slop at the end + buf = malloc(n); + p = buf; + ep = buf + n; + ce = b->cache; + off = seconds() - sec; + for(i=0; iexpire == 0) + continue; + c = (sec < ce->expire)?'v':'e'; + p += snprint(p, ep-p, "%E %2d %10ld %10ld %10ld %c\n", ce->d, + ce->port, ce->src, ce->dst, ce->expire+off, c); + } + *p = 0; + poperror(); + qunlock(b); + + return buf; +} + + + +// assumes b is locked +static void +ethermultiwrite(Bridge *b, Block *bp, Port *port) +{ + Port *oport; + Block *bp2; + Etherpkt *ep; + int i, mcast, bcast; + static uchar bcastaddr[Eaddrlen] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + + if(waserror()) { + if(bp) + freeb(bp); + nexterror(); + } + + ep = (Etherpkt*)bp->rp; + mcast = ep->d[0] & 1; + if(mcast) + bcast = memcmp(ep->d, bcastaddr, Eaddrlen) == 0; + else + bcast = 0; + + oport = nil; + for(i=0; inport; i++) { + if(i == port->id || b->port[i] == nil) + continue; + if(mcast && !bcast && !b->port[i]->mcast) + continue; + if(mcast) + b->port[i]->outmulti++; + else + b->port[i]->outunknown++; + + // delay one so that the last write does not copy + if(oport != nil) { + b->copy++; + bp2 = copyblock(bp, blocklen(bp)); + if(!waserror()) { + etherwrite(oport, bp2); + poperror(); + } + } + oport = b->port[i]; + } + + // last write free block + if(oport) { + bp2 = bp; bp = nil; USED(bp); + if(!waserror()) { + etherwrite(oport, bp2); + poperror(); + } + } else + freeb(bp); + + poperror(); +} + +static void +tcpmsshack(Etherpkt *epkt, int n) +{ + int hl; + Iphdr *iphdr; + Tcphdr *tcphdr; + ulong mss; + ulong cksum; + int optlen; + uchar *optr; + + // check it is an ip packet + if(nhgets(epkt->type) != 0x800) + return; + iphdr = (Iphdr*)(epkt->data); + n -= ETHERHDRSIZE; + if(n < IPHDR) + return; + + // check it is ok IP packet + if(iphdr->vihl != (IP_VER|IP_HLEN)) { + hl = (iphdr->vihl&0xF)<<2; + if((iphdr->vihl&0xF0) != IP_VER || hl < (IP_HLEN<<2)) + return; + } else + hl = IP_HLEN<<2; + + // check TCP + if(iphdr->proto != IP_TCPPROTO) + return; + n -= hl; + if(n < sizeof(Tcphdr)) + return; + tcphdr = (Tcphdr*)((uchar*)(iphdr) + hl); + // MSS can only appear in SYN packet + if(!(tcphdr->flag[1] & SYN)) + return; + hl = (tcphdr->flag[0] & 0xf0)>>2; + if(n < hl) + return; + + // check for MSS option + optr = (uchar*)(tcphdr) + sizeof(Tcphdr); + n = hl - sizeof(Tcphdr); + for(;;) { + if(n <= 0 || *optr == EOLOPT) + return; + if(*optr == NOOPOPT) { + n--; + optr++; + continue; + } + optlen = optr[1]; + if(optlen < 2 || optlen > n) + return; + if(*optr == MSSOPT && optlen == MSS_LENGTH) + break; + n -= optlen; + optr += optlen; + } + + mss = nhgets(optr+2); + if(mss <= TcpMssMax) + return; + // fit checksum + cksum = nhgets(tcphdr->cksum); + if(optr-(uchar*)tcphdr & 1) { +print("tcpmsshack: odd alignment!\n"); + // odd alignments are a pain + cksum += nhgets(optr+1); + cksum -= (optr[1]<<8)|(TcpMssMax>>8); + cksum += (cksum>>16); + cksum &= 0xffff; + cksum += nhgets(optr+3); + cksum -= ((TcpMssMax&0xff)<<8)|optr[4]; + cksum += (cksum>>16); + } else { + cksum += mss; + cksum -= TcpMssMax; + cksum += (cksum>>16); + } + hnputs(tcphdr->cksum, cksum); + hnputs(optr+2, TcpMssMax); +} + +/* + * process to read from the ethernet + */ +static void +etherread(void *a) +{ + Port *port = a; + Bridge *b = port->bridge; + Block *bp, *bp2; + Etherpkt *ep; + Centry *ce; + long md; + + qlock(b); + port->readp = up; /* hide identity under a rock for unbind */ + + while(!port->closed){ + // release lock to read - error means it is time to quit + qunlock(b); + if(waserror()) { +print("etherread read error: %s\n", up->env->errstr); + qlock(b); + break; + } +if(0)print("devbridge: etherread: reading\n"); + bp = devtab[port->data[0]->type]->bread(port->data[0], ETHERMAXTU, 0); +if(0)print("devbridge: etherread: blocklen = %d\n", blocklen(bp)); + poperror(); + qlock(b); + if(bp == nil || port->closed) + break; + if(waserror()) { +//print("etherread bridge error\n"); + if(bp) + freeb(bp); + continue; + } + if(blocklen(bp) < ETHERMINTU) + error("short packet"); + port->in++; + + ep = (Etherpkt*)bp->rp; + cacheupdate(b, ep->s, port->id); + if(b->tcpmss) + tcpmsshack(ep, BLEN(bp)); + + /* + * delay packets to simulate a slow link + */ + if(b->delay0 || b->delayn){ + md = b->delay0 + b->delayn * BLEN(bp); + if(md > 0) + microdelay(md); + } + + if(ep->d[0] & 1) { + logb(b, Logmcast, "multicast: port=%d src=%E dst=%E type=%#.4ux\n", + port->id, ep->s, ep->d, (ep->type[0]<<8)|ep->type[1] ); + port->inmulti++; + bp2 = bp; bp = nil; + ethermultiwrite(b, bp2, port); + } else { + ce = cachelookup(b, ep->d); + if(ce == nil) { + b->miss++; + port->inunknown++; + bp2 = bp; bp = nil; + ethermultiwrite(b, bp2, port); + }else if(ce->port != port->id){ + b->hit++; + bp2 = bp; bp = nil; + etherwrite(b->port[ce->port], bp2); + } + } + + poperror(); + if(bp) + freeb(bp); + } +//print("etherread: trying to exit\n"); + port->readp = nil; + portfree(port); + qunlock(b); + pexit("hangup", 1); +} + +static int +fragment(Etherpkt *epkt, int n) +{ + Iphdr *iphdr; + + if(n <= TunnelMtu) + return 0; + + // check it is an ip packet + if(nhgets(epkt->type) != 0x800) + return 0; + iphdr = (Iphdr*)(epkt->data); + n -= ETHERHDRSIZE; + if(n < IPHDR) + return 0; + + // check it is ok IP packet - I don't handle IP options for the momment + if(iphdr->vihl != (IP_VER|IP_HLEN)) + return 0; + + // check for don't fragment + if(iphdr->frag[0] & (IP_DF>>8)) + return 0; + + // check for short block + if(nhgets(iphdr->length) > n) + return 0; + + return 1; +} + + +static void +etherwrite(Port *port, Block *bp) +{ + Iphdr *eh, *feh; + Etherpkt *epkt; + int n, lid, len, seglen, chunk, dlen, blklen, offset, mf; + Block *xp, *nb; + ushort fragoff, frag; + + port->out++; + epkt = (Etherpkt*)bp->rp; + n = blocklen(bp); + if(port->type != Ttun || !fragment(epkt, n)) { + devtab[port->data[1]->type]->bwrite(port->data[1], bp, 0); + return; + } + port->outfrag++; + if(waserror()){ + freeblist(bp); + nexterror(); + } + + seglen = (TunnelMtu - ETHERHDRSIZE - IPHDR) & ~7; + eh = (Iphdr*)(epkt->data); + len = nhgets(eh->length); + frag = nhgets(eh->frag); + mf = frag & IP_MF; + frag <<= 3; + dlen = len - IPHDR; + xp = bp; + lid = nhgets(eh->id); + offset = ETHERHDRSIZE+IPHDR; + while(xp != nil && offset && offset >= BLEN(xp)) { + offset -= BLEN(xp); + xp = xp->next; + } + xp->rp += offset; + +if(0) print("seglen=%d, dlen=%d, mf=%x, frag=%d\n", seglen, dlen, mf, frag); + for(fragoff = 0; fragoff < dlen; fragoff += seglen) { + nb = allocb(ETHERHDRSIZE+IPHDR+seglen); + + feh = (Iphdr*)(nb->wp+ETHERHDRSIZE); + + memmove(nb->wp, epkt, ETHERHDRSIZE+IPHDR); + nb->wp += ETHERHDRSIZE+IPHDR; + + if((fragoff + seglen) >= dlen) { + seglen = dlen - fragoff; + hnputs(feh->frag, (frag+fragoff)>>3 | mf); + } + else + hnputs(feh->frag, (frag+fragoff>>3) | IP_MF); + + hnputs(feh->length, seglen + IPHDR); + hnputs(feh->id, lid); + + /* Copy up the data area */ + chunk = seglen; + while(chunk) { + blklen = chunk; + if(BLEN(xp) < chunk) + blklen = BLEN(xp); + memmove(nb->wp, xp->rp, blklen); + nb->wp += blklen; + xp->rp += blklen; + chunk -= blklen; + if(xp->rp == xp->wp) + xp = xp->next; + } + + feh->cksum[0] = 0; + feh->cksum[1] = 0; + hnputs(feh->cksum, ipcsum(&feh->vihl)); + + // don't generate small packets + if(BLEN(nb) < ETHERMINTU) + nb->wp = nb->rp + ETHERMINTU; + devtab[port->data[1]->type]->bwrite(port->data[1], nb, 0); + } + poperror(); + freeblist(bp); +} + +// hold b lock +static void +portfree(Port *port) +{ + port->ref--; + if(port->ref < 0) + panic("portfree: bad ref"); + if(port->ref > 0) + return; + + if(port->data[0]) + cclose(port->data[0]); + if(port->data[1]) + cclose(port->data[1]); + memset(port, 0, sizeof(Port)); + free(port); +} + +Dev bridgedevtab = { + 'B', + "bridge", + + devreset, + bridgeinit, + devshutdown, + bridgeattach, + bridgewalk, + bridgestat, + bridgeopen, + devcreate, + bridgeclose, + bridgeread, + devbread, + bridgewrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devcap.c b/os/port/devcap.c new file mode 100644 index 00000000..64232c57 --- /dev/null +++ b/os/port/devcap.c @@ -0,0 +1,248 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "mp.h" +#include "libsec.h" + +/* + * Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved. + */ + +enum { + Captimeout = 15, /* seconds until expiry */ + Capidletime = 60 /* idle seconds before capwatch exits */ +}; + +typedef struct Caps Caps; +struct Caps +{ + uchar hash[SHA1dlen]; + ulong time; + Caps* next; +}; + +struct { + QLock l; + Caps* caps; + int kpstarted; +} allcaps; + +enum { + Qdir, + Qhash, + Quse +}; + +static Dirtab capdir[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "capuse", {Quse, 0}, 0, 0222, + "caphash", {Qhash, 0}, 0, 0200, +}; + +static int ncapdir = nelem(capdir); + +static void +capwatch(void *a) +{ + Caps *c, **l; + int idletime; + + USED(a); + idletime = 0; + for(;;){ + tsleep(&up->sleep, return0, nil, 30*1000); + qlock(&allcaps.l); + for(l = &allcaps.caps; (c = *l) != nil;) + if(++c->time > Captimeout){ + *l = c->next; + free(c); + }else + l = &c->next; + if(allcaps.caps == nil){ + if(++idletime > Capidletime){ + allcaps.kpstarted = 0; + qunlock(&allcaps.l); + pexit("", 0); + } + }else + idletime = 0; + qunlock(&allcaps.l); + } +} + +static Chan * +capattach(char *spec) +{ + return devattach(L'¤', spec); +} + +static Walkqid* +capwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, capdir, nelem(capdir), devgen); +} + +static int +capstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, capdir, nelem(capdir), devgen); +} + +static Chan* +capopen(Chan *c, int omode) +{ + if(c->qid.type & QTDIR) { + if(omode != OREAD) + error(Eisdir); + c->mode = omode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + + if(c->qid.path == Qhash && !iseve()) + error(Eperm); + + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +capclose(Chan *c) +{ + USED(c); +} + +static long +capread(Chan *c, void *va, long n, vlong vl) +{ + USED(vl); + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, va, n, capdir, ncapdir, devgen); + + default: + error(Eperm); + break; + } + return n; +} + +static int +capwritehash(uchar *a, int l) +{ + Caps *c; + + if(l != SHA1dlen) + return -1; + c = malloc(sizeof(*c)); + if(c == nil) + return -1; + memmove(c->hash, a, l); + c->time = 0; + qlock(&allcaps.l); + c->next = allcaps.caps; + allcaps.caps = c; + if(!allcaps.kpstarted){ + allcaps.kpstarted = 1; + kproc("capwatch", capwatch, 0, 0); + } + qunlock(&allcaps.l); + return 0; +} + +static int +capwriteuse(uchar *a, int len) +{ + int n; + uchar digest[SHA1dlen]; + char buf[128], *p, *users[3]; + Caps *c, **l; + + if(len >= sizeof(buf)-1) + return -1; + memmove(buf, a, len); + buf[len] = 0; + p = strrchr(buf, '@'); + if(p == nil) + return -1; + *p++ = 0; + len = strlen(p); + n = strlen(buf); + if(len == 0 || n == 0) + return -1; + hmac_sha1((uchar*)buf, n, (uchar*)p, len, digest, nil); + n = getfields(buf, users, nelem(users), 0, "@"); + if(n == 1) + users[1] = users[0]; + else if(n != 2) + return -1; + if(*users[0] == 0 || *users[1] == 0) + return -1; + qlock(&allcaps.l); + for(l = &allcaps.caps; (c = *l) != nil; l = &c->next) + if(memcmp(c->hash, digest, sizeof(c->hash)) == 0){ + *l = c->next; + qunlock(&allcaps.l); + free(c); + if(n == 2 && strcmp(up->env->user, users[0]) != 0) + return -1; + kstrdup(&up->env->user, users[1]); + return 0; + } + qunlock(&allcaps.l); + return -1; +} + +static long +capwrite(Chan* c, void* buf, long n, vlong offset) +{ + USED(offset); + switch((ulong)c->qid.path){ + case Qhash: + if(capwritehash(buf, n) < 0) + error(Ebadarg); + return n; + case Quse: + if(capwriteuse(buf, n) < 0) + error("invalid capability"); + return n; + } + error(Ebadarg); + return 0; +} + +static void +capremove(Chan *c) +{ + if(c->qid.path != Qhash || !iseve()) + error(Eperm); + ncapdir = nelem(capdir)-1; +} + +Dev capdevtab = { + L'¤', + "cap", + + devreset, + devinit, + devshutdown, + capattach, + capwalk, + capstat, + capopen, + devcreate, + capclose, + capread, + devbread, + capwrite, + devbwrite, + capremove, + devwstat +}; diff --git a/os/port/devcons.c b/os/port/devcons.c new file mode 100644 index 00000000..7460cffc --- /dev/null +++ b/os/port/devcons.c @@ -0,0 +1,1274 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include +#include "mp.h" +#include "libsec.h" +#include "keyboard.h" + +extern int cflag; +extern int keepbroken; + +void (*serwrite)(char *, int); + +Queue* kscanq; /* keyboard raw scancodes (when needed) */ +char* kscanid; /* name of raw scan format (if defined) */ +Queue* kbdq; /* unprocessed console input */ +Queue* lineq; /* processed console input */ +Queue* printq; /* console output */ +Queue* klogq; /* kernel print (log) output */ +int iprintscreenputs; + +static struct +{ + RWlock; + Queue* q; +} kprintq; + +static struct +{ + QLock; + + int raw; /* true if we shouldn't process input */ + int ctl; /* number of opens to the control file */ + int kbdr; /* number of open reads to the keyboard */ + int scan; /* true if reading raw scancodes */ + int x; /* index into line */ + char line[1024]; /* current input line */ + + char c; + int count; + int repeat; +} kbd; + +char* sysname; +char* eve; + +enum +{ + CMreboot, + CMhalt, + CMpanic, + CMbroken, + CMnobroken, + CMconsole, +}; + +static Cmdtab sysctlcmd[] = +{ + CMreboot, "reboot", 0, + CMhalt, "halt", 0, + CMpanic, "panic", 0, + CMconsole, "console", 1, + CMbroken, "broken", 0, + CMnobroken, "nobroken", 0, +}; + +void +printinit(void) +{ + lineq = qopen(2*1024, 0, nil, nil); + if(lineq == nil) + panic("printinit"); + qnoblock(lineq, 1); +} + +/* + * return true if current user is eve + */ +int +iseve(void) +{ + Osenv *o; + + o = up->env; + return strcmp(eve, o->user) == 0; +} + +static int +consactive(void) +{ + if(printq) + return qlen(printq) > 0; + return 0; +} + +static void +prflush(void) +{ + ulong now; + + now = m->ticks; + while(serwrite==nil && consactive()) + if(m->ticks - now >= HZ) + break; +} + +/* + * Print a string on the console. Convert \n to \r\n for serial + * line consoles. Locking of the queues is left up to the screen + * or uart code. Multi-line messages to serial consoles may get + * interspersed with other messages. + */ +static void +putstrn0(char *str, int n, int usewrite) +{ + int m; + char *t; + char buf[PRINTSIZE+2]; + + /* + * if kprint is open, put the message there, otherwise + * if there's an attached bit mapped display, + * put the message there. + */ + m = consoleprint; + if(canrlock(&kprintq)){ + if(kprintq.q != nil){ + if(waserror()){ + runlock(&kprintq); + nexterror(); + } + if(usewrite) + qwrite(kprintq.q, str, n); + else + qiwrite(kprintq.q, str, n); + poperror(); + m = 0; + } + runlock(&kprintq); + } + if(m && screenputs != nil) + screenputs(str, n); + + /* + * if there's a serial line being used as a console, + * put the message there. + */ + if(serwrite != nil) { + serwrite(str, n); + return; + } + + if(printq == 0) + return; + + while(n > 0) { + t = memchr(str, '\n', n); + if(t && !kbd.raw) { + m = t - str; + if(m > sizeof(buf)-2) + m = sizeof(buf)-2; + memmove(buf, str, m); + buf[m] = '\r'; + buf[m+1] = '\n'; + if(usewrite) + qwrite(printq, buf, m+2); + else + qiwrite(printq, buf, m+2); + str = t + 1; + n -= m + 1; + } else { + if(usewrite) + qwrite(printq, str, n); + else + qiwrite(printq, str, n); + break; + } + } +} + +void +putstrn(char *str, int n) +{ + putstrn0(str, n, 0); +} + +int +snprint(char *s, int n, char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + n = vseprint(s, s+n, fmt, arg) - s; + va_end(arg); + + return n; +} + +int +sprint(char *s, char *fmt, ...) +{ + int n; + va_list arg; + + va_start(arg, fmt); + n = vseprint(s, s+PRINTSIZE, fmt, arg) - s; + va_end(arg); + + return n; +} + +int +print(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + putstrn(buf, n); + + return n; +} + +int +fprint(int fd, char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + USED(fd); + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + putstrn(buf, n); + + return n; +} + +int +kprint(char *fmt, ...) +{ + va_list arg; + char buf[PRINTSIZE]; + int n; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + if(qfull(klogq)) + qflush(klogq); + return qproduce(klogq, buf, n); +} + +int +iprint(char *fmt, ...) +{ + int n, s; + va_list arg; + char buf[PRINTSIZE]; + + s = splhi(); + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + if(screenputs != nil && iprintscreenputs) + screenputs(buf, n); + uartputs(buf, n); + splx(s); + + return n; +} + +void +panic(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + setpanic(); + kprintq.q = nil; + strcpy(buf, "panic: "); + va_start(arg, fmt); + n = vseprint(buf+strlen(buf), buf+sizeof(buf)-1, fmt, arg) - buf; + va_end(arg); + buf[n] = '\n'; + putstrn(buf, n+1); + spllo(); + dumpstack(); + + exit(1); +} + +void +_assert(char *fmt) +{ + panic("assert failed: %s", fmt); +} + +void +sysfatal(char *fmt, ...) +{ + va_list arg; + char buf[64]; + + va_start(arg, fmt); + vsnprint(buf, sizeof(buf), fmt, arg); + va_end(arg); + panic("sysfatal: %s", buf); +} + +int +pprint(char *fmt, ...) +{ + int n; + Chan *c; + Osenv *o; + va_list arg; + char buf[2*PRINTSIZE]; + + n = sprint(buf, "%s %ld: ", up->text, up->pid); + va_start(arg, fmt); + n = vseprint(buf+n, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + + o = up->env; + if(o->fgrp == 0) { + print("%s", buf); + return 0; + } + c = o->fgrp->fd[2]; + if(c==0 || (c->mode!=OWRITE && c->mode!=ORDWR)) { + print("%s", buf); + return 0; + } + + if(waserror()) { + print("%s", buf); + return 0; + } + devtab[c->type]->write(c, buf, n, c->offset); + poperror(); + + lock(c); + c->offset += n; + unlock(c); + + return n; +} + +void +echo(Rune r, char *buf, int n) +{ + if(kbd.raw) + return; + + if(r == '\n'){ + if(printq) + qiwrite(printq, "\r", 1); + } else if(r == 0x15){ + buf = "^U\n"; + n = 3; + } + if(consoleprint && screenputs != nil) + screenputs(buf, n); + if(printq) + qiwrite(printq, buf, n); +} + +/* + * Debug key support. Allows other parts of the kernel to register debug + * key handlers, instead of devcons.c having to know whatever's out there. + * A kproc is used to invoke most handlers, rather than tying up the CPU at + * splhi, which can choke some device drivers (eg softmodem). + */ +typedef struct { + Rune r; + char *m; + void (*f)(Rune); + int i; /* function called at interrupt time */ +} Dbgkey; + +static struct { + Rendez; + Dbgkey *work; + Dbgkey keys[50]; + int nkeys; + int on; +} dbg; + +static Dbgkey * +finddbgkey(Rune r) +{ + int i; + Dbgkey *dp; + + for(dp = dbg.keys, i = 0; i < dbg.nkeys; i++, dp++) + if(dp->r == r) + return dp; + return nil; +} + +static int +dbgwork(void *) +{ + return dbg.work != 0; +} + +static void +dbgproc(void *) +{ + Dbgkey *dp; + + setpri(PriRealtime); + for(;;) { + do { + sleep(&dbg, dbgwork, 0); + dp = dbg.work; + } while(dp == nil); + dp->f(dp->r); + dbg.work = nil; + } +} + +void +debugkey(Rune r, char *msg, void (*fcn)(), int iflag) +{ + Dbgkey *dp; + + if(dbg.nkeys >= nelem(dbg.keys)) + return; + if(finddbgkey(r) != nil) + return; + for(dp = &dbg.keys[dbg.nkeys++] - 1; dp >= dbg.keys; dp--) { + if(strcmp(dp->m, msg) < 0) + break; + dp[1] = dp[0]; + } + dp++; + dp->r = r; + dp->m = msg; + dp->f = fcn; + dp->i = iflag; +} + +static int +isdbgkey(Rune r) +{ + static int ctrlt; + Dbgkey *dp; + int echoctrlt = ctrlt; + + /* + * ^t hack BUG + */ + if(dbg.on || (ctrlt >= 2)) { + if(r == 0x14 || r == 0x05) { + ctrlt++; + return 0; + } + if(dp = finddbgkey(r)) { + if(dp->i || ctrlt > 2) + dp->f(r); + else { + dbg.work = dp; + wakeup(&dbg); + } + ctrlt = 0; + return 1; + } + ctrlt = 0; + } + else if(r == 0x14){ + ctrlt++; + return 1; + } + else + ctrlt = 0; + if(echoctrlt){ + char buf[3]; + + buf[0] = 0x14; + while(--echoctrlt >= 0){ + echo(buf[0], buf, 1); + qproduce(kbdq, buf, 1); + } + } + return 0; +} + +static void +dbgtoggle(Rune) +{ + dbg.on = !dbg.on; + print("Debug keys %s\n", dbg.on ? "HOT" : "COLD"); +} + +static void +dbghelp(void) +{ + int i; + Dbgkey *dp; + Dbgkey *dp2; + static char fmt[] = "%c: %-22s"; + + dp = dbg.keys; + dp2 = dp + (dbg.nkeys + 1)/2; + for(i = dbg.nkeys; i > 1; i -= 2, dp++, dp2++) { + print(fmt, dp->r, dp->m); + print(fmt, dp2->r, dp2->m); + print("\n"); + } + if(i) + print(fmt, dp->r, dp->m); + print("\n"); +} + +static void +debuginit(void) +{ + kproc("consdbg", dbgproc, nil, 0); + debugkey('|', "HOT|COLD keys", dbgtoggle, 0); + debugkey('?', "help", dbghelp, 0); +} + +/* + * Called by a uart interrupt for console input. + * + * turn '\r' into '\n' before putting it into the queue. + */ +int +kbdcr2nl(Queue *q, int ch) +{ + if(ch == '\r') + ch = '\n'; + return kbdputc(q, ch); +} + +/* + * Put character, possibly a rune, into read queue at interrupt time. + * Performs translation for compose sequences + * Called at interrupt time to process a character. + */ +int +kbdputc(Queue *q, int ch) +{ + int n; + char buf[3]; + Rune r; + static Rune kc[15]; + static int nk, collecting = 0; + + r = ch; + if(r == Latin) { + collecting = 1; + nk = 0; + return 0; + } + if(collecting) { + int c; + nk += runetochar((char*)&kc[nk], &r); + c = latin1(kc, nk); + if(c < -1) /* need more keystrokes */ + return 0; + collecting = 0; + if(c == -1) { /* invalid sequence */ + echo(kc[0], (char*)kc, nk); + qproduce(q, kc, nk); + return 0; + } + r = (Rune)c; + } + kbd.c = r; + n = runetochar(buf, &r); + if(n == 0) + return 0; + if(!isdbgkey(r)) { + echo(r, buf, n); + qproduce(q, buf, n); + } + return 0; +} + +void +kbdrepeat(int rep) +{ + kbd.repeat = rep; + kbd.count = 0; +} + +void +kbdclock(void) +{ + if(kbd.repeat == 0) + return; + if(kbd.repeat==1 && ++kbd.count>HZ){ + kbd.repeat = 2; + kbd.count = 0; + return; + } + if(++kbd.count&1) + kbdputc(kbdq, kbd.c); +} + +enum{ + Qdir, + Qcons, + Qsysctl, + Qconsctl, + Qdrivers, + Qhostowner, + Qkeyboard, + Qklog, + Qkprint, + Qscancode, + Qmemory, + Qmsec, + Qnull, + Qpin, + Qrandom, + Qnotquiterandom, + Qsysname, + Qtime, + Quser, + Qjit, +}; + +static Dirtab consdir[]= +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "cons", {Qcons}, 0, 0660, + "consctl", {Qconsctl}, 0, 0220, + "sysctl", {Qsysctl}, 0, 0644, + "drivers", {Qdrivers}, 0, 0444, + "hostowner", {Qhostowner}, 0, 0644, + "keyboard", {Qkeyboard}, 0, 0666, + "klog", {Qklog}, 0, 0444, + "kprint", {Qkprint}, 0, 0444, + "scancode", {Qscancode}, 0, 0444, + "memory", {Qmemory}, 0, 0444, + "msec", {Qmsec}, NUMSIZE, 0444, + "null", {Qnull}, 0, 0666, + "pin", {Qpin}, 0, 0666, + "random", {Qrandom}, 0, 0444, + "notquiterandom", {Qnotquiterandom}, 0, 0444, + "sysname", {Qsysname}, 0, 0664, + "time", {Qtime}, 0, 0664, + "user", {Quser}, 0, 0644, + "jit", {Qjit}, 0, 0666, +}; + +ulong boottime; /* seconds since epoch at boot */ + +long +seconds(void) +{ + return boottime + TK2SEC(MACHP(0)->ticks); +} + +vlong +mseconds(void) +{ + return ((vlong)boottime*1000)+((vlong)(TK2MS(MACHP(0)->ticks))); +} + +vlong +osusectime(void) +{ + return (((vlong)boottime*1000)+((vlong)(TK2MS(MACHP(0)->ticks)))*1000); +} + +vlong +nsec(void) +{ + return osusectime()*1000; /* TO DO */ +} + +int +readnum(ulong off, char *buf, ulong n, ulong val, int size) +{ + char tmp[64]; + + if(size > 64) size = 64; + + snprint(tmp, sizeof(tmp), "%*.0lud ", size, val); + if(off >= size) + return 0; + if(off+n > size) + n = size-off; + memmove(buf, tmp+off, n); + return n; +} + +int +readstr(ulong off, char *buf, ulong n, char *str) +{ + int size; + + size = strlen(str); + if(off >= size) + return 0; + if(off+n > size) + n = size-off; + memmove(buf, str+off, n); + return n; +} + +void +fddump() +{ + Proc *p; + Osenv *o; + int i; + Chan *c; + + p = proctab(6); + o = p->env; + for(i = 0; i <= o->fgrp->maxfd; i++) { + if((c = o->fgrp->fd[i]) == nil) + continue; + print("%d: %s\n", i, c->name == nil? "???": c->name->s); + } +} + +static void +qpanic(Rune) +{ + panic("User requested panic."); +} + +static void +rexit(Rune) +{ + exit(0); +} + +static void +consinit(void) +{ + randominit(); + debuginit(); + debugkey('f', "files/6", fddump, 0); + debugkey('q', "panic", qpanic, 1); + debugkey('r', "exit", rexit, 1); + klogq = qopen(128*1024, 0, 0, 0); +} + +static Chan* +consattach(char *spec) +{ + return devattach('c', spec); +} + +static Walkqid* +conswalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, consdir, nelem(consdir), devgen); +} + +static int +consstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, consdir, nelem(consdir), devgen); +} + +static void +flushkbdline(Queue *q) +{ + if(kbd.x){ + qwrite(q, kbd.line, kbd.x); + kbd.x = 0; + } +} + +static Chan* +consopen(Chan *c, int omode) +{ + c->aux = 0; + switch((ulong)c->qid.path){ + case Qconsctl: + if(!iseve()) + error(Eperm); + qlock(&kbd); + kbd.ctl++; + qunlock(&kbd); + break; + + case Qkeyboard: + if((omode & 3) != OWRITE) { + qlock(&kbd); + kbd.kbdr++; + flushkbdline(kbdq); + kbd.raw = 1; + qunlock(&kbd); + } + break; + + case Qscancode: + qlock(&kbd); + if(kscanq || !kscanid) { + qunlock(&kbd); + c->flag &= ~COPEN; + if(kscanq) + error(Einuse); + else + error(Ebadarg); + } + kscanq = qopen(256, 0, nil, nil); + qunlock(&kbd); + break; + + case Qkprint: + if((omode & 3) != OWRITE) { + wlock(&kprintq); + if(kprintq.q != nil){ + wunlock(&kprintq); + error(Einuse); + } + kprintq.q = qopen(32*1024, Qcoalesce, nil, nil); + if(kprintq.q == nil){ + wunlock(&kprintq); + error(Enomem); + } + qnoblock(kprintq.q, 1); + wunlock(&kprintq); + c->iounit = qiomaxatomic; + } + break; + } + return devopen(c, omode, consdir, nelem(consdir), devgen); +} + +static void +consclose(Chan *c) +{ + if((c->flag&COPEN) == 0) + return; + + switch((ulong)c->qid.path){ + case Qconsctl: + /* last close of control file turns off raw */ + qlock(&kbd); + if(--kbd.ctl == 0) + kbd.raw = 0; + qunlock(&kbd); + break; + + case Qkeyboard: + if(c->mode != OWRITE) { + qlock(&kbd); + --kbd.kbdr; + qunlock(&kbd); + } + break; + + case Qscancode: + qlock(&kbd); + if(kscanq) { + qfree(kscanq); + kscanq = 0; + } + qunlock(&kbd); + break; + + case Qkprint: + wlock(&kprintq); + qfree(kprintq.q); + kprintq.q = nil; + wunlock(&kprintq); + break; + } +} + +static long +consread(Chan *c, void *buf, long n, vlong offset) +{ + int l; + Osenv *o; + int ch, eol, i; + char *p, tmp[128]; + char *cbuf = buf; + + if(n <= 0) + return n; + o = up->env; + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, buf, n, consdir, nelem(consdir), devgen); + case Qsysctl: + return readstr(offset, buf, n, VERSION); + case Qcons: + case Qkeyboard: + qlock(&kbd); + if(waserror()) { + qunlock(&kbd); + nexterror(); + } + if(kbd.raw || kbd.kbdr) { + if(qcanread(lineq)) + n = qread(lineq, buf, n); + else { + /* read as much as possible */ + do { + i = qread(kbdq, cbuf, n); + cbuf += i; + n -= i; + } while(n>0 && qcanread(kbdq)); + n = cbuf - (char*)buf; + } + } else { + while(!qcanread(lineq)) { + qread(kbdq, &kbd.line[kbd.x], 1); + ch = kbd.line[kbd.x]; + eol = 0; + switch(ch){ + case '\b': + if(kbd.x) + kbd.x--; + break; + case 0x15: + kbd.x = 0; + break; + case '\n': + case 0x04: + eol = 1; + default: + kbd.line[kbd.x++] = ch; + break; + } + if(kbd.x == sizeof(kbd.line) || eol) { + if(ch == 0x04) + kbd.x--; + qwrite(lineq, kbd.line, kbd.x); + kbd.x = 0; + } + } + n = qread(lineq, buf, n); + } + qunlock(&kbd); + poperror(); + return n; + + case Qscancode: + if(offset == 0) + return readstr(0, buf, n, kscanid); + else + return qread(kscanq, buf, n); + + case Qpin: + p = "pin set"; + if(up->env->pgrp->pin == Nopin) + p = "no pin"; + return readstr(offset, buf, n, p); + + case Qtime: + snprint(tmp, sizeof(tmp), "%.lld", (vlong)mseconds()*1000); + return readstr(offset, buf, n, tmp); + + case Qhostowner: + return readstr(offset, buf, n, eve); + + case Quser: + return readstr(offset, buf, n, o->user); + + case Qjit: + snprint(tmp, sizeof(tmp), "%d", cflag); + return readstr(offset, buf, n, tmp); + + case Qnull: + return 0; + + case Qmsec: + return readnum(offset, buf, n, TK2MS(MACHP(0)->ticks), NUMSIZE); + + case Qsysname: + if(sysname == nil) + return 0; + return readstr(offset, buf, n, sysname); + + case Qnotquiterandom: + genrandom(buf, n); + return n; + + case Qrandom: + return randomread(buf, n); + + case Qmemory: + return poolread(buf, n, offset); + + case Qdrivers: + p = malloc(READSTR); + if(p == nil) + error(Enomem); + l = 0; + for(i = 0; devtab[i] != nil; i++) + l += snprint(p+l, READSTR-l, "#%C %s\n", devtab[i]->dc, devtab[i]->name); + if(waserror()){ + free(p); + nexterror(); + } + n = readstr(offset, buf, n, p); + free(p); + poperror(); + return n; + + case Qklog: + return qread(klogq, buf, n); + + case Qkprint: + rlock(&kprintq); + if(waserror()){ + runlock(&kprintq); + nexterror(); + } + n = qread(kprintq.q, buf, n); + poperror(); + runlock(&kprintq); + return n; + + default: + print("consread %llud\n", c->qid.path); + error(Egreg); + } + return -1; /* never reached */ +} + +static long +conswrite(Chan *c, void *va, long n, vlong offset) +{ + vlong t; + long l, bp; + char *a = va; + Cmdbuf *cb; + Cmdtab *ct; + char buf[256]; + int x; + + switch((ulong)c->qid.path){ + case Qcons: + /* + * Can't page fault in putstrn, so copy the data locally. + */ + l = n; + while(l > 0){ + bp = l; + if(bp > sizeof buf) + bp = sizeof buf; + memmove(buf, a, bp); + putstrn0(a, bp, 1); + a += bp; + l -= bp; + } + break; + + case Qconsctl: + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + strncpy(buf, a, n); + buf[n] = 0; + for(a = buf; a;){ + if(strncmp(a, "rawon", 5) == 0){ + qlock(&kbd); + flushkbdline(kbdq); + kbd.raw = 1; + qunlock(&kbd); + } else if(strncmp(a, "rawoff", 6) == 0){ + qlock(&kbd); + kbd.raw = 0; + kbd.x = 0; + qunlock(&kbd); + } + if(a = strchr(a, ' ')) + a++; + } + break; + + case Qkeyboard: + for(x=0; x= sizeof(buf)) + n = sizeof(buf)-1; + strncpy(buf, a, n); + buf[n] = 0; + t = strtoll(buf, 0, 0)/1000000; + boottime = t - TK2SEC(MACHP(0)->ticks); + break; + + case Qhostowner: + if(!iseve()) + error(Eperm); + if(offset != 0 || n >= sizeof(buf)) + error(Ebadarg); + memmove(buf, a, n); + buf[n] = '\0'; + if(n > 0 && buf[n-1] == '\n') + buf[--n] = 0; + if(n <= 0) + error(Ebadarg); + renameuser(eve, buf); + renameproguser(eve, buf); + kstrdup(&eve, buf); + kstrdup(&up->env->user, buf); + break; + + case Quser: + if(!iseve()) + error(Eperm); + if(offset != 0) + error(Ebadarg); + if(n <= 0 || n >= sizeof(buf)) + error(Ebadarg); + strncpy(buf, a, n); + buf[n] = 0; + if(buf[n-1] == '\n') + buf[n-1] = 0; + kstrdup(&up->env->user, buf); + break; + + case Qjit: + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + strncpy(buf, va, n); + buf[n] = '\0'; + x = atoi(buf); + if(x < 0 || x > 9) + error(Ebadarg); + cflag = x; + return n; + + case Qnull: + break; + + case Qpin: + if(up->env->pgrp->pin != Nopin) + error("pin already set"); + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + strncpy(buf, va, n); + buf[n] = '\0'; + up->env->pgrp->pin = atoi(buf); + return n; + + case Qsysname: + if(offset != 0) + error(Ebadarg); + if(n <= 0 || n >= sizeof(buf)) + error(Ebadarg); + strncpy(buf, a, n); + buf[n] = 0; + if(buf[n-1] == '\n') + buf[n-1] = 0; + kstrdup(&sysname, buf); + break; + + case Qsysctl: + if(!iseve()) + error(Eperm); + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + ct = lookupcmd(cb, sysctlcmd, nelem(sysctlcmd)); + switch(ct->index){ + case CMreboot: + reboot(); + break; + case CMhalt: + halt(); + break; + case CMpanic: + panic("sysctl"); + case CMconsole: + consoleprint = strcmp(cb->f[1], "off") != 0; + break; + case CMbroken: + keepbroken = 1; + break; + case CMnobroken: + keepbroken = 0; + break; + } + poperror(); + free(cb); + break; + + default: + print("conswrite: %llud\n", c->qid.path); + error(Egreg); + } + return n; +} + +Dev consdevtab = { + 'c', + "cons", + + devreset, + consinit, + devshutdown, + consattach, + conswalk, + consstat, + consopen, + devcreate, + consclose, + consread, + devbread, + conswrite, + devbwrite, + devremove, + devwstat, +}; + +static ulong randn; + +static void +seedrand(void) +{ + randomread((void*)&randn, sizeof(randn)); +} + +int +nrand(int n) +{ + if(randn == 0) + seedrand(); + randn = randn*1103515245 + 12345 + MACHP(0)->ticks; + return (randn>>16) % n; +} + +int +rand(void) +{ + nrand(1); + return randn; +} + +ulong +truerand(void) +{ + ulong x; + + randomread(&x, sizeof(x)); + return x; +} + +QLock grandomlk; + +void +_genrandomqlock(void) +{ + qlock(&grandomlk); +} + + +void +_genrandomqunlock(void) +{ + qunlock(&grandomlk); +} diff --git a/os/port/devdbg.c b/os/port/devdbg.c new file mode 100644 index 00000000..daaf314c --- /dev/null +++ b/os/port/devdbg.c @@ -0,0 +1,1000 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" +#include "../port/error.h" +#include "rdbg.h" + +#include +#include + +/* + * The following should be set in the config file to override + * the defaults. + */ +int dbgstart; +char *dbgdata; +char *dbgctl; +char *dbgctlstart; +char *dbgctlstop; +char *dbgctlflush; + +// +// Error messages sent to the remote debugger +// +static uchar Ereset[9] = { 'r', 'e', 's', 'e', 't' }; +static uchar Ecount[9] = { 'c', 'o', 'u', 'n', 't' }; +static uchar Eunk[9] = { 'u', 'n', 'k' }; +static uchar Einval[9] = { 'i', 'n', 'v', 'a', 'l' }; +static uchar Ebadpid[9] = {'p', 'i', 'd'}; +static uchar Eunsup[9] = { 'u', 'n', 's', 'u', 'p' }; +static uchar Enotstop[9] = { 'n', 'o', 't', 's', 't', 'o', 'p' }; + +// +// Error messages raised via call to error() +// +static char Erunning[] = "Not allowed while debugger is running"; +static char Enumarg[] = "Not enough args"; +static char Ebadcmd[] = "Unknown command"; + +static int PROCREG; +static struct { + Rendez; + Bkpt *b; +} brk; + +static Queue *logq; + +int dbgchat = 0; + +typedef struct Debugger Debugger; +struct Debugger { + RWlock; + int running; + char data[PRINTSIZE]; + char ctl[PRINTSIZE]; + char ctlstart[PRINTSIZE]; + char ctlstop[PRINTSIZE]; + char ctlflush[PRINTSIZE]; +}; + +static Debugger debugger = { + .data= "#t/eia0", + .ctl= "#t/eia0ctl", + .ctlstart= "b19200", + .ctlstop= "h", + .ctlflush= "f", +}; + +enum { + BkptStackSize= 256, +}; + +typedef struct SkipArg SkipArg; +struct SkipArg +{ + Bkpt *b; + Proc *p; +}; + +Bkpt *breakpoints; +void freecondlist(BkptCond *l); + +static int +getbreaks(ulong addr, Bkpt **a, int nb) +{ + Bkpt *b; + int n; + + n = 0; + for(b = breakpoints; b != nil; b = b->next){ + if(b->addr == addr){ + a[n++] = b; + if(n == nb) + break; + } + } + return n; +} + +Bkpt* +newbreak(int id, ulong addr, BkptCond *conds, void(*handler)(Bkpt*), void *aux) +{ + Bkpt *b; + + b = malloc(sizeof(*b)); + if(b == nil) + error(Enomem); + + b->id = id; + b->conditions = conds; + b->addr = addr; + b->handler = handler; + b->aux = aux; + b->next = nil; + + return b; +} + +void +freebreak(Bkpt *b) +{ + freecondlist(b->conditions); + free(b); +} + +BkptCond* +newcondition(uchar cmd, ulong val) +{ + BkptCond *c; + + c = mallocz(sizeof(*c), 0); + if(c == nil) + error(Enomem); + + c->op = cmd; + c->val = val; + c->next = nil; + + return c; +} + +void +freecondlist(BkptCond *l) +{ + BkptCond *next; + + while(l != nil){ + next = l->next; + free(l); + l = next; + } +} + + +void +breakset(Bkpt *b) +{ + Bkpt *e[1]; + + if(getbreaks(b->addr, e, 1) != 0){ + b->instr = e[0]->instr; + } else { + b->instr = machinstr(b->addr); + machbreakset(b->addr); + } + + b->next = breakpoints; + breakpoints = b; +} + +void +breakrestore(Bkpt *b) +{ + b->next = breakpoints; + breakpoints = b; + machbreakset(b->addr); +} + +Bkpt* +breakclear(int id) +{ + Bkpt *b, *e, *p; + + for(b=breakpoints, p=nil; b != nil && b->id != id; p = b, b = b->next) + ; + + if(b != nil){ + if(p == nil) + breakpoints = b->next; + else + p->next = b->next; + + if(getbreaks(b->addr, &e, 1) == 0) + machbreakclear(b->addr, b->instr); + } + + return b; +} + +void +breaknotify(Bkpt *b, Proc *p) +{ + p->dbgstop = 1; // stop running this process. + b->handler(b); +} + +int +breakmatch(BkptCond *cond, Ureg *ur, Proc *p) +{ + ulong a, b; + int pos; + ulong s[BkptStackSize]; + + memset(s, 0, sizeof(s)); + pos = 0; + + for(;cond != nil; cond = cond->next){ + switch(cond->op){ + default: + panic("breakmatch: unknown operator %c", cond->op); + break; + case 'k': + if(p == nil || p->pid != cond->val) + return 0; + s[pos++] = 1; + break; + case 'b': + if(ur->pc != cond->val) + return 0; + s[pos++] = 1; + break; + case 'p': s[pos++] = cond->val; break; + case '*': a = *(ulong*)s[--pos]; s[pos++] = a; break; + case '&': a = s[--pos]; b = s[--pos]; s[pos++] = a & b; break; + case '=': a = s[--pos]; b = s[--pos]; s[pos++] = a == b; break; + case '!': a = s[--pos]; b = s[--pos]; s[pos++] = a != b; break; + case 'a': a = s[--pos]; b = s[--pos]; s[pos++] = a && b; break; + case 'o': a = s[--pos]; b = s[--pos]; s[pos++] = a || b; break; + } + } + + if(pos && s[pos-1]) + return 1; + return 0; +} + +void +breakinit(void) +{ + machbreakinit(); +} + +static void +dbglog(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + qwrite(logq, buf, n); +} + +static int +get(int dbgfd, uchar *b) +{ + int i; + uchar c; + + if(kread(dbgfd, &c, 1) < 0) + error(Eio); + for(i=0; i<9; i++){ + if(kread(dbgfd, b++, 1) < 0) + error(Eio); + } + return c; +} + +static void +mesg(int dbgfd, int m, uchar *buf) +{ + int i; + uchar c; + + c = m; + if(kwrite(dbgfd, &c, 1) < 0) + error(Eio); + for(i=0; i<9; i++){ + if(kwrite(dbgfd, buf+i, 1) < 0) + error(Eio); + } +} + +static ulong +dbglong(uchar *s) +{ + return (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]<<0); +} + +static Proc * +dbgproc(ulong pid, int dbgok) +{ + int i; + Proc *p; + + if(!dbgok && pid == up->pid) + return 0; + p = proctab(0); + for(i = 0; i < conf.nproc; i++){ + if(p->pid == pid) + return p; + p++; + } + return 0; +} + +static void* +addr(uchar *s) +{ + ulong a; + Proc *p; + static Ureg ureg; + + a = ((s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]<<0)); + if(a < sizeof(Ureg)){ + p = dbgproc(PROCREG, 0); + if(p == 0){ + dbglog("dbg: invalid pid\n"); + return 0; + } + if(p->dbgreg){ + /* in trap(), registers are all on stack */ + memmove(&ureg, p->dbgreg, sizeof(ureg)); + } + else { + /* not in trap, only pc and sp are available */ + memset(&ureg, 0, sizeof(ureg)); + ureg.sp = p->sched.sp; + ureg.pc = p->sched.pc; + } + return (uchar*)&ureg+a; + } + return (void*)a; +} + + +static void +dumpcmd(uchar cmd, uchar *min) +{ + char *s; + int n; + + switch(cmd){ + case Terr: s = "Terr"; break; + case Tmget: s = "Tmget"; break; + case Tmput: s = "Tmput"; break; + case Tspid: s = "Tspid"; break; + case Tproc: s = "Tproc"; break; + case Tstatus: s = "Tstatus"; break; + case Trnote: s = "Trnote"; break; + case Tstartstop: s = "Tstartstop"; break; + case Twaitstop: s = "Twaitstop"; break; + case Tstart: s = "Tstart"; break; + case Tstop: s = "Tstop"; break; + case Tkill: s = "Tkill"; break; + case Tcondbreak: s = "Tcondbreak"; break; + default: s = ""; break; + } + dbglog("%s: [%2.2ux]: ", s, cmd); + for(n = 0; n < 9; n++) + dbglog("%2.2ux", min[n]); + dbglog("\n"); +} + +static int +brkpending(void *a) +{ + Proc *p; + + p = a; + if(brk.b != nil) return 1; + + p->dbgstop = 0; /* atomic */ + if(p->state == Stopped) + ready(p); + + return 0; +} + +static void +gotbreak(Bkpt *b) +{ + Bkpt *cur, *prev; + + b->link = nil; + + for(prev = nil, cur = brk.b; cur != nil; prev = cur, cur = cur->link) + ; + if(prev == nil) + brk.b = b; + else + prev->link = b; + + wakeup(&brk); +} + +static int +startstop(Proc *p) +{ + int id; + int s; + Bkpt *b; + + sleep(&brk, brkpending, p); + + s = splhi(); + b = brk.b; + brk.b = b->link; + splx(s); + + id = b->id; + + return id; +} + +static int +condbreak(char cmd, ulong val) +{ + BkptCond *c; + static BkptCond *head = nil; + static BkptCond *tail = nil; + static Proc *p = nil; + static int id = -1; + int s; + + if(waserror()){ + dbglog(up->env->errstr); + freecondlist(head); + head = tail = nil; + p = nil; + id = -1; + return 0; + } + + switch(cmd){ + case 'b': case 'p': + case '*': case '&': case '=': + case '!': case 'a': case 'o': + break; + case 'n': + id = val; + poperror(); + return 1; + case 'k': + p = dbgproc(val, 0); + if(p == nil) + error("k: unknown pid"); + break; + case 'd': { + Bkpt *b; + + s = splhi(); + b = breakclear(val); + if(b != nil){ + Bkpt *cur, *prev; + + prev = nil; + cur = brk.b; + while(cur != nil){ + if(cur->id == b->id){ + if(prev == nil) + brk.b = cur->link; + else + prev->link = cur->link; + break; + } + cur = cur->link; + } + freebreak(b); + } + splx(s); + poperror(); + return 1; + } + default: + dbglog("condbreak(): unknown op %c %lux\n", cmd, val); + error("unknown op"); + } + + c = newcondition(cmd, val); + + // + // the 'b' command comes last, (so we know we have reached the end + // of the condition list), but it should be the first thing + // checked, so put it at the head. + // + if(cmd == 'b'){ + if(p == nil) error("no pid"); + if(id == -1) error("no id"); + + c->next = head; + s = splhi(); + breakset(newbreak(id, val, c, gotbreak, p)); + splx(s); + head = tail = nil; + p = nil; + id = -1; + } else if(tail != nil){ + tail->next = c; + tail = c; + } else + head = tail = c; + + poperror(); + + return 1; +} + +static void +dbg(void*) +{ + Proc *p; + ulong val; + int n, cfd, dfd; + uchar cmd, *a, min[RDBMSGLEN-1], mout[RDBMSGLEN-1]; + + rlock(&debugger); + + setpri(PriRealtime); + + closefgrp(up->env->fgrp); + up->env->fgrp = newfgrp(nil); + + if(waserror()){ + dbglog("dbg: quits: %s\n", up->env->errstr); + runlock(&debugger); + wlock(&debugger); + debugger.running = 0; + wunlock(&debugger); + pexit("", 0); + } + + dfd = kopen(debugger.data, ORDWR); + if(dfd < 0){ + dbglog("dbg: can't open %s: %s\n",debugger.data, up->env->errstr); + error(Eio); + } + if(waserror()){ + kclose(dfd); + nexterror(); + } + + if(debugger.ctl[0] != 0){ + cfd = kopen(debugger.ctl, ORDWR); + if(cfd < 0){ + dbglog("dbg: can't open %s: %s\n", debugger.ctl, up->env->errstr); + error(Eio); + } + if(kwrite(cfd, debugger.ctlstart, strlen(debugger.ctlstart)) < 0){ + dbglog("dbg: write %s: %s\n", debugger.ctl, up->env->errstr); + error(Eio); + } + }else + cfd = -1; + if(waserror()){ + if(cfd != -1){ + kwrite(cfd, debugger.ctlflush, strlen(debugger.ctlflush)); + kclose(cfd); + } + nexterror(); + } + + mesg(dfd, Rerr, Ereset); + + for(;;){ + memset(mout, 0, sizeof(mout)); + cmd = get(dfd, min); + if(dbgchat) + dumpcmd(cmd, min); + switch(cmd){ + case Tmget: + n = min[4]; + if(n > 9){ + mesg(dfd, Rerr, Ecount); + break; + } + a = addr(min+0); + if(!isvalid_va(a)){ + mesg(dfd, Rerr, Einval); + break; + } + memmove(mout, a, n); + mesg(dfd, Rmget, mout); + break; + case Tmput: + n = min[4]; + if(n > 4){ + mesg(dfd, Rerr, Ecount); + break; + } + a = addr(min+0); + if(!isvalid_va(a)){ + mesg(dfd, Rerr, Einval); + break; + } + memmove(a, min+5, n); + segflush(a, n); + mesg(dfd, Rmput, mout); + break; + case Tproc: + p = dbgproc(dbglong(min+0), 0); + if(p == 0){ + mesg(dfd, Rerr, Ebadpid); + break; + } + PROCREG = p->pid; /* try this instead of Tspid */ + sprint((char*)mout, "%8.8lux", p); + mesg(dfd, Rproc, mout); + break; + case Tstatus: + p = dbgproc(dbglong(min+0), 1); + if(p == 0){ + mesg(dfd, Rerr, Ebadpid); + break; + } + if(p->state > Rendezvous || p->state < Dead) + sprint((char*)mout, "%8.8ux", p->state); + else if(p->dbgstop == 1) + strncpy((char*)mout, statename[Stopped], sizeof(mout)); + else + strncpy((char*)mout, statename[p->state], sizeof(mout)); + mesg(dfd, Rstatus, mout); + break; + case Trnote: + p = dbgproc(dbglong(min+0), 0); + if(p == 0){ + mesg(dfd, Rerr, Ebadpid); + break; + } + mout[0] = 0; /* should be trap status, if any */ + mesg(dfd, Rrnote, mout); + break; + case Tstop: + p = dbgproc(dbglong(min+0), 0); + if(p == 0){ + mesg(dfd, Rerr, Ebadpid); + break; + } + p->dbgstop = 1; /* atomic */ + mout[0] = 0; + mesg(dfd, Rstop, mout); + break; + case Tstart: + p = dbgproc(dbglong(min+0), 0); + if(p == 0){ + mesg(dfd, Rerr, Ebadpid); + break; + } + p->dbgstop = 0; /* atomic */ + if(p->state == Stopped) + ready(p); + mout[0] = 0; + mesg(dfd, Rstart, mout); + break; + case Tstartstop: + p = dbgproc(dbglong(min+0), 0); + if(p == 0){ + mesg(dfd, Rerr, Ebadpid); + break; + } + if(!p->dbgstop){ + mesg(dfd, Rerr, Enotstop); + break; + } + mout[0] = startstop(p); + mesg(dfd, Rstartstop, mout); + break; + case Tcondbreak: + val = dbglong(min+0); + if(!condbreak(min[4], val)){ + mesg(dfd, Rerr, Eunk); + break; + } + mout[0] = 0; + mesg(dfd, Rcondbreak, mout); + break; + default: + dumpcmd(cmd, min); + mesg(dfd, Rerr, Eunk); + break; + } + } +} + +static void +dbgnote(Proc *p, Ureg *ur) +{ + if(p){ + p->dbgreg = ur; + PROCREG = p->pid; /* acid can get the trap info from regs */ + } +} + +enum { + Qdir, + Qdbgctl, + Qdbglog, + + DBGrun = 1, + DBGstop = 2, + + Loglimit = 4096, +}; + +static Dirtab dbgdir[]= +{ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "dbgctl", {Qdbgctl}, 0, 0660, + "dbglog", {Qdbglog}, 0, 0440, +}; + +static void +start_debugger(void) +{ + breakinit(); + dbglog("starting debugger\n"); + debugger.running++; + kproc("dbg", dbg, 0, KPDUPPG); +} + +static void +dbginit(void) +{ + + logq = qopen(Loglimit, 0, 0, 0); + if(logq == nil) + error(Enomem); + qnoblock(logq, 1); + + wlock(&debugger); + if(waserror()){ + wunlock(&debugger); + qfree(logq); + logq = nil; + nexterror(); + } + + if(dbgdata != nil){ + strncpy(debugger.data, dbgdata, sizeof(debugger.data)); + debugger.data[sizeof(debugger.data)-1] = 0; + } + if(dbgctl != nil){ + strncpy(debugger.ctl, dbgctl, sizeof(debugger.ctl)); + debugger.ctl[sizeof(debugger.ctl)-1] = 0; + } + if(dbgctlstart != nil){ + strncpy(debugger.ctlstart, dbgctlstart, sizeof(debugger.ctlstart)); + debugger.ctlstart[sizeof(debugger.ctlstart)-1] = 0; + } + if(dbgctlstop != nil){ + strncpy(debugger.ctlstop, dbgctlstop, sizeof(debugger.ctlstop)); + debugger.ctlstop[sizeof(debugger.ctlstop)-1] = 0; + } + if(dbgctlflush != nil){ + strncpy(debugger.ctlflush, dbgctlflush, sizeof(debugger.ctlflush)); + debugger.ctlflush[sizeof(debugger.ctlflush)-1] = 0; + } + if(dbgstart) + start_debugger(); + + poperror(); + wunlock(&debugger); +} + +static Chan* +dbgattach(char *spec) +{ + return devattach('b', spec); +} + +static Walkqid* +dbgwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, dbgdir, nelem(dbgdir), devgen); +} + +static int +dbgstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, dbgdir, nelem(dbgdir), devgen); +} + +static Chan* +dbgopen(Chan *c, int omode) +{ + return devopen(c, omode, dbgdir, nelem(dbgdir), devgen); +} + +static long +dbgread(Chan *c, void *buf, long n, vlong offset) +{ + char *ctlstate; + + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, buf, n, dbgdir, nelem(dbgdir), devgen); + case Qdbgctl: + rlock(&debugger); + ctlstate = smprint("%s data %s ctl %s ctlstart %s ctlstop %s ctlflush %s\n", + debugger.running ? "running" : "stopped", + debugger.data, debugger.ctl, + debugger.ctlstart, debugger.ctlstop, debugger.ctlflush); + runlock(&debugger); + if(ctlstate == nil) + error(Enomem); + if(waserror()){ + free(ctlstate); + nexterror(); + } + n = readstr(offset, buf, n, ctlstate); + poperror(); + free(ctlstate); + return n; + case Qdbglog: + return qread(logq, buf, n); + default: + error(Egreg); + } + return -1; /* never reached */ +} + +static void +ctl(Cmdbuf *cb) +{ + Debugger d; + int dbgstate = 0; + int i; + char *df; + int dfsize; + int setval; + + memset(&d, 0, sizeof(d)); + for(i=0; i < cb->nf; i++){ + setval = 0; + df = nil; + dfsize = 0; + switch(cb->f[i][0]){ + case 'd': + df = d.data; + dfsize = sizeof(d.data); + setval=1; + break; + case 'c': + df = d.ctl; + dfsize = sizeof(d.ctl); + setval=1; + break; + case 'i': + df = d.ctlstart; + dfsize = sizeof(d.ctlstart); + setval=1; + break; + case 'h': + df = d.ctlstop; + dfsize = sizeof(d.ctlstop); + setval=1; + break; + case 'f': + df = d.ctlflush; + dfsize = sizeof(d.ctlflush); + setval=1; + break; + case 'r': + dbgstate = DBGrun; + break; + case 's': + dbgstate = DBGstop; + break; + default: + error(Ebadcmd); + } + if(setval){ + if(i+1 >= cb->nf) + cmderror(cb, Enumarg); + strncpy(df, cb->f[i+1], dfsize-1); + df[dfsize-1] = 0; + ++d.running; + ++i; + } + } + + if(d.running){ + wlock(&debugger); + if(debugger.running){ + wunlock(&debugger); + error(Erunning); + } + if(d.data[0] != 0){ + strcpy(debugger.data, d.data); + dbglog("data %s\n",debugger.data); + } + if(d.ctl[0] != 0){ + strcpy(debugger.ctl, d.ctl); + dbglog("ctl %s\n",debugger.ctl); + } + if(d.ctlstart[0] != 0){ + strcpy(debugger.ctlstart, d.ctlstart); + dbglog("ctlstart %s\n",debugger.ctlstart); + } + if(d.ctlstop[0] != 0){ + strcpy(debugger.ctlstop, d.ctlstop); + dbglog("ctlstop %s\n",debugger.ctlstop); + } + wunlock(&debugger); + } + + if(dbgstate == DBGrun){ + if(!debugger.running){ + wlock(&debugger); + if(waserror()){ + wunlock(&debugger); + nexterror(); + } + if(!debugger.running) + start_debugger(); + else + dbglog("debugger already running\n"); + poperror(); + wunlock(&debugger); + } else + dbglog("debugger already running\n"); + } else if(dbgstate == DBGstop){ + if(debugger.running){ + /* force hangup to stop the dbg process */ + int cfd; + if(debugger.ctl[0] == 0) + return; + cfd = kopen(debugger.ctl, OWRITE); + if(cfd == -1) + error(up->env->errstr); + dbglog("stopping debugger\n"); + if(kwrite(cfd, debugger.ctlstop, strlen(debugger.ctlstop)) == -1) + error(up->env->errstr); + kclose(cfd); + } else + dbglog("debugger not running\n"); + } +} + +static long +dbgwrite(Chan *c, void *va, long n, vlong) +{ + Cmdbuf *cb; + + switch((ulong)c->qid.path){ + default: + error(Egreg); + break; + case Qdbgctl: + cb = parsecmd(va, n); + if(waserror()){ + free(cb); + nexterror(); + } + ctl(cb); + poperror(); + break; + } + return n; +} + +static void +dbgclose(Chan*) +{ +} + +Dev dbgdevtab = { + 'b', + "dbg", + + devreset, + dbginit, + devshutdown, + dbgattach, + dbgwalk, + dbgstat, + dbgopen, + devcreate, + dbgclose, + dbgread, + devbread, + dbgwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devdraw.c b/os/port/devdraw.c new file mode 100644 index 00000000..a7c7151d --- /dev/null +++ b/os/port/devdraw.c @@ -0,0 +1,2088 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include +#include "screen.h" + +enum +{ + Qtopdir = 0, + Qnew, + Q3rd, + Q2nd, + Qcolormap, + Qctl, + Qdata, + Qrefresh, +}; + +/* + * Qid path is: + * 4 bits of file type (qids above) + * 24 bits of mux slot number +1; 0 means not attached to client + */ +#define QSHIFT 4 /* location in qid of client # */ + +#define QID(q) ((((ulong)(q).path)&0x0000000F)>>0) +#define CLIENTPATH(q) ((((ulong)q)&0x7FFFFFF0)>>QSHIFT) +#define CLIENT(q) CLIENTPATH((q).path) + +#define NHASH (1<<5) +#define HASHMASK (NHASH-1) +#define IOUNIT (64*1024) + +typedef struct Client Client; +typedef struct Draw Draw; +typedef struct DImage DImage; +typedef struct DScreen DScreen; +typedef struct CScreen CScreen; +typedef struct FChar FChar; +typedef struct Refresh Refresh; +typedef struct Refx Refx; +typedef struct DName DName; + +ulong blanktime = 30; /* in minutes; a half hour */ + +struct Draw +{ + QLock; + int clientid; + int nclient; + Client** client; + int nname; + DName* name; + int vers; + int softscreen; + int blanked; /* screen turned off */ + ulong blanktime; /* time of last operation */ + ulong savemap[3*256]; +}; + +struct Client +{ + Ref r; + DImage* dimage[NHASH]; + CScreen* cscreen; + Refresh* refresh; + Rendez refrend; + uchar* readdata; + int nreaddata; + int busy; + int clientid; + int slot; + int refreshme; + int infoid; + int op; +}; + +struct Refresh +{ + DImage* dimage; + Rectangle r; + Refresh* next; +}; + +struct Refx +{ + Client* client; + DImage* dimage; +}; + +struct DName +{ + char *name; + Client *client; + DImage* dimage; + int vers; +}; + +struct FChar +{ + int minx; /* left edge of bits */ + int maxx; /* right edge of bits */ + uchar miny; /* first non-zero scan-line */ + uchar maxy; /* last non-zero scan-line + 1 */ + schar left; /* offset of baseline */ + uchar width; /* width of baseline */ +}; + +/* + * Reference counts in DImages: + * one per open by original client + * one per screen image or fill + * one per image derived from this one by name + */ +struct DImage +{ + int id; + int ref; + char *name; + int vers; + Memimage* image; + int ascent; + int nfchar; + FChar* fchar; + DScreen* dscreen; /* 0 if not a window */ + DImage* fromname; /* image this one is derived from, by name */ + DImage* next; +}; + +struct CScreen +{ + DScreen* dscreen; + CScreen* next; +}; + +struct DScreen +{ + int id; + int public; + int ref; + DImage *dimage; + DImage *dfill; + Memscreen* screen; + Client* owner; + DScreen* next; +}; + +static Draw sdraw; +static Memimage *screenimage; +static Memdata screendata; +static Rectangle flushrect; +static int waste; +static DScreen* dscreen; +extern void flushmemscreen(Rectangle); + void drawmesg(Client*, void*, int); + void drawuninstall(Client*, int); + void drawfreedimage(DImage*); + Client* drawclientofpath(ulong); + +static char Enodrawimage[] = "unknown id for draw image"; +static char Enodrawscreen[] = "unknown id for draw screen"; +static char Eshortdraw[] = "short draw message"; +static char Eshortread[] = "draw read too short"; +static char Eimageexists[] = "image id in use"; +static char Escreenexists[] = "screen id in use"; +static char Edrawmem[] = "out of memory: image"; +static char Ereadoutside[] = "readimage outside image"; +static char Ewriteoutside[] = "writeimage outside image"; +static char Enotfont[] = "image not a font"; +static char Eindex[] = "character index out of range"; +static char Enoclient[] = "no such draw client"; +static char Enameused[] = "image name in use"; +static char Enoname[] = "no image with that name"; +static char Eoldname[] = "named image no longer valid"; +static char Enamed[] = "image already has name"; +static char Ewrongname[] = "wrong name for image"; + +static int +drawgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp) +{ + int t; + Qid q; + ulong path; + Client *cl; + + q.vers = 0; + + if(s == DEVDOTDOT){ + switch(QID(c->qid)){ + case Qtopdir: + case Q2nd: + mkqid(&q, Qtopdir, 0, QTDIR); + devdir(c, q, "#i", 0, eve, 0500, dp); + break; + case Q3rd: + cl = drawclientofpath(c->qid.path); + if(cl == nil) + strcpy(up->genbuf, "??"); + else + sprint(up->genbuf, "%d", cl->clientid); + mkqid(&q, Q2nd, 0, QTDIR); + devdir(c, q, up->genbuf, 0, eve, 0500, dp); + break; + default: + panic("drawwalk %llux", c->qid.path); + } + return 1; + } + + /* + * Top level directory contains the name of the device. + */ + t = QID(c->qid); + if(t == Qtopdir){ + switch(s){ + case 0: + mkqid(&q, Q2nd, 0, QTDIR); + devdir(c, q, "draw", 0, eve, 0555, dp); + break; + default: + return -1; + } + return 1; + } + + /* + * Second level contains "new" plus all the clients. + */ + if(t == Q2nd || t == Qnew){ + if(s == 0){ + mkqid(&q, Qnew, 0, QTFILE); + devdir(c, q, "new", 0, eve, 0666, dp); + } + else if(s <= sdraw.nclient){ + cl = sdraw.client[s-1]; + if(cl == 0) + return 0; + sprint(up->genbuf, "%d", cl->clientid); + mkqid(&q, (s<genbuf, 0, eve, 0555, dp); + return 1; + } + else + return -1; + return 1; + } + + /* + * Third level. + */ + path = c->qid.path&~((1<qid.vers; + q.type = QTFILE; + switch(s){ + case 0: + q.path = path|Qcolormap; + devdir(c, q, "colormap", 0, eve, 0600, dp); + break; + case 1: + q.path = path|Qctl; + devdir(c, q, "ctl", 0, eve, 0600, dp); + break; + case 2: + q.path = path|Qdata; + devdir(c, q, "data", 0, eve, 0600, dp); + break; + case 3: + q.path = path|Qrefresh; + devdir(c, q, "refresh", 0, eve, 0400, dp); + break; + default: + return -1; + } + return 1; +} + +static +int +drawrefactive(void *a) +{ + Client *c; + + c = a; + return c->refreshme || c->refresh!=0; +} + +static +void +drawrefreshscreen(DImage *l, Client *client) +{ + while(l != nil && l->dscreen == nil) + l = l->fromname; + if(l != nil && l->dscreen->owner != client) + l->dscreen->owner->refreshme = 1; +} + +static +void +drawrefresh(Memimage*, Rectangle r, void *v) +{ + Refx *x; + DImage *d; + Client *c; + Refresh *ref; + + if(v == 0) + return; + x = v; + c = x->client; + d = x->dimage; + for(ref=c->refresh; ref; ref=ref->next) + if(ref->dimage == d){ + combinerect(&ref->r, r); + return; + } + ref = malloc(sizeof(Refresh)); + if(ref){ + ref->dimage = d; + ref->r = r; + ref->next = c->refresh; + c->refresh = ref; + } +} + +static void +addflush(Rectangle r) +{ + int abb, ar, anbb; + Rectangle nbb; + + if(sdraw.softscreen==0 || !rectclip(&r, screenimage->r)) + return; + + if(flushrect.min.x >= flushrect.max.x){ + flushrect = r; + waste = 0; + return; + } + nbb = flushrect; + combinerect(&nbb, r); + ar = Dx(r)*Dy(r); + abb = Dx(flushrect)*Dy(flushrect); + anbb = Dx(nbb)*Dy(nbb); + /* + * Area of new waste is area of new bb minus area of old bb, + * less the area of the new segment, which we assume is not waste. + * This could be negative, but that's OK. + */ + waste += anbb-abb - ar; + if(waste < 0) + waste = 0; + /* + * absorb if: + * total area is small + * waste is less than half total area + * rectangles touch + */ + if(anbb<=1024 || waste*2layer; + if(l == nil) + return; + do{ + if(l->screen->image->data != screenimage->data) + return; + r = rectaddpt(r, l->delta); + l = l->screen->image->layer; + }while(l); + addflush(r); +} + +void +drawflush(void) +{ + if(flushrect.min.x < flushrect.max.x) + flushmemscreen(flushrect); + flushrect = Rect(10000, 10000, -10000, -10000); +} + +static +int +drawcmp(char *a, char *b, int n) +{ + if(strlen(a) != n) + return 1; + return memcmp(a, b, n); +} + +DName* +drawlookupname(int n, char *str) +{ + DName *name, *ename; + + name = sdraw.name; + ename = &name[sdraw.nname]; + for(; namename, str, n) == 0) + return name; + return 0; +} + +int +drawgoodname(DImage *d) +{ + DName *n; + + /* if window, validate the screen's own images */ + if(d->dscreen) + if(drawgoodname(d->dscreen->dimage) == 0 + || drawgoodname(d->dscreen->dfill) == 0) + return 0; + if(d->name == nil) + return 1; + n = drawlookupname(strlen(d->name), d->name); + if(n==nil || n->vers!=d->vers) + return 0; + return 1; +} + +DImage* +drawlookup(Client *client, int id, int checkname) +{ + DImage *d; + + d = client->dimage[id&HASHMASK]; + while(d){ + if(d->id == id){ + if(checkname && !drawgoodname(d)) + error(Eoldname); + return d; + } + d = d->next; + } + return 0; +} + +DScreen* +drawlookupdscreen(int id) +{ + DScreen *s; + + s = dscreen; + while(s){ + if(s->id == id) + return s; + s = s->next; + } + return 0; +} + +DScreen* +drawlookupscreen(Client *client, int id, CScreen **cs) +{ + CScreen *s; + + s = client->cscreen; + while(s){ + if(s->dscreen->id == id){ + *cs = s; + return s->dscreen; + } + s = s->next; + } + error(Enodrawscreen); + return 0; +} + +Memimage* +drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen) +{ + DImage *d; + + d = malloc(sizeof(DImage)); + if(d == 0) + return 0; + d->id = id; + d->ref = 1; + d->name = 0; + d->vers = 0; + d->image = i; + d->nfchar = 0; + d->fchar = 0; + d->fromname = 0; + d->dscreen = dscreen; + d->next = client->dimage[id&HASHMASK]; + client->dimage[id&HASHMASK] = d; + return i; +} + +Memscreen* +drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *dfill, int public) +{ + Memscreen *s; + CScreen *c; + + c = malloc(sizeof(CScreen)); + if(dimage && dimage->image && dimage->image->chan == 0) + panic("bad image %p in drawinstallscreen", dimage->image); + + if(c == 0) + return 0; + if(d == 0){ + d = malloc(sizeof(DScreen)); + if(d == 0){ + free(c); + return 0; + } + s = malloc(sizeof(Memscreen)); + if(s == 0){ + free(c); + free(d); + return 0; + } + s->frontmost = 0; + s->rearmost = 0; + d->dimage = dimage; + if(dimage){ + s->image = dimage->image; + dimage->ref++; + } + d->dfill = dfill; + if(dfill){ + s->fill = dfill->image; + dfill->ref++; + } + d->ref = 0; + d->id = id; + d->screen = s; + d->public = public; + d->next = dscreen; + d->owner = client; + dscreen = d; + } + c->dscreen = d; + d->ref++; + c->next = client->cscreen; + client->cscreen = c; + return d->screen; +} + +void +drawdelname(DName *name) +{ + int i; + + i = name-sdraw.name; + memmove(name, name+1, (sdraw.nname-(i+1))*sizeof(DName)); + sdraw.nname--; +} + +void +drawfreedscreen(DScreen *this) +{ + DScreen *ds, *next; + + this->ref--; + if(this->ref < 0) + print("negative ref in drawfreedscreen\n"); + if(this->ref > 0) + return; + ds = dscreen; + if(ds == this){ + dscreen = this->next; + goto Found; + } + while(next = ds->next){ /* assign = */ + if(next == this){ + ds->next = this->next; + goto Found; + } + ds = next; + } + error(Enodrawimage); + + Found: + if(this->dimage) + drawfreedimage(this->dimage); + if(this->dfill) + drawfreedimage(this->dfill); + free(this->screen); + free(this); +} + +void +drawfreedimage(DImage *dimage) +{ + int i; + Memimage *l; + DScreen *ds; + + dimage->ref--; + if(dimage->ref < 0) + print("negative ref in drawfreedimage\n"); + if(dimage->ref > 0) + return; + + /* any names? */ + for(i=0; ifromname){ /* acquired by name; owned by someone else*/ + drawfreedimage(dimage->fromname); + goto Return; + } + if(dimage->image == screenimage) /* don't free the display */ + goto Return; + ds = dimage->dscreen; + if(ds){ + l = dimage->image; + if(l->data == screenimage->data) + dstflush(l->layer->screen->image, l->layer->screenr); + if(l->layer->refreshfn == drawrefresh) /* else true owner will clean up */ + free(l->layer->refreshptr); + l->layer->refreshptr = nil; + if(drawgoodname(dimage)) + memldelete(l); + else + memlfree(l); + drawfreedscreen(ds); + }else + freememimage(dimage->image); + Return: + free(dimage->fchar); + free(dimage); +} + +void +drawuninstallscreen(Client *client, CScreen *this) +{ + CScreen *cs, *next; + + cs = client->cscreen; + if(cs == this){ + client->cscreen = this->next; + drawfreedscreen(this->dscreen); + free(this); + return; + } + while(next = cs->next){ /* assign = */ + if(next == this){ + cs->next = this->next; + drawfreedscreen(this->dscreen); + free(this); + return; + } + cs = next; + } +} + +void +drawuninstall(Client *client, int id) +{ + DImage *d, *next; + + d = client->dimage[id&HASHMASK]; + if(d == 0) + error(Enodrawimage); + if(d->id == id){ + client->dimage[id&HASHMASK] = d->next; + drawfreedimage(d); + return; + } + while(next = d->next){ /* assign = */ + if(next->id == id){ + d->next = next->next; + drawfreedimage(next); + return; + } + d = next; + } + error(Enodrawimage); +} + +void +drawaddname(Client *client, DImage *di, int n, char *str) +{ + DName *name, *ename, *new, *t; + + name = sdraw.name; + ename = &name[sdraw.nname]; + for(; namename, str, n) == 0) + error(Enameused); + t = smalloc((sdraw.nname+1)*sizeof(DName)); + memmove(t, sdraw.name, sdraw.nname*sizeof(DName)); + free(sdraw.name); + sdraw.name = t; + new = &sdraw.name[sdraw.nname++]; + new->name = smalloc(n+1); + memmove(new->name, str, n); + new->name[n] = 0; + new->dimage = di; + new->client = client; + new->vers = ++sdraw.vers; +} + +Client* +drawnewclient(void) +{ + Client *cl, **cp; + int i; + + for(i=0; islot = i; + cl->clientid = ++sdraw.clientid; + cl->op = SoverD; + sdraw.client[i] = cl; + return cl; +} + +static int +drawclientop(Client *cl) +{ + int op; + + op = cl->op; + cl->op = SoverD; + return op; +} + +int +drawhasclients(void) +{ + /* + * if draw has ever been used, we can't resize the frame buffer, + * even if all clients have exited (nclients is cumulative); it's too + * hard to make work. + */ + return sdraw.nclient != 0; +} + +Client* +drawclientofpath(ulong path) +{ + Client *cl; + int slot; + + slot = CLIENTPATH(path); + if(slot == 0) + return nil; + cl = sdraw.client[slot-1]; + if(cl==0 || cl->clientid==0) + return nil; + return cl; +} + + +Client* +drawclient(Chan *c) +{ + Client *client; + + client = drawclientofpath(c->qid.path); + if(client == nil) + error(Enoclient); + return client; +} + +Memimage* +drawimage(Client *client, uchar *a) +{ + DImage *d; + + d = drawlookup(client, BGLONG(a), 1); + if(d == nil) + error(Enodrawimage); + return d->image; +} + +void +drawrectangle(Rectangle *r, uchar *a) +{ + r->min.x = BGLONG(a+0*4); + r->min.y = BGLONG(a+1*4); + r->max.x = BGLONG(a+2*4); + r->max.y = BGLONG(a+3*4); +} + +void +drawpoint(Point *p, uchar *a) +{ + p->x = BGLONG(a+0*4); + p->y = BGLONG(a+1*4); +} + +Point +drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op) +{ + FChar *fc; + Rectangle r; + Point sp1; + + fc = &font->fchar[index]; + r.min.x = p.x+fc->left; + r.min.y = p.y-(font->ascent-fc->miny); + r.max.x = r.min.x+(fc->maxx-fc->minx); + r.max.y = r.min.y+(fc->maxy-fc->miny); + sp1.x = sp->x+fc->left; + sp1.y = sp->y+fc->miny; + memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op); + p.x += fc->width; + sp->x += fc->width; + return p; +} + +static int +initscreenimage(void) +{ + int width, depth; + ulong chan; + Rectangle r; + + if(screenimage != nil) + return 1; + + screendata.base = nil; + screendata.bdata = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen); + if(screendata.bdata == nil) + return 0; + screendata.ref = 1; + + screenimage = allocmemimaged(r, chan, &screendata); + if(screenimage == nil){ + /* RSC: BUG: detach screen */ + return 0; + } + + screenimage->width = width; + screenimage->clipr = r; + return 1; +} + +void +deletescreenimage(void) +{ + qlock(&sdraw); + /* RSC: BUG: detach screen */ + if(screenimage) + freememimage(screenimage); + screenimage = nil; + qunlock(&sdraw); +} + +static Chan* +drawattach(char *spec) +{ + qlock(&sdraw); + if(!initscreenimage()){ + qunlock(&sdraw); + error("no frame buffer"); + } + qunlock(&sdraw); + return devattach('i', spec); +} + +static Walkqid* +drawwalk(Chan *c, Chan *nc, char **name, int nname) +{ + if(screendata.bdata == nil) + error("no frame buffer"); + return devwalk(c, nc, name, nname, 0, 0, drawgen); +} + +static int +drawstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, drawgen); +} + +static Chan* +drawopen(Chan *c, int omode) +{ + Client *cl; + + if(c->qid.type & QTDIR) + return devopen(c, omode, 0, 0, drawgen); + + qlock(&sdraw); + if(waserror()){ + qunlock(&sdraw); + nexterror(); + } + + if(QID(c->qid) == Qnew){ + cl = drawnewclient(); + if(cl == 0) + error(Enodev); + c->qid.path = Qctl|((cl->slot+1)<qid)){ + case Qnew: + break; + + case Qctl: + cl = drawclient(c); + if(cl->busy) + error(Einuse); + cl->busy = 1; + flushrect = Rect(10000, 10000, -10000, -10000); + drawinstall(cl, 0, screenimage, 0); + incref(&cl->r); + break; + case Qcolormap: + case Qdata: + case Qrefresh: + cl = drawclient(c); + incref(&cl->r); + break; + } + qunlock(&sdraw); + poperror(); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + c->iounit = IOUNIT; + return c; +} + +static void +drawclose(Chan *c) +{ + int i; + DImage *d, **dp; + Client *cl; + Refresh *r; + + if(QID(c->qid) < Qcolormap) /* Qtopdir, Qnew, Q3rd, Q2nd have no client */ + return; + qlock(&sdraw); + if(waserror()){ + qunlock(&sdraw); + nexterror(); + } + + cl = drawclient(c); + if(QID(c->qid) == Qctl) + cl->busy = 0; + if((c->flag&COPEN) && (decref(&cl->r)==0)){ + while(r = cl->refresh){ /* assign = */ + cl->refresh = r->next; + free(r); + } + /* free names */ + for(i=0; icscreen) + drawuninstallscreen(cl, cl->cscreen); + /* all screens are freed, so now we can free images */ + dp = cl->dimage; + for(i=0; inext; + drawfreedimage(d); + } + dp++; + } + sdraw.client[cl->slot] = 0; + drawflush(); /* to erase visible, now dead windows */ + free(cl); + } + qunlock(&sdraw); + poperror(); +} + +long +drawread(Chan *c, void *a, long n, vlong off) +{ + int index, m; + ulong red, green, blue; + Client *cl; + uchar *p; + Refresh *r; + DImage *di; + Memimage *i; + ulong offset = off; + char buf[16]; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, drawgen); + cl = drawclient(c); + qlock(&sdraw); + if(waserror()){ + qunlock(&sdraw); + nexterror(); + } + switch(QID(c->qid)){ + case Qctl: + if(n < 12*12) + error(Eshortread); + if(cl->infoid < 0) + error(Enodrawimage); + if(cl->infoid == 0){ + i = screenimage; + if(i == nil) + error(Enodrawimage); + }else{ + di = drawlookup(cl, cl->infoid, 1); + if(di == nil) + error(Enodrawimage); + i = di->image; + } + n = sprint(a, "%11d %11d %11s %11d %11d %11d %11d %11d %11d %11d %11d %11d ", + cl->clientid, cl->infoid, chantostr(buf, i->chan), (i->flags&Frepl)==Frepl, + i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y, + i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y); + cl->infoid = -1; + break; + + case Qcolormap: + drawactive(1); /* to restore map from backup */ + p = malloc(4*12*256+1); + if(p == 0) + error(Enomem); + m = 0; + for(index = 0; index < 256; index++){ + getcolor(index, &red, &green, &blue); + m += sprint((char*)p+m, "%11d %11lud %11lud %11lud\n", index, red>>24, green>>24, blue>>24); + } + n = readstr(offset, a, n, (char*)p); + free(p); + break; + + case Qdata: + if(cl->readdata == nil) + error("no draw data"); + if(n < cl->nreaddata) + error(Eshortread); + n = cl->nreaddata; + memmove(a, cl->readdata, cl->nreaddata); + free(cl->readdata); + cl->readdata = nil; + break; + + case Qrefresh: + if(n < 5*4) + error(Ebadarg); + for(;;){ + if(cl->refreshme || cl->refresh) + break; + qunlock(&sdraw); + if(waserror()){ + qlock(&sdraw); /* restore lock for waserror() above */ + nexterror(); + } + sleep(&cl->refrend, drawrefactive, cl); + poperror(); + qlock(&sdraw); + } + p = a; + while(cl->refresh && n>=5*4){ + r = cl->refresh; + BPLONG(p+0*4, r->dimage->id); + BPLONG(p+1*4, r->r.min.x); + BPLONG(p+2*4, r->r.min.y); + BPLONG(p+3*4, r->r.max.x); + BPLONG(p+4*4, r->r.max.y); + cl->refresh = r->next; + free(r); + p += 5*4; + n -= 5*4; + } + cl->refreshme = 0; + n = p-(uchar*)a; + } + qunlock(&sdraw); + poperror(); + return n; +} + +void +drawwakeall(void) +{ + Client *cl; + int i; + + for(i=0; irefreshme || cl->refresh)) + wakeup(&cl->refrend); + } +} + +static long +drawwrite(Chan *c, void *a, long n, vlong) +{ + char buf[128], *fields[4], *q; + Client *cl; + int i, m, red, green, blue, x; + + if(c->qid.type & QTDIR) + error(Eisdir); + cl = drawclient(c); + qlock(&sdraw); + if(waserror()){ + drawwakeall(); + qunlock(&sdraw); + nexterror(); + } + switch(QID(c->qid)){ + case Qctl: + if(n != 4) + error("unknown draw control request"); + cl->infoid = BGLONG((uchar*)a); + break; + + case Qcolormap: + drawactive(1); /* to restore map from backup */ + m = n; + n = 0; + while(m > 0){ + x = m; + if(x > sizeof(buf)-1) + x = sizeof(buf)-1; + q = memccpy(buf, a, '\n', x); + if(q == 0) + break; + i = q-buf; + n += i; + a = (char*)a + i; + m -= i; + *q = 0; + if(tokenize(buf, fields, nelem(fields)) != 4) + error(Ebadarg); + i = strtoul(fields[0], 0, 0); + red = strtoul(fields[1], 0, 0); + green = strtoul(fields[2], 0, 0); + blue = strtoul(fields[3], &q, 0); + if(fields[3] == q) + error(Ebadarg); + if(red>255 || green>255 || blue>255 || i<0 || i>255) + error(Ebadarg); + red |= red<<8; + red |= red<<16; + green |= green<<8; + green |= green<<16; + blue |= blue<<8; + blue |= blue<<16; + setcolor(i, red, green, blue); + } + break; + + case Qdata: + drawmesg(cl, a, n); + drawwakeall(); + break; + + default: + error(Ebadusefd); + } + qunlock(&sdraw); + poperror(); + return n; +} + +uchar* +drawcoord(uchar *p, uchar *maxp, int oldx, int *newx) +{ + int b, x; + + if(p >= maxp) + error(Eshortdraw); + b = *p++; + x = b & 0x7F; + if(b & 0x80){ + if(p+1 >= maxp) + error(Eshortdraw); + x |= *p++ << 7; + x |= *p++ << 15; + if(x & (1<<22)) + x |= ~0<<23; + }else{ + if(b & 0x40) + x |= ~0<<7; + x += oldx; + } + *newx = x; + return p; +} + +static void +printmesg(char *fmt, uchar *a, int plsprnt) +{ + char buf[256]; + char *p, *q; + int s; + + if(1|| plsprnt==0){ + SET(s,q,p); + USED(fmt, a, buf, p, q, s); + return; + } + q = buf; + *q++ = *a++; + for(p=fmt; *p; p++){ + switch(*p){ + case 'l': + q += sprint(q, " %ld", (long)BGLONG(a)); + a += 4; + break; + case 'L': + q += sprint(q, " %.8lux", (ulong)BGLONG(a)); + a += 4; + break; + case 'R': + q += sprint(q, " [%d %d %d %d]", BGLONG(a), BGLONG(a+4), BGLONG(a+8), BGLONG(a+12)); + a += 16; + break; + case 'P': + q += sprint(q, " [%d %d]", BGLONG(a), BGLONG(a+4)); + a += 8; + break; + case 'b': + q += sprint(q, " %d", *a++); + break; + case 's': + q += sprint(q, " %d", BGSHORT(a)); + a += 2; + break; + case 'S': + q += sprint(q, " %.4ux", BGSHORT(a)); + a += 2; + break; + } + } + *q++ = '\n'; + *q = 0; + iprint("%.*s", (int)(q-buf), buf); +} + +void +drawmesg(Client *client, void *av, int n) +{ + int c, op, repl, m, y, dstid, scrnid, ni, ci, j, nw, e0, e1, ox, oy, esize, oesize, doflush; + uchar *u, *a, refresh; + char *fmt; + ulong value, chan; + Rectangle r, clipr; + Point p, q, *pp, sp; + Memimage *i, *dst, *src, *mask; + Memimage *l, **lp; + Memscreen *scrn; + DImage *font, *ll, *di, *ddst, *dsrc; + DName *dn; + DScreen *dscrn; + FChar *fc; + Refx *refx; + CScreen *cs; + Refreshfn reffn; + + a = av; + m = 0; + fmt = nil; + if(waserror()){ + if(fmt) printmesg(fmt, a, 1); + /* iprint("error: %s\n", up->env->errstr); */ + nexterror(); + } + while((n-=m) > 0){ + USED(fmt); + a += m; + switch(*a){ + default: + error("bad draw command"); + /* new allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1] R[4*4] clipR[4*4] rrggbbaa[4] */ + case 'b': + printmesg(fmt="LLbLbRRL", a, 0); + m = 1+4+4+1+4+1+4*4+4*4+4; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + scrnid = BGSHORT(a+5); + refresh = a[9]; + chan = BGLONG(a+10); + repl = a[14]; + drawrectangle(&r, a+15); + drawrectangle(&clipr, a+31); + value = BGLONG(a+47); + if(drawlookup(client, dstid, 0)) + error(Eimageexists); + if(scrnid){ + dscrn = drawlookupscreen(client, scrnid, &cs); + scrn = dscrn->screen; + if(repl || chan!=scrn->image->chan) + error("image parameters incompatible with screen"); + reffn = nil; + switch(refresh){ + case Refbackup: + break; + case Refnone: + reffn = memlnorefresh; + break; + case Refmesg: + reffn = drawrefresh; + break; + default: + error("unknown refresh method"); + } + l = memlalloc(scrn, r, reffn, 0, value); + if(l == 0) + error(Edrawmem); + dstflush(l->layer->screen->image, l->layer->screenr); + l->clipr = clipr; + rectclip(&l->clipr, r); + if(drawinstall(client, dstid, l, dscrn) == 0){ + memldelete(l); + error(Edrawmem); + } + dscrn->ref++; + if(reffn){ + refx = nil; + if(reffn == drawrefresh){ + refx = malloc(sizeof(Refx)); + if(refx == 0){ + drawuninstall(client, dstid); + error(Edrawmem); + } + refx->client = client; + refx->dimage = drawlookup(client, dstid, 1); + } + memlsetrefresh(l, reffn, refx); + } + continue; + } + i = allocmemimage(r, chan); + if(i == 0) + error(Edrawmem); + if(repl) + i->flags |= Frepl; + i->clipr = clipr; + if(!repl) + rectclip(&i->clipr, r); + if(drawinstall(client, dstid, i, 0) == 0){ + freememimage(i); + error(Edrawmem); + } + memfillcolor(i, value); + continue; + + /* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */ + case 'A': + printmesg(fmt="LLLb", a, 1); + m = 1+4+4+4+1; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(dstid == 0) + error(Ebadarg); + if(drawlookupdscreen(dstid)) + error(Escreenexists); + ddst = drawlookup(client, BGLONG(a+5), 1); + dsrc = drawlookup(client, BGLONG(a+9), 1); + if(ddst==0 || dsrc==0) + error(Enodrawimage); + if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[13]) == 0) + error(Edrawmem); + continue; + + /* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */ + case 'c': + printmesg(fmt="LbR", a, 0); + m = 1+4+1+4*4; + if(n < m) + error(Eshortdraw); + ddst = drawlookup(client, BGLONG(a+1), 1); + if(ddst == nil) + error(Enodrawimage); + if(ddst->name) + error("cannot change repl/clipr of shared image"); + dst = ddst->image; + if(a[5]) + dst->flags |= Frepl; + drawrectangle(&dst->clipr, a+6); + continue; + + /* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */ + case 'd': + printmesg(fmt="LLLRPP", a, 0); + m = 1+4+4+4+4*4+2*4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + src = drawimage(client, a+5); + mask = drawimage(client, a+9); + drawrectangle(&r, a+13); + drawpoint(&p, a+29); + drawpoint(&q, a+37); + op = drawclientop(client); + memdraw(dst, r, src, p, mask, q, op); + dstflush(dst, r); + continue; + + /* toggle debugging: 'D' val[1] */ + case 'D': + printmesg(fmt="b", a, 0); + m = 1+1; + if(n < m) + error(Eshortdraw); + drawdebug = a[1]; + continue; + + /* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/ + case 'e': + case 'E': + printmesg(fmt="LLPlllPll", a, 0); + m = 1+4+4+2*4+4+4+4+2*4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + src = drawimage(client, a+5); + drawpoint(&p, a+9); + e0 = BGLONG(a+17); + e1 = BGLONG(a+21); + if(e0<0 || e1<0) + error("invalid ellipse semidiameter"); + j = BGLONG(a+25); + if(j < 0) + error("negative ellipse thickness"); + drawpoint(&sp, a+29); + c = j; + if(*a == 'E') + c = -1; + ox = BGLONG(a+37); + oy = BGLONG(a+41); + op = drawclientop(client); + /* high bit indicates arc angles are present */ + if(ox & (1<<31)){ + if((ox & (1<<30)) == 0) + ox &= ~(1<<31); + memarc(dst, p, e0, e1, c, src, sp, ox, oy, op); + }else + memellipse(dst, p, e0, e1, c, src, sp, op); + dstflush(dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1)); + continue; + + /* free: 'f' id[4] */ + case 'f': + printmesg(fmt="L", a, 1); + m = 1+4; + if(n < m) + error(Eshortdraw); + ll = drawlookup(client, BGLONG(a+1), 0); + if(ll && ll->dscreen && ll->dscreen->owner != client) + ll->dscreen->owner->refreshme = 1; + drawuninstall(client, BGLONG(a+1)); + continue; + + /* free screen: 'F' id[4] */ + case 'F': + printmesg(fmt="L", a, 1); + m = 1+4; + if(n < m) + error(Eshortdraw); + drawlookupscreen(client, BGLONG(a+1), &cs); + drawuninstallscreen(client, cs); + continue; + + /* initialize font: 'i' fontid[4] nchars[4] ascent[1] */ + case 'i': + printmesg(fmt="Llb", a, 1); + m = 1+4+4+1; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + dst = drawimage(client, a+1); + if(dstid == 0 || dst == screenimage) + error("cannot use display as font"); + font = drawlookup(client, dstid, 1); + if(font == 0) + error(Enodrawimage); + if(font->image->layer) + error("cannot use window as font"); + ni = BGLONG(a+5); + if(ni<=0 || ni>4096) + error("bad font size (4096 chars max)"); + free(font->fchar); /* should we complain if non-zero? */ + font->fchar = malloc(ni*sizeof(FChar)); + if(font->fchar == 0) + error(Enomem); + memset(font->fchar, 0, ni*sizeof(FChar)); + font->nfchar = ni; + font->ascent = a[9]; + continue; + + /* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */ + case 'l': + printmesg(fmt="LLSRPbb", a, 0); + m = 1+4+4+2+4*4+2*4+1+1; + if(n < m) + error(Eshortdraw); + font = drawlookup(client, BGLONG(a+1), 1); + if(font == 0) + error(Enodrawimage); + if(font->nfchar == 0) + error(Enotfont); + src = drawimage(client, a+5); + ci = BGSHORT(a+9); + if(ci >= font->nfchar) + error(Eindex); + drawrectangle(&r, a+11); + drawpoint(&p, a+27); + memdraw(font->image, r, src, p, memopaque, p, S); + fc = &font->fchar[ci]; + fc->minx = r.min.x; + fc->maxx = r.max.x; + fc->miny = r.min.y; + fc->maxy = r.max.y; + fc->left = a[35]; + fc->width = a[36]; + continue; + + /* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */ + case 'L': + printmesg(fmt="LPPlllLP", a, 0); + m = 1+4+2*4+2*4+4+4+4+4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + drawpoint(&p, a+5); + drawpoint(&q, a+13); + e0 = BGLONG(a+21); + e1 = BGLONG(a+25); + j = BGLONG(a+29); + if(j < 0) + error("negative line width"); + src = drawimage(client, a+33); + drawpoint(&sp, a+37); + op = drawclientop(client); + memline(dst, p, q, e0, e1, j, src, sp, op); + /* avoid memlinebbox if possible */ + if(dst == screenimage || dst->layer!=nil){ + /* BUG: this is terribly inefficient: update maximal containing rect*/ + r = memlinebbox(p, q, e0, e1, j); + dstflush(dst, insetrect(r, -(1+1+j))); + } + continue; + + /* create image mask: 'm' newid[4] id[4] */ +/* + * + case 'm': + printmesg("LL", a, 0); + m = 4+4; + if(n < m) + error(Eshortdraw); + break; + * + */ + + /* attach to a named image: 'n' dstid[4] j[1] name[j] */ + case 'n': + printmesg(fmt="Lz", a, 0); + m = 1+4+1; + if(n < m) + error(Eshortdraw); + j = a[5]; + if(j == 0) /* give me a non-empty name please */ + error(Eshortdraw); + m += j; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(drawlookup(client, dstid, 0)) + error(Eimageexists); + dn = drawlookupname(j, (char*)a+6); + if(dn == nil) + error(Enoname); + if(drawinstall(client, dstid, dn->dimage->image, 0) == 0) + error(Edrawmem); + di = drawlookup(client, dstid, 0); + if(di == 0) + error("draw: cannot happen"); + di->vers = dn->vers; + di->name = smalloc(j+1); + di->fromname = dn->dimage; + di->fromname->ref++; + memmove(di->name, a+6, j); + di->name[j] = 0; + client->infoid = dstid; + continue; + + /* name an image: 'N' dstid[4] in[1] j[1] name[j] */ + case 'N': + printmesg(fmt="Lbz", a, 0); + m = 1+4+1+1; + if(n < m) + error(Eshortdraw); + c = a[5]; + j = a[6]; + if(j == 0) /* give me a non-empty name please */ + error(Eshortdraw); + m += j; + if(n < m) + error(Eshortdraw); + di = drawlookup(client, BGLONG(a+1), 0); + if(di == 0) + error(Enodrawimage); + if(di->name) + error(Enamed); + if(c) + drawaddname(client, di, j, (char*)a+7); + else{ + dn = drawlookupname(j, (char*)a+7); + if(dn == nil) + error(Enoname); + if(dn->dimage != di) + error(Ewrongname); + drawdelname(dn); + } + continue; + + /* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */ + case 'o': + printmesg(fmt="LPP", a, 0); + m = 1+4+2*4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + if(dst->layer){ + drawpoint(&p, a+5); + drawpoint(&q, a+13); + r = dst->layer->screenr; + ni = memlorigin(dst, p, q); + if(ni < 0) + error("image origin failed"); + if(ni > 0){ + dstflush(dst->layer->screen->image, r); + dstflush(dst->layer->screen->image, dst->layer->screenr); + ll = drawlookup(client, BGLONG(a+1), 1); + drawrefreshscreen(ll, client); + } + } + continue; + + /* set compositing operator for next draw operation: 'O' op */ + case 'O': + printmesg(fmt="b", a, 0); + m = 1+1; + if(n < m) + error(Eshortdraw); + client->op = a[1]; + continue; + + /* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ + /* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ + case 'p': + case 'P': + printmesg(fmt="LslllLPP", a, 0); + m = 1+4+2+4+4+4+4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + ni = BGSHORT(a+5); + if(ni < 0) + error("negative count in polygon"); + e0 = BGLONG(a+7); + e1 = BGLONG(a+11); + j = 0; + if(*a == 'p'){ + j = BGLONG(a+15); + if(j < 0) + error("negative polygon line width"); + } + src = drawimage(client, a+19); + drawpoint(&sp, a+23); + drawpoint(&p, a+31); + ni++; + pp = malloc(ni*sizeof(Point)); + if(pp == nil) + error(Enomem); + doflush = 0; + if(dst == screenimage || (dst->layer && dst->layer->screen->image->data == screenimage->data)) + doflush = 1; /* simplify test in loop */ + ox = oy = 0; + esize = 0; + u = a+m; + for(y=0; y esize) + esize = c; + } + if(y == ni-1){ + c = memlineendsize(e1); + if(c > esize) + esize = c; + } + } + if(*a=='P' && e0!=1 && e0 !=~0) + r = dst->clipr; + else if(y > 0){ + r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1); + combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); + } + if(rectclip(&r, dst->clipr)) /* should perhaps be an arg to dstflush */ + dstflush(dst, r); + } + pp[y] = p; + } + if(y == 1) + dstflush(dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); + op = drawclientop(client); + if(*a == 'p') + mempoly(dst, pp, ni, e0, e1, j, src, sp, op); + else + memfillpoly(dst, pp, ni, e0, src, sp, op); + free(pp); + m = u-a; + continue; + + /* read: 'r' id[4] R[4*4] */ + case 'r': + printmesg(fmt="LR", a, 0); + m = 1+4+4*4; + if(n < m) + error(Eshortdraw); + i = drawimage(client, a+1); + drawrectangle(&r, a+5); + if(!rectinrect(r, i->r)) + error(Ereadoutside); + c = bytesperline(r, i->depth); + c *= Dy(r); + free(client->readdata); + client->readdata = mallocz(c, 0); + if(client->readdata == nil) + error("readimage malloc failed"); + client->nreaddata = memunload(i, r, client->readdata, c); + if(client->nreaddata < 0){ + free(client->readdata); + client->readdata = nil; + error("bad readimage call"); + } + continue; + + /* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */ + /* stringbg: 'x' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] bgid[4] bgpt[2*4] ni*(index[2]) */ + case 's': + case 'x': + printmesg(fmt="LLLPRPs", a, 0); + m = 1+4+4+4+2*4+4*4+2*4+2; + if(*a == 'x') + m += 4+2*4; + if(n < m) + error(Eshortdraw); + + dst = drawimage(client, a+1); + src = drawimage(client, a+5); + font = drawlookup(client, BGLONG(a+9), 1); + if(font == 0) + error(Enodrawimage); + if(font->nfchar == 0) + error(Enotfont); + drawpoint(&p, a+13); + drawrectangle(&r, a+21); + drawpoint(&sp, a+37); + ni = BGSHORT(a+45); + u = a+m; + m += ni*2; + if(n < m) + error(Eshortdraw); + clipr = dst->clipr; + dst->clipr = r; + op = drawclientop(client); + if(*a == 'x'){ + /* paint background */ + l = drawimage(client, a+47); + drawpoint(&q, a+51); + r.min.x = p.x; + r.min.y = p.y-font->ascent; + r.max.x = p.x; + r.max.y = r.min.y+Dy(font->image->r); + j = ni; + while(--j >= 0){ + ci = BGSHORT(u); + if(ci<0 || ci>=font->nfchar){ + dst->clipr = clipr; + error(Eindex); + } + r.max.x += font->fchar[ci].width; + u += 2; + } + memdraw(dst, r, l, q, memopaque, ZP, op); + u -= 2*ni; + } + q = p; + while(--ni >= 0){ + ci = BGSHORT(u); + if(ci<0 || ci>=font->nfchar){ + dst->clipr = clipr; + error(Eindex); + } + q = drawchar(dst, q, src, &sp, font, ci, op); + u += 2; + } + dst->clipr = clipr; + p.y -= font->ascent; + dstflush(dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r))); + continue; + + /* use public screen: 'S' id[4] chan[4] */ + case 'S': + printmesg(fmt="Ll", a, 0); + m = 1+4+4; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(dstid == 0) + error(Ebadarg); + dscrn = drawlookupdscreen(dstid); + if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client)) + error(Enodrawscreen); + if(dscrn->screen->image->chan != BGLONG(a+5)) + error("inconsistent chan"); + if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0) + error(Edrawmem); + continue; + + /* top or bottom windows: 't' top[1] nw[2] n*id[4] */ + case 't': + printmesg(fmt="bsL", a, 0); + m = 1+1+2; + if(n < m) + error(Eshortdraw); + nw = BGSHORT(a+2); + if(nw < 0) + error(Ebadarg); + if(nw == 0) + continue; + m += nw*4; + if(n < m) + error(Eshortdraw); + lp = malloc(nw*sizeof(Memimage*)); + if(lp == 0) + error(Enomem); + if(waserror()){ + free(lp); + nexterror(); + } + for(j=0; jlayer == 0) + error("images are not windows"); + for(j=1; jlayer->screen != lp[0]->layer->screen) + error("images not on same screen"); + if(a[1]) + memltofrontn(lp, nw); + else + memltorearn(lp, nw); + if(lp[0]->layer->screen->image->data == screenimage->data) + for(j=0; jlayer->screen->image, lp[j]->layer->screenr); + ll = drawlookup(client, BGLONG(a+1+1+2), 1); + drawrefreshscreen(ll, client); + poperror(); + free(lp); + continue; + + /* visible: 'v' */ + case 'v': + printmesg(fmt="", a, 0); + m = 1; + drawflush(); + continue; + + /* write: 'y' id[4] R[4*4] data[x*1] */ + /* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */ + case 'y': + case 'Y': + printmesg(fmt="LR", a, 0); + // iprint("load %c\n", *a); + m = 1+4+4*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + drawrectangle(&r, a+5); + if(!rectinrect(r, dst->r)) + error(Ewriteoutside); + y = memload(dst, r, a+m, n-m, *a=='Y'); + if(y < 0) + error("bad writeimage call"); + dstflush(dst, r); + m += y; + continue; + } + } + poperror(); +} + +int +drawlsetrefresh(ulong qidpath, int id, void *reffn, void *refx) +{ + DImage *d; + Memimage *i; + Client *client; + + client = drawclientofpath(qidpath); + if(client == 0) + return 0; + d = drawlookup(client, id, 0); + if(d == nil) + return 0; + i = d->image; + if(i->layer == nil) + return 0; + return memlsetrefresh(i, reffn, refx); +} + +Dev drawdevtab = { + 'i', + "draw", + + devreset, + devinit, + devshutdown, + drawattach, + drawwalk, + drawstat, + drawopen, + devcreate, + drawclose, + drawread, + devbread, + drawwrite, + devbwrite, + devremove, + devwstat, +}; + +/* + * On 8 bit displays, load the default color map + */ +void +drawcmap(void) +{ + int r, g, b, cr, cg, cb, v; + int num, den; + int i, j; + + drawactive(1); /* to restore map from backup */ + for(r=0,i=0; r!=4; r++) + for(v=0; v!=4; v++,i+=16){ + for(g=0,j=v-r; g!=4; g++) + for(b=0;b!=4;b++,j++){ + den = r; + if(g > den) + den = g; + if(b > den) + den = b; + if(den == 0) /* divide check -- pick grey shades */ + cr = cg = cb = v*17; + else{ + num = 17*(4*den+v); + cr = r*num/den; + cg = g*num/den; + cb = b*num/den; + } + setcolor(i+(j&15), + cr*0x01010101, cg*0x01010101, cb*0x01010101); + } + } +} + +void +drawblankscreen(int blank) +{ + int i, nc; + ulong *p; + + if(blank == sdraw.blanked) + return; + if(!canqlock(&sdraw)) + return; + if(!initscreenimage()){ + qunlock(&sdraw); + return; + } + p = sdraw.savemap; + nc = screenimage->depth > 8 ? 256 : 1<depth; + + /* + * blankscreen uses the hardware to blank the screen + * when possible. to help in cases when it is not possible, + * we set the color map to be all black. + */ + if(blank == 0){ /* turn screen on */ + for(i=0; i= blanktime) + drawblankscreen(1); + else + sdraw.blanktime++; + } +} + +void +interf(void) +{ + /* force it to load */ + drawreplxy(0, 0, 0); +} + +int +drawidletime(void) +{ + return TK2SEC(MACHP(0)->ticks - sdraw.blanktime)/60; +} diff --git a/os/port/devds.c b/os/port/devds.c new file mode 100644 index 00000000..6be12007 --- /dev/null +++ b/os/port/devds.c @@ -0,0 +1,605 @@ +/* + * (file system) device subsystems + * '#k'. + * Follows device config in Ken's file server. + * Builds mirrors, device cats, interleaving, and partition of devices out of + * other (inner) devices. + * + * This code is from Plan 9, and subject to the Lucent Public License 1.02. + * Only the name changed for Inferno (name clash). + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +enum { + Fmirror, // mirror of others + Fcat, // catenation of others + Finter, // interleaving of others + Fpart, // part of others + + Blksize = 8*1024, // for Finter only + Maxconf = 1024, // max length for config + + Nfsdevs = 64, + Ndevs = 8, + + Qtop = 0, // top dir (contains "ds") + Qdir = 1, // actual dir + Qctl = 2, // ctl file + Qfirst = 3, // first fs file +}; + +#define Cfgstr "fsdev:\n" + +typedef struct Fsdev Fsdev; + +struct Fsdev +{ + int type; + char *name; // name for this fsdev + vlong start; // start address (for Fpart) + vlong size; // min(idev sizes) + int ndevs; // number of inner devices + char *iname[Ndevs]; // inner device names + Chan *idev[Ndevs]; // inner devices + vlong isize[Ndevs]; // sizes for inneer devices +}; + +/* + * Once configured, a fsdev is never removed. The name of those + * configured is never nil. We have no locks here. + */ +static Fsdev fsdev[Nfsdevs]; + +static Qid tqid = {Qtop, 0, QTDIR}; +static Qid dqid = {Qdir, 0, QTDIR}; +static Qid cqid = {Qctl, 0, 0}; + +static Cmdtab configs[] = { + Fmirror,"mirror", 0, + Fcat, "cat", 0, + Finter, "inter", 0, + Fpart, "part", 5, +}; + +static char confstr[Maxconf]; +static int configed; + + +static Fsdev* +path2dev(int i, int mustexist) +{ + if (i < 0 || i >= nelem(fsdev)) + error("bug: bad index in devfsdev"); + if (mustexist && fsdev[i].name == nil) + error(Enonexist); + + if (fsdev[i].name == nil) + return nil; + else + return &fsdev[i]; +} + +static Fsdev* +devalloc(void) +{ + int i; + + for (i = 0; i < nelem(fsdev); i++) + if (fsdev[i].name == nil) + break; + if (i == nelem(fsdev)) + error(Enodev); + + return &fsdev[i]; +} + +static void +setdsize(Fsdev* mp) +{ + uchar buf[128]; /* old DIRLEN plus a little should be plenty */ + int i; + Chan *mc; + Dir d; + long l; + + if (mp->type != Fpart){ + mp->start= 0; + mp->size = 0LL; + } + for (i = 0; i < mp->ndevs; i++){ + mc = mp->idev[i]; + l = devtab[mc->type]->stat(mc, buf, sizeof(buf)); + convM2D(buf, l, &d, nil); + mp->isize[i] = d.length; + switch(mp->type){ + case Fmirror: + if (mp->size == 0LL || mp->size > d.length) + mp->size = d.length; + break; + case Fcat: + mp->size += d.length; + break; + case Finter: + // truncate to multiple of Blksize + d.length = (d.length & ~(Blksize-1)); + mp->isize[i] = d.length; + mp->size += d.length; + break; + case Fpart: + // should raise errors here? + if (mp->start > d.length) + mp->start = d.length; + if (d.length < mp->start + mp->size) + mp->size = d.length - mp->start; + break; + } + } +} + +static void +mpshut(Fsdev *mp) +{ + int i; + char *nm; + + nm = mp->name; + mp->name = nil; // prevent others from using this. + if (nm) + free(nm); + for (i = 0; i < mp->ndevs; i++){ + if (mp->idev[i] != nil) + cclose(mp->idev[i]); + if (mp->iname[i]) + free(mp->iname[i]); + } + memset(mp, 0, sizeof(*mp)); +} + + +static void +mconfig(char* a, long n) // "name idev0 idev1" +{ + static QLock lck; + Cmdbuf *cb; + Cmdtab *ct; + Fsdev *mp; + int i; + char *oldc; + char *c; + vlong size, start; + + size = 0; + start = 0; + if (confstr[0] == 0) + seprint(confstr, confstr+sizeof(confstr), Cfgstr); + oldc = confstr + strlen(confstr); + qlock(&lck); + if (waserror()){ + *oldc = 0; + qunlock(&lck); + nexterror(); + } + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + c = oldc; + for (i = 0; i < cb->nf; i++) + c = seprint(c, confstr+sizeof(confstr), "%s ", cb->f[i]); + *(c-1) = '\n'; + ct = lookupcmd(cb, configs, nelem(configs)); + cb->f++; // skip command + cb->nf--; + if (ct->index == Fpart){ + size = strtoll(cb->f[3], nil, 10); + cb->nf--; + start = strtoll(cb->f[2], nil, 10); + cb->nf--; + } + for (i = 0; i < nelem(fsdev); i++) + if (fsdev[i].name != nil && strcmp(fsdev[i].name, cb->f[0])==0) + error(Eexist); + if (cb->nf - 1 > Ndevs) + error("too many devices; fix me"); + for (i = 0; i < cb->nf; i++) + validname(cb->f[i], (i != 0)); + mp = devalloc(); + if(waserror()){ + mpshut(mp); + nexterror(); + } + mp->type = ct->index; + if (mp->type == Fpart){ + mp->size = size; + mp->start = start; + } + kstrdup(&mp->name, cb->f[0]); + for (i = 1; i < cb->nf; i++){ + kstrdup(&mp->iname[i-1], cb->f[i]); + mp->idev[i-1] = namec(mp->iname[i-1], Aopen, ORDWR, 0); + if (mp->idev[i-1] == nil) + error(Egreg); + mp->ndevs++; + } + setdsize(mp); + poperror(); + free(cb); + poperror(); + poperror(); + configed = 1; + qunlock(&lck); + +} + +static void +rdconf(void) +{ + int mustrd; + char *s; + char *c; + char *p; + char *e; + Chan *cc; + + s = getconf("fsconfig"); + if (s == nil){ + mustrd = 0; + s = "/dev/sdC0/fscfg"; + } else + mustrd = 1; + if (waserror()){ + configed = 1; + if (!mustrd) + return; + nexterror(); + } + cc = namec(s, Aopen, OREAD, 0); + if(waserror()){ + cclose(cc); + nexterror(); + } + devtab[cc->type]->read(cc, confstr, sizeof(confstr), 0); + poperror(); + cclose(cc); + if (strncmp(confstr, Cfgstr, strlen(Cfgstr)) != 0) + error("config string must start with `fsdev:'"); + kstrdup(&c, confstr + strlen(Cfgstr)); + if(waserror()){ + free(c); + nexterror(); + } + memset(confstr, 0, sizeof(confstr)); + for (p = c; p != nil && *p != 0; p = e){ + e = strchr(p, '\n'); + if (e == p){ + e++; + continue; + } + if (e == nil) + e = p + strlen(p); + mconfig(p, e - p); + } + poperror(); + poperror(); +} + + +static int +mgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp) +{ + Qid qid; + Fsdev *mp; + + if (c->qid.path == Qtop){ + switch(i){ + case DEVDOTDOT: + devdir(c, tqid, "#k", 0, eve, DMDIR|0775, dp); + return 1; + case 0: + devdir(c, dqid, "ds", 0, eve, DMDIR|0775, dp); + return 1; + default: + return -1; + } + } + if (c->qid.path != Qdir){ + switch(i){ + case DEVDOTDOT: + devdir(c, dqid, "ds", 0, eve, DMDIR|0775, dp); + return 1; + default: + return -1; + } + } + switch(i){ + case DEVDOTDOT: + devdir(c, tqid, "#k", 0, eve, DMDIR|0775, dp); + return 1; + case 0: + devdir(c, cqid, "ctl", 0, eve, 0664, dp); + return 1; + } + i--; // for ctl + qid.path = Qfirst + i; + qid.vers = 0; + qid.type = 0; + mp = path2dev(i, 0); + if (mp == nil) + return -1; + kstrcpy(up->genbuf, mp->name, sizeof(up->genbuf)); + devdir(c, qid, up->genbuf, mp->size, eve, 0664, dp); + return 1; +} + +static Chan* +mattach(char *spec) +{ + *confstr = 0; + return devattach(L'k', spec); +} + +static Walkqid* +mwalk(Chan *c, Chan *nc, char **name, int nname) +{ + if (!configed) + rdconf(); + return devwalk(c, nc, name, nname, 0, 0, mgen); +} + +static int +mstat(Chan *c, uchar *db, int n) +{ + Dir d; + Fsdev *mp; + int p; + + p = c->qid.path; + memset(&d, 0, sizeof(d)); + switch(p){ + case Qtop: + devdir(c, tqid, "#k", 0, eve, DMDIR|0775, &d); + break; + case Qdir: + devdir(c, dqid, "ds", 0, eve, DMDIR|0775, &d); + break; + case Qctl: + devdir(c, cqid, "ctl", 0, eve, 0664, &d); + break; + default: + mp = path2dev(p - Qfirst, 1); + devdir(c, c->qid, mp->name, mp->size, eve, 0664, &d); + } + n = convD2M(&d, db, n); + if (n == 0) + error(Ebadarg); + return n; +} + +static Chan* +mopen(Chan *c, int omode) +{ + if((c->qid.type & QTDIR) && omode != OREAD) + error(Eperm); + if (omode & OTRUNC) + omode &= ~OTRUNC; + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +mclose(Chan*) +{ + // that's easy +} + +static long +catio(Fsdev *mp, int isread, void *a, long n, vlong off) +{ + int i; + Chan* mc; + long l, wl, res; + //print("catio %d %p %ld %lld\n", isread, a, n, off); + res = n; + for (i = 0; n >= 0 && i < mp->ndevs ; i++){ + mc = mp->idev[i]; + if (off > mp->isize[i]){ + off -= mp->isize[i]; + continue; + } + if (off + n > mp->isize[i]) + l = mp->isize[i] - off; + else + l = n; + //print("\tdev %d %p %ld %lld\n", i, a, l, off); + + if (isread) + wl = devtab[mc->type]->read(mc, a, l, off); + else + wl = devtab[mc->type]->write(mc, a, l, off); + if (wl != l) + error("#k: write failed"); + a = (char*)a + l; + off = 0; + n -= l; + } + //print("\tres %ld\n", res - n); + return res - n; +} + +static long +interio(Fsdev *mp, int isread, void *a, long n, vlong off) +{ + int i; + Chan* mc; + long l, wl, wsz; + vlong woff, blk, mblk; + long boff, res; + + blk = off / Blksize; + boff = off % Blksize; + wsz = Blksize - boff; + res = n; + while(n > 0){ + i = blk % mp->ndevs; + mc = mp->idev[i]; + mblk = blk / mp->ndevs; + woff = mblk * Blksize + boff; + if (n > wsz) + l = wsz; + else + l = n; + if (isread) + wl = devtab[mc->type]->read(mc, a, l, woff); + else + wl = devtab[mc->type]->write(mc, a, l, woff); + if (wl != l || l == 0) + error(Eio); + a = (char*)a + l; + n -= l; + blk++; + boff = 0; + wsz = Blksize; + } + return res; +} + +static long +mread(Chan *c, void *a, long n, vlong off) +{ + int i; + Fsdev *mp; + Chan *mc; + long l; + long res; + + if (c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, mgen); + if (c->qid.path == Qctl) + return readstr((long)off, a, n, confstr + strlen(Cfgstr)); + i = c->qid.path - Qfirst; + mp = path2dev(i, 1); + + if (off >= mp->size) + return 0; + if (off + n > mp->size) + n = mp->size - off; + if (n == 0) + return 0; + + res = -1; + switch(mp->type){ + case Fmirror: + for (i = 0; i < mp->ndevs; i++){ + mc = mp->idev[i]; + if (waserror()){ + // if a read fails we let the user know and try + // another device. + print("#k: mread: (%llx %d): %s\n", + c->qid.path, i, up->env->errstr); + continue; + } + l = devtab[mc->type]->read(mc, a, n, off); + poperror(); + if (l >=0){ + res = l; + break; + } + } + if (i == mp->ndevs) + error(Eio); + break; + case Fcat: + res = catio(mp, 1, a, n, off); + break; + case Finter: + res = interio(mp, 1, a, n, off); + break; + case Fpart: + off += mp->start; + mc = mp->idev[0]; + res = devtab[mc->type]->read(mc, a, n, off); + break; + } + return res; +} + +static long +mwrite(Chan *c, void *a, long n, vlong off) +{ + Fsdev *mp; + long l, res; + int i; + Chan *mc; + + if (c->qid.type & QTDIR) + error(Eperm); + if (c->qid.path == Qctl){ + mconfig(a, n); + return n; + } + mp = path2dev(c->qid.path - Qfirst, 1); + + if (off >= mp->size) + return 0; + if (off + n > mp->size) + n = mp->size - off; + if (n == 0) + return 0; + res = n; + switch(mp->type){ + case Fmirror: + for (i = mp->ndevs-1; i >=0; i--){ + mc = mp->idev[i]; + l = devtab[mc->type]->write(mc, a, n, off); + if (l < res) + res = l; + } + break; + case Fcat: + res = catio(mp, 0, a, n, off); + break; + case Finter: + res = interio(mp, 0, a, n, off); + break; + case Fpart: + mc = mp->idev[0]; + off += mp->start; + l = devtab[mc->type]->write(mc, a, n, off); + if (l < res) + res = l; + break; + } + return res; +} + +Dev dsdevtab = { + 'k', + "ds", + + devreset, + devinit, + devshutdown, + mattach, + mwalk, + mstat, + mopen, + devcreate, + mclose, + mread, + devbread, + mwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devdup.c b/os/port/devdup.c new file mode 100644 index 00000000..b176d6c0 --- /dev/null +++ b/os/port/devdup.c @@ -0,0 +1,151 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +/* Qid is (2*fd + (file is ctl))+1 */ + +static int +dupgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp) +{ + Fgrp *fgrp = up->env->fgrp; + Chan *f; + static int perm[] = { 0400, 0200, 0600, 0 }; + int p; + Qid q; + + if(s == DEVDOTDOT){ + devdir(c, c->qid, ".", 0, eve, DMDIR|0555, dp); + return 1; + } + if(s == 0) + return 0; + s--; + if(s/2 > fgrp->maxfd) + return -1; + if((f=fgrp->fd[s/2]) == nil) + return 0; + if(s & 1){ + p = 0400; + sprint(up->genbuf, "%dctl", s/2); + }else{ + p = perm[f->mode&3]; + sprint(up->genbuf, "%d", s/2); + } + mkqid(&q, s+1, 0, QTFILE); + devdir(c, q, up->genbuf, 0, eve, p, dp); + return 1; +} + +static Chan* +dupattach(char *spec) +{ + return devattach('d', spec); +} + +static Walkqid* +dupwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, dupgen); +} + +static int +dupstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, nil, 0, dupgen); +} + +static Chan* +dupopen(Chan *c, int omode) +{ + Chan *f; + int fd, twicefd; + + if(c->qid.type & QTDIR){ + if(omode != 0) + error(Eisdir); + c->mode = 0; + c->flag |= COPEN; + c->offset = 0; + return c; + } + if(c->qid.type & QTAUTH) + error(Eperm); + twicefd = c->qid.path - 1; + fd = twicefd/2; + if((twicefd & 1)){ + /* ctl file */ + f = c; + f->mode = openmode(omode); + f->flag |= COPEN; + f->offset = 0; + }else{ + /* fd file */ + f = fdtochan(up->env->fgrp, fd, openmode(omode), 0, 1); + cclose(c); + } + if(omode & OCEXEC) + f->flag |= CCEXEC; + return f; +} + +static void +dupclose(Chan*) +{ +} + +static long +dupread(Chan *c, void *va, long n, vlong offset) +{ + char *a = va; + char buf[256]; + int fd, twicefd; + + if(c->qid.type == QTDIR) + return devdirread(c, a, n, nil, 0, dupgen); + twicefd = c->qid.path - 1; + fd = twicefd/2; + if(twicefd & 1){ + c = fdtochan(up->env->fgrp, fd, -1, 0, 1); + if(waserror()){ + cclose(c); + nexterror(); + } + progfdprint(c, fd, 0, buf, sizeof buf); + poperror(); + cclose(c); + return readstr((ulong)offset, va, n, buf); + } + panic("dupread"); + return 0; +} + +static long +dupwrite(Chan*, void*, long, vlong) +{ + panic("dupwrite"); + return 0; /* not reached */ +} + +Dev dupdevtab = { + 'd', + "dup", + + devreset, + devinit, + devshutdown, + dupattach, + dupwalk, + dupstat, + dupopen, + devcreate, + dupclose, + dupread, + devbread, + dupwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devdynld.c b/os/port/devdynld.c new file mode 100644 index 00000000..cc53308c --- /dev/null +++ b/os/port/devdynld.c @@ -0,0 +1,365 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include +#include +#include + +/* + * TO DO + * - dynamic allocation of Dev.dc + * - inter-module dependencies + * - reference count on Dev to allow error("inuse") [or how else to do it] + * - is Dev.shutdown the right function to call? Dev.config perhaps? + */ + +#define DBG if(1) print +#define NATIVE + + +extern ulong ndevs; + +enum +{ + Qdir, + Qdynld, + Qdynsyms, + + DEVCHAR = 'L', +}; + +static Dirtab dltab[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "dynld", {Qdynld}, 0, 0644, + "dynsyms", {Qdynsyms}, 0, 0444, +}; + +typedef struct Dyndev Dyndev; + +struct Dyndev +{ + char* name; /* device name (eg, "dynld") */ + char* tag; /* version tag (eg, MD5 or SHA1 hash of content) */ + char* path; /* file from whence it came */ + Dynobj* o; + Dev* dev; + Dyndev* next; +}; + +static Dyndev *loaded; +static QLock dllock; + +static Dyndev** finddyndev(char*); +static int matched(Dyndev*, char*, char*); + +extern Dynobj* kdynloadfd(int, Dynsym*, int, ulong); + +static void +dlfree(Dyndev *l) +{ + if(l != nil){ + free(l->tag); + free(l->name); + free(l->path); + dynobjfree(l->o); + free(l); + } +} + +static Dyndev* +dlload(char *path, Dynsym *tab, int ntab) +{ + Dyndev *l; + int fd; + + /* in Plan 9, would probably use Chan* interface here */ + fd = kopen(path, OREAD); + if(fd < 0) + error("cannot open"); + if(waserror()){ + kclose(fd); + nexterror(); + } + l = mallocz(sizeof(Dyndev), 1); + if(l == nil) + error(Enomem); + if(waserror()){ + dlfree(l); + nexterror(); + } + l->path = strdup(path); + if(l->path == nil) + error(Enomem); + l->o = kdynloadfd(fd, tab, ntab, 0); + if(l->o == nil) + error(up->env->errstr); + poperror(); + poperror(); + kclose(fd); + return l; +} + +static void +devload(char *name, char *path, char *tag) +{ + int i; + Dyndev *l, **lp; + Dev *dev; + char tabname[32]; + + lp = finddyndev(name); + if(*lp != nil) + error("already loaded"); /* i'm assuming the name (eg, "cons") is to be unique */ + l = dlload(path, _exporttab, dyntabsize(_exporttab)); + if(waserror()){ + dlfree(l); + nexterror(); + } + snprint(tabname, sizeof(tabname), "%sdevtab", name); + dev = dynimport(l->o, tabname, signof(*dev)); + if(dev == nil) + errorf("can't find %sdevtab in module", name); + kstrdup(&l->name, name); + kstrdup(&l->tag, tag != nil? tag: ""); + if(dev->name == nil) + dev->name = l->name; + else if(strcmp(dev->name, l->name) != 0) + errorf("module file has device %s", dev->name); + /* TO DO: allocate dev->dc dynamically (cf. brucee's driver) */ + if(devno(dev->dc, 1) >= 0) + errorf("devchar %C already used", dev->dc); + for(i = 0; devtab[i] != nil; i++) + ; + if(i >= ndevs || devtab[i+1] != nil) + error("device table full"); +#ifdef NATIVE + i = splhi(); + dev->reset(); + splx(i); +#endif + dev->init(); + l->dev = devtab[i] = dev; + l->next = loaded; + loaded = l; /* recently loaded ones first: good unload order? */ + poperror(); +} + +static Dyndev** +finddyndev(char *name) +{ + Dyndev *l, **lp; + + for(lp = &loaded; (l = *lp) != nil; lp = &l->next) + if(strcmp(l->name, name) == 0) + break; + return lp; +} + +static int +matched(Dyndev *l, char *path, char *tag) +{ + if(path != nil && strcmp(l->path, path) != 0) + return 0; + if(tag != nil && strcmp(l->tag, tag) != 0) + return 0; + return 1; +} + +static void +devunload(char *name, char *path, char *tag) +{ + int i; + Dyndev *l, **lp; + + lp = finddyndev(name); + l = *lp; + if(l == nil) + error("not loaded"); + if(!matched(l, path, tag)) + error("path/tag mismatch"); + for(i = 0; i < ndevs; i++) + if(l->dev == devtab[i]){ + devtab[i] = nil; /* TO DO: ensure driver is not currently in use */ + break; + } +#ifdef NATIVE + l->dev->shutdown(); +#endif + *lp = l->next; + dlfree(l); +} + +static long +readdynld(void *a, ulong n, ulong offset) +{ + char *p; + Dyndev *l; + int m, len; + + m = 0; + for(l = loaded; l != nil; l = l->next) + m += 48 + strlen(l->name) + strlen(l->path) + strlen(l->tag); + p = malloc(m); + if(p == nil) + error(Enomem); + if(waserror()){ + free(p); + nexterror(); + } + *p = 0; + len = 0; + for(l = loaded; l != nil; l = l->next) + if(l->dev) + len += snprint(p+len, m-len, "#%C\t%.8p\t%.8lud\t%q\t%q\t%q\n", + l->dev->dc, l->o->base, l->o->size, l->name, l->path, l->tag); + n = readstr(offset, a, n, p); + poperror(); + free(p); + return n; +} + +static long +readsyms(char *a, ulong n, ulong offset) +{ + char *p; + Dynsym *t; + long l, nr; + + p = malloc(READSTR); + if(p == nil) + error(Enomem); + if(waserror()){ + free(p); + nexterror(); + } + nr = 0; + for(t = _exporttab; n > 0 && t->name != nil; t++){ + l = snprint(p, READSTR, "%.8lux %.8lux %s\n", t->addr, t->sig, t->name); + if(offset >= l){ + offset -= l; + continue; + } + l = readstr(offset, a, n, p); + offset = 0; + n -= l; + a += l; + nr += l; + } + poperror(); + free(p); + return nr; +} + +static Chan* +dlattach(char *spec) +{ + return devattach(DEVCHAR, spec); +} + +static Walkqid* +dlwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, dltab, nelem(dltab), devgen); +} + +static int +dlstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, dltab, nelem(dltab), devgen); +} + +static Chan* +dlopen(Chan *c, int omode) +{ + return devopen(c, omode, dltab, nelem(dltab), devgen); +} + +static void +dlclose(Chan *c) +{ + USED(c); +} + +static long +dlread(Chan *c, void *a, long n, vlong voffset) +{ + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, dltab, nelem(dltab), devgen); + case Qdynld: + return readdynld(a, n, voffset); + case Qdynsyms: + return readsyms(a, n, voffset); + default: + error(Egreg); + } + return n; +} + +static long +dlwrite(Chan *c, void *a, long n, vlong voffset) +{ + Cmdbuf *cb; + char *name, *tag, *path; + + USED(voffset); + switch((ulong)c->qid.path){ + case Qdynld: + cb = parsecmd(a, n); + qlock(&dllock); + if(waserror()){ + qunlock(&dllock); + free(cb); + nexterror(); + } + if(cb->nf < 3 || strcmp(cb->f[1], "dev") != 0) /* only do devices */ + cmderror(cb, Ebadctl); + name = cb->f[2]; + path = nil; + if(cb->nf > 3) + path = cb->f[3]; + tag = nil; + if(cb->nf > 4) + tag = cb->f[4]; + if(strcmp(cb->f[0], "load") == 0){ + if(path == nil) + cmderror(cb, "missing load path"); + devload(name, path, tag); /* TO DO: remaining parameters might be dependencies? */ + }else if(strcmp(cb->f[0], "unload") == 0) + devunload(name, path, tag); + else + cmderror(cb, Ebadctl); + poperror(); + qunlock(&dllock); + free(cb); + break; + default: + error(Egreg); + } + return n; +} + +Dev dynlddevtab = { + DEVCHAR, + "dynld", + + devreset, + devinit, + devshutdown, /* TO DO */ + dlattach, + dlwalk, + dlstat, + dlopen, + devcreate, + dlclose, + dlread, + devbread, + dlwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devenv.c b/os/port/devenv.c new file mode 100644 index 00000000..da6373a2 --- /dev/null +++ b/os/port/devenv.c @@ -0,0 +1,338 @@ +/* + * devenv - environment + */ +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +static void envremove(Chan*); + +enum +{ + Maxenvsize = 16300, +}; + +static int +envgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp) +{ + Egrp *eg; + Evalue *e; + + if(s == DEVDOTDOT){ + devdir(c, c->qid, "#e", 0, eve, DMDIR|0775, dp); + return 1; + } + eg = up->env->egrp; + qlock(eg); + for(e = eg->entries; e != nil && s != 0; e = e->next) + s--; + if(e == nil) { + qunlock(eg); + return -1; + } + /* make sure name string continues to exist after we release lock */ + kstrcpy(up->genbuf, e->var, sizeof up->genbuf); + devdir(c, e->qid, up->genbuf, e->len, eve, 0666, dp); + qunlock(eg); + return 1; +} + +static Chan* +envattach(char *spec) +{ + if(up->env == nil || up->env->egrp == nil) + error(Enodev); + return devattach('e', spec); +} + +static Walkqid* +envwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, envgen); +} + +static int +envstat(Chan *c, uchar *db, int n) +{ + if(c->qid.type & QTDIR) + c->qid.vers = up->env->egrp->vers; + return devstat(c, db, n, 0, 0, envgen); +} + +static Chan * +envopen(Chan *c, int mode) +{ + Egrp *eg; + Evalue *e; + + if(c->qid.type & QTDIR) { + if(mode != OREAD) + error(Eperm); + c->mode = mode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + eg = up->env->egrp; + qlock(eg); + for(e = eg->entries; e != nil; e = e->next) + if(e->qid.path == c->qid.path) + break; + if(e == nil) { + qunlock(eg); + error(Enonexist); + } + if((mode & OTRUNC) && e->val) { + free(e->val); + e->val = 0; + e->len = 0; + e->qid.vers++; + } + qunlock(eg); + c->mode = openmode(mode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +envcreate(Chan *c, char *name, int mode, ulong) +{ + Egrp *eg; + Evalue *e, **le; + + if(c->qid.type != QTDIR) + error(Eperm); + if(strlen(name) >= sizeof(up->genbuf)) + error("name too long"); /* needs to fit for stat */ + mode = openmode(mode); + eg = up->env->egrp; + qlock(eg); + if(waserror()){ + qunlock(eg); + nexterror(); + } + for(le = &eg->entries; (e = *le) != nil; le = &e->next) + if(strcmp(e->var, name) == 0) + error(Eexist); + e = smalloc(sizeof(Evalue)); + e->var = smalloc(strlen(name)+1); + strcpy(e->var, name); + e->val = 0; + e->len = 0; + e->qid.path = ++eg->path; + e->next = nil; + e->qid.vers = 0; + *le = e; + c->qid = e->qid; + eg->vers++; + poperror(); + qunlock(eg); + c->offset = 0; + c->flag |= COPEN; + c->mode = mode; +} + +static void +envclose(Chan *c) +{ + if(c->flag & CRCLOSE) + envremove(c); +} + +static long +envread(Chan *c, void *a, long n, vlong offset) +{ + Egrp *eg; + Evalue *e; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, envgen); + eg = up->env->egrp; + qlock(eg); + if(waserror()){ + qunlock(eg); + nexterror(); + } + for(e = eg->entries; e != nil; e = e->next) + if(e->qid.path == c->qid.path) + break; + if(e == nil) + error(Enonexist); + if(offset > e->len) /* protects against overflow converting vlong to ulong */ + n = 0; + else if(offset + n > e->len) + n = e->len - offset; + if(n <= 0) + n = 0; + else + memmove(a, e->val+offset, n); + poperror(); + qunlock(eg); + return n; +} + +static long +envwrite(Chan *c, void *a, long n, vlong offset) +{ + char *s; + int ve; + Egrp *eg; + Evalue *e; + + if(n <= 0) + return 0; + eg = up->env->egrp; + qlock(eg); + if(waserror()){ + qunlock(eg); + nexterror(); + } + for(e = eg->entries; e != nil; e = e->next) + if(e->qid.path == c->qid.path) + break; + if(e == nil) + error(Enonexist); + ve = offset+n; + if(ve > Maxenvsize) + error(Etoobig); + if(ve > e->len) { + s = smalloc(ve); + memmove(s, e->val, e->len); + if(e->val != nil) + free(e->val); + e->val = s; + e->len = ve; + } + memmove(e->val+offset, a, n); + e->qid.vers++; + poperror(); + qunlock(eg); + return n; +} + +static void +envremove(Chan *c) +{ + Egrp *eg; + Evalue *e, **l; + + if(c->qid.type & QTDIR) + error(Eperm); + eg = up->env->egrp; + qlock(eg); + for(l = &eg->entries; (e = *l) != nil; l = &e->next) + if(e->qid.path == c->qid.path) + break; + if(e == nil) { + qunlock(eg); + error(Enonexist); + } + *l = e->next; + eg->vers++; + qunlock(eg); + free(e->var); + if(e->val != nil) + free(e->val); + free(e); +} + +Dev envdevtab = { + 'e', + "env", + + devreset, + devinit, + devshutdown, + envattach, + envwalk, + envstat, + envopen, + envcreate, + envclose, + envread, + devbread, + envwrite, + devbwrite, + envremove, + devwstat +}; + +/* + * kernel interface to environment variables + */ +Egrp* +newegrp(void) +{ + Egrp *e; + + e = smalloc(sizeof(Egrp)); + e->ref = 1; + return e; +} + +void +closeegrp(Egrp *e) +{ + Evalue *el, *nl; + + if(e == nil || decref(e) != 0) + return; + for (el = e->entries; el != nil; el = nl) { + free(el->var); + if (el->val) + free(el->val); + nl = el->next; + free(el); + } + free(e); +} + +void +egrpcpy(Egrp *to, Egrp *from) +{ + Evalue *e, *ne, **last; + + if(from == nil) + return; + last = &to->entries; + qlock(from); + for (e = from->entries; e != nil; e = e->next) { + ne = smalloc(sizeof(Evalue)); + ne->var = smalloc(strlen(e->var)+1); + strcpy(ne->var, e->var); + if (e->val) { + ne->val = smalloc(e->len); + memmove(ne->val, e->val, e->len); + ne->len = e->len; + } + ne->qid.path = ++to->path; + *last = ne; + last = &ne->next; + } + qunlock(from); +} + +void +ksetenv(char *var, char *val, int) +{ + Chan *c; + char buf[2*KNAMELEN]; + + snprint(buf, sizeof(buf), "#e/%s", var); + if(waserror()) + return; + c = namec(buf, Acreate, OWRITE, 0600); + poperror(); + if(!waserror()){ + if(!waserror()){ + devtab[c->type]->write(c, val, strlen(val), 0); + poperror(); + } + poperror(); + } + cclose(c); +} diff --git a/os/port/devflash.c b/os/port/devflash.c new file mode 100644 index 00000000..a014fa46 --- /dev/null +++ b/os/port/devflash.c @@ -0,0 +1,641 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +/* + * flash memory + */ + +#include "../port/flashif.h" + +typedef struct Flashtype Flashtype; +struct Flashtype { + char* name; + int (*reset)(Flash*); + Flashtype* next; +}; + +enum { + Nbanks = 2, +}; + +static struct +{ + Flash* card[Nbanks]; /* actual card type, reset for access */ + Flashtype* types; /* possible card types */ +}flash; + +enum{ + Qtopdir, + Qflashdir, + Qdata, + Qctl, +}; + +#define TYPE(q) ((ulong)(q) & 0xFF) +#define PART(q) ((ulong)(q)>>8) +#define QID(p,t) (((p)<<8) | (t)) + +static Flashregion* flashregion(Flash*, ulong); +static char* flashnewpart(Flash*, char*, ulong, ulong); +static ulong flashaddr(Flash*, Flashpart*, char*); +static void protect(Flash*, ulong); +static void eraseflash(Flash*, Flashregion*, ulong); +static long readflash(Flash*, void*, long, int); +static long writeflash(Flash*, long, void*,int); + +static char Eprotect[] = "flash region protected"; + +static int +flash2gen(Chan *c, ulong p, Dir *dp) +{ + Flashpart *fp; + Flash *f; + Qid q; + int mode; + + f = flash.card[c->dev]; + fp = &f->part[PART(p)]; + if(fp->name == nil) + return 0; + mkqid(&q, p, 0, QTFILE); + switch(TYPE(p)){ + case Qdata: + mode = 0660; + if(f->write == nil) + mode = 0440; + devdir(c, q, fp->name, fp->end-fp->start, eve, mode, dp); + return 1; + case Qctl: + snprint(up->genbuf, sizeof(up->genbuf), "%sctl", fp->name); + devdir(c, q, up->genbuf, 0, eve, 0660, dp); + return 1; + default: + return -1; + } +} + +static int +flashgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp) +{ + Qid q; + char *n; + + if(s == DEVDOTDOT){ + mkqid(&q, QID(0, Qtopdir), 0, QTDIR); + n = "#F"; + if(c->dev != 0){ + sprint(up->genbuf, "#F%ld", c->dev); + n = up->genbuf; + } + devdir(c, q, n, 0, eve, 0555, dp); + return 1; + } + switch(TYPE(c->qid.path)){ + case Qtopdir: + if(s != 0) + break; + mkqid(&q, QID(0, Qflashdir), 0, QTDIR); + n = "flash"; + if(c->dev != 0){ + sprint(up->genbuf, "flash%ld", c->dev); + n = up->genbuf; + } + devdir(c, q, n, 0, eve, 0555, dp); + return 1; + case Qflashdir: + if(s >= 2*nelem(flash.card[c->dev]->part)) + return -1; + return flash2gen(c, QID(s>>1, s&1?Qctl:Qdata), dp); + case Qctl: + case Qdata: + return flash2gen(c, (ulong)c->qid.path, dp); + } + return -1; +} + +static void +flashreset(void) +{ + Flash *f; + Flashtype *t; + char *e; + int bank; + + for(bank = 0; bank < Nbanks; bank++){ + f = malloc(sizeof(*f)); + if(f == nil){ + print("#F%d: can't allocate Flash data\n", bank); + return; + } + f->cmask = ~(ulong)0; + if(archflashreset(bank, f) < 0 || f->type == nil || f->addr == nil){ + free(f); + return; + } + for(t = flash.types; t != nil; t = t->next) + if(strcmp(f->type, t->name) == 0) + break; + if(t == nil){ + iprint("#F%d: no flash driver for type %s (addr %p)\n", bank, f->type, f->addr); + free(f); + return; + } + f->reset = t->reset; + f->protect = 1; + if(f->reset(f) == 0){ + flash.card[bank] = f; + iprint("#F%d: %s addr 0x%lux len %lud width %d interleave %d\n", bank, f->type, PADDR(f->addr), f->size, f->width, f->interleave); + e = flashnewpart(f, "flash", 0, f->size); + if(e != nil) + panic("#F%d: couldn't init table: %s\n", bank, e); /* shouldn't happen */ + }else + iprint("#F%d: reset failed (%s)\n", bank, f->type); + } +} + +static Chan* +flashattach(char *spec) +{ + Flash *f; + int bank; + Chan *c; + + bank = strtol(spec, nil, 0); + if(bank < 0 || bank >= Nbanks || + (f = flash.card[bank]) == nil || + f->attach != nil && f->attach(f) < 0) + error(Enodev); + c = devattach('F', spec); + c->dev = bank; + return c; +} + +static Walkqid* +flashwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, flashgen); +} + +static int +flashstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, nil, 0, flashgen); +} + +static Chan* +flashopen(Chan *c, int omode) +{ + omode = openmode(omode); + switch(TYPE(c->qid.path)){ + case Qdata: + case Qctl: + if(flash.card[c->dev] == nil) + error(Enodev); + break; + } + return devopen(c, omode, nil, 0, flashgen); +} + +static void +flashclose(Chan*) +{ +} + +static long +flashread(Chan *c, void *buf, long n, vlong offset) +{ + Flash *f; + char *s, *o; + Flashpart *fp; + Flashregion *r; + int i; + ulong start, end; + + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, nil, 0, flashgen); + + f = flash.card[c->dev]; + fp = &f->part[PART(c->qid.path)]; + if(fp->name == nil) + error(Egreg); + switch(TYPE(c->qid.path)){ + case Qdata: + offset += fp->start; + if(offset >= fp->end) + return 0; + if(offset+n > fp->end) + n = fp->end - offset; + n = readflash(f, buf, offset, n); + if(n < 0) + error(Eio); + return n; + case Qctl: + s = malloc(READSTR); + if(waserror()){ + free(s); + nexterror(); + } + o = seprint(s, s+READSTR, "%#2.2ux %#4.4ux %d %q\n", + f->id, f->devid, f->width, f->sort!=nil? f->sort: "nor"); + for(i=0; inr; i++){ + r = &f->regions[i]; + if(r->start < fp->end && fp->start < r->end){ + start = r->start; + if(fp->start > start) + start = fp->start; + end = r->end; + if(fp->end < end) + end = fp->end; + o = seprint(o, s+READSTR, "%#8.8lux %#8.8lux %#8.8lux", start, end, r->erasesize); + if(r->pagesize) + o = seprint(o, s+READSTR, " %#8.8lux", r->pagesize); + o = seprint(o, s+READSTR, "\n"); + } + } + n = readstr(offset, buf, n, s); + poperror(); + free(s); + return n; + } + error(Egreg); + return 0; /* not reached */ +} + +enum { + CMerase, + CMadd, + CMremove, + CMsync, + CMprotectboot, +}; + +static Cmdtab flashcmds[] = { + {CMerase, "erase", 2}, + {CMadd, "add", 0}, + {CMremove, "remove", 2}, + {CMsync, "sync", 0}, + {CMprotectboot, "protectboot", 0}, +}; + +static long +flashwrite(Chan *c, void *buf, long n, vlong offset) +{ + Cmdbuf *cb; + Cmdtab *ct; + ulong addr, start, end; + char *e; + Flashpart *fp; + Flashregion *r; + Flash *f; + + f = flash.card[c->dev]; + fp = &f->part[PART(c->qid.path)]; + if(fp->name == nil) + error(Egreg); + switch(TYPE(c->qid.path)){ + case Qdata: + if(f->write == nil) + error(Eperm); + offset += fp->start; + if(offset >= fp->end) + return 0; + if(offset+n > fp->end) + n = fp->end - offset; + n = writeflash(f, offset, buf, n); + if(n < 0) + error(Eio); + return n; + case Qctl: + cb = parsecmd(buf, n); + if(waserror()){ + free(cb); + nexterror(); + } + ct = lookupcmd(cb, flashcmds, nelem(flashcmds)); + switch(ct->index){ + case CMerase: + if(strcmp(cb->f[1], "all") != 0){ + addr = flashaddr(f, fp, cb->f[1]); + r = flashregion(f, addr); + if(r == nil) + error("nonexistent flash region"); + if(addr%r->erasesize != 0) + error("invalid erase block address"); + eraseflash(f, r, addr); + }else if(fp->start == 0 && fp->end == f->size && f->eraseall != nil){ + eraseflash(f, nil, 0); + }else{ + for(addr = fp->start; addr < fp->end; addr += r->erasesize){ + r = flashregion(f, addr); + if(r == nil) + error("nonexistent flash region"); + if(addr%r->erasesize != 0) + error("invalid erase block address"); + eraseflash(f, r, addr); + } + } + break; + case CMadd: + if(cb->nf < 3) + error(Ebadarg); + start = flashaddr(f, fp, cb->f[2]); + if(cb->nf > 3 && strcmp(cb->f[3], "end") != 0) + end = flashaddr(f, fp, cb->f[3]); + else + end = fp->end; + if(start > end || start >= fp->end || end > fp->end) + error(Ebadarg); + e = flashnewpart(f, cb->f[1], start, end); + if(e != nil) + error(e); + break; + case CMremove: + /* TO DO */ + break; + case CMprotectboot: + if(cb->nf > 1 && strcmp(cb->f[1], "off") == 0) + f->protect = 0; + else + f->protect = 1; + break; + case CMsync: + /* TO DO? */ + break; + default: + error(Ebadarg); + } + poperror(); + free(cb); + return n; + } + error(Egreg); + return 0; /* not reached */ +} + +static char* +flashnewpart(Flash *f, char *name, ulong start, ulong end) +{ + Flashpart *fp, *empty; + int i; + + empty = nil; + for(i = 0; i < nelem(f->part); i++){ + fp = &f->part[i]; + if(fp->name == nil){ + if(empty == nil) + empty = fp; + }else if(strcmp(fp->name, name) == 0) + return Eexist; + } + if((fp = empty) == nil) + return "partition table full"; + fp->name = strdup(name); + if(fp->name == nil) + return Enomem; + fp->start = start; + fp->end = end; + return nil; +} + +static ulong +flashaddr(Flash *f, Flashpart *fp, char *s) +{ + Flashregion *r; + ulong addr; + + addr = strtoul(s, &s, 0); + if(*s) + error(Ebadarg); + if(fp->name == nil) + error("partition removed"); + addr += fp->start; + r = flashregion(f, addr); + if(r != nil && addr%r->erasesize != 0) + error("invalid erase unit address"); + if(addr < fp->start || addr > fp->end || addr > f->size) + error(Ebadarg); + return addr; +} + +static Flashregion* +flashregion(Flash *f, ulong a) +{ + int i; + Flashregion *r; + + for(i=0; inr; i++){ + r = &f->regions[i]; + if(r->start <= a && a < r->end) + return r; + } + return nil; +} + +Dev flashdevtab = { + 'F', + "flash", + + flashreset, + devinit, + devshutdown, + flashattach, + flashwalk, + flashstat, + flashopen, + devcreate, + flashclose, + flashread, + devbread, + flashwrite, + devbwrite, + devremove, + devwstat, +}; + +/* + * called by flash card types named in link section (eg, flashamd.c) + */ +void +addflashcard(char *name, int (*reset)(Flash*)) +{ + Flashtype *f, **l; + + f = (Flashtype*)malloc(sizeof(*f)); + f->name = name; + f->reset = reset; + f->next = nil; + for(l = &flash.types; *l != nil; l = &(*l)->next) + ; + *l = f; +} + +static long +readflash(Flash *f, void *buf, long offset, int n) +{ + int r, width, wmask; + uchar tmp[16]; + uchar *p; + ulong o; + + if(offset < 0 || offset+n > f->size) + error(Ebadarg); + qlock(f); + if(waserror()){ + qunlock(f); + nexterror(); + } + if(f->read != nil){ + width = f->width; + wmask = width-1; + p = buf; + if(offset&wmask) { + o = offset & ~wmask; + if(f->read(f, o, (ulong*)tmp, width) < 0) + error(Eio); + memmove(tmp, (uchar*)f->addr+o, width); + for(; n > 0 && offset&wmask; n--) + *p++ = tmp[offset++&wmask]; + } + r = n&wmask; + n &= ~wmask; + if(n){ + if(f->read(f, offset, (ulong*)p, n) < 0) + error(Eio); + offset += n; + p += n; + } + if(r){ + if(f->read(f, offset, (ulong*)tmp, width)) + error(Eio); + memmove(p, tmp, r); + } + }else + memmove(buf, (uchar*)f->addr+offset, n); /* assumes hardware supports byte access */ + poperror(); + qunlock(f); + return n; +} + +static long +writeflash(Flash *f, long offset, void *buf, int n) +{ + uchar tmp[16]; + uchar *p; + ulong o; + int r, width, wmask; + Flashregion *rg; + + if(f->write == nil || offset < 0 || offset+n > f->size) + error(Ebadarg); + rg = flashregion(f, offset); + if(f->protect && rg != nil && rg->start == 0 && offset < rg->erasesize) + error(Eprotect); + width = f->width; + wmask = width-1; + qlock(f); + archflashwp(f, 0); + if(waserror()){ + archflashwp(f, 1); + qunlock(f); + nexterror(); + } + p = buf; + if(offset&wmask){ + o = offset & ~wmask; + if(f->read != nil){ + if(f->read(f, o, tmp, width) < 0) + error(Eio); + }else + memmove(tmp, (uchar*)f->addr+o, width); + for(; n > 0 && offset&wmask; n--) + tmp[offset++&wmask] = *p++; + if(f->write(f, o, tmp, width) < 0) + error(Eio); + } + r = n&wmask; + n &= ~wmask; + if(n){ + if(f->write(f, offset, p, n) < 0) + error(Eio); + offset += n; + p += n; + } + if(r){ + if(f->read != nil){ + if(f->read(f, offset, tmp, width) < 0) + error(Eio); + }else + memmove(tmp, (uchar*)f->addr+offset, width); + memmove(tmp, p, r); + if(f->write(f, offset, tmp, width) < 0) + error(Eio); + } + poperror(); + archflashwp(f, 1); + qunlock(f); + return n; +} + +static void +eraseflash(Flash *f, Flashregion *r, ulong addr) +{ + int rv; + + if(f->protect && r != nil && r->start == 0 && addr < r->erasesize) + error(Eprotect); + qlock(f); + archflashwp(f, 0); + if(waserror()){ + archflashwp(f, 1); + qunlock(f); + nexterror(); + } + if(r == nil){ + if(f->eraseall != nil) + rv = f->eraseall(f); + else + rv = -1; + }else + rv = f->erasezone(f, r, addr); + if(rv < 0) + error(Eio); + poperror(); + archflashwp(f, 1); + qunlock(f); +} + +/* + * flash access taking width and interleave into account + */ +int +flashget(Flash *f, ulong a) +{ + switch(f->width){ + default: + return ((uchar*)f->addr)[a<bshift]; + case 2: + return ((ushort*)f->addr)[a]; + case 4: + return ((ulong*)f->addr)[a]; + } +} + +void +flashput(Flash *f, ulong a, int v) +{ + switch(f->width){ + default: + ((uchar*)f->addr)[a<bshift] = v; + break; + case 2: + ((ushort*)f->addr)[a] = v; + break; + case 4: + ((ulong*)f->addr)[a] = v; + break; + } +} diff --git a/os/port/devftl.c b/os/port/devftl.c new file mode 100644 index 00000000..ab24588a --- /dev/null +++ b/os/port/devftl.c @@ -0,0 +1,1365 @@ +/* + * basic Flash Translation Layer driver + * see for instance the Intel technical paper + * ``Understanding the Flash Translation Layer (FTL) Specification'' + * Order number 297816-001 (online at www.intel.com) + * + * a public driver by David Hinds, dhinds@allegro.stanford.edu + * further helps with some details. + * + * this driver uses the common simplification of never storing + * the VBM on the medium (a waste of precious flash!) but + * rather building it on the fly as the block maps are read. + * + * Plan 9 driver (c) 1997 by C H Forsyth (forsyth@terzarima.net) + * This driver may be used or adapted by anyone for any non-commercial purpose. + * + * adapted for Inferno 1998 by C H Forsyth, Vita Nuova Limited, York, England (forsyth@vitanuova.com) + * + * TO DO: + * check error handling details for get/put flash + * bad block handling + * reserved space in formatted size + * possibly block size as parameter + */ + +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +#include "kernel.h" + +#ifndef offsetof +#define offsetof(s, m) (ulong)(&(((s*)0)->m)) +#endif + +typedef struct Ftl Ftl; +typedef struct Merase Merase; +typedef struct Terase Terase; + +enum { + Eshift = 18, /* 2^18=256k; log2(eraseunit) */ + Flashseg = 1<>Bshift) +#define MKBAM(b,t) (((b)<>8),((p)[0]=(v))) +#define PUT4(p,v) (((p)[3]=(v)>>24),((p)[2]=(v)>>16),((p)[1]=(v)>>8),((p)[0]=(v))) + +static Lock ftllock; +static Ftl *ftls; +static int ftlpct = USABLEPCT; + +static ulong allocblk(Ftl*); +static int erasedetect(Ftl *ftl, ulong base, ulong size, ushort *pstart, ushort *nunits); +static void eraseflash(Ftl*, ulong); +static void erasefree(Terase*); +static void eraseinit(Ftl*, ulong, int, int); +static Terase* eraseload(Ftl*, int, ulong); +static void ftlfree(Ftl*); +static void getflash(Ftl*, void*, ulong, long); +static int mapblk(Ftl*, ulong, Terase**, ulong*); +static Ftl* mkftl(char*, ulong, ulong, int, char*); +static void putbam(Ftl*, Terase*, int, ulong); +static void putflash(Ftl*, ulong, void*, long); +static int scavenge(Ftl*); + +enum { + Qdir, + Qctl, + Qdata, +}; + +#define DATAQID(q) ((q) >= Qdata && (q) <= Qdata + NPART) + +static void +ftlpartcmd(Ftl *ftl, char **fields, int nfields) +{ + ulong start, end; + char *p; + int n, newn; + + /* name start [end] */ + if(nfields < 2 || nfields > 3) + error(Ebadarg); + if(ftl->npart >= NPART) + error("table full"); + if(strcmp(fields[0], "ctl") == 0 || strcmp(fields[0], "data") == 0) + error(Ebadarg); + newn = -1; + for(n = 0; n < NPART; n++){ + if(ftl->part[n].name == nil){ + if(newn < 0) + newn = n; + continue; + } + if(strcmp(fields[0], ftl->part[n].name + 3) == 0) + error(Ebadarg); + } + start = strtoul(fields[1], 0, 0); + if(nfields > 2) + end = strtoul(fields[2], 0, 0); + else + end = ftl->rwlimit * Bsize; + if(start >= end || start % Bsize || end % Bsize) + error(Ebadarg); + ftl->part[newn].start = start; + ftl->part[newn].size = end - start; + ftl->part[newn].rwlimit = end / Bsize; + free(ftl->part[newn].name); + p = malloc(strlen(fields[0]) + 3 + 1); + strcpy(p, "ftl"); + strcat(p, fields[0]); + ftl->part[newn].name = p; + ftl->npart++; +} + +static void +ftldelpartcmd(Ftl *ftl, char **fields, int nfields) +{ + int n; + // name + if(nfields != 1) + error(Ebadarg); + for(n = 0; n < NPART; n++) + if(strcmp(fields[0], ftl->part[n].name + 3) == 0){ + free(ftl->part[n].name); + ftl->part[n].name = nil; + ftl->npart--; + return; + } + error(Ebadarg); +} + +static int +ftlgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp) +{ + int n; + switch(i){ + case DEVDOTDOT: + devdir(c, (Qid){Qdir, 0, QTDIR}, "#X", 0, eve, 0555, dp); + break; + case 0: + devdir(c, (Qid){Qctl, 0, QTFILE}, "ftlctl", 0, eve, 0660, dp); + break; + case 1: + devdir(c, (Qid){Qdata, 0, QTFILE}, "ftldata", ftls ? ftls->rwlimit * Bsize : 0, eve, 0660, dp); + break; + default: + if(ftls == nil) + return -1; + i -= 2; + if(i >= ftls->npart) + return -1; + for(n = 0; n < NPART; n++) + if(ftls->part[n].name != nil){ + if(i == 0) + break; + i--; + } + if(i != 0){ + print("wierd\n"); + return -1; + } + devdir(c, (Qid){Qdata + 1 + n, 0, QTFILE}, ftls->part[n].name, ftls->part[n].size, eve, 0660, dp); + } + return 1; +} + +static Ftl * +ftlget(void) +{ + Ftl *ftl; + + lock(&ftllock); + ftl = ftls; + if(ftl != nil) + incref(ftl); + unlock(&ftllock); + return ftl; +} + +static void +ftlput(Ftl *ftl) +{ + if(ftl != nil){ + lock(&ftllock); + if(decref(ftl) == 0 && ftl->detach == Detached){ + ftls = nil; + if(ftl->hasproc){ /* no lock needed: can't change if ftl->ref==0 */ + ftl->detach = Deferred; + wakeup(&ftl->workr); + }else + ftlfree(ftl); + } + unlock(&ftllock); + } +} + +static Chan * +ftlattach(char *spec) +{ + return devattach('X', spec); +} + +static Walkqid* +ftlwalk(Chan *c, Chan *nc, char **name, int nname) +{ + Walkqid *wq; + + wq = devwalk(c, nc, name, nname, 0, 0, ftlgen); + if(wq != nil && wq->clone != nil && wq->clone != c) + if(DATAQID(wq->clone->qid.path)) + wq->clone->aux = ftlget(); + return wq; +} + +static int +ftlstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, 0, 0, ftlgen); +} + +static Chan* +ftlopen(Chan *c, int omode) +{ + Ftl *ftl; + omode = openmode(omode); + if(DATAQID(c->qid.path)){ + ftl = ftls; + if(ftl == nil) + error(Enodev); + if(strcmp(up->env->user, eve)!=0) + error(Eperm); + } + else if(c->qid.path == Qctl){ + if(strcmp(up->env->user, eve)!=0) + error(Eperm); + } + c = devopen(c, omode, 0, 0, ftlgen); + if(DATAQID(c->qid.path)){ + c->aux = ftlget(); + if(c->aux == nil) + error(Enodev); + } + return c; +} + +static void +ftlclose(Chan *c) +{ + if(DATAQID(c->qid.path) && (c->flag&COPEN) != 0) + ftlput((Ftl*)c->aux); +} + +static long +ftlread(Chan *c, void *buf, long n, vlong offset) +{ + Ftl *ftl; + Terase *e; + int nb; + uchar *a; + ulong pb; + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, 0, 0, ftlgen); + + if(DATAQID(c->qid.path)){ + ulong rwlimit; + + if(n <= 0 || n%Bsize || offset%Bsize) + error(Eio); + ftl = c->aux; + if(c->qid.path > Qdata){ + int p = c->qid.path - Qdata - 1; + offset += ftl->part[p].start; + rwlimit = ftl->part[p].rwlimit; + } + else + rwlimit = ftl->rwlimit; + nb = n/Bsize; + offset /= Bsize; + if(offset >= rwlimit) + return 0; + if(offset+nb > rwlimit) + nb = rwlimit - offset; + a = buf; + for(n = 0; n < nb; n++){ + qlock(ftl); + if(waserror()){ + qunlock(ftl); + nexterror(); + } + if(mapblk(ftl, offset+n, &e, &pb)) + getflash(ftl, a, e->offset + pb*Bsize, Bsize); + else + memset(a, 0, Bsize); + poperror(); + qunlock(ftl); + a += Bsize; + } + return a-(uchar*)buf; + } + + if(c->qid.path != Qctl) + error(Egreg); + + return 0; +} + +static long +ftlwrite(Chan *c, void *buf, long n, vlong offset) +{ + char cmd[64], *fields[6]; + int ns, i, k, nb; + uchar *a; + Terase *e, *oe; + ulong ob, v, base, size, segsize; + Ftl *ftl; + + if(n <= 0) + return 0; + + if(DATAQID(c->qid.path)){ + ulong rwlimit; + ftl = c->aux; + if(n <= 0 || n%Bsize || offset%Bsize) + error(Eio); + if(c->qid.path > Qdata){ + int p = c->qid.path - Qdata - 1; + offset += ftl->part[p].start; + rwlimit = ftl->part[p].rwlimit; + } + else + rwlimit = ftl->rwlimit; + nb = n/Bsize; + offset /= Bsize; + if(offset >= rwlimit) + return 0; + if(offset+nb > rwlimit) + nb = rwlimit - offset; + a = buf; + for(n = 0; n < nb; n++){ + ns = 0; + while((v = allocblk(ftl)) == 0) + if(!scavenge(ftl) || ++ns > 3){ + static int stop; + + if(stop < 3){ + stop++; + print("ftl: flash memory full\n"); + } + error("flash memory full"); + } + qlock(ftl); + if(waserror()){ + qunlock(ftl); + nexterror(); + } + if(!mapblk(ftl, offset+n, &oe, &ob)) + oe = nil; + e = ftl->unit[v>>16]; + v &= 0xffff; + putflash(ftl, e->offset + v*Bsize, a, Bsize); + putbam(ftl, e, v, MKBAM(offset+n, DataBlock)); + /* both old and new block references exist in this window (can't be closed?) */ + ftl->vbm[offset+n] = (e->x<<16) | v; + if(oe != nil){ + putbam(ftl, oe, ob, Bdeleted); + oe->ndead++; + } + poperror(); + qunlock(ftl); + a += Bsize; + } + return a-(uchar*)buf; + } + else if(c->qid.path == Qctl){ + if(n > sizeof(cmd)-1) + n = sizeof(cmd)-1; + memmove(cmd, buf, n); + cmd[n] = 0; + i = getfields(cmd, fields, 6, 1, " \t\n"); + if(i <= 0) + error(Ebadarg); + if(i >= 2 && (strcmp(fields[0], "init") == 0 || strcmp(fields[0], "format") == 0)){ + if(i > 2) + base = strtoul(fields[2], nil, 0); + else + base = 0; /* TO DO: hunt for signature */ + if(i > 3) + size = strtoul(fields[3], nil, 0); + else + size = Nolimit; + if(i > 4) + segsize = strtoul(fields[4], nil, 0); + else + segsize = 0; + /* segsize must be power of two and size and base must be multiples of it + * if segsize is zero, then use whatever the device returns + */ + if(segsize != 0 && (segsize > size + || segsize&(segsize-1) + || (base != Nolimit && base&(segsize-1)) + || size == 0 + || (size != Nolimit && size&(segsize-1)))) + error(Ebadarg); + if(segsize == 0) + k = 0; + else { + for(k=0; k<32 && (1<trace = i>1? strtol(fields[1], nil, 0): 1; + }else if(strcmp(fields[0], "detach") == 0){ + if((ftl = ftlget()) != nil){ + if(ftl->ref > 1){ + ftlput(ftl); + error(Einuse); + } + ftl->detach = Detached; + ftlput(ftl); + }else + error(Enodev); + }else if(strcmp(fields[0], "part")==0){ + if((ftl = ftlget()) != nil){ + if(ftl->ref > 1){ + ftlput(ftl); + error(Einuse); + } + if(waserror()){ + ftlput(ftl); + nexterror(); + } + ftlpartcmd(ftl, fields + 1, i - 1); + poperror(); + ftlput(ftl); + }else + error(Enodev); + }else if(strcmp(fields[0], "delpart")==0){ + if((ftl = ftlget()) != nil){ + if(ftl->ref > 1){ + ftlput(ftl); + error(Einuse); + } + if(waserror()){ + ftlput(ftl); + nexterror(); + } + ftldelpartcmd(ftls, fields + 1, i - 1); + poperror(); + ftlput(ftl); + }else + error(Enodev); + }else if(i >= 2 && strcmp(fields[0], "pct")==0){ + v = strtoul(fields[1], nil, 0); + if(v >= 50) + ftlpct = v; + }else + error(Ebadarg); + return n; + } + error(Egreg); + return 0; /* not reached */ +} + +static Chan * +ftlkopen(char *name, char *suffix, int mode) +{ + Chan *c; + char *fn; + int fd; + + if(suffix != nil && *suffix){ + fn = smalloc(strlen(name)+strlen(suffix)+1); + if(fn == nil) + return nil; + strcpy(fn, name); + strcat(fn, suffix); + fd = kopen(fn, mode); + free(fn); + }else + fd = kopen(name, mode); + if(fd < 0) + return nil; + c = fdtochan(up->env->fgrp, fd, mode, 0, 1); + kclose(fd); + return c; +} + +static ulong +ftlfsize(Chan *c) +{ + uchar dbuf[STATFIXLEN+32*4]; + Dir d; + int n; + + n = devtab[c->type]->stat(c, dbuf, sizeof(dbuf)); + if(convM2D(dbuf, n, &d, nil) == 0) + return 0; + return d.length; +} + +static Ftl * +mkftl(char *fname, ulong base, ulong size, int eshift, char *op) +{ + int i, j, nov, segblocks, n, badseg, old, valid; + ulong limit; + Terase *e; + Ftl *ftl; + char buf[64], *fields[8]; + ulong ea; + Chan *statfd; + + ftl = malloc(sizeof(*ftl)); + if(ftl == nil) + error(Enomem); + e = nil; + if(waserror()){ + ftlfree(ftl); + if(e) + free(e); + nexterror(); + } + ftl->lastx = 0; + ftl->trace = 0; + ftl->flash = ftlkopen(fname, "", ORDWR); + if(ftl->flash == nil) + error(up->env->errstr); + ftl->flashctl = ftlkopen(fname, "ctl", ORDWR); + if(ftl->flashctl == nil) + error(up->env->errstr); + old = 1; + statfd = ftlkopen(fname, "stat", OREAD); /* old scheme */ + if(statfd == nil){ + statfd = ftl->flashctl; /* new just uses ctl */ + old = 0; + } + statfd->offset = 0; + if((n = kchanio(statfd, buf, sizeof(buf), OREAD)) <= 0){ + print("ftl: read stat/ctl failed: %s\n", up->env->errstr); + error(up->env->errstr); + } + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + buf[n] = 0; + if(statfd != ftl->flashctl) + cclose(statfd); + + n = getfields(buf, fields, nelem(fields), 1, " \t\n"); + ea = 0; + if(old){ + if(n >= 4) + if((ea = strtoul(fields[3], nil, 16)) < 8*1024) + ea = 0; /* assume the format is wrong */ + }else{ + if(n >= 7) + if((ea = strtoul(fields[6], nil, 0)) < 2*1024) + ea = 0; /* assume the format is wrong */ + } + if(ea != 0){ + for(i=0; i < 32 && (1<flash); + if(limit == 0) + error("no space for flash translation"); + ftl->segsize = 1 << eshift; + if(base == Nolimit){ + ushort pstart, nunits; + erasedetect(ftl, 0, limit, &pstart, &nunits); + base = pstart * ftl->segsize; + size = nunits * ftl->segsize; + print("ftl: partition in %s at 0x%.8lux, length 0x%.8lux\n", fname, base, size); + } else if(size == Nolimit) + size = limit-base; + if(base >= limit || size > limit || base+size > limit || eshift < 8 || (1< size){ + print("ftl: bad: base=%#lux limit=%#lux size=%ld eshift=%d\n", base, limit, size, eshift); + error("bad flash space parameters"); + } + if(FTLDEBUG) + print("%s flash %s #%lux:#%lux limit #%lux\n", op, fname, base, size, limit); + ftl->base = base; + ftl->size = size; + ftl->bshift = Bshift; + ftl->bsize = Bsize; + ftl->eshift = eshift; + ftl->nunit = size>>eshift; + nov = ((ftl->segsize/Bsize)*4 + BAMoffset + Bsize - 1)/Bsize; /* number of overhead blocks per segment (header, and BAM itself) */ + ftl->fstart = nov; + segblocks = ftl->segsize/Bsize - nov; + ftl->nblock = ftl->nunit*segblocks; + if(ftl->nblock > 0x10000){ + /* oops - too many blocks */ + ftl->nunit = 0x10000 / segblocks; + ftl->nblock = ftl->nunit * segblocks; + size = ftl->nunit * ftl->segsize; + ftl->size = size; + print("ftl: too many blocks - limiting to %ld bytes %d units %lud blocks\n", + size, ftl->nunit, ftl->nblock); + } + ftl->vbm = malloc(ftl->nblock*sizeof(*ftl->vbm)); + ftl->unit = malloc(ftl->nunit*sizeof(*ftl->unit)); + if(ftl->vbm == nil || ftl->unit == nil) + error(Enomem); + if(strcmp(op, "format") == 0){ + for(i=0; inunit-1; i++) + eraseinit(ftl, i*ftl->segsize, i, 1); + eraseinit(ftl, i*ftl->segsize, XferID, 1); + } + badseg = -1; + ftl->xfer = -1; + valid = 0; + for(i=0; inunit; i++){ + e = eraseload(ftl, i, i*ftl->segsize); + if(e == nil){ + print("ftl: logical segment %d: bad format\n", i); + if(badseg == -1) + badseg = i; + else + badseg = -2; + continue; + } + if(e->id == XferBusy){ + e->nerase++; + eraseinit(ftl, e->offset, XferID, e->nerase); + e->id = XferID; + } + for(j=0; jnunit; j++) + if(ftl->unit[j] != nil && ftl->unit[j]->id == e->id){ + print("ftl: duplicate erase unit #%x\n", e->id); + erasefree(e); + e = nil; + break; + } + if(e){ + valid++; + ftl->unit[e->x] = e; + if(e->id == XferID) + ftl->xfer = e->x; + if(FTLDEBUG) + print("ftl: unit %d:#%x used %lud free %lud dead %lud bad %lud nerase %lud\n", + e->x, e->id, e->nused, e->nfree, e->ndead, e->nbad, e->nerase); + e = nil; + USED(e); + } + } + if(badseg >= 0){ + if(ftl->xfer >= 0) + error("invalid ftl format"); + i = badseg; + eraseinit(ftl, i*ftl->segsize, XferID, 1); + e = eraseload(ftl, i, i*ftl->segsize); + if(e == nil) + error("bad ftl format"); + ftl->unit[e->x] = e; + ftl->xfer = e->x; + print("ftl: recovered transfer unit %d\n", e->x); + valid++; + e = nil; + USED(e); + } + if(ftl->xfer < 0 && valid <= 0 || ftl->xfer >= 0 && valid <= 1) + error("no valid flash data units"); + if(ftl->xfer < 0) + error("ftl: no transfer unit: device is WORM\n"); + else + ftl->nblock -= segblocks; /* discount transfer segment */ + if(ftl->nblock >= 1000) + ftl->rwlimit = ftl->nblock-100; /* TO DO: variable reserve */ + else + ftl->rwlimit = ftl->nblock*ftlpct/100; + poperror(); + return ftl; +} + +static void +ftlfree(Ftl *ftl) +{ + int i, n; + + if(ftl != nil){ + if(ftl->flashctl != nil) + cclose(ftl->flashctl); + if(ftl->flash != nil) + cclose(ftl->flash); + + if(ftl->unit){ + for(i = 0; i < ftl->nunit; i++) + erasefree(ftl->unit[i]); + free(ftl->unit); + } + free(ftl->vbm); + for(n = 0; n < NPART; n++) + free(ftl->part[n].name); + free(ftl); + } +} + +/* + * this simple greedy algorithm weighted by nerase does seem to lead + * to even wear of erase units (cf. the eNVy file system) + */ +static Terase * +bestcopy(Ftl *ftl) +{ + Terase *e, *be; + int i; + + be = nil; + for(i=0; inunit; i++) + if((e = ftl->unit[i]) != nil && e->id != XferID && e->id != XferBusy && e->ndead+e->nbad && + (be == nil || e->nerase <= be->nerase && e->ndead >= be->ndead)) + be = e; + return be; +} + +static int +copyunit(Ftl *ftl, Terase *from, Terase *to) +{ + int i, nb; + uchar id[2]; + ulong *bam; + uchar *buf; + ulong v, bno; + + if(FTLDEBUG || ftl->trace) + print("ftl: copying %d (#%lux) to #%lux\n", from->id, from->offset, to->offset); + to->nbam = 0; + free(to->bam); + to->bam = nil; + bam = nil; + buf = malloc(Bsize); + if(buf == nil) + return 0; + if(waserror()){ + free(buf); + free(bam); + return 0; + } + PUT2(id, XferBusy); + putflash(ftl, to->offset+offsetof(Merase,id[0]), id, 2); + /* make new BAM */ + nb = from->nbam*sizeof(*to->bam); + bam = malloc(nb); + if(bam == nil) + error(Enomem); + memmove(bam, from->bam, nb); + to->nused = 0; + to->nbad = 0; + to->nfree = 0; + to->ndead = 0; + for(i = 0; i < from->nbam; i++) + switch(bam[i]){ + case Bwriting: + case Bdeleted: + case Bfree: + bam[i] = Bfree; + to->nfree++; + break; + default: + switch(bam[i]&BlockType){ + default: + case BadBlock: /* it isn't necessarily bad in this unit */ + to->nfree++; + bam[i] = Bfree; + break; + case DataBlock: + case ReplacePage: + v = bam[i]; + bno = BNO(v & ~BlockType); + if(i < ftl->fstart || bno >= ftl->nblock){ + print("ftl: unit %d:#%x bad bam[%d]=#%lux\n", from->x, from->id, i, v); + to->nfree++; + bam[i] = Bfree; + break; + } + getflash(ftl, buf, from->offset+i*Bsize, Bsize); + putflash(ftl, to->offset+i*Bsize, buf, Bsize); + to->nused++; + break; + case ControlBlock: + to->nused++; + break; + } + } + for(i=0; inbam; i++){ + uchar *p = (uchar*)&bam[i]; + v = bam[i]; + if(v != Bfree && ftl->trace > 1) + print("to[%d]=#%lux\n", i, v); + PUT4(p, v); + } + putflash(ftl, to->bamoffset, bam, nb); /* BUG: PUT4 */ + for(i=0; inbam; i++){ + uchar *p = (uchar*)&bam[i]; + v = bam[i]; + PUT4(p, v); + } + to->id = from->id; + PUT2(id, to->id); + putflash(ftl, to->offset+offsetof(Merase,id[0]), id, 2); + to->nbam = from->nbam; + to->bam = bam; + ftl->nfree += to->nfree - from->nfree; + poperror(); + free(buf); + return 1; +} + +static int +mustscavenge(void *a) +{ + return ((Ftl*)a)->needspace || ((Ftl*)a)->detach == Deferred; +} + +static int +donescavenge(void *a) +{ + return ((Ftl*)a)->needspace == 0; +} + +static void +scavengeproc(void *arg) +{ + Ftl *ftl; + int i; + Terase *e, *ne; + + ftl = arg; + if(waserror()){ + print("ftl: kproc noted\n"); + pexit("ftldeath", 0); + } + for(;;){ + sleep(&ftl->workr, mustscavenge, ftl); + if(ftl->detach == Deferred){ + ftlfree(ftl); + pexit("", 0); + } + if(FTLDEBUG || ftl->trace) + print("ftl: scavenge %ld\n", ftl->nfree); + qlock(ftl); + if(waserror()){ + qunlock(ftl); + nexterror(); + } + e = bestcopy(ftl); + if(e == nil || ftl->xfer < 0 || (ne = ftl->unit[ftl->xfer]) == nil || ne->id != XferID || e == ne) + goto Fail; + if(copyunit(ftl, e, ne)){ + i = ne->x; ne->x = e->x; e->x = i; + ftl->unit[ne->x] = ne; + ftl->unit[e->x] = e; + ftl->xfer = e->x; + e->id = XferID; + e->nbam = 0; + free(e->bam); + e->bam = nil; + e->bamx = 0; + e->nerase++; + eraseinit(ftl, e->offset, XferID, e->nerase); + } + Fail: + if(FTLDEBUG || ftl->trace) + print("ftl: end scavenge %ld\n", ftl->nfree); + ftl->needspace = 0; + wakeup(&ftl->wantr); + poperror(); + qunlock(ftl); + } +} + +static int +scavenge(Ftl *ftl) +{ + if(ftl->xfer < 0 || bestcopy(ftl) == nil) + return 0; /* you worm! */ + + qlock(ftl); + if(waserror()){ + qunlock(ftl); + return 0; + } + if(!ftl->hasproc){ + ftl->hasproc = 1; + kproc("ftl.scavenge", scavengeproc, ftl, 0); + } + ftl->needspace = 1; + wakeup(&ftl->workr); + poperror(); + qunlock(ftl); + + qlock(&ftl->wantq); + if(waserror()){ + qunlock(&ftl->wantq); + nexterror(); + } + while(ftl->needspace) + sleep(&ftl->wantr, donescavenge, ftl); + poperror(); + qunlock(&ftl->wantq); + + return ftl->nfree; +} + +static void +putbam(Ftl *ftl, Terase *e, int n, ulong entry) +{ + uchar b[4]; + + e->bam[n] = entry; + PUT4(b, entry); + putflash(ftl, e->bamoffset + n*4, b, 4); +} + +static ulong +allocblk(Ftl *ftl) +{ + Terase *e; + int i, j; + + qlock(ftl); + i = ftl->lastx; + do{ + e = ftl->unit[i]; + if(e != nil && e->id != XferID && e->nfree){ + ftl->lastx = i; + for(j=e->bamx; jnbam; j++) + if(e->bam[j] == Bfree){ + putbam(ftl, e, j, Bwriting); + ftl->nfree--; + e->nfree--; + e->bamx = j+1; + qunlock(ftl); + return (e->x<<16) | j; + } + e->nfree = 0; + qunlock(ftl); + print("ftl: unit %d:#%x nfree %ld but not free in BAM\n", e->x, e->id, e->nfree); + qlock(ftl); + } + if(++i >= ftl->nunit) + i = 0; + }while(i != ftl->lastx); + qunlock(ftl); + return 0; +} + +static int +mapblk(Ftl *ftl, ulong bno, Terase **ep, ulong *bp) +{ + ulong v; + int x; + + if(bno < ftl->nblock){ + v = ftl->vbm[bno]; + if(v == 0 || v == ~0) + return 0; + x = v>>16; + if(x >= ftl->nunit || x == ftl->xfer || ftl->unit[x] == nil){ + print("ftl: corrupt format: bad block mapping %lud -> unit #%x\n", bno, x); + return 0; + } + *ep = ftl->unit[x]; + *bp = v & 0xFFFF; + return 1; + } + return 0; +} + +static void +eraseinit(Ftl *ftl, ulong offset, int id, int nerase) +{ + union { + Merase; + uchar block[ERASEHDRLEN]; + } *m; + uchar *bam, *p; + int i, nov; + + nov = ((ftl->segsize/Bsize)*4 + BAMoffset + Bsize - 1)/Bsize; /* number of overhead blocks (header, and BAM itself) */ + if(nov*Bsize >= ftl->segsize) + error("ftl -- too small for files"); + eraseflash(ftl, offset); + m = malloc(sizeof(*m)); + if(m == nil) + error(Enomem); + memset(m, 0xFF, sizeof(*m)); + m->linktuple[0] = 0x13; + m->linktuple[1] = 0x3; + memmove(m->linktuple+2, "CIS", 3); + m->orgtuple[0] = 0x46; + m->orgtuple[1] = 0x57; + m->orgtuple[2] = 0x00; + memmove(m->orgtuple+3, "FTL100", 7); + m->nxfer = 1; + PUT4(m->nerase, nerase); + PUT2(m->id, id); + m->bshift = ftl->bshift; + m->eshift = ftl->eshift; + PUT2(m->pstart, ftl->base >> ftl->eshift); + PUT2(m->nunits, ftl->nunit); + PUT4(m->psize, ftl->size - nov*Bsize*ftl->nunit); + PUT4(m->vbmbase, 0xffffffff); /* we always calculate the VBM */ + PUT2(m->nvbm, 0); + m->flags = 0; + m->code = 0xFF; + memmove(m->serial, "Inf1", 4); + PUT4(m->altoffset, 0); + PUT4(m->bamoffset, BAMoffset); + putflash(ftl, offset, m, ERASEHDRLEN); + free(m); + if(id == XferID) + return; + nov *= 4; /* now bytes of BAM */ + bam = malloc(nov); + if(bam == nil) + error(Enomem); + for(i=0; isegsize){ + if(waserror()) + continue; + getflash(ftl, m, o, ERASEHDRLEN); + poperror(); + if(memcmp(m->orgtuple + 3, "FTL100", 7) == 0 + && memcmp(m->serial, "Inf1", 4) == 0){ + *pstart = GET2(m->pstart); + *nunits = GET2(m->nunits); + rv = 1; + break; + } + } + free(m); + return rv; +} + +static Terase * +eraseload(Ftl *ftl, int x, ulong offset) +{ + union { + Merase; + uchar block[ERASEHDRLEN]; + } *m; + Terase *e; + uchar *p; + int i, nbam; + ulong bno, v; + + m = malloc(sizeof(*m)); + if(m == nil) + error(Enomem); + if(waserror()){ + free(m); + return nil; + } + getflash(ftl, m, offset, ERASEHDRLEN); + poperror(); + if(memcmp(m->orgtuple+3, "FTL100", 7) != 0 || + memcmp(m->serial, "Inf1", 4) != 0){ + free(m); +print("%8.8lux: bad sig\n", offset); + return nil; + } + e = malloc(sizeof(*e)); + if(e == nil){ + free(m); + error(Enomem); + } + e->x = x; + e->id = GET2(m->id); + e->offset = offset; + e->bamoffset = GET4(m->bamoffset); + e->nerase = GET4(m->nerase); + free(m); + m = nil; + USED(m); + if(e->bamoffset != BAMoffset){ + free(e); +print("%8.8lux: bad bamoffset %8.8lux\n", offset, e->bamoffset); + return nil; + } + e->bamoffset += offset; + if(e->id == XferID || e->id == XferBusy){ + e->bam = nil; + e->nbam = 0; + return e; + } + nbam = ftl->segsize/Bsize; + e->bam = malloc(nbam*sizeof(*e->bam)); + e->nbam = nbam; + if(waserror()){ + free(e); + nexterror(); + } + getflash(ftl, e->bam, e->bamoffset, nbam*4); + poperror(); + /* scan BAM to build VBM */ + e->bamx = 0; + for(i=0; ibam[i]; + e->bam[i] = v = GET4(p); + if(v == Bwriting || v == Bdeleted) + e->ndead++; + else if(v == Bfree){ + if(e->bamx == 0) + e->bamx = i; + e->nfree++; + ftl->nfree++; + }else{ + switch(v & BlockType){ + case ControlBlock: + break; + case DataBlock: + /* add to VBM */ + if(v & (1<<31)) + break; /* negative => VBM page, ignored */ + bno = BNO(v & ~BlockType); + if(i < ftl->fstart || bno >= ftl->nblock){ + print("ftl: unit %d:#%x bad bam[%d]=#%lux\n", e->x, e->id, i, v); + e->nbad++; + break; + } + ftl->vbm[bno] = (e->x<<16) | i; + e->nused++; + break; + case ReplacePage: + /* replacement VBM page; ignored */ + break; + default: + print("ftl: unit %d:#%x bad bam[%d]=%lux\n", e->x, e->id, i, v); + case BadBlock: + e->nbad++; + break; + } + } + } + return e; +} + +static void +erasefree(Terase *e) +{ + if(e){ + free(e->bam); + free(e); + } +} + +static void +eraseflash(Ftl *ftl, ulong offset) +{ + char cmd[40]; + + offset += ftl->base; + if(FTLDEBUG || ftl->trace) + print("ftl: erase seg @#%lux\n", offset); + snprint(cmd, sizeof(cmd), "erase 0x%8.8lux", offset); + if(kchanio(ftl->flashctl, cmd, strlen(cmd), OWRITE) <= 0){ + print("ftl: erase failed: %s\n", up->env->errstr); + error(up->env->errstr); + } +} + +static void +putflash(Ftl *ftl, ulong offset, void *buf, long n) +{ + offset += ftl->base; + if(ftl->trace) + print("ftl: write(#%lux, %ld)\n", offset, n); + ftl->flash->offset = offset; + if(kchanio(ftl->flash, buf, n, OWRITE) != n){ + print("ftl: flash write error: %s\n", up->env->errstr); + error(up->env->errstr); + } +} + +static void +getflash(Ftl *ftl, void *buf, ulong offset, long n) +{ + offset += ftl->base; + if(ftl->trace) + print("ftl: read(#%lux, %ld)\n", offset, n); + ftl->flash->offset = offset; + if(kchanio(ftl->flash, buf, n, OREAD) != n){ + print("ftl: flash read error %s\n", up->env->errstr); + error(up->env->errstr); + } +} + +Dev ftldevtab = { + 'X', /* TO DO */ + "ftl", + + devreset, + devinit, + devshutdown, + ftlattach, + ftlwalk, + ftlstat, + ftlopen, + devcreate, + ftlclose, + ftlread, + devbread, + ftlwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devi2c.c b/os/port/devi2c.c new file mode 100644 index 00000000..50512248 --- /dev/null +++ b/os/port/devi2c.c @@ -0,0 +1,227 @@ +/* + * i2c + * + * Copyright © 1998, 2003 Vita Nuova Limited. + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +typedef struct I2Cdir I2Cdir; + +enum{ + Qdir, + Qdata, + Qctl, +}; + +static +Dirtab i2ctab[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "i2cdata", {Qdata, 0}, 256, 0660, + "i2cctl", {Qctl, 0}, 0, 0660, +}; + +struct I2Cdir { + Ref; + I2Cdev; + Dirtab tab[nelem(i2ctab)]; +}; + +static void +i2creset(void) +{ + i2csetup(0); +} + +static Chan* +i2cattach(char* spec) +{ + char *s; + ulong addr; + I2Cdir *d; + Chan *c; + + addr = strtoul(spec, &s, 16); + if(*spec == 0 || *s || addr >= (1<<10)) + error("invalid i2c address"); + d = malloc(sizeof(I2Cdir)); + if(d == nil) + error(Enomem); + d->ref = 1; + d->addr = addr; + d->salen = 0; + d->tenbit = addr >= 128; + memmove(d->tab, i2ctab, sizeof(d->tab)); + sprint(d->tab[1].name, "i2c.%lux.data", addr); + sprint(d->tab[2].name, "i2c.%lux.ctl", addr); + + c = devattach('J', spec); + c->aux = d; + return c; +} + +static Walkqid* +i2cwalk(Chan* c, Chan *nc, char **name, int nname) +{ + Walkqid *wq; + I2Cdir *d; + + d = c->aux; + wq = devwalk(c, nc, name, nname, d->tab, nelem(d->tab), devgen); + if(wq != nil && wq->clone != nil && wq->clone != c) + incref(d); + return wq; +} + +static int +i2cstat(Chan* c, uchar *dp, int n) +{ + I2Cdir *d; + + d = c->aux; + return devstat(c, dp, n, d->tab, nelem(d->tab), devgen); +} + +static Chan* +i2copen(Chan* c, int omode) +{ + I2Cdir *d; + + d = c->aux; + return devopen(c, omode, d->tab, nelem(d->tab), devgen); +} + +static void +i2cclose(Chan *c) +{ + I2Cdir *d; + + d = c->aux; + if(decref(d) == 0) + free(d); +} + +static long +i2cread(Chan *c, void *a, long n, vlong offset) +{ + I2Cdir *d; + char *s, *e; + ulong len; + + d = c->aux; + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, d->tab, nelem(d->tab), devgen); + case Qdata: + len = d->tab[1].length; + if(offset+n >= len){ + n = len - offset; + if(n <= 0) + return 0; + } + n = i2crecv(d, a, n, offset); + break; + case Qctl: + s = smalloc(READSTR); + if(waserror()){ + free(s); + nexterror(); + } + e = seprint(s, s+READSTR, "size %lud\n", (ulong)d->tab[1].length); + if(d->salen) + e = seprint(e, s+READSTR, "subaddress %d\n", d->salen); + if(d->tenbit) + seprint(e, s+READSTR, "a10\n"); + n = readstr(offset, a, n, s); + poperror(); + free(s); + return n; + default: + n=0; + break; + } + return n; +} + +static long +i2cwrite(Chan *c, void *a, long n, vlong offset) +{ + I2Cdir *d; + long len; + Cmdbuf *cb; + + USED(offset); + switch((ulong)c->qid.path){ + case Qdata: + d = c->aux; + len = d->tab[1].length; + if(offset+n >= len){ + n = len - offset; + if(n <= 0) + return 0; + } + n = i2csend(d, a, n, offset); + break; + case Qctl: + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + if(cb->nf < 1) + error(Ebadctl); + d = c->aux; + if(strcmp(cb->f[0], "subaddress") == 0){ + if(cb->nf > 1){ + len = strtol(cb->f[1], nil, 0); + if(len <= 0) + len = 0; + if(len > 4) + cmderror(cb, "subaddress too long"); + }else + len = 1; + d->salen = len; + }else if(cb->nf > 1 && strcmp(cb->f[0], "size") == 0){ + len = strtol(cb->f[1], nil, 0); + if(len < 0) + cmderror(cb, "size is negative"); + d->tab[1].length = len; + }else if(strcmp(cb->f[0], "a10") == 0) + d->tenbit = 1; + else + cmderror(cb, "unknown control request"); + poperror(); + free(cb); + break; + default: + error(Ebadusefd); + } + return n; +} + +Dev i2cdevtab = { + 'J', + "i2c", + + i2creset, + devinit, + devshutdown, + i2cattach, + i2cwalk, + i2cstat, + i2copen, + devcreate, + i2cclose, + i2cread, + devbread, + i2cwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devindir.c b/os/port/devindir.c new file mode 100644 index 00000000..9f713bbc --- /dev/null +++ b/os/port/devindir.c @@ -0,0 +1,40 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +static Chan * +indirattach(char *spec) +{ + char *p; + Dev *d; + + if(*spec == 0) + error(Ebadspec); + p = strrchr(spec, '!'); + if(p == nil) + p = ""; + else + *p++ = 0; + d = devbyname(spec); + if(d == nil || d->dc == '*'){ + snprint(up->env->errstr, ERRMAX, "unknown device: %s", spec); + error(up->env->errstr); + } + if(up->env->pgrp->nodevs && + (utfrune("|esDa", d->dc) == nil || d->dc == 's' && *p!='\0')) + error(Enoattach); + return d->attach(p); +} + +Dev indirdevtab = { + '*', + "indir", + + devreset, + devinit, + devshutdown, + indirattach, +}; diff --git a/os/port/devkprof.c b/os/port/devkprof.c new file mode 100644 index 00000000..134f346a --- /dev/null +++ b/os/port/devkprof.c @@ -0,0 +1,230 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#ifndef LRES +#define LRES 3 /* log of PC resolution */ +#endif + +#define SZ 4 /* sizeof of count cell; well known as 4 */ + +enum { + SpecialTotalTicks, + SpecialOutsideTicks, + SpecialMicroSecondsPerTick, + SpecialSamples, + SpecialSampleSize, + SpecialSampleLogBucketSize, + SpecialMax +}; + +struct +{ + int minpc; + int maxpc; + int nbuf; + int time; + ulong *buf; +}kprof; + +enum{ + Qdir, + Qdata, + Qctl, + Kprofmaxqid, +}; + +Dirtab kproftab[]={ + ".", {Qdir, 0, QTDIR}, 0, 0500, + "kpdata", {Qdata}, 0, 0600, + "kpctl", {Qctl}, 0, 0600, +}; + +void kproftimer(ulong); +void (*kproftick)(ulong); + +static void +kprofinit(void) +{ + if(SZ != sizeof kprof.buf[0]) + panic("kprof size"); +} + +static void +kprofbufinit(void) +{ + kprof.buf[SpecialMicroSecondsPerTick] = archkprofmicrosecondspertick(); + kprof.buf[SpecialSamples] = kprof.nbuf; + kprof.buf[SpecialSampleSize] = SZ; + kprof.buf[SpecialSampleLogBucketSize] = LRES; +} + +static Chan * +kprofattach(char *spec) +{ + ulong n; + + /* allocate when first used */ + kproftick = kproftimer; + kprof.minpc = KTZERO; + kprof.maxpc = (ulong)etext; + kprof.nbuf = (kprof.maxpc-kprof.minpc) >> LRES; + n = kprof.nbuf*SZ; + if(kprof.buf == 0) { + kprof.buf = xalloc(n); + if(kprof.buf == 0) + error(Enomem); + } + kproftab[0].length = n; + kprofbufinit(); + return devattach('K', spec); +} + +static Walkqid* +kprofwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, kproftab, nelem(kproftab), devgen); +} + +static int +kprofstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, kproftab, nelem(kproftab), devgen); +} + +static Chan * +kprofopen(Chan *c, int omode) +{ + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Eperm); + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +void +kprofclose(Chan*) +{ +} + +static long +kprofread(Chan *c, void *va, long n, vlong offset) +{ + ulong tabend; + ulong w, *bp; + uchar *a, *ea; + + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, va, n, kproftab, nelem(kproftab), devgen); + + case Qdata: + tabend = kprof.nbuf*SZ; + if(offset & (SZ-1)) + error(Ebadarg); + if(offset >= tabend){ + n = 0; + break; + } + if(offset+n > tabend) + n = tabend-offset; + n &= ~(SZ-1); + a = va; + ea = a + n; + bp = kprof.buf + offset/SZ; + while(a < ea){ + w = *bp++; + *a++ = w>>24; + *a++ = w>>16; + *a++ = w>>8; + *a++ = w>>0; + } + break; + + default: + n = 0; + break; + } + return n; +} + +static long +kprofwrite(Chan *c, void *vp, long n, vlong offset) +{ + char *a; + USED(offset); + + a = vp; + switch((ulong)c->qid.path){ + case Qctl: + if(strncmp(a, "startclr", 8) == 0){ + memset((char *)kprof.buf, 0, kprof.nbuf*SZ); + kprofbufinit(); + archkprofenable(1); + kprof.time = 1; + }else if(strncmp(a, "start", 5) == 0) { + archkprofenable(1); + kprof.time = 1; + } + else if(strncmp(a, "stop", 4) == 0) { + archkprofenable(0); + kprof.time = 0; + } + else + error(Ebadctl); + break; + default: + error(Ebadusefd); + } + return n; +} + +void +kproftimer(ulong pc) +{ + extern void spldone(void); + + if(kprof.time == 0) + return; + /* + * if the pc is coming out of spllo or splx, + * use the pc saved when we went splhi. + */ +// if(pc>=(ulong)splx && pc<=(ulong)spldone) +// pc = m->splpc; + + kprof.buf[SpecialTotalTicks]++; + if(kprof.minpc + (SpecialMax << LRES) <= pc && pc < kprof.maxpc){ + pc -= kprof.minpc; + pc >>= LRES; + kprof.buf[pc]++; + }else + kprof.buf[SpecialOutsideTicks]++; +} + +Dev kprofdevtab = { + 'K', + "kprof", + + devreset, + kprofinit, + devshutdown, + kprofattach, + kprofwalk, + kprofstat, + kprofopen, + devcreate, + kprofclose, + kprofread, + devbread, + kprofwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devlogfs.c b/os/port/devlogfs.c new file mode 100755 index 00000000..630d3eba --- /dev/null +++ b/os/port/devlogfs.c @@ -0,0 +1,1531 @@ +#ifndef EMU +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#else +#include "error.h" +#endif +#include "dat.h" +#include "fns.h" +#include "kernel.h" +#include "logfs.h" +#include "nandfs.h" + +#ifndef EMU +#define Sleep sleep +#define Wakeup wakeup +#endif + +#ifndef offsetof +#define offsetof(T,X) ((ulong)&(((T*)0)->X)) +#endif + +typedef struct Devlogfs Devlogfs; +typedef struct DevlogfsSession DevlogfsSession; + +//#define CALLTRACE + +enum { + DEVLOGFSDEBUG = 0, + DEVLOGFSIODEBUG = 0, + DEVLOGFSBAD = 1, +}; + +enum { + Qdir, + Qctl, + Qusers, + Qdump, + Qfs, + Qfsboot, + Qend, +}; + +typedef enum DevlogfsServerState { Closed, BootOpen, NeedVersion, NeedAttach, Attached, Hungup } DevlogfsServerState; + +struct Devlogfs { + QLock qlock; + QLock rlock; + QLock wlock; + Ref ref; + int instance; + int trace; /* (debugging) trace of read/write actions */ + int nand; + char *name; + char *device; + char *filename[Qend - Qfs]; + LogfsLowLevel *ll; + Chan *flash, *flashctl; + QLock bootqlock; + int logfstrace; + LogfsBoot *lb; + /* stuff for server */ + ulong openflags; + Fcall in; + Fcall out; + int reading; + DevlogfsServerState state; + Rendez readrendez; + Rendez writerendez; + uint readcount; + ulong readbufsize; + uchar *readbuf; + uchar *readp; + LogfsServer *server; + Devlogfs *next; +}; + +#define MAXMSIZE 8192 + +static struct { + RWlock rwlock; /* rlock when walking, wlock when changing */ + QLock configqlock; /* serialises addition of new configurations */ + Devlogfs *head; + char *defname; +} devlogfslist; + +static LogfsIdentityStore *is; + +#ifndef EMU +char Eunknown[] = "unknown user or group id"; +#endif + +static void devlogfsfree(Devlogfs*); + +#define SPLITPATH(path, qtype, instance, qid, qt) { instance = path >> 4; qid = path & 0xf; qt = qtype & QTDIR; } +#define DATAQID(q, qt) (!(qt) && (q) >= Qfs && (q) < Qend) +#define MKPATH(instance, qid) ((instance << 4) | qid) + +#define PREFIX "logfs" + +static char *devlogfsprefix = PREFIX; +static char *devlogfsctlname = PREFIX "ctl"; +static char *devlogfsusersname = PREFIX "users"; +static char *devlogfsdumpname = PREFIX "dump"; +static char *devlogfsbootsuffix = "boot"; +static char *devlogfs9pversion = "9P2000"; + +static void +errorany(char *errmsg) +{ + if (errmsg) + error(errmsg); +} + +static void * +emalloc(ulong size) +{ + void *p; + p = logfsrealloc(nil, size); + if (p == nil) + error(Enomem); + return p; +} + +static char * +estrdup(char *q) +{ + void *p; + if (q == nil) + return nil; + p = logfsrealloc(nil, strlen(q) + 1); + if (p == nil) + error(Enomem); + return strcpy(p, q); +} + +static char * +estrconcat(char *a, ...) +{ + va_list l; + char *p, *r; + int t; + + t = strlen(a); + va_start(l, a); + while ((p = va_arg(l, char *)) != nil) + t += strlen(p); + + r = logfsrealloc(nil, t + 1); + if (r == nil) + error(Enomem); + + strcpy(r, a); + va_start(l, a); + while ((p = va_arg(l, char *)) != nil) + strcat(r, p); + + va_end(l); + + return r; +} + +static int +gen(Chan *c, int i, Dir *dp, int lockit) +{ + Devlogfs *l; + long size; + Qid qid; + qid.vers = 0; + qid.type = 0; + + if (i + Qctl < Qfs) { + switch (i + Qctl) { + case Qctl: + qid.path = Qctl; + devdir(c, qid, devlogfsctlname, 0, eve, 0666, dp); + return 1; + case Qusers: + qid.path = Qusers; + devdir(c, qid, devlogfsusersname, 0, eve, 0444, dp); + return 1; + case Qdump: + qid.path = Qdump; + devdir(c, qid, devlogfsdumpname, 0, eve, 0444, dp); + return 1; + } + } + + i -= Qfs - Qctl; + + if (lockit) + rlock(&devlogfslist.rwlock); + + if (waserror()) { + if (lockit) + runlock(&devlogfslist.rwlock); + nexterror(); + } + + for (l = devlogfslist.head; l; l = l->next) { + if (i < Qend - Qfs) + break; + i -= Qend - Qfs; + } + + if (l == nil) { + poperror(); + if (lockit) + runlock(&devlogfslist.rwlock); + return -1; + } + + switch (Qfs + i) { + case Qfsboot: + size = l->lb ? logfsbootgetsize(l->lb) : 0; + break; + default: + size = 0; + break; + } + /* perhaps the user id should come from the underlying file */ + qid.path = MKPATH(l->instance, Qfs + i); + devdir(c, qid, l->filename[i], size, eve, 0666, dp); + + poperror(); + if (lockit) + runlock(&devlogfslist.rwlock); + + return 1; +} + +static int +devlogfsgen(Chan *c, char *n, Dirtab *tab, int ntab, int i, Dir *dp) +{ + USED(n); + USED(tab); + USED(ntab); + return gen(c, i, dp, 1); +} + +static int +devlogfsgennolock(Chan *c, char *n, Dirtab *tab, int ntab, int i, Dir *dp) +{ + USED(n); + USED(tab); + USED(ntab); + return gen(c, i, dp, 0); +} + +/* called under lock */ +static Devlogfs * +devlogfsfind(int instance) +{ + Devlogfs *l; + + for (l = devlogfslist.head; l; l = l->next) + if (l->instance == instance) + break; + return l; +} + +static Devlogfs * +devlogfsget(int instance) +{ + Devlogfs *l; + rlock(&devlogfslist.rwlock); + for (l = devlogfslist.head; l; l = l->next) + if (l->instance == instance) + break; + if (l) + incref(&l->ref); + runlock(&devlogfslist.rwlock); + return l; +} + +static Devlogfs * +devlogfsfindbyname(char *name) +{ + Devlogfs *l; + + rlock(&devlogfslist.rwlock); + for (l = devlogfslist.head; l; l = l->next) + if (strcmp(l->name, name) == 0) + break; + runlock(&devlogfslist.rwlock); + return l; +} + +static Devlogfs * +devlogfssetdefname(char *name) +{ + Devlogfs *l; + char *searchname; + wlock(&devlogfslist.rwlock); + if (waserror()) { + wunlock(&devlogfslist.rwlock); + nexterror(); + } + if (name == nil) + searchname = devlogfslist.defname; + else + searchname = name; + for (l = devlogfslist.head; l; l = l->next) + if (strcmp(l->name, searchname) == 0) + break; + if (l == nil) { + logfsfreemem(devlogfslist.defname); + devlogfslist.defname = nil; + } + else if (name) { + if (devlogfslist.defname) { + logfsfreemem(devlogfslist.defname); + devlogfslist.defname = nil; + } + devlogfslist.defname = estrdup(name); + } + poperror(); + wunlock(&devlogfslist.rwlock); + return l; +} + +static Chan * +devlogfskopen(char *name, char *suffix, int mode) +{ + Chan *c; + char *fn; + int fd; + + fn = estrconcat(name, suffix, 0); + fd = kopen(fn, mode); + logfsfreemem(fn); + if (fd < 0) + error(up->env->errstr); + c = fdtochan(up->env->fgrp, fd, mode, 0, 1); + kclose(fd); + return c; +} + +static char * +xread(void *a, void *buf, long nbytes, ulong offset) +{ + Devlogfs *l = a; + long rv; + + if (DEVLOGFSIODEBUG || l->trace) + print("devlogfs: %s: read(0x%lux, %ld)\n", l->device, offset, nbytes); + l->flash->offset = offset; + rv = kchanio(l->flash, buf, nbytes, OREAD); + if (rv < 0) { + print("devlogfs: %s: flash read error: %s\n", l->device, up->env->errstr); + return up->env->errstr; + } + if (rv != nbytes) { + print("devlogfs: %s: short flash read: offset %lud, %ld not %ld\n", l->device, offset, rv, nbytes); + return "short read"; + } + return nil; +} + +static char * +xwrite(void *a, void *buf, long nbytes, ulong offset) +{ + Devlogfs *l = a; + long rv; + + if (DEVLOGFSIODEBUG || l->trace) + print("devlogfs: %s: write(0x%lux, %ld)\n", l->device, offset, nbytes); + l->flash->offset = offset; + rv = kchanio(l->flash, buf, nbytes, OWRITE); + if (rv < 0) { + print("devlogfs: %s: flash write error: %s\n", l->device, up->env->errstr); + return up->env->errstr; + } + if (rv != nbytes) { + print("devlogfs: %s: short flash write: offset %lud, %ld not %ld\n", l->device, offset, rv, nbytes); + return "short write"; + } + return nil; +} + +static char * +xerase(void *a, long address) +{ + Devlogfs *l = a; + char cmd[40]; + + if (DEVLOGFSIODEBUG || l->trace) + print("devlogfs: %s: erase(0x%lux)\n", l->device, address); + snprint(cmd, sizeof(cmd), "erase 0x%8.8lux", address); + if (kchanio(l->flashctl, cmd, strlen(cmd), OWRITE) <= 0) { + print("devlogfs: %s: flash erase error: %s\n", l->device, up->env->errstr); + return up->env->errstr; + } + return nil; +} + +static char * +xsync(void *a) +{ + Devlogfs *l = a; + + if (DEVLOGFSIODEBUG || l->trace) + print("devlogfs: %s: sync()\n", l->device); + if (kchanio(l->flashctl, "sync", 4, OWRITE) <= 0){ + print("devlogfs: %s: flash sync error: %s\n", l->device, up->env->errstr); + return up->env->errstr; + } + return nil; +} + +//#define LEAKHUNT +#ifdef LEAKHUNT +#define MAXLIVE 2000 +typedef struct Live { + void *p; + int freed; + ulong callerpc; +} Live; + +static Live livemem[MAXLIVE]; + +static void +leakalloc(void *p, ulong callerpc) +{ + int x; + int use = -1; + for (x = 0; x < MAXLIVE; x++) { + if (livemem[x].p == p) { + if (!livemem[x].freed) + print("leakalloc: unexpected realloc of 0x%.8lux from 0x%.8lux\n", p, callerpc); +// else +// print("leakalloc: reusing address 0x%.8lux from 0x%.8lux\n", p, callerpc); + livemem[x].freed = 0; + livemem[x].callerpc = callerpc; + return; + } + else if (use < 0 && livemem[x].p == 0) + use = x; + } + if (use < 0) + panic("leakalloc: too many live entries"); + livemem[use].p = p; + livemem[use].freed = 0; + livemem[use].callerpc = callerpc; +} + +static void +leakaudit(void) +{ + int x; + for (x = 0; x < MAXLIVE; x++) { + if (livemem[x].p && !livemem[x].freed) + print("leakaudit: 0x%.8lux from 0x%.8lux\n", livemem[x].p, livemem[x].callerpc); + } +} + +static void +leakfree(void *p, ulong callerpc) +{ + int x; + if (p == nil) + return; + for (x = 0; x < MAXLIVE; x++) { + if (livemem[x].p == p) { + if (livemem[x].freed) + print("leakfree: double free of 0x%.8lux from 0x%.8lux, originally by 0x%.8lux\n", + p, callerpc, livemem[x].callerpc); + livemem[x].freed = 1; + livemem[x].callerpc = callerpc; + return; + } + } + print("leakfree: free of unalloced address 0x%.8lux from 0x%.8lux\n", p, callerpc); + leakaudit(); +} + +static void +leakrealloc(void *newp, void *oldp, ulong callerpc) +{ + leakfree(oldp, callerpc); + leakalloc(newp, callerpc); +} +#endif + + +#ifdef LEAKHUNT +static void *_realloc(void *p, ulong size, ulong callerpc) +#else +void * +logfsrealloc(void *p, ulong size) +#endif +{ + void *q; + ulong osize; + if (waserror()) { + print("wobbly thrown in memory allocator: %s\n", up->env->errstr); + nexterror(); + } + if (p == nil) { + q = smalloc(size); + poperror(); +#ifdef LEAKHUNT + leakrealloc(q, nil, callerpc); +#endif + return q; + } + q = realloc(p, size); + if (q) { + poperror(); +#ifdef LEAKHUNT + leakrealloc(q, p, callerpc); +#endif + return q; + } + q = smalloc(size); + osize = msize(p); + if (osize > size) + osize = size; + memmove(q, p, osize); + free(p); + poperror(); +#ifdef LEAKHUNT + leakrealloc(q, p, callerpc); +#endif + return q; +} + +#ifdef LEAKHUNT +void * +logfsrealloc(void *p, ulong size) +{ + return _realloc(p, size, getcallerpc(&p)); +} + +void * +nandfsrealloc(void *p, ulong size) +{ + return _realloc(p, size, getcallerpc(&p)); +} +#else +void * +nandfsrealloc(void *p, ulong size) +{ + return logfsrealloc(p, size); +} +#endif + +void +logfsfreemem(void *p) +{ +#ifdef LEAKHUNT + leakfree(p, getcallerpc(&p)); +#endif + free(p); +} + +void +nandfsfreemem(void *p) +{ +#ifdef LEAKHUNT + leakfree(p, getcallerpc(&p)); +#endif + free(p); +} + +static Devlogfs * +devlogfsconfig(char *name, char *device) +{ + Devlogfs *newl, *l; + int i; + int n; + char buf[100], *fields[12]; + long rawblocksize, rawsize; + + newl = nil; + + qlock(&devlogfslist.configqlock); + + if (waserror()) { + qunlock(&devlogfslist.configqlock); + devlogfsfree(newl); + nexterror(); + } + + rlock(&devlogfslist.rwlock); + for (l = devlogfslist.head; l; l = l->next) + if (strcmp(l->name, name) == 0) { + runlock(&devlogfslist.rwlock); + error(Einuse); + } + + /* horrid n^2 solution to finding a unique instance number */ + + for (i = 0;; i++) { + for (l = devlogfslist.head; l; l = l->next) + if (l->instance == i) + break; + if (l == nil) + break; + } + runlock(&devlogfslist.rwlock); + + newl = emalloc(sizeof(Devlogfs)); + newl->instance = i; + newl->name = estrdup(name); + newl->device = estrdup(device); + newl->filename[Qfs - Qfs] = estrconcat(devlogfsprefix, name, nil); + newl->filename[Qfsboot - Qfs] = estrconcat(devlogfsprefix, name, devlogfsbootsuffix, nil); + newl->flash = devlogfskopen(device, nil, ORDWR); + newl->flashctl = devlogfskopen(device, "ctl", ORDWR); + newl->flashctl->offset = 0; + if ((n = kchanio(newl->flashctl, buf, sizeof(buf), OREAD)) <= 0) { + print("devlogfsconfig: read ctl failed: %s\n", up->env->errstr); + error(up->env->errstr); + } + + if (n >= sizeof(buf)) + n = sizeof(buf) - 1; + buf[n] = 0; + n = tokenize(buf, fields, nelem(fields)); + if(n < 7) + error("unexpected flashctl format"); + newl->nand = strcmp(fields[3], "nand") == 0; + rawblocksize = strtol(fields[6], nil, 0); + rawsize = strtol(fields[5], nil, 0)-strtol(fields[4], nil, 0); + if(newl->nand == 0) + error("only NAND supported at the moment"); + errorany(nandfsinit(newl, rawsize, rawblocksize, xread, xwrite, xerase, xsync, &newl->ll)); + wlock(&devlogfslist.rwlock); + newl->next = devlogfslist.head; + devlogfslist.head = newl; + logfsfreemem(devlogfslist.defname); + devlogfslist.defname = nil; + if (!waserror()){ + devlogfslist.defname = estrdup(name); + poperror(); + } + wunlock(&devlogfslist.rwlock); + poperror(); + qunlock(&devlogfslist.configqlock); + return newl; +} + +static void +devlogfsunconfig(Devlogfs *devlogfs) +{ + Devlogfs **lp; + + qlock(&devlogfslist.configqlock); + + if (waserror()) { + qunlock(&devlogfslist.configqlock); + nexterror(); + } + + wlock(&devlogfslist.rwlock); + + if (waserror()) { + wunlock(&devlogfslist.rwlock); + nexterror(); + } + + for (lp = &devlogfslist.head; *lp && (*lp) != devlogfs; lp = &(*lp)->next) + ; + if (*lp == nil) { + if (DEVLOGFSBAD) + print("devlogfsunconfig: not in list\n"); + } + else + *lp = devlogfs->next; + + poperror(); + wunlock(&devlogfslist.rwlock); + + /* now invisible to the naked eye */ + devlogfsfree(devlogfs); + poperror(); + qunlock(&devlogfslist.configqlock); +} + +static void +devlogfsllopen(Devlogfs *l) +{ + qlock(&l->qlock); + if (waserror()) { + qunlock(&l->qlock); + nexterror(); + } + if (l->lb == nil) + errorany(logfsbootopen(l->ll, 0, 0, l->logfstrace, 1, &l->lb)); + l->state = BootOpen; + poperror(); + qunlock(&l->qlock); +} + +static void +devlogfsllformat(Devlogfs *l, long bootsize) +{ + qlock(&l->qlock); + if (waserror()) { + qunlock(&l->qlock); + nexterror(); + } + if (l->lb == nil) + errorany(logfsformat(l->ll, 0, 0, bootsize, l->logfstrace)); + poperror(); + qunlock(&l->qlock); +} + +static Chan * +devlogfsattach(char *spec) +{ + Chan *c; +#ifdef CALLTRACE + print("devlogfsattach(spec = %s) - start\n", spec); +#endif + /* create the identity store on first attach */ + if (is == nil) + errorany(logfsisnew(&is)); + c = devattach(0x29f, spec); +// c = devattach(L'ʟ', spec); +#ifdef CALLTRACE + print("devlogfsattach(spec = %s) - return %.8lux\n", spec, (ulong)c); +#endif + return c; +} + +static Walkqid* +devlogfswalk(Chan *c, Chan *nc, char **name, int nname) +{ + int instance, qid, qt, clone; + Walkqid *wq; + +#ifdef CALLTRACE + print("devlogfswalk(c = 0x%.8lux, nc = 0x%.8lux, name = 0x%.8lux, nname = %d) - start\n", + (ulong)c, (ulong)nc, (ulong)name, nname); +#endif + clone = 0; + if(nc == nil){ + nc = devclone(c); + nc->type = 0; + SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt); + if(DATAQID(qid, qt)) + nc->aux = devlogfsget(instance); + clone = 1; + } + wq = devwalk(c, nc, name, nname, 0, 0, devlogfsgen); + if (wq == nil || wq->nqid < nname) { + if(clone) + cclose(nc); + } + else if (clone) { + wq->clone = nc; + nc->type = c->type; + } +#ifdef CALLTRACE + print("devlogfswalk(c = 0x%.8lux, nc = 0x%.8lux, name = 0x%.8lux, nname = %d) - return\n", + (ulong)c, (ulong)nc, (ulong)name, nname); +#endif + return wq; +} + +static int +devlogfsstat(Chan *c, uchar *dp, int n) +{ +#ifdef CALLTRACE + print("devlogfsstat(c = 0x%.8lux, dp = 0x%.8lux n= %d)\n", + (ulong)c, (ulong)dp, n); +#endif + return devstat(c, dp, n, 0, 0, devlogfsgen); +} + +static Chan* +devlogfsopen(Chan *c, int omode) +{ + int instance, qid, qt; + + omode = openmode(omode); + SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt); +#ifdef CALLTRACE + print("devlogfsopen(c = 0x%.8lux, omode = %o, instance = %d, qid = %d, qt = %d)\n", + (ulong)c, omode, instance, qid, qt); +#endif + + + rlock(&devlogfslist.rwlock); + if (waserror()) { + runlock(&devlogfslist.rwlock); +#ifdef CALLTRACE + print("devlogfsopen(c = 0x%.8lux, omode = %o) - error %s\n", (ulong)c, omode, up->env->errstr); +#endif + nexterror(); + } + + if (DATAQID(qid, qt)) { + Devlogfs *d; + d = devlogfsfind(instance); + if (d == nil) + error(Enodev); + if (strcmp(up->env->user, eve) != 0) + error(Eperm); + if (qid == Qfs && d->state != BootOpen) + error(Eperm); + if (d->server == nil) { + errorany(logfsservernew(d->lb, d->ll, is, d->openflags, d->logfstrace, &d->server)); + d->state = NeedVersion; + } + c = devopen(c, omode, 0, 0, devlogfsgennolock); + incref(&d->ref); + c->aux = d; + } + else if (qid == Qctl || qid == Qusers) { + if (strcmp(up->env->user, eve) != 0) + error(Eperm); + c = devopen(c, omode, 0, 0, devlogfsgennolock); + } + else + c = devopen(c, omode, 0, 0, devlogfsgennolock); + poperror(); + runlock(&devlogfslist.rwlock); +#ifdef CALLTRACE + print("devlogfsopen(c = 0x%.8lux, omode = %o) - return\n", (ulong)c, omode); +#endif + return c; +} + +static void +devlogfsclose(Chan *c) +{ + int instance, qid, qt; +#ifdef CALLTRACE + print("devlogfsclose(c = 0x%.8lux)\n", (ulong)c); +#endif + SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt); + USED(instance); + if(DATAQID(qid, qt) && (c->flag & COPEN) != 0) { + Devlogfs *d; + d = c->aux; + qlock(&d->qlock); + if (qid == Qfs && d->state == Attached) { + logfsserverflush(d->server); + logfsserverfree(&d->server); + d->state = BootOpen; + } + qunlock(&d->qlock); + decref(&d->ref); + } +#ifdef CALLTRACE + print("devlogfsclose(c = 0x%.8lux) - return\n", (ulong)c); +#endif +} + +typedef char *(SMARTIOFN)(void *magic, void *buf, long n, ulong offset, int write); + +static void +smartio(SMARTIOFN *io, void *magic, void *buf, long n, ulong offset, long blocksize, int write) +{ + void *tmp = nil; + ulong blocks, toread; + + if (waserror()) { + logfsfreemem(tmp); + nexterror(); + } + if (offset % blocksize) { + ulong aoffset; + int tmpoffset; + int tocopy; + + if (tmp == nil) + tmp = emalloc(blocksize); + aoffset = offset / blocksize; + aoffset *= blocksize; + errorany((*io)(magic, tmp, blocksize, aoffset, 0)); + tmpoffset = offset - aoffset; + tocopy = blocksize - tmpoffset; + if (tocopy > n) + tocopy = n; + if (write) { + memmove((uchar *)tmp + tmpoffset, buf, tocopy); + errorany((*io)(magic, tmp, blocksize, aoffset, 1)); + } + else + memmove(buf, (uchar *)tmp + tmpoffset, tocopy); + buf = (uchar *)buf + tocopy; + n -= tocopy; + offset = aoffset + blocksize; + } + blocks = n / blocksize; + toread = blocks * blocksize; + errorany((*io)(magic, buf, toread, offset, write)); + buf = (uchar *)buf + toread; + n -= toread; + offset += toread; + if (n) { + if (tmp == nil) + tmp = emalloc(blocksize); + errorany((*io)(magic, tmp, blocksize, offset, 0)); + if (write) { + memmove(tmp, buf, n); + errorany((*io)(magic, tmp, blocksize, offset, 1)); + } + memmove(buf, tmp, n); + } + poperror(); + logfsfreemem(tmp); +} + +static int +readok(void *a) +{ + Devlogfs *d = a; + return d->reading; +} + +static int +writeok(void *a) +{ + Devlogfs *d = a; + return !d->reading; +} + +static long +lfsrvread(Devlogfs *d, void *buf, long n) +{ + qlock(&d->rlock); + if(waserror()){ + qunlock(&d->rlock); + nexterror(); + } + if (d->state == Hungup) + error(Ehungup); + Sleep(&d->readrendez, readok, d); + if (n > d->readcount) + n = d->readcount; + memmove(buf, d->readp, n); + d->readp += n; + d->readcount -= n; + if (d->readcount == 0) { + d->reading = 0; + Wakeup(&d->writerendez); + } + poperror(); + qunlock(&d->rlock); + return n; +} + +static void +reply(Devlogfs *d) +{ + d->readp = d->readbuf; + d->readcount = convS2M(&d->out, d->readp, d->readbufsize); +//print("reply is %d bytes\n", d->readcount); + if (d->readcount == 0) + panic("logfs: reply: did not fit\n"); + d->reading = 1; + Wakeup(&d->readrendez); +} + +static void +rerror(Devlogfs *d, char *ename) +{ + d->out.type = Rerror; + d->out.ename = ename; + reply(d); +} + +static struct { + QLock qlock; + int (*read)(void *magic, Devlogfs *d, int line, char *buf, int buflen); + void *magic; + Devlogfs *d; + int line; +} dump; + +static void * +extentdumpinit(Devlogfs *d, int argc, char **argv) +{ + int *p; + ulong path; + u32int flashaddr, length; + long block; + int page, offset; + + if (argc != 1) + error(Ebadarg); + path = strtoul(argv[0], 0, 0); + errorany(logfsserverreadpathextent(d->server, path, 0, &flashaddr, &length, &block, &page, &offset)); + p = emalloc(sizeof(ulong)); + *p = path; + return p; +} + +static int +extentdumpread(void *magic, Devlogfs *d, int line, char *buf, int buflen) +{ + ulong *p = magic; + u32int flashaddr, length; + long block; + int page, offset; + USED(d); + errorany(logfsserverreadpathextent(d->server, *p, line, &flashaddr, &length, &block, &page, &offset)); + if (length == 0) + return 0; + return snprint(buf, buflen, "%.8ux %ud %ld %d %d\n", flashaddr, length, block, page, offset); +} + +static void +devlogfsdumpinit(Devlogfs *d, + void *(*init)(Devlogfs *d, int argc, char **argv), + int (*read)(void *magic, Devlogfs *d, int line, char *buf, int buflen), int argc, char **argv) +{ + qlock(&dump.qlock); + if (waserror()) { + qunlock(&dump.qlock); + nexterror(); + } + if (d) { + if (d->state < NeedVersion) + error("not mounted"); + qlock(&d->qlock); + if (waserror()) { + qunlock(&d->qlock); + nexterror(); + } + } + if (dump.magic) { + logfsfreemem(dump.magic); + dump.magic = nil; + } + dump.d = d; + dump.magic = (*init)(d, argc, argv); + dump.read = read; + dump.line = 0; + if (d) { + poperror(); + qunlock(&d->qlock); + } + poperror(); + qunlock(&dump.qlock); +} + +static long +devlogfsdumpread(char *buf, int buflen) +{ + char *tmp = nil; + long n; + qlock(&dump.qlock); + if (waserror()) { + logfsfreemem(tmp); + qunlock(&dump.qlock); + nexterror(); + } + if (dump.magic == nil) + error(Eio); + tmp = emalloc(READSTR); + if (dump.d) { + if (dump.d->state < NeedVersion) + error("not mounted"); + qlock(&dump.d->qlock); + if (waserror()) { + qunlock(&dump.d->qlock); + nexterror(); + } + } + n = (*dump.read)(dump.magic, dump.d, dump.line, tmp, READSTR); + if (n) { + dump.line++; + n = readstr(0, buf, buflen, tmp); + } + if (dump.d) { + poperror(); + qunlock(&dump.d->qlock); + } + logfsfreemem(tmp); + poperror(); + qunlock(&dump.qlock); + return n; +} + +static void +devlogfsserverlogsweep(Devlogfs *d, int justone) +{ + int didsomething; + if (d->state < NeedVersion) + error("not mounted"); + qlock(&d->qlock); + if (waserror()) { + qunlock(&d->qlock); + nexterror(); + } + errorany(logfsserverlogsweep(d->server, justone, &didsomething)); + poperror(); + qunlock(&d->qlock); +} + +static void +devlogfsserversync(Devlogfs *d) +{ + if (d->state < NeedVersion) + return; + qlock(&d->qlock); + if (waserror()) { + qunlock(&d->qlock); + nexterror(); + } + errorany(logfsserverflush(d->server)); + poperror(); + qunlock(&d->qlock); +} + +static void +lfssrvwrite(Devlogfs *d, void *buf, long n) +{ + volatile int locked = 0; + + qlock(&d->wlock); + if(waserror()){ + qunlock(&d->wlock); + nexterror(); + } + if (d->state == Hungup) + error(Ehungup); + Sleep(&d->writerendez, writeok, d); + if (convM2S(buf, n, &d->in) != n) { + /* + * someone is writing drivel; have nothing to do with them anymore + * most common cause; trying to mount authenticated + */ + d->state = Hungup; + error(Ehungup); + } + d->out.tag = d->in.tag; + d->out.fid = d->in.fid; + d->out.type = d->in.type + 1; + if (waserror()) { + if (locked) + qunlock(&d->qlock); + rerror(d, up->env->errstr); + goto Replied; + } + if (d->in.type != Tversion && d->in.type != Tattach) { + if (d->state != Attached) + error("must be attached"); + qlock(&d->qlock); + locked = 1; + } + switch (d->in.type) { + case Tauth: + error("no authentication needed"); + case Tversion: { + char *rversion; + if (d->state != NeedVersion) + error("unexpected Tversion"); + if (d->in.tag != NOTAG) + error("protocol botch"); + /* + * check the version string + */ + if (strcmp(d->in.version, devlogfs9pversion) != 0) + rversion = "unknown"; + else + rversion = devlogfs9pversion; + /* + * allocate the reply buffer + */ + d->readbufsize = d->in.msize; + if (d->readbufsize > MAXMSIZE) + d->readbufsize = MAXMSIZE; + d->readbuf = emalloc(d->readbufsize); + /* + * compose the Rversion + */ + d->out.msize = d->readbufsize; + d->out.version = rversion; + d->state = NeedAttach; + break; + } + case Tattach: + if (d->state != NeedAttach) + error("unexpected attach"); + if (d->in.afid != NOFID) + error("unexpected afid"); + errorany(logfsserverattach(d->server, d->in.fid, d->in.uname, &d->out.qid)); + d->state = Attached; + break; + case Tclunk: + errorany(logfsserverclunk(d->server, d->in.fid)); + break; + case Tcreate: + errorany(logfsservercreate(d->server, d->in.fid, d->in.name, d->in.perm, d->in.mode, &d->out.qid)); + d->out.iounit = d->readbufsize - 11; + break; + case Tflush: + break; + case Topen: + errorany(logfsserveropen(d->server, d->in.fid, d->in.mode, &d->out.qid)); + d->out.iounit = d->readbufsize - 11; + break; + case Tread: + d->out.data = (char *)d->readbuf + 11; + /* TODO - avoid memmove */ + errorany(logfsserverread(d->server, d->in.fid, d->in.offset, d->in.count, (uchar *)d->out.data, + d->readbufsize - 11, &d->out.count)); + break; + case Tremove: + errorany(logfsserverremove(d->server, d->in.fid)); + break; + case Tstat: + d->out.stat = d->readbuf + 9; + /* TODO - avoid memmove */ + errorany(logfsserverstat(d->server, d->in.fid, d->out.stat, d->readbufsize - 9, &d->out.nstat)); +// print("nstat %d\n", d->out.nstat); + break; + case Twalk: + errorany(logfsserverwalk(d->server, d->in.fid, d->in.newfid, + d->in.nwname, d->in.wname, &d->out.nwqid, d->out.wqid)); + break; + case Twrite: + errorany(logfsserverwrite(d->server, d->in.fid, d->in.offset, d->in.count, (uchar *)d->in.data, + &d->out.count)); + break; + case Twstat: + errorany(logfsserverwstat(d->server, d->in.fid, d->in.stat, d->in.nstat)); + break; + default: + print("lfssrvwrite: msg %d unimplemented\n", d->in.type); + error("unimplemented"); + } + poperror(); + if (locked) + qunlock(&d->qlock); + reply(d); +Replied: + poperror(); + qunlock(&d->wlock); +} + +static long +devlogfsread(Chan *c, void *buf, long n, vlong off) +{ + int instance, qid, qt; + + SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt); + USED(instance); +#ifdef CALLTRACE + print("devlogfsread(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - start\n", + (ulong)c, (ulong)buf, n, instance, qid, qt); +#endif + if(qt & QTDIR) { +#ifdef CALLTRACE + print("devlogfsread(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - calling devdirread\n", + (ulong)c, (ulong)buf, n, instance, qid, qt); +#endif + return devdirread(c, buf, n, 0, 0, devlogfsgen); + } + + if(DATAQID(qid, qt)) { + if (qid == Qfsboot) { + Devlogfs *l = c->aux; + qlock(&l->bootqlock); + if (waserror()) { + qunlock(&l->bootqlock); + nexterror(); + } + smartio((SMARTIOFN *)logfsbootio, l->lb, buf, n, off, logfsbootgetiosize(l->lb), 0); + poperror(); + qunlock(&l->bootqlock); + return n; + } + else if (qid == Qfs) { + Devlogfs *d = c->aux; + return lfsrvread(d, buf, n); + } + error(Eio); + } + + if (qid == Qusers) { + long nr; + errorany(logfsisusersread(is, buf, n, (ulong)off, &nr)); + return nr; + } + else if (qid == Qdump) + return devlogfsdumpread(buf, n); + + if (qid != Qctl) + error(Egreg); + + return 0; +} + +enum { + CMconfig, + CMformat, + CMopen, + CMsweep, + CMtrace, + CMunconfig, + CMextent, + CMsweepone, + CMtest, + CMleakaudit, + CMsync +}; + +static Cmdtab fscmds[] = { + {CMconfig, "config", 2}, + {CMformat, "format", 2}, + {CMopen, "open", 0}, + {CMsweep, "sweep", 1}, + {CMsweepone, "sweepone", 1}, + {CMtrace, "trace", 0}, + {CMunconfig, "unconfig", 1}, + {CMextent, "extent", 0}, + {CMtest, "test", 0}, + {CMleakaudit, "leakaudit", 1}, + {CMsync, "sync", 1}, +}; + +static long +devlogfswrite(Chan *c, void *buf, long n, vlong off) +{ + int instance, qid, qt, i; + Cmdbuf *cmd; + Cmdtab *ct; + + if(n <= 0) + return 0; + SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt); +#ifdef CALLTRACE + print("devlogfswrite(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - start\n", + (ulong)c, (ulong)buf, n, instance, qid, qt); +#endif + USED(instance); + if(DATAQID(qid, qt)){ + if (qid == Qfsboot) { + Devlogfs *l = c->aux; + qlock(&l->bootqlock); + if (waserror()) { + qunlock(&l->bootqlock); + nexterror(); + } + smartio((SMARTIOFN *)logfsbootio, l->lb, buf, n, off, logfsbootgetiosize(l->lb), 1); + poperror(); + qunlock(&l->bootqlock); + return n; + } + else if (qid == Qfs) { + Devlogfs *d = c->aux; + lfssrvwrite(d, buf, n); + return n; + } + error(Eio); + } + else if (qid == Qctl) { + Devlogfs *l = nil; + int a; + + cmd = parsecmd(buf, n); + if(waserror()){ + free(cmd); + nexterror(); + } + i = cmd->nf; + if(0){print("i=%d", i); for(i=0; inf; i++)print(" %q", cmd->f[i]); print("\n");} + if (i <= 0) + error(Ebadarg); + if (i == 3 && strcmp(cmd->f[0], "uname") == 0) { + switch (cmd->f[2][0]) { + default: + errorany(logfsisgroupcreate(is, cmd->f[1], cmd->f[2])); + break; + case ':': + errorany(logfsisgroupcreate(is, cmd->f[1], cmd->f[2] + 1)); + break; + case '%': + errorany(logfsisgrouprename(is, cmd->f[1], cmd->f[2] + 1)); + break; + case '=': + errorany(logfsisgroupsetleader(is, cmd->f[1], cmd->f[2] + 1)); + break; + case '+': + errorany(logfsisgroupaddmember(is, cmd->f[1], cmd->f[2] + 1)); + break; + case '-': + errorany(logfsisgroupremovemember(is, cmd->f[1], cmd->f[2] + 1)); + break; + } + i = 0; + } + if (i == 4 && strcmp(cmd->f[0], "fsys") == 0 && strcmp(cmd->f[2], "config") == 0) { + l = devlogfsconfig(cmd->f[1], cmd->f[3]); + i = 0; + } + else if (i >= 2 && strcmp(cmd->f[0], "fsys") == 0) { + l = devlogfssetdefname(cmd->f[1]); + if (l == nil) + errorf("file system %q not configured", cmd->f[1]); + i -= 2; + cmd->f += 2; + cmd->nf = i; + } + if (i != 0) { + ct = lookupcmd(cmd, fscmds, nelem(fscmds)); + if (l == nil) + l = devlogfssetdefname(nil); + if(l == nil && ct->index != CMleakaudit) + error("file system not configured"); + switch(ct->index){ + case CMopen: + for (a = 1; a < i; a++) + if (cmd->f[a][0] == '-') + switch (cmd->f[a][1]) { + case 'P': + l->openflags |= LogfsOpenFlagNoPerm; + break; + case 'W': + l->openflags |= LogfsOpenFlagWstatAllow; + break; + default: + error(Ebadarg); + } + devlogfsllopen(l); + break; + case CMformat: + devlogfsllformat(l, strtol(cmd->f[1], nil, 0)); + break; + case CMsweep: + devlogfsserverlogsweep(l, 0); + break; + case CMsweepone: + devlogfsserverlogsweep(l, 1); + break; + case CMtrace: + l->logfstrace = i > 1 ? strtol(cmd->f[1], nil, 0) : 0; + if (l->server) + logfsservertrace(l->server, l->logfstrace); + if (l->lb) + logfsboottrace(l->lb, l->logfstrace); + break; + case CMunconfig: + if (l->ref.ref > 0) + error(Einuse); + devlogfsunconfig(l); + break; + case CMextent: + if(i < 2) + error(Ebadarg); + devlogfsdumpinit(l, extentdumpinit, extentdumpread, i - 1, cmd->f + 1); + break; + case CMtest: + if(i < 2) + error(Ebadarg); + errorany(logfsservertestcmd(l->server, i - 1, cmd->f + 1)); + break; + case CMleakaudit: +#ifdef LEAKHUNT + leakaudit(); +#endif + break; + case CMsync: + devlogfsserversync(l); + break; + default: + error(Ebadarg); + } + } + poperror(); + free(cmd); + return n; + } + error(Egreg); + return 0; /* not reached */ +} + +static void +devlogfsfree(Devlogfs *devlogfs) +{ + if (devlogfs != nil) { + int i; + logfsfreemem(devlogfs->device); + logfsfreemem(devlogfs->name); + for (i = 0; i < Qend - Qfs; i++) + logfsfreemem(devlogfs->filename[i]); + cclose(devlogfs->flash); + cclose(devlogfs->flashctl); + qlock(&devlogfs->qlock); + logfsserverfree(&devlogfs->server); + logfsbootfree(devlogfs->lb); + if (devlogfs->ll) + (*devlogfs->ll->free)(devlogfs->ll); + logfsfreemem(devlogfs->readbuf); + qunlock(&devlogfs->qlock); + logfsfreemem(devlogfs); + } +} + +#ifdef EMU +ulong +logfsnow(void) +{ + extern vlong timeoffset; + return (timeoffset + osusectime()) / 1000000; +} +#endif + +Dev logfsdevtab = { + 0x29f, +// L'ʟ', + "logfs", + +#ifndef EMU + devreset, +#endif + devinit, +#ifndef EMU + devshutdown, +#endif + devlogfsattach, + devlogfswalk, + devlogfsstat, + devlogfsopen, + devcreate, + devlogfsclose, + devlogfsread, + devbread, + devlogfswrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devloopback.c b/os/port/devloopback.c new file mode 100644 index 00000000..e6d6508d --- /dev/null +++ b/os/port/devloopback.c @@ -0,0 +1,743 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +typedef struct Link Link; +typedef struct Loop Loop; + +struct Link +{ + Lock; + + int ref; + + long packets; /* total number of packets sent */ + long bytes; /* total number of bytes sent */ + int indrop; /* enable dropping on iq overflow */ + long soverflows; /* packets dropped because iq overflowed */ + long droprate; /* drop 1/droprate packets in tq */ + long drops; /* packets deliberately dropped */ + + vlong delay0ns; /* nanosec of delay in the link */ + long delaynns; /* nanosec of delay per byte */ + + Block *tq; /* transmission queue */ + Block *tqtail; + vlong tout; /* time the last packet in tq is really out */ + vlong tin; /* time the head packet in tq enters the remote side */ + + long limit; /* queue buffering limit */ + Queue *oq; /* output queue from other side & packets in the link */ + Queue *iq; + + Timer ci; /* time to move packets from next packet from oq */ +}; + +struct Loop +{ + QLock; + int ref; + int minmtu; /* smallest block transmittable */ + Loop *next; + ulong path; + Link link[2]; +}; + +static struct +{ + Lock; + ulong path; +} loopbackalloc; + +enum +{ + Qtopdir= 1, /* top level directory */ + + Qloopdir, /* loopback* directory */ + + Qportdir, /* directory each end of the loop */ + Qctl, + Qstatus, + Qstats, + Qdata, + + MaxQ, + + Nloopbacks = 5, + + Statelen = 23*1024, /* status buffer size */ + + Tmsize = 8, + Delayn = 10000, /* default delays in ns */ + Delay0 = 2500000, + + Loopqlim = 32*1024, /* default size of queues */ +}; + +static Dirtab loopportdir[] = +{ + "ctl", {Qctl}, 0, 0222, + "status", {Qstatus}, 0, 0444, + "stats", {Qstats}, 0, 0444, + "data", {Qdata}, 0, 0666, +}; +static Dirtab loopdirs[MaxQ]; + +static Loop loopbacks[Nloopbacks]; + +#define TYPE(x) (((ulong)(x))&0xff) +#define ID(x) (((ulong)(x))>>8) +#define QID(x,y) ((((ulong)(x))<<8)|((ulong)(y))) + +static void looper(Loop *lb); +static long loopoput(Loop *lb, Link *link, Block *bp); +static void ptime(uchar *p, vlong t); +static vlong gtime(uchar *p); +static void closelink(Link *link, int dofree); +static void pushlink(Link *link, vlong now); +static void freelb(Loop *lb); +static void linkintr(Ureg*, Timer *ci); + +static void +loopbackinit(void) +{ + int i; + + for(i = 0; i < Nloopbacks; i++) + loopbacks[i].path = i; + + /* invert directory tables for non-directory entries */ + for(i=0; i= Nloopbacks) + error(Ebadspec); + } + + c = devattach('X', spec); + lb = &loopbacks[dev]; + + qlock(lb); + if(waserror()){ + lb->ref--; + qunlock(lb); + nexterror(); + } + + lb->ref++; + if(lb->ref == 1){ + for(chan = 0; chan < 2; chan++){ + lb->link[chan].ci.tmode = Tabsolute; + lb->link[chan].ci.ta = &lb->link[chan]; + lb->link[chan].ci.tf = linkintr; + lb->link[chan].limit = Loopqlim; + q = qopen(lb->link[chan].limit, 0, 0, 0); + lb->link[chan].iq = q; + if(q == nil){ + freelb(lb); + exhausted("memory"); + } + q = qopen(lb->link[chan].limit, 0, 0, 0); + lb->link[chan].oq = q; + if(q == nil){ + freelb(lb); + exhausted("memory"); + } + lb->link[chan].indrop = 1; + + lb->link[chan].delaynns = Delayn; + lb->link[chan].delay0ns = Delay0; + } + } + poperror(); + qunlock(lb); + + mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR); + c->aux = lb; + c->dev = dev; + return c; +} + +static int +loopbackgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp) +{ + Dirtab *tab; + int len, type; + Qid qid; + + type = TYPE(c->qid.path); + if(i == DEVDOTDOT){ + switch(type){ + case Qtopdir: + case Qloopdir: + snprint(up->genbuf, sizeof(up->genbuf), "#X%ld", c->dev); + mkqid(&qid, QID(0, Qtopdir), 0, QTDIR); + devdir(c, qid, up->genbuf, 0, eve, 0555, dp); + break; + case Qportdir: + snprint(up->genbuf, sizeof(up->genbuf), "loopback%ld", c->dev); + mkqid(&qid, QID(0, Qloopdir), 0, QTDIR); + devdir(c, qid, up->genbuf, 0, eve, 0555, dp); + break; + default: + panic("loopbackgen %llux", c->qid.path); + } + return 1; + } + + switch(type){ + case Qtopdir: + if(i != 0) + return -1; + snprint(up->genbuf, sizeof(up->genbuf), "loopback%ld", c->dev); + mkqid(&qid, QID(0, Qloopdir), 0, QTDIR); + devdir(c, qid, up->genbuf, 0, eve, 0555, dp); + return 1; + case Qloopdir: + if(i >= 2) + return -1; + snprint(up->genbuf, sizeof(up->genbuf), "%d", i); + mkqid(&qid, QID(i, QID(0, Qportdir)), 0, QTDIR); + devdir(c, qid, up->genbuf, 0, eve, 0555, dp); + return 1; + case Qportdir: + if(i >= nelem(loopportdir)) + return -1; + tab = &loopportdir[i]; + mkqid(&qid, QID(ID(c->qid.path), tab->qid.path), 0, QTFILE); + devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp); + return 1; + default: + /* non directory entries end up here; must be in lowest level */ + if(c->qid.type & QTDIR) + panic("loopbackgen: unexpected directory"); + if(i != 0) + return -1; + tab = &loopdirs[type]; + if(tab == nil) + panic("loopbackgen: unknown type: %d", type); + len = tab->length; + devdir(c, c->qid, tab->name, len, eve, tab->perm, dp); + return 1; + } +} + + +static Walkqid* +loopbackwalk(Chan *c, Chan *nc, char **name, int nname) +{ + Walkqid *wq; + Loop *lb; + + wq = devwalk(c, nc, name, nname, nil, 0, loopbackgen); + if(wq != nil && wq->clone != nil && wq->clone != c){ + lb = c->aux; + qlock(lb); + lb->ref++; + if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata) + lb->link[ID(c->qid.path)].ref++; + qunlock(lb); + } + return wq; +} + +static int +loopbackstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, nil, 0, loopbackgen); +} + +/* + * if the stream doesn't exist, create it + */ +static Chan* +loopbackopen(Chan *c, int omode) +{ + Loop *lb; + + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Ebadarg); + c->mode = omode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + + lb = c->aux; + qlock(lb); + if(TYPE(c->qid.path) == Qdata){ + if(lb->link[ID(c->qid.path)].ref){ + qunlock(lb); + error(Einuse); + } + lb->link[ID(c->qid.path)].ref++; + } + qunlock(lb); + + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + c->iounit = qiomaxatomic; + return c; +} + +static void +loopbackclose(Chan *c) +{ + Loop *lb; + int ref, chan; + + lb = c->aux; + + qlock(lb); + + /* + * closing either side hangs up the stream + */ + if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata){ + chan = ID(c->qid.path); + if(--lb->link[chan].ref == 0){ + qhangup(lb->link[chan ^ 1].oq, nil); + looper(lb); + } + } + + + /* + * if both sides are closed, they are reusable + */ + if(lb->link[0].ref == 0 && lb->link[1].ref == 0){ + for(chan = 0; chan < 2; chan++){ + closelink(&lb->link[chan], 0); + qreopen(lb->link[chan].iq); + qreopen(lb->link[chan].oq); + qsetlimit(lb->link[chan].oq, lb->link[chan].limit); + qsetlimit(lb->link[chan].iq, lb->link[chan].limit); + } + } + ref = --lb->ref; + if(ref == 0) + freelb(lb); + qunlock(lb); +} + +static void +freelb(Loop *lb) +{ + int chan; + + for(chan = 0; chan < 2; chan++) + closelink(&lb->link[chan], 1); +} + +/* + * called with the Loop qlocked, + * so only pushlink can mess with the queues + */ +static void +closelink(Link *link, int dofree) +{ + Queue *iq, *oq; + Block *bp; + + ilock(link); + iq = link->iq; + oq = link->oq; + bp = link->tq; + link->tq = nil; + link->tqtail = nil; + link->tout = 0; + link->tin = 0; + timerdel(&link->ci); + iunlock(link); + if(iq != nil){ + qclose(iq); + if(dofree){ + ilock(link); + free(iq); + link->iq = nil; + iunlock(link); + } + } + if(oq != nil){ + qclose(oq); + if(dofree){ + ilock(link); + free(oq); + link->oq = nil; + iunlock(link); + } + } + freeblist(bp); +} + +static long +loopbackread(Chan *c, void *va, long n, vlong offset) +{ + Loop *lb; + Link *link; + char *buf; + long rv; + + lb = c->aux; + switch(TYPE(c->qid.path)){ + default: + error(Eperm); + return -1; /* not reached */ + case Qtopdir: + case Qloopdir: + case Qportdir: + return devdirread(c, va, n, nil, 0, loopbackgen); + case Qdata: + return qread(lb->link[ID(c->qid.path)].iq, va, n); + case Qstatus: + link = &lb->link[ID(c->qid.path)]; + buf = smalloc(Statelen); + rv = snprint(buf, Statelen, "delay %lld %ld\n", link->delay0ns, link->delaynns); + rv += snprint(buf+rv, Statelen-rv, "limit %ld\n", link->limit); + rv += snprint(buf+rv, Statelen-rv, "indrop %d\n", link->indrop); + snprint(buf+rv, Statelen-rv, "droprate %ld\n", link->droprate); + rv = readstr(offset, va, n, buf); + free(buf); + break; + case Qstats: + link = &lb->link[ID(c->qid.path)]; + buf = smalloc(Statelen); + rv = snprint(buf, Statelen, "packets: %ld\n", link->packets); + rv += snprint(buf+rv, Statelen-rv, "bytes: %ld\n", link->bytes); + rv += snprint(buf+rv, Statelen-rv, "dropped: %ld\n", link->drops); + snprint(buf+rv, Statelen-rv, "soft overflows: %ld\n", link->soverflows); + rv = readstr(offset, va, n, buf); + free(buf); + break; + } + return rv; +} + +static Block* +loopbackbread(Chan *c, long n, ulong offset) +{ + Loop *lb; + + lb = c->aux; + if(TYPE(c->qid.path) == Qdata) + return qbread(lb->link[ID(c->qid.path)].iq, n); + + return devbread(c, n, offset); +} + +static long +loopbackbwrite(Chan *c, Block *bp, ulong off) +{ + Loop *lb; + + lb = c->aux; + if(TYPE(c->qid.path) == Qdata) + return loopoput(lb, &lb->link[ID(c->qid.path) ^ 1], bp); + return devbwrite(c, bp, off); +} + +static long +loopbackwrite(Chan *c, void *va, long n, vlong off) +{ + Loop *lb; + Link *link; + Cmdbuf *volatile cb; + Block *volatile bp; + vlong d0ns; + long dnns; + + switch(TYPE(c->qid.path)){ + case Qdata: + bp = allocb(n); + if(waserror()){ + freeb(bp); + nexterror(); + } + memmove(bp->wp, va, n); + poperror(); + bp->wp += n; + return loopbackbwrite(c, bp, off); + case Qctl: + lb = c->aux; + link = &lb->link[ID(c->qid.path)]; + cb = parsecmd(va, n); + if(waserror()){ + free(cb); + nexterror(); + } + if(cb->nf < 1) + error("short control request"); + if(strcmp(cb->f[0], "delay") == 0){ + if(cb->nf != 3) + error("usage: delay latency bytedelay"); + d0ns = strtoll(cb->f[1], nil, 10); + dnns = strtol(cb->f[2], nil, 10); + + /* + * it takes about 20000 cycles on a pentium ii + * to run pushlink; perhaps this should be accounted. + */ + + ilock(link); + link->delay0ns = d0ns; + link->delaynns = dnns; + iunlock(link); + }else if(strcmp(cb->f[0], "indrop") == 0){ + if(cb->nf != 2) + error("usage: indrop [01]"); + ilock(link); + link->indrop = strtol(cb->f[1], nil, 0) != 0; + iunlock(link); + }else if(strcmp(cb->f[0], "droprate") == 0){ + if(cb->nf != 2) + error("usage: droprate ofn"); + ilock(link); + link->droprate = strtol(cb->f[1], nil, 0); + iunlock(link); + }else if(strcmp(cb->f[0], "limit") == 0){ + if(cb->nf != 2) + error("usage: limit maxqsize"); + ilock(link); + link->limit = strtol(cb->f[1], nil, 0); + qsetlimit(link->oq, link->limit); + qsetlimit(link->iq, link->limit); + iunlock(link); + }else if(strcmp(cb->f[0], "reset") == 0){ + if(cb->nf != 1) + error("usage: reset"); + ilock(link); + link->packets = 0; + link->bytes = 0; + link->indrop = 0; + link->soverflows = 0; + link->drops = 0; + iunlock(link); + }else + error("unknown control request"); + poperror(); + free(cb); + break; + default: + error(Eperm); + } + + return n; +} + +static long +loopoput(Loop *lb, Link *link, Block *volatile bp) +{ + long n; + + n = BLEN(bp); + + /* make it a single block with space for the loopback timing header */ + if(waserror()){ + freeb(bp); + nexterror(); + } + bp = padblock(bp, Tmsize); + if(bp->next) + bp = concatblock(bp); + if(BLEN(bp) < lb->minmtu) + bp = adjustblock(bp, lb->minmtu); + poperror(); + ptime(bp->rp, todget(nil)); + + link->packets++; + link->bytes += n; + + qbwrite(link->oq, bp); + + looper(lb); + return n; +} + +static void +looper(Loop *lb) +{ + vlong t; + int chan; + + t = todget(nil); + for(chan = 0; chan < 2; chan++) + pushlink(&lb->link[chan], t); +} + +static void +linkintr(Ureg*, Timer *ci) +{ + Link *link; + + link = ci->ta; + pushlink(link, ci->tns); +} + +/* + * move blocks between queues if they are ready. + * schedule an interrupt for the next interesting time. + * + * must be called with the link ilocked. + */ +static void +pushlink(Link *link, vlong now) +{ + Block *bp; + vlong tout, tin; + + /* + * put another block in the link queue + */ + ilock(link); + if(link->iq == nil || link->oq == nil){ + iunlock(link); + return; + + } + timerdel(&link->ci); + + /* + * put more blocks into the xmit queue + * use the time the last packet was supposed to go out + * as the start time for the next packet, rather than + * the current time. this more closely models a network + * device which can queue multiple output packets. + */ + tout = link->tout; + if(!tout) + tout = now; + while(tout <= now){ + bp = qget(link->oq); + if(bp == nil){ + tout = 0; + break; + } + + /* + * can't send the packet before it gets queued + */ + tin = gtime(bp->rp); + if(tin > tout) + tout = tin; + tout = tout + (BLEN(bp) - Tmsize) * link->delaynns; + + /* + * drop packets + */ + if(link->droprate && nrand(link->droprate) == 0) + link->drops++; + else{ + ptime(bp->rp, tout + link->delay0ns); + if(link->tq == nil) + link->tq = bp; + else + link->tqtail->next = bp; + link->tqtail = bp; + } + } + + /* + * record the next time a packet can be sent, + * but don't schedule an interrupt if none is waiting + */ + link->tout = tout; + if(!qcanread(link->oq)) + tout = 0; + + /* + * put more blocks into the receive queue + */ + tin = 0; + while(bp = link->tq){ + tin = gtime(bp->rp); + if(tin > now) + break; + bp->rp += Tmsize; + link->tq = bp->next; + bp->next = nil; + if(!link->indrop) + qpassnolim(link->iq, bp); + else if(qpass(link->iq, bp) < 0) + link->soverflows++; + tin = 0; + } + if(bp == nil && qisclosed(link->oq) && !qcanread(link->oq) && !qisclosed(link->iq)) + qhangup(link->iq, nil); + link->tin = tin; + if(!tin || tin > tout && tout) + tin = tout; + + link->ci.tns = tin; + if(tin){ + if(tin < now) + panic("loopback unfinished business"); + timeradd(&link->ci); + } + iunlock(link); +} + +static void +ptime(uchar *p, vlong t) +{ + ulong tt; + + tt = t >> 32; + p[0] = tt >> 24; + p[1] = tt >> 16; + p[2] = tt >> 8; + p[3] = tt; + tt = t; + p[4] = tt >> 24; + p[5] = tt >> 16; + p[6] = tt >> 8; + p[7] = tt; +} + +static vlong +gtime(uchar *p) +{ + ulong t1, t2; + + t1 = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; + t2 = (p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]; + return ((vlong)t1 << 32) | t2; +} + +Dev loopbackdevtab = { + 'X', + "loopback", + + devreset, + loopbackinit, + devshutdown, + loopbackattach, + loopbackwalk, + loopbackstat, + loopbackopen, + devcreate, + loopbackclose, + loopbackread, + loopbackbread, + loopbackwrite, + loopbackbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devmnt.c b/os/port/devmnt.c new file mode 100644 index 00000000..6fd3ec6b --- /dev/null +++ b/os/port/devmnt.c @@ -0,0 +1,1204 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +/* + * References are managed as follows: + * The channel to the server - a network connection or pipe - has one + * reference for every Chan open on the server. The server channel has + * c->mux set to the Mnt used for muxing control to that server. Mnts + * have no reference count; they go away when c goes away. + * Each channel derived from the mount point has mchan set to c, + * and increfs/decrefs mchan to manage references on the server + * connection. + */ + +#define MAXRPC (IOHDRSZ+8192) + +struct Mntrpc +{ + Chan* c; /* Channel for whom we are working */ + Mntrpc* list; /* Free/pending list */ + Fcall request; /* Outgoing file system protocol message */ + Fcall reply; /* Incoming reply */ + Mnt* m; /* Mount device during rpc */ + Rendez r; /* Place to hang out */ + uchar* rpc; /* I/O Data buffer */ + uint rpclen; /* len of buffer */ + Block *b; /* reply blocks */ + char done; /* Rpc completed */ + uvlong stime; /* start time for mnt statistics */ + ulong reqlen; /* request length for mnt statistics */ + ulong replen; /* reply length for mnt statistics */ + Mntrpc* flushed; /* message this one flushes */ +}; + +enum +{ + TAGSHIFT = 5, /* ulong has to be 32 bits */ + TAGMASK = (1<>TAGSHIFT, +}; + +struct Mntalloc +{ + Lock; + Mnt* list; /* Mount devices in use */ + Mnt* mntfree; /* Free list */ + Mntrpc* rpcfree; + int nrpcfree; + int nrpcused; + ulong id; + ulong tagmask[NMASK]; +}mntalloc; + +void mattach(Mnt*, Chan*, char*); +Mnt* mntchk(Chan*); +void mntdirfix(uchar*, Chan*); +Mntrpc* mntflushalloc(Mntrpc*, ulong); +void mntflushfree(Mnt*, Mntrpc*); +void mntfree(Mntrpc*); +void mntgate(Mnt*); +void mntpntfree(Mnt*); +void mntqrm(Mnt*, Mntrpc*); +Mntrpc* mntralloc(Chan*, ulong); +long mntrdwr(int, Chan*, void*, long, vlong); +int mntrpcread(Mnt*, Mntrpc*); +void mountio(Mnt*, Mntrpc*); +void mountmux(Mnt*, Mntrpc*); +void mountrpc(Mnt*, Mntrpc*); +int rpcattn(void*); +Chan* mntchan(void); + +char Esbadstat[] = "invalid directory entry received from server"; +char Enoversion[] = "version not established for mount channel"; + + +void (*mntstats)(int, Chan*, uvlong, ulong); + +static void +mntreset(void) +{ + mntalloc.id = 1; + mntalloc.tagmask[0] = 1; /* don't allow 0 as a tag */ + mntalloc.tagmask[NMASK-1] = 0x80000000UL; /* don't allow NOTAG */ + fmtinstall('F', fcallfmt); +/* fmtinstall('D', dirfmt); */ +/* fmtinstall('M', dirmodefmt); */ + + cinit(); +} + +/* + * Version is not multiplexed: message sent only once per connection. + */ +long +mntversion(Chan *c, char *version, int msize, int returnlen) +{ + Fcall f; + uchar *msg; + Mnt *m; + char *v; + long k, l; + uvlong oo; + char buf[128]; + + qlock(&c->umqlock); /* make sure no one else does this until we've established ourselves */ + if(waserror()){ + qunlock(&c->umqlock); + nexterror(); + } + + /* defaults */ + if(msize == 0) + msize = MAXRPC; + if(msize > c->iounit && c->iounit != 0) + msize = c->iounit; + v = version; + if(v == nil || v[0] == '\0') + v = VERSION9P; + + /* validity */ + if(msize < 0) + error("bad iounit in version call"); + if(strncmp(v, VERSION9P, strlen(VERSION9P)) != 0) + error("bad 9P version specification"); + + m = c->mux; + + if(m != nil){ + qunlock(&c->umqlock); + poperror(); + + strecpy(buf, buf+sizeof buf, m->version); + k = strlen(buf); + if(strncmp(buf, v, k) != 0){ + snprint(buf, sizeof buf, "incompatible 9P versions %s %s", m->version, v); + error(buf); + } + if(returnlen > 0){ + if(returnlen < k) + error(Eshort); + memmove(version, buf, k); + } + return k; + } + + f.type = Tversion; + f.tag = NOTAG; + f.msize = msize; + f.version = v; + msg = malloc(8192+IOHDRSZ); + if(msg == nil) + exhausted("version memory"); + if(waserror()){ + free(msg); + nexterror(); + } + k = convS2M(&f, msg, 8192+IOHDRSZ); + if(k == 0) + error("bad fversion conversion on send"); + + lock(c); + oo = c->offset; + c->offset += k; + unlock(c); + + l = devtab[c->type]->write(c, msg, k, oo); + + if(l < k){ + lock(c); + c->offset -= k - l; + unlock(c); + error("short write in fversion"); + } + + /* message sent; receive and decode reply */ + k = devtab[c->type]->read(c, msg, 8192+IOHDRSZ, c->offset); + if(k <= 0) + error("EOF receiving fversion reply"); + + lock(c); + c->offset += k; + unlock(c); + + l = convM2S(msg, k, &f); + if(l != k) + error("bad fversion conversion on reply"); + if(f.type != Rversion){ + if(f.type == Rerror) + error(f.ename); + error("unexpected reply type in fversion"); + } + if(f.msize > msize) + error("server tries to increase msize in fversion"); + if(f.msize<256 || f.msize>1024*1024) + error("nonsense value of msize in fversion"); + if(strncmp(f.version, v, strlen(f.version)) != 0) + error("bad 9P version returned from server"); + + /* now build Mnt associated with this connection */ + lock(&mntalloc); + m = mntalloc.mntfree; + if(m != 0) + mntalloc.mntfree = m->list; + else { + m = malloc(sizeof(Mnt)); + if(m == 0) { + unlock(&mntalloc); + exhausted("mount devices"); + } + } + m->list = mntalloc.list; + mntalloc.list = m; + m->version = nil; + kstrdup(&m->version, f.version); + m->id = mntalloc.id++; + m->q = qopen(10*MAXRPC, 0, nil, nil); + m->msize = f.msize; + unlock(&mntalloc); + + poperror(); /* msg */ + free(msg); + + lock(m); + m->queue = 0; + m->rip = 0; + + c->flag |= CMSG; + c->mux = m; + m->c = c; + unlock(m); + + poperror(); /* c */ + qunlock(&c->umqlock); + + k = strlen(f.version); + if(returnlen > 0){ + if(returnlen < k) + error(Eshort); + memmove(version, f.version, k); + } + + return k; +} + +Chan* +mntauth(Chan *c, char *spec) +{ + Mnt *m; + Mntrpc *r; + + m = c->mux; + + if(m == nil){ + mntversion(c, VERSION9P, MAXRPC, 0); + m = c->mux; + if(m == nil) + error(Enoversion); + } + + c = mntchan(); + if(waserror()) { + /* Close must not be called since it will + * call mnt recursively + */ + chanfree(c); + nexterror(); + } + + r = mntralloc(0, m->msize); + + if(waserror()) { + mntfree(r); + nexterror(); + } + + r->request.type = Tauth; + r->request.afid = c->fid; + r->request.uname = up->env->user; + r->request.aname = spec; + mountrpc(m, r); + + c->qid = r->reply.aqid; + c->mchan = m->c; + incref(m->c); + c->mqid = c->qid; + c->mode = ORDWR; + + poperror(); /* r */ + mntfree(r); + + poperror(); /* c */ + + return c; + +} + +static Chan* +mntattach(char *muxattach) +{ + Mnt *m; + Chan *c; + Mntrpc *r; + struct bogus{ + Chan *chan; + Chan *authchan; + char *spec; + int flags; + }bogus; + + bogus = *((struct bogus *)muxattach); + c = bogus.chan; + + m = c->mux; + + if(m == nil){ + mntversion(c, nil, 0, 0); + m = c->mux; + if(m == nil) + error(Enoversion); + } + + c = mntchan(); + if(waserror()) { + /* Close must not be called since it will + * call mnt recursively + */ + chanfree(c); + nexterror(); + } + + r = mntralloc(0, m->msize); + + if(waserror()) { + mntfree(r); + nexterror(); + } + + r->request.type = Tattach; + r->request.fid = c->fid; + if(bogus.authchan == nil) + r->request.afid = NOFID; + else + r->request.afid = bogus.authchan->fid; + r->request.uname = up->env->user; + r->request.aname = bogus.spec; + mountrpc(m, r); + + c->qid = r->reply.qid; + c->mchan = m->c; + incref(m->c); + c->mqid = c->qid; + + poperror(); /* r */ + mntfree(r); + + poperror(); /* c */ + + if(bogus.flags&MCACHE) + c->flag |= CCACHE; + return c; +} + +Chan* +mntchan(void) +{ + Chan *c; + + c = devattach('M', 0); + lock(&mntalloc); + c->dev = mntalloc.id++; + unlock(&mntalloc); + + if(c->mchan) + panic("mntchan non-zero %p", c->mchan); + return c; +} + +static Walkqid* +mntwalk(Chan *c, Chan *nc, char **name, int nname) +{ + int i, alloc; + Mnt *m; + Mntrpc *r; + Walkqid *wq; + + if(nc != nil) + print("mntwalk: nc != nil\n"); + if(nname > MAXWELEM) + error("devmnt: too many name elements"); + alloc = 0; + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + if(waserror()){ + if(alloc && wq->clone!=nil) + cclose(wq->clone); + free(wq); + return nil; + } + + alloc = 0; + m = mntchk(c); + r = mntralloc(c, m->msize); + if(nc == nil){ + nc = devclone(c); + /* + * Until the other side accepts this fid, we can't mntclose it. + * Therefore set type to 0 for now; rootclose is known to be safe. + */ + nc->type = 0; + alloc = 1; + } + wq->clone = nc; + + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = Twalk; + r->request.fid = c->fid; + r->request.newfid = nc->fid; + r->request.nwname = nname; + memmove(r->request.wname, name, nname*sizeof(char*)); + + mountrpc(m, r); + + if(r->reply.nwqid > nname) + error("too many QIDs returned by walk"); + if(r->reply.nwqid < nname){ + if(alloc) + cclose(nc); + wq->clone = nil; + if(r->reply.nwqid == 0){ + free(wq); + wq = nil; + goto Return; + } + } + + /* move new fid onto mnt device and update its qid */ + if(wq->clone != nil){ + if(wq->clone != c){ + wq->clone->type = c->type; + wq->clone->mchan = c->mchan; + incref(c->mchan); + } + if(r->reply.nwqid > 0) + wq->clone->qid = r->reply.wqid[r->reply.nwqid-1]; + } + wq->nqid = r->reply.nwqid; + for(i=0; inqid; i++) + wq->qid[i] = r->reply.wqid[i]; + + Return: + poperror(); + mntfree(r); + poperror(); + return wq; +} + +static int +mntstat(Chan *c, uchar *dp, int n) +{ + Mnt *m; + Mntrpc *r; + + if(n < BIT16SZ) + error(Eshortstat); + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = Tstat; + r->request.fid = c->fid; + mountrpc(m, r); + + if(r->reply.nstat > n){ + /* doesn't fit; just patch the count and return */ + PBIT16((uchar*)dp, r->reply.nstat); + n = BIT16SZ; + }else{ + n = r->reply.nstat; + memmove(dp, r->reply.stat, n); + validstat(dp, n); + mntdirfix(dp, c); + } + poperror(); + mntfree(r); + return n; +} + +static Chan* +mntopencreate(int type, Chan *c, char *name, int omode, ulong perm) +{ + Mnt *m; + Mntrpc *r; + + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = type; + r->request.fid = c->fid; + r->request.mode = omode; + if(type == Tcreate){ + r->request.perm = perm; + r->request.name = name; + } + mountrpc(m, r); + + c->qid = r->reply.qid; + c->offset = 0; + c->mode = openmode(omode); + c->iounit = r->reply.iounit; + if(c->iounit == 0 || c->iounit > m->msize-IOHDRSZ) + c->iounit = m->msize-IOHDRSZ; + c->flag |= COPEN; + poperror(); + mntfree(r); + + if(c->flag & CCACHE) + copen(c); + + return c; +} + +static Chan* +mntopen(Chan *c, int omode) +{ + return mntopencreate(Topen, c, nil, omode, 0); +} + +static void +mntcreate(Chan *c, char *name, int omode, ulong perm) +{ + mntopencreate(Tcreate, c, name, omode, perm); +} + +static void +mntclunk(Chan *c, int t) +{ + Mnt *m; + Mntrpc *r; + + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()){ + mntfree(r); + nexterror(); + } + + r->request.type = t; + r->request.fid = c->fid; + mountrpc(m, r); + mntfree(r); + poperror(); +} + +void +muxclose(Mnt *m) +{ + Mntrpc *q, *r; + + for(q = m->queue; q; q = r) { + r = q->list; + mntfree(q); + } + m->id = 0; + free(m->version); + m->version = nil; + mntpntfree(m); +} + +void +mntpntfree(Mnt *m) +{ + Mnt *f, **l; + Queue *q; + + lock(&mntalloc); + l = &mntalloc.list; + for(f = *l; f; f = f->list) { + if(f == m) { + *l = m->list; + break; + } + l = &f->list; + } + m->list = mntalloc.mntfree; + mntalloc.mntfree = m; + q = m->q; + unlock(&mntalloc); + + qfree(q); +} + +static void +mntclose(Chan *c) +{ + mntclunk(c, Tclunk); +} + +static void +mntremove(Chan *c) +{ + mntclunk(c, Tremove); +} + +static int +mntwstat(Chan *c, uchar *dp, int n) +{ + Mnt *m; + Mntrpc *r; + + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = Twstat; + r->request.fid = c->fid; + r->request.nstat = n; + r->request.stat = dp; + mountrpc(m, r); + poperror(); + mntfree(r); + return n; +} + +static long +mntread(Chan *c, void *buf, long n, vlong off) +{ + uchar *p, *e; + int nc, cache, isdir, dirlen; + + isdir = 0; + cache = c->flag & CCACHE; + if(c->qid.type & QTDIR) { + cache = 0; + isdir = 1; + } + + p = buf; + if(cache) { + nc = cread(c, buf, n, off); + if(nc > 0) { + n -= nc; + if(n == 0) + return nc; + p += nc; + off += nc; + } + n = mntrdwr(Tread, c, p, n, off); + cupdate(c, p, n, off); + return n + nc; + } + + n = mntrdwr(Tread, c, buf, n, off); + if(isdir) { + for(e = &p[n]; p+BIT16SZ < e; p += dirlen){ + dirlen = BIT16SZ+GBIT16(p); + if(p+dirlen > e) + break; + validstat(p, dirlen); + mntdirfix(p, c); + } + if(p != e) + error(Esbadstat); + } + return n; +} + +static long +mntwrite(Chan *c, void *buf, long n, vlong off) +{ + return mntrdwr(Twrite, c, buf, n, off); +} + +long +mntrdwr(int type, Chan *c, void *buf, long n, vlong off) +{ + Mnt *m; + Mntrpc *r; + char *uba; + int cache; + ulong cnt, nr, nreq; + + m = mntchk(c); + uba = buf; + cnt = 0; + cache = c->flag & CCACHE; + if(c->qid.type & QTDIR) + cache = 0; + for(;;) { + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = type; + r->request.fid = c->fid; + r->request.offset = off; + r->request.data = uba; + nr = n; + if(nr > m->msize-IOHDRSZ) + nr = m->msize-IOHDRSZ; + r->request.count = nr; + mountrpc(m, r); + nreq = r->request.count; + nr = r->reply.count; + if(nr > nreq) + nr = nreq; + + if(type == Tread) + r->b = bl2mem((uchar*)uba, r->b, nr); + else if(cache) + cwrite(c, (uchar*)uba, nr, off); + + poperror(); + mntfree(r); + off += nr; + uba += nr; + cnt += nr; + n -= nr; + if(nr != nreq || n == 0 || up->killed) + break; + } + return cnt; +} + +void +mountrpc(Mnt *m, Mntrpc *r) +{ + char *sn, *cn; + int t; + + r->reply.tag = 0; + r->reply.type = Tmax; /* can't ever be a valid message type */ + + mountio(m, r); + + t = r->reply.type; + switch(t) { + case Rerror: + error(r->reply.ename); + case Rflush: + error(Eintr); + default: + if(t == r->request.type+1) + break; + sn = "?"; + if(m->c->name != nil) + sn = m->c->name->s; + cn = "?"; + if(r->c != nil && r->c->name != nil) + cn = r->c->name->s; + print("mnt: proc %s %lud: mismatch from %s %s rep 0x%lux tag %d fid %d T%d R%d rp %d\n", + up->text, up->pid, sn, cn, + r, r->request.tag, r->request.fid, r->request.type, + r->reply.type, r->reply.tag); + error(Emountrpc); + } +} + +void +mountio(Mnt *m, Mntrpc *r) +{ + int n; + + while(waserror()) { + if(m->rip == up) + mntgate(m); + if(strcmp(up->env->errstr, Eintr) != 0){ + mntflushfree(m, r); + nexterror(); + } + r = mntflushalloc(r, m->msize); + } + + lock(m); + r->m = m; + r->list = m->queue; + m->queue = r; + unlock(m); + + /* Transmit a file system rpc */ + if(m->msize == 0) + panic("msize"); + n = convS2M(&r->request, r->rpc, m->msize); + if(n < 0) + panic("bad message type in mountio"); + if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n) + error(Emountrpc); +/* r->stime = fastticks(nil); */ + r->reqlen = n; + + /* Gate readers onto the mount point one at a time */ + for(;;) { + lock(m); + if(m->rip == 0) + break; + unlock(m); + sleep(&r->r, rpcattn, r); + if(r->done){ + poperror(); + mntflushfree(m, r); + return; + } + } + m->rip = up; + unlock(m); + while(r->done == 0) { + if(mntrpcread(m, r) < 0) + error(Emountrpc); + mountmux(m, r); + } + mntgate(m); + poperror(); + mntflushfree(m, r); +} + +static int +doread(Mnt *m, int len) +{ + Block *b; + + while(qlen(m->q) < len){ + b = devtab[m->c->type]->bread(m->c, m->msize, 0); + if(b == nil) + return -1; + if(blocklen(b) == 0){ + freeblist(b); + return -1; + } + qaddlist(m->q, b); + } + return 0; +} + +int +mntrpcread(Mnt *m, Mntrpc *r) +{ + int i, t, len, hlen; + Block *b, **l, *nb; + + r->reply.type = 0; + r->reply.tag = 0; + + /* read at least length, type, and tag and pullup to a single block */ + if(doread(m, BIT32SZ+BIT8SZ+BIT16SZ) < 0) + return -1; + nb = pullupqueue(m->q, BIT32SZ+BIT8SZ+BIT16SZ); + + /* read in the rest of the message, avoid ridiculous (for now) message sizes */ + len = GBIT32(nb->rp); + if(len > m->msize){ + qdiscard(m->q, qlen(m->q)); + return -1; + } + if(doread(m, len) < 0) + return -1; + + /* pullup the header (i.e. everything except data) */ + t = nb->rp[BIT32SZ]; + switch(t){ + case Rread: + hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ; + break; + default: + hlen = len; + break; + } + nb = pullupqueue(m->q, hlen); + + if(convM2S(nb->rp, len, &r->reply) <= 0){ + /* bad message, dump it */ + print("mntrpcread: convM2S failed\n"); + qdiscard(m->q, len); + return -1; + } + + /* hang the data off of the fcall struct */ + l = &r->b; + *l = nil; + do { + b = qremove(m->q); + if(hlen > 0){ + b->rp += hlen; + len -= hlen; + hlen = 0; + } + i = BLEN(b); + if(i <= len){ + len -= i; + *l = b; + l = &(b->next); + } else { + /* split block and put unused bit back */ + nb = allocb(i-len); + memmove(nb->wp, b->rp+len, i-len); + b->wp = b->rp+len; + nb->wp += i-len; + qputback(m->q, nb); + *l = b; + return 0; + } + }while(len > 0); + + return 0; +} + +void +mntgate(Mnt *m) +{ + Mntrpc *q; + + lock(m); + m->rip = 0; + for(q = m->queue; q; q = q->list) { + if(q->done == 0) + if(wakeup(&q->r)) + break; + } + unlock(m); +} + +void +mountmux(Mnt *m, Mntrpc *r) +{ + Mntrpc **l, *q; + + lock(m); + l = &m->queue; + for(q = *l; q; q = q->list) { + /* look for a reply to a message */ + if(q->request.tag == r->reply.tag) { + *l = q->list; + if(q != r) { + /* + * Completed someone else. + * Trade pointers to receive buffer. + */ + q->reply = r->reply; + q->b = r->b; + r->b = nil; + } + q->done = 1; + unlock(m); + if(mntstats != nil) + (*mntstats)(q->request.type, + m->c, q->stime, + q->reqlen + r->replen); + if(q != r) + wakeup(&q->r); + return; + } + l = &q->list; + } + unlock(m); + print("unexpected reply tag %ud; type %d\n", r->reply.tag, r->reply.type); +} + +/* + * Create a new flush request and chain the previous + * requests from it + */ +Mntrpc* +mntflushalloc(Mntrpc *r, ulong iounit) +{ + Mntrpc *fr; + + fr = mntralloc(0, iounit); + + fr->request.type = Tflush; + if(r->request.type == Tflush) + fr->request.oldtag = r->request.oldtag; + else + fr->request.oldtag = r->request.tag; + fr->flushed = r; + + return fr; +} + +/* + * Free a chain of flushes. Remove each unanswered + * flush and the original message from the unanswered + * request queue. Mark the original message as done + * and if it hasn't been answered set the reply to to + * Rflush. + */ +void +mntflushfree(Mnt *m, Mntrpc *r) +{ + Mntrpc *fr; + + while(r){ + fr = r->flushed; + if(!r->done){ + r->reply.type = Rflush; + mntqrm(m, r); + } + if(fr) + mntfree(r); + r = fr; + } +} + +static int +alloctag(void) +{ + int i, j; + ulong v; + + for(i = 0; i < NMASK; i++){ + v = mntalloc.tagmask[i]; + if(v == ~0UL) + continue; + for(j = 0; j < 1<>TAGSHIFT] &= ~(1<<(t&TAGMASK)); +} + +Mntrpc* +mntralloc(Chan *c, ulong msize) +{ + Mntrpc *new; + + lock(&mntalloc); + new = mntalloc.rpcfree; + if(new == nil){ + new = malloc(sizeof(Mntrpc)); + if(new == nil) { + unlock(&mntalloc); + exhausted("mount rpc header"); + } + /* + * The header is split from the data buffer as + * mountmux may swap the buffer with another header. + */ + new->rpc = mallocz(msize, 0); + if(new->rpc == nil){ + free(new); + unlock(&mntalloc); + exhausted("mount rpc buffer"); + } + new->rpclen = msize; + new->request.tag = alloctag(); + if(new->request.tag == NOTAG){ + free(new); + unlock(&mntalloc); + exhausted("rpc tags"); + } + } + else { + mntalloc.rpcfree = new->list; + mntalloc.nrpcfree--; + if(new->rpclen < msize){ + free(new->rpc); + new->rpc = mallocz(msize, 0); + if(new->rpc == nil){ + free(new); + mntalloc.nrpcused--; + unlock(&mntalloc); + exhausted("mount rpc buffer"); + } + new->rpclen = msize; + } + } + mntalloc.nrpcused++; + unlock(&mntalloc); + new->c = c; + new->done = 0; + new->flushed = nil; + new->b = nil; + return new; +} + +void +mntfree(Mntrpc *r) +{ + if(r->b != nil) + freeblist(r->b); + lock(&mntalloc); + if(mntalloc.nrpcfree >= 10){ + free(r->rpc); + free(r); + freetag(r->request.tag); + } + else{ + r->list = mntalloc.rpcfree; + mntalloc.rpcfree = r; + mntalloc.nrpcfree++; + } + mntalloc.nrpcused--; + unlock(&mntalloc); +} + +void +mntqrm(Mnt *m, Mntrpc *r) +{ + Mntrpc **l, *f; + + lock(m); + r->done = 1; + + l = &m->queue; + for(f = *l; f; f = f->list) { + if(f == r) { + *l = r->list; + break; + } + l = &f->list; + } + unlock(m); +} + +Mnt* +mntchk(Chan *c) +{ + Mnt *m; + + /* This routine is mostly vestiges of prior lives; now it's just sanity checking */ + + if(c->mchan == nil) + panic("mntchk 1: nil mchan c %s\n", channame(c)); + + m = c->mchan->mux; + + if(m == nil) + print("mntchk 2: nil mux c %s c->mchan %s \n", channame(c), channame(c->mchan)); + + /* + * Was it closed and reused (was error(Eshutdown); now, it can't happen) + */ + if(m->id == 0 || m->id >= c->dev) + panic("mntchk 3: can't happen"); + + return m; +} + +/* + * Rewrite channel type and dev for in-flight data to + * reflect local values. These entries are known to be + * the first two in the Dir encoding after the count. + */ +void +mntdirfix(uchar *dirbuf, Chan *c) +{ + uint r; + + r = devtab[c->type]->dc; + dirbuf += BIT16SZ; /* skip count */ + PBIT16(dirbuf, r); + dirbuf += BIT16SZ; + PBIT32(dirbuf, c->dev); +} + +int +rpcattn(void *v) +{ + Mntrpc *r; + + r = v; + return r->done || r->m->rip == 0; +} + +Dev mntdevtab = { + 'M', + "mnt", + + mntreset, + devinit, + devshutdown, + mntattach, + mntwalk, + mntstat, + mntopen, + mntcreate, + mntclose, + mntread, + devbread, + mntwrite, + devbwrite, + mntremove, + mntwstat, +}; diff --git a/os/port/devns16552.c b/os/port/devns16552.c new file mode 100644 index 00000000..b260eec8 --- /dev/null +++ b/os/port/devns16552.c @@ -0,0 +1,1090 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "../port/netif.h" + +/* + * Driver for the ns16552. + */ +enum +{ + /* + * register numbers + */ + Data= 0, /* xmit/rcv buffer */ + Iena= 1, /* interrupt enable */ + Ircv= (1<<0), /* for char rcv'd */ + Ixmt= (1<<1), /* for xmit buffer empty */ + Irstat=(1<<2), /* for change in rcv'er status */ + Imstat=(1<<3), /* for change in modem status */ + Istat= 2, /* interrupt flag (read) */ + Ipend= 1, /* interrupt pending (not) */ + Fenabd=(3<<6), /* on if fifo's enabled */ + Fifoctl=2, /* fifo control (write) */ + Fena= (1<<0), /* enable xmit/rcv fifos */ + Ftrig= (1<<6), /* trigger after 4 input characters */ + Fclear=(3<<1), /* clear xmit & rcv fifos */ + Format= 3, /* byte format */ + Bits8= (3<<0), /* 8 bits/byte */ + Stop2= (1<<2), /* 2 stop bits */ + Pena= (1<<3), /* generate parity */ + Peven= (1<<4), /* even parity */ + Pforce=(1<<5), /* force parity */ + Break= (1<<6), /* generate a break */ + Dra= (1<<7), /* address the divisor */ + Mctl= 4, /* modem control */ + Dtr= (1<<0), /* data terminal ready */ + Rts= (1<<1), /* request to send */ + Ri= (1<<2), /* ring */ + Inton= (1<<3), /* turn on interrupts */ + Loop= (1<<4), /* loop back */ + Lstat= 5, /* line status */ + Inready=(1<<0), /* receive buffer full */ + Oerror=(1<<1), /* receiver overrun */ + Perror=(1<<2), /* receiver parity error */ + Ferror=(1<<3), /* rcv framing error */ + Berror=(1<<4), /* break alarm */ + Outready=(1<<5), /* output buffer full */ + Mstat= 6, /* modem status */ + Ctsc= (1<<0), /* clear to send changed */ + Dsrc= (1<<1), /* data set ready changed */ + Rire= (1<<2), /* rising edge of ring indicator */ + Dcdc= (1<<3), /* data carrier detect changed */ + Cts= (1<<4), /* complement of clear to send line */ + Dsr= (1<<5), /* complement of data set ready line */ + Ring= (1<<6), /* complement of ring indicator line */ + Dcd= (1<<7), /* complement of data carrier detect line */ + Scratch=7, /* scratchpad */ + Dlsb= 0, /* divisor lsb */ + Dmsb= 1, /* divisor msb */ + + CTLS= 023, + CTLQ= 021, + + Stagesize= 1024, + Nuart= 32, /* max per machine */ +}; + +typedef struct Uart Uart; +struct Uart +{ + QLock; + int opens; + + int enabled; + Uart *elist; /* next enabled interface */ + char name[KNAMELEN]; + + uchar sticky[8]; /* sticky write register values */ + ulong port; + ulong freq; /* clock frequency */ + uchar mask; /* bits/char */ + int dev; + int baud; /* baud rate */ + + uchar istat; /* last istat read */ + int frame; /* framing errors */ + int overrun; /* rcvr overruns */ + + /* buffers */ + int (*putc)(Queue*, int); + Queue *iq; + Queue *oq; + + Lock flock; /* fifo */ + uchar fifoon; /* fifo's enabled */ + uchar nofifo; /* earlier chip version with nofifo */ + + Lock rlock; /* receive */ + uchar istage[Stagesize]; + uchar *ip; + uchar *ie; + + int haveinput; + + Lock tlock; /* transmit */ + uchar ostage[Stagesize]; + uchar *op; + uchar *oe; + + int modem; /* hardware flow control on */ + int xonoff; /* software flow control on */ + int blocked; + int cts, dsr, dcd; /* keep track of modem status */ + int ctsbackoff; + int hup_dsr, hup_dcd; /* send hangup upstream? */ + int dohup; + + Rendez r; +}; + +static Uart *uart[Nuart]; +static int nuart; +static Uart *consuart; + +struct Uartalloc { + Lock; + Uart *elist; /* list of enabled interfaces */ +} uartalloc; + +void ns16552intr(int); + +/* + * pick up architecture specific routines and definitions + */ +#include "ns16552.h" + +/* + * set the baud rate by calculating and setting the baudrate + * generator constant. This will work with fairly non-standard + * baud rates. + */ +static void +ns16552setbaud(Uart *p, int rate) +{ + ulong brconst; + + if(rate <= 0) + return; + + brconst = (p->freq+8*rate-1)/(16*rate); + + uartwrreg(p, Format, Dra); + outb(p->port + Dmsb, (brconst>>8) & 0xff); + outb(p->port + Dlsb, brconst & 0xff); + uartwrreg(p, Format, 0); + + p->baud = rate; +} + +/* + * decide if we should hangup when dsr or dcd drops. + */ +static void +ns16552dsrhup(Uart *p, int n) +{ + p->hup_dsr = n; +} + +static void +ns16552dcdhup(Uart *p, int n) +{ + p->hup_dcd = n; +} + +static void +ns16552parity(Uart *p, char type) +{ + switch(type){ + case 'e': + p->sticky[Format] |= Pena|Peven; + break; + case 'o': + p->sticky[Format] &= ~Peven; + p->sticky[Format] |= Pena; + break; + default: + p->sticky[Format] &= ~(Pena|Peven); + break; + } + uartwrreg(p, Format, 0); +} + +/* + * set bits/character, default 8 + */ +void +ns16552bits(Uart *p, int bits) +{ + if(bits < 5 || bits > 8) + error(Ebadarg); + + p->sticky[Format] &= ~3; + p->sticky[Format] |= bits-5; + + uartwrreg(p, Format, 0); +} + + +/* + * toggle DTR + */ +void +ns16552dtr(Uart *p, int n) +{ + if(n) + p->sticky[Mctl] |= Dtr; + else + p->sticky[Mctl] &= ~Dtr; + + uartwrreg(p, Mctl, 0); +} + +/* + * toggle RTS + */ +void +ns16552rts(Uart *p, int n) +{ + if(n) + p->sticky[Mctl] |= Rts; + else + p->sticky[Mctl] &= ~Rts; + + uartwrreg(p, Mctl, 0); +} + +/* + * send break + */ +static void +ns16552break(Uart *p, int ms) +{ + if(ms == 0) + ms = 200; + + uartwrreg(p, Format, Break); + tsleep(&up->sleep, return0, 0, ms); + uartwrreg(p, Format, 0); +} + +static void +ns16552fifoon(Uart *p) +{ + ulong i, x; + + if(p->nofifo || uartrdreg(p, Istat) & Fenabd) + return; + + x = splhi(); + + /* reset fifos */ + p->sticky[Fifoctl] = 0; + uartwrreg(p, Fifoctl, Fclear); + + /* empty buffer and interrupt conditions */ + for(i = 0; i < 16; i++){ + if(uartrdreg(p, Istat)){ + /* nothing to do */ + } + if(uartrdreg(p, Data)){ + /* nothing to do */ + } + } + + /* turn on fifo */ + p->fifoon = 1; + p->sticky[Fifoctl] = Fena|Ftrig; + uartwrreg(p, Fifoctl, 0); + p->istat = uartrdreg(p, Istat); + if((p->istat & Fenabd) == 0) { + /* didn't work, must be an earlier chip type */ + p->nofifo = 1; + } + + splx(x); +} + +/* + * modem flow control on/off (rts/cts) + */ +static void +ns16552mflow(Uart *p, int n) +{ + ilock(&p->tlock); + if(n){ + p->sticky[Iena] |= Imstat; + uartwrreg(p, Iena, 0); + p->modem = 1; + p->cts = uartrdreg(p, Mstat) & Cts; + } else { + p->sticky[Iena] &= ~Imstat; + uartwrreg(p, Iena, 0); + p->modem = 0; + p->cts = 1; + } + iunlock(&p->tlock); + +// ilock(&p->flock); +// if(1) +// /* turn on fifo's */ +// ns16552fifoon(p); +// else { +// /* turn off fifo's */ +// p->fifoon = 0; +// p->sticky[Fifoctl] = 0; +// uartwrreg(p, Fifoctl, Fclear); +// } +// iunlock(&p->flock); +} + +/* + * turn on a port's interrupts. set DTR and RTS + */ +static void +ns16552enable(Uart *p) +{ + Uart **l; + + if(p->enabled) + return; + + uartpower(p->dev, 1); + + p->hup_dsr = p->hup_dcd = 0; + p->cts = p->dsr = p->dcd = 0; + + /* + * turn on interrupts + */ + p->sticky[Iena] = Ircv | Ixmt | Irstat; + uartwrreg(p, Iena, 0); + + /* + * turn on DTR and RTS + */ + ns16552dtr(p, 1); + ns16552rts(p, 1); + + ns16552fifoon(p); + + /* + * assume we can send + */ + ilock(&p->tlock); + p->cts = 1; + p->blocked = 0; + iunlock(&p->tlock); + + /* + * set baud rate to the last used + */ + ns16552setbaud(p, p->baud); + + lock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p) + break; + } + if(*l == 0){ + p->elist = uartalloc.elist; + uartalloc.elist = p; + } + p->enabled = 1; + unlock(&uartalloc); +} + +/* + * turn off a port's interrupts. reset DTR and RTS + */ +static void +ns16552disable(Uart *p) +{ + Uart **l; + + /* + * turn off interrupts + */ + p->sticky[Iena] = 0; + uartwrreg(p, Iena, 0); + + /* + * revert to default settings + */ + p->sticky[Format] = Bits8; + uartwrreg(p, Format, 0); + + /* + * turn off DTR, RTS, hardware flow control & fifo's + */ + ns16552dtr(p, 0); + ns16552rts(p, 0); + ns16552mflow(p, 0); + ilock(&p->tlock); + p->xonoff = p->blocked = 0; + iunlock(&p->tlock); + + uartpower(p->dev, 0); + + lock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p){ + *l = p->elist; + break; + } + } + p->enabled = 0; + unlock(&uartalloc); +} + +/* + * put some bytes into the local queue to avoid calling + * qconsume for every character + */ +static int +stageoutput(Uart *p) +{ + int n; + + n = qconsume(p->oq, p->ostage, Stagesize); + if(n <= 0) + return 0; + p->op = p->ostage; + p->oe = p->ostage + n; + return n; +} + +/* + * (re)start output + */ +static void +ns16552kick0(Uart *p) +{ + int i; + if((p->modem && (p->cts == 0)) || p->blocked) + return; + + /* + * 128 here is an arbitrary limit to make sure + * we don't stay in this loop too long. If the + * chips output queue is longer than 128, too + * bad -- presotto + */ + for(i = 0; i < 128; i++){ + if(!(uartrdreg(p, Lstat) & Outready)) + break; + if(p->op >= p->oe && stageoutput(p) == 0) + break; + outb(p->port + Data, *(p->op++)); + } +} + +static void +ns16552kick(void *v) +{ + Uart *p; + + p = v; + ilock(&p->tlock); + ns16552kick0(p); + iunlock(&p->tlock); +} + +/* + * restart input if it's off + */ +static void +ns16552flow(void *v) +{ + Uart *p; + + p = v; + if(p->modem) + ns16552rts(p, 1); + ilock(&p->rlock); + p->haveinput = 1; + iunlock(&p->rlock); +} + +/* + * default is 9600 baud, 1 stop bit, 8 bit chars, no interrupts, + * transmit and receive enabled, interrupts disabled. + */ +static void +ns16552setup0(Uart *p) +{ + memset(p->sticky, 0, sizeof(p->sticky)); + /* + * set rate to 9600 baud. + * 8 bits/character. + * 1 stop bit. + * interrupts enabled. + */ + p->sticky[Format] = Bits8; + uartwrreg(p, Format, 0); + p->sticky[Mctl] |= Inton; + uartwrreg(p, Mctl, 0x0); + + ns16552setbaud(p, 9600); + + p->iq = qopen(4*1024, 0, ns16552flow, p); + p->oq = qopen(4*1024, 0, ns16552kick, p); + if(p->iq == nil || p->oq == nil) + panic("ns16552setup0"); + + p->ip = p->istage; + p->ie = &p->istage[Stagesize]; + p->op = p->ostage; + p->oe = p->ostage; +} + +/* + * called by main() to create a new duart + */ +void +ns16552setup(ulong port, ulong freq, char *name) +{ + Uart *p; + + if(nuart >= Nuart) + return; + + p = xalloc(sizeof(Uart)); + uart[nuart] = p; + strcpy(p->name, name); + p->dev = nuart; + nuart++; + p->port = port; + p->freq = freq; + ns16552setup0(p); +} + +/* + * called by main() to configure a duart port as a console or a mouse + */ +void +ns16552special(int port, int baud, Queue **in, Queue **out, int (*putc)(Queue*, int)) +{ + Uart *p = uart[port]; + ns16552enable(p); + if(baud) + ns16552setbaud(p, baud); + p->putc = putc; + if(in) + *in = p->iq; + if(out) + *out = p->oq; + p->opens++; +} + +/* + * handle an interrupt to a single uart + */ +void +ns16552intr(int dev) +{ + uchar ch; + int s, l; + Uart *p = uart[dev]; + + for (s = uartrdreg(p, Istat); !(s&Ipend); s = uartrdreg(p, Istat)) { + switch(s&0x3f){ + case 4: /* received data available */ + case 6: /* receiver line status (alarm or error) */ + case 12: /* character timeout indication */ + while ((l = uartrdreg(p, Lstat)) & Inready) { + if(l & Ferror) + p->frame++; + if(l & Oerror) + p->overrun++; + ch = uartrdreg(p, Data) & 0xff; + if (l & (Berror|Perror|Ferror)) { + /* ch came with break, parity or framing error - consume */ + continue; + } + if (ch == CTLS || ch == CTLQ) { + ilock(&p->tlock); + if(p->xonoff){ + if(ch == CTLS) + p->blocked = 1; + else + p->blocked = 0; /* clock gets output going again */ + } + iunlock(&p->tlock); + } + if(p->putc) + p->putc(p->iq, ch); + else { + ilock(&p->rlock); + if(p->ip < p->ie) + *p->ip++ = ch; + else + p->overrun++; + p->haveinput = 1; + iunlock(&p->rlock); + } + } + break; + + case 2: /* transmitter not full */ + ns16552kick(p); + break; + + case 0: /* modem status */ + ch = uartrdreg(p, Mstat); + if(ch & Ctsc){ + ilock(&p->tlock); + l = p->cts; + p->cts = ch & Cts; + if(l == 0 && p->cts) + p->ctsbackoff = 2; /* clock gets output going again */ + iunlock(&p->tlock); + } + if (ch & Dsrc) { + l = ch & Dsr; + if(p->hup_dsr && p->dsr && !l){ + ilock(&p->rlock); + p->dohup = 1; + iunlock(&p->rlock); + } + p->dsr = l; + } + if (ch & Dcdc) { + l = ch & Dcd; + if(p->hup_dcd && p->dcd && !l){ + ilock(&p->rlock); + p->dohup = 1; + iunlock(&p->rlock); + } + p->dcd = l; + } + break; + + default: + print("weird uart interrupt #%2.2ux\n", s); + break; + } + } + p->istat = s; +} + +/* + * we save up input characters till clock time + * + * There's also a bit of code to get a stalled print going. + * It shouldn't happen, but it does. Obviously I don't + * understand something. Since it was there, I bundled a + * restart after flow control with it to give some hysteresis + * to the hardware flow control. This makes compressing + * modems happier but will probably bother something else. + * -- presotto + */ +void +uartclock(void) +{ + int n; + Uart *p; + + for(p = uartalloc.elist; p; p = p->elist){ + + /* this amortizes cost of qproduce to many chars */ + if(p->haveinput){ + ilock(&p->rlock); + if(p->haveinput){ + n = p->ip - p->istage; + if(n > 0 && p->iq){ + if(n > Stagesize) + panic("uartclock"); + if(qproduce(p->iq, p->istage, n) < 0) + ns16552rts(p, 0); + else + p->ip = p->istage; + } + p->haveinput = 0; + } + iunlock(&p->rlock); + } + if(p->dohup){ + ilock(&p->rlock); + if(p->dohup){ + qhangup(p->iq, 0); + qhangup(p->oq, 0); + } + p->dohup = 0; + iunlock(&p->rlock); + } + + /* this adds hysteresis to hardware flow control */ + if(p->ctsbackoff){ + ilock(&p->tlock); + if(p->ctsbackoff){ + if(--(p->ctsbackoff) == 0) + ns16552kick0(p); + } + iunlock(&p->tlock); + } + } +} + +Dirtab *ns16552dir; +int ndir; + +static void +setlength(int i) +{ + Uart *p; + + if(i > 0){ + p = uart[i]; + if(p && p->opens && p->iq) + ns16552dir[1+3*i].length = qlen(p->iq); + } else for(i = 0; i < nuart; i++){ + p = uart[i]; + if(p && p->opens && p->iq) + ns16552dir[1+3*i].length = qlen(p->iq); + } +} + +/* + * all uarts must be ns16552setup() by this point or inside of ns16552install() + */ +static void +ns16552reset(void) +{ + int i; + Dirtab *dp; + ns16552install(); /* architecture specific */ + + ndir = 1+3*nuart; + ns16552dir = xalloc(ndir * sizeof(Dirtab)); + dp = ns16552dir; + strcpy(dp->name, "."); + mkqid(&dp->qid, 0, 0, QTDIR); + dp->length = 0; + dp->perm = DMDIR|0555; + dp++; + for(i = 0; i < nuart; i++){ + /* 3 directory entries per port */ + strcpy(dp->name, uart[i]->name); + dp->qid.path = NETQID(i, Ndataqid); + dp->perm = 0666; + dp++; + sprint(dp->name, "%sctl", uart[i]->name); + dp->qid.path = NETQID(i, Nctlqid); + dp->perm = 0666; + dp++; + sprint(dp->name, "%sstatus", uart[i]->name); + dp->qid.path = NETQID(i, Nstatqid); + dp->perm = 0444; + dp++; + } +} + +static Chan* +ns16552attach(char *spec) +{ + return devattach('t', spec); +} + +static Walkqid* +ns16552walk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, ns16552dir, ndir, devgen); +} + +static int +ns16552stat(Chan *c, uchar *dp, int n) +{ + if(NETTYPE(c->qid.path) == Ndataqid) + setlength(NETID(c->qid.path)); + return devstat(c, dp, n, ns16552dir, ndir, devgen); +} + +static Chan* +ns16552open(Chan *c, int omode) +{ + Uart *p; + + c = devopen(c, omode, ns16552dir, ndir, devgen); + + switch(NETTYPE(c->qid.path)){ + case Nctlqid: + case Ndataqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(p->opens++ == 0){ + ns16552enable(p); + qreopen(p->iq); + qreopen(p->oq); + } + qunlock(p); + break; + } + + return c; +} + +static void +ns16552close(Chan *c) +{ + Uart *p; + + if(c->qid.type & QTDIR) + return; + if((c->flag & COPEN) == 0) + return; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + case Nctlqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(--(p->opens) == 0){ + ns16552disable(p); + qclose(p->iq); + qclose(p->oq); + p->ip = p->istage; + p->dcd = p->dsr = p->dohup = 0; + } + qunlock(p); + break; + } +} + +static long +uartstatus(Chan*, Uart *p, void *buf, long n, long offset) +{ + uchar mstat, fstat, istat, tstat; + char str[256]; + + str[0] = 0; + tstat = p->sticky[Mctl]; + mstat = uartrdreg(p, Mstat); + istat = p->sticky[Iena]; + fstat = p->sticky[Format]; + snprint(str, sizeof str, + "b%d c%d d%d e%d l%d m%d p%c r%d s%d\n" + "%d %d %d%s%s%s%s%s\n", + + p->baud, + p->hup_dcd, + (tstat & Dtr) != 0, + p->hup_dsr, + (fstat & Bits8) + 5, + (istat & Imstat) != 0, + (fstat & Pena) ? ((fstat & Peven) ? 'e' : 'o') : 'n', + (tstat & Rts) != 0, + (fstat & Stop2) ? 2 : 1, + + p->dev, + p->frame, + p->overrun, + uartrdreg(p, Istat) & Fenabd ? " fifo" : "", + (mstat & Cts) ? " cts" : "", + (mstat & Dsr) ? " dsr" : "", + (mstat & Dcd) ? " dcd" : "", + (mstat & Ring) ? " ring" : "" + ); + return readstr(offset, buf, n, str); +} + +static long +ns16552read(Chan *c, void *buf, long n, vlong off) +{ + Uart *p; + ulong offset = off; + + if(c->qid.type & QTDIR){ + setlength(-1); + return devdirread(c, buf, n, ns16552dir, ndir, devgen); + } + + p = uart[NETID(c->qid.path)]; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qread(p->iq, buf, n); + case Nctlqid: + return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE); + case Nstatqid: + return uartstatus(c, p, buf, n, offset); + } + + return 0; +} + +static void +ns16552ctl(Uart *p, char *cmd) +{ + int i, n; + + /* let output drain for a while */ + for(i = 0; i < 16 && qlen(p->oq); i++) + tsleep(&p->r, (int(*)(void*))qlen, p->oq, 125); + + if(strncmp(cmd, "break", 5) == 0){ + ns16552break(p, 0); + return; + } + + + n = atoi(cmd+1); + switch(*cmd){ + case 'B': + case 'b': + ns16552setbaud(p, n); + break; + case 'C': + case 'c': + ns16552dcdhup(p, n); + break; + case 'D': + case 'd': + ns16552dtr(p, n); + break; + case 'E': + case 'e': + ns16552dsrhup(p, n); + break; + case 'f': + case 'F': + qflush(p->oq); + break; + case 'H': + case 'h': + qhangup(p->iq, 0); + qhangup(p->oq, 0); + break; + case 'L': + case 'l': + ns16552bits(p, n); + break; + case 'm': + case 'M': + ns16552mflow(p, n); + break; + case 'n': + case 'N': + qnoblock(p->oq, n); + break; + case 'P': + case 'p': + ns16552parity(p, *(cmd+1)); + break; + case 'K': + case 'k': + ns16552break(p, n); + break; + case 'R': + case 'r': + ns16552rts(p, n); + break; + case 'Q': + case 'q': + qsetlimit(p->iq, n); + qsetlimit(p->oq, n); + break; + case 'W': + case 'w': + /* obsolete */ + break; + case 'X': + case 'x': + ilock(&p->tlock); + p->xonoff = n; + iunlock(&p->tlock); + break; + } +} + +static long +ns16552write(Chan *c, void *buf, long n, vlong) +{ + Uart *p; + char cmd[32]; + + if(c->qid.type & QTDIR) + error(Eperm); + + p = uart[NETID(c->qid.path)]; + + /* + * The fifo's turn themselves off sometimes. + * It must be something I don't understand. -- presotto + */ + lock(&p->flock); + if((p->istat & Fenabd) == 0 && p->fifoon && p->nofifo == 0) + ns16552fifoon(p); + unlock(&p->flock); + + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qwrite(p->oq, buf, n); + case Nctlqid: + if(n >= sizeof(cmd)) + n = sizeof(cmd)-1; + memmove(cmd, buf, n); + cmd[n] = 0; + ns16552ctl(p, cmd); + return n; + } +} + +static int +ns16552wstat(Chan *c, uchar *dp, int n) +{ + Dir d; + Dirtab *dt; + + if(!iseve()) + error(Eperm); + if(c->qid.type & QTDIR) + error(Eperm); + if(NETTYPE(c->qid.path) == Nstatqid) + error(Eperm); + + dt = &ns16552dir[1+3 * NETID(c->qid.path)]; + n = convM2D(dp, n, &d, nil); + if(n == 0) + error(Eshortstat); + if(d.mode != ~0UL){ + d.mode &= 0666; + dt[0].perm = dt[1].perm = d.mode; + } + return n; +} + +Dev ns16552devtab = { + 't', + "ns16552", + + ns16552reset, + devinit, + devshutdown, + ns16552attach, + ns16552walk, + ns16552stat, + ns16552open, + devcreate, + ns16552close, + ns16552read, + devbread, + ns16552write, + devbwrite, + devremove, + ns16552wstat, +}; + +void +uartputc(int c) +{ + Uart *p; + int i; + + p = consuart; + if(p == nil) + return; + for(i = 0; !(uartrdreg(p, Lstat)&Outready) && i < 128; i++) + delay(1); + outb(p->port+Data, c); + for(i = 0; !(uartrdreg(p, Lstat)&Outready) && i < 128; i++) + delay(1); +} + +void +uartputs(char *s, int n) +{ + char *e; + + if(consuart == nil) + return; + e = s+n; + for(; s < e; s++){ + if(*s == '\n') + uartputc('\r'); + uartputc(*s); + } +} diff --git a/os/port/devpci.c b/os/port/devpci.c new file mode 100644 index 00000000..90967e49 --- /dev/null +++ b/os/port/devpci.c @@ -0,0 +1,227 @@ +/* + * access to PCI configuration space (devpnp.c without PNP) + * + * TODO + * - extend PCI raw access to configuration space (writes, byte/short access?) + * - implement PCI access to memory/io space/BIOS ROM + * - use c->aux instead of performing lookup on each read/write? + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define DPRINT if(0) print +#define XPRINT if(1) print + +enum { + Qtopdir = 0, + + Qpcidir, + Qpcictl, + Qpciraw, +}; + +#define TYPE(q) ((ulong)(q).path & 0x0F) +#define QID(c, t) (((c)<<4)|(t)) + +static Dirtab topdir[] = { + ".", { Qtopdir, 0, QTDIR }, 0, 0555, + "pci", { Qpcidir, 0, QTDIR }, 0, 0555, +}; + +extern Dev pcidevtab; + +static int +pcigen2(Chan *c, int t, int tbdf, Dir *dp) +{ + Qid q; + + q = (Qid){BUSBDF(tbdf)|t, 0, 0}; + switch(t) { + case Qpcictl: + sprint(up->genbuf, "%d.%d.%dctl", BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf)); + devdir(c, q, up->genbuf, 0, eve, 0444, dp); + return 1; + case Qpciraw: + sprint(up->genbuf, "%d.%d.%draw", BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf)); + devdir(c, q, up->genbuf, 128, eve, 0444, dp); + return 1; + } + return -1; +} + +static int +pcigen(Chan *c, char *, Dirtab*, int, int s, Dir *dp) +{ + Qid q; + Pcidev *p; + int tbdf; + + switch(TYPE(c->qid)){ + case Qtopdir: + if(s == DEVDOTDOT){ + q = (Qid){QID(0, Qtopdir), 0, QTDIR}; + sprint(up->genbuf, "#%C", pcidevtab.dc); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + return devgen(c, nil, topdir, nelem(topdir), s, dp); + case Qpcidir: + if(s == DEVDOTDOT){ + q = (Qid){QID(0, Qtopdir), 0, QTDIR}; + sprint(up->genbuf, "#%C", pcidevtab.dc); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + p = pcimatch(nil, 0, 0); + while(s >= 2 && p != nil) { + p = pcimatch(p, 0, 0); + s -= 2; + } + if(p == nil) + return -1; + return pcigen2(c, s+Qpcictl, p->tbdf, dp); + case Qpcictl: + case Qpciraw: + tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path); + p = pcimatchtbdf(tbdf); + if(p == nil) + return -1; + return pcigen2(c, TYPE(c->qid), tbdf, dp); + default: + break; + } + return -1; +} + +static Chan* +pciattach(char *spec) +{ + return devattach(pcidevtab.dc, spec); +} + +Walkqid* +pciwalk(Chan* c, Chan *nc, char** name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, pcigen); +} + +static int +pcistat(Chan* c, uchar* dp, int n) +{ + return devstat(c, dp, n, nil, 0, pcigen); +} + +static Chan* +pciopen(Chan *c, int omode) +{ + return devopen(c, omode, nil, 0, pcigen); +} + +static void +pciclose(Chan*) +{ +} + +static long +pciread(Chan *c, void *va, long n, vlong offset) +{ + ulong x; + Pcidev *p; + char buf[256], *ebuf, *w; + char *a = va; + int i, tbdf, r; + + switch(TYPE(c->qid)){ + case Qtopdir: + case Qpcidir: + return devdirread(c, a, n, nil, 0, pcigen); + case Qpcictl: + tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path); + p = pcimatchtbdf(tbdf); + if(p == nil) + error(Egreg); + ebuf = buf+sizeof buf-1; /* -1 for newline */ + w = seprint(buf, ebuf, "%.2x.%.2x.%.2x %.4x/%.4x %3d", + p->ccrb, p->ccru, p->ccrp, p->vid, p->did, p->intl); + for(i=0; imem); i++){ + if(p->mem[i].size == 0) + continue; + w = seprint(w, ebuf, " %d:%.8lux %d", i, p->mem[i].bar, p->mem[i].size); + } + *w++ = '\n'; + *w = '\0'; + return readstr(offset, a, n, buf); + case Qpciraw: + tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path); + p = pcimatchtbdf(tbdf); + if(p == nil) + error(Egreg); + if(offset > 256) + return 0; + if(n+offset > 256) + n = 256-offset; + if(offset%4) + error(Ebadarg); + r = offset; + for(i = 0; i+4 <= n; i+=4) { + x = pcicfgr32(p, r); + a[0] = x; + a[1] = (x>>8); + a[2] = (x>>16); + a[3] = (x>>24); + a += 4; + r += 4; + } + return i; + default: + error(Egreg); + } + return n; +} + +static long +pciwrite(Chan *c, void *a, long n, vlong) +{ + char buf[256]; + + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + strncpy(buf, a, n); + buf[n] = 0; + + switch(TYPE(c->qid)){ + case Qpcictl: + case Qpciraw: + error(Eperm); + default: + error(Egreg); + } + return n; +} + + +Dev pcidevtab = { + '$', + "pci", + + devreset, + devinit, + devshutdown, + pciattach, + pciwalk, + pcistat, + pciopen, + devcreate, + pciclose, + pciread, + devbread, + pciwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devpipe.c b/os/port/devpipe.c new file mode 100644 index 00000000..2aa799aa --- /dev/null +++ b/os/port/devpipe.c @@ -0,0 +1,464 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "interp.h" + +#define NETTYPE(x) ((ulong)(x)&0x1f) +#define NETID(x) (((ulong)(x))>>5) +#define NETQID(i,t) (((i)<<5)|(t)) + +typedef struct Pipe Pipe; +struct Pipe +{ + QLock; + Pipe* next; + int ref; + ulong path; + Queue* q[2]; + int qref[2]; + Dirtab *pipedir; + char* user; +}; + +static struct +{ + Lock; + ulong path; + int pipeqsize; +} pipealloc; + +enum +{ + Qdir, + Qdata0, + Qdata1, +}; + +static +Dirtab pipedir[] = +{ + ".", {Qdir,0,QTDIR}, 0, DMDIR|0500, + "data", {Qdata0}, 0, 0660, + "data1", {Qdata1}, 0, 0660, +}; + +static void +freepipe(Pipe *p) +{ + if(p != nil){ + free(p->user); + free(p->q[0]); + free(p->q[1]); + free(p->pipedir); + free(p); + } +} + +static void +pipeinit(void) +{ + pipealloc.pipeqsize = 32*1024; +} + +/* + * create a pipe, no streams are created until an open + */ +static Chan* +pipeattach(char *spec) +{ + Pipe *p; + Chan *c; + + c = devattach('|', spec); + p = malloc(sizeof(Pipe)); + if(p == 0) + error(Enomem); + if(waserror()){ + freepipe(p); + nexterror(); + } + p->pipedir = malloc(sizeof(pipedir)); + if (p->pipedir == 0) + error(Enomem); + memmove(p->pipedir, pipedir, sizeof(pipedir)); + kstrdup(&p->user, up->env->user); + p->ref = 1; + + p->q[0] = qopen(pipealloc.pipeqsize, 0, 0, 0); + if(p->q[0] == 0) + error(Enomem); + p->q[1] = qopen(pipealloc.pipeqsize, 0, 0, 0); + if(p->q[1] == 0) + error(Enomem); + poperror(); + + lock(&pipealloc); + p->path = ++pipealloc.path; + unlock(&pipealloc); + + c->qid.path = NETQID(2*p->path, Qdir); + c->qid.vers = 0; + c->qid.type = QTDIR; + c->aux = p; + c->dev = 0; + return c; +} + +static int +pipegen(Chan *c, char *, Dirtab *tab, int ntab, int i, Dir *dp) +{ + int id, len; + Qid qid; + Pipe *p; + + if(i == DEVDOTDOT){ + devdir(c, c->qid, "#|", 0, eve, 0555, dp); + return 1; + } + i++; /* skip . */ + if(tab==0 || i>=ntab) + return -1; + tab += i; + p = c->aux; + switch(NETTYPE(tab->qid.path)){ + case Qdata0: + len = qlen(p->q[0]); + break; + case Qdata1: + len = qlen(p->q[1]); + break; + default: + len = tab->length; + break; + } + id = NETID(c->qid.path); + qid.path = NETQID(id, tab->qid.path); + qid.vers = 0; + qid.type = QTFILE; + devdir(c, qid, tab->name, len, eve, tab->perm, dp); + return 1; +} + + +static Walkqid* +pipewalk(Chan *c, Chan *nc, char **name, int nname) +{ + Walkqid *wq; + Pipe *p; + + p = c->aux; + wq = devwalk(c, nc, name, nname, p->pipedir, nelem(pipedir), pipegen); + if(wq != nil && wq->clone != nil && wq->clone != c){ + qlock(p); + p->ref++; + if(c->flag & COPEN){ + switch(NETTYPE(c->qid.path)){ + case Qdata0: + p->qref[0]++; + break; + case Qdata1: + p->qref[1]++; + break; + } + } + qunlock(p); + } + return wq; +} + +static int +pipestat(Chan *c, uchar *db, int n) +{ + Pipe *p; + Dir dir; + Dirtab *tab; + + p = c->aux; + tab = p->pipedir; + + switch(NETTYPE(c->qid.path)){ + case Qdir: + devdir(c, c->qid, ".", 0, eve, DMDIR|0555, &dir); + break; + case Qdata0: + devdir(c, c->qid, tab[1].name, qlen(p->q[0]), eve, tab[1].perm, &dir); + break; + case Qdata1: + devdir(c, c->qid, tab[2].name, qlen(p->q[1]), eve, tab[2].perm, &dir); + break; + default: + panic("pipestat"); + } + n = convD2M(&dir, db, n); + if(n < BIT16SZ) + error(Eshortstat); + return n; +} + +/* + * if the stream doesn't exist, create it + */ +static Chan* +pipeopen(Chan *c, int omode) +{ + Pipe *p; + + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Ebadarg); + c->mode = omode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + + openmode(omode); /* check it */ + + p = c->aux; + qlock(p); + if(waserror()){ + qunlock(p); + nexterror(); + } + switch(NETTYPE(c->qid.path)){ + case Qdata0: + devpermcheck(p->user, p->pipedir[1].perm, omode); + p->qref[0]++; + break; + case Qdata1: + devpermcheck(p->user, p->pipedir[2].perm, omode); + p->qref[1]++; + break; + } + poperror(); + qunlock(p); + + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + c->iounit = qiomaxatomic; + return c; +} + +static void +pipeclose(Chan *c) +{ + Pipe *p; + + p = c->aux; + qlock(p); + + if(c->flag & COPEN){ + /* + * closing either side hangs up the stream + */ + switch(NETTYPE(c->qid.path)){ + case Qdata0: + p->qref[0]--; + if(p->qref[0] == 0){ + qhangup(p->q[1], 0); + qclose(p->q[0]); + } + break; + case Qdata1: + p->qref[1]--; + if(p->qref[1] == 0){ + qhangup(p->q[0], 0); + qclose(p->q[1]); + } + break; + } + } + + + /* + * if both sides are closed, they are reusable + */ + if(p->qref[0] == 0 && p->qref[1] == 0){ + qreopen(p->q[0]); + qreopen(p->q[1]); + } + + /* + * free the structure on last close + */ + p->ref--; + if(p->ref == 0){ + qunlock(p); + freepipe(p); + } else + qunlock(p); +} + +static long +piperead(Chan *c, void *va, long n, vlong) +{ + Pipe *p; + + p = c->aux; + + switch(NETTYPE(c->qid.path)){ + case Qdir: + return devdirread(c, va, n, p->pipedir, nelem(pipedir), pipegen); + case Qdata0: + return qread(p->q[0], va, n); + case Qdata1: + return qread(p->q[1], va, n); + default: + panic("piperead"); + } + return -1; /* not reached */ +} + +static Block* +pipebread(Chan *c, long n, ulong offset) +{ + Pipe *p; + + p = c->aux; + + switch(NETTYPE(c->qid.path)){ + case Qdata0: + return qbread(p->q[0], n); + case Qdata1: + return qbread(p->q[1], n); + } + + return devbread(c, n, offset); +} + +/* + * a write to a closed pipe causes an exception to be sent to + * the prog. + */ +static long +pipewrite(Chan *c, void *va, long n, vlong) +{ + Pipe *p; + Prog *r; + + if(waserror()) { + /* avoid exceptions when pipe is a mounted queue */ + if((c->flag & CMSG) == 0) { + r = up->iprog; + if(r != nil && r->kill == nil) + r->kill = "write on closed pipe"; + } + nexterror(); + } + + p = c->aux; + + switch(NETTYPE(c->qid.path)){ + case Qdata0: + n = qwrite(p->q[1], va, n); + break; + + case Qdata1: + n = qwrite(p->q[0], va, n); + break; + + default: + panic("pipewrite"); + } + + poperror(); + return n; +} + +static long +pipebwrite(Chan *c, Block *bp, ulong junk) +{ + long n; + Pipe *p; + Prog *r; + + USED(junk); + if(waserror()) { + /* avoid exceptions when pipe is a mounted queue */ + if((c->flag & CMSG) == 0) { + r = up->iprog; + if(r != nil && r->kill == nil) + r->kill = "write on closed pipe"; + } + nexterror(); + } + + p = c->aux; + switch(NETTYPE(c->qid.path)){ + case Qdata0: + n = qbwrite(p->q[1], bp); + break; + + case Qdata1: + n = qbwrite(p->q[0], bp); + break; + + default: + n = 0; + panic("pipebwrite"); + } + + poperror(); + return n; +} + +static int +pipewstat(Chan *c, uchar *dp, int n) +{ + Dir *d; + Pipe *p; + int d1; + + if (c->qid.type&QTDIR) + error(Eperm); + p = c->aux; + if(strcmp(up->env->user, p->user) != 0) + error(Eperm); + d = smalloc(sizeof(*d)+n); + if(waserror()){ + free(d); + nexterror(); + } + n = convM2D(dp, n, d, (char*)&d[1]); + if(n == 0) + error(Eshortstat); + d1 = NETTYPE(c->qid.path) == Qdata1; + if(!emptystr(d->name)){ + validwstatname(d->name); + if(strlen(d->name) >= KNAMELEN) + error(Efilename); + if(strcmp(p->pipedir[1+!d1].name, d->name) == 0) + error(Eexist); + kstrcpy(p->pipedir[1+d1].name, d->name, KNAMELEN); + } + if(d->mode != ~0UL) + p->pipedir[d1 + 1].perm = d->mode & 0777; + poperror(); + free(d); + return n; +} + +Dev pipedevtab = { + '|', + "pipe", + + devreset, + pipeinit, + devshutdown, + pipeattach, + pipewalk, + pipestat, + pipeopen, + devcreate, + pipeclose, + piperead, + pipebread, + pipewrite, + pipebwrite, + devremove, + pipewstat, +}; diff --git a/os/port/devpointer.c b/os/port/devpointer.c new file mode 100644 index 00000000..7b069397 --- /dev/null +++ b/os/port/devpointer.c @@ -0,0 +1,279 @@ +/* + * mouse or stylus + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include +#include +#include +#include "screen.h" + +enum{ + Qdir, + Qpointer, + Qcursor, +}; + +typedef struct Pointer Pointer; + +struct Pointer { + int x; + int y; + int b; + ulong msec; +}; + +static struct +{ + Pointer; + int modify; + int lastb; + Rendez r; + Ref ref; + QLock q; +} mouse; + +static +Dirtab pointertab[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "pointer", {Qpointer}, 0, 0666, + "cursor", {Qcursor}, 0, 0222, +}; + +enum { + Nevent = 16 /* enough for some */ +}; + +static struct { + int rd; + int wr; + Pointer clicks[Nevent]; + Rendez r; + int full; + int put; + int get; +} ptrq; + +/* + * called by any source of pointer data + */ +void +mousetrack(int b, int x, int y, int isdelta) +{ + int lastb; + ulong msec; + Pointer e; + + if(isdelta){ + x += mouse.x; + y += mouse.y; + } + msec = TK2MS(MACHP(0)->ticks); + if(b && (mouse.b ^ b)&0x1f){ + if(msec - mouse.msec < 300 && mouse.lastb == b + && abs(mouse.x - x) < 12 && abs(mouse.y - y) < 12) + b |= 1<<8; + mouse.lastb = b & 0x1f; + mouse.msec = msec; + } + if(x == mouse.x && y == mouse.y && mouse.b == b) + return; + lastb = mouse.b; + mouse.x = x; + mouse.y = y; + mouse.b = b; + mouse.msec = msec; + if(!ptrq.full && lastb != b){ + e = mouse.Pointer; + ptrq.clicks[ptrq.wr] = e; + if(++ptrq.wr >= Nevent) + ptrq.wr = 0; + if(ptrq.wr == ptrq.rd) + ptrq.full = 1; + } + mouse.modify = 1; + ptrq.put++; + wakeup(&ptrq.r); + drawactive(1); + /* TO DO: cursor update */ +} + +static int +ptrqnotempty(void*) +{ + return ptrq.full || ptrq.put != ptrq.get; +} + +static Pointer +mouseconsume(void) +{ + Pointer e; + + sleep(&ptrq.r, ptrqnotempty, 0); + ptrq.full = 0; + ptrq.get = ptrq.put; + if(ptrq.rd != ptrq.wr){ + e = ptrq.clicks[ptrq.rd]; + if(++ptrq.rd >= Nevent) + ptrq.rd = 0; + }else + e = mouse.Pointer; + return e; +} + +Point +mousexy(void) +{ + return Pt(mouse.x, mouse.y); +} + + +static Chan* +pointerattach(char* spec) +{ + return devattach('m', spec); +} + +static Walkqid* +pointerwalk(Chan *c, Chan *nc, char **name, int nname) +{ + Walkqid *wq; + + wq = devwalk(c, nc, name, nname, pointertab, nelem(pointertab), devgen); + if(wq != nil && wq->clone != c && wq->clone != nil && (ulong)c->qid.path == Qpointer) + incref(&mouse.ref); /* can this happen? */ + return wq; +} + +static int +pointerstat(Chan* c, uchar *db, int n) +{ + return devstat(c, db, n, pointertab, nelem(pointertab), devgen); +} + +static Chan* +pointeropen(Chan* c, int omode) +{ + c = devopen(c, omode, pointertab, nelem(pointertab), devgen); + if((ulong)c->qid.path == Qpointer){ + if(waserror()){ + c->flag &= ~COPEN; + nexterror(); + } + if(!canqlock(&mouse.q)) + error(Einuse); + if(incref(&mouse.ref) != 1){ + qunlock(&mouse.q); + error(Einuse); + } + cursorenable(); + qunlock(&mouse.q); + poperror(); + } + return c; +} + +static void +pointerclose(Chan* c) +{ + if((c->flag & COPEN) == 0) + return; + switch((ulong)c->qid.path){ + case Qpointer: + qlock(&mouse.q); + if(decref(&mouse.ref) == 0) + cursordisable(); + qunlock(&mouse.q); + break; + } +} + +static long +pointerread(Chan* c, void* a, long n, vlong) +{ + Pointer mt; + char tmp[128]; + int l; + + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, pointertab, nelem(pointertab), devgen); + case Qpointer: + qlock(&mouse.q); + if(waserror()) { + qunlock(&mouse.q); + nexterror(); + } + mt = mouseconsume(); + poperror(); + qunlock(&mouse.q); + l = sprint(tmp, "m%11d %11d %11d %11lud ", mt.x, mt.y, mt.b, mt.msec); + if(l < n) + n = l; + memmove(a, tmp, n); + break; + case Qcursor: + /* TO DO: interpret data written as Image; give to drawcursor() */ + break; + default: + n=0; + break; + } + return n; +} + +static long +pointerwrite(Chan* c, void* va, long n, vlong) +{ + char *a = va; + char buf[128]; + int b, x, y; + + switch((ulong)c->qid.path){ + case Qpointer: + if(n > sizeof buf-1) + n = sizeof buf -1; + memmove(buf, va, n); + buf[n] = 0; + x = strtoul(buf+1, &a, 0); + if(*a == 0) + error(Eshort); + y = strtoul(a, &a, 0); + if(*a != 0) + b = strtoul(a, 0, 0); + else + b = mouse.b; + mousetrack(b, x, y, 0); + break; + default: + error(Ebadusefd); + } + return n; +} + +Dev pointerdevtab = { + 'm', + "pointer", + + devreset, + devinit, + devshutdown, + pointerattach, + pointerwalk, + pointerstat, + pointeropen, + devcreate, + pointerclose, + pointerread, + devbread, + pointerwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devprof.c b/os/port/devprof.c new file mode 100644 index 00000000..f0ee3ba8 --- /dev/null +++ b/os/port/devprof.c @@ -0,0 +1,722 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include +#include +#include "runt.h" + +static void cpxec(Prog *); +static void memprof(int, Heap*, ulong); + +extern Inst* pc2dispc(Inst*, Module*); + +static int interval = 100; /* Sampling interval in milliseconds */ + +enum +{ + HSIZE = 32, +}; + +#define HASH(m) ((m)%HSIZE) + +/* cope with multiple profilers some day */ + +typedef struct Record Record; +struct Record +{ + int id; + char* name; + char* path; + Inst* base; + int size; + /*Module* m; */ + ulong mtime; + Qid qid; + Record* hash; + Record* link; + ulong bucket[1]; +}; + +struct +{ + Lock l; + vlong time; + Record* hash[HSIZE]; + Record* list; +} profile; + +typedef struct Pmod Pmod; +struct Pmod +{ + char* name; + Pmod* link; +} *pmods; + +#define QSHIFT 4 +#define QID(q) ((ulong)(q).path&0xf) +#define QPID(pid) ((pid)<link) + if(r->id == id) + break; + return r; +} + +static void +addpmod(char *m) +{ + Pmod *p = malloc(sizeof(Pmod)); + + if(p == nil) + return; + p->name = malloc(strlen(m)+1); + if(p->name == nil){ + free(p); + return; + } + strcpy(p->name, m); + p->link = pmods; + pmods = p; +} + +static void +freepmods(void) +{ + Pmod *p, *np; + + for(p = pmods; p != nil; p = np){ + free(p->name); + np = p->link; + free(p); + } + pmods = nil; +} + +static int +inpmods(char *m) +{ + Pmod *p; + + for(p = pmods; p != nil; p = p->link) + if(strcmp(p->name, m) == 0) + return 1; + return 0; +} + +static void +freeprof(void) +{ + int i; + Record *r, *nr; + + ids = 0; + profiler = Pnil; + freepmods(); + for(r = profile.list; r != nil; r = nr){ + free(r->path); + nr = r->link; + free(r); + } + profile.list = nil; + profile.time = 0; + for(i = 0; i < HSIZE; i++) + profile.hash[i] = nil; +} + +static int +profgen(Chan *c, char *name, Dirtab *d, int nd, int s, Dir *dp) +{ + Qid qid; + Record *r; + ulong path, perm, len; + Dirtab *tab; + + USED(name); + USED(d); + USED(nd); + + if(s == DEVDOTDOT) { + mkqid(&qid, Qdir, 0, QTDIR); + devdir(c, qid, "#P", 0, eve, 0555, dp); + return 1; + } + + if(c->qid.path == Qdir && c->qid.type & QTDIR) { + acquire(); + if(s-- == 0){ + tab = &profdir[Qctl]; + mkqid(&qid, PATH(c->qid)|tab->qid.path, c->qid.vers, QTFILE); + devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp); + release(); + return 1; + } + r = profile.list; + while(s-- && r != nil) + r = r->link; + if(r == nil) { + release(); + return -1; + } + sprint(up->genbuf, "%.8lux", (ulong)r->id); + mkqid(&qid, (r->id<id, QTDIR); + devdir(c, qid, up->genbuf, 0, eve, DMDIR|0555, dp); + release(); + return 1; + } + if(s >= nelem(profdir)-1) + error(Enonexist); /* was return -1; */ + tab = &profdir[s]; + path = PATH(c->qid); + + acquire(); + r = getrec(PID(c->qid)); + if(r == nil) { + release(); + error(Enonexist); /* was return -1; */ + } + + perm = tab->perm; + len = tab->length; + mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE); + devdir(c, qid, tab->name, len, eve, perm, dp); + release(); + return 1; +} + +static Chan* +profattach(char *spec) +{ + return devattach('P', spec); +} + +static Walkqid* +profwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, profgen); +} + +static int +profstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, profgen); +} + +static Chan* +profopen(Chan *c, int omode) +{ + int qid; + Record *r; + + if(c->qid.type & QTDIR) { + if(omode != OREAD) + error(Eisdir); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; + } + + if(omode&OTRUNC) + error(Eperm); + + qid = QID(c->qid); + if(qid == Qctl || qid == Qpctl){ + if (omode != OWRITE) + error(Eperm); + } + else{ + if(omode != OREAD) + error(Eperm); + } + + if(qid != Qctl){ + acquire(); + r = getrec(PID(c->qid)); + release(); + if(r == nil) + error(Ethread); + } + + c->offset = 0; + c->flag |= COPEN; + c->mode = openmode(omode); + if(QID(c->qid) == Qhist) + c->aux = nil; + return c; +} + +static int +profwstat(Chan *c, uchar *dp, int n) +{ + Dir d; + Record *r; + + if(strcmp(up->env->user, eve)) + error(Eperm); + if(c->qid.type & QTDIR) + error(Eperm); + acquire(); + r = getrec(PID(c->qid)); + release(); + if(r == nil) + error(Ethread); + n = convM2D(dp, n, &d, nil); + if(n == 0) + error(Eshortstat); + d.mode &= 0777; + /* TO DO: copy to c->aux->perm, once that exists */ + return n; +} + +static void +profclose(Chan *c) +{ + USED(c); +} + +static long +profread(Chan *c, void *va, long n, vlong offset) +{ + int i; + Record *r; + char *a = va; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, profgen); + acquire(); + r = getrec(PID(c->qid)); + release(); + if(r == nil) + error(Ethread); + switch(QID(c->qid)){ + case Qname: + return readstr(offset, va, n, r->name); + case Qpath: + return readstr(offset, va, n, r->path); + case Qhist: + i = (int)c->aux; + while(i < r->size && r->bucket[i] == 0) + i++; + if(i >= r->size) + return 0; + c->aux = (void*)(i+1); + if(n < 20) + error(Etoosmall); + return sprint(a, "%d %lud", i, r->bucket[i]); + case Qctl: + error(Eperm); + } + return 0; +} + +static long +profwrite(Chan *c, void *va, long n, vlong offset) +{ + int i; + char *a = va; + char buf[128], *fields[128]; + + USED(va); + USED(n); + USED(offset); + + if(c->qid.type & QTDIR) + error(Eisdir); + switch(QID(c->qid)){ + case Qctl: + if(n > sizeof(buf)-1) + n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = 0; + i = getfields(buf, fields, nelem(fields), 1, " \t\n"); + if(i > 0 && strcmp(fields[0], "module") == 0){ + freepmods(); + while(--i > 0) + addpmod(fields[i]); + return n; + } + if(i == 1){ + if(strcmp(fields[0], "start") == 0){ + if(profiler == Pnil) { + profiler = Psam; + if(!samplefn){ + samplefn = 1; + kproc("prof", sampler, 0, 0); + } + } + } + else if(strcmp(fields[0], "startmp") == 0){ + if(profiler == Pnil){ + profiler = Pmem; + heapmonitor = memprof; + } + } + else if(strcmp(fields[0], "stop") == 0) + profiler = Pnil; + else if(strcmp(fields[0], "end") == 0){ + profiler = Pnil; + freeprof(); + interval = 100; + } + else + error(Ebadarg); + } + else if (i == 2){ + if(strcmp(fields[0], "interval") == 0) + interval = strtoul(fields[1], nil, 0); + else if(strcmp(fields[0], "startcp") == 0){ + Prog *p; + + acquire(); + p = progpid(strtoul(fields[1], nil, 0)); + if(p == nil){ + release(); + return -1; + } + if(profiler == Pnil){ + profiler = Pcov; + p->xec = cpxec; + } + release(); + } + else + error(Ebadarg); + } + else + error(Ebadarg); + return n; + default: + error(Eperm); + } + return 0; +} + +static Record* +newmodule(Module *m, int vm, int scale, int origin) +{ + int dsize; + Record *r, **l; + + if(!vm) + acquire(); + if((m->compiled && m->pctab == nil) || m->prog == nil) { + if(!vm) + release(); + return nil; + } +/* print("newmodule %x %s %s %d %d %d\n", m, m->name, m->path, m->mtime, m->qid.path, m->qid.vers); */ + if(m->compiled) + dsize = m->nprog * sizeof(r->bucket[0]); + else + dsize = (msize(m->prog)/sizeof(Inst)) * sizeof(r->bucket[0]); + dsize *= scale; + dsize += origin; + r = malloc(sizeof(Record)+dsize); + if(r == nil) { + if(!vm) + release(); + return nil; + } + + r->id = ++ids; + if(ids == (1<<8)-1) + ids = 0; + kstrdup(&r->name, m->name); + kstrdup(&r->path, m->path); + r->base = m->prog; + r->size = dsize/sizeof(r->bucket[0]); + // r->m = m; + r->mtime = m->mtime; + r->qid.path = m->qid.path; + r->qid.vers = m->qid.vers; + memset(r->bucket, 0, dsize); + r->link = profile.list; + profile.list = r; + + l = &profile.hash[HASH(m->mtime)]; + r->hash = *l; + *l = r; + + if(!vm) + release(); + return r; +} + +static Record* +mlook(Module *m, int vm, int scale, int origin) +{ + Record *r; + + for(r = profile.hash[HASH(m->mtime)]; r; r = r->hash){ + /* if(r->m == m){ /* bug - r->m could be old exited module */ + if(r->mtime == m->mtime && r->qid.path == m->qid.path && r->qid.vers == m->qid.vers && strcmp(r->name, m->name) == 0 && strcmp(r->path, m->path) == 0){ + r->base = m->prog; + return r; + } + } + if(pmods == nil || inpmods(m->name) || inpmods(m->path)){ + if(0 && profiler == Pmem) + heapmonitor = nil; + r = newmodule(m, vm, scale, origin); + if(0 && profiler == Pmem) + heapmonitor = memprof; + return r; + } + return nil; +} + +static void +sampler(void* a) +{ + int i; + Module *m; + Record *r; + Inst *p; + + USED(a); + for(;;) { + tsleep(&up->sleep, return0, nil, interval); + if(profiler != Psam) + break; + lock(&profile.l); + profile.time += interval; + if(R.M == H || (m = R.M->m) == nil){ + unlock(&profile.l); + continue; + } + p = R.PC; + r = mlook(m, 0, 1, 0); + if(r == nil){ + unlock(&profile.l); + continue; + } + if(m->compiled && m->pctab != nil) + p = pc2dispc(p, m); + if((i = p-r->base) >= 0 && i < r->size) + r->bucket[i]++; + unlock(&profile.l); + } + samplefn = 0; + pexit("", 0); +} + +/* + * coverage profiling + */ + +static void +cpxec(Prog *p) +{ + int op, i; + Module *m; + Record *r; + Prog *n; + + R = p->R; + R.MP = R.M->MP; + R.IC = p->quanta; + + if(p->kill != nil){ + char *m; + m = p->kill; + p->kill = nil; + error(m); + } + + if(R.M->compiled) + comvec(); + else{ + m = R.M->m; + r = profiler == Pcov ? mlook(m, 1, 1, 0) : nil; + do{ + dec[R.PC->add](); + op = R.PC->op; + if(r != nil){ + i = R.PC-r->base; + if(i >= 0 && i < r->size) + r->bucket[i]++; + } + R.PC++; + optab[op](); + if(op == ISPAWN || op == IMSPAWN){ + n = delruntail(Pdebug); // any state will do + n->xec = cpxec; + addrun(n); + } + if(m != R.M->m){ + m = R.M->m; + r = profiler == Pcov ? mlook(m, 1, 1, 0) : nil; + } + }while(--R.IC != 0); + } + + p->R = R; +} + +/* memory profiling */ + +enum{ + Halloc, + Hfree, + Hgcfree, +}; + +#define MPAD sizeof(double) + +static void +memprof(int c, Heap *h, ulong n) +{ + int i, j, k; + ulong kk, *b; + Module *m; + Record *r; + Inst *p; + +/* print("%d %x %uld\n", c, h, n); */ + USED(h); + if(profiler != Pmem){ + heapmonitor = nil; + return; + } + lock(&profile.l); + m = nil; + if(c != Hgcfree && (R.M == H || (m = R.M->m) == nil)){ + unlock(&profile.l); + return; + } + j = n; + if(c == 0 || c == 4){ /* heap or main allocation */ + p = R.PC; + if(m->compiled && m->pctab != nil) + p = pc2dispc(p, m); + if((r = mlook(m, 1, 2, 2)) == nil){ + unlock(&profile.l); + return; + } + i = p-r->base; + k = (r->id<<24) | i; + if(c == 0){ + h->pad = k; + k = sizeof(Heap); + } + else{ + *(ulong*)h = k; + k = MPAD; + } + /* 31 is pool quanta - dependency on alloc.c */ + j = ((j+k+BHDRSIZE+31)&~31) - (k+BHDRSIZE); + } + else{ + /* c == 1 is ref count free */ + /* c == 2 is gc free */ + /* c == 3 is main free */ + if(c == 3) + k = *(ulong*)h; + else + k = h->pad; + if((r = getrec(k>>24)) == nil){ + unlock(&profile.l); + return; + } + i = k&0xffffff; + if(c == 3) + j = msize(h)-MPAD; + else + j = hmsize(h)-sizeof(Heap); + j = -j; + } + i = 2*(i+1); + b = r->bucket; + if(i >= 0 && i < r->size){ + if(0){ + if(c == 1){ + b[0] -= j; + b[i] -= j; + } + else if(c == 2){ + b[1] -= j; + b[i+1] -= j; + } + } + else{ + b[0] += j; + if((int)b[0] < 0) + b[0] = 0; + b[i] += j; + if((int)b[i] < 0) + b[i] = 0; + if(j > 0){ + if((kk = b[0]) > b[1]) + b[1] = kk; + if((kk = b[i]) > b[i+1]) + b[i+1] = kk; + } + } + } + unlock(&profile.l); +} + +Dev profdevtab = { + 'P', + "prof", + + devreset, + devinit, + devshutdown, + profattach, + profwalk, + profstat, + profopen, + devcreate, + profclose, + profread, + devbread, + profwrite, + devbwrite, + devremove, + profwstat +}; diff --git a/os/port/devprog.c b/os/port/devprog.c new file mode 100644 index 00000000..c6c97efc --- /dev/null +++ b/os/port/devprog.c @@ -0,0 +1,1510 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" +#include +#include +#include "runt.h" + +/* + * Enable the heap device for environments that allow debugging => + * Must be 1 for a production environment. + */ +int SECURE = 0; + +enum +{ + Qdir, + Qctl, + Qdbgctl, + Qheap, + Qns, + Qnsgrp, + Qpgrp, + Qstack, + Qstatus, + Qtext, + Qwait, + Qfd, + Qexception, +}; + +/* + * must be in same order as enum + */ +Dirtab progdir[] = +{ + "ctl", {Qctl}, 0, 0200, + "dbgctl", {Qdbgctl}, 0, 0600, + "heap", {Qheap}, 0, 0600, + "ns", {Qns}, 0, 0400, + "nsgrp", {Qnsgrp}, 0, 0444, + "pgrp", {Qpgrp}, 0, 0444, + "stack", {Qstack}, 0, 0400, + "status", {Qstatus}, 0, 0444, + "text", {Qtext}, 0, 0000, + "wait", {Qwait}, 0, 0400, + "fd", {Qfd}, 0, 0400, + "exception", {Qexception}, 0, 0400, +}; + +enum +{ + CMkill, + CMkillgrp, + CMrestricted, + CMexceptions, + CMprivate +}; + +static +Cmdtab progcmd[] = { + CMkill, "kill", 1, + CMkillgrp, "killgrp", 1, + CMrestricted, "restricted", 1, + CMexceptions, "exceptions", 2, + CMprivate, "private", 1, +}; + +enum +{ + CDstep, + CDtoret, + CDcont, + CDstart, + CDstop, + CDunstop, + CDmaim, + CDbpt +}; + +static +Cmdtab progdbgcmd[] = { + CDstep, "step", 0, /* known below to be first, to cope with stepN */ + CDtoret, "toret", 1, + CDcont, "cont", 1, + CDstart, "start", 1, + CDstop, "stop", 1, + CDunstop, "unstop", 1, + CDmaim, "maim", 1, + CDbpt, "bpt", 4, +}; + +typedef struct Heapqry Heapqry; +struct Heapqry +{ + char fmt; + ulong addr; + ulong module; + int count; +}; + +typedef struct Bpt Bpt; + +struct Bpt +{ + Bpt *next; + int pc; + char *file; + char path[1]; +}; + +typedef struct Progctl Progctl; +struct Progctl +{ + Rendez r; + int ref; + Proc *debugger; /* waiting for dbgxec */ + char *msg; /* reply from dbgxec */ + int step; /* instructions to try */ + int stop; /* stop running the program */ + Bpt* bpts; /* active breakpoints */ + Queue* q; /* status queue */ +}; + +#define QSHIFT 4 /* location in qid of pid */ +#define QID(q) (((ulong)(q).path&0x0000000F)>>0) +#define QPID(pid) (((pid)<qid.path == Qdir) { + if(name != nil){ + /* ignore s and use name to find pid */ + pid = strtoul(name, &e, 0); + if(pid == 0 || *e != '\0') + return -1; + acquire(); + p = progpid(pid); + if(p == nil){ + release(); + return -1; + } + }else{ + acquire(); + p = progn(s); + if(p == nil) { + release(); + return -1; + } + pid = p->pid; + } + o = p->osenv; + sprint(up->genbuf, "%lud", pid); + if(name != nil && strcmp(name, up->genbuf) != 0){ + release(); + return -1; + } + mkqid(&qid, pid<genbuf, 0, o->user, DMDIR|0555, dp); + release(); + return 1; + } + + if(s >= nelem(progdir)) + return -1; + tab = &progdir[s]; + path = PATH(c->qid); + + acquire(); + p = progpid(PID(c->qid)); + if(p == nil) { + release(); + return -1; + } + + o = p->osenv; + + perm = tab->perm; + if((perm & 7) == 0) + perm = (perm|(perm>>3)|(perm>>6)) & o->pgrp->progmode; + + len = tab->length; + mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE); + devdir(c, qid, tab->name, len, o->user, perm, dp); + release(); + return 1; +} + +static Chan* +progattach(char *spec) +{ + return devattach('p', spec); +} + +static Walkqid* +progwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, proggen); +} + +static int +progstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, proggen); +} + +static Chan* +progopen(Chan *c, int omode) +{ + Prog *p; + Osenv *o; + Progctl *ctl; + int perm; + + if(c->qid.type & QTDIR) + return devopen(c, omode, 0, 0, proggen); + + acquire(); + if (waserror()) { + release(); + nexterror(); + } + p = progpid(PID(c->qid)); + if(p == nil) + error(Ethread); + + o = p->osenv; + perm = progdir[QID(c->qid)-1].perm; + if((perm & 7) == 0) + perm = (perm|(perm>>3)|(perm>>6)) & o->pgrp->progmode; + devpermcheck(o->user, perm, omode); + omode = openmode(omode); + + switch(QID(c->qid)){ + default: + error(Egreg); + case Qnsgrp: + case Qpgrp: + case Qtext: + case Qstatus: + case Qstack: + case Qctl: + case Qfd: + case Qexception: + break; + case Qwait: + c->aux = qopen(1024, Qmsg, nil, nil); + if(c->aux == nil) + error(Enomem); + o->childq = c->aux; + break; + case Qns: + c->aux = malloc(sizeof(Mntwalk)); + if(c->aux == nil) + error(Enomem); + break; + case Qheap: + if(SECURE || o->pgrp->privatemem || omode != ORDWR) + error(Eperm); + c->aux = malloc(sizeof(Heapqry)); + if(c->aux == nil) + error(Enomem); + break; + case Qdbgctl: + if(SECURE || o->pgrp->privatemem || omode != ORDWR) + error(Eperm); + ctl = malloc(sizeof(Progctl)); + if(ctl == nil) + error(Enomem); + ctl->q = qopen(1024, Qmsg, nil, nil); + if(ctl->q == nil) { + free(ctl); + error(Enomem); + } + ctl->bpts = nil; + ctl->ref = 1; + c->aux = ctl; + break; + } + if(p->state != Pexiting) + c->qid.vers = p->pid; + + poperror(); + release(); + c->offset = 0; + c->mode = omode; + c->flag |= COPEN; + return c; +} + +static int +progwstat(Chan *c, uchar *db, int n) +{ + Dir d; + Prog *p; + char *u; + Osenv *o; + + if(c->qid.type&QTDIR) + error(Eperm); + acquire(); + p = progpid(PID(c->qid)); + if(p == nil) { + release(); + error(Ethread); + } + + u = up->env->user; + o = p->osenv; + if(strcmp(u, o->user) != 0 && strcmp(u, eve) != 0) { + release(); + error(Eperm); + } + + n = convM2D(db, n, &d, nil); + if(n == 0){ + release(); + error(Eshortstat); + } + if(d.mode != ~0UL) + o->pgrp->progmode = d.mode&0777; + release(); + return n; +} + +static void +closedbgctl(Progctl *ctl, Prog *p) +{ + Osenv *o; + + if(ctl->ref-- > 1) + return; + freebpts(ctl->bpts); + if(p != nil){ + o = p->osenv; + if(o->debug == ctl){ + o->debug = nil; + p->xec = xec; + } + } + qfree(ctl->q); + free(ctl); +} + +static void +progclose(Chan *c) +{ + int i; + Prog *f; + Osenv *o; + Progctl *ctl; + + switch(QID(c->qid)) { + case Qns: + case Qheap: + free(c->aux); + break; + case Qdbgctl: + if((c->flag & COPEN) == 0) + return; + ctl = c->aux; + acquire(); + closedbgctl(ctl, progpid(PID(c->qid))); + release(); + break; + case Qwait: + acquire(); + i = 0; + for(;;) { + f = progn(i++); + if(f == nil) + break; + o = f->osenv; + if(o->waitq == c->aux) + o->waitq = nil; + if(o->childq == c->aux) + o->childq = nil; + } + release(); + qfree(c->aux); + } +} + +static int +progsize(Prog *p) +{ + int size; + Frame *f; + uchar *fp; + Modlink *m; + + m = p->R.M; + size = 0; + if(m->MP != H) + size += hmsize(D2H(m->MP)); + if(m->prog != nil) + size += msize(m->prog); + + fp = p->R.FP; + while(fp != nil) { + f = (Frame*)fp; + fp = f->fp; + if(f->mr != nil) { + if(f->mr->MP != H) + size += hmsize(D2H(f->mr->MP)); + if(f->mr->prog != nil) + size += msize(f->mr->prog); + } + if(f->t == nil) + size += msize(SEXTYPE(f)); + } + return size/1024; +} + +static long +progoffset(long offset, char *va, int *np) +{ + if(offset > 0) { + offset -= *np; + if(offset < 0) { + memmove(va, va+*np+offset, -offset); + *np = -offset; + } + else + *np = 0; + } + return offset; +} + +static int +progqidwidth(Chan *c) +{ + char buf[32]; + + return sprint(buf, "%lud", c->qid.vers); +} + +int +progfdprint(Chan *c, int fd, int w, char *s, int ns) +{ + int n; + + if(w == 0) + w = progqidwidth(c); + n = snprint(s, ns, "%3d %.2s %C %4ld (%.16llux %*lud %.2ux) %5ld %8lld %s\n", + fd, + &"r w rw"[(c->mode&3)<<1], + devtab[c->type]->dc, c->dev, + c->qid.path, w, c->qid.vers, c->qid.type, + c->iounit, c->offset, c->name->s); + return n; +} + +static int +progfds(Osenv *o, char *va, int count, long offset) +{ + Fgrp *f; + Chan *c; + int n, i, w, ww; + + f = o->fgrp; /* f is not locked because we've acquired */ + n = readstr(0, va, count, o->pgrp->dot->name->s); + n += snprint(va+n, count-n, "\n"); + offset = progoffset(offset, va, &n); + /* compute width of qid.path */ + w = 0; + for(i = 0; i <= f->maxfd; i++) { + c = f->fd[i]; + if(c == nil) + continue; + ww = progqidwidth(c); + if(ww > w) + w = ww; + } + for(i = 0; i <= f->maxfd; i++) { + c = f->fd[i]; + if(c == nil) + continue; + n += progfdprint(c, i, w, va+n, count-n); + offset = progoffset(offset, va, &n); + } + return n; +} + +Inst * +pc2dispc(Inst *pc, Module *mod) +{ + ulong l, u, m, v; + ulong *tab = mod->pctab; + + v = (ulong)pc - (ulong)mod->prog; + l = 0; + u = mod->nprog-1; + while(l < u){ + m = (l+u+1)/2; + if(tab[m] < v) + l = m; + else if(tab[m] > v) + u = m-1; + else + l = u = m; + } + if(l == u && tab[u] <= v && u != mod->nprog-1 && tab[u+1] > v) + return &mod->prog[u]; + return 0; +} + +static int +progstack(REG *reg, int state, char *va, int count, long offset) +{ + int n; + Frame *f; + Inst *pc; + uchar *fp; + Modlink *m; + + n = 0; + m = reg->M; + fp = reg->FP; + pc = reg->PC; + + /* + * all states other than debug and ready block, + * but interp has already advanced the PC + */ + if(!m->compiled && state != Pready && state != Pdebug && pc > m->prog) + pc--; + if(m->compiled && m->m->pctab != nil) + pc = pc2dispc(pc, m->m); + + while(fp != nil) { + f = (Frame*)fp; + n += snprint(va+n, count-n, "%.8lux %.8lux %.8lux %.8lux %d %s\n", + (ulong)f, /* FP */ + (ulong)(pc - m->prog), /* PC in dis instructions */ + (ulong)m->MP, /* MP */ + (ulong)m->prog, /* Code for module */ + m->compiled && m->m->pctab == nil, /* True if native assembler: fool stack utility for now */ + m->m->path); /* File system path */ + + if(offset > 0) { + offset -= n; + if(offset < 0) { + memmove(va, va+n+offset, -offset); + n = -offset; + } + else + n = 0; + } + + pc = f->lr; + fp = f->fp; + if(f->mr != nil) + m = f->mr; + if(!m->compiled) + pc--; + else if(m->m->pctab != nil) + pc = pc2dispc(pc, m->m)-1; + } + return n; +} + +static int +calldepth(REG *reg) +{ + int n; + uchar *fp; + + n = 0; + for(fp = reg->FP; fp != nil; fp = ((Frame*)fp)->fp) + n++; + return n; +} + +static int +progheap(Heapqry *hq, char *va, int count, ulong offset) +{ + WORD *w; + void *p; + List *hd; + Array *a; + char *fmt, *str; + Module *m; + Modlink *ml; + Channel *c; + ulong addr; + String *ss; + union { REAL r; LONG l; WORD w[2]; } rock; + int i, s, n, len, signed_off; + Type *t; + + n = 0; + s = 0; + signed_off = offset; + addr = hq->addr; + for(i = 0; i < hq->count; i++) { + switch(hq->fmt) { + case 'W': + if(addr & 3) + return -1; + n += snprint(va+n, count-n, "%d\n", *(WORD*)addr); + s = sizeof(WORD); + break; + case 'B': + n += snprint(va+n, count-n, "%d\n", *(BYTE*)addr); + s = sizeof(BYTE); + break; + case 'V': + if(addr & 3) + return -1; + w = (WORD*)addr; + rock.w[0] = w[0]; + rock.w[1] = w[1]; + n += snprint(va+n, count-n, "%lld\n", rock.l); + s = sizeof(LONG); + break; + case 'R': + if(addr & 3) + return -1; + w = (WORD*)addr; + rock.w[0] = w[0]; + rock.w[1] = w[1]; + n += snprint(va+n, count-n, "%g\n", rock.r); + s = sizeof(REAL); + break; + case 'I': + if(addr & 3) + return -1; + for(m = modules; m != nil; m = m->link) + if(m == (Module*)hq->module) + break; + if(m == nil) + error(Ebadctl); + addr = (ulong)(m->prog+addr); + n += snprint(va+n, count-n, "%D\n", (Inst*)addr); + s = sizeof(Inst); + break; + case 'P': + if(addr & 3) + return -1; + p = *(void**)addr; + fmt = "nil\n"; + if(p != H) + fmt = "%lux\n"; + n += snprint(va+n, count-n, fmt, p); + s = sizeof(WORD); + break; + case 'L': + if(addr & 3) + return -1; + hd = *(List**)addr; + if(hd == H || D2H(hd)->t != &Tlist) + return -1; + n += snprint(va+n, count-n, "%lux.%lux\n", (ulong)&hd->tail, (ulong)hd->data); + s = sizeof(WORD); + break; + case 'A': + if(addr & 3) + return -1; + a = *(Array**)addr; + if(a == H) + n += snprint(va+n, count-n, "nil\n"); + else { + if(D2H(a)->t != &Tarray) + return -1; + n += snprint(va+n, count-n, "%d.%lux\n", a->len, (ulong)a->data); + } + s = sizeof(WORD); + break; + case 'C': + if(addr & 3) + return -1; + ss = *(String**)addr; + if(ss == H) + ss = &snil; + else + if(D2H(ss)->t != &Tstring) + return -1; + n += snprint(va+n, count-n, "%d.", abs(ss->len)); + str = string2c(ss); + len = strlen(str); + if(count-n < len) + len = count-n; + if(len > 0) { + memmove(va+n, str, len); + n += len; + } + break; + case 'M': + if(addr & 3) + return -1; + ml = *(Modlink**)addr; + fmt = ml == H ? "nil\n" : "%lux\n"; + n += snprint(va+n, count-n, fmt, ml->MP); + s = sizeof(WORD); + break; + case 'c': + if(addr & 3) + return -1; + c = *(Channel**)addr; + if(c == H) + n += snprint(va+n, count-n, "nil\n"); + else{ + t = D2H(c)->t; + if(t != &Tchannel && t != Trdchan && t != Twrchan) + return -1; + if(c->buf == H) + n += snprint(va+n, count-n, "0.%lux\n", (ulong)c); + else + n += snprint(va+n, count-n, "%d.%lux.%d.%d\n", c->buf->len, (ulong)c->buf->data, c->front, c->size); + } + break; + + } + addr += s; + if(signed_off > 0) { + signed_off -= n; + if(signed_off < 0) { + memmove(va, va+n+signed_off, -signed_off); + n = -signed_off; + } + else + n = 0; + } + } + return n; +} + +WORD +modstatus(REG *r, char *ptr, int len) +{ + Inst *PC; + Frame *f; + + if(r->M->m->name[0] == '$') { + f = (Frame*)r->FP; + snprint(ptr, len, "%s[%s]", f->mr->m->name, r->M->m->name); + if(f->mr->compiled) + return (WORD)f->lr; + return f->lr - f->mr->prog; + } + memmove(ptr, r->M->m->name, len); + if(r->M->compiled) + return (WORD)r->PC; + PC = r->PC; + /* should really check for blocked states */ + if(PC > r->M->prog) + PC--; + return PC - r->M->prog; +} + +static void +int2flag(int flag, char *s) +{ + if(flag == 0){ + *s = '\0'; + return; + } + *s++ = '-'; + if(flag & MAFTER) + *s++ = 'a'; + if(flag & MBEFORE) + *s++ = 'b'; + if(flag & MCREATE) + *s++ = 'c'; + if(flag & MCACHE) + *s++ = 'C'; + *s = '\0'; +} + +static char* +progtime(ulong msec, char *buf, char *ebuf) +{ + int tenths, sec; + + tenths = msec/100; + sec = tenths/10; + seprint(buf, ebuf, "%4d:%2.2d.%d", sec/60, sec%60, tenths%10); + return buf; +} + +static long +progread(Chan *c, void *va, long n, vlong offset) +{ + int i; + Prog *p; + Osenv *o; + Mntwalk *mw; + ulong grpid; + char *a = va; + Progctl *ctl; + char mbuf[64], timebuf[12]; + char flag[10]; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, proggen); + + switch(QID(c->qid)){ + case Qdbgctl: + ctl = c->aux; + return qread(ctl->q, va, n); + case Qstatus: + acquire(); + p = progpid(PID(c->qid)); + if(p == nil || p->state == Pexiting || p->R.M == H) { + release(); + snprint(up->genbuf, sizeof(up->genbuf), "%8lud %8d %10s %s %10s %5dK %s", + PID(c->qid), + 0, + eve, + progtime(0, timebuf, timebuf+sizeof(timebuf)), + progstate[Pexiting], + 0, + "[$Sys]"); + return readstr(offset, va, n, up->genbuf); + } + modstatus(&p->R, mbuf, sizeof(mbuf)); + o = p->osenv; + snprint(up->genbuf, sizeof(up->genbuf), "%8d %8d %10s %s %10s %5dK %s", + p->pid, + p->group!=nil? p->group->id: 0, + o->user, + progtime(TK2MS(p->ticks), timebuf, timebuf+sizeof(timebuf)), + progstate[p->state], + progsize(p), + mbuf); + release(); + return readstr(offset, va, n, up->genbuf); + case Qwait: + return qread(c->aux, va, n); + case Qns: + acquire(); + if(waserror()){ + release(); + nexterror(); + } + p = progpid(PID(c->qid)); + if(p == nil) + error(Ethread); + mw = c->aux; + if(mw->cddone){ + poperror(); + release(); + return 0; + } + o = p->osenv; + mntscan(mw, o->pgrp); + if(mw->mh == 0) { + mw->cddone = 1; + i = snprint(a, n, "cd %s\n", o->pgrp->dot->name->s); + poperror(); + release(); + return i; + } + int2flag(mw->cm->mflag, flag); + if(strcmp(mw->cm->to->name->s, "#M") == 0){ + i = snprint(a, n, "mount %s %s %s %s\n", flag, + mw->cm->to->mchan->name->s, + mw->mh->from->name->s, mw->cm->spec? mw->cm->spec : ""); + }else + i = snprint(a, n, "bind %s %s %s\n", flag, + mw->cm->to->name->s, mw->mh->from->name->s); + poperror(); + release(); + return i; + case Qnsgrp: + acquire(); + p = progpid(PID(c->qid)); + if(p == nil) { + release(); + error(Ethread); + } + grpid = ((Osenv *)p->osenv)->pgrp->pgrpid; + release(); + return readnum(offset, va, n, grpid, NUMSIZE); + case Qpgrp: + acquire(); + p = progpid(PID(c->qid)); + if(p == nil) { + release(); + error(Ethread); + } + grpid = p->group!=nil? p->group->id: 0; + release(); + return readnum(offset, va, n, grpid, NUMSIZE); + case Qstack: + acquire(); + p = progpid(PID(c->qid)); + if(p == nil || p->state == Pexiting) { + release(); + error(Ethread); + } + if(p->state == Pready) { + release(); + error(Estopped); + } + n = progstack(&p->R, p->state, va, n, offset); + release(); + return n; + case Qheap: + acquire(); + if(waserror()){ + release(); + nexterror(); + } + n = progheap(c->aux, va, n, offset); + if(n == -1) + error(Emisalign); + poperror(); + release(); + return n; + case Qfd: + acquire(); + if(waserror()) { + release(); + nexterror(); + } + p = progpid(PID(c->qid)); + if(p == nil) + error(Ethread); + o = p->osenv; + n = progfds(o, va, n, offset); + poperror(); + release(); + return n; + case Qexception: + acquire(); + p = progpid(PID(c->qid)); + if(p == nil) { + release(); + error(Ethread); + } + if(p->exstr == nil) + up->genbuf[0] = 0; + else + snprint(up->genbuf, sizeof(up->genbuf), p->exstr); + release(); + return readstr(offset, va, n, up->genbuf); + } + error(Egreg); + return 0; +} + +static void +mntscan(Mntwalk *mw, Pgrp *pg) +{ + Mount *t; + Mhead *f; + int nxt, i; + ulong last, bestmid; + + rlock(&pg->ns); + + nxt = 0; + bestmid = ~0; + + last = 0; + if(mw->mh) + last = mw->cm->mountid; + + for(i = 0; i < MNTHASH; i++) { + for(f = pg->mnthash[i]; f; f = f->hash) { + for(t = f->mount; t; t = t->next) { + if(mw->mh == 0 || + (t->mountid > last && t->mountid < bestmid)) { + mw->cm = t; + mw->mh = f; + bestmid = mw->cm->mountid; + nxt = 1; + } + } + } + } + if(nxt == 0) + mw->mh = 0; + + runlock(&pg->ns); +} + +static long +progwrite(Chan *c, void *va, long n, vlong offset) +{ + Prog *p, *f; + Heapqry *hq; + char buf[128]; + Progctl *ctl; + char *b; + int i, pc; + Cmdbuf *cb; + Cmdtab *ct; + Osenv *o; + + USED(offset); + USED(va); + + if(c->qid.type & QTDIR) + error(Eisdir); + + acquire(); + if(waserror()) { + release(); + nexterror(); + } + p = progpid(PID(c->qid)); + if(p == nil) + error(Ethread); + + switch(QID(c->qid)){ + case Qctl: + cb = parsecmd(va, n); + if(waserror()){ + free(cb); + nexterror(); + } + ct = lookupcmd(cb, progcmd, nelem(progcmd)); + switch(ct->index){ + case CMkillgrp: + killgrp(p, "killed"); + break; + case CMkill: + killprog(p, "killed"); + break; + case CMrestricted: + p->flags |= Prestrict; + break; + case CMexceptions: + if(p->group->id != p->pid) + error(Eperm); + if(strcmp(cb->f[1], "propagate") == 0) + p->flags |= Ppropagate; + else if(strcmp(cb->f[1], "notifyleader") == 0) + p->flags |= Pnotifyleader; + else + error(Ebadctl); + break; + case CMprivate: + o = p->osenv; + o->pgrp->privatemem = 1; + break; + } + poperror(); + free(cb); + break; + case Qdbgctl: + cb = parsecmd(va, n); + if(waserror()){ + free(cb); + nexterror(); + } + if(cb->nf == 1 && strncmp(cb->f[0], "step", 4) == 0) + ct = progdbgcmd; + else + ct = lookupcmd(cb, progdbgcmd, nelem(progdbgcmd)); + switch(ct->index){ + case CDstep: + if(cb->nf == 1) + i = strtoul(cb->f[0]+4, nil, 0); + else + i = strtoul(cb->f[1], nil, 0); + dbgstep(c->aux, p, i); + break; + case CDtoret: + f = currun(); + i = calldepth(&p->R); + while(f->kill == nil) { + dbgstep(c->aux, p, 1024); + if(i > calldepth(&p->R)) + break; + } + break; + case CDcont: + f = currun(); + while(f->kill == nil) + dbgstep(c->aux, p, 1024); + break; + case CDstart: + dbgstart(p); + break; + case CDstop: + ctl = c->aux; + ctl->stop = 1; + break; + case CDunstop: + ctl = c->aux; + ctl->stop = 0; + break; + case CDbpt: + pc = strtoul(cb->f[3], nil, 10); + ctl = c->aux; + if(strcmp(cb->f[1], "set") == 0) + ctl->bpts = setbpt(ctl->bpts, cb->f[2], pc); + else if(strcmp(cb->f[1], "del") == 0) + ctl->bpts = delbpt(ctl->bpts, cb->f[2], pc); + else + error(Ebadctl); + break; + case CDmaim: + p->kill = "maim"; + break; + } + poperror(); + free(cb); + break; + case Qheap: + /* + * Heap query: + * addr.Fn + * pc+module.In + */ + i = n; + if(i > sizeof(buf)-1) + i = sizeof(buf)-1; + memmove(buf, va, i); + buf[i] = '\0'; + hq = c->aux; + hq->addr = strtoul(buf, &b, 0); + if(*b == '+') + hq->module = strtoul(b, &b, 0); + if(*b++ != '.') + error(Ebadctl); + hq->fmt = *b++; + hq->count = strtoul(b, nil, 0); + break; + default: + print("unknown qid in procwrite\n"); + error(Egreg); + } + poperror(); + release(); + return n; +} + +static Bpt* +setbpt(Bpt *bpts, char *path, int pc) +{ + int n; + Bpt *b; + + n = strlen(path); + b = mallocz(sizeof *b + n, 0); + if(b == nil) + return bpts; + b->pc = pc; + memmove(b->path, path, n+1); + b->file = b->path; + path = strrchr(b->path, '/'); + if(path != nil) + b->file = path + 1; + b->next = bpts; + return b; +} + +static Bpt* +delbpt(Bpt *bpts, char *path, int pc) +{ + Bpt *b, **last; + + last = &bpts; + for(b = bpts; b != nil; b = b->next){ + if(b->pc == pc && strcmp(b->path, path) == 0) { + *last = b->next; + free(b); + break; + } + last = &b->next; + } + return bpts; +} + +static void +freebpts(Bpt *b) +{ + Bpt *next; + + for(; b != nil; b = next){ + next = b->next; + free(b); + } +} + +static void +telldbg(Progctl *ctl, char *msg) +{ + kstrcpy(ctl->msg, msg, ERRMAX); + ctl->debugger = nil; + wakeup(&ctl->r); +} + +static void +dbgstart(Prog *p) +{ + Osenv *o; + Progctl *ctl; + + o = p->osenv; + ctl = o->debug; + if(ctl != nil && ctl->debugger != nil) + error("prog debugged"); + if(p->state == Pdebug) + addrun(p); + o->debug = nil; + p->xec = xec; +} + +static int +xecdone(void *vc) +{ + Progctl *ctl = vc; + + return ctl->debugger == nil; +} + +static void +dbgstep(Progctl *vctl, Prog *p, int n) +{ + Osenv *o; + Progctl *ctl; + char buf[ERRMAX+20], *msg; + + if(p == currun()) + error("cannot step yourself"); + + if(p->state == Pbroken) + error("prog broken"); + + ctl = vctl; + if(ctl->debugger != nil) + error("prog already debugged"); + + o = p->osenv; + if(o->debug == nil) { + o->debug = ctl; + p->xec = dbgxec; + }else if(o->debug != ctl) + error("prog already debugged"); + + ctl->step = n; + if(p->state == Pdebug) + addrun(p); + ctl->debugger = up; + strcpy(buf, "child: "); + msg = buf+7; + ctl->msg = msg; + + /* + * wait for reply from dbgxec; release is okay because prog is now + * debugged, cannot exit. + */ + if(waserror()){ + acquire(); + ctl->debugger = nil; + ctl->msg = nil; + o->debug = nil; + p->xec = xec; + nexterror(); + } + release(); + sleep(&ctl->r, xecdone, ctl); + poperror(); + acquire(); + if(msg[0] != '\0') + error(buf); +} + +void +dbgexit(Prog *kid, int broken, char *estr) +{ + int n; + Osenv *e; + Progctl *ctl; + char buf[ERRMAX+20]; + + e = kid->osenv; + ctl = e->debug; + e->debug = nil; + kid->xec = xec; + + if(broken) + n = snprint(buf, sizeof(buf), "broken: %s", estr); + else + n = snprint(buf, sizeof(buf), "exited"); + if(ctl->debugger) + telldbg(ctl, buf); + qproduce(ctl->q, buf, n); +} + +static void +dbgaddrun(Prog *p) +{ + Osenv *o; + + p->state = Pdebug; + p->addrun = nil; + o = p->osenv; + if(o->rend != nil) + wakeup(o->rend); + o->rend = nil; +} + +static int +bdone(void *vp) +{ + Prog *p = vp; + + return p->addrun == nil; +} + +static void +dbgblock(Prog *p) +{ + Osenv *o; + Progctl *ctl; + + o = p->osenv; + ctl = o->debug; + qproduce(ctl->q, progstate[p->state], strlen(progstate[p->state])); + pushrun(p); + p->addrun = dbgaddrun; + o->rend = &up->sleep; + + /* + * bdone(p) is safe after release because p is being debugged, + * so cannot exit. + */ + if(waserror()){ + acquire(); + nexterror(); + } + release(); + if(o->rend != nil) + sleep(o->rend, bdone, p); + poperror(); + acquire(); + if(p->kill != nil) + error(p->kill); + ctl = o->debug; + if(ctl != nil) + qproduce(ctl->q, "run", 3); +} + +void +dbgxec(Prog *p) +{ + Bpt *b; + Prog *kid; + Osenv *env; + Progctl *ctl; + int op, pc, n; + char buf[ERRMAX+10]; + extern void (*dec[])(void); + + env = p->osenv; + ctl = env->debug; + ctl->ref++; + if(waserror()){ + closedbgctl(ctl, p); + nexterror(); + } + + R = p->R; + R.MP = R.M->MP; + + R.IC = p->quanta; + if((ulong)R.IC > ctl->step) + R.IC = ctl->step; + if(ctl->stop) + R.IC = 0; + + + buf[0] = '\0'; + + if(R.IC != 0 && R.M->compiled) { + comvec(); + if(p != currun()) + dbgblock(p); + goto save; + } + + while(R.IC != 0) { + if(0) + print("step: %lux: %s %4ld %D\n", + (ulong)p, R.M->m->name, R.PC-R.M->prog, R.PC); + + dec[R.PC->add](); + op = R.PC->op; + R.PC++; + optab[op](); + + /* + * check notification about new progs + */ + if(op == ISPAWN || op == IMSPAWN) { + /* pick up the kid from the end of the run queue */ + kid = delruntail(Pdebug); + n = snprint(buf, sizeof buf, "new %d", kid->pid); + qproduce(ctl->q, buf, n); + buf[0] = '\0'; + } + if(op == ILOAD) { + n = snprint(buf, sizeof buf, "load %s", string2c(*(String**)R.s)); + qproduce(ctl->q, buf, n); + buf[0] = '\0'; + } + + /* + * check for returns at big steps + */ + if(op == IRET) + R.IC = 1; + + /* + * check for blocked progs + */ + if(R.IC == 1 && p != currun()) + dbgblock(p); + if(ctl->stop) + R.IC = 1; + R.IC--; + if(ctl->bpts == nil) + continue; + pc = R.PC - R.M->prog; + for(b = ctl->bpts; b != nil; b = b->next) { + if(pc == b->pc && + (strcmp(R.M->m->path, b->path) == 0 || + strcmp(R.M->m->path, b->file) == 0)) { + strcpy(buf, "breakpoint"); + goto save; + } + } + } + +save: + if(ctl->stop) + strcpy(buf, "stopped"); + + p->R = R; + + if(env->debug == nil) { + poperror(); + return; + } + + if(p == currun()) + delrun(Pdebug); + + telldbg(env->debug, buf); + poperror(); + closedbgctl(env->debug, p); +} + +Dev progdevtab = { + 'p', + "prog", + + devreset, + devinit, + devshutdown, + progattach, + progwalk, + progstat, + progopen, + devcreate, + progclose, + progread, + devbread, + progwrite, + devbwrite, + devremove, + progwstat, +}; diff --git a/os/port/devroot.c b/os/port/devroot.c new file mode 100644 index 00000000..02fd8833 --- /dev/null +++ b/os/port/devroot.c @@ -0,0 +1,153 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +extern Rootdata rootdata[]; +extern Dirtab roottab[]; +extern int rootmaxq; + +static Chan* +rootattach(char *spec) +{ + int i; + ulong len; + Rootdata *r; + + if(*spec) + error(Ebadspec); + for (i = 0; i < rootmaxq; i++){ + r = &rootdata[i]; + if (r->sizep){ + len = *r->sizep; + r->size = len; + roottab[i].length = len; + } + } + return devattach('/', spec); +} + +static int +rootgen(Chan *c, char *name, Dirtab *tab, int nd, int s, Dir *dp) +{ + int p, i; + Rootdata *r; + + if(s == DEVDOTDOT){ + p = rootdata[c->qid.path].dotdot; + c->qid.path = p; + c->qid.type = QTDIR; + name = "#/"; + if(p != 0){ + for(i = 0; i < rootmaxq; i++) + if(roottab[i].qid.path == c->qid.path){ + name = roottab[i].name; + break; + } + } + devdir(c, c->qid, name, 0, eve, 0555, dp); + return 1; + } + if(name != nil){ + isdir(c); + r = &rootdata[(int)c->qid.path]; + tab = r->ptr; + for(i=0; isize; i++, tab++) + if(strcmp(tab->name, name) == 0){ + devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp); + return 1; + } + return -1; + } + if(s >= nd) + return -1; + tab += s; + devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp); + return 1; +} + +static Walkqid* +rootwalk(Chan *c, Chan *nc, char **name, int nname) +{ + ulong p; + + p = c->qid.path; + if(nname == 0) + p = rootdata[p].dotdot; + return devwalk(c, nc, name, nname, rootdata[p].ptr, rootdata[p].size, rootgen); +} + +static int +rootstat(Chan *c, uchar *dp, int n) +{ + int p; + + p = rootdata[c->qid.path].dotdot; + return devstat(c, dp, n, rootdata[p].ptr, rootdata[p].size, rootgen); +} + +static Chan* +rootopen(Chan *c, int omode) +{ + int p; + + p = rootdata[c->qid.path].dotdot; + return devopen(c, omode, rootdata[p].ptr, rootdata[p].size, rootgen); +} + +/* + * sysremove() knows this is a nop + */ +static void +rootclose(Chan*) +{ +} + +static long +rootread(Chan *c, void *buf, long n, vlong offset) +{ + ulong p, len; + uchar *data; + + p = c->qid.path; + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, rootdata[p].ptr, rootdata[p].size, rootgen); + len = rootdata[p].size; + if(offset < 0 || offset >= len) + return 0; + if(offset+n > len) + n = len - offset; + data = rootdata[p].ptr; + memmove(buf, data+offset, n); + return n; +} + +static long +rootwrite(Chan*, void*, long, vlong) +{ + error(Eperm); + return 0; +} + +Dev rootdevtab = { + '/', + "root", + + devreset, + devinit, + devshutdown, + rootattach, + rootwalk, + rootstat, + rootopen, + devcreate, + rootclose, + rootread, + devbread, + rootwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devsd.c b/os/port/devsd.c new file mode 100644 index 00000000..5bdc4048 --- /dev/null +++ b/os/port/devsd.c @@ -0,0 +1,1474 @@ +/* + * Storage Device. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +#include "../port/sd.h" + +extern Dev sddevtab; +extern SDifc* sdifc[]; + +typedef struct SDevgrp { + SDev* dev; + int nunits; /* num units in dev */ +} SDevgrp; + +static SDevgrp* devs; /* all devices */ +static QLock devslock; /* insertion and removal of devices */ +static int ndevs; /* total number of devices in the system */ + +enum { + Rawcmd, + Rawdata, + Rawstatus, +}; + +enum { + Qtopdir = 1, /* top level directory */ + Qtopbase, + Qtopctl = Qtopbase, + Qtopstat, + + Qunitdir, /* directory per unit */ + Qunitbase, + Qctl = Qunitbase, + Qraw, + Qpart, + + TypeLOG = 4, + NType = (1<>TypeSHIFT) & TypeMASK) +#define PART(q) ((((ulong)(q).path)>>PartSHIFT) & PartMASK) +#define UNIT(q) ((((ulong)(q).path)>>UnitSHIFT) & UnitMASK) +#define DEV(q) ((((ulong)(q).path)>>DevSHIFT) & DevMASK) +#define QID(d,u, p, t) (((d)<part != nil){ + partno = -1; + for(i = 0; i < unit->npart; i++){ + pp = &unit->part[i]; + if(!pp->valid){ + if(partno == -1) + partno = i; + break; + } + if(strcmp(name, pp->name) == 0){ + if(pp->start == start && pp->end == end) + return; + error(Ebadctl); + } + } + } + else{ + if((unit->part = malloc(sizeof(SDpart)*SDnpart)) == nil) + error(Enomem); + unit->npart = SDnpart; + partno = 0; + } + + /* + * If no free slot found then increase the + * array size (can't get here with unit->part == nil). + */ + if(partno == -1){ + if(unit->npart >= NPart) + error(Enomem); + if((pp = malloc(sizeof(SDpart)*(unit->npart+SDnpart))) == nil) + error(Enomem); + memmove(pp, unit->part, sizeof(SDpart)*unit->npart); + free(unit->part); + unit->part = pp; + partno = unit->npart; + unit->npart += SDnpart; + } + + /* + * Check size and extent are valid. + */ + if(start > end || end > unit->sectors) + error(Eio); + pp = &unit->part[partno]; + pp->start = start; + pp->end = end; + kstrdup(&pp->name, name); + kstrdup(&pp->user, eve); + pp->perm = 0640; + pp->valid = 1; +} + +static void +sddelpart(SDunit* unit, char* name) +{ + int i; + SDpart *pp; + + /* + * Look for the partition to delete. + * Can't delete if someone still has it open. + */ + pp = unit->part; + for(i = 0; i < unit->npart; i++){ + if(strcmp(name, pp->name) == 0) + break; + pp++; + } + if(i >= unit->npart) + error(Ebadctl); + if(strcmp(up->env->user, pp->user) && !iseve()) + error(Eperm); + pp->valid = 0; + pp->vers++; +} + +static int +sdinitpart(SDunit* unit) +{ + int i, nf; + ulong start, end; + char *f[4], *p, *q, buf[10]; + + unit->vers++; + unit->sectors = unit->secsize = 0; + if(unit->part){ + for(i = 0; i < unit->npart; i++){ + unit->part[i].valid = 0; + unit->part[i].vers++; + } + } + + if(unit->inquiry[0] & 0xC0) + return 0; + switch(unit->inquiry[0] & 0x1F){ + case 0x00: /* DA */ + case 0x04: /* WORM */ + case 0x05: /* CD-ROM */ + case 0x07: /* MO */ + break; + default: + return 0; + } + + if(unit->dev->ifc->online) + unit->dev->ifc->online(unit); + if(unit->sectors){ + sdaddpart(unit, "data", 0, unit->sectors); + + /* + * Use partitions passed from boot program, + * e.g. + * sdC0part=dos 63 123123/plan9 123123 456456 + * This happens before /boot sets hostname so the + * partitions will have the null-string for user. + * The gen functions patch it up. + */ + snprint(buf, sizeof buf, "%spart", unit->name); + for(p = getconf(buf); p != nil; p = q){ + if(q = strchr(p, '/')) + *q++ = '\0'; + nf = tokenize(p, f, nelem(f)); + if(nf < 3) + continue; + + start = strtoul(f[1], 0, 0); + end = strtoul(f[2], 0, 0); + if(!waserror()){ + sdaddpart(unit, f[0], start, end); + poperror(); + } + } + } + + return 1; +} + +static SDev* +sdgetdev(int idno) +{ + SDev *sdev; + int i; + + qlock(&devslock); + for(i = 0; i != ndevs; i++) + if(devs[i].dev->idno == idno) + break; + + if(i == ndevs) + sdev = nil; + else{ + sdev = devs[i].dev; + incref(&sdev->r); + } + qunlock(&devslock); + return sdev; +} + +static SDunit* +sdgetunit(SDev* sdev, int subno) +{ + SDunit *unit; + char buf[32]; + + /* + * Associate a unit with a given device and sub-unit + * number on that device. + * The device will be probed if it has not already been + * successfully accessed. + */ + qlock(&sdev->unitlock); + if(subno > sdev->nunit){ + qunlock(&sdev->unitlock); + return nil; + } + + unit = sdev->unit[subno]; + if(unit == nil){ + /* + * Probe the unit only once. This decision + * may be a little severe and reviewed later. + */ + if(sdev->unitflg[subno]){ + qunlock(&sdev->unitlock); + return nil; + } + if((unit = malloc(sizeof(SDunit))) == nil){ + qunlock(&sdev->unitlock); + return nil; + } + sdev->unitflg[subno] = 1; + + snprint(buf, sizeof(buf), "%s%d", sdev->name, subno); + kstrdup(&unit->name, buf); + kstrdup(&unit->user, eve); + unit->perm = 0555; + unit->subno = subno; + unit->dev = sdev; + + if(sdev->enabled == 0 && sdev->ifc->enable) + sdev->ifc->enable(sdev); + sdev->enabled = 1; + + /* + * No need to lock anything here as this is only + * called before the unit is made available in the + * sdunit[] array. + */ + if(unit->dev->ifc->verify(unit) == 0){ + qunlock(&sdev->unitlock); + free(unit); + return nil; + } + sdev->unit[subno] = unit; + } + qunlock(&sdev->unitlock); + return unit; +} + +static void +sdreset(void) +{ + int i; + SDev *sdev, *tail, *sdlist; + + /* + * Probe all configured controllers and make a list + * of devices found, accumulating a possible maximum number + * of units attached and marking each device with an index + * into the linear top-level directory array of units. + */ + tail = sdlist = nil; + for(i = 0; sdifc[i] != nil; i++){ + if(sdifc[i]->pnp == nil || (sdev = sdifc[i]->pnp()) == nil) + continue; + if(sdlist != nil) + tail->next = sdev; + else + sdlist = sdev; + for(tail = sdev; tail->next != nil; tail = tail->next){ + tail->unit = (SDunit**)malloc(tail->nunit * sizeof(SDunit*)); + tail->unitflg = (int*)malloc(tail->nunit * sizeof(int)); + assert(tail->unit && tail->unitflg); + ndevs++; + } + tail->unit = (SDunit**)malloc(tail->nunit * sizeof(SDunit*)); + tail->unitflg = (int*)malloc(tail->nunit * sizeof(int)); + ndevs++; + } + + /* + * Legacy and option code goes here. This will be hard... + */ + + /* + * The maximum number of possible units is known, allocate + * placeholders for their datastructures; the units will be + * probed and structures allocated when attached. + * Allocate controller names for the different types. + */ + if(ndevs == 0) + return; + for(i = 0; sdifc[i] != nil; i++){ + /* + * BUG: no check is made here or later when a + * unit is attached that the id and name are set. + */ + if(sdifc[i]->id) + sdifc[i]->id(sdlist); + } + + /* + * The IDs have been set, unlink the sdlist and copy the spec to + * the devtab. + */ + devs = (SDevgrp*)malloc(ndevs * sizeof(SDevgrp)); + memset(devs, 0, ndevs * sizeof(SDevgrp)); + i = 0; + while(sdlist != nil){ + devs[i].dev = sdlist; + devs[i].nunits = sdlist->nunit; + sdlist = sdlist->next; + devs[i].dev->next = nil; + i++; + } +} + +static int +sd2gen(Chan* c, int i, Dir* dp) +{ + Qid q; + vlong l; + SDpart *pp; + SDperm *perm; + SDunit *unit; + SDev *sdev; + int rv; + + sdev = sdgetdev(DEV(c->qid)); + assert(sdev); + unit = sdev->unit[UNIT(c->qid)]; + + rv = -1; + switch(i){ + case Qctl: + mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qctl), + unit->vers, QTFILE); + perm = &unit->ctlperm; + if(emptystr(perm->user)){ + kstrdup(&perm->user, eve); + perm->perm = 0640; + } + devdir(c, q, "ctl", 0, perm->user, perm->perm, dp); + rv = 1; + break; + + case Qraw: + mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qraw), + unit->vers, QTFILE); + perm = &unit->rawperm; + if(emptystr(perm->user)){ + kstrdup(&perm->user, eve); + perm->perm = DMEXCL|0600; + } + devdir(c, q, "raw", 0, perm->user, perm->perm, dp); + rv = 1; + break; + + case Qpart: + pp = &unit->part[PART(c->qid)]; + l = (pp->end - pp->start) * (vlong)unit->secsize; + mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qpart), + unit->vers+pp->vers, QTFILE); + if(emptystr(pp->user)) + kstrdup(&pp->user, eve); + devdir(c, q, pp->name, l, pp->user, pp->perm, dp); + rv = 1; + break; + } + + decref(&sdev->r); + return rv; +} + +static int +sd1gen(Chan* c, int i, Dir* dp) +{ + Qid q; + + switch(i){ + case Qtopctl: + mkqid(&q, QID(0, 0, 0, Qtopctl), 0, QTFILE); + devdir(c, q, "sdctl", 0, eve, 0640, dp); + return 1; + case Qtopstat: + mkqid(&q, QID(0, 0, 0, Qtopstat), 0, QTFILE); + devdir(c, q, "sdstat", 0, eve, 0640, dp); + return 1; + } + return -1; +} + +static int +sdgen(Chan* c, char*, Dirtab*, int, int s, Dir* dp) +{ + Qid q; + vlong l; + int i, r; + SDpart *pp; + SDunit *unit; + SDev *sdev; + + switch(TYPE(c->qid)){ + case Qtopdir: + if(s == DEVDOTDOT){ + mkqid(&q, QID(0, 0, 0, Qtopdir), 0, QTDIR); + sprint(up->genbuf, "#%C", sddevtab.dc); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + + if(s == 0 || s == 1) + return sd1gen(c, s + Qtopbase, dp); + s -= 2; + + qlock(&devslock); + for(i = 0; i != ndevs; i++){ + if(s < devs[i].nunits) + break; + s -= devs[i].nunits; + } + + if(i == ndevs){ + /* Run of the end of the list */ + qunlock(&devslock); + return -1; + } + + if((sdev = devs[i].dev) == nil){ + qunlock(&devslock); + return 0; + } + + incref(&sdev->r); + qunlock(&devslock); + + if((unit = sdev->unit[s]) == nil) + if((unit = sdgetunit(sdev, s)) == nil){ + decref(&sdev->r); + return 0; + } + + mkqid(&q, QID(sdev->idno, s, 0, Qunitdir), 0, QTDIR); + if(emptystr(unit->user)) + kstrdup(&unit->user, eve); + devdir(c, q, unit->name, 0, unit->user, unit->perm, dp); + decref(&sdev->r); + return 1; + + case Qunitdir: + if(s == DEVDOTDOT){ + mkqid(&q, QID(0, 0, 0, Qtopdir), 0, QTDIR); + sprint(up->genbuf, "#%C", sddevtab.dc); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + + if((sdev = sdgetdev(DEV(c->qid))) == nil){ + devdir(c, q, "unavailable", 0, eve, 0, dp); + return 1; + } + + unit = sdev->unit[UNIT(c->qid)]; + qlock(&unit->ctl); + + /* + * Check for media change. + * If one has already been detected, sectors will be zero. + * If there is one waiting to be detected, online + * will return > 1. + * Online is a bit of a large hammer but does the job. + */ + if(unit->sectors == 0 + || (unit->dev->ifc->online && unit->dev->ifc->online(unit) > 1)) + sdinitpart(unit); + + i = s+Qunitbase; + if(i < Qpart){ + r = sd2gen(c, i, dp); + qunlock(&unit->ctl); + decref(&sdev->r); + return r; + } + i -= Qpart; + if(unit->part == nil || i >= unit->npart){ + qunlock(&unit->ctl); + decref(&sdev->r); + break; + } + pp = &unit->part[i]; + if(!pp->valid){ + qunlock(&unit->ctl); + decref(&sdev->r); + return 0; + } + l = (pp->end - pp->start) * (vlong)unit->secsize; + mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), i, Qpart), + unit->vers+pp->vers, QTFILE); + if(emptystr(pp->user)) + kstrdup(&pp->user, eve); + devdir(c, q, pp->name, l, pp->user, pp->perm, dp); + qunlock(&unit->ctl); + decref(&sdev->r); + return 1; + case Qraw: + case Qctl: + case Qpart: + if((sdev = sdgetdev(DEV(c->qid))) == nil){ + devdir(c, q, "unavailable", 0, eve, 0, dp); + return 1; + } + unit = sdev->unit[UNIT(c->qid)]; + qlock(&unit->ctl); + r = sd2gen(c, TYPE(c->qid), dp); + qunlock(&unit->ctl); + decref(&sdev->r); + return r; + case Qtopctl: + case Qtopstat: + return sd1gen(c, TYPE(c->qid), dp); + default: + break; + } + + return -1; +} + +static Chan* +sdattach(char* spec) +{ + Chan *c; + char *p; + SDev *sdev; + int idno, subno, i; + + if(ndevs == 0 || *spec == '\0'){ + c = devattach(sddevtab.dc, spec); + mkqid(&c->qid, QID(0, 0, 0, Qtopdir), 0, QTDIR); + return c; + } + + if(spec[0] != 's' || spec[1] != 'd') + error(Ebadspec); + idno = spec[2]; + subno = strtol(&spec[3], &p, 0); + if(p == &spec[3]) + error(Ebadspec); + + qlock(&devslock); + for (sdev = nil, i = 0; i != ndevs; i++) + if((sdev = devs[i].dev) != nil && sdev->idno == idno) + break; + + if(i == ndevs || subno >= sdev->nunit || sdgetunit(sdev, subno) == nil){ + qunlock(&devslock); + error(Enonexist); + } + incref(&sdev->r); + qunlock(&devslock); + + c = devattach(sddevtab.dc, spec); + mkqid(&c->qid, QID(sdev->idno, subno, 0, Qunitdir), 0, QTDIR); + c->dev = (sdev->idno << UnitLOG) + subno; + decref(&sdev->r); + return c; +} + +static Walkqid* +sdwalk(Chan* c, Chan* nc, char** name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, sdgen); +} + +static int +sdstat(Chan* c, uchar* db, int n) +{ + return devstat(c, db, n, nil, 0, sdgen); +} + +static Chan* +sdopen(Chan* c, int omode) +{ + SDpart *pp; + SDunit *unit; + SDev *sdev; + uchar tp; + + c = devopen(c, omode, 0, 0, sdgen); + if((tp = TYPE(c->qid)) != Qctl && tp != Qraw && tp != Qpart) + return c; + + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil) + error(Enonexist); + unit = sdev->unit[UNIT(c->qid)]; + + switch(TYPE(c->qid)){ + case Qctl: + c->qid.vers = unit->vers; + break; + case Qraw: + c->qid.vers = unit->vers; + if(_tas(&unit->rawinuse) != 0){ + c->flag &= ~COPEN; + error(Einuse); + } + unit->state = Rawcmd; + break; + case Qpart: + qlock(&unit->ctl); + if(waserror()){ + qunlock(&unit->ctl); + c->flag &= ~COPEN; + nexterror(); + } + pp = &unit->part[PART(c->qid)]; + c->qid.vers = unit->vers+pp->vers; + qunlock(&unit->ctl); + poperror(); + break; + } + decref(&sdev->r); + return c; +} + +static void +sdclose(Chan* c) +{ + SDunit *unit; + SDev *sdev; + + if(c->qid.type & QTDIR) + return; + if(!(c->flag & COPEN)) + return; + + switch(TYPE(c->qid)){ + default: + break; + case Qraw: + sdev = sdgetdev(DEV(c->qid)); + if(sdev){ + unit = sdev->unit[UNIT(c->qid)]; + unit->rawinuse = 0; + decref(&sdev->r); + } + break; + } +} + +static long +sdbio(Chan* c, int write, char* a, long len, vlong off) +{ + int nchange; + long l; + uchar *b; + SDpart *pp; + SDunit *unit; + SDev *sdev; + ulong bno, max, nb, offset; + + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil) + error(Enonexist); + unit = sdev->unit[UNIT(c->qid)]; + if(unit == nil) + error(Enonexist); + + nchange = 0; + qlock(&unit->ctl); + while(waserror()){ + /* notification of media change; go around again */ + if(strcmp(up->env->errstr, Eio) == 0 && unit->sectors == 0 && nchange++ == 0){ + sdinitpart(unit); + continue; + } + + /* other errors; give up */ + qunlock(&unit->ctl); + decref(&sdev->r); + nexterror(); + } + pp = &unit->part[PART(c->qid)]; + if(unit->vers+pp->vers != c->qid.vers) + error(Eio); + + /* + * Check the request is within bounds. + * Removeable drives are locked throughout the I/O + * in case the media changes unexpectedly. + * Non-removeable drives are not locked during the I/O + * to allow the hardware to optimise if it can; this is + * a little fast and loose. + * It's assumed that non-removeable media parameters + * (sectors, secsize) can't change once the drive has + * been brought online. + */ + bno = (off/unit->secsize) + pp->start; + nb = ((off+len+unit->secsize-1)/unit->secsize) + pp->start - bno; + max = SDmaxio/unit->secsize; + if(nb > max) + nb = max; + if(bno+nb > pp->end) + nb = pp->end - bno; + if(bno >= pp->end || nb == 0){ + if(write) + error(Eio); + qunlock(&unit->ctl); + decref(&sdev->r); + poperror(); + return 0; + } + if(!(unit->inquiry[1] & 0x80)){ + qunlock(&unit->ctl); + poperror(); + } + + b = sdmalloc(nb*unit->secsize); + if(b == nil) + error(Enomem); + if(waserror()){ + sdfree(b); + if(!(unit->inquiry[1] & 0x80)) + decref(&sdev->r); /* gadverdamme! */ + nexterror(); + } + + offset = off%unit->secsize; + if(offset+len > nb*unit->secsize) + len = nb*unit->secsize - offset; + if(write){ + if(offset || (len%unit->secsize)){ + l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno); + if(l < 0) + error(Eio); + if(l < (nb*unit->secsize)){ + nb = l/unit->secsize; + l = nb*unit->secsize - offset; + if(len > l) + len = l; + } + } + memmove(b+offset, a, len); + l = unit->dev->ifc->bio(unit, 0, 1, b, nb, bno); + if(l < 0) + error(Eio); + if(l < offset) + len = 0; + else if(len > l - offset) + len = l - offset; + } + else{ + l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno); + if(l < 0) + error(Eio); + if(l < offset) + len = 0; + else if(len > l - offset) + len = l - offset; + memmove(a, b+offset, len); + } + sdfree(b); + poperror(); + + if(unit->inquiry[1] & 0x80){ + qunlock(&unit->ctl); + poperror(); + } + + decref(&sdev->r); + return len; +} + +static long +sdrio(SDreq* r, void* a, long n) +{ + void *data; + + if(n >= SDmaxio || n < 0) + error(Etoobig); + + data = nil; + if(n){ + if((data = sdmalloc(n)) == nil) + error(Enomem); + if(r->write) + memmove(data, a, n); + } + r->data = data; + r->dlen = n; + + if(waserror()){ + if(data != nil){ + sdfree(data); + r->data = nil; + } + nexterror(); + } + + if(r->unit->dev->ifc->rio(r) != SDok) + error(Eio); + + if(!r->write && r->rlen > 0) + memmove(a, data, r->rlen); + if(data != nil){ + sdfree(data); + r->data = nil; + } + poperror(); + + return r->rlen; +} + +static long +sdread(Chan *c, void *a, long n, vlong off) +{ + char *p, *e, *buf; + SDpart *pp; + SDunit *unit; + SDev *sdev; + ulong offset; + int i, l, status; + + offset = off; + switch(TYPE(c->qid)){ + default: + error(Eperm); + case Qtopstat: + p = buf = malloc(READSTR); + assert(p); + e = p + READSTR; + qlock(&devslock); + for(i = 0; i != ndevs; i++){ + SDev *sdev = devs[i].dev; + + if(sdev->ifc->stat) + p = sdev->ifc->stat(sdev, p, e); + else + p = seprint(e, "%s; no statistics available\n", sdev->name); + } + qunlock(&devslock); + n = readstr(off, a, n, buf); + free(buf); + return n; + + case Qtopdir: + case Qunitdir: + return devdirread(c, a, n, 0, 0, sdgen); + + case Qctl: + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil) + error(Enonexist); + + unit = sdev->unit[UNIT(c->qid)]; + p = malloc(READSTR); + l = snprint(p, READSTR, "inquiry %.48s\n", + (char*)unit->inquiry+8); + qlock(&unit->ctl); + /* + * If there's a device specific routine it must + * provide all information pertaining to night geometry + * and the garscadden trains. + */ + if(unit->dev->ifc->rctl) + l += unit->dev->ifc->rctl(unit, p+l, READSTR-l); + if(unit->sectors == 0) + sdinitpart(unit); + if(unit->sectors){ + if(unit->dev->ifc->rctl == nil) + l += snprint(p+l, READSTR-l, + "geometry %ld %ld\n", + unit->sectors, unit->secsize); + pp = unit->part; + for(i = 0; i < unit->npart; i++){ + if(pp->valid) + l += snprint(p+l, READSTR-l, + "part %s %lud %lud\n", + pp->name, pp->start, pp->end); + pp++; + } + } + qunlock(&unit->ctl); + decref(&sdev->r); + l = readstr(offset, a, n, p); + free(p); + return l; + + case Qraw: + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil) + error(Enonexist); + + unit = sdev->unit[UNIT(c->qid)]; + qlock(&unit->raw); + if(waserror()){ + qunlock(&unit->raw); + decref(&sdev->r); + nexterror(); + } + if(unit->state == Rawdata){ + unit->state = Rawstatus; + i = sdrio(unit->req, a, n); + } + else if(unit->state == Rawstatus){ + status = unit->req->status; + unit->state = Rawcmd; + free(unit->req); + unit->req = nil; + i = readnum(0, a, n, status, NUMSIZE); + } else + i = 0; + qunlock(&unit->raw); + decref(&sdev->r); + poperror(); + return i; + + case Qpart: + return sdbio(c, 0, a, n, off); + } + + return 0; +} + +typedef struct Confdata Confdata; +struct Confdata { + int on; + char* spec; + DevConf cf; +}; + +static void +parseswitch(Confdata* cd, char* option) +{ + if(!strcmp("on", option)) + cd->on = 1; + else if(!strcmp("off", option)) + cd->on = 0; + else + error(Ebadarg); +} + +static void +parsespec(Confdata* cd, char* option) +{ + if(strlen(option) > 1) + error(Ebadarg); + cd->spec = option; +} + +static Devport* +getnewport(DevConf* dc) +{ + Devport *p; + + p = (Devport *)malloc((dc->nports + 1) * sizeof(Devport)); + if(dc->nports > 0){ + memmove(p, dc->ports, dc->nports * sizeof(Devport)); + free(dc->ports); + } + dc->ports = p; + p = &dc->ports[dc->nports++]; + p->size = -1; + p->port = (ulong)-1; + return p; +} + +static void +parseport(Confdata* cd, char* option) +{ + char *e; + Devport *p; + + if(cd->cf.nports == 0 || cd->cf.ports[cd->cf.nports-1].port != (ulong)-1) + p = getnewport(&cd->cf); + else + p = &cd->cf.ports[cd->cf.nports-1]; + p->port = strtol(option, &e, 0); + if(e == nil || *e != '\0') + error(Ebadarg); +} + +static void +parsesize(Confdata* cd, char* option) +{ + char *e; + Devport *p; + + if(cd->cf.nports == 0 || cd->cf.ports[cd->cf.nports-1].size != -1) + p = getnewport(&cd->cf); + else + p = &cd->cf.ports[cd->cf.nports-1]; + p->size = (int)strtol(option, &e, 0); + if(e == nil || *e != '\0') + error(Ebadarg); +} + +static void +parseirq(Confdata* cd, char* option) +{ + char *e; + + cd->cf.intnum = strtoul(option, &e, 0); + if(e == nil || *e != '\0') + error(Ebadarg); +} + +static void +parsetype(Confdata* cd, char* option) +{ + cd->cf.type = option; +} + +static struct { + char *option; + void (*parse)(Confdata*, char*); +} options[] = { + { "switch", parseswitch, }, + { "spec", parsespec, }, + { "port", parseport, }, + { "size", parsesize, }, + { "irq", parseirq, }, + { "type", parsetype, }, +}; + +static long +sdwrite(Chan* c, void* a, long n, vlong off) +{ + Cmdbuf *cb; + SDreq *req; + SDunit *unit; + SDev *sdev; + ulong end, start; + + switch(TYPE(c->qid)){ + default: + error(Eperm); + case Qtopctl: { + Confdata cd; + char buf[256], *field[Ncmd]; + int nf, i, j; + + memset(&cd, 0, sizeof(Confdata)); + if(n > sizeof(buf)-1) n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = '\0'; + + cd.on = -1; + cd.spec = '\0'; + memset(&cd.cf, 0, sizeof(DevConf)); + + nf = tokenize(buf, field, Ncmd); + for(i = 0; i < nf; i++){ + char *opt = field[i++]; + if(i >= nf) + error(Ebadarg); + for(j = 0; j != nelem(options); j++) + if(!strcmp(opt, options[j].option)) + break; + + if(j == nelem(options)) + error(Ebadarg); + options[j].parse(&cd, field[i]); + } + + if(cd.on < 0) + error(Ebadarg); + + if(cd.on){ + if(cd.spec == '\0' || cd.cf.nports == 0 || + cd.cf.intnum == 0 || cd.cf.type == nil) + error(Ebadarg); + } + else{ + if(cd.spec == '\0') + error(Ebadarg); + } + + if(sddevtab.config == nil) + error("No configuration function"); + sddevtab.config(cd.on, cd.spec, &cd.cf); + break; + } + case Qctl: + cb = parsecmd(a, n); + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil) + error(Enonexist); + unit = sdev->unit[UNIT(c->qid)]; + + qlock(&unit->ctl); + if(waserror()){ + qunlock(&unit->ctl); + decref(&sdev->r); + free(cb); + nexterror(); + } + if(unit->vers != c->qid.vers) + error(Eio); + + if(cb->nf < 1) + error(Ebadctl); + if(strcmp(cb->f[0], "part") == 0){ + if(cb->nf != 4) + error(Ebadctl); + if(unit->sectors == 0 && !sdinitpart(unit)) + error(Eio); + start = strtoul(cb->f[2], 0, 0); + end = strtoul(cb->f[3], 0, 0); + sdaddpart(unit, cb->f[1], start, end); + } + else if(strcmp(cb->f[0], "delpart") == 0){ + if(cb->nf != 2 || unit->part == nil) + error(Ebadctl); + sddelpart(unit, cb->f[1]); + } + else if(unit->dev->ifc->wctl) + unit->dev->ifc->wctl(unit, cb); + else + error(Ebadctl); + qunlock(&unit->ctl); + decref(&sdev->r); + poperror(); + free(cb); + break; + + case Qraw: + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil) + error(Enonexist); + unit = sdev->unit[UNIT(c->qid)]; + qlock(&unit->raw); + if(waserror()){ + qunlock(&unit->raw); + decref(&sdev->r); + nexterror(); + } + switch(unit->state){ + case Rawcmd: + if(n < 6 || n > sizeof(req->cmd)) + error(Ebadarg); + if((req = malloc(sizeof(SDreq))) == nil) + error(Enomem); + req->unit = unit; + memmove(req->cmd, a, n); + req->clen = n; + req->flags = SDnosense; + req->status = ~0; + + unit->req = req; + unit->state = Rawdata; + break; + + case Rawstatus: + unit->state = Rawcmd; + free(unit->req); + unit->req = nil; + error(Ebadusefd); + + case Rawdata: + if(unit->state != Rawdata) + error(Ebadusefd); + unit->state = Rawstatus; + + unit->req->write = 1; + n = sdrio(unit->req, a, n); + } + qunlock(&unit->raw); + decref(&sdev->r); + poperror(); + break; + case Qpart: + return sdbio(c, 1, a, n, off); + } + + return n; +} + +static int +sdwstat(Chan* c, uchar* dp, int n) +{ + Dir *d; + SDpart *pp; + SDperm *perm; + SDunit *unit; + SDev *sdev; + + if(c->qid.type & QTDIR) + error(Eperm); + + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil) + error(Enonexist); + unit = sdev->unit[UNIT(c->qid)]; + qlock(&unit->ctl); + d = nil; + if(waserror()){ + free(d); + qunlock(&unit->ctl); + decref(&sdev->r); + nexterror(); + } + + switch(TYPE(c->qid)){ + default: + error(Eperm); + case Qctl: + perm = &unit->ctlperm; + break; + case Qraw: + perm = &unit->rawperm; + break; + case Qpart: + pp = &unit->part[PART(c->qid)]; + if(unit->vers+pp->vers != c->qid.vers) + error(Enonexist); + perm = &pp->SDperm; + break; + } + + if(strcmp(up->env->user, perm->user) && !iseve()) + error(Eperm); + + d = smalloc(sizeof(Dir)+n); + n = convM2D(dp, n, &d[0], (char*)&d[1]); + if(n == 0) + error(Eshortstat); + if(!emptystr(d[0].uid)) + kstrdup(&perm->user, d[0].uid); + if(d[0].mode != ~0UL) + perm->perm = (perm->perm & ~0777) | (d[0].mode & 0777); + + free(d); + qunlock(&unit->ctl); + decref(&sdev->r); + poperror(); + return n; +} + +static char +getspec(char base) +{ + while(1){ + int i; + SDev *sdev; + + for(i = 0; i != ndevs; i++) + if((sdev = devs[i].dev) != nil && (char)sdev->idno == base) + break; + + if(i == ndevs) + return base; + base++; + } + return '\0'; +} + +static int +configure(char* spec, DevConf* cf) +{ + ISAConf isa; + SDevgrp *tmpdevs; + SDev *tail, *sdev, *(*probe)(DevConf*); + char *p, name[32]; + int i, nnew; + + if((p = strchr(cf->type, '/')) != nil) + *p++ = '\0'; + + for(i = 0; sdifc[i] != nil; i++) + if(!strcmp(sdifc[i]->name, cf->type)) + break; + + if(sdifc[i] == nil) + error("type not found"); + + if((probe = sdifc[i]->probe) == nil) + error("No probe function"); + + if(p){ + /* Try to find the card on the ISA bus. This code really belongs + in sdata and I'll move it later. Really! */ + memset(&isa, 0, sizeof(isa)); + isa.port = cf->ports[0].port; + isa.irq = cf->intnum; + + if(pcmspecial(p, &isa) < 0) + error("Cannot find controller"); + } + + qlock(&devslock); + if(waserror()){ + qunlock(&devslock); + nexterror(); + } + + for(i = 0; i != ndevs; i++) + if((sdev = devs[i].dev) != nil && sdev->idno == *spec) + break; + if(i != ndevs) + error(Eexist); + + if((sdev = (*probe)(cf)) == nil) + error("Cannot probe controller"); + poperror(); + + nnew = 0; + tail = sdev; + while(tail){ + nnew++; + tail = tail->next; + } + + tmpdevs = (SDevgrp*)malloc((ndevs + nnew) * sizeof(SDevgrp)); + memmove(tmpdevs, devs, ndevs * sizeof(SDevgrp)); + free(devs); + devs = tmpdevs; + + while(sdev){ + /* Assign `spec' to the device */ + *spec = getspec(*spec); + snprint(name, sizeof(name), "sd%c", *spec); + kstrdup(&sdev->name, name); + sdev->idno = *spec; + sdev->unit = (SDunit **)malloc(sdev->nunit * sizeof(SDunit*)); + sdev->unitflg = (int *)malloc(sdev->nunit * sizeof(int)); + assert(sdev->unit && sdev->unitflg); + + devs[ndevs].dev = sdev; + devs[ndevs].nunits = sdev->nunit; + sdev = sdev->next; + devs[ndevs].dev->next = nil; + ndevs++; + } + + qunlock(&devslock); + return 0; +} + +static int +unconfigure(char* spec) +{ + int i; + SDev *sdev; + + qlock(&devslock); + if(waserror()){ + qunlock(&devslock); + nexterror(); + } + + sdev = nil; + for(i = 0; i != ndevs; i++) + if((sdev = devs[i].dev) != nil && sdev->idno == *spec) + break; + + if(i == ndevs) + error(Enonexist); + + if(sdev->r.ref) + error(Einuse); + + /* make sure no interrupts arrive anymore before removing resources */ + if(sdev->enabled && sdev->ifc->disable) + sdev->ifc->disable(sdev); + + /* we're alone and the device tab is locked; make the device unavailable */ + memmove(&devs[i], &devs[ndevs - 1], sizeof(SDevgrp)); + memset(&devs[ndevs - 1], 0, sizeof(SDevgrp)); + ndevs--; + + qunlock(&devslock); + poperror(); + + for(i = 0; i != sdev->nunit; i++) + if(sdev->unit[i]){ + SDunit *unit = sdev->unit[i]; + + free(unit->name); + free(unit->user); + free(unit); + } + + if(sdev->ifc->clear) + sdev->ifc->clear(sdev); + return 0; +} + +static int +sdconfig(int on, char* spec, DevConf* cf) +{ + if(on) + return configure(spec, cf); + return unconfigure(spec); +} + +Dev sddevtab = { + 'S', + "sd", + + sdreset, + devinit, + devshutdown, + sdattach, + sdwalk, + sdstat, + sdopen, + devcreate, + sdclose, + sdread, + devbread, + sdwrite, + devbwrite, + devremove, + sdwstat, + devpower, + sdconfig, +}; diff --git a/os/port/devsign.c b/os/port/devsign.c new file mode 100644 index 00000000..4fe8edb3 --- /dev/null +++ b/os/port/devsign.c @@ -0,0 +1,446 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "interp.h" +#include +#include "runt.h" +#include "mp.h" +#include "libsec.h" +#include "../../libkeyring/keys.h" + +/* + * experimental version of signed modules + */ + +enum +{ + Qdir, + Qkey, + Qctl, + + Maxkey = 2048 +}; + +static Dirtab signdir[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "signerkey", {Qkey}, 0, 0644, + "signerctl", {Qctl}, 0, 0600, +}; + +typedef struct Get Get; +struct Get { + uchar* p; + uchar* ep; +}; + +#define G32(b) ((b[0]<<24)|(b[1]<<16)|(b[2]<<8)|b[3]) + +static int vc(Get*); +static int vs(void*, int, Get*, int); +static Signerkey* findsignerkey(Skeyset*, char*, int, char*); +extern vlong osusectime(void); + +int +verifysigner(uchar *sign, int len, uchar *data, ulong ndata) +{ + Get sig; + int alg; + ulong issued, expires, now; + int footprint, r, n; + uchar buf[128], digest[SHA1dlen]; + DigestState *ds; + volatile struct {BigInt b;} b; + volatile struct {BigInt s;} s; + SigAlgVec *sa; + Signerkey *key; + Skeyset *sigs; + + /* alg[1] issued[4] expires[4] footprint[2] signer[n] sig[m] */ + sigs = up->env->sigs; + if(sigs == nil) + return 1; /* not enforcing signed modules */ + sig.p = sign; + sig.ep = sign+len; + alg = vc(&sig); + if(alg != 2) + return 0; /* we do only SHA1/RSA */ + sa = findsigalg("rsa"); + if(sa == nil) + return 0; + if(vs(buf, sizeof(buf), &sig, 4) < 0) + return 0; + now = osusectime()/1000000; + issued = G32(buf); + if(vs(buf, sizeof(buf), &sig, 4) < 0) + return 0; + if(issued != 0 && now < issued) + return 0; + expires = G32(buf); + if(expires != 0 && now >= expires) + return 0; + footprint = vc(&sig) << 8; + footprint |= vc(&sig); + if(footprint < 0) + return 0; + r = 0; + b.b = nil; + s.s = nil; + qlock(sigs); + if(waserror()) + goto out; + if((n = vs(buf, sizeof(buf)-NUMSIZE-1, &sig, -1)) < 0) /* owner */ + goto out; + buf[n] = 0; + key = findsignerkey(sigs, sa->name, footprint, (char*)buf); + if(key == nil) + goto out; + n += snprint((char*)buf+n, NUMSIZE, " %lud", expires); + ds = sha1(buf, n, nil, nil); + sha1(data, ndata, digest, ds); + b.b = betomp(digest, SHA1dlen, nil); + if(b.b == nil) + goto out; + s.s = betomp(sig.p, sig.ep-sig.p, nil); + if(s.s == nil) + goto out; + r = (*sa->verify)(b.b, s.s, key->pk); +out: + qunlock(sigs); + if(b.b != nil) + mpfree(b.b); + if(s.s != nil) + mpfree(s.s); + return r; +} + +int +mustbesigned(char *path, uchar*, ulong, Dir *dir) +{ + USED(path); +if(0)print("load %s: %d %C\n", path, up->env->sigs!=nil, dir==nil?'?':dir->type); + /* allow only signed modules and those in #/; already loaded modules are reloaded from cache */ + return up->env->sigs != nil && (dir == nil || dir->type != '/'); +} + +static int +vc(Get *g) +{ + return g->p < g->ep? *g->p++: -1; +} + +static int +vs(void *s, int lim, Get *g, int n) +{ + int nr; + + if(n < 0){ + if(g->p >= g->ep) + return -1; + n = *g->p++; + lim--; + } + if(n > lim) + return -1; + nr = g->ep - g->p; + if(n > nr) + return -1; + if(s != nil) + memmove(s, g->p, n); + g->p += n; + return n; +} + +static char* +cstring(char *str, char **strp) +{ + char *p, *s; + int n; + + p = strchr(str, '\n'); + if(p == 0) + p = str + strlen(str); + n = p - str; + s = malloc(n+1); + if(s == nil) + return nil; + memmove(s, str, n); + s[n] = 0; + + if(strp){ + if(*p) + p++; + *strp = p; + } + + return s; +} + +static SigAlgVec* +cstrtoalg(char *str, char **strp) +{ + int n; + char *p, name[KNAMELEN]; + + p = strchr(str, '\n'); + if(p == 0){ + p = str + strlen(str); + if(strp) + *strp = p; + } else { + if(strp) + *strp = p+1; + } + + n = p - str; + if(n >= sizeof(name)) + return nil; + strncpy(name, str, n); + name[n] = 0; + return findsigalg(name); +} + +static Signerkey* +strtopk(char *buf) +{ + SigAlgVec *sa; + char *p; + Signerkey *key; + + key = malloc(sizeof(*key)); + if(key == nil) + return nil; + key->ref = 1; + sa = cstrtoalg(buf, &p); + if(sa == nil){ + free(key); + return nil; + } + key->alg = sa; + key->pkfree = sa->pkfree; + key->owner = cstring(p, &p); + if(key->owner == nil){ + free(key); + return nil; + } + key->pk = (*sa->str2pk)(p, &p); + if(key->pk == nil){ + free(key->owner); + free(key); + return nil; + } + return key; +} + +static Signerkey* +findsignerkey(Skeyset *sigs, char *alg, int footprint, char *owner) +{ + int i; + Signerkey *key; + + for(i=0; inkey; i++){ + key = sigs->keys[i]; + if(key->footprint == footprint && + strcmp(alg, ((SigAlgVec*)key->alg)->name) == 0 && + strcmp(key->owner, owner) == 0) + return key; + } + return nil; +} + +static Chan* +signattach(char *spec) +{ + return devattach(L'Σ', spec); +} + +static Walkqid* +signwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, signdir, nelem(signdir), devgen); +} + +static int +signstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, signdir, nelem(signdir), devgen); +} + +static Chan* +signopen(Chan *c, int omode) +{ + if(c->qid.type & QTDIR) { + if(omode != OREAD) + error(Eisdir); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; + } + + switch((ulong)c->qid.path){ + case Qctl: + if(!iseve()) + error(Eperm); + break; + + case Qkey: + if(omode != OREAD && !iseve()) + error(Eperm); + break; + } + + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +signclose(Chan *c) +{ + USED(c); +} + +static long +signread(Chan *c, void *va, long n, vlong offset) +{ + char *buf, *p; + SigAlgVec *sa; + Skeyset *sigs; + Signerkey *key; + int i; + + if(c->qid.type & QTDIR) + return devdirread(c, va, n, signdir, nelem(signdir), devgen); + sigs = up->env->sigs; + if(sigs == nil) + return 0; + switch((ulong)c->qid.path){ + case Qkey: + buf = smalloc(Maxkey); + if(waserror()){ + free(buf); + nexterror(); + } + qlock(sigs); + if(waserror()){ + qunlock(sigs); + nexterror(); + } + p = buf; + for(i=0; inkey; i++){ + key = sigs->keys[i]; + sa = key->alg; + p = seprint(p, buf+Maxkey, "owner=%s alg=%s footprint=%ud expires=%lud\n", + key->owner, sa->name, key->footprint, key->expires); + } + poperror(); + qunlock(sigs); + n = readstr(offset, va, n, buf); + poperror(); + free(buf); + return n; + + case Qctl: + return readnum(offset, va, n, sigs->nkey, NUMSIZE); + } + return 0; +} + +static long +signwrite(Chan *c, void *va, long n, vlong offset) +{ + char *buf; + Skeyset *sigs; + Signerkey *okey, *key; + int i; + + if(c->qid.type & QTDIR) + error(Eisdir); + USED(offset); + switch((ulong)c->qid.path){ + case Qkey: + if(n >= Maxkey) + error(Etoobig); + buf = smalloc(Maxkey); + if(waserror()){ + free(buf); + nexterror(); + } + memmove(buf, va, n); + buf[n] = 0; + + key = strtopk(buf); + if(key == nil) + error("bad key syntax"); + poperror(); + free(buf); + + if(waserror()){ + freeskey(key); + nexterror(); + } + sigs = up->env->sigs; + if(sigs == nil){ + sigs = malloc(sizeof(*sigs)); + if(sigs == nil) + error(Enomem); + sigs->ref = 1; + up->env->sigs = sigs; + } + qlock(sigs); + if(waserror()){ + qunlock(sigs); + nexterror(); + } + for(i=0; inkey; i++){ + okey = sigs->keys[i]; + if(strcmp(okey->owner, key->owner) == 0){ + /* replace existing key */ + sigs->keys[i] = key; + freeskey(okey); + break; + } + } + if(i >= sigs->nkey){ + if(sigs->nkey >= nelem(sigs->keys)) + error("too many keys"); + sigs->keys[sigs->nkey++] = key; + } + poperror(); + qunlock(sigs); + poperror(); /* key */ + + return n; + case Qctl: + error(Ebadctl); + break; + } + return 0; +} + +Dev signdevtab = { + L'Σ', + "sign", + + devreset, + devinit, + devshutdown, + signattach, + signwalk, + signstat, + signopen, + devcreate, + signclose, + signread, + devbread, + signwrite, + devbwrite, + devremove, + devwstat +}; diff --git a/os/port/devsrv.c b/os/port/devsrv.c new file mode 100644 index 00000000..a80a3984 --- /dev/null +++ b/os/port/devsrv.c @@ -0,0 +1,726 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "interp.h" +#include "isa.h" +#include "runt.h" + +typedef struct SrvFile SrvFile; +struct SrvFile +{ + char* spec; + char* name; + char* user; + ulong perm; + vlong length; + Qid qid; + int ref; + int opens; + int flags; + Channel* read; + Channel* write; + SrvFile* entry; + SrvFile* dir; + SrvFile* devlist; +}; + +enum +{ + SORCLOSE = (1<<0), + SRDCLOSE = (1<<1), + SWRCLOSE = (1<<2), + SREMOVED = (1<<3), +}; + +typedef struct SrvDev SrvDev; +struct SrvDev +{ + Type* Rread; + Type* Rwrite; + QLock l; + ulong pathgen; + SrvFile* devices; +}; + +static SrvDev dev; + +void freechan(Heap*, int); +static void freerdchan(Heap*, int); +static void freewrchan(Heap*, int); + +Type *Trdchan; +Type *Twrchan; + +static int +srvgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp) +{ + SrvFile *f; + + if(s == DEVDOTDOT){ + devdir(c, c->qid, "#s", 0, eve, 0555, dp); + return 1; + } + f = c->aux; + if((c->qid.type & QTDIR) == 0){ + if(s > 0) + return -1; + devdir(c, f->qid, f->name, f->length, f->user, f->perm, dp); + return 1; + } + + for(f = f->entry; f != nil; f = f->entry){ + if(s-- == 0) + break; + } + if(f == nil) + return -1; + + devdir(c, f->qid, f->name, f->length, f->user, f->perm, dp); + return 1; +} + +static void +srvinit(void) +{ + static uchar rmap[] = Sys_Rread_map; + static uchar wmap[] = Sys_Rwrite_map; + + Trdchan = dtype(freerdchan, sizeof(Channel), Tchannel.map, Tchannel.np); + Twrchan = dtype(freewrchan, sizeof(Channel), Tchannel.map, Tchannel.np); + + dev.pathgen = 1; + dev.Rread = dtype(freeheap, Sys_Rread_size, rmap, sizeof(rmap)); + dev.Rwrite = dtype(freeheap, Sys_Rwrite_size, wmap, sizeof(wmap)); +} + +static int +srvchkattach(SrvFile *d) +{ + if(strcmp(d->user, up->env->user) == 0) + return 1; + + /* + * Need write permission in other to allow attaches if + * we are not the owner + */ + if(d->perm & 2) + return 1; + + return 0; +} + +static Chan* +srvattach(char *spec) +{ + Chan *c; + SrvFile *d; + + if(spec[0] != '\0'){ + qlock(&dev.l); + for(d = dev.devices; d != nil; d = d->devlist){ + if(strcmp(spec, d->spec) == 0){ + if(srvchkattach(d) == 0){ + qunlock(&dev.l); + error(Eperm); + } + d->ref++; + break; + } + } + qunlock(&dev.l); + + if(d != nil){ + c = devattach('s', spec); + c->aux = d; + c->qid = d->qid; + return c; + } + } + + d = malloc(sizeof(SrvFile)); + if(d == nil) + error(Enomem); + + c = devattach('s', spec); + + d->ref = 1; + kstrdup(&d->spec, spec); + kstrdup(&d->user, up->env->user); + snprint(up->genbuf, sizeof(up->genbuf), "srv%ld", up->env->pgrp->pgrpid); + kstrdup(&d->name, up->genbuf); + d->perm = DMDIR|0770; + + qlock(&dev.l); + mkqid(&d->qid, dev.pathgen++, 0, QTDIR); + d->devlist = dev.devices; + dev.devices = d; + qunlock(&dev.l); + + c->aux = d; + c->qid = d->qid; + + return c; +} + +static Walkqid* +srvwalk(Chan *c, Chan *nc, char **name, int nname) +{ + SrvFile *d, *pd; + Walkqid *w; + + pd = c->aux; + qlock(&dev.l); + if(waserror()){ + qunlock(&dev.l); + nexterror(); + } + + w = devwalk(c, nc, name, nname, nil, 0, srvgen); + if(w != nil && w->clone != nil){ + if(nname != 0){ + for(d = pd->entry; d != nil; d = d->entry) + if(d->qid.path == w->clone->qid.path) + break; + if(d == nil) + panic("srvwalk"); + if(w->clone == c) + pd->ref--; + }else + d = pd; + w->clone->aux = d; + d->ref++; + } + poperror(); + qunlock(&dev.l); + return w; +} + +static int +srvstat(Chan *c, uchar *db, int n) +{ + qlock(&dev.l); + if(waserror()){ + qunlock(&dev.l); + nexterror(); + } + n = devstat(c, db, n, 0, 0, srvgen); + poperror(); + qunlock(&dev.l); + return n; +} + +static Chan* +srvopen(Chan *c, int omode) +{ + SrvFile *sf; + + openmode(omode); /* check it */ + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Eisdir); + c->mode = omode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + + sf = c->aux; + + qlock(&dev.l); + if(waserror()){ + qunlock(&dev.l); + nexterror(); + } + devpermcheck(sf->user, sf->perm, omode); + if(omode&ORCLOSE && strcmp(sf->user, up->env->user) != 0) + error(Eperm); + if(sf->perm & DMEXCL && sf->opens != 0) + error(Einuse); + sf->opens++; + if(omode&ORCLOSE) + sf->flags |= SORCLOSE; + poperror(); + qunlock(&dev.l); + + c->offset = 0; + c->flag |= COPEN; + c->mode = openmode(omode); + + return c; +} + +static int +srvwstat(Chan *c, uchar *dp, int n) +{ + Dir *d; + SrvFile *sf, *f; + + sf = c->aux; + if(strcmp(up->env->user, sf->user) != 0) + error(Eperm); + + d = smalloc(sizeof(*d)+n); + if(waserror()){ + free(d); + nexterror(); + } + n = convM2D(dp, n, d, (char*)&d[1]); + if(n == 0) + error(Eshortstat); + if(!emptystr(d->name)){ + if(sf->dir == nil) + error(Eperm); + validwstatname(d->name); + qlock(&dev.l); + for(f = sf->dir; f != nil; f = f->entry) + if(strcmp(f->name, d->name) == 0){ + qunlock(&dev.l); + error(Eexist); + } + kstrdup(&sf->name, d->name); + qunlock(&dev.l); + } + if(d->mode != ~0UL) + sf->perm = d->mode & (DMEXCL|DMAPPEND|0777); + if(d->length != (vlong)-1) + sf->length = d->length; + poperror(); + free(d); + return n; +} + +static void +srvputdir(SrvFile *sf) +{ + SrvFile **l, *d; + + sf->ref--; + if(sf->ref != 0) + return; + + for(l = &dev.devices; (d = *l) != nil; l = &d->devlist) + if(d == sf){ + *l = d->devlist; + break; + } + free(sf->spec); + free(sf->user); + free(sf->name); + free(sf); +} + +static void +srvunblock(SrvFile *sf, int fid) +{ + Channel *d; + Sys_FileIO_read rreq; + Sys_FileIO_write wreq; + + acquire(); + if(waserror()){ + release(); + nexterror(); + } + d = sf->read; + if(d != H){ + rreq.t0 = 0; + rreq.t1 = 0; + rreq.t2 = fid; + rreq.t3 = H; + csendalt(d, &rreq, d->mid.t, -1); + } + + d = sf->write; + if(d != H){ + wreq.t0 = 0; + wreq.t1 = H; + wreq.t2 = fid; + wreq.t3 = H; + csendalt(d, &wreq, d->mid.t, -1); + } + poperror(); + release(); +} + +static void +srvdecr(SrvFile *sf, int remove) +{ + SrvFile *f, **l; + + if(remove){ + l = &sf->dir->entry; + for(f = *l; f != nil; f = f->entry){ + if(sf == f){ + *l = f->entry; + break; + } + l = &f->entry; + } + sf->ref--; + sf->flags |= SREMOVED; + } + + if(sf->ref != 0) + return; + + if(sf->dir != nil) + srvputdir(sf->dir); + + free(sf->spec); + free(sf->user); + free(sf->name); + free(sf); +} + +static void +srvfree(SrvFile *sf, int flag) +{ + sf->flags |= flag; + if((sf->flags & (SRDCLOSE | SWRCLOSE)) == (SRDCLOSE | SWRCLOSE)){ + sf->ref--; + srvdecr(sf, (sf->flags & SREMOVED) == 0); + } +} + +static void +freerdchan(Heap *h, int swept) +{ + SrvFile *sf; + + release(); + qlock(&dev.l); + sf = H2D(Channel*, h)->aux; + sf->read = H; + srvfree(sf, SRDCLOSE); + qunlock(&dev.l); + acquire(); + freechan(h, swept); +} + +static void +freewrchan(Heap *h, int swept) +{ + SrvFile *sf; + + release(); + qlock(&dev.l); + sf = H2D(Channel*, h)->aux; + sf->write = H; + srvfree(sf, SWRCLOSE); + qunlock(&dev.l); + acquire(); + freechan(h, swept); +} + +static void +srvclunk(Chan *c, int remove) +{ + int opens, noperm; + SrvFile *sf; + + sf = c->aux; + qlock(&dev.l); + if(c->qid.type & QTDIR){ + srvputdir(sf); + qunlock(&dev.l); + if(remove) + error(Eperm); + return; + } + + opens = 0; + if(c->flag & COPEN){ + opens = sf->opens--; + if (sf->read != H || sf->write != H) + srvunblock(sf, c->fid); + } + + sf->ref--; + if(opens == 1){ + if((sf->flags & (SORCLOSE | SREMOVED)) == SORCLOSE) + remove = 1; + } + + noperm = 0; + if(remove && strcmp(sf->dir->user, up->env->user) != 0){ + noperm = 1; + remove = 0; + } + + srvdecr(sf, remove); + qunlock(&dev.l); + + if(noperm) + error(Eperm); +} + +static void +srvclose(Chan *c) +{ + srvclunk(c, 0); +} + +static void +srvremove(Chan *c) +{ + srvclunk(c, 1); +} + +static long +srvread(Chan *c, void *va, long count, vlong offset) +{ + int l; + Heap * volatile h; + Array *a; + SrvFile *sp; + Channel *rc; + Channel *rd; + Sys_Rread * volatile r; + Sys_FileIO_read req; + + if(c->qid.type & QTDIR){ + qlock(&dev.l); + if(waserror()){ + qunlock(&dev.l); + nexterror(); + } + l = devdirread(c, va, count, 0, 0, srvgen); + poperror(); + qunlock(&dev.l); + return l; + } + + sp = c->aux; + + acquire(); + if(waserror()){ + release(); + nexterror(); + } + + rd = sp->read; + if(rd == H) + error(Eshutdown); + + rc = cnewc(dev.Rread, movtmp, 1); + ptradd(D2H(rc)); + if(waserror()){ + ptrdel(D2H(rc)); + destroy(rc); + nexterror(); + } + + req.t0 = offset; + req.t1 = count; + req.t2 = c->fid; + req.t3 = rc; + csend(rd, &req); + + h = heap(dev.Rread); + r = H2D(Sys_Rread *, h); + ptradd(h); + if(waserror()){ + ptrdel(h); + destroy(r); + nexterror(); + } + + crecv(rc, r); + if(r->t1 != H) + error(string2c(r->t1)); + + a = r->t0; + l = 0; + if(a != H){ + l = a->len; + if(l > count) + l = count; + memmove(va, a->data, l); + } + + poperror(); + ptrdel(h); + destroy(r); + + poperror(); + ptrdel(D2H(rc)); + destroy(rc); + + poperror(); + release(); + + return l; +} + +static long +srvwrite(Chan *c, void *va, long count, vlong offset) +{ + long l; + Heap * volatile h; + SrvFile *sp; + Channel *wc; + Channel *wr; + Sys_Rwrite * volatile w; + Sys_FileIO_write req; + + if(c->qid.type & QTDIR) + error(Eperm); + + acquire(); + if(waserror()){ + release(); + nexterror(); + } + + sp = c->aux; + wr = sp->write; + if(wr == H) + error(Eshutdown); + + wc = cnewc(dev.Rwrite, movtmp, 1); + ptradd(D2H(wc)); + if(waserror()){ + ptrdel(D2H(wc)); + destroy(wc); + nexterror(); + } + + req.t0 = offset; + req.t1 = mem2array(va, count); + req.t2 = c->fid; + req.t3 = wc; + + ptradd(D2H(req.t1)); + if(waserror()){ + ptrdel(D2H(req.t1)); + destroy(req.t1); + nexterror(); + } + + csend(wr, &req); + + poperror(); + ptrdel(D2H(req.t1)); + destroy(req.t1); + + poperror(); + ptrdel(D2H(wc)); + destroy(wc); + + h = heap(dev.Rwrite); + w = H2D(Sys_Rwrite *, h); + ptradd(h); + if(waserror()){ + ptrdel(h); + destroy(w); + nexterror(); + } + crecv(wc, w); + if(w->t1 != H) + error(string2c(w->t1)); + poperror(); + ptrdel(h); + l = w->t0; + destroy(w); + + poperror(); + release(); + if(l < 0) + l = 0; + return l; +} + +static void +srvretype(Channel *c, SrvFile *f, Type *t) +{ + Heap *h; + + h = D2H(c); + h->t->ref--; + h->t = t; + t->ref++; + c->aux = f; +} + +int +srvf2c(char *dir, char *file, Sys_FileIO *io) +{ + SrvFile *s, *f; + volatile struct { Chan *c; } c; + + c.c = nil; + if(waserror()){ + cclose(c.c); + return -1; + } + + if(strchr(file, '/') != nil || strlen(file) >= 64 || strcmp(file, ".") == 0 || strcmp(file, "..") == 0) + error(Efilename); + + c.c = namec(dir, Aaccess, 0, 0); + if((c.c->qid.type&QTDIR) == 0 || devtab[c.c->type]->dc != 's') + error("directory not a srv device"); + + s = c.c->aux; + + qlock(&dev.l); + for(f = s->entry; f != nil; f = f->entry){ + if(strcmp(f->name, file) == 0){ + qunlock(&dev.l); + error(Eexist); + } + } + + f = malloc(sizeof(SrvFile)); + if(f == nil){ + qunlock(&dev.l); + error(Enomem); + } + + srvretype(io->read, f, Trdchan); + srvretype(io->write, f, Twrchan); + f->read = io->read; + f->write = io->write; + + kstrdup(&f->name, file); + kstrdup(&f->user, up->env->user); + f->perm = 0666 & (~0666 | (s->perm & 0666)); + f->length = 0; + f->ref = 2; + mkqid(&f->qid, dev.pathgen++, 0, QTFILE); + + f->entry = s->entry; + s->entry = f; + s->ref++; + f->dir = s; + qunlock(&dev.l); + + cclose(c.c); + poperror(); + + return 0; +} + +Dev srvdevtab = { + 's', + "srv", + + devreset, + srvinit, + devshutdown, + srvattach, + srvwalk, + srvstat, + srvopen, + devcreate, + srvclose, + srvread, + devbread, + srvwrite, + devbwrite, + srvremove, + srvwstat +}; diff --git a/os/port/devssl.c b/os/port/devssl.c new file mode 100644 index 00000000..23c3fec5 --- /dev/null +++ b/os/port/devssl.c @@ -0,0 +1,1436 @@ +/* + * devssl - secure sockets layer + */ +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "kernel.h" + +#include "mp.h" +#include "libsec.h" + +typedef struct OneWay OneWay; +struct OneWay +{ + QLock q; + QLock ctlq; + + void *state; /* encryption state */ + int slen; /* secret data length */ + uchar *secret; /* secret */ + ulong mid; /* message id */ +}; + +enum +{ + /* connection states */ + Sincomplete= 0, + Sclear= 1, + Sencrypting= 2, + Sdigesting= 4, + Sdigenc= Sencrypting|Sdigesting, + + /* encryption algorithms */ + Noencryption= 0, + DESCBC= 1, + DESECB= 2, + RC4= 3, + IDEACBC= 4, + IDEAECB= 5 +}; + +typedef struct Dstate Dstate; +struct Dstate +{ + Chan *c; /* io channel */ + uchar state; /* state of connection */ + int ref; /* serialized by dslock for atomic destroy */ + + uchar encryptalg; /* encryption algorithm */ + ushort blocklen; /* blocking length */ + + ushort diglen; /* length of digest */ + DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*); /* hash func */ + + /* for SSL format */ + int max; /* maximum unpadded data per msg */ + int maxpad; /* maximum padded data per msg */ + + /* input side */ + OneWay in; + Block *processed; + Block *unprocessed; + + /* output side */ + OneWay out; + + /* protections */ + char* user; + int perm; +}; + +Lock dslock; +int dshiwat; +int maxdstate = 20; +Dstate** dstate; +char** dsname; + +enum +{ + Maxdmsg= 1<<16, + Maxdstate= 1<<10, +}; + +enum{ + Qtopdir = 1, /* top level directory */ + Qclonus, + Qconvdir, /* directory for a conversation */ + Qdata, + Qctl, + Qsecretin, + Qsecretout, + Qencalgs, + Qhashalgs +}; + +#define TYPE(x) ((ulong)(x).path & 0xf) +#define CONV(x) (((ulong)(x).path >> 4)&(Maxdstate-1)) +#define QID(c, y) (((c)<<4) | (y)) + +/* for generating random fill */ +ulong badlong; +uchar *badarray = (uchar*)&badlong; +static char* encalgs; +static char* hashalgs; + +void producerand(void); + +static void alglistinit(void); +static void ensure(Dstate*, Block**, int); +static void consume(Block**, uchar*, int); +static void setsecret(OneWay*, uchar*, int); +static Block* encryptb(Dstate*, Block*, int); +static Block* decryptb(Dstate*, Block*); +static Block* digestb(Dstate*, Block*, int); +static void checkdigestb(Dstate*, Block*); +static Chan* buftochan(char*); +static void sslhangup(Dstate*); +static Dstate* dsclone(Chan *c); +static void dsnew(Chan *c, Dstate **); + +static int +sslgen(Chan *c, char*, Dirtab *d, int nd, int s, Dir *dp) +{ + Qid q; + Dstate *ds; + char name[16], *p, *nm; + + USED(nd); + USED(d); + q.type = QTFILE; + q.vers = 0; + if(s == DEVDOTDOT){ + q.path = QID(0, Qtopdir); + q.type = QTDIR; + devdir(c, q, "#D", 0, eve, 0555, dp); + return 1; + } + switch(TYPE(c->qid)) { + case Qtopdir: + if(s < dshiwat) { + q.path = QID(s, Qconvdir); + q.type = QTDIR; + ds = dstate[s]; + if(ds != 0) + nm = ds->user; + else + nm = eve; + if(dsname[s] == nil){ + sprint(name, "%d", s); + kstrdup(&dsname[s], name); + } + devdir(c, q, dsname[s], 0, nm, DMDIR|0555, dp); + return 1; + } + if(s > dshiwat) + return -1; + /* fall through */ + case Qclonus: + q.path = QID(0, Qclonus); + devdir(c, q, "clone", 0, eve, 0666, dp); + return 1; + case Qconvdir: + ds = dstate[CONV(c->qid)]; + if(ds != 0) + nm = ds->user; + else + nm = eve; + switch(s) { + default: + return -1; + case 0: + q.path = QID(CONV(c->qid), Qctl); + p = "ctl"; + break; + case 1: + q.path = QID(CONV(c->qid), Qdata); + p = "data"; + break; + case 2: + q.path = QID(CONV(c->qid), Qsecretin); + p = "secretin"; + break; + case 3: + q.path = QID(CONV(c->qid), Qsecretout); + p = "secretout"; + break; + case 4: + q.path = QID(CONV(c->qid), Qencalgs); + p = "encalgs"; + break; + case 5: + q.path = QID(CONV(c->qid), Qhashalgs); + p = "hashalgs"; + break; + } + devdir(c, q, p, 0, nm, 0660, dp); + return 1; + } + return -1; +} + +static void +sslinit(void) +{ + if((dstate = malloc(sizeof(Dstate*) * maxdstate)) == 0) + panic("sslinit"); + if((dsname = malloc(sizeof(*dsname) * maxdstate)) == 0) + panic("sslinit"); + alglistinit(); +} + +static Chan * +sslattach(char *spec) +{ + Chan *c; + + c = devattach('D', spec); + c->qid.path = QID(0, Qtopdir); + c->qid.vers = 0; + c->qid.type = QTDIR; + return c; +} + +static Walkqid* +sslwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, sslgen); +} + +static int +sslstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, sslgen); +} + +static Chan* +sslopen(Chan *c, int omode) +{ + Dstate *s, **pp; + int perm; + + perm = 0; + omode &= 3; + switch(omode) { + case OREAD: + perm = 4; + break; + case OWRITE: + perm = 2; + break; + case ORDWR: + perm = 6; + break; + } + + switch(TYPE(c->qid)) { + default: + panic("sslopen"); + case Qtopdir: + case Qconvdir: + if(omode != OREAD) + error(Eperm); + break; + case Qclonus: + s = dsclone(c); + if(s == 0) + error(Enodev); + break; + case Qctl: + case Qdata: + case Qsecretin: + case Qsecretout: + if(waserror()) { + unlock(&dslock); + nexterror(); + } + lock(&dslock); + pp = &dstate[CONV(c->qid)]; + s = *pp; + if(s == 0) + dsnew(c, pp); + else { + if((perm & (s->perm>>6)) != perm + && (strcmp(up->env->user, s->user) != 0 + || (perm & s->perm) != perm)) + error(Eperm); + + s->ref++; + } + unlock(&dslock); + poperror(); + break; + case Qencalgs: + case Qhashalgs: + if(omode != OREAD) + error(Eperm); + break; + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static int +sslwstat(Chan *c, uchar *dp, int n) +{ + Dir d; + Dstate *s; + + n = convM2D(dp, n, &d, nil); + if(n == 0) + error(Eshortstat); + s = dstate[CONV(c->qid)]; + if(s == 0) + error(Ebadusefd); + if(strcmp(s->user, up->env->user) != 0) + error(Eperm); + if(!emptystr(d.uid)) + kstrdup(&s->user, d.uid); + if(d.mode != ~0UL) + s->perm = d.mode; + return n; +} + +static void +sslclose(Chan *c) +{ + Dstate *s; + + switch(TYPE(c->qid)) { + case Qctl: + case Qdata: + case Qsecretin: + case Qsecretout: + if((c->flag & COPEN) == 0) + break; + + s = dstate[CONV(c->qid)]; + if(s == 0) + break; + + lock(&dslock); + if(--s->ref > 0) { + unlock(&dslock); + break; + } + dstate[CONV(c->qid)] = 0; + unlock(&dslock); + + sslhangup(s); + if(s->c) + cclose(s->c); + free(s->user); + free(s->in.secret); + free(s->out.secret); + free(s->in.state); + free(s->out.state); + free(s); + } +} + +/* + * make sure we have at least 'n' bytes in list 'l' + */ +static void +ensure(Dstate *s, Block **l, int n) +{ + int sofar, i; + Block *b, *bl; + + sofar = 0; + for(b = *l; b; b = b->next){ + sofar += BLEN(b); + if(sofar >= n) + return; + l = &b->next; + } + + while(sofar < n){ + bl = devtab[s->c->type]->bread(s->c, Maxdmsg, 0); + if(bl == 0) + error(Ehungup); + *l = bl; + i = 0; + for(b = bl; b; b = b->next){ + i += BLEN(b); + l = &b->next; + } + if(i == 0) + error(Ehungup); + + sofar += i; + } +} + +/* + * copy 'n' bytes from 'l' into 'p' and free + * the bytes in 'l' + */ +static void +consume(Block **l, uchar *p, int n) +{ + Block *b; + int i; + + for(; *l && n > 0; n -= i){ + b = *l; + i = BLEN(b); + if(i > n) + i = n; + memmove(p, b->rp, i); + b->rp += i; + p += i; + if(BLEN(b) < 0) + panic("consume"); + if(BLEN(b)) + break; + *l = b->next; + freeb(b); + } +} + +/* + * remove at most n bytes from the queue, if discard is set + * dump the remainder + */ +static Block* +qtake(Block **l, int n, int discard) +{ + Block *nb, *b, *first; + int i; + + first = *l; + for(b = first; b; b = b->next){ + i = BLEN(b); + if(i == n){ + if(discard){ + freeblist(b->next); + *l = 0; + } else + *l = b->next; + b->next = 0; + return first; + } else if(i > n){ + i -= n; + if(discard){ + freeblist(b->next); + *l = 0; + } else { + nb = allocb(i); + memmove(nb->wp, b->rp+n, i); + nb->wp += i; + nb->next = b->next; + *l = nb; + } + b->wp -= i; + b->next = 0; + if(BLEN(b) < 0) + panic("qtake"); + return first; + } else + n -= i; + if(BLEN(b) < 0) + panic("qtake"); + } + *l = 0; + return first; +} + +static Block* +sslbread(Chan *c, long n, ulong offset) +{ + volatile struct { Dstate *s; } s; + Block *b; + uchar count[2]; + int len, pad; + + USED(offset); + + s.s = dstate[CONV(c->qid)]; + if(s.s == 0) + panic("sslbread"); + if(s.s->state == Sincomplete) + error(Ebadusefd); + + if(waserror()){ + qunlock(&s.s->in.q); + sslhangup(s.s); + nexterror(); + } + qlock(&s.s->in.q); + + if(s.s->processed == 0){ + /* read in the whole message */ + ensure(s.s, &s.s->unprocessed, 2); + consume(&s.s->unprocessed, count, 2); + if(count[0] & 0x80){ + len = ((count[0] & 0x7f)<<8) | count[1]; + ensure(s.s, &s.s->unprocessed, len); + pad = 0; + } else { + len = ((count[0] & 0x3f)<<8) | count[1]; + ensure(s.s, &s.s->unprocessed, len+1); + consume(&s.s->unprocessed, count, 1); + pad = count[0]; + if(pad > len){ + print("pad %d buf len %d\n", pad, len); + error("bad pad in ssl message"); + } + } + + /* put extra on unprocessed queue */ + s.s->processed = qtake(&s.s->unprocessed, len, 0); + + if(waserror()){ + qunlock(&s.s->in.ctlq); + nexterror(); + } + qlock(&s.s->in.ctlq); + switch(s.s->state){ + case Sencrypting: + s.s->processed = decryptb(s.s, s.s->processed); + break; + case Sdigesting: + s.s->processed = pullupblock(s.s->processed, s.s->diglen); + if(s.s->processed == 0) + error("ssl message too short"); + checkdigestb(s.s, s.s->processed); + s.s->processed->rp += s.s->diglen; + break; + case Sdigenc: + s.s->processed = decryptb(s.s, s.s->processed); + s.s->processed = pullupblock(s.s->processed, s.s->diglen); + if(s.s->processed == 0) + error("ssl message too short"); + checkdigestb(s.s, s.s->processed); + s.s->processed->rp += s.s->diglen; + len -= s.s->diglen; + break; + } + s.s->in.mid++; + qunlock(&s.s->in.ctlq); + poperror(); + + /* remove pad */ + if(pad) + s.s->processed = qtake(&s.s->processed, len - pad, 1); + } + + /* return at most what was asked for */ + b = qtake(&s.s->processed, n, 0); + + qunlock(&s.s->in.q); + poperror(); + + return b; +} + +static long +sslread(Chan *c, void *a, long n, vlong offset) +{ + volatile struct { Block *b; } b; + Block *nb; + uchar *va; + int i; + char buf[128]; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, sslgen); + + switch(TYPE(c->qid)) { + default: + error(Ebadusefd); + case Qctl: + sprint(buf, "%ld", CONV(c->qid)); + return readstr(offset, a, n, buf); + case Qdata: + b.b = sslbread(c, n, offset); + break; + case Qencalgs: + return readstr(offset, a, n, encalgs); + case Qhashalgs: + return readstr(offset, a, n, hashalgs); + } + + n = 0; + va = a; + for(nb = b.b; nb; nb = nb->next){ + i = BLEN(nb); + memmove(va+n, nb->rp, i); + n += i; + } + + freeblist(b.b); + + return n; +} + +/* + * this algorithm doesn't have to be great since we're just + * trying to obscure the block fill + */ +static void +randfill(uchar *buf, int len) +{ + while(len-- > 0) + *buf++ = nrand(256); +} + +/* + * use SSL record format, add in count and digest or encrypt + */ +static long +sslbwrite(Chan *c, Block *b, ulong offset) +{ + volatile struct { Dstate *s; } s; + volatile struct { Block *b; } bb; + Block *nb; + int h, n, m, pad, rv; + uchar *p; + + bb.b = b; + + s.s = dstate[CONV(c->qid)]; + if(s.s == 0) + panic("sslbwrite"); + if(s.s->state == Sincomplete){ + freeb(b); + error(Ebadusefd); + } + + if(waserror()){ + qunlock(&s.s->out.q); + if(bb.b) + freeb(bb.b); + sslhangup(s.s); + nexterror(); + } + qlock(&s.s->out.q); + + rv = 0; + while(bb.b){ + m = n = BLEN(bb.b); + h = s.s->diglen + 2; + + /* trim to maximum block size */ + pad = 0; + if(m > s.s->max){ + m = s.s->max; + } else if(s.s->blocklen != 1){ + pad = (m + s.s->diglen)%s.s->blocklen; + if(pad){ + if(m > s.s->maxpad){ + pad = 0; + m = s.s->maxpad; + } else { + pad = s.s->blocklen - pad; + h++; + } + } + } + + rv += m; + if(m != n){ + nb = allocb(m + h + pad); + memmove(nb->wp + h, bb.b->rp, m); + nb->wp += m + h; + bb.b->rp += m; + } else { + /* add header space */ + nb = padblock(bb.b, h); + bb.b = 0; + } + m += s.s->diglen; + + /* SSLv2 style count */ + if(pad){ + nb = padblock(nb, -pad); + randfill(nb->wp, pad); + nb->wp += pad; + m += pad; + + p = nb->rp; + p[0] = (m>>8); + p[1] = m; + p[2] = pad; + offset = 3; + } else { + p = nb->rp; + p[0] = (m>>8) | 0x80; + p[1] = m; + offset = 2; + } + + switch(s.s->state){ + case Sencrypting: + nb = encryptb(s.s, nb, offset); + break; + case Sdigesting: + nb = digestb(s.s, nb, offset); + break; + case Sdigenc: + nb = digestb(s.s, nb, offset); + nb = encryptb(s.s, nb, offset); + break; + } + + s.s->out.mid++; + + m = BLEN(nb); + devtab[s.s->c->type]->bwrite(s.s->c, nb, s.s->c->offset); + s.s->c->offset += m; + } + qunlock(&s.s->out.q); + poperror(); + + return rv; +} + +static void +setsecret(OneWay *w, uchar *secret, int n) +{ + free(w->secret); + w->secret = mallocz(n, 0); + if(w->secret == nil) + error(Enomem); + memmove(w->secret, secret, n); + w->slen = n; +} + +static void +initIDEAkey(OneWay *w) +{ + free(w->state); + w->state = malloc(sizeof(IDEAstate)); + if(w->state == nil) + error(Enomem); + if(w->slen >= 24) + setupIDEAstate(w->state, w->secret, w->secret+16); + else if(w->slen >= 16) + setupIDEAstate(w->state, w->secret, 0); + else + error("secret too short"); +} + +static void +initDESkey(OneWay *w) +{ + free(w->state); + w->state = malloc(sizeof(DESstate)); + if (!w->state) + error(Enomem); + if(w->slen >= 16) + setupDESstate(w->state, w->secret, w->secret+8); + else if(w->slen >= 8) + setupDESstate(w->state, w->secret, 0); + else + error("secret too short"); +} + +/* + * 40 bit DES is the same as 56 bit DES. However, + * 16 bits of the key are masked to zero. + */ +static void +initDESkey_40(OneWay *w) +{ + uchar key[8]; + + if(w->slen >= 8) { + memmove(key, w->secret, 8); + key[0] &= 0x0f; + key[2] &= 0x0f; + key[4] &= 0x0f; + key[6] &= 0x0f; + } + + free(w->state); + w->state = malloc(sizeof(DESstate)); + if (!w->state) + error(Enomem); + if(w->slen >= 16) + setupDESstate(w->state, key, w->secret+8); + else if(w->slen >= 8) + setupDESstate(w->state, key, 0); + else + error("secret too short"); +} + +static void +initRC4key(OneWay *w) +{ + free(w->state); + w->state = malloc(sizeof(RC4state)); + if (!w->state) + error(Enomem); + setupRC4state(w->state, w->secret, w->slen); +} + +/* + * 40 bit RC4 is the same as n-bit RC4. However, + * we ignore all but the first 40 bits of the key. + */ +static void +initRC4key_40(OneWay *w) +{ + int slen = w->slen; + + if(slen > 5) + slen = 5; + + free(w->state); + w->state = malloc(sizeof(RC4state)); + if (!w->state) + error(Enomem); + setupRC4state(w->state, w->secret, slen); +} + +/* + * 128 bit RC4 is the same as n-bit RC4. However, + * we ignore all but the first 128 bits of the key. + */ +static void +initRC4key_128(OneWay *w) +{ + int slen = w->slen; + + if(slen > 16) + slen = 16; + + free(w->state); + w->state = malloc(sizeof(RC4state)); + if (!w->state) + error(Enomem); + setupRC4state(w->state, w->secret, slen); +} + +typedef struct Hashalg Hashalg; +struct Hashalg +{ + char *name; + int diglen; + DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*); +}; + +Hashalg hashtab[] = +{ + { "md4", MD4dlen, md4, }, + { "md5", MD5dlen, md5, }, + { "sha1", SHA1dlen, sha1, }, + { "sha", SHA1dlen, sha1, }, + { 0 } +}; + +static int +parsehashalg(char *p, Dstate *s) +{ + Hashalg *ha; + + for(ha = hashtab; ha->name; ha++){ + if(strcmp(p, ha->name) == 0){ + s->hf = ha->hf; + s->diglen = ha->diglen; + s->state &= ~Sclear; + s->state |= Sdigesting; + return 0; + } + } + return -1; +} + +typedef struct Encalg Encalg; +struct Encalg +{ + char *name; + int blocklen; + int alg; + void (*keyinit)(OneWay*); +}; + +Encalg encrypttab[] = +{ + { "descbc", 8, DESCBC, initDESkey, }, /* DEPRECATED -- use des_56_cbc */ + { "desecb", 8, DESECB, initDESkey, }, /* DEPRECATED -- use des_56_ecb */ + { "des_56_cbc", 8, DESCBC, initDESkey, }, + { "des_56_ecb", 8, DESECB, initDESkey, }, + { "des_40_cbc", 8, DESCBC, initDESkey_40, }, + { "des_40_ecb", 8, DESECB, initDESkey_40, }, + { "rc4", 1, RC4, initRC4key_40, }, /* DEPRECATED -- use rc4_X */ + { "rc4_256", 1, RC4, initRC4key, }, + { "rc4_128", 1, RC4, initRC4key_128, }, + { "rc4_40", 1, RC4, initRC4key_40, }, + { "ideacbc", 8, IDEACBC, initIDEAkey, }, + { "ideaecb", 8, IDEAECB, initIDEAkey, }, + { 0 } +}; + +static int +parseencryptalg(char *p, Dstate *s) +{ + Encalg *ea; + + for(ea = encrypttab; ea->name; ea++){ + if(strcmp(p, ea->name) == 0){ + s->encryptalg = ea->alg; + s->blocklen = ea->blocklen; + (*ea->keyinit)(&s->in); + (*ea->keyinit)(&s->out); + s->state &= ~Sclear; + s->state |= Sencrypting; + return 0; + } + } + return -1; +} + +static void +alglistinit(void) +{ + Hashalg *h; + Encalg *e; + int n; + + n = 1; + for(e = encrypttab; e->name != nil; e++) + n += strlen(e->name) + 1; + encalgs = malloc(n); + if(encalgs == nil) + panic("sslinit"); + n = 0; + for(e = encrypttab; e->name != nil; e++){ + strcpy(encalgs+n, e->name); + n += strlen(e->name); + if(e[1].name == nil) + break; + encalgs[n++] = ' '; + } + encalgs[n] = 0; + + n = 1; + for(h = hashtab; h->name != nil; h++) + n += strlen(h->name) + 1; + hashalgs = malloc(n); + if(hashalgs == nil) + panic("sslinit"); + n = 0; + for(h = hashtab; h->name != nil; h++){ + strcpy(hashalgs+n, h->name); + n += strlen(h->name); + if(h[1].name == nil) + break; + hashalgs[n++] = ' '; + } + hashalgs[n] = 0; +} + +static long +sslwrite(Chan *c, void *a, long n, vlong offset) +{ + volatile struct { Dstate *s; } s; + volatile struct { Block *b; } b; + int m, t; + char *p, *np, *e, buf[32]; + uchar *x; + + s.s = dstate[CONV(c->qid)]; + if(s.s == 0) + panic("sslwrite"); + + t = TYPE(c->qid); + if(t == Qdata){ + if(s.s->state == Sincomplete) + error(Ebadusefd); + + p = a; + e = p + n; + do { + m = e - p; + if(m > s.s->max) + m = s.s->max; + + b.b = allocb(m); + memmove(b.b->wp, p, m); + b.b->wp += m; + + sslbwrite(c, b.b, offset); + + p += m; + } while(p < e); + return n; + } + + /* mutex with operations using what we're about to change */ + if(waserror()){ + qunlock(&s.s->in.ctlq); + qunlock(&s.s->out.q); + nexterror(); + } + qlock(&s.s->in.ctlq); + qlock(&s.s->out.q); + + switch(t){ + default: + panic("sslwrite"); + case Qsecretin: + setsecret(&s.s->in, a, n); + goto out; + case Qsecretout: + setsecret(&s.s->out, a, n); + goto out; + case Qctl: + break; + } + + if(n >= sizeof(buf)) + error(Ebadarg); + strncpy(buf, a, n); + buf[n] = 0; + p = strchr(buf, '\n'); + if(p) + *p = 0; + p = strchr(buf, ' '); + if(p) + *p++ = 0; + + if(strcmp(buf, "fd") == 0){ + s.s->c = buftochan(p); + + /* default is clear (msg delimiters only) */ + s.s->state = Sclear; + s.s->blocklen = 1; + s.s->diglen = 0; + s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1; + s.s->in.mid = 0; + s.s->out.mid = 0; + } else if(strcmp(buf, "alg") == 0 && p != 0){ + s.s->blocklen = 1; + s.s->diglen = 0; + + if(s.s->c == 0) + error("must set fd before algorithm"); + + if(strcmp(p, "clear") == 0){ + s.s->state = Sclear; + s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1; + goto out; + } + + if(s.s->in.secret && s.s->out.secret == 0) + setsecret(&s.s->out, s.s->in.secret, s.s->in.slen); + if(s.s->out.secret && s.s->in.secret == 0) + setsecret(&s.s->in, s.s->out.secret, s.s->out.slen); + if(s.s->in.secret == 0 || s.s->out.secret == 0) + error("algorithm but no secret"); + + s.s->hf = 0; + s.s->encryptalg = Noencryption; + s.s->blocklen = 1; + + for(;;){ + np = strchr(p, ' '); + if(np) + *np++ = 0; + else{ + np = strchr(p, '/'); + if(np) + *np++ = 0; + } + if(parsehashalg(p, s.s) < 0) + if(parseencryptalg(p, s.s) < 0) + error(Ebadarg); + + if(np == 0) + break; + p = np; + } + + if(s.s->hf == 0 && s.s->encryptalg == Noencryption) + error(Ebadarg); + + if(s.s->blocklen != 1){ + /* make multiple of blocklen */ + s.s->max = (1<<15) - s.s->diglen - 1; + s.s->max -= s.s->max % s.s->blocklen; + s.s->maxpad = (1<<14) - s.s->diglen - 1; + s.s->maxpad -= s.s->maxpad % s.s->blocklen; + } else + s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1; + } else if(strcmp(buf, "secretin") == 0 && p != 0) { + m = (strlen(p)*3)/2; + x = smalloc(m); + if(waserror()){ + free(x); + nexterror(); + } + t = dec64(x, m, p, strlen(p)); + setsecret(&s.s->in, x, t); + poperror(); + free(x); + } else if(strcmp(buf, "secretout") == 0 && p != 0) { + m = (strlen(p)*3)/2; + x = smalloc(m); + if(waserror()){ + free(x); + nexterror(); + } + t = dec64(x, m, p, strlen(p)); + setsecret(&s.s->out, x, t); + poperror(); + free(x); + } else + error(Ebadarg); + +out: + qunlock(&s.s->in.ctlq); + qunlock(&s.s->out.q); + poperror(); + return n; +} + +Dev ssldevtab = { + 'D', + "ssl", + + devreset, + sslinit, + devshutdown, + sslattach, + sslwalk, + sslstat, + sslopen, + devcreate, + sslclose, + sslread, + sslbread, + sslwrite, + sslbwrite, + devremove, + sslwstat, +}; + +static Block* +encryptb(Dstate *s, Block *b, int offset) +{ + uchar *p, *ep, *p2, *ip, *eip; + DESstate *ds; + IDEAstate *is; + + switch(s->encryptalg){ + case DESECB: + ds = s->out.state; + ep = b->rp + BLEN(b); + for(p = b->rp + offset; p < ep; p += 8) + block_cipher(ds->expanded, p, 0); + break; + case DESCBC: + ds = s->out.state; + ep = b->rp + BLEN(b); + for(p = b->rp + offset; p < ep; p += 8){ + p2 = p; + ip = ds->ivec; + for(eip = ip+8; ip < eip; ) + *p2++ ^= *ip++; + block_cipher(ds->expanded, p, 0); + memmove(ds->ivec, p, 8); + } + break; + case IDEAECB: + is = s->out.state; + ep = b->rp + BLEN(b); + for(p = b->rp + offset; p < ep; p += 8) + idea_cipher(is->edkey, p, 0); + break; + case IDEACBC: + is = s->out.state; + ep = b->rp + BLEN(b); + for(p = b->rp + offset; p < ep; p += 8){ + p2 = p; + ip = is->ivec; + for(eip = ip+8; ip < eip; ) + *p2++ ^= *ip++; + idea_cipher(is->edkey, p, 0); + memmove(is->ivec, p, 8); + } + break; + case RC4: + rc4(s->out.state, b->rp + offset, BLEN(b) - offset); + break; + } + return b; +} + +static Block* +decryptb(Dstate *s, Block *inb) +{ + Block *b, **l; + uchar *p, *ep, *tp, *ip, *eip; + DESstate *ds; + IDEAstate *is; + uchar tmp[8]; + int i; + + l = &inb; + for(b = inb; b; b = b->next){ + /* make sure we have a multiple of s->blocklen */ + if(s->blocklen > 1){ + i = BLEN(b); + if(i % s->blocklen){ + *l = b = pullupblock(b, i + s->blocklen - (i%s->blocklen)); + if(b == 0) + error("ssl encrypted message too short"); + } + } + l = &b->next; + + /* decrypt */ + switch(s->encryptalg){ + case DESECB: + ds = s->in.state; + ep = b->rp + BLEN(b); + for(p = b->rp; p < ep; p += 8) + block_cipher(ds->expanded, p, 1); + break; + case DESCBC: + ds = s->in.state; + ep = b->rp + BLEN(b); + for(p = b->rp; p < ep;){ + memmove(tmp, p, 8); + block_cipher(ds->expanded, p, 1); + tp = tmp; + ip = ds->ivec; + for(eip = ip+8; ip < eip; ){ + *p++ ^= *ip; + *ip++ = *tp++; + } + } + break; + case IDEAECB: + is = s->in.state; + ep = b->rp + BLEN(b); + for(p = b->rp; p < ep; p += 8) + idea_cipher(is->edkey, p, 1); + break; + case IDEACBC: + is = s->in.state; + ep = b->rp + BLEN(b); + for(p = b->rp; p < ep;){ + memmove(tmp, p, 8); + idea_cipher(is->edkey, p, 1); + tp = tmp; + ip = is->ivec; + for(eip = ip+8; ip < eip; ){ + *p++ ^= *ip; + *ip++ = *tp++; + } + } + break; + case RC4: + rc4(s->in.state, b->rp, BLEN(b)); + break; + } + } + return inb; +} + +static Block* +digestb(Dstate *s, Block *b, int offset) +{ + uchar *p; + DigestState ss; + uchar msgid[4]; + ulong n, h; + OneWay *w; + + w = &s->out; + + memset(&ss, 0, sizeof(ss)); + h = s->diglen + offset; + n = BLEN(b) - h; + + /* hash secret + message */ + (*s->hf)(w->secret, w->slen, 0, &ss); + (*s->hf)(b->rp + h, n, 0, &ss); + + /* hash message id */ + p = msgid; + n = w->mid; + *p++ = n>>24; + *p++ = n>>16; + *p++ = n>>8; + *p = n; + (*s->hf)(msgid, 4, b->rp + offset, &ss); + + return b; +} + +static void +checkdigestb(Dstate *s, Block *inb) +{ + uchar *p; + DigestState ss; + uchar msgid[4]; + int n, h; + OneWay *w; + uchar digest[128]; + Block *b; + + w = &s->in; + + memset(&ss, 0, sizeof(ss)); + + /* hash secret */ + (*s->hf)(w->secret, w->slen, 0, &ss); + + /* hash message */ + h = s->diglen; + for(b = inb; b; b = b->next){ + n = BLEN(b) - h; + if(n < 0) + panic("checkdigestb"); + (*s->hf)(b->rp + h, n, 0, &ss); + h = 0; + } + + /* hash message id */ + p = msgid; + n = w->mid; + *p++ = n>>24; + *p++ = n>>16; + *p++ = n>>8; + *p = n; + (*s->hf)(msgid, 4, digest, &ss); + + /* requires pullupblock */ + if(memcmp(digest, inb->rp, s->diglen) != 0) + error("bad digest"); +} + +/* get channel associated with an fd */ +static Chan* +buftochan(char *p) +{ + Chan *c; + int fd; + + if(p == 0) + error(Ebadarg); + fd = strtoul(p, 0, 0); + if(fd < 0) + error(Ebadarg); + c = fdtochan(up->env->fgrp, fd, -1, 0, 1); /* error check and inc ref */ + return c; +} + +/* hang up a digest connection */ +static void +sslhangup(Dstate *s) +{ + qlock(&s->in.q); + freeblist(s->processed); + s->processed = 0; + freeblist(s->unprocessed); + s->unprocessed = 0; + s->state = Sincomplete; + qunlock(&s->in.q); +} + +extern void rbcheck(char*); + +static Dstate* +dsclone(Chan *ch) +{ + Dstate **pp, **ep, **np; + int newmax; + + if(waserror()) { + unlock(&dslock); + nexterror(); + } + lock(&dslock); + ep = &dstate[maxdstate]; + for(pp = dstate; pp < ep; pp++) { + if(*pp == 0) { + dsnew(ch, pp); + break; + } + } + if(pp >= ep) { + if(maxdstate >= Maxdstate) { + unlock(&dslock); + poperror(); + return 0; + } + newmax = 2 * maxdstate; + if(newmax > Maxdstate) + newmax = Maxdstate; + np = realloc(dstate, sizeof(Dstate*) * newmax); + if(np == 0) + error(Enomem); + dstate = np; + pp = &dstate[maxdstate]; + memset(pp, 0, sizeof(Dstate*)*(newmax - maxdstate)); + maxdstate = newmax; + dsnew(ch, pp); + } + unlock(&dslock); + poperror(); + return *pp; +} + +static void +dsnew(Chan *ch, Dstate **pp) +{ + Dstate *s; + int t; + + *pp = s = malloc(sizeof(*s)); + if(!s) + error(Enomem); + if(pp - dstate >= dshiwat) + dshiwat++; + s->state = Sincomplete; + s->ref = 1; + kstrdup(&s->user, up->env->user); + s->perm = 0660; + t = TYPE(ch->qid); + if(t == Qclonus) + t = Qctl; + ch->qid.path = QID(pp - dstate, t); + ch->qid.vers = 0; + ch->qid.type = QTFILE; +} diff --git a/os/port/devtest.c b/os/port/devtest.c new file mode 100644 index 00000000..2c935014 --- /dev/null +++ b/os/port/devtest.c @@ -0,0 +1,125 @@ +/* + * Test device + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "libcrypt.h" + +#include + +#define DEBUG 0 + +extern void _startup(void); + +enum{ + Qdir, + Qkt5sum, + Qkerndate, +}; + +static +Dirtab testtab[]={ + ".", { Qdir, 0, QTDIR}, 0, 0555, + "kt5sum", { Qkt5sum }, 0, 0444, + "kerndate", { Qkerndate }, 0, 0444, +}; + + +void ktsum(char *digest) +{ + uchar rawdigest[MD5dlen+1]; + int i; + void *start = _startup; + ulong size = (ulong)etext - (ulong) start; + md5(start, size, rawdigest, nil); + for (i=0; iqid.path) { + case Qdir: + return devdirread(c, a, n, testtab, nelem(testtab), devgen); + case Qkt5sum: + ktsum(digest); + return readstr(offset, a, n, digest); + case Qkerndate: + sprint(digest, "%ld\n", kerndate); + return readstr(offset, a, n, digest); + default: + n = 0; + break; + } + return n; +} + + +static long +testwrite(Chan*, void*, long, vlong) +{ + error(Ebadusefd); + return 0; +} + +Dev testdevtab = { + 'Z', + "test", + + devreset, + devinit, + devshutdown, + testattach, + testwalk, + teststat, + testopen, + devcreate, + testclose, + testread, + devbread, + testwrite, + devbwrite, + devremove, + devwstat, +}; + diff --git a/os/port/devtinyfs.c b/os/port/devtinyfs.c new file mode 100644 index 00000000..b63caf3f --- /dev/null +++ b/os/port/devtinyfs.c @@ -0,0 +1,915 @@ +/* + * a pity the code isn't also tiny... + */ +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +enum{ + Qdir, + Qmedium, + + Maxfs= 10, /* max file systems */ + + Blen= 48, /* block length */ + Nlen= 28, /* name length */ + Dlen= Blen - 4, + + Tagdir= 'd', + Tagdata= 'D', + Tagend= 'e', + Tagfree= 'f', + + Notapin= 0xffff, + Notabno= 0xffff, + + Fcreating= 1, + Frmonclose= 2 +}; + +/* representation of a Tdir on medium */ +typedef struct Mdir Mdir; +struct Mdir { + uchar type; + uchar bno[2]; + uchar pin[2]; + char name[Nlen]; + char pad[Blen - Nlen - 6]; + uchar sum; +}; + +/* representation of a Tdata/Tend on medium */ +typedef struct Mdata Mdata; +struct Mdata { + uchar type; + uchar bno[2]; + uchar data[Dlen]; + uchar sum; +}; + +typedef struct Tfile Tfile; +struct Tfile { + int r; + char name[Nlen]; + ushort bno; + ushort dbno; + ushort pin; + uchar flag; + ulong length; + + /* hint to avoid egregious reading */ + ushort fbno; + ulong finger; +}; + +typedef struct Tfs Tfs; +struct Tfs { + QLock ql; + int r; + Chan *c; + uchar *map; + int nblocks; + Tfile *f; + int nf; + int fsize; +}; + +static struct { + Tfs fs[Maxfs]; +} tinyfs; + +#define GETS(x) ((x)[0]|((x)[1]<<8)) +#define PUTS(x, v) {(x)[0] = (v);(x)[1] = ((v)>>8);} + +#define GETL(x) (GETS(x)|(GETS(x+2)<<16)) +#define PUTL(x, v) {PUTS(x, v);PUTS(x+2, (v)>>16)}; + +static uchar +checksum(uchar *p) +{ + uchar *e; + uchar s; + + s = 0; + for(e = p + Blen; p < e; p++) + s += *p; + return s; +} + +static void +mapclr(Tfs *fs, ulong bno) +{ + fs->map[bno>>3] &= ~(1<<(bno&7)); +} + +static void +mapset(Tfs *fs, ulong bno) +{ + fs->map[bno>>3] |= 1<<(bno&7); +} + +static int +isalloced(Tfs *fs, ulong bno) +{ + return fs->map[bno>>3] & (1<<(bno&7)); +} + +static int +mapalloc(Tfs *fs) +{ + int i, j, lim; + uchar x; + + lim = (fs->nblocks + 8 - 1)/8; + for(i = 0; i < lim; i++){ + x = fs->map[i]; + if(x == 0xff) + continue; + for(j = 0; j < 8; j++) + if((x & (1<map[i] = x|(1<bno); + if(x >= fs->nblocks) + return 0; + return md; +} + +static Mdata* +validdata(Tfs *fs, uchar *p, int *lenp) +{ + Mdata *md; + ulong x; + + if(checksum(p) != 0) + return 0; + md = (Mdata*)p; + switch(md->type){ + case Tagdata: + x = GETS(md->bno); + if(x >= fs->nblocks) + return 0; + if(lenp) + *lenp = Dlen; + break; + case Tagend: + x = GETS(md->bno); + if(x > Dlen) + return 0; + if(lenp) + *lenp = x; + break; + default: + return 0; + } + return md; +} + +static Mdata* +readdata(Tfs *fs, ulong bno, uchar *buf, int *lenp) +{ + if(bno >= fs->nblocks) + return 0; + if(devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno) != Blen) + error(Eio); + return validdata(fs, buf, lenp); +} + +static void +writedata(Tfs *fs, ulong bno, ulong next, uchar *buf, int len, int last) +{ + Mdata md; + + if(bno >= fs->nblocks) + error(Eio); + if(len > Dlen) + len = Dlen; + if(len < 0) + error(Eio); + memset(&md, 0, sizeof(md)); + if(last){ + md.type = Tagend; + PUTS(md.bno, len); + } else { + md.type = Tagdata; + PUTS(md.bno, next); + } + memmove(md.data, buf, len); + md.sum = 0 - checksum((uchar*)&md); + + if(devtab[fs->c->type]->write(fs->c, &md, Blen, Blen*bno) != Blen) + error(Eio); +} + +static void +writedir(Tfs *fs, Tfile *f) +{ + Mdir *md; + uchar buf[Blen]; + + if(f->bno == Notabno) + return; + + md = (Mdir*)buf; + memset(buf, 0, Blen); + md->type = Tagdir; + strncpy(md->name, f->name, sizeof(md->name)-1); + PUTS(md->bno, f->dbno); + PUTS(md->pin, f->pin); + md->sum = 0 - checksum(buf); + + if(devtab[fs->c->type]->write(fs->c, buf, Blen, Blen*f->bno) != Blen) + error(Eio); +} + +static void +freeblocks(Tfs *fs, ulong bno, ulong bend) +{ + uchar buf[Blen]; + Mdata *md; + + if(waserror()) + return; + + while(bno != bend && bno != Notabno){ + mapclr(fs, bno); + if(devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno) != Blen) + break; + md = validdata(fs, buf, 0); + if(md == 0) + break; + if(md->type == Tagend) + break; + bno = GETS(md->bno); + } + + poperror(); +} + +static void +freefile(Tfs *fs, Tfile *f, ulong bend) +{ + uchar buf[Blen]; + + /* remove blocks from map */ + freeblocks(fs, f->dbno, bend); + + /* change file type to free on medium */ + if(f->bno != Notabno){ + memset(buf, 0x55, Blen); + devtab[fs->c->type]->write(fs->c, buf, Blen, Blen*f->bno); + mapclr(fs, f->bno); + } + + /* forget we ever knew about it */ + memset(f, 0, sizeof(*f)); +} + +static void +expand(Tfs *fs) +{ + Tfile *f; + + fs->fsize += 8; + f = malloc(fs->fsize*sizeof(*f)); + if(f == nil) + error(Enomem); + + if(fs->f){ + memmove(f, fs->f, fs->nf*sizeof(*f)); + free(fs->f); + } + fs->f = f; +} + +static Tfile* +newfile(Tfs *fs, char *name) +{ + int i; + volatile struct { + Tfile *f; + Tfs *fs; + } rock; + + /* find free entry in file table */ + rock.f = 0; + rock.fs = fs; + for(;;) { + for(i = 0; i < rock.fs->fsize; i++){ + rock.f = &rock.fs->f[i]; + if(rock.f->name[0] == 0){ + strncpy(rock.f->name, name, sizeof(rock.f->name)-1); + break; + } + } + + if(i < rock.fs->fsize){ + if(i >= rock.fs->nf) + rock.fs->nf = i+1; + break; + } + + expand(rock.fs); + } + + rock.f->flag = Fcreating; + rock.f->dbno = Notabno; + rock.f->bno = mapalloc(rock.fs); + rock.f->fbno = Notabno; + rock.f->r = 1; + rock.f->pin = up->env->pgrp->pin; + + /* write directory block */ + if(waserror()){ + freefile(rock.fs, rock.f, Notabno); + nexterror(); + } + if(rock.f->bno == Notabno) + error("out of space"); + writedir(rock.fs, rock.f); + poperror(); + + return rock.f; +} + +/* + * Read the whole medium and build a file table and used + * block bitmap. Inconsistent files are purged. The medium + * had better be small or this could take a while. + */ +static void +tfsinit(Tfs *fs) +{ + uchar dbuf[STATFIXLEN+4*KNAMELEN]; + Dir d; + uchar buf[Blen]; + ulong x, bno; + int n, done; + Tfile *f; + Mdir *mdir; + Mdata *mdata; + + n = devtab[fs->c->type]->stat(fs->c, dbuf, sizeof(dbuf)); + if(n == 0) + error(Eshortstat); + n = convM2D(dbuf, n, &d, nil); + if(n == 0) + error(Eshortstat); + fs->nblocks = d.length/Blen; + if(fs->nblocks < 3) + error("tinyfs medium too small"); + + /* bitmap for block usage */ + x = (fs->nblocks + 8 - 1)/8; + fs->map = malloc(x); + if(fs->map == nil) + error(Enomem); + memset(fs->map, 0x0, x); + for(bno = fs->nblocks; bno < x*8; bno++) + mapset(fs, bno); + + /* find files */ + for(bno = 0; bno < fs->nblocks; bno++){ + n = devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno); + if(n != Blen) + break; + + mdir = validdir(fs, buf); + if(mdir == 0) + continue; + + if(fs->nf >= fs->fsize) + expand(fs); + + f = &fs->f[fs->nf++]; + + x = GETS(mdir->bno); + mapset(fs, bno); + strncpy(f->name, mdir->name, sizeof(f->name)); + f->pin = GETS(mdir->pin); + f->bno = bno; + f->dbno = x; + f->fbno = Notabno; + } + + /* follow files */ + for(f = fs->f; f < &(fs->f[fs->nf]); f++){ + bno = f->dbno; + for(done = 0; !done;) { + if(isalloced(fs, bno)){ + freefile(fs, f, bno); + break; + } + n = devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno); + if(n != Blen){ + freefile(fs, f, bno); + break; + } + mdata = validdata(fs, buf, 0); + if(mdata == 0){ + freefile(fs, f, bno); + break; + } + mapset(fs, bno); + switch(mdata->type){ + case Tagdata: + bno = GETS(mdata->bno); + f->length += Dlen; + break; + case Tagend: + f->length += GETS(mdata->bno); + done = 1; + break; + } + if(done) + f->flag &= ~Fcreating; + } + } +} + +/* + * single directory + */ +static int +tinyfsgen(Chan *c, char*, Dirtab *tab, int ntab, int i, Dir *dp) +{ + Tfs *fs; + Tfile *f; + Qid qid; + + USED(ntab); + USED(tab); + + fs = &tinyfs.fs[c->dev]; + if(i >= fs->nf) + return -1; + if(i == DEVDOTDOT){ + mkqid(&qid, Qdir, 0, QTDIR); + devdir(c, qid, ".", 0, eve, 0555, dp); + return 1; + } + f = &fs->f[i]; + if(f->name[0] == 0) + return 0; + mkqid(&qid, i, 0, QTFILE); + devdir(c, qid, f->name, f->length, eve, 0664, dp); + return 1; +} + +static void +tinyfsinit(void) +{ + if(Nlen > KNAMELEN) + panic("tinyfsinit"); +} + +/* + * specifier is the name of a device in /dev + */ +static Chan* +tinyfsattach(char *spec) +{ + Tfs *fs; + Chan *c; + volatile struct { Chan *cc; } rock; + int i; + char buf[KNAMELEN*3]; + + if(*spec == 0) + error("bad specifier"); + + snprint(buf, sizeof(buf), "/dev/%s", spec); + rock.cc = namec(buf, Aopen, ORDWR, 0); + if(waserror()){ + cclose(rock.cc); + nexterror(); + } + + fs = 0; + for(i = 0; i < Maxfs; i++){ + fs = &tinyfs.fs[i]; + qlock(&fs->ql); + if(fs->r && eqchan(rock.cc, fs->c, 1)) + break; + qunlock(&fs->ql); + } + if(i < Maxfs){ + fs->r++; + qunlock(&fs->ql); + cclose(rock.cc); + } else { + for(fs = tinyfs.fs; fs < &tinyfs.fs[Maxfs]; fs++){ + qlock(&fs->ql); + if(fs->r == 0) + break; + qunlock(&fs->ql); + } + if(fs == &tinyfs.fs[Maxfs]) + error("too many tinyfs's"); + fs->c = rock.cc; + fs->r = 1; + fs->f = 0; + fs->nf = 0; + fs->fsize = 0; + tfsinit(fs); + qunlock(&fs->ql); + } + poperror(); + + c = devattach('F', spec); + c->dev = fs - tinyfs.fs; + c->qid.path = Qdir; + c->qid.type = QTDIR; + c->qid.vers = 0; + + return c; +} + +static Walkqid* +tinyfswalk(Chan *c, Chan *nc, char **name, int nname) +{ + Tfs *fs; + Walkqid *w; + + fs = &tinyfs.fs[c->dev]; + + qlock(&fs->ql); + if(waserror()){ + qunlock(&fs->ql); + nexterror(); + } + w = devwalk(c, nc, name, nname, 0, 0, tinyfsgen); + if(w != nil && w->clone!=nil && w->clone->qid.type != QTDIR){ + fs = &tinyfs.fs[w->clone->dev]; + fs->r++; + fs->f[(ulong)w->clone->qid.path].r++; + } + poperror(); + qunlock(&fs->ql); + return w; +} + +static int +tinyfsstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, tinyfsgen); +} + +static Chan* +tinyfsopen(Chan *c, int omode) +{ + Tfile *f; + volatile struct { Tfs *fs; } rock; + + rock.fs = &tinyfs.fs[c->dev]; + + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Eperm); + } else { + qlock(&rock.fs->ql); + if(waserror()){ + qunlock(&rock.fs->ql); + nexterror(); + } + switch(omode){ + case OTRUNC|ORDWR: + case OTRUNC|OWRITE: + f = newfile(rock.fs, rock.fs->f[c->qid.path].name); + rock.fs->f[c->qid.path].r--; + c->qid.path = f - rock.fs->f; + break; + case OREAD: + break; + default: + error(Eperm); + } + qunlock(&rock.fs->ql); + poperror(); + } + + return devopen(c, omode, 0, 0, tinyfsgen); +} + +static void +tinyfscreate(Chan *c, char *name, int omode, ulong perm) +{ + volatile struct { Tfs *fs; } rock; + Tfile *f; + + USED(perm); + + rock.fs = &tinyfs.fs[c->dev]; + + qlock(&rock.fs->ql); + if(waserror()){ + qunlock(&rock.fs->ql); + nexterror(); + } + f = newfile(rock.fs, name); + qunlock(&rock.fs->ql); + poperror(); + + c->qid.path = f - rock.fs->f; + c->qid.vers = 0; + c->mode = openmode(omode); +} + +static void +tinyfsremove(Chan *c) +{ + Tfs *fs; + Tfile *f; + + if(c->qid.type & QTDIR) + error(Eperm); + fs = &tinyfs.fs[c->dev]; + f = &fs->f[c->qid.path]; + qlock(&fs->ql); + freefile(fs, f, Notabno); + qunlock(&fs->ql); +} + +static void +tinyfsclose(Chan *c) +{ + volatile struct { Tfs *fs; } rock; + Tfile *f, *nf; + int i; + + rock.fs = &tinyfs.fs[c->dev]; + + qlock(&rock.fs->ql); + + /* dereference file and remove old versions */ + if(!waserror()){ + if(c->qid.path != Qdir){ + f = &rock.fs->f[c->qid.path]; + f->r--; + if(f->r == 0){ + if(f->flag & Frmonclose) + freefile(rock.fs, f, Notabno); + else if(f->flag & Fcreating){ + /* remove all other files with this name */ + for(i = 0; i < rock.fs->fsize; i++){ + nf = &rock.fs->f[i]; + if(f == nf) + continue; + if(strcmp(nf->name, f->name) == 0){ + if(nf->r) + nf->flag |= Frmonclose; + else + freefile(rock.fs, nf, Notabno); + } + } + f->flag &= ~Fcreating; + } + } + } + poperror(); + } + + /* dereference rock.fs and remove on zero refs */ + rock.fs->r--; + if(rock.fs->r == 0){ + if(rock.fs->f) + free(rock.fs->f); + rock.fs->f = 0; + rock.fs->nf = 0; + rock.fs->fsize = 0; + if(rock.fs->map) + free(rock.fs->map); + rock.fs->map = 0; + cclose(rock.fs->c); + rock.fs->c = 0; + } + qunlock(&rock.fs->ql); +} + +static long +tinyfsread(Chan *c, void *a, long n, vlong offset) +{ + volatile struct { Tfs *fs; } rock; + Tfile *f; + int sofar, i, off; + ulong bno; + Mdata *md; + uchar buf[Blen]; + uchar *p; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, tinyfsgen); + + p = a; + rock.fs = &tinyfs.fs[c->dev]; + f = &rock.fs->f[c->qid.path]; + if(offset >= f->length) + return 0; + + qlock(&rock.fs->ql); + if(waserror()){ + qunlock(&rock.fs->ql); + nexterror(); + } + if(n + offset >= f->length) + n = f->length - offset; + + /* walk to starting data block */ + if(0 && f->finger <= offset && f->fbno != Notabno){ + sofar = f->finger; + bno = f->fbno; + } else { + sofar = 0; + bno = f->dbno; + } + for(; sofar + Dlen <= offset; sofar += Dlen){ + md = readdata(rock.fs, bno, buf, 0); + if(md == 0) + error(Eio); + bno = GETS(md->bno); + } + + /* read data */ + off = offset%Dlen; + offset -= off; + for(sofar = 0; sofar < n; sofar += i){ + md = readdata(rock.fs, bno, buf, &i); + if(md == 0) + error(Eio); + + /* update finger for successful read */ + f->finger = offset; + f->fbno = bno; + offset += Dlen; + + i -= off; + if(i > n - sofar) + i = n - sofar; + memmove(p, md->data+off, i); + p += i; + bno = GETS(md->bno); + off = 0; + } + qunlock(&rock.fs->ql); + poperror(); + + return sofar; +} + +/* + * if we get a write error in this routine, blocks will + * be lost. They should be recovered next fsinit. + */ +static long +tinyfswrite(Chan *c, void *a, long n, vlong offset) +{ + Tfile *f; + int last, next, i, finger, off, used; + ulong bno, fbno; + Mdata *md; + uchar buf[Blen]; + uchar *p; + volatile struct { + Tfs *fs; + ulong dbno; + } rock; + + if(c->qid.type & QTDIR) + error(Eperm); + + if(n == 0) + return 0; + + p = a; + rock.fs = &tinyfs.fs[c->dev]; + f = &rock.fs->f[c->qid.path]; + + qlock(&rock.fs->ql); + rock.dbno = Notabno; + if(waserror()){ + freeblocks(rock.fs, rock.dbno, Notabno); + qunlock(&rock.fs->ql); + nexterror(); + } + + /* files are append only, anything else is illegal */ + if(offset != f->length) + error("append only"); + + /* write blocks backwards */ + p += n; + last = offset + n; + fbno = Notabno; + finger = 0; + off = offset; /* so we have something signed to compare against */ + for(next = ((last-1)/Dlen)*Dlen; next >= off; next -= Dlen){ + bno = mapalloc(rock.fs); + if(bno == Notabno) + error("out of space"); + i = last - next; + p -= i; + if(last == n+offset){ + writedata(rock.fs, bno, rock.dbno, p, i, 1); + finger = next; /* remember for later */ + fbno = bno; + } else { + writedata(rock.fs, bno, rock.dbno, p, i, 0); + } + rock.dbno = bno; + last = next; + } + + /* walk to last data block */ + md = (Mdata*)buf; + if(0 && f->finger < offset && f->fbno != Notabno){ + next = f->finger; + bno = f->fbno; + } else { + next = 0; + bno = f->dbno; + } + + used = 0; + while(bno != Notabno){ + md = readdata(rock.fs, bno, buf, &used); + if(md == 0) + error(Eio); + if(md->type == Tagend){ + if(next + Dlen < offset) + panic("devtinyfs1"); + break; + } + next += Dlen; + if(next > offset) + panic("devtinyfs1"); + bno = GETS(md->bno); + } + + /* point to new blocks */ + if(offset == 0){ + /* first block in a file */ + f->dbno = rock.dbno; + writedir(rock.fs, f); + } else { + /* updating a current block */ + i = last - offset; + if(i > 0){ + p -= i; + memmove(md->data + used, p, i); + used += i; + } + writedata(rock.fs, bno, rock.dbno, md->data, used, last == n+offset); + } + f->length += n; + + /* update finger */ + if(fbno != Notabno){ + f->finger = finger; + f->fbno = fbno; + } + poperror(); + qunlock(&rock.fs->ql); + + return n; +} + +Dev tinyfsdevtab = { + 'F', + "tinyfs", + + devreset, + tinyfsinit, + devshutdown, + tinyfsattach, + tinyfswalk, + tinyfsstat, + tinyfsopen, + tinyfscreate, + tinyfsclose, + tinyfsread, + devbread, + tinyfswrite, + devbwrite, + tinyfsremove, + devwstat, +}; diff --git a/os/port/devtk.c b/os/port/devtk.c new file mode 100644 index 00000000..61afd667 --- /dev/null +++ b/os/port/devtk.c @@ -0,0 +1,180 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include + +#include "draw.h" + +enum{ + Qdir, + Qtkevents +}; + +static +Dirtab tkdirtab[]={ + {".", {Qdir,0,QTDIR}, 0, 0500}, + {"tkevents", {Qtkevents, 0}, 0, 0600}, +}; + +static struct { + QLock l; + Queue* eq; + Ref inuse; +} tkevents; + +static void +tkwiretapper(void *top, char *cmd, char *result, void *image, Rectangle *rp) +{ + Block *b; + int n; + char *s, *e; + + n = 12; + if(cmd != nil) + n += strlen(cmd)+2+1; + if(result != nil) + n += strlen(result)+2+1; + if(image != nil) + n += 12; + if(rp != nil) + n += 4*20; + n++; + b = allocb(n); + if(b != nil){ + s = (char*)b->wp; + e = s+n; + s += snprint(s, e-s, "%p", top); + if(cmd != nil){ + *s++ = ' '; + *s++ = '['; + n = strlen(cmd); + memmove(s, cmd, n); + s += n; + *s++ = ']'; + } + /* ignore result for now */ + if(image != nil) + s += snprint(s, e-s, " %p", image); + if(rp != nil) + s += snprint(s, e-s, " %d %d %d %d", rp->min.x, rp->min.y, rp->max.x, rp->max.y); + *s++ = '\n'; + b->wp = (uchar*)s; + release(); + qlock(&tkevents.l); + if(waserror()){ + qunlock(&tkevents.l); + acquire(); + return; + } + if(tkevents.eq != nil) + qbwrite(tkevents.eq, b); + poperror(); + qunlock(&tkevents.l); + acquire(); + } +} + +void (*tkwiretap)(void*, char*, char*, void*, Rectangle*); + +static Chan* +tkattach(char* spec) +{ + return devattach(L'τ', spec); +} + +static Walkqid* +tkwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, tkdirtab, nelem(tkdirtab), devgen); +} + +static int +tkstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, tkdirtab, nelem(tkdirtab), devgen); +} + +static Chan* +tkopen(Chan* c, int omode) +{ + if(c->qid.type & QTDIR) + return devopen(c, omode, tkdirtab, nelem(tkdirtab), devgen); + switch((ulong)c->qid.path){ + case Qtkevents: + c = devopen(c, omode, tkdirtab, nelem(tkdirtab), devgen); + qlock(&tkevents.l); + if(incref(&tkevents.inuse) != 1){ + qunlock(&tkevents.l); + error(Einuse); + } + if(tkevents.eq == nil) + tkevents.eq = qopen(256*1024, 0, nil, nil); + else + qreopen(tkevents.eq); + tkwiretap = tkwiretapper; + qunlock(&tkevents.l); + break; + } + return c; +} + +static void +tkclose(Chan* c) +{ + if(c->qid.type & QTDIR || (c->flag & COPEN) == 0) + return; + qlock(&tkevents.l); + if(decref(&tkevents.inuse) == 0){ + tkwiretap = nil; + qclose(tkevents.eq); + } + qunlock(&tkevents.l); +} + +static long +tkread(Chan* c, void* a, long n, vlong offset) +{ + USED(offset); + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, tkdirtab, nelem(tkdirtab), devgen); + case Qtkevents: + return qread(tkevents.eq, a, n); + default: + n=0; + break; + } + return n; +} + +static long +tkwrite(Chan*, void*, long, vlong) +{ + error(Ebadusefd); + return 0; +} + +Dev tkdevtab = { + L'τ', + "tk", + + devreset, + devinit, + devshutdown, + tkattach, + tkwalk, + tkstat, + tkopen, + devcreate, + tkclose, + tkread, + devbread, + tkwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/port/devuart.c b/os/port/devuart.c new file mode 100644 index 00000000..52e1841f --- /dev/null +++ b/os/port/devuart.c @@ -0,0 +1,748 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "../port/netif.h" + +#include "../port/uart.h" + +enum +{ + /* soft flow control chars */ + CTLS= 023, + CTLQ= 021, +}; + +extern Dev uartdevtab; +extern PhysUart* physuart[]; + +static Uart* uartlist; +static Uart** uart; +static int uartnuart; +static Dirtab *uartdir; +static int uartndir; + +struct Uartalloc { + Lock; + Uart *elist; /* list of enabled interfaces */ +} uartalloc; + +static void uartclock(void); +static void uartflow(void*); + +/* + * enable/disable uart and add/remove to list of enabled uarts + */ +static Uart* +uartenable(Uart *p) +{ + Uart **l; + + if(p->iq == nil){ + if((p->iq = qopen(4*1024, 0, uartflow, p)) == nil) + return nil; + } + else + qreopen(p->iq); + if(p->oq == nil){ + if((p->oq = qopen(4*1024, 0, uartkick, p)) == nil){ + qfree(p->iq); + p->iq = nil; + return nil; + } + } + else + qreopen(p->oq); + + p->ir = p->istage; + p->iw = p->istage; + p->ie = &p->istage[Stagesize]; + p->op = p->ostage; + p->oe = p->ostage; + + p->hup_dsr = p->hup_dcd = 0; + p->dsr = p->dcd = 0; + + /* assume we can send */ + p->cts = 1; + p->ctsbackoff = 0; + + if(p->bits == 0) + uartctl(p, "l8"); + if(p->stop == 0) + uartctl(p, "s1"); + if(p->parity == 0) + uartctl(p, "pn"); + if(p->baud == 0) + uartctl(p, "b9600"); + (*p->phys->enable)(p, 1); + + lock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p) + break; + } + if(*l == 0){ + p->elist = uartalloc.elist; + uartalloc.elist = p; + } + p->enabled = 1; + unlock(&uartalloc); + + return p; +} + +static void +uartdisable(Uart *p) +{ + Uart **l; + + (*p->phys->disable)(p); + + lock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p){ + *l = p->elist; + break; + } + } + p->enabled = 0; + unlock(&uartalloc); +} + +void +uartmouse(Uart* p, int (*putc)(Queue*, int), int setb1200) +{ + qlock(p); + if(p->opens++ == 0 && uartenable(p) == nil){ + qunlock(p); + error(Enodev); + } + if(setb1200) + uartctl(p, "b1200"); + p->putc = putc; + p->special = 1; + qunlock(p); +} + +void +uartsetmouseputc(Uart* p, int (*putc)(Queue*, int)) +{ + qlock(p); + if(p->opens == 0 || p->special == 0){ + qunlock(p); + error(Enodev); + } + p->putc = putc; + qunlock(p); +} + +static void +setlength(int i) +{ + Uart *p; + + if(i > 0){ + p = uart[i]; + if(p && p->opens && p->iq) + uartdir[1+3*i].length = qlen(p->iq); + } else for(i = 0; i < uartnuart; i++){ + p = uart[i]; + if(p && p->opens && p->iq) + uartdir[1+3*i].length = qlen(p->iq); + } +} + +/* + * set up the '#t' directory + */ +static void +uartreset(void) +{ + int i; + Dirtab *dp; + Uart *p, *tail; + + tail = nil; + for(i = 0; physuart[i] != nil; i++){ + if(physuart[i]->pnp == nil) + continue; + if((p = physuart[i]->pnp()) == nil) + continue; + if(uartlist != nil) + tail->next = p; + else + uartlist = p; + for(tail = p; tail->next != nil; tail = tail->next) + uartnuart++; + uartnuart++; + } + + if(uartnuart) + uart = xalloc(uartnuart*sizeof(Uart*)); + + uartndir = 1 + 3*uartnuart; + uartdir = xalloc(uartndir * sizeof(Dirtab)); + dp = uartdir; + strcpy(dp->name, "."); + mkqid(&dp->qid, 0, 0, QTDIR); + dp->length = 0; + dp->perm = DMDIR|0555; + dp++; + p = uartlist; + for(i = 0; i < uartnuart; i++){ + /* 3 directory entries per port */ + sprint(dp->name, "eia%d", i); + dp->qid.path = NETQID(i, Ndataqid); + dp->perm = 0660; + dp++; + sprint(dp->name, "eia%dctl", i); + dp->qid.path = NETQID(i, Nctlqid); + dp->perm = 0660; + dp++; + sprint(dp->name, "eia%dstatus", i); + dp->qid.path = NETQID(i, Nstatqid); + dp->perm = 0444; + dp++; + + uart[i] = p; + p->dev = i; + if(p->console || p->special){ + if(uartenable(p) != nil){ + if(p->console){ + kbdq = p->iq; + printq = p->oq; + p->putc = kbdcr2nl; + } + p->opens++; + } + } + p = p->next; + } + + if(uartnuart){ + /* + * at 115200 baud, the 1024 char buffer takes 56 ms to process, + * processing it every 22 ms should be fine + */ + addclock0link(uartclock, 22); + } +} + + +static Chan* +uartattach(char *spec) +{ + return devattach('t', spec); +} + +static Walkqid* +uartwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, uartdir, uartndir, devgen); +} + +static int +uartstat(Chan *c, uchar *dp, int n) +{ + if(NETTYPE(c->qid.path) == Ndataqid) + setlength(NETID(c->qid.path)); + return devstat(c, dp, n, uartdir, uartndir, devgen); +} + +static Chan* +uartopen(Chan *c, int omode) +{ + Uart *p; + + c = devopen(c, omode, uartdir, uartndir, devgen); + + switch(NETTYPE(c->qid.path)){ + case Nctlqid: + case Ndataqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(p->opens++ == 0 && uartenable(p) == nil){ + qunlock(p); + c->flag &= ~COPEN; + error(Enodev); + } + qunlock(p); + break; + } + + c->iounit = qiomaxatomic; + return c; +} + +static int +uartdrained(void* arg) +{ + Uart *p; + + p = arg; + return qlen(p->oq) == 0 && p->op == p->oe; +} + +static void +uartdrainoutput(Uart *p) +{ + if(!p->enabled) + return; + + p->drain = 1; + if(waserror()){ + p->drain = 0; + nexterror(); + } + sleep(&p->r, uartdrained, p); + poperror(); +} + +static void +uartclose(Chan *c) +{ + Uart *p; + + if(c->qid.type & QTDIR) + return; + if((c->flag & COPEN) == 0) + return; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + case Nctlqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(--(p->opens) == 0){ + qclose(p->iq); + p->ir = p->iw = p->istage; + + /* + */ + qhangup(p->oq, nil); + if(!waserror()){ + uartdrainoutput(p); + poperror(); + } + qclose(p->oq); + uartdisable(p); + p->dcd = p->dsr = p->dohup = 0; + } + qunlock(p); + break; + } +} + +static long +uartread(Chan *c, void *buf, long n, vlong off) +{ + Uart *p; + ulong offset = off; + + if(c->qid.type & QTDIR){ + setlength(-1); + return devdirread(c, buf, n, uartdir, uartndir, devgen); + } + + p = uart[NETID(c->qid.path)]; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qread(p->iq, buf, n); + case Nctlqid: + return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE); + case Nstatqid: + return (*p->phys->status)(p, buf, n, offset); + } + + return 0; +} + +int +uartctl(Uart *p, char *cmd) +{ + char *f[16]; + int i, n, nf; + + nf = tokenize(cmd, f, nelem(f)); + for(i = 0; i < nf; i++){ + if(strncmp(f[i], "break", 5) == 0){ + (*p->phys->dobreak)(p, 0); + continue; + } + + n = atoi(f[i]+1); + switch(*f[i]){ + case 'B': + case 'b': + uartdrainoutput(p); + if((*p->phys->baud)(p, n) < 0) + return -1; + break; + case 'C': + case 'c': + p->hup_dcd = n; + break; + case 'D': + case 'd': + uartdrainoutput(p); + (*p->phys->dtr)(p, n); + break; + case 'E': + case 'e': + p->hup_dsr = n; + break; + case 'f': + case 'F': + if(p->oq != nil) + qflush(p->oq); + break; + case 'H': + case 'h': + if(p->iq != nil) + qhangup(p->iq, 0); + if(p->oq != nil) + qhangup(p->oq, 0); + break; + case 'i': + case 'I': + uartdrainoutput(p); + (*p->phys->fifo)(p, n); + break; + case 'K': + case 'k': + uartdrainoutput(p); + (*p->phys->dobreak)(p, n); + break; + case 'L': + case 'l': + uartdrainoutput(p); + if((*p->phys->bits)(p, n) < 0) + return -1; + break; + case 'm': + case 'M': + uartdrainoutput(p); + (*p->phys->modemctl)(p, n); + break; + case 'n': + case 'N': + if(p->oq != nil) + qnoblock(p->oq, n); + break; + case 'P': + case 'p': + uartdrainoutput(p); + if((*p->phys->parity)(p, *(f[i]+1)) < 0) + return -1; + break; + case 'Q': + case 'q': + if(p->iq != nil) + qsetlimit(p->iq, n); + if(p->oq != nil) + qsetlimit(p->oq, n); + break; + case 'R': + case 'r': + uartdrainoutput(p); + (*p->phys->rts)(p, n); + break; + case 'S': + case 's': + uartdrainoutput(p); + if((*p->phys->stop)(p, n) < 0) + return -1; + break; + case 'T': + case 't': + p->dcdts = n; + break; + case 'W': + case 'w': + /* obsolete */ + break; + case 'X': + case 'x': + if(p->enabled){ + ilock(&p->tlock); + p->xonoff = n; + iunlock(&p->tlock); + } + break; + } + } + return 0; +} + +static long +uartwrite(Chan *c, void *buf, long n, vlong) +{ + Uart *p; + char *cmd; + + if(c->qid.type & QTDIR) + error(Eperm); + + p = uart[NETID(c->qid.path)]; + + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + qlock(p); + if(waserror()){ + qunlock(p); + nexterror(); + } + + n = qwrite(p->oq, buf, n); + + qunlock(p); + poperror(); + break; + case Nctlqid: + cmd = malloc(n+1); + memmove(cmd, buf, n); + cmd[n] = 0; + qlock(p); + if(waserror()){ + qunlock(p); + free(cmd); + nexterror(); + } + + /* let output drain */ + if(uartctl(p, cmd) < 0) + error(Ebadarg); + + qunlock(p); + poperror(); + free(cmd); + break; + } + + return n; +} + +static int +uartwstat(Chan *c, uchar *dp, int n) +{ + Dir d; + Dirtab *dt; + + if(!iseve()) + error(Eperm); + if(QTDIR & c->qid.type) + error(Eperm); + if(NETTYPE(c->qid.path) == Nstatqid) + error(Eperm); + + dt = &uartdir[1 + 3 * NETID(c->qid.path)]; + n = convM2D(dp, n, &d, nil); + if(n == 0) + error(Eshortstat); + if(d.mode != ~0UL) + dt[0].perm = dt[1].perm = d.mode; + return n; +} + +void +uartpower(int on) +{ + Uart *p; + + for(p = uartlist; p != nil; p = p->next) { + if(p->phys->power) + (*p->phys->power)(p, on); + } +} + +Dev uartdevtab = { + 't', + "uart", + + uartreset, + devinit, + devshutdown, + uartattach, + uartwalk, + uartstat, + uartopen, + devcreate, + uartclose, + uartread, + devbread, + uartwrite, + devbwrite, + devremove, + uartwstat, + uartpower, +}; + +/* + * restart input if it's off + */ +static void +uartflow(void *v) +{ + Uart *p; + + p = v; + if(p->modem) + (*p->phys->rts)(p, 1); +} + +/* + * put some bytes into the local queue to avoid calling + * qconsume for every character + */ +int +uartstageoutput(Uart *p) +{ + int n; + + n = qconsume(p->oq, p->ostage, Stagesize); + if(n <= 0) + return 0; + p->op = p->ostage; + p->oe = p->ostage + n; + return n; +} + +/* + * restart output + */ +void +uartkick(void *v) +{ + Uart *p = v; + + if(p->blocked) + return; + + ilock(&p->tlock); + (*p->phys->kick)(p); + iunlock(&p->tlock); + + if(p->drain && uartdrained(p)){ + p->drain = 0; + wakeup(&p->r); + } +} + +/* + * receive a character at interrupt time + */ +void +uartrecv(Uart *p, char ch) +{ + uchar *next; + + /* software flow control */ + if(p->xonoff){ + if(ch == CTLS){ + p->blocked = 1; + }else if(ch == CTLQ){ + p->blocked = 0; + p->ctsbackoff = 2; /* clock gets output going again */ + } + } + + /* receive the character */ + if(p->putc) + p->putc(p->iq, ch); + else{ + next = p->iw + 1; + if(next == p->ie) + next = p->istage; + if(next != p->ir){ + *p->iw = ch; + p->iw = next; + } + } +} + +/* + * we save up input characters till clock time to reduce + * per character interrupt overhead. + */ +static void +uartclock(void) +{ + Uart *p; + uchar *iw; + + for(p = uartalloc.elist; p; p = p->elist){ + + /* this amortizes cost of qproduce to many chars */ + if(p->iw != p->ir){ + iw = p->iw; + if(iw < p->ir){ + if(qproduce(p->iq, p->ir, p->ie-p->ir) < 0) + (*p->phys->rts)(p, 0); + p->ir = p->istage; + } + if(iw > p->ir) + if(qproduce(p->iq, p->ir, iw-p->ir) < 0) + (*p->phys->rts)(p, 0); + p->ir = iw; + } + + /* hang up if requested */ + if(p->dohup){ + qhangup(p->iq, 0); + qhangup(p->oq, 0); + p->dohup = 0; + } + + /* this adds hysteresis to hardware/software flow control */ + if(p->ctsbackoff){ + ilock(&p->tlock); + if(p->ctsbackoff){ + if(--(p->ctsbackoff) == 0) + (*p->phys->kick)(p); + } + iunlock(&p->tlock); + } + } +} + +/* + * polling console input, output + */ + +Uart* consuart; + +int +uartgetc(void) +{ + if(consuart == nil || consuart->phys->getc == nil) + return -1; + return consuart->phys->getc(consuart); +} + +void +uartputc(int c) +{ + if(consuart == nil || consuart->phys->putc == nil) + return; + consuart->phys->putc(consuart, c); +} + +void +uartputs(char *s, int n) +{ + char *e; + + if(consuart == nil || consuart->phys->putc == nil) + return; + + e = s+n; + for(; sphys->putc(consuart, '\r'); + consuart->phys->putc(consuart, *s); + } +} diff --git a/os/port/dial.c b/os/port/dial.c new file mode 100644 index 00000000..d07671dc --- /dev/null +++ b/os/port/dial.c @@ -0,0 +1,417 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "kernel.h" + +typedef struct DS DS; + +static int call(char*, char*, DS*); +static int csdial(DS*); +static void _dial_string_parse(char*, DS*); +static int nettrans(char*, char*, int na, char*, int); + +enum +{ + Maxstring= 128, + Maxpath= 100 +}; + +struct DS +{ + char buf[Maxstring]; /* dist string */ + char *netdir; + char *proto; + char *rem; + char *local; /* other args */ + char *dir; + int *cfdp; +}; + +/* + * the dialstring is of the form '[/net/]proto!dest' + */ +int +kdial(char *dest, char *local, char *dir, int *cfdp) +{ + DS ds; + int rv; + char err[ERRMAX], alterr[ERRMAX]; + + ds.local = local; + ds.dir = dir; + ds.cfdp = cfdp; + + _dial_string_parse(dest, &ds); + if(ds.netdir) + return csdial(&ds); + + ds.netdir = "/net"; + rv = csdial(&ds); + if(rv >= 0) + return rv; + + err[0] = 0; + kerrstr(err, sizeof err); + if(strstr(err, "refused") != 0){ + kerrstr(err, sizeof err); + return rv; + } + + ds.netdir = "/net.alt"; + rv = csdial(&ds); + if(rv >= 0) + return rv; + + alterr[0] = 0; + kerrstr(alterr, sizeof err); + + if(strstr(alterr, "translate") || strstr(alterr, "does not exist")) + kerrstr(err, sizeof err); + else + kerrstr(alterr, sizeof alterr); + return rv; +} + +static int +csdial(DS *ds) +{ + int n, fd, rv; + char *p, buf[Maxstring], clone[Maxpath], err[ERRMAX], besterr[ERRMAX]; + + /* + * open connection server + */ + snprint(buf, sizeof(buf), "%s/cs", ds->netdir); + fd = kopen(buf, ORDWR); + if(fd < 0){ + /* no connection server, don't translate */ + snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto); + return call(clone, ds->rem, ds); + } + + /* + * ask connection server to translate + */ + sprint(buf, "%s!%s", ds->proto, ds->rem); + if(kwrite(fd, buf, strlen(buf)) < 0){ + kerrstr(err, sizeof err); + kclose(fd); + kwerrstr("%s (%s)", err, buf); + return -1; + } + + /* + * loop through each address from the connection server till + * we get one that works. + */ + *besterr = 0; + strcpy(err, Egreg); + rv = -1; + kseek(fd, 0, 0); + while((n = kread(fd, buf, sizeof(buf) - 1)) > 0){ + buf[n] = 0; + p = strchr(buf, ' '); + if(p == 0) + continue; + *p++ = 0; + rv = call(buf, p, ds); + if(rv >= 0) + break; + err[0] = 0; + kerrstr(err, sizeof err); + if(strstr(err, "does not exist") == 0) + memmove(besterr, err, sizeof besterr); + } + kclose(fd); + + if(rv < 0 && *besterr) + kerrstr(besterr, sizeof besterr); + else + kerrstr(err, sizeof err); + return rv; +} + +static int +call(char *clone, char *dest, DS *ds) +{ + int fd, cfd, n; + char name[Maxpath], data[Maxpath], err[ERRMAX], *p; + + cfd = kopen(clone, ORDWR); + if(cfd < 0){ + kerrstr(err, sizeof err); + kwerrstr("%s (%s)", err, clone); + return -1; + } + + /* get directory name */ + n = kread(cfd, name, sizeof(name)-1); + if(n < 0){ + kerrstr(err, sizeof err); + kclose(cfd); + kwerrstr("read %s: %s", clone, err); + return -1; + } + name[n] = 0; + for(p = name; *p == ' '; p++) + ; + sprint(name, "%ld", strtoul(p, 0, 0)); + p = strrchr(clone, '/'); + *p = 0; + if(ds->dir) + snprint(ds->dir, NETPATHLEN, "%s/%s", clone, name); + snprint(data, sizeof(data), "%s/%s/data", clone, name); + + /* connect */ + if(ds->local) + snprint(name, sizeof(name), "connect %s %s", dest, ds->local); + else + snprint(name, sizeof(name), "connect %s", dest); + if(kwrite(cfd, name, strlen(name)) < 0){ + err[0] = 0; + kerrstr(err, sizeof err); + kclose(cfd); + kwerrstr("%s (%s)", err, name); + return -1; + } + + /* open data connection */ + fd = kopen(data, ORDWR); + if(fd < 0){ + err[0] = 0; + kerrstr(err, sizeof err); + kwerrstr("%s (%s)", err, data); + kclose(cfd); + return -1; + } + if(ds->cfdp) + *ds->cfdp = cfd; + else + kclose(cfd); + + return fd; +} + +/* + * parse a dial string + */ +static void +_dial_string_parse(char *str, DS *ds) +{ + char *p, *p2; + + strncpy(ds->buf, str, Maxstring); + ds->buf[Maxstring-1] = 0; + + p = strchr(ds->buf, '!'); + if(p == 0) { + ds->netdir = 0; + ds->proto = "net"; + ds->rem = ds->buf; + } else { + if(*ds->buf != '/' && *ds->buf != '#'){ + ds->netdir = 0; + ds->proto = ds->buf; + } else { + for(p2 = p; *p2 != '/'; p2--) + ; + *p2++ = 0; + ds->netdir = ds->buf; + ds->proto = p2; + } + *p = 0; + ds->rem = p + 1; + } +} + +/* + * announce a network service. + */ +int +kannounce(char *addr, char *dir) +{ + int ctl, n, m; + char buf[NETPATHLEN]; + char buf2[Maxpath]; + char netdir[NETPATHLEN]; + char naddr[Maxpath]; + char *cp; + + /* + * translate the address + */ + if(nettrans(addr, naddr, sizeof(naddr), netdir, sizeof(netdir)) < 0) + return -1; + + /* + * get a control channel + */ + ctl = kopen(netdir, ORDWR); + if(ctl<0) + return -1; + cp = strrchr(netdir, '/'); + *cp = 0; + + /* + * find out which line we have + */ + n = sprint(buf, "%.*s/", sizeof buf, netdir); + m = kread(ctl, &buf[n], sizeof(buf)-n-1); + if(m <= 0){ + kclose(ctl); + return -1; + } + buf[n+m] = 0; + + /* + * make the call + */ + n = snprint(buf2, sizeof buf2, "announce %s", naddr); + if(kwrite(ctl, buf2, n)!=n){ + kclose(ctl); + return -1; + } + + /* + * return directory etc. + */ + if(dir) + strcpy(dir, buf); + return ctl; +} + +/* + * listen for an incoming call + */ +int +klisten(char *dir, char *newdir) +{ + int ctl, n, m; + char buf[NETPATHLEN]; + char *cp; + + /* + * open listen, wait for a call + */ + snprint(buf, sizeof buf, "%s/listen", dir); + ctl = kopen(buf, ORDWR); + if(ctl < 0) + return -1; + + /* + * find out which line we have + */ + strcpy(buf, dir); + cp = strrchr(buf, '/'); + *++cp = 0; + n = cp-buf; + m = kread(ctl, cp, sizeof(buf) - n - 1); + if(m <= 0){ + kclose(ctl); + return -1; + } + buf[n+m] = 0; + + /* + * return directory etc. + */ + if(newdir) + strcpy(newdir, buf); + return ctl; + +} + +/* + * perform the identity translation (in case we can't reach cs) + */ +static int +identtrans(char *netdir, char *addr, char *naddr, int na, char *file, int nf) +{ + char proto[Maxpath]; + char *p; + + USED(nf); + + /* parse the protocol */ + strncpy(proto, addr, sizeof(proto)); + proto[sizeof(proto)-1] = 0; + p = strchr(proto, '!'); + if(p) + *p++ = 0; + + snprint(file, nf, "%s/%s/clone", netdir, proto); + strncpy(naddr, p, na); + naddr[na-1] = 0; + + return 1; +} + +/* + * call up the connection server and get a translation + */ +static int +nettrans(char *addr, char *naddr, int na, char *file, int nf) +{ + int i, fd; + char buf[Maxpath]; + char netdir[NETPATHLEN]; + char *p, *p2; + long n; + + /* + * parse, get network directory + */ + p = strchr(addr, '!'); + if(p == 0){ + kwerrstr("bad dial string: %s", addr); + return -1; + } + if(*addr != '/'){ + strcpy(netdir, "/net"); + } else { + for(p2 = p; *p2 != '/'; p2--) + ; + i = p2 - addr; + if(i == 0 || i >= sizeof(netdir)){ + kwerrstr("bad dial string: %s", addr); + return -1; + } + strncpy(netdir, addr, i); + netdir[i] = 0; + addr = p2 + 1; + } + + /* + * ask the connection server + */ + sprint(buf, "%s/cs", netdir); + fd = kopen(buf, ORDWR); + if(fd < 0) + return identtrans(netdir, addr, naddr, na, file, nf); + if(kwrite(fd, addr, strlen(addr)) < 0){ + kclose(fd); + return -1; + } + kseek(fd, 0, 0); + n = kread(fd, buf, sizeof(buf)-1); + kclose(fd); + if(n <= 0) + return -1; + buf[n] = 0; + + /* + * parse the reply + */ + p = strchr(buf, ' '); + if(p == 0) + return -1; + *p++ = 0; + strncpy(naddr, p, na); + naddr[na-1] = 0; + strncpy(file, buf, nf); + file[nf-1] = 0; + return 0; +} diff --git a/os/port/dis.c b/os/port/dis.c new file mode 100644 index 00000000..2b5ac937 --- /dev/null +++ b/os/port/dis.c @@ -0,0 +1,1125 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include +#include +#include +#include "raise.h" + + void vmachine(void*); + +struct +{ + Lock l; + Prog* runhd; + Prog* runtl; + Prog* head; + Prog* tail; + Rendez irend; + int idle; + int nyield; + int creating; + Proc* vmq; /* queue of procs wanting vm */ + Proc* vmqt; + Proc* idlevmq; /* queue of procs wanting work */ + Atidle* idletasks; +} isched; + +int bflag; +int cflag; +uvlong gcbusy; +uvlong gcidle; +uvlong gcidlepass; +uvlong gcpartial; +int keepbroken = 1; +static Prog* proghash[64]; + +static Progs* delgrp(Prog*); +static void addgrp(Prog*, Prog*); +void printgrp(Prog*, char*); + +static Prog** +pidlook(int pid) +{ + ulong h; + Prog **l; + + h = (ulong)pid % nelem(proghash); + for(l = &proghash[h]; *l != nil && (*l)->pid != pid; l = &(*l)->pidlink) + ; + return l; +} + +int +tready(void *a) +{ + USED(a); + return isched.runhd != nil || isched.vmq != nil; +} + +Prog* +progpid(int pid) +{ + return *pidlook(pid); +} + +Prog* +progn(int n) +{ + Prog *p; + + for(p = isched.head; p && n--; p = p->next) + ; + return p; +} + +int +nprog(void) +{ + int n; + Prog *p; + + n = 0; + for(p = isched.head; p; p = p->next) + n++; + return n; +} + +static void +accountant(void) +{ + Prog *p; + + p = isched.runhd; + if(p != nil) + p->ticks++; +} + +static void +execatidle(void) +{ + int done; + + if(tready(nil)) + return; + + gcidle++; + up->type = IdleGC; + up->iprog = nil; + addrun(up->prog); + + done = gccolor+3; + while(gccolor < done && gcruns()) { + if(isched.vmq != nil || isched.runhd != isched.runtl) { + gcpartial++; + break; + } + rungc(isched.head); + gcidlepass++; + sched(); + } + + up->type = Interp; + delrunq(up->prog); +} + +Prog* +newprog(Prog *p, Modlink *m) +{ + Heap *h; + Prog *n, **ph; + Osenv *on, *op; + static int pidnum; + + n = malloc(sizeof(Prog)+sizeof(Osenv)); + if(n == 0){ + if(p == nil) + panic("no memory"); + else + error(exNomem); + } + + n->pid = ++pidnum; + if(n->pid <= 0) + panic("no pids"); + n->group = nil; + + if(isched.tail != nil) { + n->prev = isched.tail; + isched.tail->next = n; + } + else { + isched.head = n; + n->prev = nil; + } + isched.tail = n; + + ph = pidlook(n->pid); + if(*ph != nil) + panic("dup pid"); + n->pidlink = nil; + *ph = n; + + n->osenv = (Osenv*)((uchar*)n + sizeof(Prog)); + n->xec = xec; + n->quanta = PQUANTA; + n->flags = 0; + n->exval = H; + + h = D2H(m); + h->ref++; + Setmark(h); + n->R.M = m; + n->R.MP = m->MP; + if(m->MP != H) + Setmark(D2H(m->MP)); + addrun(n); + + if(p == nil){ + newgrp(n); + return n; + } + + addgrp(n, p); + n->flags = p->flags; + if(p->flags & Prestrict) + n->flags |= Prestricted; + memmove(n->osenv, p->osenv, sizeof(Osenv)); + op = p->osenv; + on = n->osenv; + on->waitq = op->childq; + on->childq = nil; + on->debug = nil; + incref(on->pgrp); + incref(on->fgrp); + if(on->egrp != nil) + incref(on->egrp); + if(on->sigs != nil) + incref(on->sigs); + on->user = nil; + kstrdup(&on->user, op->user); + on->errstr = on->errbuf0; + on->syserrstr = on->errbuf1; + + return n; +} + +void +delprog(Prog *p, char *msg) +{ + Osenv *o; + Prog **ph; + + tellsomeone(p, msg); /* call before being removed from prog list */ + + o = p->osenv; + release(); + closepgrp(o->pgrp); + closefgrp(o->fgrp); + closeegrp(o->egrp); + closesigs(o->sigs); + acquire(); + + delgrp(p); + + if(p->prev) + p->prev->next = p->next; + else + isched.head = p->next; + + if(p->next) + p->next->prev = p->prev; + else + isched.tail = p->prev; + + ph = pidlook(p->pid); + if(*ph == nil) + panic("lost pid"); + *ph = p->pidlink; + + if(p == isched.runhd) { + isched.runhd = p->link; + if(p->link == nil) + isched.runtl = nil; + } + p->state = 0xdeadbeef; + free(o->user); + free(p->killstr); + free(p->exstr); + free(p); +} + +void +renameproguser(char *old, char *new) +{ + Prog *p; + Osenv *o; + + acquire(); + for(p = isched.head; p; p = p->next){ + o = p->osenv; + if(o->user != nil && strcmp(o->user, old) == 0) + kstrdup(&o->user, new); + } + release(); +} + +void +tellsomeone(Prog *p, char *buf) +{ + Osenv *o; + + if(waserror()) + return; + o = p->osenv; + if(o->childq != nil) + qproduce(o->childq, buf, strlen(buf)); + if(o->waitq != nil) + qproduce(o->waitq, buf, strlen(buf)); + poperror(); +} + +static void +swiprog(Prog *p) +{ + Proc *q, *eq; + + q = proctab(0); + for(eq = q+conf.nproc; q < eq; q++) { + if(q->iprog == p) { + swiproc(q, 1); + return; + } + } + /*print("didn't find\n");*/ +} + +static Prog* +grpleader(Prog *p) +{ + Progs *g; + Prog *l; + + g = p->group; + if(g != nil && (l = g->head) != nil && l->pid == g->id) + return l; + return nil; +} + +int +exprog(Prog *p, char *exc) +{ + switch(p->state) { + case Palt: + altdone(p->R.s, p, nil, -1); + break; + case Psend: + cqdelp(&p->chan->send, p); + break; + case Precv: + cqdelp(&p->chan->recv, p); + break; + case Pready: + break; + case Prelease: + swiprog(p); + break; + case Pexiting: + case Pbroken: + case Pdebug: + return 0; + default: + panic("exprog - bad state 0x%x\n", p->state); + } + if(p->state != Pready && p->state != Prelease) + addrun(p); + if(p->kill == nil){ + if(p->killstr == nil){ + p->killstr = malloc(ERRMAX); + if(p->killstr == nil){ + p->kill = Enomem; + return 1; + } + } + kstrcpy(p->killstr, exc, ERRMAX); + p->kill = p->killstr; + } + return 1; +} + +static void +propex(Prog *p, char *estr) +{ + Prog *f, *nf, *pgl; + + if(!(p->flags & (Ppropagate|Pnotifyleader)) || p->group == nil) + return; + if(*estr == 0){ + if((p->flags & Pkilled) == 0) + return; + estr = "killed"; + } + pgl = grpleader(p); + if(pgl == nil) + pgl = p; + if(!(pgl->flags & (Ppropagate|Pnotifyleader))) + return; /* exceptions are local; don't propagate */ + for(f = p->group->head; f != nil; f = nf){ + nf = f->grpnext; + if(f != p && f != pgl){ + if(pgl->flags & Ppropagate) + exprog(f, estr); + else{ + f->flags &= ~(Ppropagate|Pnotifyleader); /* prevent recursion */ + killprog(f, "killed"); + } + } + } + if(p != pgl) + exprog(pgl, estr); +} + +int +killprog(Prog *p, char *cause) +{ + Osenv *env; + char msg[ERRMAX+2*KNAMELEN]; + + if(p == isched.runhd) { + p->kill = ""; + p->flags |= Pkilled; + p->state = Pexiting; + return 0; + } + + switch(p->state) { + case Palt: + altdone(p->R.s, p, nil, -1); + break; + case Psend: + cqdelp(&p->chan->send, p); + break; + case Precv: + cqdelp(&p->chan->recv, p); + break; + case Pready: + delrunq(p); + break; + case Prelease: + p->kill = ""; + p->flags |= Pkilled; + p->state = Pexiting; + swiprog(p); + /* No break */ + case Pexiting: + return 0; + case Pbroken: + case Pdebug: + break; + default: + panic("killprog - bad state 0x%x\n", p->state); + } + + if(p->addrun != nil) { + p->kill = ""; + p->flags |= Pkilled; + p->addrun(p); + p->addrun = nil; + return 0; + } + + env = p->osenv; + if(env->debug != nil) { + p->state = Pbroken; + dbgexit(p, 0, cause); + return 0; + } + + propex(p, "killed"); + + snprint(msg, sizeof(msg), "%d \"%s\":%s", p->pid, p->R.M->m->name, cause); + + p->state = Pexiting; + gclock(); + destroystack(&p->R); + delprog(p, msg); + gcunlock(); + + return 1; +} + +void +newgrp(Prog *p) +{ + Progs *pg, *g; + + if(p->group != nil && p->group->id == p->pid) + return; + g = malloc(sizeof(*g)); + if(g == nil) + error(Enomem); + p->flags &= ~(Ppropagate|Pnotifyleader); + g->id = p->pid; + g->flags = 0; + g->child = nil; + pg = delgrp(p); + g->head = g->tail = p; + p->group = g; + if(pg != nil){ + g->sib = pg->child; + pg->child = g; + } + g->parent = pg; +} + +static void +addgrp(Prog *n, Prog *p) +{ + Progs *g; + + n->group = p->group; + if((g = n->group) != nil){ + n->grpnext = nil; + if(g->head != nil){ + n->grpprev = g->tail; + g->tail->grpnext = n; + }else{ + n->grpprev = nil; + g->head = n; + } + g->tail = n; + } +} + +static Progs* +delgrp(Prog *p) +{ + Progs *g, *pg, *cg, **l; + + g = p->group; + if(g == nil) + return nil; + if(p->grpprev) + p->grpprev->grpnext = p->grpnext; + else + g->head = p->grpnext; + if(p->grpnext) + p->grpnext->grpprev = p->grpprev; + else + g->tail = p->grpprev; + p->grpprev = p->grpnext = nil; + p->group = nil; + + if(g->head == nil){ + /* move up, giving subgroups of groups with no Progs to their parents */ + do{ + if((pg = g->parent) != nil){ + pg = g->parent; + for(l = &pg->child; *l != nil && *l != g; l = &(*l)->sib) + ; + *l = g->sib; + } + /* put subgroups in new parent group */ + while((cg = g->child) != nil){ + g->child = cg->sib; + cg->parent = pg; + if(pg != nil){ + cg->sib = pg->child; + pg->child = cg; + } + } + free(g); + }while((g = pg) != nil && g->head == nil); + } + return g; +} + +void +printgrp(Prog *p, char *v) +{ + Progs *g; + Prog *q; + + g = p->group; + print("%s pid %d grp %d pgrp %d: [pid", v, p->pid, g->id, g->parent!=nil?g->parent->id:0); + for(q = g->head; q != nil; q = q->grpnext) + print(" %d", q->pid); + print(" subgrp"); + for(g = g->child; g != nil; g = g->sib) + print(" %d", g->id); + print("]\n"); +} + +int +killgrp(Prog *p, char *msg) +{ + int i, npid, *pids; + Prog *f; + Progs *g; + + /* interpreter has been acquired */ + g = p->group; + if(g == nil || g->head == nil) + return 0; + npid = 0; + for(f = g->head; f != nil; f = f->grpnext) + if(f->group != g) + panic("killgrp"); + else + npid++; + /* use pids not Prog* because state can change during killprog */ + pids = malloc(npid*sizeof(int)); + if(pids == nil) + error(Enomem); + npid = 0; + for(f = g->head; f != nil; f = f->grpnext) + pids[npid++] = f->pid; + if(waserror()) { + free(pids); + nexterror(); + } + for(i = 0; i < npid; i++) { + f = progpid(pids[i]); + if(f != nil && f != currun()) + killprog(f, msg); + } + poperror(); + free(pids); + return 1; +} + +char changup[] = "channel hangup"; + +void +killcomm(Progq **q) +{ + Prog *p; + Progq *f; + + for (f = *q; f != nil; f = *q) { + *q = f->next; + p = f->prog; + free(f); + if(p == nil) + return; + p->ptr = nil; + switch(p->state) { + case Prelease: + swiprog(p); + break; + case Psend: + case Precv: + p->kill = changup; + addrun(p); + break; + case Palt: + altgone(p); + break; + } + } +} + +void +addprog(Proc *p) +{ + Prog *n; + + if((n = p->prog) == nil) { + n = malloc(sizeof(Prog)); + if(n == nil) + panic("no memory"); + p->prog = n; + } else + memset(n, 0, sizeof(Prog)); + n->osenv = p->env; +} + +static void +cwakeme(Prog *p) +{ + Osenv *o; + + p->addrun = nil; + o = p->osenv; + wakeup(o->rend); +} + +static int +cdone(void *vp) +{ + Prog *p = vp; + + return p->addrun == nil || p->kill != nil; +} + +void +cblock(Prog *p) +{ + Osenv *o; + + p->addrun = cwakeme; + o = p->osenv; + o->rend = &up->sleep; + release(); + + /* + * To allow cdone(p) safely after release, + * p must be currun before the release. + * Exits in the error case with the vm acquired. + */ + if(waserror()) { + acquire(); + p->addrun = nil; + nexterror(); + } + sleep(o->rend, cdone, p); + if (p->kill != nil) + error(Eintr); + poperror(); + acquire(); +} + +void +addrun(Prog *p) +{ + if(p->addrun != 0) { + p->addrun(p); + return; + } + if(p->state == Pready && p != (Prog *)up->prog) + panic("addrun of ready prog %8.8p by %8.8lux\n", p, getcallerpc(&p)); + p->state = Pready; + p->link = nil; + if(isched.runhd == nil) + isched.runhd = p; + else + isched.runtl->link = p; + + isched.runtl = p; +} + +Prog* +delrun(int state) +{ + Prog *p; + + p = isched.runhd; + p->state = state; + isched.runhd = p->link; + if(p->link == nil) + isched.runtl = nil; + + return p; +} + +void +delrunq(Prog *p) +{ + Prog *prev, *f; + + prev = nil; + for(f = isched.runhd; f; f = f->link) { + if(f == p) + break; + prev = f; + } + if(f == nil) + return; + if(prev == nil) + isched.runhd = p->link; + else + prev->link = p->link; + if(p == isched.runtl) + isched.runtl = prev; +} + +Prog* +delruntail(int state) +{ + Prog *p; + + p = isched.runtl; + delrunq(isched.runtl); + p->state = state; + return p; +} + +Prog* +currun(void) +{ + return isched.runhd; +} + +Prog* +schedmod(Module *m) +{ + Heap *h; + Type *t; + Prog *p; + Modlink *ml; + Frame f, *fp; + + ml = mklinkmod(m, 0); + + if(m->origmp != H && m->ntype > 0) { + t = m->type[0]; + h = nheap(t->size); + h->t = t; + t->ref++; + ml->MP = H2D(uchar*, h); + newmp(ml->MP, m->origmp, t); + } + + p = newprog(nil, ml); + h = D2H(ml); + h->ref--; + p->R.PC = m->entry; + fp = &f; + R.s = &fp; + f.t = m->entryt; + newstack(p); + initmem(m->entryt, p->R.FP); + + return p; +} + +void +acquire(void) +{ + int empty; + Prog *p; + + lock(&isched.l); + if(isched.idle) { + isched.idle = 0; + unlock(&isched.l); + } + else { + up->qnext = nil; + if(isched.vmq != nil){ + empty = 0; + isched.vmqt->qnext = up; + }else{ + isched.vmq = up; + empty = 1; + } + isched.vmqt = up; + + up->state = Queueing; + up->pc = getcallerpc(&empty); + unlock(&isched.l); + if(empty) + wakeup(&isched.irend); + sched(); + } + + if(up->type == Interp) { + p = up->iprog; + up->iprog = nil; + irestore(p); + } + else + p = up->prog; + + p->state = Pready; + p->link = isched.runhd; + isched.runhd = p; + if(p->link == nil) + isched.runtl = p; +} + +void +release(void) +{ + Proc *p, **pq; + int f; + + if(up->type == Interp){ + if(up->iprog != nil) + panic("Double release (Interp)?"); + up->iprog = isave(); + }else{ + if(((Prog *)up->prog)->state != Pready) panic("double release (GC)?"); + delrun(Prelease); + } + + lock(&isched.l); + if(*(pq = &isched.vmq) == nil && *(pq = &isched.idlevmq) == nil) { + isched.idle = 1; + f = isched.creating; + isched.creating = 1; + unlock(&isched.l); + if(f == 0) + kproc("dis", vmachine, nil, 0); + return; + } + p = *pq; + *pq = p->qnext; + unlock(&isched.l); + + ready(p); +} + +void +iyield(void) +{ + Proc *p; + + lock(&isched.l); + p = isched.vmq; + if(p == nil) { + unlock(&isched.l); + return; + } + isched.nyield++; + isched.vmq = p->qnext; + + if(up->iprog != nil) + panic("iyield but iprog, type %d", up->type); + if(up->type != Interp){ + static int once; + if(!once++) + print("tell charles: #%p->type==%d\n", up, up->type); + } + up->qnext = isched.idlevmq; + isched.idlevmq = up; + + up->state = Queueing; + up->pc = getcallerpc(&p); + unlock(&isched.l); + ready(p); + sched(); +} + +void +startup(void) +{ + int x; + + up->type = Interp; + up->iprog = nil; + + lock(&isched.l); + isched.creating = 0; + if(isched.idle) { + isched.idle = 0; + unlock(&isched.l); + return; + } + up->qnext = isched.idlevmq; + isched.idlevmq = up; + up->state = Queueing; + up->pc = getcallerpc(&x); + unlock(&isched.l); + sched(); +} + +void +progexit(void) +{ + Prog *r; + Module *m; + int broken; + char *estr, msg[ERRMAX+2*KNAMELEN]; + + estr = up->env->errstr; + broken = 0; + if(estr[0] != '\0' && strcmp(estr, Eintr) != 0 && strncmp(estr, "fail:", 5) != 0) + broken = 1; + + r = up->iprog; + if(r != nil) + acquire(); + else + r = currun(); + + if(*estr == '\0' && r->flags & Pkilled) + estr = "killed"; + + m = R.M->m; + if(broken) + print("[%s] Broken: \"%s\"\n", m->name, estr); + + snprint(msg, sizeof(msg), "%d \"%s\":%s", r->pid, m->name, estr); + + if(up->env->debug != nil) { + dbgexit(r, broken, estr); + broken = 1; + /* must force it to break if in debug */ + }else if(broken && (!keepbroken || strncmp(estr, "out of memory", 13)==0 || memusehigh())) + broken = 0; /* don't want them or short of memory */ + + if(broken){ + tellsomeone(r, msg); + r = isave(); + r->state = Pbroken; + return; + } + + gclock(); + destroystack(&R); + delprog(r, msg); + gcunlock(); +} + +void +disfault(void *reg, char *msg) +{ + Prog *p; + + USED(reg); + + if(strncmp(msg, Eintr, 6) == 0 || up == nil) { + print("EMU: faults: %s\n", msg); + panic("disfault"); + } + if(up->type != Interp) { + print("SYS: process %s faults: %s\n", up->text, msg); + panic("disfault"); + } + + if(up->iprog != nil) + acquire(); + + p = currun(); + if(p == nil) + panic("Interp faults with no dis prog"); + + /* cause an exception in the dis prog. */ + error(msg); +} + +void +vmachine(void*) +{ + Prog *r; + Osenv *o; + int cycles; + + startup(); + + while(waserror()) { + if(up->type != Interp) + panic("vmachine: non-interp kproc"); + if(up->iprog != nil) + acquire(); + if(handler(up->env->errstr) == 0) { + propex(currun(), up->env->errstr); + progexit(); + } + up->env = &up->defenv; + } + + cycles = 0; + for(;;) { + if(tready(nil) == 0) { + execatidle(); + sleep(&isched.irend, tready, 0); + } + + if(isched.vmq != nil && (isched.runhd == nil || ++cycles > 2)){ + iyield(); + cycles = 0; + } + + r = isched.runhd; + if(r != nil) { + o = r->osenv; + up->env = o; + + FPrestore(&o->fpu); + r->xec(r); + FPsave(&o->fpu); + + if(isched.runhd != nil) + if(r == isched.runhd) + if(isched.runhd != isched.runtl) { + isched.runhd = r->link; + r->link = nil; + isched.runtl->link = r; + isched.runtl = r; + } + up->env = &up->defenv; + if(isched.runhd != nil) + if (up->iprog == nil) { + up->type = BusyGC; + pushrun(up->prog); + rungc(isched.head); + up->type = Interp; + delrunq(up->prog); + } else + print("up->iprog not nil (%lux)\n", up->iprog); + } + } +} + +void +disinit(void *a) +{ + Prog *p; + Osenv *o; + Module *root; + char *initmod = a; + + if(waserror()) + panic("disinit error: %r"); + + print("Initial Dis: \"%s\"\n", initmod); + + fmtinstall('D', Dconv); + + addclock0link(accountant, MS2HZ); + + FPinit(); + FPsave(&up->env->fpu); + + opinit(); + modinit(); + excinit(); + + root = load(initmod); + if(root == 0) { + kgerrstr(up->genbuf, sizeof up->genbuf); + panic("loading \"%s\": %s", initmod, up->genbuf); + } + + p = schedmod(root); + + memmove(p->osenv, up->env, sizeof(Osenv)); + o = p->osenv; + incref(o->pgrp); + incref(o->fgrp); + if(o->egrp != nil) + incref(o->egrp); + if(o->sigs != nil) + incref(o->sigs); + o->user = nil; + kstrdup(&o->user, up->env->user); + o->errstr = o->errbuf0; + o->syserrstr = o->errbuf1; + + isched.idle = 1; + + if(kopen("#c/cons", OREAD) != 0) + panic("failed to make fd0 from #c/cons"); + kopen("#c/cons", OWRITE); + kopen("#c/cons", OWRITE); + + poperror(); + vmachine(nil); +} + +void +pushrun(Prog *p) +{ + if(p->addrun != nil) + panic("pushrun addrun"); + p->state = Pready; + p->link = isched.runhd; + isched.runhd = p; + if(p->link == nil) + isched.runtl = p; +} diff --git a/os/port/discall.c b/os/port/discall.c new file mode 100644 index 00000000..c5db5d79 --- /dev/null +++ b/os/port/discall.c @@ -0,0 +1,185 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include +#include +#include "kernel.h" + +#define QP(l) (Prog**)((char*)(l)+sizeof(QLock)) + +void* +libqlowner(void *l) +{ + return *QP(l); +} + +void +libqlock(void *l) +{ + Prog *p; + QLock *q; + + q = l; + p = currun(); + if(p == nil) + panic("libqlock"); + + if(!canqlock(q)) { + release(); + qlock(q); + acquire(); + } + *QP(l) = p; +} + +void +libqunlock(void *l) +{ + Prog *p; + QLock *q; + + q = l; + p = currun(); + if(p == nil) + panic("libqunlock 1"); + if(*QP(l) != p) + panic("libqunlock 2"); + + *QP(l) = nil; + qunlock(q); +} + +void* +libqlalloc(void) +{ + QLock *q; + + q = mallocz(sizeof(QLock)+sizeof(Prog*), 1); + return q; +} + +void +libqlfree(void *l) +{ + free(l); +} + +vlong +libseek(int fd, vlong off, int whence) +{ + release(); + off = kseek(fd, off, whence); + acquire(); + return off; +} + +int +libread(int fd, void *buf, int n) +{ + release(); + n = kread(fd, buf, n); + acquire(); + return n; +} + +int +libreadn(int fd, void *av, long n) +{ + char *a; + long m, t; + + a = av; + t = 0; + release(); + while(t < n){ + m = kread(fd, a+t, n-t); + if(m <= 0){ + if(t == 0){ + acquire(); + return m; + } + break; + } + t += m; + } + acquire(); + return t; +} + +int +libwrite(int fd, void *buf, int n) +{ + release(); + n = kwrite(fd, buf, n); + acquire(); + return n; +} + +int +libopen(char *name, int omode) +{ + int fd; + + release(); + fd = kopen(name, omode); + acquire(); + return fd; +} + +int +libclose(int fd) +{ + release(); + fd = kclose(fd); + acquire(); + return fd; +} + +Dir* +libdirfstat(int fd) +{ + Dir *d; + + release(); + d = kdirfstat(fd); + acquire(); + return d; +} + +int +libbind(char *s, char *t, int f) +{ + int n; + + release(); + n = kbind(s, t, f); + acquire(); + return n; +} + +void +libchanclose(void *chan) +{ + release(); + cclose(chan); + acquire(); +} + +void* +libfdtochan(int fd, int mode) +{ + Chan *c; + + release(); + if(waserror()) { + acquire(); + return nil; + } + c = fdtochan(up->env->fgrp, fd, mode, 0, 1); + poperror(); + acquire(); + return c; +} diff --git a/os/port/dynld.c b/os/port/dynld.c new file mode 100644 index 00000000..77f74230 --- /dev/null +++ b/os/port/dynld.c @@ -0,0 +1,75 @@ +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include +#include +#include + +/* + * kernel interface to dynld, for use by devdynld.c, + * libinterp/dlm.c, and possibly others + */ + +typedef struct Fd Fd; +struct Fd { + int fd; +}; + +static long +readfd(void *a, void *buf, long nbytes) +{ + return kread(((Fd*)a)->fd, buf, nbytes); +} + +static vlong +seekfd(void *a, vlong off, int t) +{ + return kseek(((Fd*)a)->fd, off, t); +} + +static void +errfd(char *s) +{ + kstrcpy(up->env->errstr, s, ERRMAX); +} + +Dynobj* +kdynloadfd(int fd, Dynsym *tab, int ntab) +{ + Fd f; + + f.fd = fd; + return dynloadgen(&f, readfd, seekfd, errfd, tab, ntab, 0); +} + +int +kdynloadable(int fd) +{ + Fd f; + + f.fd = fd; + return dynloadable(&f, readfd, seekfd); +} + +/* auxiliary routines for dynamic loading of C modules */ + +Dynobj* +dynld(int fd) +{ + Fd f; + + f.fd = fd; + return dynloadgen(&f, readfd, seekfd, errfd, _exporttab, dyntabsize(_exporttab), 0); +} + +int +dynldable(int fd) +{ + Fd f; + + f.fd = fd; + return dynloadable(&f, readfd, seekfd); +} diff --git a/os/port/edf.c b/os/port/edf.c new file mode 100644 index 00000000..c406f4cc --- /dev/null +++ b/os/port/edf.c @@ -0,0 +1,626 @@ +/* EDF scheduling */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "../port/edf.h" +#include + +/* debugging */ +int edfprint = 0; +#define DPRINT if(edfprint)print + +static vlong now; +extern ulong delayedscheds; +extern Schedq runq[Nrq]; +extern int nrdy; +extern ulong runvec; + +/* Statistics stuff */ +ulong nilcount; +ulong scheds; +vlong edfruntime; +ulong edfnrun; +int misseddeadlines; + +/* Edfschedlock protects modification of admission params */ +int edfinited; +QLock edfschedlock; +static Lock thelock; + +enum{ + Dl, /* Invariant for schedulability test: Dl < Rl */ + Rl, +}; + +static char *testschedulability(Proc*); +static Proc *qschedulability; + +enum { + Onemicrosecond = 1000ULL, + Onemillisecond = 1000000ULL, + Onesecond = 1000000000ULL, + OneRound = Onemillisecond/2LL, + MilliRound = Onemicrosecond/2LL, +}; + +static int +timeconv(Fmt *f) +{ + char buf[128], *sign; + vlong t; + + buf[0] = 0; + switch(f->r) { + case 'U': + t = va_arg(f->args, uvlong); + break; + case 't': // vlong in nanoseconds + t = va_arg(f->args, vlong); + break; + default: + return fmtstrcpy(f, "(timeconv)"); + } + if (t < 0) { + sign = "-"; + t = -t; + } + else + sign = ""; + if (t > Onesecond){ + t += OneRound; + sprint(buf, "%s%d.%.3ds", sign, (int)(t / Onesecond), (int)(t % Onesecond)/1000000); + }else if (t > Onemillisecond){ + t += MilliRound; + sprint(buf, "%s%d.%.3dms", sign, (int)(t / Onemillisecond), (int)(t % Onemillisecond)/1000); + }else if (t > Onemicrosecond) + sprint(buf, "%s%d.%.3dµs", sign, (int)(t / Onemicrosecond), (int)(t % Onemicrosecond)); + else + sprint(buf, "%s%dns", sign, (int)t); + return fmtstrcpy(f, buf); +} + +Edf* +edflock(Proc *p) +{ + Edf *e; + + if (p->edf == nil) + return nil; + ilock(&thelock); + if ((e = p->edf) && (e->flags & Admitted)){ + now = todget(nil); + return e; + } + iunlock(&thelock); + return nil; +} + +void +edfunlock(void) +{ + edfruntime += todget(nil) - now; + edfnrun++; + iunlock(&thelock); +} + +void +edfinit(Proc*p) +{ + if(!edfinited){ + fmtinstall('t', timeconv); + edfinited++; + } + now = todget(nil); + DPRINT("%t edfinit %lud[%s]\n", now, p->pid, statename[p->state]); + p->edf = malloc(sizeof(Edf)); + if(p->edf == nil) + error(Enomem); + return; +} + +static void +deadlineintr(Ureg*, Timer *t) +{ + /* Proc reached deadline */ + extern int panicking; + Proc *p; + void (*pt)(Proc*, int, vlong); + + if(panicking || active.exiting) + return; + + p = t->ta; + + DPRINT("%t deadlineintr %lud[%s]\n", todget(nil), p->pid, statename[p->state]); + /* If we're interrupting something other than the proc pointed to by t->a, + * we've already achieved recheduling, so we need not do anything + * Otherwise, we must cause a reschedule, but if we call sched() + * here directly, the timer interrupt routine will not finish its business + * Instead, we cause the resched to happen when the interrupted proc + * returns to user space + */ + if (p == up){ + pt = proctrace; + if(up->trace && pt) + pt(up, SInts, 0); + up->delaysched++; + delayedscheds++; + } +} + +static void +release(Proc *p) +{ + /* Called with edflock held */ + Edf *e; + void (*pt)(Proc*, int, vlong); + + e = p->edf; + e->flags &= ~Yield; + if (e->d < now){ + e->periods++; + e->r = now; + if ((e->flags & Sporadic) == 0){ + /* Non sporadic processes stay true to their period; + * calculate next release time + */ + while(e->t <= now) + e->t += e->T; + }else{ + /* Sporadic processes may not be released earlier than + * one period after this release + */ + e->t = e->r + e->T; + } + e->d = e->r + e->D; + e->S = e->C; + DPRINT("%t release %lud[%s], r=%t, d=%t, t=%t, S=%t\n", + now, p->pid, statename[p->state], e->r, e->d, e->t, e->S); + if (pt = proctrace){ + pt(p, SRelease, e->r); + pt(p, SDeadline, e->d); + } + }else{ + DPRINT("%t release %lud[%s], too late t=%t, called from 0x%lux\n", + now, p->pid, statename[p->state], e->t, getcallerpc(&p)); + } +} + +static void +releaseintr(Ureg*, Timer *t) +{ + Proc *p; + extern int panicking; + Schedq *rq; + + if(panicking || active.exiting) + return; + + p = t->ta; + if((edflock(p)) == nil) + return; + DPRINT("%t releaseintr %lud[%s]\n", now, p->pid, statename[p->state]); + switch(p->state){ + default: + edfunlock(); + return; + case Ready: + /* remove proc from current runq */ + rq = &runq[p->pri]; + if (dequeueproc(rq, p) != p){ + DPRINT("releaseintr: can't find proc or lock race\n"); + release(p); /* It'll start best effort */ + edfunlock(); + return; + } + p->state = Waitrelease; + /* fall through */ + case Waitrelease: + release(p); + edfunlock(); + ready(p); + if (up){ + up->delaysched++; + delayedscheds++; + } + return; + case Running: + release(p); + edfrun(p, 1); + break; + case Wakeme: + release(p); + edfunlock(); + if (p->trend) + wakeup(p->trend); + p->trend = nil; + if (up){ + up->delaysched++; + delayedscheds++; + } + return; + } + edfunlock(); +} + +void +edfrecord(Proc *p) +{ + vlong used; + Edf *e; + void (*pt)(Proc*, int, vlong); + + if((e = edflock(p)) == nil) + return; + used = now - e->s; + if (e->d <= now) + e->edfused += used; + else + e->extraused += used; + if (e->S > 0){ + if (e->S <= used){ + if(pt = proctrace) + pt(p, SSlice, now); + DPRINT("%t edfrecord slice used up\n", now); + e->d = now; + e->S = 0; + }else + e->S -= used; + } + e->s = now; + edfunlock(); +} + +void +edfrun(Proc *p, int edfpri) +{ + Edf *e; + void (*pt)(Proc*, int, vlong); + + e = p->edf; + /* Called with edflock held */ + if(edfpri){ + if (e->d <= now || e->S == 0){ + /* Deadline reached or resources exhausted, + * deschedule forthwith + */ + p->delaysched++; + delayedscheds++; + e->s = now; + return; + } + e->tns = now + e->S; + if (e->d < e->tns) + e->tns = e->d; + if(e->tt == nil || e->tf != deadlineintr){ + DPRINT("%t edfrun, deadline=%t\n", now, e->tns); + }else{ + DPRINT("v"); + } + if(p->trace && (pt = proctrace)) + pt(p, SInte, e->tns); + e->tmode = Tabsolute; + e->tf = deadlineintr; + e->ta = p; + timeradd(e); + }else{ + DPRINT("<"); + } + e->s = now; +} + +char * +edfadmit(Proc *p) +{ + char *err; + Edf *e; + int i; + Proc *r; + void (*pt)(Proc*, int, vlong); + + e = p->edf; + if (e->flags & Admitted) + return "task state"; /* should never happen */ + + /* simple sanity checks */ + if (e->T == 0) + return "T not set"; + if (e->C == 0) + return "C not set"; + if (e->D > e->T) + return "D > T"; + if (e->D == 0) /* if D is not set, set it to T */ + e->D = e->T; + if (e->C > e->D) + return "C > D"; + + qlock(&edfschedlock); + if (err = testschedulability(p)){ + qunlock(&edfschedlock); + return err; + } + e->flags |= Admitted; + + edflock(p); + + if(pt = proctrace) + pt(p, SAdmit, now); + + /* Look for another proc with the same period to synchronize to */ + SET(r); + for(i=0; istate == Dead || r == p) + continue; + if (r->edf == nil || (r->edf->flags & Admitted) == 0) + continue; + if (r->edf->T == e->T) + break; + } + if (i == conf.nproc){ + /* Can't synchronize to another proc, release now */ + e->t = now; + e->d = 0; + release(p); + if (p == up){ + DPRINT("%t edfadmit self %lud[%s], release now: r=%t d=%t t=%t\n", + now, p->pid, statename[p->state], e->r, e->d, e->t); + /* We're already running */ + edfrun(p, 1); + }else{ + /* We're releasing another proc */ + DPRINT("%t edfadmit other %lud[%s], release now: r=%t d=%t t=%t\n", + now, p->pid, statename[p->state], e->r, e->d, e->t); + p->ta = p; + edfunlock(); + qunlock(&edfschedlock); + releaseintr(nil, p); + return nil; + } + }else{ + /* Release in synch to something else */ + e->t = r->edf->t; + if (p == up){ + DPRINT("%t edfadmit self %lud[%s], release at %t\n", + now, p->pid, statename[p->state], e->t); + edfunlock(); + qunlock(&edfschedlock); + return nil; + }else{ + DPRINT("%t edfadmit other %lud[%s], release at %t\n", + now, p->pid, statename[p->state], e->t); + if(e->tt == nil){ + e->tf = releaseintr; + e->ta = p; + e->tns = e->t; + e->tmode = Tabsolute; + timeradd(e); + } + } + } + edfunlock(); + qunlock(&edfschedlock); + return nil; +} + +void +edfstop(Proc *p) +{ + Edf *e; + void (*pt)(Proc*, int, vlong); + + if (e = edflock(p)){ + DPRINT("%t edfstop %lud[%s]\n", now, p->pid, statename[p->state]); + if(pt = proctrace) + pt(p, SExpel, now); + e->flags &= ~Admitted; + if (e->tt) + timerdel(e); + edfunlock(); + } +} + +static int +yfn(void *) +{ + return up->trend == nil || todget(nil) >= up->edf->r; +} + +void +edfyield(void) +{ + /* sleep until next release */ + Edf *e; + void (*pt)(Proc*, int, vlong); + + if((e = edflock(up)) == nil) + return; + if(pt = proctrace) + pt(up, SYield, now); + while(e->t < now) + e->t += e->T; + e->r = e->t; + e->flags |= Yield; + e->d = now; + if (up->tt == nil){ + up->tns = e->t; + up->tf = releaseintr; + up->tmode = Tabsolute; + up->ta = up; + up->trend = &up->sleep; + timeradd(up); + }else if(up->tf != releaseintr) + print("edfyield: surprise! 0x%lux\n", up->tf); + edfunlock(); + sleep(&up->sleep, yfn, nil); +} + +int +edfready(Proc *p) +{ + Edf *e; + Schedq *rq; + Proc *l, *pp; + void (*pt)(Proc*, int, vlong); + + if((e = edflock(p)) == nil) + return 0; + if (e->d <= now){ + /* past deadline, arrange for next release */ + if ((e->flags & Sporadic) == 0){ + /* Non sporadic processes stay true to their period, calculate next release time */ + while(e->t < now) + e->t += e->T; + } + if (now < e->t){ + /* Next release is in the future, schedule it */ + if (e->tt == nil || e->tf != releaseintr){ + e->tns = e->t; + e->tmode = Tabsolute; + e->tf = releaseintr; + e->ta = p; + timeradd(e); + DPRINT("%t edfready %lud[%s], release=%t\n", + now, p->pid, statename[p->state], e->t); + } + if(p->state == Running && (e->flags & (Yield|Yieldonblock)) == 0 && (e->flags & Extratime)){ + /* If we were running, we've overrun our CPU allocation + * or missed the deadline, continue running best-effort at low priority + * Otherwise we were blocked. If we don't yield on block, we continue + * best effort + */ + DPRINT(">"); + p->pri = PriExtra; + edfunlock(); + return 0; /* Stick on runq[PriExtra] */ + } + DPRINT("%t edfready %lud[%s] wait release at %t\n", + now, p->pid, statename[p->state], e->t); + p->state = Waitrelease; + edfunlock(); + return 1; /* Make runnable later */ + } + DPRINT("%t edfready %lud %s release now\n", now, p->pid, statename[p->state]); + /* release now */ + release(p); + } + edfunlock(); + DPRINT("^"); + rq = &runq[PriEdf]; + /* insert in queue in earliest deadline order */ + lock(runq); + l = nil; + for(pp = rq->head; pp; pp = pp->rnext){ + if(pp->edf->d > e->d) + break; + l = pp; + } + p->rnext = pp; + if (l == nil) + rq->head = p; + else + l->rnext = p; + if(pp == nil) + rq->tail = p; + rq->n++; + nrdy++; + runvec |= 1 << PriEdf; + p->pri = PriEdf; + p->readytime = m->ticks; + p->state = Ready; + unlock(runq); + if(pt = proctrace) + pt(p, SReady, now); + return 1; +} + + +static void +testenq(Proc *p) +{ + Proc *xp, **xpp; + Edf *e; + + e = p->edf; + e->testnext = nil; + if (qschedulability == nil) { + qschedulability = p; + return; + } + SET(xp); + for (xpp = &qschedulability; *xpp; xpp = &xp->edf->testnext) { + xp = *xpp; + if (e->testtime < xp->edf->testtime + || (e->testtime == xp->edf->testtime && e->testtype < xp->edf->testtype)){ + e->testnext = xp; + *xpp = p; + return; + } + } + assert(xp->edf->testnext == nil); + xp->edf->testnext = p; +} + +static char * +testschedulability(Proc *theproc) +{ + Proc *p; + vlong H, G, Cb, ticks; + int steps, i; + + /* initialize */ + DPRINT("schedulability test %lud\n", theproc->pid); + qschedulability = nil; + for(i=0; istate == Dead) + continue; + if ((p->edf == nil || (p->edf->flags & Admitted) == 0) && p != theproc) + continue; + p->edf->testtype = Rl; + p->edf->testtime = 0; + DPRINT("\tInit: edfenqueue %lud\n", p->pid); + testenq(p); + } + H=0; + G=0; + for(steps = 0; steps < Maxsteps; steps++){ + p = qschedulability; + qschedulability = p->edf->testnext; + ticks = p->edf->testtime; + switch (p->edf->testtype){ + case Dl: + H += p->edf->C; + Cb = 0; + DPRINT("\tStep %3d, Ticks %t, pid %lud, deadline, H += %t → %t, Cb = %t\n", + steps, ticks, p->pid, p->edf->C, H, Cb); + if (H+Cb>ticks){ + DPRINT("not schedulable\n"); + return "not schedulable"; + } + p->edf->testtime += p->edf->T - p->edf->D; + p->edf->testtype = Rl; + testenq(p); + break; + case Rl: + DPRINT("\tStep %3d, Ticks %t, pid %lud, release, G %t, C%t\n", + steps, ticks, p->pid, p->edf->C, G); + if(ticks && G <= ticks){ + DPRINT("schedulable\n"); + return nil; + } + G += p->edf->C; + p->edf->testtime += p->edf->D; + p->edf->testtype = Dl; + testenq(p); + break; + default: + assert(0); + } + } + DPRINT("probably not schedulable\n"); + return "probably not schedulable"; +} diff --git a/os/port/edf.h b/os/port/edf.h new file mode 100644 index 00000000..dfd2b4a9 --- /dev/null +++ b/os/port/edf.h @@ -0,0 +1,53 @@ +enum { + Maxsteps = 200 * 100 * 2, /* 100 periods of 200 procs */ + + /* Edf.flags field */ + Admitted = 0x01, + Sporadic = 0x02, + Yieldonblock = 0x04, + Sendnotes = 0x08, + Deadline = 0x10, + Yield = 0x20, + Extratime = 0x40, + + Infinity = ~0ULL, +}; + +typedef struct Edf Edf; + +struct Edf { + /* time intervals */ + vlong D; /* Deadline */ + vlong Delta; /* Inherited deadline */ + vlong T; /* period */ + vlong C; /* Cost */ + vlong S; /* Slice: time remaining in this period */ + /* times */ + vlong r; /* (this) release time */ + vlong d; /* (this) deadline */ + vlong t; /* Start of next period, t += T at release */ + vlong s; /* Time at which this proc was last scheduled */ + /* for schedulability testing */ + vlong testDelta; + int testtype; /* Release or Deadline */ + vlong testtime; + Proc *testnext; + /* other */ + ushort flags; + Timer; + /* Stats */ + vlong edfused; + vlong extraused; + vlong aged; + ulong periods; + ulong missed; +}; + +extern Lock edftestlock; /* for atomic admitting/expelling */ + +#pragma varargck type "t" vlong +#pragma varargck type "U" uvlong + +/* Interface: */ +Edf* edflock(Proc*); +void edfunlock(void); diff --git a/os/port/error.h b/os/port/error.h new file mode 100644 index 00000000..5d5c2f66 --- /dev/null +++ b/os/port/error.h @@ -0,0 +1,64 @@ +extern char Enoerror[]; /* no error */ +extern char Emount[]; /* inconsistent mount */ +extern char Eunmount[]; /* not mounted */ +extern char Eunion[]; /* not in union */ +extern char Emountrpc[]; /* mount rpc error */ +extern char Eshutdown[]; /* mounted device shut down */ +extern char Enocreate[]; /* mounted directory forbids creation */ +extern char Enonexist[]; /* file does not exist */ +extern char Eexist[]; /* file already exists */ +extern char Ebadsharp[]; /* unknown device in # filename */ +extern char Enotdir[]; /* not a directory */ +extern char Eisdir[]; /* file is a directory */ +extern char Ebadchar[]; /* bad character in file name */ +extern char Efilename[]; /* file name syntax */ +extern char Eperm[]; /* permission denied */ +extern char Ebadusefd[]; /* inappropriate use of fd */ +extern char Ebadarg[]; /* bad arg in system call */ +extern char Einuse[]; /* device or object already in use */ +extern char Eio[]; /* i/o error */ +extern char Etoobig[]; /* read or write too large */ +extern char Etoosmall[]; /* read or write too small */ +extern char Enetaddr[]; /* bad network address */ +extern char Emsgsize[]; /* message is too big for protocol */ +extern char Enetbusy[]; /* network device is busy or allocated */ +extern char Enoproto[]; /* network protocol not supported */ +extern char Enoport[]; /* network port not available */ +extern char Enoifc[]; /* bad interface or no free interface slots */ +extern char Enolisten[]; /* not announced */ +extern char Ehungup[]; /* i/o on hungup channel */ +extern char Ebadctl[]; /* bad process or channel control request */ +extern char Enodev[]; /* no free devices */ +extern char Enoenv[]; /* no free environment resources */ +extern char Emuxshutdown[]; /* mux server shut down */ +extern char Emuxbusy[]; /* all mux channels busy */ +extern char Emuxmsg[]; /* bad mux message format or mismatch */ +extern char Ethread[]; /* thread exited */ +extern char Estopped[]; /* thread must be stopped */ +extern char Enochild[]; /* no living children */ +extern char Eioload[]; /* i/o error in demand load */ +extern char Enovmem[]; /* out of memory: virtual memory */ +extern char Ebadld[]; /* illegal line discipline */ +extern char Ebadfd[]; /* fd out of range or not open */ +extern char Eisstream[]; /* seek on a stream */ +extern char Ebadexec[]; /* exec header invalid */ +extern char Etimedout[]; /* connection timed out */ +extern char Econrefused[]; /* connection refused */ +extern char Econinuse[]; /* connection in use */ +extern char Eintr[]; /* interrupted */ +extern char Eneedservice[]; /* service required for tcp/udp/il calls */ +extern char Enomem[]; /* out of memory: kernel */ +extern char Esfnotcached[]; /* subfont not cached */ +extern char Esoverlap[]; /* segments overlap */ +extern char Emouseset[]; /* mouse type already set */ +extern char Erecover[]; /* failed to recover fd */ +extern char Eshort[]; /* i/o count too small */ +extern char Enobitstore[]; /* out of screen memory */ +extern char Egreg[]; /* jim'll fix it */ +extern char Ebadspec[]; /* bad attach specifier */ +extern char Enoattach[]; /* mount/attach disallowed */ +extern char Eshortstat[]; /* stat buffer too small */ +extern char Enegoff[]; /* negative i/o offset */ +extern char Ecmdargs[]; /* wrong #args in control message */ +extern char Ebadstat[]; /* malformed stat buffer */ +extern char Enofd[]; /* no free file descriptors */ diff --git a/os/port/ethermii.c b/os/port/ethermii.c new file mode 100644 index 00000000..584cdf08 --- /dev/null +++ b/os/port/ethermii.c @@ -0,0 +1,238 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" +#include "ethermii.h" + +int +mii(Mii* mii, int mask) +{ + MiiPhy *miiphy; + int bit, oui, phyno, r, rmask; + + /* + * Probe through mii for PHYs in mask; + * return the mask of those found in the current probe. + * If the PHY has not already been probed, update + * the Mii information. + */ + rmask = 0; + for(phyno = 0; phyno < NMiiPhy; phyno++){ + bit = 1<mask & bit){ + rmask |= bit; + continue; + } + if(mii->mir(mii, phyno, Bmsr) == -1) + continue; + r = mii->mir(mii, phyno, Phyidr1); + oui = (r & 0x3FFF)<<6; + r = mii->mir(mii, phyno, Phyidr2); + oui |= r>>10; + if(oui == 0xFFFFF || oui == 0) + continue; + + if((miiphy = malloc(sizeof(MiiPhy))) == nil) + continue; + + miiphy->mii = mii; + miiphy->oui = oui; + miiphy->phyno = phyno; + + miiphy->anar = ~0; + miiphy->fc = ~0; + miiphy->mscr = ~0; + + mii->phy[phyno] = miiphy; + if(mii->curphy == nil) + mii->curphy = miiphy; + mii->mask |= bit; + mii->nphy++; + + rmask |= bit; + } + return rmask; +} + +int +miimir(Mii* mii, int r) +{ + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + return mii->mir(mii, mii->curphy->phyno, r); +} + +int +miimiw(Mii* mii, int r, int data) +{ + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + return mii->miw(mii, mii->curphy->phyno, r, data); +} + +int +miireset(Mii* mii) +{ + int bmcr; + + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + bmcr = mii->mir(mii, mii->curphy->phyno, Bmcr); + bmcr |= BmcrR; + mii->miw(mii, mii->curphy->phyno, Bmcr, bmcr); +/* microdelay(1);*/ + microdelay(500); /* DP83847, at least */ + + return 0; +} + +int +miiane(Mii* mii, int a, int p, int e) +{ + int anar, bmsr, mscr, r, phyno; + + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + phyno = mii->curphy->phyno; + + bmsr = mii->mir(mii, phyno, Bmsr); + if(!(bmsr & BmsrAna)) + return -1; + + if(a != ~0) + anar = (AnaTXFD|AnaTXHD|Ana10FD|Ana10HD) & a; + else if(mii->curphy->anar != ~0) + anar = mii->curphy->anar; + else{ + anar = mii->mir(mii, phyno, Anar); + anar &= ~(AnaAP|AnaP|AnaT4|AnaTXFD|AnaTXHD|Ana10FD|Ana10HD); + if(bmsr & Bmsr10THD) + anar |= Ana10HD; + if(bmsr & Bmsr10TFD) + anar |= Ana10FD; + if(bmsr & Bmsr100TXHD) + anar |= AnaTXHD; + if(bmsr & Bmsr100TXFD) + anar |= AnaTXFD; + } + mii->curphy->anar = anar; + + if(p != ~0) + anar |= (AnaAP|AnaP) & p; + else if(mii->curphy->fc != ~0) + anar |= mii->curphy->fc; + mii->curphy->fc = (AnaAP|AnaP) & anar; + + if(bmsr & BmsrEs){ + mscr = mii->mir(mii, phyno, Mscr); + mscr &= ~(Mscr1000TFD|Mscr1000THD); + if(e != ~0) + mscr |= (Mscr1000TFD|Mscr1000THD) & e; + else if(mii->curphy->mscr != ~0) + mscr = mii->curphy->mscr; + else{ + r = mii->mir(mii, phyno, Esr); + if(r & Esr1000THD) + mscr |= Mscr1000THD; + if(r & Esr1000TFD) + mscr |= Mscr1000TFD; + } + mii->curphy->mscr = mscr; + mii->miw(mii, phyno, Mscr, mscr); + } + mii->miw(mii, phyno, Anar, anar); + + r = mii->mir(mii, phyno, Bmcr); + if(!(r & BmcrR)){ + r |= BmcrAne|BmcrRan; + mii->miw(mii, phyno, Bmcr, r); + } + + return 0; +} + +int +miistatus(Mii* mii) +{ + MiiPhy *phy; + int anlpar, bmsr, p, r, phyno; + + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + phy = mii->curphy; + phyno = phy->phyno; + + /* + * Check Auto-Negotiation is complete and link is up. + * (Read status twice as the Ls bit is sticky). + */ + bmsr = mii->mir(mii, phyno, Bmsr); + if(!(bmsr & (BmsrAnc|BmsrAna))) +{ +print("miistatus 1\n"); + return -1; +} + + bmsr = mii->mir(mii, phyno, Bmsr); + if(!(bmsr & BmsrLs)){ +print("miistatus 2\n"); + phy->link = 0; + return -1; + } + + phy->speed = phy->fd = phy->rfc = phy->tfc = 0; + if(phy->mscr){ + r = mii->mir(mii, phyno, Mssr); + if((phy->mscr & Mscr1000TFD) && (r & Mssr1000TFD)){ + phy->speed = 1000; + phy->fd = 1; + } + else if((phy->mscr & Mscr1000THD) && (r & Mssr1000THD)) + phy->speed = 1000; + } + + anlpar = mii->mir(mii, phyno, Anlpar); + if(phy->speed == 0){ + r = phy->anar & anlpar; + if(r & AnaTXFD){ + phy->speed = 100; + phy->fd = 1; + } + else if(r & AnaTXHD) + phy->speed = 100; + else if(r & Ana10FD){ + phy->speed = 10; + phy->fd = 1; + } + else if(r & Ana10HD) + phy->speed = 10; + } + if(phy->speed == 0) +{ +print("miistatus 3\n"); + return -1; +} + + if(phy->fd){ + p = phy->fc; + r = anlpar & (AnaAP|AnaP); + if(p == AnaAP && r == (AnaAP|AnaP)) + phy->tfc = 1; + else if(p == (AnaAP|AnaP) && r == AnaAP) + phy->rfc = 1; + else if((p & AnaP) && (r & AnaP)) + phy->rfc = phy->tfc = 1; + } + + phy->link = 1; + + return 0; +} diff --git a/os/port/ethermii.h b/os/port/ethermii.h new file mode 100644 index 00000000..02a45ee5 --- /dev/null +++ b/os/port/ethermii.h @@ -0,0 +1,116 @@ +typedef struct Mii Mii; +typedef struct MiiPhy MiiPhy; + +enum { /* registers */ + Bmcr = 0x00, /* Basic Mode Control */ + Bmsr = 0x01, /* Basic Mode Status */ + Phyidr1 = 0x02, /* PHY Identifier #1 */ + Phyidr2 = 0x03, /* PHY Identifier #2 */ + Anar = 0x04, /* Auto-Negotiation Advertisement */ + Anlpar = 0x05, /* AN Link Partner Ability */ + Aner = 0x06, /* AN Expansion */ + Annptr = 0x07, /* AN Next Page TX */ + Annprr = 0x08, /* AN Next Page RX */ + Mscr = 0x09, /* MASTER-SLAVE Control */ + Mssr = 0x0A, /* MASTER-SLAVE Status */ + Esr = 0x0F, /* Extended Status */ + + NMiiPhyr = 32, + NMiiPhy = 32, +}; + +enum { /* Bmcr */ + BmcrSs1 = 0x0040, /* Speed Select[1] */ + BmcrCte = 0x0080, /* Collision Test Enable */ + BmcrDm = 0x0100, /* Duplex Mode */ + BmcrRan = 0x0200, /* Restart Auto-Negotiation */ + BmcrI = 0x0400, /* Isolate */ + BmcrPd = 0x0800, /* Power Down */ + BmcrAne = 0x1000, /* Auto-Negotiation Enable */ + BmcrSs0 = 0x2000, /* Speed Select[0] */ + BmcrLe = 0x4000, /* Loopback Enable */ + BmcrR = 0x8000, /* Reset */ +}; + +enum { /* Bmsr */ + BmsrEc = 0x0001, /* Extended Capability */ + BmsrJd = 0x0002, /* Jabber Detect */ + BmsrLs = 0x0004, /* Link Status */ + BmsrAna = 0x0008, /* Auto-Negotiation Ability */ + BmsrRf = 0x0010, /* Remote Fault */ + BmsrAnc = 0x0020, /* Auto-Negotiation Complete */ + BmsrPs = 0x0040, /* Preamble Suppression Capable */ + BmsrEs = 0x0100, /* Extended Status */ + Bmsr100T2HD = 0x0200, /* 100BASE-T2 HD Capable */ + Bmsr100T2FD = 0x0400, /* 100BASE-T2 FD Capable */ + Bmsr10THD = 0x0800, /* 10BASE-T HD Capable */ + Bmsr10TFD = 0x1000, /* 10BASE-T FD Capable */ + Bmsr100TXHD = 0x2000, /* 100BASE-TX HD Capable */ + Bmsr100TXFD = 0x4000, /* 100BASE-TX FD Capable */ + Bmsr100T4 = 0x8000, /* 100BASE-T4 Capable */ +}; + +enum { /* Anar/Anlpar */ + Ana10HD = 0x0020, /* Advertise 10BASE-T */ + Ana10FD = 0x0040, /* Advertise 10BASE-T FD */ + AnaTXHD = 0x0080, /* Advertise 100BASE-TX */ + AnaTXFD = 0x0100, /* Advertise 100BASE-TX FD */ + AnaT4 = 0x0200, /* Advertise 100BASE-T4 */ + AnaP = 0x0400, /* Pause */ + AnaAP = 0x0800, /* Asymmetrical Pause */ + AnaRf = 0x2000, /* Remote Fault */ + AnaAck = 0x4000, /* Acknowledge */ + AnaNp = 0x8000, /* Next Page Indication */ +}; + +enum { /* Mscr */ + Mscr1000THD = 0x0100, /* Advertise 1000BASE-T HD */ + Mscr1000TFD = 0x0200, /* Advertise 1000BASE-T FD */ +}; + +enum { /* Mssr */ + Mssr1000THD = 0x0400, /* Link Partner 1000BASE-T HD able */ + Mssr1000TFD = 0x0800, /* Link Partner 1000BASE-T FD able */ +}; + +enum { /* Esr */ + Esr1000THD = 0x1000, /* 1000BASE-T HD Capable */ + Esr1000TFD = 0x2000, /* 1000BASE-T FD Capable */ + Esr1000XHD = 0x4000, /* 1000BASE-X HD Capable */ + Esr1000XFD = 0x8000, /* 1000BASE-X FD Capable */ +}; + +typedef struct Mii { + Lock; + int nphy; + int mask; + MiiPhy* phy[NMiiPhy]; + MiiPhy* curphy; + + void* ctlr; + int (*mir)(Mii*, int, int); + int (*miw)(Mii*, int, int, int); +} Mii; + +typedef struct MiiPhy { + Mii* mii; + int oui; + int phyno; + + int anar; + int fc; + int mscr; + + int link; + int speed; + int fd; + int rfc; + int tfc; +}; + +extern int mii(Mii*, int); +extern int miiane(Mii*, int, int, int); +extern int miimir(Mii*, int); +extern int miimiw(Mii*, int, int); +extern int miireset(Mii*); +extern int miistatus(Mii*); diff --git a/os/port/ethersink.c b/os/port/ethersink.c new file mode 100644 index 00000000..dd701804 --- /dev/null +++ b/os/port/ethersink.c @@ -0,0 +1,65 @@ +/* + * An ethernet /dev/null. + * Useful as a bridging target with ethernet-based VPN. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" +#include "etherif.h" + +static long +ctl(Ether *ether, void *buf, long n) +{ + uchar ea[Eaddrlen]; + Cmdbuf *cb; + + cb = parsecmd(buf, n); + if(cb->nf >= 2 + && strcmp(cb->f[0], "ea")==0 + && parseether(ea, cb->f[1]) == 0){ + free(cb); + memmove(ether->ea, ea, Eaddrlen); + memmove(ether->addr, ether->ea, Eaddrlen); + return 0; + } + free(cb); + error(Ebadctl); + return -1; /* not reached */ +} + +static void +nop(Ether*) +{ +} + +static int +reset(Ether* ether) +{ + uchar ea[Eaddrlen]; + + if(ether->type==nil) + return -1; + memset(ea, 0, sizeof ea); + ether->mbps = 1000; + ether->attach = nop; + ether->transmit = nop; + ether->irq = -1; + ether->interrupt = nil; + ether->ifstat = nil; + ether->ctl = ctl; + ether->promiscuous = nil; + ether->multicast = nil; + ether->arg = ether; + return 0; +} + +void +ethersinklink(void) +{ + addethercard("sink", reset); +} diff --git a/os/port/exception.c b/os/port/exception.c new file mode 100644 index 00000000..84a74711 --- /dev/null +++ b/os/port/exception.c @@ -0,0 +1,216 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "interp.h" +#include "isa.h" +#include "runt.h" +#include "kernel.h" +#include "raise.h" + +static int +ematch(char *pat, char *exp) +{ + int l; + + if(strcmp(pat, exp) == 0) + return 1; + + l = strlen(pat); + if(l == 0) + return 0; + if(pat[l-1] == '*') { + if(l == 1) + return 1; + if(strncmp(pat, exp, l-1) == 0) + return 1; + } + return 0; +} + +static void +setstr(String *s, char *p) +{ + if(s == H) + return; + if(s->len < 0 || s->max < 4) + return; + kstrcpy(s->Sascii, p, s->max); /* TO DO: we are assuming they aren't runes */ + s->len = strlen(s->Sascii); +} + +static String *exstr; + +void +excinit(void) +{ + exstr = newstring(ERRMAX); + poolimmutable(D2H(exstr)); +} + +static String* +newestring(char *estr) +{ + String *s; + + if(waserror()){ + setstr(exstr, estr); + D2H(exstr)->ref++; + return exstr; + } + s = c2string(estr, strlen(estr)); + poperror(); + return s; +} + +#define NOPC 0xffffffff + +#define FRTYPE(f) ((f)->t == nil ? SEXTYPE(f)->reg.TR : (f)->t) + +/* + * clear up an uncalled frame + */ +static void +freeframe(uchar *fp, int setsp) +{ + Frame *f; + + f = (Frame*)fp; + if(f->t == nil) + unextend(f); + else if(f->t->np) + freeptrs(f, f->t); + if(setsp) + R.SP = fp; +} + +int +handler(char *estr) +{ + Prog *p; + Modlink *m, *mr; + int str, ne; + ulong pc, newpc; + long eoff; + uchar *fp, **eadr; + Frame *f; + Type *t, *zt; + Handler *h; + Except *e; + void *v; + + p = currun(); + if(*estr == 0 || p == nil) + return 0; + str = p->exval == H || D2H(p->exval)->t == &Tstring; + m = R.M; + if(m->compiled) + pc = (ulong)R.PC-(ulong)m->prog; + else + pc = R.PC-m->prog; + pc--; + fp = R.FP; + while(fp != nil){ /* look for a handler */ + if((h = m->m->htab) != nil){ + for( ; h->etab != nil; h++){ + if(pc < h->pc1 || pc >= h->pc2) + continue; + eoff = h->eoff; + zt = h->t; + for(e = h->etab, ne = h->ne; e->s != nil; e++, ne--){ + if(ematch(e->s, estr) && (str && ne <= 0 || !str && ne > 0)){ + newpc = e->pc; + goto found; + } + } + newpc = e->pc; + if(newpc != NOPC) + goto found; + } + } + if(!str && fp != R.FP){ /* becomes a string exception in immediate caller */ + v = p->exval; + p->exval = *(String**)v; + D2H(p->exval)->ref++; + destroy(v); + str = 1; + continue; + } + f = (Frame*)fp; + if(f->mr != nil) + m = f->mr; + if(m->compiled) + pc = (ulong)f->lr-(ulong)m->prog; + else + pc = f->lr-m->prog; + pc--; + fp = f->fp; + } + destroy(p->exval); + p->exval = H; + return 0; +found: + { + int n; + char name[3*KNAMELEN]; + + pc = modstatus(&R, name, sizeof(name)); + n = 10+1+strlen(name)+1+strlen(estr)+1; + p->exstr = realloc(p->exstr, n); + if(p->exstr != nil) + snprint(p->exstr, n, "%lud %s %s", pc, name, estr); + } + + /* + * there may be an uncalled frame at the top of the stack + */ + f = (Frame*)R.FP; + t = FRTYPE(f); + if(R.FP < R.EX || R.FP >= R.TS) + freeframe(R.EX+OA(Stkext, reg.tos.fr), 0); + else if(R.FP+t->size < R.SP) + freeframe(R.FP+t->size, 1); + + m = R.M; + while(R.FP != fp){ + f = (Frame*)R.FP; + R.PC = f->lr; + R.FP = f->fp; + R.SP = (uchar*)f; + mr = f->mr; + if(f->t == nil) + unextend(f); + else if(f->t->np) + freeptrs(f, f->t); + if(mr != nil){ + m = mr; + destroy(R.M); + R.M = m; + R.MP = m->MP; + } + } + if(zt != nil){ + freeptrs(fp, zt); + initmem(zt, fp); + } + eadr = (uchar**)(fp+eoff); + destroy(*eadr); + *eadr = H; + if(p->exval == H) + *eadr = (uchar*)newestring(estr); /* might fail */ + else{ + D2H(p->exval)->ref++; + *eadr = p->exval; + } + if(m->compiled) + R.PC = (Inst*)((ulong)m->prog+newpc); + else + R.PC = m->prog+newpc; + memmove(&p->R, &R, sizeof(R)); + p->kill = nil; + destroy(p->exval); + p->exval = H; + return 1; +} diff --git a/os/port/exportfs.c b/os/port/exportfs.c new file mode 100644 index 00000000..354574ef --- /dev/null +++ b/os/port/exportfs.c @@ -0,0 +1,1325 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "kernel.h" + +typedef struct Fid Fid; +typedef struct Export Export; +typedef struct Exq Exq; +typedef struct Uqid Uqid; + +enum +{ + Nfidhash = 32, + Nqidhash = 32, + QIDMASK = ((vlong)1<<48)-1, + MAXFDATA = 8192, + MAXRPCDEF = IOHDRSZ+MAXFDATA, /* initial/default */ + MAXRPCMAX = IOHDRSZ+64*1024, /* most every allowed */ + MSGHDRSZ = BIT32SZ+BIT8SZ+BIT16SZ +}; + +struct Export +{ + Lock; + Ref r; + Exq* work; + Lock fidlock; + Fid* fid[Nfidhash]; + QLock qidlock; + Uqid* qids[Nqidhash]; + ulong pathgen; + Chan* io; + Chan* root; + Pgrp* pgrp; + Egrp* egrp; + Fgrp* fgrp; + int async; + int readonly; + int msize; + char* user; +}; + +struct Fid +{ + Fid* next; + Fid** last; + Chan* chan; + int fid; + int ref; /* fcalls using the fid; locked by Export.Lock */ + vlong offset; /* last offset used (within directory) */ + int attached; /* fid attached or cloned but not clunked */ + Uqid* qid; /* generated qid */ +}; + +struct Uqid +{ + Ref; + int type; + int dev; + vlong oldpath; + vlong newpath; + Uqid* next; +}; + +struct Exq +{ + Lock; + int busy; /* fcall in progress */ + int finished; /* will do no more work on this request or flushes */ + Exq* next; + int shut; /* has been noted for shutdown */ + Exq* flush; /* queued flush requests */ + Exq* flusht; /* tail of flush queue */ + Export* export; + Proc* slave; + Fcall in, out; + uchar* buf; + int bsize; +}; + +struct +{ + Lock l; + QLock qwait; + Rendez rwait; + Exq *head; /* work waiting for a slave */ + Exq *tail; +}exq; + +static void exshutdown(Export*); +static int exflushed(Export*, Exq*); +static void exslave(void*); +static void exfree(Export*); +static void exfreeq(Exq*); +static void exportproc(void*); +static void exreply(Exq*, char*); +static int exisroot(Export*, Chan*); +static Uqid* uqidalloc(Export*, Chan*); +static void freeuqid(Export*, Uqid*); + +static char* Exversion(Export*, Fcall*, Fcall*); +static char* Exauth(Export*, Fcall*, Fcall*); +static char* Exattach(Export*, Fcall*, Fcall*); +static char* Exclunk(Export*, Fcall*, Fcall*); +static char* Excreate(Export*, Fcall*, Fcall*); +static char* Exopen(Export*, Fcall*, Fcall*); +static char* Exread(Export*, Fcall*, Fcall*); +static char* Exremove(Export*, Fcall*, Fcall*); +static char* Exstat(Export*, Fcall*, Fcall*); +static char* Exwalk(Export*, Fcall*, Fcall*); +static char* Exwrite(Export*, Fcall*, Fcall*); +static char* Exwstat(Export*, Fcall*, Fcall*); + +static char *(*fcalls[Tmax])(Export*, Fcall*, Fcall*); + +static char Enofid[] = "no such fid"; +static char Eseekdir[] = "can't seek on a directory"; +static char Eopen[] = "walk of open fid"; +static char Emode[] = "open/create -- unknown mode"; +static char Edupfid[] = "fid in use"; +static char Eaccess[] = "read/write -- not open in suitable mode"; +static char Ecount[] = "read/write -- count too big"; +int exdebug = 0; + +int +export(int fd, char *dir, int async) +{ + Chan *c, *dc; + Pgrp *pg; + Egrp *eg; + Export *fs; + + if(waserror()) + return -1; + c = fdtochan(up->env->fgrp, fd, ORDWR, 1, 1); + poperror(); + + if(waserror()){ + cclose(c); + return -1; + } + dc = namec(dir, Atodir, 0, 0); + poperror(); + + fs = malloc(sizeof(Export)); + if(fs == nil){ + cclose(c); + cclose(dc); + error(Enomem); + } + + fs->r.ref = 1; + pg = up->env->pgrp; + fs->pgrp = pg; + incref(pg); + eg = up->env->egrp; + fs->egrp = eg; + if(eg != nil) + incref(eg); + fs->fgrp = newfgrp(nil); + kstrdup(&fs->user, up->env->user); + fs->root = dc; + fs->io = c; + fs->pathgen = 0; + fs->msize = 0; + c->flag |= CMSG; + fs->async = async; + + if(async){ + if(waserror()) + return -1; + kproc("exportfs", exportproc, fs, 0); + poperror(); + }else + exportproc(fs); + + return 0; +} + +static void +exportinit(void) +{ + lock(&exq.l); + if(fcalls[Tversion] != nil) { + unlock(&exq.l); + return; + } + fcalls[Tversion] = Exversion; + fcalls[Tauth] = Exauth; + fcalls[Tattach] = Exattach; + fcalls[Twalk] = Exwalk; + fcalls[Topen] = Exopen; + fcalls[Tcreate] = Excreate; + fcalls[Tread] = Exread; + fcalls[Twrite] = Exwrite; + fcalls[Tclunk] = Exclunk; + fcalls[Tremove] = Exremove; + fcalls[Tstat] = Exstat; + fcalls[Twstat] = Exwstat; + unlock(&exq.l); +} + +static int +exisroot(Export *fs, Chan *c) +{ + return eqchan(fs->root, c, 1); +} + +static int +exreadn(Chan *c, void *buf, int n) +{ + int nr, t; + + if(waserror()) + return -1; + for(nr = 0; nr < n;){ + t = devtab[c->type]->read(c, (char*)buf+nr, n-nr, 0); + if(t <= 0) + break; + nr += t; + } + poperror(); + return nr; +} + +static int +exreadmsg(Chan *c, void *a, uint n) +{ + int m, len; + uchar *buf; + + buf = a; + m = exreadn(c, buf, BIT32SZ); + if(m < BIT32SZ){ + if(m < 0) + return -1; + return 0; + } + len = GBIT32(buf); + if(len <= BIT32SZ || len > n){ + kwerrstr("bad length in Styx message header"); + return -1; + } + len -= BIT32SZ; + m = exreadn(c, buf+BIT32SZ, len); + if(m < len){ + if(m < 0) + return -1; + return 0; + } + return BIT32SZ+m; +} + +static void +exportproc(void *a) +{ + Exq *q; + int async, msize; + int n, type; + Export *fs = a; + + exportinit(); + + for(;;){ + + msize = fs->msize; + if(msize == 0) + msize = MAXRPCDEF; + for(n=0;; n++){ /* we don't use smalloc, to avoid memset */ + q = mallocz(sizeof(*q)+msize, 0); + if(q != nil || n > 6000) + break; + if(n%600 == 0) + print("exportproc %ld: waiting for memory (%d) for request\n", up->pid, msize); + tsleep(&up->sleep, return0, nil, 100); + } + if(q == nil){ + kwerrstr("out of memory: read request"); + n = -1; + break; + } + memset(q, 0, sizeof(*q)); + q->buf = (uchar*)q + sizeof(*q); + q->bsize = msize; + + n = exreadmsg(fs->io, q->buf, msize); /* TO DO: avoid copy */ + if(n <= 0) + break; + if(convM2S(q->buf, n, &q->in) != n){ + kwerrstr("bad T-message"); + n = -1; + break; + } + type = q->in.type; + if(type < Tversion || type >= Tmax || type&1 || type == Terror){ + kwerrstr("invalid T-message type %d", type); + n = -1; + break; + } + + if(exdebug) + print("export %ld <- %F\n", up->pid, &q->in); + + q->out.type = type+1; + q->out.tag = q->in.tag; + + q->export = fs; + incref(&fs->r); + + if(fs->readonly){ + switch(type){ + case Topen: + if((q->in.mode & (ORCLOSE|OTRUNC|3)) == OREAD) + break; + /* FALL THROUGH */ + case Tcreate: + case Twrite: + case Tremove: + case Twstat: + q->out.type = Rerror; + q->out.ename = "file system read only"; + exreply(q, "exportproc"); + exfreeq(q); + continue; + } + } + + if(q->in.type == Tflush){ + if(exflushed(fs, q)){ + /* not yet started or not found (flush arrived after reply); we reply */ + if(exdebug) + print("export: flush %d\n", q->in.oldtag); + exreply(q, "exportproc"); + exfreeq(q); + } + continue; + } + + lock(&exq.l); + if(exq.head == nil) + exq.head = q; + else + exq.tail->next = q; + q->next = nil; + exq.tail = q; + unlock(&exq.l); + if(exq.qwait.head == nil) + kproc("exslave", exslave, nil, 0); + wakeup(&exq.rwait); + } + + if(exdebug){ + if(n < 0) + print("exportproc %ld shut down: %s\n", up->pid, up->env->errstr); + else + print("exportproc %ld shut down\n", up->pid); + } + + free(q); + exshutdown(fs); + async = fs->async; + exfree(fs); + + if(async) + pexit("mount shut down", 0); +} + +static int +exflushed(Export *fs, Exq *fq) +{ + Exq *q, **last; + ulong pid; + + /* not yet started? */ + lock(&exq.l); + for(last = &exq.head; (q = *last) != nil; last = &q->next) + if(q->export == fs && q->in.tag == fq->in.oldtag){ + *last = q->next; + unlock(&exq.l); + /* not yet started: discard, and Rflush */ + exfreeq(q); + return 1; + } + unlock(&exq.l); + + /* tricky case: in progress */ + lock(fs); + for(q = fs->work; q != nil; q = q->next) + if(q->in.tag == fq->in.oldtag){ + pid = 0; + lock(q); + if(q->finished){ + /* slave replied and emptied its flush queue; we can Rflush now */ + unlock(q); + return 1; + } + /* append to slave's flush queue */ + fq->next = nil; + if(q->flush != nil) + q->flusht->next = fq; + else + q->flush = fq; + q->flusht = fq; + if(q->busy){ + pid = q->slave->pid; + swiproc(q->slave, 0); + } + unlock(q); + unlock(fs); + if(exdebug && pid) + print("export: swiproc %ld to flush %d\n", pid, fq->in.oldtag); + return 0; + } + unlock(fs); + + /* not found */ + return 1; +} + +static void +exfreeq(Exq *q) +{ + Exq *fq; + + while((fq = q->flush) != nil){ + q->flush = fq->next; + exfree(fq->export); + free(fq); + } + exfree(q->export); + free(q); +} + +static void +exshutdown(Export *fs) +{ + Exq *q, **last; + + /* work not started */ + lock(&exq.l); + for(last = &exq.head; (q = *last) != nil;) + if(q->export == fs){ + *last = q->next; + exfreeq(q); + }else + last = &q->next; + unlock(&exq.l); + + /* tell slaves to abandon work in progress */ + lock(fs); + while((q = fs->work) != nil){ + fs->work = q->next; + lock(q); + q->shut = 1; + swiproc(q->slave, 0); /* whether busy or not */ + unlock(q); + } + unlock(fs); +} + +static void +exfreefids(Export *fs) +{ + Fid *f, *n; + int i; + + for(i = 0; i < Nfidhash; i++){ + for(f = fs->fid[i]; f != nil; f = n){ + n = f->next; + f->attached = 0; + if(f->ref == 0) { + if(f->chan != nil) + cclose(f->chan); + freeuqid(fs, f->qid); + free(f); + } else + print("exfreefids: busy fid\n"); + } + } +} + +static void +exfree(Export *fs) +{ + if(exdebug) + print("export p/s %ld free %p ref %ld\n", up->pid, fs, fs->r.ref); + if(decref(&fs->r) != 0) + return; + closepgrp(fs->pgrp); + closeegrp(fs->egrp); + closefgrp(fs->fgrp); + cclose(fs->root); + cclose(fs->io); + exfreefids(fs); + free(fs->user); + free(fs); +} + +static int +exwork(void*) +{ + return exq.head != nil; +} + +static void +exslave(void*) +{ + Export *fs; + Exq *q, *t, *fq, **last; + char *err; + + for(;;){ + qlock(&exq.qwait); + if(waserror()){ + qunlock(&exq.qwait); + continue; + } + sleep(&exq.rwait, exwork, nil); + poperror(); + + lock(&exq.l); + q = exq.head; + if(q == nil) { + unlock(&exq.l); + qunlock(&exq.qwait); + continue; + } + exq.head = q->next; + + qunlock(&exq.qwait); + + /* + * put the job on the work queue before it's + * visible as off of the head queue, so it's always + * findable for flushes and shutdown + */ + notkilled(); + q->slave = up; + q->busy = 1; /* fcall in progress: interruptible */ + fs = q->export; + lock(fs); + q->next = fs->work; + fs->work = q; + unlock(fs); + unlock(&exq.l); + + up->env->pgrp = q->export->pgrp; + up->env->egrp = q->export->egrp; + up->env->fgrp = q->export->fgrp; + kstrdup(&up->env->user, q->export->user); + + if(exdebug > 1) + print("exslave %ld dispatch %F\n", up->pid, &q->in); + + if(waserror()){ + print("exslave %ld err %s\n", up->pid, up->env->errstr); /* shouldn't happen */ + err = up->env->errstr; + }else{ + if(q->in.type >= Tmax || !fcalls[q->in.type]){ + snprint(up->genbuf, sizeof(up->genbuf), "unknown message: %F", &q->in); + err = up->genbuf; + }else{ + switch(q->in.type){ + case Tread: + q->out.data = (char*)q->buf + IOHDRSZ; + break; + case Tstat: + q->out.stat = q->buf + MSGHDRSZ + BIT16SZ; /* leaves it just where we want it */ + q->out.nstat = q->bsize-(MSGHDRSZ+BIT16SZ); + break; + } + err = (*fcalls[q->in.type])(fs, &q->in, &q->out); + } + poperror(); + } + + /* + * if the fcall completed without error we must reply, + * even if a flush is pending (because the underlying server + * might have changed state), unless the export has shut down completely. + * must also reply to each flush in order, and only after the original reply (if sent). + */ + lock(q); + notkilled(); + q->busy = 0; /* operation complete */ + if(!q->shut){ + if(q->flush == nil || err == nil){ + unlock(q); + q->out.type = q->in.type+1; + q->out.tag = q->in.tag; + if(err){ + q->out.type = Rerror; + q->out.ename = err; + } + exreply(q, "exslave"); + lock(q); + } + while((fq = q->flush) != nil && !q->shut){ + q->flush = fq->next; + unlock(q); + exreply(fq, "exslave"); + exfreeq(fq); + lock(q); + } + } + q->finished = 1; /* promise not to send any more */ + unlock(q); + + lock(fs); + for(last = &fs->work; (t = *last) != nil; last = &t->next) + if(t == q){ + *last = q->next; + break; + } + unlock(fs); + + notkilled(); + exfreeq(q); + } + print("exslave %ld shut down", up->pid); /* not reached */ + pexit("exslave shut down", 0); +} + +static void +exreply(Exq *q, char *who) +{ + Export *fs; + Fcall *r; + int n; + + fs = q->export; + r = &q->out; + + n = convS2M(r, q->buf, q->bsize); + if(n == 0){ + r->type = Rerror; + if(fs->msize == 0) + r->ename = "Tversion not seen"; + else + r->ename = "failed to convert R-message"; + n = convS2M(r, q->buf, q->bsize); + } + + if(exdebug) + print("%s %ld -> %F\n", who, up->pid, r); + + if(!waserror()){ + devtab[fs->io->type]->write(fs->io, q->buf, n, 0); + poperror(); + } +} + +static int +exiounit(Export *fs, Chan *c) +{ + int iounit; + + iounit = fs->msize-IOHDRSZ; + if(c->iounit != 0 && c->iounit < fs->msize) + iounit = c->iounit; + return iounit; +} + +static Qid +Exrmtqid(Chan *c, Uqid *qid) +{ + Qid q; + + q.path = qid->newpath; + q.vers = c->qid.vers; + q.type = c->qid.type; + return q; +} + +static Fid* +Exmkfid(Export *fs, ulong fid) +{ + ulong h; + Fid *f, *nf; + + nf = malloc(sizeof(Fid)); + if(nf == nil) + return nil; + lock(&fs->fidlock); + h = fid % Nfidhash; + for(f = fs->fid[h]; f != nil; f = f->next){ + if(f->fid == fid){ + unlock(&fs->fidlock); + free(nf); + return nil; + } + } + + nf->next = fs->fid[h]; + if(nf->next != nil) + nf->next->last = &nf->next; + nf->last = &fs->fid[h]; + fs->fid[h] = nf; + + nf->fid = fid; + nf->ref = 1; + nf->attached = 1; + nf->offset = 0; + nf->chan = nil; + nf->qid = nil; + unlock(&fs->fidlock); + return nf; +} + +static Fid* +Exgetfid(Export *fs, ulong fid) +{ + Fid *f; + ulong h; + + lock(&fs->fidlock); + h = fid % Nfidhash; + for(f = fs->fid[h]; f; f = f->next) { + if(f->fid == fid){ + if(f->attached == 0) + break; + f->ref++; + unlock(&fs->fidlock); + return f; + } + } + unlock(&fs->fidlock); + return nil; +} + +static void +Exputfid(Export *fs, Fid *f) +{ + Chan *c; + + lock(&fs->fidlock); + f->ref--; + if(f->ref == 0 && f->attached == 0){ + c = f->chan; + f->chan = nil; + *f->last = f->next; + if(f->next != nil) + f->next->last = f->last; + unlock(&fs->fidlock); + if(c != nil) + cclose(c); + freeuqid(fs, f->qid); + free(f); + return; + } + unlock(&fs->fidlock); +} + +static Chan* +exmount(Chan *c, Mhead **mp, int doname) +{ + Chan *nc; + Cname *oname; + + nc = nil; + if((c->flag & COPEN) == 0 && findmount(&nc, mp, c->type, c->dev, c->qid)){ + if(waserror()){ + cclose(nc); + nexterror(); + } + nc = cunique(nc); + poperror(); + if(doname){ + oname = c->name; + incref(oname); + cnameclose(nc->name); + nc->name = oname; + } + return nc; + } + incref(c); + return c; +} + +static char* +Exversion(Export *fs, Fcall *t, Fcall *r) +{ + char *p; + static char version[] = VERSION9P; + int iounit; + + r->msize = t->msize; + if(r->msize > MAXRPCMAX) + r->msize = MAXRPCMAX; + iounit = fs->io->iounit; + if(iounit != 0 && iounit > 64 && iounit < r->msize) + r->msize = iounit; + if(r->msize < 64) + return "message size too small"; + if((p = strchr(t->version, '.')) != nil) + *p = 0; + if(strncmp(t->version, "9P", 2) ==0 && strcmp(version, t->version) <= 0){ + r->version = version; + fs->msize = r->msize; + }else + r->version = "unknown"; + return nil; +} + +static char* +Exauth(Export *fs, Fcall *t, Fcall *r) +{ + USED(fs); + USED(t); + USED(r); + return "authentication not required"; +} + +static char* +Exattach(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + + f = Exmkfid(fs, t->fid); + if(f == nil) + return Edupfid; + if(waserror()){ + f->attached = 0; + Exputfid(fs, f); + return up->env->errstr; + } + f->chan = cclone(fs->root); + f->qid = uqidalloc(fs, f->chan); + poperror(); + r->qid = Exrmtqid(f->chan, f->qid); + Exputfid(fs, f); + return nil; +} + +static char* +Exclunk(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + + USED(r); + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + f->attached = 0; + Exputfid(fs, f); + return nil; +} + +static int +safewalk(Chan **cp, char **names, int nnames, int nomount, int *nerror) +{ + int r; + + /* walk can raise error */ + if(waserror()) + return -1; + r = walk(cp, names, nnames, nomount, nerror); + poperror(); + return r; +} + +static char* +Exwalk(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f, *nf; + Chan *c; + char *name; + Uqid *qid; + int i; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(f->chan->flag & COPEN){ + Exputfid(fs, f); + return Eopen; + } + + if(waserror()) + return up->env->errstr; + c = cclone(f->chan); + poperror(); + qid = f->qid; + incref(qid); + r->nwqid = 0; + if(t->nwname > 0){ + for(i=0; inwname; i++){ + name = t->wname[i]; + if(!exisroot(fs, c) || *name != '\0' && strcmp(name, "..") != 0){ + if(safewalk(&c, &name, 1, 0, nil) < 0){ + /* leave the original state on error */ + cclose(c); + freeuqid(fs, qid); + Exputfid(fs, f); + if(i == 0) + return up->env->errstr; + return nil; + } + freeuqid(fs, qid); + qid = uqidalloc(fs, c); + } + r->wqid[r->nwqid++] = Exrmtqid(c, qid); + } + } + + if(t->newfid != t->fid){ + nf = Exmkfid(fs, t->newfid); + if(nf == nil){ + cclose(c); + freeuqid(fs, qid); + Exputfid(fs, f); + return Edupfid; + } + nf->chan = c; + nf->qid = qid; + Exputfid(fs, nf); + }else{ + cclose(f->chan); + f->chan = c; + freeuqid(fs, f->qid); + f->qid = qid; + } + Exputfid(fs, f); + return nil; +} + +static char* +Exopen(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + Uqid *qid; + Mhead *m; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(f->chan->flag & COPEN){ + Exputfid(fs, f); + return Emode; + } + m = nil; + c = exmount(f->chan, &m, 1); + if(waserror()){ + cclose(c); + Exputfid(fs, f); + return up->env->errstr; + } + + /* only save the mount head if it's a multiple element union */ + if(m && m->mount && m->mount->next) + c->umh = m; + else + putmhead(m); + + c = devtab[c->type]->open(c, t->mode); + if(t->mode & ORCLOSE) + c->flag |= CRCLOSE; + + qid = uqidalloc(fs, c); + poperror(); + freeuqid(fs, f->qid); + cclose(f->chan); + f->chan = c; + f->qid = qid; + f->offset = 0; + r->qid = Exrmtqid(c, f->qid); + r->iounit = exiounit(fs, c); + Exputfid(fs, f); + return nil; +} + +static char* +Excreate(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + volatile struct {Chan *c;} c, dc; + Cname *oname; + Uqid *qid; + Mhead *m; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(f->chan->flag & COPEN){ + Exputfid(fs, f); + return Emode; + } + if(waserror()){ + Exputfid(fs, f); + return up->env->errstr; + } + validname(t->name, 0); + if(t->name[0] == '.' && (t->name[1] == '\0' || t->name[1] == '.' && t->name[2] == '\0')) + error(Efilename); /* underlying server should check, but stop it here */ + + m = nil; + c.c = exmount(f->chan, &m, 1); + if(waserror()){ + cclose(c.c); + if(m != nil) + putmhead(m); + nexterror(); + } + if(m != nil){ + oname = c.c->name; + incref(oname); + if(waserror()){ + cnameclose(oname); + nexterror(); + } + dc.c = createdir(c.c, m); + if(waserror()){ + cclose(dc.c); + nexterror(); + } + c.c = cunique(dc.c); + poperror(); + cnameclose(c.c->name); + poperror(); + c.c->name = oname; + } + devtab[c.c->type]->create(c.c, t->name, t->mode, t->perm); + c.c->name = addelem(c.c->name, t->name); + if(t->mode & ORCLOSE) + c.c->flag |= CRCLOSE; + qid = uqidalloc(fs, c.c); + poperror(); + if(m != nil) + putmhead(m); + + poperror(); + cclose(f->chan); + f->chan = c.c; + freeuqid(fs, f->qid); + f->qid = qid; + r->qid = Exrmtqid(c.c, f->qid); + r->iounit = exiounit(fs, c.c); + Exputfid(fs, f); + return nil; +} + +static char* +Exread(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + long off; + int dir, n, seek; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + + if(waserror()) { + Exputfid(fs, f); + return up->env->errstr; + } + c = f->chan; + if((c->flag & COPEN) == 0) + error(Emode); + if(c->mode != OREAD && c->mode != ORDWR) + error(Eaccess); + if(t->count < 0 || t->count > fs->msize-IOHDRSZ) + error(Ecount); + if(t->offset < 0) + error(Enegoff); + dir = c->qid.type & QTDIR; + if(dir && t->offset != f->offset){ + if(t->offset != 0) + error(Eseekdir); + f->offset = 0; + c->uri = 0; + c->dri = 0; + } + + for(;;){ + n = t->count; + seek = 0; + off = t->offset; + if(dir && f->offset != off){ + off = f->offset; + n = t->offset - off; + if(n > MAXFDATA) + n = MAXFDATA; + seek = 1; + } + if(dir && c->umh != nil){ + if(0) + print("union read %d uri %d dri %d\n", seek, c->uri, c->dri); + n = unionread(c, r->data, n); + } + else{ + c->offset = off; + n = devtab[c->type]->read(c, r->data, n, off); + lock(c); + c->offset += n; + unlock(c); + } + f->offset = off + n; + if(n == 0 || !seek) + break; + } + r->count = n; + + poperror(); + Exputfid(fs, f); + return nil; +} + +static char* +Exwrite(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->env->errstr; + } + c = f->chan; + if((c->flag & COPEN) == 0) + error(Emode); + if(c->mode != OWRITE && c->mode != ORDWR) + error(Eaccess); + if(c->qid.type & QTDIR) + error(Eisdir); + if(t->count < 0 || t->count > fs->msize-IOHDRSZ) + error(Ecount); + if(t->offset < 0) + error(Enegoff); + r->count = devtab[c->type]->write(c, t->data, t->count, t->offset); + poperror(); + Exputfid(fs, f); + return nil; +} + +static char* +Exstat(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + int n; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + c = exmount(f->chan, nil, 1); + if(waserror()){ + cclose(c); + Exputfid(fs, f); + return up->env->errstr; + } + n = devtab[c->type]->stat(c, r->stat, r->nstat); + if(n <= BIT16SZ) + error(Eshortstat); + r->nstat = n; + poperror(); + cclose(c); + Exputfid(fs, f); + return nil; +} + +static char* +Exwstat(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + + USED(r); + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->env->errstr; + } + validstat(t->stat, t->nstat); /* check name */ + + c = exmount(f->chan, nil, 0); + if(waserror()){ + cclose(c); + nexterror(); + } + devtab[c->type]->wstat(c, t->stat, t->nstat); + poperror(); + + cclose(c); + poperror(); + Exputfid(fs, f); + return nil; +} + +static char* +Exremove(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + + USED(r); + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(waserror()){ + f->attached = 0; + Exputfid(fs, f); + return up->env->errstr; + } + c = exmount(f->chan, nil, 0); + if(waserror()){ + c->type = 0; /* see below */ + cclose(c); + nexterror(); + } + devtab[c->type]->remove(c); + poperror(); + poperror(); + + /* + * chan is already clunked by remove. + * however, we need to recover the chan, + * and follow sysremove's lead in making it point to root. + */ + c->type = 0; + + cclose(c); + f->attached = 0; + Exputfid(fs, f); + return nil; +} + +/* + * unique path generation + */ + +static int +uqidhash(vlong path) +{ + ulong p; + p = (ulong)path; + return ((p>>16) ^ (p>>8) ^ p) & (Nqidhash-1); +} + +static Uqid ** +uqidlook(Uqid **tab, Chan *c, vlong path) +{ + Uqid **hp, *q; + + for(hp = &tab[uqidhash(path)]; (q = *hp) != nil; hp = &q->next) + if(q->type == c->type && q->dev == c->dev && q->oldpath == path) + break; + return hp; +} + +static int +uqidexists(Uqid **tab, vlong path) +{ + int i; + Uqid *q; + + for(i=0; inext) + if(q->newpath == path) + return 1; + return 0; +} + +static Uqid * +uqidalloc(Export *fs, Chan *c) +{ + Uqid **hp, *q; + + qlock(&fs->qidlock); + hp = uqidlook(fs->qids, c, c->qid.path); + if((q = *hp) != nil){ + incref(q); + qunlock(&fs->qidlock); + return q; + } + q = mallocz(sizeof(*q), 1); + if(q == nil){ + qunlock(&fs->qidlock); + error(Enomem); + } + q->ref = 1; + q->type = c->type; + q->dev = c->dev; + q->oldpath = c->qid.path; + q->newpath = c->qid.path; + while(uqidexists(fs->qids, q->newpath)){ + if(++fs->pathgen >= (1<<16)) + fs->pathgen = 1; + q->newpath = ((vlong)fs->pathgen<<48) | (q->newpath & QIDMASK); + } + q->next = nil; + *hp = q; + qunlock(&fs->qidlock); + return q; +} + +static void +freeuqid(Export *fs, Uqid *q) +{ + Uqid **hp; + + if(q == nil) + return; + qlock(&fs->qidlock); + if(decref(q) == 0){ + hp = &fs->qids[uqidhash(q->oldpath)]; + for(; *hp != nil; hp = &(*hp)->next) + if(*hp == q){ + *hp = q->next; + free(q); + break; + } + } + qunlock(&fs->qidlock); +} diff --git a/os/port/flashamd29f0x0.c b/os/port/flashamd29f0x0.c new file mode 100644 index 00000000..06798d05 --- /dev/null +++ b/os/port/flashamd29f0x0.c @@ -0,0 +1,167 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "../port/flashif.h" + +/* + * AMD29F0x0 with 4 interleaved to give 32 bits + */ + +enum { + DQ7 = 0x80808080, + DQ6 = 0x40404040, + DQ5 = 0x20202020, + DQ3 = 0x08080808, + DQ2 = 0x04040404, +}; + +#define DPRINT if(0)print +#define EPRINT if(1)print + +static char* +amdwait(ulong *p, ulong ticks) +{ + ulong v0, v1; + + ticks += m->ticks+1; + v0 = *p; + for(;;){ + sched(); + v1 = *p; + if((v1 & DQ6) == (v0 & DQ6)) + break; + if((v1 & DQ5) == DQ5){ + v0 = *p; + v1 = *p; + if((v1 & DQ6) == (v0 & DQ6)) + break; + EPRINT("flash: DQ5 error: %8.8lux %8.8lux\n", v0, v1); + return "flash write error"; + } + if(m->ticks >= ticks){ + EPRINT("flash: timed out: %8.8lux\n", *p); + return "flash write timed out"; + } + v0 = v1; + } + return nil; +} + +static int +eraseall(Flash *f) +{ + ulong *p; + int s; + char *e; + + DPRINT("flash: erase all\n"); + p = (ulong*)f->addr; + s = splhi(); + *(p+0x555) = 0xAAAAAAAA; + *(p+0x2AA) = 0x55555555; + *(p+0x555) = 0x80808080; + *(p+0x555) = 0xAAAAAAAA; + *(p+0x2AA) = 0x55555555; + *(p+0x555) = 0x10101010; /* chip erase */ + splx(s); + e = amdwait(p, MS2TK(64*1000)); + *p = 0xF0F0F0F0; /* reset */ + if(e != nil) + error(e); + return 0; +} + +static int +erasezone(Flash *f, Flashregion *r, ulong addr) +{ + ulong *p; + int s; + char *e; + + DPRINT("flash: erase %8.8lux\n", addr); + if(addr & (r->erasesize-1)) + return -1; /* bad zone */ + p = (ulong*)f->addr; + s = splhi(); + *(p+0x555) = 0xAAAAAAAA; + *(p+0x2AA) = 0x55555555; + *(p+0x555) = 0x80808080; + *(p+0x555) = 0xAAAAAAAA; + *(p+0x2AA) = 0x55555555; + p += addr>>2; + *p = 0x30303030; /* sector erase */ + splx(s); + e = amdwait(p, MS2TK(8*1000)); + *p = 0xF0F0F0F0; /* reset */ + if(e != nil) + error(e); + return 0; +} + +static int +write4(Flash *f, ulong offset, void *buf, long n) +{ + ulong *p, *a, *v, w; + int s; + char *e; + + p = (ulong*)f->addr; + if(((ulong)p|offset|n)&3) + return -1; + n >>= 2; + a = p + (offset>>2); + v = buf; + for(; --n >= 0; v++, a++){ + w = *a; + DPRINT("flash: write %lux %lux -> %lux\n", (ulong)a, w, *v); + if(w == *v) + continue; /* already set */ + if(~w & *v) + error("flash not erased"); + s = splhi(); + *(p+0x555) = 0xAAAAAAAA; + *(p+0x2AA) = 0x55555555; + *(p+0x555) = 0xA0A0A0A0; /* program */ + *a = *v; + splx(s); + microdelay(8); + if(*a != *v){ + microdelay(8); + while(*a != *v){ + e = amdwait(a, 1); + if(e != nil) + error(e); + } + } + } + return 0; +} + +static int +reset(Flash *f) +{ + f->id = 0x01; /* can't use autoselect: might be running in flash */ + f->devid = 0; + f->write = write4; + f->eraseall = eraseall; + f->erasezone = erasezone; + f->suspend = nil; + f->resume = nil; + f->width = 4; + f->interleave = 0; /* TO DO */ + f->nr = 1; + f->regions[0] = (Flashregion){f->size/(4*64*1024), 0, f->size, 4*64*1024, 0}; + *(ulong*)f->addr = 0xF0F0F0F0; /* reset (just in case) */ + return 0; +} + +void +flashamd29f0x0link(void) +{ + addflashcard("AMD29F0x0", reset); +} diff --git a/os/port/flashcfi16.c b/os/port/flashcfi16.c new file mode 100644 index 00000000..3c22f475 --- /dev/null +++ b/os/port/flashcfi16.c @@ -0,0 +1,134 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "../port/flashif.h" + +/* + * Common Flash Interface (1x16 and 2x16) + */ + +/* interleaved flash has chips at even and odd word addresses */ +#define I(x) (((x)<<24)|((x)<<16)|((x)<<8)|(x)) + +enum { + ReadArray = I(0xFF), + ReadQuery = I(0x98), +}; + +#include "flashintel" + +static int +cfiget1(Flash *f, ulong a) +{ + ulong v; + + v = flashget(f, a); +//iprint("%.8lux->%.4ux\n", a, v); + if(f->width == 2 && v == 0xFFFF) + return 0; /* get this on old, partially-conforming CFI */ + return v & 0xFF; +} + +static int +cfiget2(Flash *f, ulong i) +{ + return (cfiget1(f, i+1)<<8) | cfiget1(f, i); +} + +static int +cfiquery(Flash *f) +{ + Flashregion *r; + ulong addr; + int i; + + flashput(f, 0x55, ReadQuery); + if(!(cfiget1(f, 0x10) == 'Q' && cfiget1(f, 0x11) == 'R' && cfiget1(f, 0x12) == 'Y')) /* TO DO: detect interleave */ + return 0; + f->alg = cfiget2(f, 0x13); + i = cfiget1(f, 0x27); + if(i > 0 && i < 32) + i = 1<devsize = i; + f->size = f->devsize; + if(f->interleave) + f->size *= 2; + i = cfiget2(f, 0x2A); + if(i > 0 && i < 32) + i = 1<maxwb = i; + f->nr = cfiget1(f, 0x2C); + if(f->nr != 0){ + addr = 0; + for(i=0; inr; i++){ + r = &f->regions[i]; + r->n = cfiget2(f, 0x2D+4*i)+1; + r->erasesize = cfiget2(f, 0x2D+2+4*i)*256; + if(r->erasesize == 0) + r->erasesize = 128; + if(f->interleave) + r->erasesize *= 2; /* TO DO */ + r->start = addr; + r->end = r->start + r->n*r->erasesize; + } + if(1){ + iprint("cfi: devsize=%lud maxwb=%d\n", f->devsize, f->maxwb); + for(i=0; inr; i++){ + r = &f->regions[i]; + iprint("flash %d: %d %lud %8.8lux %8.8lux\n", i, r->n, r->erasesize, r->start, r->end); + } + } + }else{ + f->nr = 1; + f->regions[0] = (Flashregion){1, 0, f->devsize, f->devsize, 0}; + } + return 1; +} + +static int +reset(Flash *f) +{ + if(f->xip) + return -1; /* can't use this interface if executing from flash */ + if(f->width == 0) + f->width = 2; + if(!cfiquery(f) || f->alg != 1 && f->alg != 3){ + /* apparently not CFI: try to reset to read mode before return */ + flashput(f, 0x55, ClearStatus); + flashput(f, 0x55, ReadArray); + return -1; + } + f->cmask = 0x00FF00FF; + flashput(f, 0x55, ClearStatus); + flashput(f, 0x55, ReadID); + f->id = cfiget1(f, 0x00); + f->devid = cfiget1(f, 0x01); + flashput(f, 0x55, ClearStatus); + flashput(f, 0x55, ReadArray); + if(f->width == 2){ + f->cmask = 0x00FF; + f->write = intelwrite2; + }else{ + f->cmask = 0x00FF00FF; + f->write = intelwrite4; + } + f->erasezone = intelerase; + f->suspend = nil; + f->resume = nil; + return 0; +} + +void +flashcfi16link(void) +{ + addflashcard("cfi16", reset); +} diff --git a/os/port/flashcfi8.c b/os/port/flashcfi8.c new file mode 100644 index 00000000..099267b6 --- /dev/null +++ b/os/port/flashcfi8.c @@ -0,0 +1,143 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "../port/flashif.h" + +/* + * Common Flash Interface (1x8 and 2x8) + */ + +/* interleaved flash has chips at even and odd word addresses */ +#define I(x) (((x)<<24)|((x)<<16)|((x)<<8)|(x)) + +enum { + ReadArray = I(0xFF), + ReadQuery = I(0x98), +}; + +/* TO DO: flash amd */ +#include "flashintel" + +static int +cfiget1(Flash *f, ulong a) +{ + ulong v; + + v = flashget(f, a); +//iprint("%.8lux->%.4ux\n", a, v); + return v & 0xFF; +} + +static int +cfiget2(Flash *f, ulong i) +{ + return (cfiget1(f, i+1)<<8) | cfiget1(f, i); +} + +static int +cfiquery(Flash *f) +{ + Flashregion *r; + ulong addr; + int i; + + flashput(f, 0x55, ReadQuery); + if(!(cfiget1(f, 0x10) == 'Q' && cfiget1(f, 0x11) == 'R' && cfiget1(f, 0x12) == 'Y')) /* TO DO: detect interleave */ + return 0; + f->alg = cfiget2(f, 0x13); + i = cfiget1(f, 0x27); + if(i > 0 && i < 32) + i = 1<devsize = i; + f->size = f->devsize; + if(f->interleave) + f->size *= 2; + i = cfiget2(f, 0x2A); + if(i > 0 && i < 32) + i = 1<maxwb = i; + f->nr = cfiget1(f, 0x2C); + if(f->nr != 0){ + addr = 0; + for(i=0; inr; i++){ + r = &f->regions[i]; + r->n = cfiget2(f, 0x2D+4*i)+1; + r->erasesize = cfiget2(f, 0x2D+2+4*i)*256; + if(r->erasesize == 0) + r->erasesize = 128; + if(f->interleave) + r->erasesize *= 2; /* TO DO */ + r->start = addr; + r->end = r->start + r->n*r->erasesize; + } + if(1){ + iprint("cfi: devsize=%lud maxwb=%d\n", f->devsize, f->maxwb); + for(i=0; inr; i++){ + r = &f->regions[i]; + iprint("flash %d: %d %lud %8.8lux %8.8lux\n", i, r->n, r->erasesize, r->start, r->end); + } + } + }else{ + f->nr = 1; + f->regions[0] = (Flashregion){1, 0, f->devsize, f->devsize, 0}; + } + return 1; +} + +static int +reset(Flash *f) +{ + if(f->xip) + return -1; /* can't use this interface if executing from flash */ + if(f->width == 0) + f->width = 1; + if(!cfiquery(f) && f->bshift==0) + f->bshift = 1; /* try this */ + if(!cfiquery(f) || f->alg != 1 && f->alg != 3){ + if(f->alg == 2){ + print("amd algorithm\n"); + goto ok; + } + /* apparently not CFI: try to reset to read mode before return */ + flashput(f, 0x55, ClearStatus); + flashput(f, 0x55, ReadArray); + return -1; + } +ok: + switch(f->width){ + case 1: + f->cmask = 0x00FF; + break; + case 2: + f->cmask = 0xFFFF; + break; + case 4: + f->cmask = 0xFFFFFFFF; + break; + } + flashput(f, 0x55, ClearStatus); + flashput(f, 0x55, ReadID); + f->id = cfiget1(f, 0x00); + f->devid = cfiget1(f, 0x01); + flashput(f, 0x55, ClearStatus); + flashput(f, 0x55, ReadArray); + f->erasezone = nil; + f->suspend = nil; + f->resume = nil; + return 0; +} + +void +flashcfi8link(void) +{ + addflashcard("cfi8", reset); +} diff --git a/os/port/flashif.h b/os/port/flashif.h new file mode 100644 index 00000000..cc013760 --- /dev/null +++ b/os/port/flashif.h @@ -0,0 +1,147 @@ +typedef struct Flash Flash; +typedef struct Flashchip Flashchip; +typedef struct Flashpart Flashpart; +typedef struct Flashregion Flashregion; + +/* + * logical partitions + */ +enum { + Maxflashpart = 8 +}; + +struct Flashpart { + char* name; + ulong start; + ulong end; +}; + +enum { + Maxflashregion = 4 +}; + +/* + * physical erase block regions + */ +struct Flashregion { + int n; /* number of blocks in region */ + ulong start; /* physical base address (allowing for banks) */ + ulong end; + ulong erasesize; + ulong pagesize; /* if non-zero, the size of pages within the erase block */ +}; + +/* + * one of a set of chips in a given region + */ +struct Flashchip { + int nr; + Flashregion regions[Maxflashregion]; + + uchar id; /* flash manufacturer ID */ + ushort devid; /* flash device ID */ + int width; /* bytes per flash line */ + int maxwb; /* max write buffer size */ + ulong devsize; /* physical device size */ + int alg; /* programming algorithm (if CFI) */ + int protect; /* software protection */ +}; + +/* + * structure defining a contiguous region of flash memory + */ +struct Flash { + QLock; /* interlock on flash operations */ + Flash* next; + + /* the following are filled in before calling Flash.reset */ + char* type; + void* addr; + ulong size; + int xip; /* executing in place: don't query */ + int (*reset)(Flash*); + + /* the following are filled in by the reset routine */ + int (*eraseall)(Flash*); + int (*erasezone)(Flash*, Flashregion*, ulong); + int (*read)(Flash*, ulong, void*, long); /* (optional) reads of correct width and alignment */ + int (*write)(Flash*, ulong, void*, long); /* writes of correct width and alignment */ + int (*suspend)(Flash*); + int (*resume)(Flash*); + int (*attach)(Flash*); + + /* the following might be filled in by either archflashreset or the reset routine */ + int nr; + Flashregion regions[Maxflashregion]; + + uchar id; /* flash manufacturer ID */ + ushort devid; /* flash device ID */ + int width; /* bytes per flash line */ + int interleave; /* addresses are interleaved across set of chips */ + int bshift; /* byte addresses are shifted */ + ulong cmask; /* command mask for interleaving */ + int maxwb; /* max write buffer size */ + ulong devsize; /* physical device size */ + int alg; /* programming algorithm (if CFI) */ + void* data; /* flash type routines' private storage, or nil */ + Flashpart part[Maxflashpart]; /* logical partitions */ + int protect; /* software protection */ + char* sort; /* "nand", "nor", "serial", nil (unspecified) */ +}; + +/* + * called by link routine of driver for specific flash type: arguments are + * conventional name for card type/model, and card driver's reset routine. + */ +void addflashcard(char*, int (*)(Flash*)); + +/* + * called by devflash.c:/^flashreset; if flash exists, + * sets type, address, and size in bytes of flash + * and returns 0; returns -1 if flash doesn't exist + */ +int archflashreset(int, Flash*); + +/* + * enable/disable write protect + */ +void archflashwp(Flash*, int); + +/* + * flash access taking width and interleave into account + */ +int flashget(Flash*, ulong); +void flashput(Flash*, ulong, int); + +/* + * Architecture specific routines for managing nand devices + */ + +/* + * do any device spcific initialisation + */ +void archnand_init(Flash*); + +/* + * if claim is 1, claim device exclusively, and enable it (power it up) + * if claim is 0, release, and disable it (power it down) + * claiming may be as simple as a qlock per device + */ +void archnand_claim(Flash*, int claim); + +/* + * set command latch enable (CLE) and address latch enable (ALE) + * appropriately + */ +void archnand_setCLEandALE(Flash*, int cle, int ale); + +/* + * write a sequence of bytes to the device + */ +void archnand_write(Flash*, void *buf, int len); + +/* + * read a sequence of bytes from the device + * if buf is 0, throw away the data + */ +void archnand_read(Flash*, void *buf, int len); diff --git a/os/port/flashintel b/os/port/flashintel new file mode 100644 index 00000000..f0093acf --- /dev/null +++ b/os/port/flashintel @@ -0,0 +1,179 @@ + +enum { + DQ7 = I(0x80), + DQ6 = I(0x40), + DQ5 = I(0x20), + DQ4 = I(0x10), + DQ3 = I(0x08), + DQ2 = I(0x04), + DQ1 = I(0x02), + DQ0 = I(0x01), +}; + +/* + * intel algorithm + */ + +enum { + ReadID = I(0x90), + ClearStatus = I(0x50), + ReadStatus = I(0x70), + Program = I(0x40), + BlockErase = I(0x20), + Confirm = I(0xD0), +}; + +#define DPRINT if(0)print +#define EPRINT if(1)print + +static char* +intelwait(Flash *f, void *p, ulong ticks) +{ + ulong csr, mask; + + ticks += m->ticks+1; + mask = f->cmask; + for(;;){ + if(f->width == 2) + csr = *(ushort*)p; + else + csr = *(ulong*)p; + if((csr & mask) == (DQ7 & mask)) + break; + sched(); + if(m->ticks >= ticks) + return "flash write timed out"; + } + if(csr & (DQ5|DQ4|DQ3|DQ1)){ + EPRINT("flash: error: %8.8lux %8.8lux\n", p, csr); + if(csr & DQ1) + return "flash block locked"; + if(csr & DQ3) + return "low flash programming voltage"; + return Eio; + } + return nil; +} + +static int +intelerase(Flash *f, Flashregion *r, ulong addr) +{ + int s; + char *e; + + DPRINT("flash: erase zone %8.8lux\n", addr); + if(addr & (r->erasesize-1)) + return -1; /* bad zone */ + if(f->width == 2){ + ushort *p = (ushort*)((ulong)f->addr + addr); + s = splhi(); + *p = BlockErase & f->cmask; + *p = Confirm & f->cmask; + splx(s); + e = intelwait(f, p, MS2TK(8*1000)); + *p = ClearStatus & f->cmask; + *p = ReadArray & f->cmask; + }else{ + ulong *p = (ulong*)((ulong)f->addr + addr); + s = splhi(); + *p = BlockErase & f->cmask; + *p = Confirm & f->cmask; + splx(s); + e = intelwait(f, p, MS2TK(8*1000)); + *p = ClearStatus & f->cmask; + *p = ReadArray & f->cmask; + } + if(e != nil) + error(e); + return 0; +} + +static int +intelwrite2(Flash *f, ulong offset, void *buf, long n) +{ + ushort *a, *v; + ulong w; + int s; + char *e; + + if(((ulong)f->addr|offset|n)&(f->width-1)) + return -1; + a = (ushort*)((ulong)f->addr + offset); + n /= f->width; + v = buf; + if(waserror()){ + *a = ClearStatus & f->cmask; + *a = ReadArray & f->cmask; + nexterror(); + } + for(; --n >= 0; v++, a++){ + w = *a; + DPRINT("flash: write %p %#ulx -> %#lux\n", a, w, (ulong)*v); + if(w == *v) + continue; /* already set */ + if(~w & *v) + error("flash not erased"); + s = splhi(); + *a = Program & f->cmask; /* program */ + *a = *v; + splx(s); + microdelay(8); + e = intelwait(f, a, 5); + *a = ClearStatus & f->cmask; + *a = ReadArray & f->cmask; + if(e != nil) + error(e); + w = *a; + if(w != *v){ + EPRINT("flash: write %p %#8.8lux -> %#8.8lux failed\n", a, w, (ulong)*v); + error(Eio); + } + } + poperror(); + return 0; +} + +static int +intelwrite4(Flash *f, ulong offset, void *buf, long n) +{ + ulong *a, *v; + ulong w; + int s; + char *e; + + if(((ulong)f->addr|offset|n)&(f->width-1)) + return -1; + a = (ulong*)((ulong)f->addr + offset); + n /= f->width; + v = buf; + if(waserror()){ + *a = ClearStatus & f->cmask; + *a = ReadArray & f->cmask; + nexterror(); + } + for(; --n >= 0; v++, a++){ + w = *a; + DPRINT("flash: write %p %#ulx -> %#lux\n", a, w, (ulong)*v); + if(w == *v) + continue; /* already set */ + if(~w & *v) + error("flash not erased"); + s = splhi(); + *a = Program; /* program */ + *a = *v; + splx(s); + microdelay(8); + e = intelwait(f, a, 5); + *a = ClearStatus & f->cmask; + *a = ReadArray & f->cmask; + if(e != nil) + error(e); + w = *a; + if(w != *v){ + EPRINT("flash: write %p %#8.8lux -> %#8.8lux failed\n", a, w, *v); + error(Eio); + } + } + poperror(); + return 0; +} diff --git a/os/port/flashnand.c b/os/port/flashnand.c new file mode 100644 index 00000000..e58c5a63 --- /dev/null +++ b/os/port/flashnand.c @@ -0,0 +1,337 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "flashif.h" + +typedef struct Nandtab Nandtab; + +struct Nandtab { + short manufacturer; + uchar id; + uchar l2bytesperpage; + ushort pagesperblock; + ushort blocks; + uchar tPROGms; + ushort tBERASEms; + uchar tRus; +}; + +static Nandtab nandtab[] = { + { 0xec, 0xe6, 9, 16, 1024, 1, 4, 7 }, /* Samsung KM29U64000T */ + + { 0x98, 0xe6, 9, 16, 1024, 1, 4, 25 }, /* Toshiba TC58V64AFT */ + { 0x98, 0x73, 9, 32, 1024, 1, 10, 25}, /* Toshiba TC56V128AFT */ + /* Generic entries which take timings from Toshiba SMIL example code */ + { -1, 0xea, 8, 16, 512, 20, 400, 100 }, + { -1, 0xe3, 9, 16, 512, 20, 400, 100 }, + { -1, 0xe5, 9, 16, 512, 20, 400, 100 }, + { -1, 0x73, 9, 32, 1024, 20, 400, 100 }, + { -1, 0x75, 9, 32, 2048, 20, 400, 100 }, + { -1, 0x76, 9, 32, 4096, 20, 400, 100 }, +}; + +enum { + ReadMode1 = 0x00, + ReadMode2 = 0x01, + Program = 0x10, + ReadMode3 = 0x50, + Erase1 = 0x60, + ReadStatus = 0x70, + Write = 0x80, + Identify = 0x90, + Erase2 = 0xd0, + + StatusReady = 0x40, + StatusFail = 0x01, +}; + +/* + * NAND flash driver + */ + +#define DPRINT if(0)print +#define EPRINT if(1)print + +static int idchip(Flash *f); + +static void +nand_writebyte(Flash *f, uchar b) +{ + archnand_write(f, &b, 1); +} + +static uchar +nand_readbyte(Flash *f) +{ + uchar b; + archnand_read(f, &b, 1); + return b; +} + +static int +idchip(Flash *f) +{ + int x; + uchar maker, device; + + f->id = 0; + f->devid = 0; + f->width = 1; + archnand_claim(f, 1); + archnand_setCLEandALE(f, 1, 0); + nand_writebyte(f, Identify); + archnand_setCLEandALE(f, 0, 1); + nand_writebyte(f, 0); + archnand_setCLEandALE(f, 0, 0); + maker = nand_readbyte(f); + device = nand_readbyte(f); + archnand_claim(f, 0); + iprint("man=%#ux device=%#ux\n", maker, device); + for(x = 0; x < sizeof(nandtab) / sizeof(nandtab[0]); x++){ + if(nandtab[x].id == (device & 0xff) + && (nandtab[x].manufacturer == maker || nandtab[x].manufacturer == -1)){ + ulong bpp; + f->id = maker; + f->devid = device; + f->nr = 1; + bpp = 1 << nandtab[x].l2bytesperpage; + bpp |= bpp >> 5; + f->regions[0].erasesize = bpp * nandtab[x].pagesperblock; + f->size = f->regions[0].erasesize * nandtab[x].blocks; + f->regions[0].n = nandtab[x].blocks; + f->regions[0].start = 0; + f->regions[0].end = f->size; + f->regions[0].pagesize = bpp; + f->data = &nandtab[x]; + return 0; + } + } + print("nand: device %#.2ux/%#.2ux not recognised\n", maker, device); + return -1; +} + +static int +erasezone(Flash *f, Flashregion *r, ulong byteaddr) +{ + Nandtab *nt = f->data; + int paddress; + uchar val; + int rv; + uchar addr[2]; + + if(byteaddr%r->erasesize || byteaddr >= f->size) + return -1; /* bad zone */ + paddress = byteaddr/r->erasesize * nt->pagesperblock; /* can simplify ... */ +//print("erasezone(%.8lux) page %d %.8lux\n", byteaddr, paddress, r->erasesize); + archnand_claim(f, 1); + archnand_setCLEandALE(f, 1, 0); // command mode + nand_writebyte(f, Erase1); + archnand_setCLEandALE(f, 0, 1); // address mode + addr[0] = paddress; + addr[1] = paddress >> 8; + archnand_write(f, addr, 2); + archnand_setCLEandALE(f, 1, 0); // command mode + nand_writebyte(f, Erase2); + nand_writebyte(f, ReadStatus); + archnand_setCLEandALE(f, 0, 0); // data mode + + do { + val = nand_readbyte(f); + } while((val & StatusReady) != StatusReady); + + if((val & StatusFail) != 0){ + print("erasezone failed: %.2ux\n", val); + rv = -1; + } + else + rv = 0; + archnand_claim(f, 0); // turn off chip + return rv; +} + +static int +writepage(Flash *f, ulong page, ushort addr, void *buf, long n) +{ + uchar cmd; + uchar val; + int rv; + uchar cmdbuf[3]; + +//print("writepage(%ld, %d, %ld)\n", page, addr, n); + // Fake a read to set the pointer + if(addr < 256) + cmd = ReadMode1; + else if(addr < 512){ + cmd = ReadMode2; + addr -= 256; + }else{ + cmd = ReadMode3; + addr -= 512; + } + archnand_claim(f, 1); + archnand_setCLEandALE(f, 1, 0); // command mode + nand_writebyte(f, cmd); + nand_writebyte(f, Write); + archnand_setCLEandALE(f, 0, 1); // address mode + cmdbuf[0] = addr; + cmdbuf[1] = page; + cmdbuf[2] = page >> 8; + archnand_write(f, cmdbuf, 3); + archnand_setCLEandALE(f, 0, 0); // data mode + archnand_write(f, buf, n); + archnand_setCLEandALE(f, 1, 0); // command mode + nand_writebyte(f, Program); + nand_writebyte(f, ReadStatus); + archnand_setCLEandALE(f, 0, 0); // data mode + + do { + val = nand_readbyte(f); + }while((val & StatusReady) != StatusReady); + + if((val & StatusFail) != 0){ + print("writepage failed: %.2ux\n", val); + rv = -1; + }else + rv = 0; + + archnand_claim(f, 0); + return rv; +} + +static int +write(Flash *f, ulong offset, void *buf, long n) +{ + Nandtab *nt = f->data; + ulong page; + ulong addr; + ulong xbpp; + +//print("write(%ld, %ld)\n", offset, n); + + xbpp = (1 << nt->l2bytesperpage); + xbpp |= (xbpp >> 5); + page = offset / xbpp; + addr = offset % xbpp; + + while(n > 0){ + int count; + count = xbpp - addr; + if(count > n) + count = n; + if(writepage(f, page, addr, buf, count) < 0) + return -1; + offset += count; + n -= count; + buf = (uchar *)buf + count; + addr = 0; + } +//print("write done\n"); + return 0; +} + +static int +read(Flash *f, ulong offset, void *buf, long n) +{ + Nandtab *nt = f->data; + uchar cmd; + ulong page; + ulong addr; + ushort bytesperpage, xbytesperpage, skip, partialaddr; + uchar cmdbuf[3]; + int toread; + +//print("read(%ld, %.8lux, %ld)\n", offset, buf, n); + + bytesperpage = (1 << nt->l2bytesperpage); + xbytesperpage = bytesperpage; + xbytesperpage += bytesperpage >> 5; // 512 => 16, 256 => 8 + page = offset / xbytesperpage; + partialaddr = offset % xbytesperpage; + skip = 0; + if(partialaddr >= bytesperpage && xbytesperpage - partialaddr < n){ + // cannot start read in extended area, and then chain into main area, + // so start on last byte of main area, and skip the extra bytes + // stupid chip design this one + skip = partialaddr - bytesperpage + 1; + n += skip; + partialaddr = bytesperpage - 1; + } + addr = partialaddr; + if(addr >= bytesperpage){ + cmd = ReadMode3; + addr -= bytesperpage; + }else if(addr >= 256){ + cmd = ReadMode2; + addr -= 256; + }else + cmd = ReadMode1; + +//print("cmd %.2x page %.4lux addr %.8lux partialaddr %d skip %d\n", cmd, page, addr, partialaddr, skip); + // Read first page + archnand_claim(f, 1); + archnand_setCLEandALE(f, 1, 0); + nand_writebyte(f, cmd); + archnand_setCLEandALE(f, 0, 1); + cmdbuf[0] = addr; + cmdbuf[1] = page; + cmdbuf[2] = page >> 8; + archnand_write(f, cmdbuf, 3); + archnand_setCLEandALE(f, 0, 0); + if(partialaddr){ + // partial first page + microdelay(nt->tRus); + toread = partialaddr < xbytesperpage ? xbytesperpage - partialaddr : 0; + if(toread > n) + toread = n; + if(skip){ + archnand_read(f, 0, skip); + toread -= skip; + n -= skip; +// partialaddr += skip; + } + archnand_read(f, buf, toread); + n -= toread; +// partialaddr += toread; + buf = (uchar *)buf + toread; + } + while(n){ + microdelay(nt->tRus); + toread = xbytesperpage; + if(n < toread) + toread = n; + archnand_read(f, buf, toread); + n -= toread; + buf = (uchar *)buf + toread; + } + archnand_claim(f, 0); +//print("readn done\n"); + return 0; +} + +static int +reset(Flash *f) +{ +//iprint("nandreset\n"); + if(f->data != nil) + return 1; + f->write = write; + f->read = read; + f->eraseall = nil; + f->erasezone = erasezone; + f->suspend = nil; + f->resume = nil; + f->sort = "nand"; + archnand_init(f); + return idchip(f); +} + +void +flashnandlink(void) +{ + addflashcard("nand", reset); +} diff --git a/os/port/fpi.c b/os/port/fpi.c new file mode 100644 index 00000000..fc6d4e80 --- /dev/null +++ b/os/port/fpi.c @@ -0,0 +1,304 @@ +/* + * Floating Point Interpreter. + * shamelessly stolen from an original by ark. + */ +#include "fpi.h" + +void +fpiround(Internal *i) +{ + unsigned long guard; + + guard = i->l & GuardMask; + i->l &= ~GuardMask; + if(guard > (LsBit>>1) || (guard == (LsBit>>1) && (i->l & LsBit))){ + i->l += LsBit; + if(i->l & CarryBit){ + i->l &= ~CarryBit; + i->h++; + if(i->h & CarryBit){ + if (i->h & 0x01) + i->l |= CarryBit; + i->l >>= 1; + i->h >>= 1; + i->e++; + } + } + } +} + +static void +matchexponents(Internal *x, Internal *y) +{ + int count; + + count = y->e - x->e; + x->e = y->e; + if(count >= 2*FractBits){ + x->l = x->l || x->h; + x->h = 0; + return; + } + if(count >= FractBits){ + count -= FractBits; + x->l = x->h|(x->l != 0); + x->h = 0; + } + while(count > 0){ + count--; + if(x->h & 0x01) + x->l |= CarryBit; + if(x->l & 0x01) + x->l |= 2; + x->l >>= 1; + x->h >>= 1; + } +} + +static void +shift(Internal *i) +{ + i->e--; + i->h <<= 1; + i->l <<= 1; + if(i->l & CarryBit){ + i->l &= ~CarryBit; + i->h |= 0x01; + } +} + +static void +normalise(Internal *i) +{ + while((i->h & HiddenBit) == 0) + shift(i); +} + +static void +renormalise(Internal *i) +{ + if(i->e < -2 * FractBits) + i->e = -2 * FractBits; + while(i->e < 1){ + i->e++; + if(i->h & 0x01) + i->l |= CarryBit; + i->h >>= 1; + i->l = (i->l>>1)|(i->l & 0x01); + } + if(i->e >= ExpInfinity) + SetInfinity(i); +} + +void +fpinormalise(Internal *x) +{ + if(!IsWeird(x) && !IsZero(x)) + normalise(x); +} + +void +fpiadd(Internal *x, Internal *y, Internal *i) +{ + Internal *t; + + i->s = x->s; + if(IsWeird(x) || IsWeird(y)){ + if(IsNaN(x) || IsNaN(y)) + SetQNaN(i); + else + SetInfinity(i); + return; + } + if(x->e > y->e){ + t = x; + x = y; + y = t; + } + matchexponents(x, y); + i->e = x->e; + i->h = x->h + y->h; + i->l = x->l + y->l; + if(i->l & CarryBit){ + i->h++; + i->l &= ~CarryBit; + } + if(i->h & (HiddenBit<<1)){ + if(i->h & 0x01) + i->l |= CarryBit; + i->l = (i->l>>1)|(i->l & 0x01); + i->h >>= 1; + i->e++; + } + if(IsWeird(i)) + SetInfinity(i); +} + +void +fpisub(Internal *x, Internal *y, Internal *i) +{ + Internal *t; + + if(y->e < x->e + || (y->e == x->e && (y->h < x->h || (y->h == x->h && y->l < x->l)))){ + t = x; + x = y; + y = t; + } + i->s = y->s; + if(IsNaN(y)){ + SetQNaN(i); + return; + } + if(IsInfinity(y)){ + if(IsInfinity(x)) + SetQNaN(i); + else + SetInfinity(i); + return; + } + matchexponents(x, y); + i->e = y->e; + i->h = y->h - x->h; + i->l = y->l - x->l; + if(i->l < 0){ + i->l += CarryBit; + i->h--; + } + if(i->h == 0 && i->l == 0) + SetZero(i); + else while(i->e > 1 && (i->h & HiddenBit) == 0) + shift(i); +} + +#define CHUNK (FractBits/2) +#define CMASK ((1<>CHUNK) & CMASK) +#define LO(x) ((short)(x) & CMASK) +#define SPILL(x) ((x)>>CHUNK) +#define M(x, y) ((long)a[x]*(long)b[y]) +#define C(h, l) (((long)((h) & CMASK)<s = x->s^y->s; + if(IsWeird(x) || IsWeird(y)){ + if(IsNaN(x) || IsNaN(y) || IsZero(x) || IsZero(y)) + SetQNaN(i); + else + SetInfinity(i); + return; + } + else if(IsZero(x) || IsZero(y)){ + SetZero(i); + return; + } + normalise(x); + normalise(y); + i->e = x->e + y->e - (ExpBias - 1); + + a[0] = HI(x->h); b[0] = HI(y->h); + a[1] = LO(x->h); b[1] = LO(y->h); + a[2] = HI(x->l); b[2] = HI(y->l); + a[3] = LO(x->l); b[3] = LO(y->l); + + c[6] = M(3, 3); + c[5] = M(2, 3) + M(3, 2) + SPILL(c[6]); + c[4] = M(1, 3) + M(2, 2) + M(3, 1) + SPILL(c[5]); + c[3] = M(0, 3) + M(1, 2) + M(2, 1) + M(3, 0) + SPILL(c[4]); + c[2] = M(0, 2) + M(1, 1) + M(2, 0) + SPILL(c[3]); + c[1] = M(0, 1) + M(1, 0) + SPILL(c[2]); + c[0] = M(0, 0) + SPILL(c[1]); + + f[0] = c[0]; + f[1] = C(c[1], c[2]); + f[2] = C(c[3], c[4]); + f[3] = C(c[5], c[6]); + + if((f[0] & HiddenBit) == 0){ + f[0] <<= 1; + f[1] <<= 1; + f[2] <<= 1; + f[3] <<= 1; + if(f[1] & CarryBit){ + f[0] |= 1; + f[1] &= ~CarryBit; + } + if(f[2] & CarryBit){ + f[1] |= 1; + f[2] &= ~CarryBit; + } + if(f[3] & CarryBit){ + f[2] |= 1; + f[3] &= ~CarryBit; + } + i->e--; + } + i->h = f[0]; + i->l = f[1]; + if(f[2] || f[3]) + i->l |= 1; + renormalise(i); +} + +void +fpidiv(Internal *x, Internal *y, Internal *i) +{ + i->s = x->s^y->s; + if(IsNaN(x) || IsNaN(y) + || (IsInfinity(x) && IsInfinity(y)) || (IsZero(x) && IsZero(y))){ + SetQNaN(i); + return; + } + else if(IsZero(x) || IsInfinity(y)){ + SetInfinity(i); + return; + } + else if(IsInfinity(x) || IsZero(y)){ + SetZero(i); + return; + } + normalise(x); + normalise(y); + i->h = 0; + i->l = 0; + i->e = y->e - x->e + (ExpBias + 2*FractBits - 1); + do{ + if(y->h > x->h || (y->h == x->h && y->l >= x->l)){ + i->l |= 0x01; + y->h -= x->h; + y->l -= x->l; + if(y->l < 0){ + y->l += CarryBit; + y->h--; + } + } + shift(y); + shift(i); + }while ((i->h & HiddenBit) == 0); +/* + if(y->h > x->h || (y->h == x->h && y->l >= x->l)) + i->l |= 0x01; +*/ + if(y->h || y->l) + i->l |= 0x01; + renormalise(i); +} + +int +fpicmp(Internal *x, Internal *y) +{ + if(IsNaN(x) && IsNaN(y)) + return 0; + if(IsInfinity(x) && IsInfinity(y)) + return y->s - x->s; + if(x->e == y->e && x->h == y->h && x->l == y->l) + return y->s - x->s; + if(x->e < y->e + || (x->e == y->e && (x->h < y->h || (x->h == y->h && x->l < y->l)))) + return y->s ? 1: -1; + return x->s ? -1: 1; +} diff --git a/os/port/fpimem.c b/os/port/fpimem.c new file mode 100644 index 00000000..4799d0a5 --- /dev/null +++ b/os/port/fpimem.c @@ -0,0 +1,136 @@ +#include "fpi.h" + +/* + * the following routines depend on memory format, not the machine + */ + +void +fpis2i(Internal *i, void *v) +{ + Single *s = v; + + i->s = (*s & 0x80000000) ? 1: 0; + if((*s & ~0x80000000) == 0){ + SetZero(i); + return; + } + i->e = ((*s>>23) & 0x00FF) - SingleExpBias + ExpBias; + i->h = (*s & 0x007FFFFF)<<(1+NGuardBits); + i->l = 0; + if(i->e) + i->h |= HiddenBit; + else + i->e++; +} + +void +fpid2i(Internal *i, void *v) +{ + Double *d = v; + + i->s = (d->h & 0x80000000) ? 1: 0; + i->e = (d->h>>20) & 0x07FF; + i->h = ((d->h & 0x000FFFFF)<<(4+NGuardBits))|((d->l>>25) & 0x7F); + i->l = (d->l & 0x01FFFFFF)<e) + i->h |= HiddenBit; + else + i->e++; +} + +void +fpiw2i(Internal *i, void *v) +{ + Word w, word = *(Word*)v; + int e; + + if(word < 0){ + i->s = 1; + word = -word; + } + else + i->s = 0; + if(word == 0){ + SetZero(i); + return; + } + if(word > 0){ + for (e = 0, w = word; w; w >>= 1, e++) + ; + } else + e = 32; + if(e > FractBits){ + i->h = word>>(e - FractBits); + i->l = (word & ((1<<(e - FractBits)) - 1))<<(2*FractBits - e); + } + else { + i->h = word<<(FractBits - e); + i->l = 0; + } + i->e = (e - 1) + ExpBias; +} + +void +fpii2s(void *v, Internal *i) +{ + int e; + Single *s = (Single*)v; + + fpiround(i); + if(i->h & HiddenBit) + i->h &= ~HiddenBit; + else + i->e--; + *s = i->s ? 0x80000000: 0; + e = i->e; + if(e < ExpBias){ + if(e <= (ExpBias - SingleExpBias)) + return; + e = SingleExpBias - (ExpBias - e); + } + else if(e >= (ExpBias + (SingleExpMax-SingleExpBias))){ + *s |= SingleExpMax<<23; + return; + } + else + e = SingleExpBias + (e - ExpBias); + *s |= (e<<23)|(i->h>>(1+NGuardBits)); +} + +void +fpii2d(void *v, Internal *i) +{ + Double *d = (Double*)v; + + fpiround(i); + if(i->h & HiddenBit) + i->h &= ~HiddenBit; + else + i->e--; + i->l = ((i->h & GuardMask)<<25)|(i->l>>NGuardBits); + i->h >>= NGuardBits; + d->h = i->s ? 0x80000000: 0; + d->h |= (i->e<<20)|((i->h & 0x00FFFFFF)>>4); + d->l = (i->h<<28)|i->l; +} + +void +fpii2w(Word *word, Internal *i) +{ + Word w; + int e; + + fpiround(i); + e = (i->e - ExpBias) + 1; + if(e <= 0) + w = 0; + else if(e > 31) + w = 0x7FFFFFFF; + else if(e > FractBits) + w = (i->h<<(e - FractBits))|(i->l>>(2*FractBits - e)); + else + w = i->h>>(FractBits-e); + if(i->s) + w = -w; + *word = w; +} diff --git a/os/port/inferno.c b/os/port/inferno.c new file mode 100644 index 00000000..de9ea600 --- /dev/null +++ b/os/port/inferno.c @@ -0,0 +1,1030 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "isa.h" +#include "interp.h" +#include "runt.h" +#include "kernel.h" + +/* + * here because Sys_FileIO is not public + */ +extern int srvf2c(char*, char*, Sys_FileIO*); + +/* + * System types connected to gc + */ +uchar FDmap[] = Sys_FD_map; +uchar FileIOmap[] = Sys_FileIO_map; +void freeFD(Heap*, int); +void freeFileIO(Heap*, int); +Type* TFD; +Type* TFileIO; + +static uchar rmap[] = Sys_FileIO_read_map; +static uchar wmap[] = Sys_FileIO_write_map; +static Type* FioTread; +static Type* FioTwrite; +static uchar dmap[] = Sys_Dir_map; +static Type* Tdir; + +typedef struct FD FD; +struct FD +{ + Sys_FD fd; + Fgrp* grp; +}; + +void +sysinit(void) +{ + TFD = dtype(freeFD, sizeof(FD), FDmap, sizeof(FDmap)); + TFileIO = dtype(freeFileIO, Sys_FileIO_size, FileIOmap, sizeof(FileIOmap)); + + /* Support for devsrv.c */ + FioTread = dtype(freeheap, Sys_FileIO_read_size, rmap, sizeof(rmap)); + FioTwrite = dtype(freeheap, Sys_FileIO_write_size, wmap, sizeof(wmap)); + + /* Support for dirread */ + Tdir = dtype(freeheap, Sys_Dir_size, dmap, sizeof(dmap)); +} + +void +freeFD(Heap *h, int swept) +{ + FD *handle; + + USED(swept); + + handle = H2D(FD*, h); + + release(); + kfgrpclose(handle->grp, handle->fd.fd); + closefgrp(handle->grp); + acquire(); +} + +void +freeFileIO(Heap *h, int swept) +{ + Sys_FileIO *fio; + + if(swept) + return; + + fio = H2D(Sys_FileIO*, h); + destroy(fio->read); + destroy(fio->write); +} + +Sys_FD* +mkfd(int fd) +{ + Heap *h; + Fgrp *fg; + FD *handle; + + h = heap(TFD); + handle = H2D(FD*, h); + handle->fd.fd = fd; + fg = up->env->fgrp; + handle->grp = fg; + incref(fg); + return (Sys_FD*)handle; +} +#define fdchk(x) ((x) == (Sys_FD*)H ? -1 : (x)->fd) + +void +seterror(char *err, ...) +{ + char *estr; + va_list arg; + + estr = up->env->errstr; + va_start(arg, err); + vseprint(estr, estr+ERRMAX, err, arg); + va_end(arg); +} + +char* +syserr(char *s, char *es, Prog *p) +{ + Osenv *o; + + o = p->osenv; + kstrcpy(s, o->errstr, es - s); + return s + strlen(s); +} + +void +Sys_millisec(void *fp) +{ + F_Sys_millisec *f; + + f = fp; + *f->ret = TK2MS(MACHP(0)->ticks); +} + +void +Sys_open(void *fp) +{ + int fd; + F_Sys_open *f; + + f = fp; + destroy(*f->ret); + *f->ret = H; + release(); + fd = kopen(string2c(f->s), f->mode); + acquire(); + if(fd == -1) + return; + + *f->ret = mkfd(fd); +} + +void +Sys_pipe(void *fp) +{ + Array *a; + int fd[2]; + Sys_FD **sfd; + F_Sys_pipe *f; + + f = fp; + *f->ret = -1; + + a = f->fds; + if(a->len < 2) + return; + if(kpipe(fd) < 0) + return; + + sfd = (Sys_FD**)a->data; + destroy(sfd[0]); + destroy(sfd[1]); + sfd[0] = H; + sfd[1] = H; + sfd[0] = mkfd(fd[0]); + sfd[1] = mkfd(fd[1]); + *f->ret = 0; +} + +void +Sys_fildes(void *fp) +{ + F_Sys_fildes *f; + int fd; + + f = fp; + destroy(*f->ret); + *f->ret = H; + release(); + fd = kdup(f->fd, -1); + acquire(); + if(fd == -1) + return; + *f->ret = mkfd(fd); +} + +void +Sys_dup(void *fp) +{ + F_Sys_dup *f; + + f = fp; + release(); + *f->ret = kdup(f->old, f->new); + acquire(); +} + +void +Sys_create(void *fp) +{ + int fd; + F_Sys_create *f; + + f = fp; + destroy(*f->ret); + *f->ret = H; + release(); + fd = kcreate(string2c(f->s), f->mode, f->perm); + acquire(); + if(fd == -1) + return; + + *f->ret = mkfd(fd); +} + +void +Sys_remove(void *fp) +{ + F_Sys_remove *f; + + f = fp; + release(); + *f->ret = kremove(string2c(f->s)); + acquire(); +} + +void +Sys_seek(void *fp) +{ + F_Sys_seek *f; + + f = fp; + release(); + *f->ret = kseek(fdchk(f->fd), f->off, f->start); + acquire(); +} + +void +Sys_unmount(void *fp) +{ + F_Sys_unmount *f; + + f = fp; + release(); + *f->ret = kunmount(string2c(f->s1), string2c(f->s2)); + acquire(); +} + +void +Sys_read(void *fp) +{ + int n; + F_Sys_read *f; + + f = fp; + n = f->n; + if(f->buf == (Array*)H || n < 0) { + *f->ret = 0; + return; + } + if(n > f->buf->len) + n = f->buf->len; + + release(); + *f->ret = kread(fdchk(f->fd), f->buf->data, n); + acquire(); +} + +void +Sys_pread(void *fp) +{ + int n; + F_Sys_pread *f; + + f = fp; + n = f->n; + if(f->buf == (Array*)H || n < 0) { + *f->ret = 0; + return; + } + if(n > f->buf->len) + n = f->buf->len; + + release(); + *f->ret = kpread(fdchk(f->fd), f->buf->data, n, f->off); + acquire(); +} + +void +Sys_chdir(void *fp) +{ + F_Sys_chdir *f; + + f = fp; + release(); + *f->ret = kchdir(string2c(f->path)); + acquire(); +} + +void +Sys_write(void *fp) +{ + int n; + F_Sys_write *f; + + f = fp; + n = f->n; + if(f->buf == (Array*)H || n < 0) { + *f->ret = 0; + return; + } + if(n > f->buf->len) + n = f->buf->len; + + release(); + *f->ret = kwrite(fdchk(f->fd), f->buf->data, n); + acquire(); +} + +void +Sys_pwrite(void *fp) +{ + int n; + F_Sys_pwrite *f; + + f = fp; + n = f->n; + if(f->buf == (Array*)H || n < 0) { + *f->ret = 0; + return; + } + if(n > f->buf->len) + n = f->buf->len; + + release(); + *f->ret = kpwrite(fdchk(f->fd), f->buf->data, n, f->off); + acquire(); +} + +static void +unpackdir(Dir *d, Sys_Dir *sd) +{ + retstr(d->name, &sd->name); + retstr(d->uid, &sd->uid); + retstr(d->gid, &sd->gid); + retstr(d->muid, &sd->muid); + sd->qid.path = d->qid.path; + sd->qid.vers = d->qid.vers; + sd->qid.qtype = d->qid.type; + sd->mode = d->mode; + sd->atime = d->atime; + sd->mtime = d->mtime; + sd->length = d->length; + sd->dtype = d->type; + sd->dev = d->dev; +} + +static Dir* +packdir(Sys_Dir *sd) +{ + char *nm[4], *p; + int i, n; + Dir *d; + + nm[0] = string2c(sd->name); + nm[1] = string2c(sd->uid); + nm[2] = string2c(sd->gid); + nm[3] = string2c(sd->muid); + n = 0; + for(i=0; i<4; i++) + n += strlen(nm[i])+1; + d = smalloc(sizeof(*d)+n); + p = (char*)d+sizeof(*d); + for(i=0; i<4; i++){ + n = strlen(nm[i])+1; + memmove(p, nm[i], n); + p += n; + } + d->name = nm[0]; + d->uid = nm[1]; + d->gid = nm[2]; + d->muid = nm[3]; + d->qid.path = sd->qid.path; + d->qid.vers = sd->qid.vers; + d->qid.type = sd->qid.qtype; + d->mode = sd->mode; + d->atime = sd->atime; + d->mtime = sd->mtime; + d->length = sd->length; + d->type = sd->dtype; + d->dev = sd->dev; + return d; +} + +void +Sys_fstat(void *fp) +{ + Dir *d; + F_Sys_fstat *f; + + f = fp; + f->ret->t0 = -1; + release(); + d = kdirfstat(fdchk(f->fd)); + acquire(); + if(d == nil) + return; + if(waserror() == 0){ + unpackdir(d, &f->ret->t1); + f->ret->t0 = 0; + poperror(); + } + free(d); +} + +void +Sys_stat(void *fp) +{ + Dir *d; + F_Sys_stat *f; + + f = fp; + f->ret->t0 = -1; + release(); + d = kdirstat(string2c(f->s)); + acquire(); + if(d == nil) + return; + if(waserror() == 0){ + unpackdir(d, &f->ret->t1); + f->ret->t0 = 0; + poperror(); + } + free(d); +} + +void +Sys_fd2path(void *fp) +{ + F_Sys_fd2path *f; + char *s; + void *r; + + f = fp; + r = *f->ret; + *f->ret = H; + destroy(r); + release(); + s = kfd2path(fdchk(f->fd)); + acquire(); + if(waserror() == 0){ + retstr(s, f->ret); + poperror(); + } + free(s); +} + +void +Sys_mount(void *fp) +{ + F_Sys_mount *f; + + f = fp; + release(); + *f->ret = kmount(fdchk(f->fd), fdchk(f->afd), string2c(f->on), f->flags, string2c(f->spec)); + acquire(); +} + +void +Sys_bind(void *fp) +{ + F_Sys_bind *f; + + f = fp; + release(); + *f->ret = kbind(string2c(f->s), string2c(f->on), f->flags); + acquire(); +} + +void +Sys_wstat(void *fp) +{ + Dir *d; + F_Sys_wstat *f; + + f = fp; + d = packdir(&f->d); + release(); + *f->ret = kdirwstat(string2c(f->s), d); + acquire(); + free(d); +} + +void +Sys_fwstat(void *fp) +{ + Dir *d; + F_Sys_fwstat *f; + + f = fp; + d = packdir(&f->d); + release(); + *f->ret = kdirfwstat(fdchk(f->fd), d); + acquire(); + free(d); +} + +void +Sys_print(void *fp) +{ + int n; + Prog *p; + Chan *c; + char buf[1024], *b = buf; + F_Sys_print *f; + f = fp; + c = up->env->fgrp->fd[1]; + if(c == nil) + return; + p = currun(); + + release(); + n = xprint(p, f, &f->vargs, f->s, buf, sizeof(buf)); + if (n >= sizeof(buf)-UTFmax-2) + n = bigxprint(p, f, &f->vargs, f->s, &b, sizeof(buf)); + *f->ret = kwrite(1, b, n); + if (b != buf) + free(b); + acquire(); +} + +void +Sys_fprint(void *fp) +{ + int n; + Prog *p; + char buf[1024], *b = buf; + F_Sys_fprint *f; + + f = fp; + p = currun(); + release(); + n = xprint(p, f, &f->vargs, f->s, buf, sizeof(buf)); + if (n >= sizeof(buf)-UTFmax-2) + n = bigxprint(p, f, &f->vargs, f->s, &b, sizeof(buf)); + *f->ret = kwrite(fdchk(f->fd), b, n); + if (b != buf) + free(b); + acquire(); +} + +void +Sys_werrstr(void *fp) +{ + F_Sys_werrstr *f; + + f = fp; + *f->ret = 0; + kstrcpy(up->env->errstr, string2c(f->s), ERRMAX); +} + +void +Sys_dial(void *fp) +{ + int cfd; + char dir[NETPATHLEN], *a, *l; + F_Sys_dial *f; + + f = fp; + a = string2c(f->addr); + l = string2c(f->local); + release(); + f->ret->t0 = kdial(a, l, dir, &cfd); + acquire(); + destroy(f->ret->t1.dfd); + f->ret->t1.dfd = H; + destroy(f->ret->t1.cfd); + f->ret->t1.cfd = H; + if(f->ret->t0 == -1) + return; + + f->ret->t1.dfd = mkfd(f->ret->t0); + f->ret->t1.cfd = mkfd(cfd); + retstr(dir, &f->ret->t1.dir); +} + +void +Sys_announce(void *fp) +{ + char dir[NETPATHLEN], *a; + F_Sys_announce *f; + + f = fp; + a = string2c(f->addr); + release(); + f->ret->t0 = kannounce(a, dir); + acquire(); + destroy(f->ret->t1.dfd); + f->ret->t1.dfd = H; + destroy(f->ret->t1.cfd); + f->ret->t1.cfd = H; + if(f->ret->t0 == -1) + return; + + f->ret->t1.cfd = mkfd(f->ret->t0); + retstr(dir, &f->ret->t1.dir); +} + +void +Sys_listen(void *fp) +{ + F_Sys_listen *f; + char dir[NETPATHLEN], *d; + + f = fp; + d = string2c(f->c.dir); + release(); + f->ret->t0 = klisten(d, dir); + acquire(); + + destroy(f->ret->t1.dfd); + f->ret->t1.dfd = H; + destroy(f->ret->t1.cfd); + f->ret->t1.cfd = H; + if(f->ret->t0 == -1) + return; + + f->ret->t1.cfd = mkfd(f->ret->t0); + retstr(dir, &f->ret->t1.dir); +} + +void +Sys_sleep(void *fp) +{ + F_Sys_sleep *f; + + f = fp; + release(); + if(f->period > 0){ + if(waserror()){ + acquire(); + error(""); + } + tsleep(&up->sleep, return0, 0, f->period); + poperror(); + } + acquire(); + *f->ret = 0; +} + +void +Sys_stream(void *fp) +{ + Prog *p; + uchar *buf; + int src, dst; + F_Sys_stream *f; + int nbytes, t, n; + + f = fp; + buf = malloc(f->bufsiz); + if(buf == nil) { + kwerrstr(Enomem); + *f->ret = -1; + return; + } + + src = fdchk(f->src); + dst = fdchk(f->dst); + + p = currun(); + + release(); + t = 0; + nbytes = 0; + while(p->kill == nil) { + n = kread(src, buf+t, f->bufsiz-t); + if(n <= 0) + break; + t += n; + if(t >= f->bufsiz) { + if(kwrite(dst, buf, t) != t) { + t = 0; + break; + } + + nbytes += t; + t = 0; + } + } + if(t != 0) { + kwrite(dst, buf, t); + nbytes += t; + } + acquire(); + free(buf); + *f->ret = nbytes; +} + +void +Sys_export(void *fp) +{ + F_Sys_export *f; + + f = fp; + release(); + *f->ret = export(fdchk(f->c), string2c(f->dir), f->flag&Sys_EXPASYNC); + acquire(); +} + +void +Sys_file2chan(void *fp) +{ + int r; + Heap *h; + Channel *c; + Sys_FileIO *fio; + F_Sys_file2chan *f; + void *sv; + + h = heap(TFileIO); + + fio = H2D(Sys_FileIO*, h); + + c = cnewc(FioTread, movtmp, 16); + fio->read = c; + + c = cnewc(FioTwrite, movtmp, 16); + fio->write = c; + + f = fp; + sv = *f->ret; + *f->ret = fio; + destroy(sv); + + release(); + r = srvf2c(string2c(f->dir), string2c(f->file), fio); + acquire(); + if(r == -1) { + *f->ret = H; + destroy(fio); + } +} + +enum +{ + /* the following pctl calls can block and must release the virtual machine */ + BlockingPctl= Sys_NEWFD|Sys_FORKFD|Sys_NEWNS|Sys_FORKNS|Sys_NEWENV|Sys_FORKENV +}; + +void +Sys_pctl(void *fp) +{ + int fd; + Prog *p; + List *l; + Chan *c; + volatile struct {Pgrp *np;} np; + Pgrp *opg; + Chan *dot; + Osenv *o; + F_Sys_pctl *f; + Fgrp *fg, *ofg, *nfg; + volatile struct {Egrp *ne;} ne; + Egrp *oe; + + f = fp; + + p = currun(); + if(f->flags & BlockingPctl) + release(); + + np.np = nil; + ne.ne = nil; + if(waserror()) { + closepgrp(np.np); + closeegrp(ne.ne); + if(f->flags & BlockingPctl) + acquire(); + *f->ret = -1; + return; + } + + o = p->osenv; + if(f->flags & Sys_NEWFD) { + ofg = o->fgrp; + nfg = newfgrp(ofg); + lock(ofg); + /* file descriptors to preserve */ + for(l = f->movefd; l != H; l = l->tail) { + fd = *(int*)l->data; + if(fd >= 0 && fd <= ofg->maxfd) { + c = ofg->fd[fd]; + if(c != nil && fd < nfg->nfd && nfg->fd[fd] == nil) { + incref(c); + nfg->fd[fd] = c; + if(nfg->maxfd < fd) + nfg->maxfd = fd; + } + } + } + unlock(ofg); + o->fgrp = nfg; + closefgrp(ofg); + } + else + if(f->flags & Sys_FORKFD) { + ofg = o->fgrp; + fg = dupfgrp(ofg); + /* file descriptors to close */ + for(l = f->movefd; l != H; l = l->tail) + kclose(*(int*)l->data); + o->fgrp = fg; + closefgrp(ofg); + } + + if(f->flags & Sys_NEWNS) { + np.np = newpgrp(); + dot = o->pgrp->dot; + np.np->dot = cclone(dot); + np.np->slash = cclone(dot); + cnameclose(np.np->slash->name); + np.np->slash->name = newcname("/"); + np.np->pin = o->pgrp->pin; /* pin is ALWAYS inherited */ + np.np->nodevs = o->pgrp->nodevs; + opg = o->pgrp; + o->pgrp = np.np; + np.np = nil; + closepgrp(opg); + } + else + if(f->flags & Sys_FORKNS) { + np.np = newpgrp(); + pgrpcpy(np.np, o->pgrp); + opg = o->pgrp; + o->pgrp = np.np; + np.np = nil; + closepgrp(opg); + } + + if(f->flags & Sys_NEWENV) { + oe = o->egrp; + o->egrp = newegrp(); + closeegrp(oe); + } + else + if(f->flags & Sys_FORKENV) { + ne.ne = newegrp(); + egrpcpy(ne.ne, o->egrp); + oe = o->egrp; + o->egrp = ne.ne; + ne.ne = nil; + closeegrp(oe); + } + + if(f->flags & Sys_NEWPGRP) + newgrp(p); + + if(f->flags & Sys_NODEVS) + o->pgrp->nodevs = 1; + + poperror(); + + if(f->flags & BlockingPctl) + acquire(); + + *f->ret = p->pid; +} + +void +Sys_dirread(void *fp) +{ + + Dir *b; + int i, n; + Heap *h; + uchar *d; + void *r; + F_Sys_dirread *f; + + f = fp; + f->ret->t0 = -1; + r = f->ret->t1; + f->ret->t1 = H; + destroy(r); + release(); + n = kdirread(fdchk(f->fd), &b); + acquire(); + if(n <= 0) { + f->ret->t0 = n; + free(b); + return; + } + if(waserror()){ + free(b); + return; + } + h = heaparray(Tdir, n); + poperror(); + d = H2D(Array*, h)->data; + for(i = 0; i < n; i++) { + unpackdir(b+i, (Sys_Dir*)d); + d += Sys_Dir_size; + } + f->ret->t0 = n; + f->ret->t1 = H2D(Array*, h); + free(b); +} + +void +Sys_fauth(void *fp) +{ + + int fd; + F_Sys_fauth *f; + void *r; + + f = fp; + r = *f->ret; + *f->ret = H; + destroy(r); + release(); + fd = kfauth(fdchk(f->fd), string2c(f->aname)); + acquire(); + if(fd >= 0) + *f->ret = mkfd(fd); +} + +void +Sys_fversion(void *fp) +{ + void *r; + F_Sys_fversion *f; + int n; + char buf[20], *s; + + f = fp; + f->ret->t0 = -1; + r = f->ret->t1; + f->ret->t1 = H; + destroy(r); + s = string2c(f->version); + n = strlen(s); + if(n >= sizeof(buf)-1) + n = sizeof(buf)-1; + memmove(buf, s, n); + buf[n] = 0; + release(); + n = kfversion(fdchk(f->fd), f->msize, buf, sizeof(buf)); + acquire(); + if(n >= 0){ + f->ret->t0 = f->msize; + retnstr(buf, n, &f->ret->t1); + } +} + +void +Sys_iounit(void *fp) +{ + F_Sys_iounit *f; + + f = fp; + release(); + *f->ret = kiounit(fdchk(f->fd)); + acquire(); +} + +void +ccom(Progq **cl, Prog *p) +{ + volatile struct {Progq **cl;} vcl; + + cqadd(cl, p); + vcl.cl = cl; + if(waserror()) { + if(p->ptr != nil) { /* no killcomm */ + cqdelp(vcl.cl, p); + p->ptr = nil; + } + nexterror(); + } + cblock(p); + poperror(); +} + +void +crecv(Channel *c, void *ip) +{ + Prog *p; + REG rsav; + + if(c->send->prog == nil && c->size == 0) { + p = currun(); + p->ptr = ip; + ccom(&c->recv, p); + return; + } + + rsav = R; + R.s = &c; + R.d = ip; + irecv(); + R = rsav; +} + +void +csend(Channel *c, void *ip) +{ + Prog *p; + REG rsav; + + if(c->recv->prog == nil && (c->buf == H || c->size == c->buf->len)) { + p = currun(); + p->ptr = ip; + ccom(&c->send, p); + return; + } + + rsav = R; + R.s = ip; + R.d = &c; + isend(); + R = rsav; +} diff --git a/os/port/latin1.c b/os/port/latin1.c new file mode 100644 index 00000000..8dcb0d7e --- /dev/null +++ b/os/port/latin1.c @@ -0,0 +1,76 @@ +#include + +/* + * The code makes two assumptions: strlen(ld) is 1 or 2; latintab[i].ld can be a + * prefix of latintab[j].ld only when j=5) + return unicode(k); + else + return -5; + for(l=latintab; l->ld!=0; l++) + if(k[0] == l->ld[0]){ + if(n == 1) + return -2; + if(l->ld[1] == 0) + c = k[1]; + else if(l->ld[1] != k[1]) + continue; + else if(n == 2) + return -3; + else + c = k[2]; + for(p=l->si; *p!=0; p++) + if(*p == c) + return l->so[p - l->si]; + return -1; + } + return -1; +} diff --git a/os/port/latin1.h b/os/port/latin1.h new file mode 100644 index 00000000..382df2bf --- /dev/null +++ b/os/port/latin1.h @@ -0,0 +1,100 @@ + " ", " i", L"␣ı", + "!~", "-=~", L"≄≇≉", + "!", "!<=>?bmp", L"¡≮≠≯‽⊄∉⊅", + "\"*", "IUiu", L"ΪΫϊϋ", + "\"", "\"AEIOUYaeiouy", L"¨ÄËÏÖÜŸäëïöüÿ", + "$*", "fhk", L"ϕϑϰ", + "$", "BEFHILMRVaefglopv", L"ℬℰℱℋℐℒℳℛƲɑℯƒℊℓℴ℘ʋ", + "\'\"", "Uu", L"Ǘǘ", + "\'", "\'ACEILNORSUYZacegilnorsuyz", L"´ÁĆÉÍĹŃÓŔŚÚÝŹáćéģíĺńóŕśúýź", + "*", "*ABCDEFGHIKLMNOPQRSTUWXYZabcdefghiklmnopqrstuwxyz", L"∗ΑΒΞΔΕΦΓΘΙΚΛΜΝΟΠΨΡΣΤΥΩΧΗΖαβξδεφγθικλμνοπψρστυωχηζ", + "+", "-O", L"±⊕", + ",", ",ACEGIKLNORSTUacegiklnorstu", L"¸ĄÇĘĢĮĶĻŅǪŖŞŢŲąçęģįķļņǫŗşţų", + "-*", "l", L"ƛ", + "-", "+-2:>DGHILOTZbdghiltuz~", L"∓­ƻ÷→ÐǤĦƗŁ⊖ŦƵƀðǥℏɨłŧʉƶ≂", + ".", ".CEGILOZceglz", L"·ĊĖĠİĿ⊙Żċėġŀż", + "/", "Oo", L"Øø", + "1", "234568", L"½⅓¼⅕⅙⅛", + "2", "-35", L"ƻ⅔⅖", + "3", "458", L"¾⅗⅜", + "4", "5", L"⅘", + "5", "68", L"⅚⅝", + "7", "8", L"⅞", + ":", "()-=", L"☹☺÷≔", + "~", L"←«≤≶≲", + "=", ":<=>OV", L"≕⋜≡⋝⊜⇒", + ">!", "=~", L"≩⋧", + ">", "<=>~", L"≷≥»≳", + "?", "!?", L"‽¿", + "@\'", "\'", L"ъ", + "@@", "\'EKSTYZekstyz", L"ьЕКСТЫЗекстыз", + "@C", "Hh", L"ЧЧ", + "@E", "Hh", L"ЭЭ", + "@K", "Hh", L"ХХ", + "@S", "CHch", L"ЩШЩШ", + "@T", "Ss", L"ЦЦ", + "@Y", "AEOUaeou", L"ЯЕЁЮЯЕЁЮ", + "@Z", "Hh", L"ЖЖ", + "@c", "h", L"ч", + "@e", "h", L"э", + "@k", "h", L"х", + "@s", "ch", L"щш", + "@t", "s", L"ц", + "@y", "aeou", L"яеёю", + "@z", "h", L"ж", + "@", "ABDFGIJLMNOPRUVXabdfgijlmnopruvx", L"АБДФГИЙЛМНОПРУВХабдфгийлмнопрувх", + "A", "E", L"Æ", + "C", "ACU", L"⋂ℂ⋃", + "Dv", "Zz", L"DŽDž", + "D", "-e", L"Ð∆", + "G", "-", L"Ǥ", + "H", "-H", L"Ħℍ", + "I", "-J", L"ƗIJ", + "L", "&-Jj|", L"⋀ŁLJLj⋁", + "M", "#48bs", L"♮♩♪♭♯", + "N", "JNj", L"NJℕNj", + "O", "*+-./=EIcoprx", L"⊛⊕⊖⊙⊘⊜ŒƢ©⊚℗®⊗", + "P", "P", L"ℙ", + "Q", "Q", L"ℚ", + "R", "R", L"ℝ", + "S", "123S", L"¹²³§", + "T", "-u", L"Ŧ⊨", + "V", "=", L"⇐", + "Y", "R", L"Ʀ", + "Z", "-ACSZ", L"Ƶℤ", + "^", "ACEGHIJOSUWYaceghijosuwy", L"ÂĈÊĜĤÎĴÔŜÛŴŶâĉêĝĥîĵôŝûŵŷ", + "_\"", "AUau", L"ǞǕǟǖ", + "_,", "Oo", L"Ǭǭ", + "_.", "Aa", L"Ǡǡ", + "_", "AEIOU_aeiou", L"ĀĒĪŌŪ¯āēīōū", + "`\"", "Uu", L"Ǜǜ", + "`", "AEIOUaeiou", L"ÀÈÌÒÙàèìòù", + "a", "ben", L"↔æ∠", + "b", "()+-0123456789=bknpqru", L"₍₎₊₋₀₁₂₃₄₅₆₇₈₉₌♝♚♞♟♛♜•", + "c", "$Oagu", L"¢©∩≅∪", + "dv", "z", L"dž", + "d", "-adegz", L"ð↓‡°†ʣ", + "e", "$lmns", L"€⋯—–∅", + "f", "a", L"∀", + "g", "$-r", L"¤ǥ∇", + "h", "-v", L"ℏƕ", + "i", "-bfjps", L"ɨ⊆∞ij⊇∫", + "l", "\"$&\'-jz|", L"“£∧‘łlj⋄∨", + "m", "iou", L"µ∈×", + "n", "jo", L"nj¬", + "o", "AOUaeiu", L"Å⊚Ůåœƣů", + "p", "Odgrt", L"℗∂¶∏∝", + "r", "\"\'O", L"”’®", + "s", "()+-0123456789=abnoprstu", L"⁽⁾⁺⁻⁰ⁱ⁲⁳⁴⁵⁶⁷⁸⁹⁼ª⊂ⁿº⊃√ß∍∑", + "t", "-efmsu", L"ŧ∃∴™ς⊢", + "u", "-AEGIOUaegiou", L"ʉĂĔĞĬŎŬ↑ĕğĭŏŭ", + "v\"", "Uu", L"Ǚǚ", + "v", "ACDEGIKLNORSTUZacdegijklnorstuz", L"ǍČĎĚǦǏǨĽŇǑŘŠŤǓŽǎčďěǧǐǰǩľňǒřšťǔž", + "w", "bknpqr", L"♗♔♘♙♕♖", + "x", "O", L"⊗", + "y", "$", L"¥", + "z", "-", L"ƶ", + "|", "Pp|", L"Þþ¦", + "~!", "=", L"≆", + "~", "-=AINOUainou~", L"≃≅ÃĨÑÕŨãĩñõũ≈", diff --git a/os/port/lib.h b/os/port/lib.h new file mode 100644 index 00000000..0233dc72 --- /dev/null +++ b/os/port/lib.h @@ -0,0 +1,211 @@ + +/* + * functions (possibly) linked in, complete, from libc. + */ + +/* + * mem routines + */ +extern void* memccpy(void*, void*, int, ulong); +extern void* memset(void*, int, ulong); +extern int memcmp(void*, void*, ulong); +extern void* memmove(void*, void*, ulong); +extern void* memchr(void*, int, ulong); + +/* + * string routines + */ +extern char* strcat(char*, char*); +extern char* strchr(char*, int); +extern char* strrchr(char*, int); +extern int strcmp(char*, char*); +extern char* strcpy(char*, char*); +extern char* strecpy(char*, char*, char*); +extern char* strncat(char*, char*, long); +extern char* strncpy(char*, char*, long); +extern int strncmp(char*, char*, long); +extern long strlen(char*); +extern char* strdup(char*); +extern char* strstr(char*, char*); +extern int cistrncmp(char*, char*, int); +extern int cistrcmp(char*, char*); +extern char* cistrstr(char*, char*); +extern int atoi(char*); +extern int fullrune(char*, int); + +enum +{ + UTFmax = 3, /* maximum bytes per rune */ + Runesync = 0x80, /* cannot represent part of a UTF sequence */ + Runeself = 0x80, /* rune and UTF sequences are the same (<) */ + Runeerror = 0x80, /* decoding error in UTF */ +}; + +/* + * rune routines + */ +extern int runetochar(char*, Rune*); +extern int chartorune(Rune*, char*); +extern char* utfrune(char*, long); +extern int utflen(char*); +extern int runelen(long); + +extern int abs(int); + +/* + * print routines + */ +typedef struct Fmt Fmt; +typedef int (*Fmts)(Fmt*); +struct Fmt{ + uchar runes; /* output buffer is runes or chars? */ + void *start; /* of buffer */ + void *to; /* current place in the buffer */ + void *stop; /* end of the buffer; overwritten if flush fails */ + int (*flush)(Fmt *); /* called when to == stop */ + void *farg; /* to make flush a closure */ + int nfmt; /* num chars formatted so far */ + va_list args; /* args passed to dofmt */ + int r; /* % format Rune */ + int width; + int prec; + ulong flags; +}; +extern int print(char*, ...); +extern char* seprint(char*, char*, char*, ...); +extern char* vseprint(char*, char*, char*, va_list); +extern char* smprint(char*, ...); +extern char* vsmprint(char*, va_list); +extern int snprint(char*, int, char*, ...); +extern int vsnprint(char*, int, char*, va_list); +extern int sprint(char*, char*, ...); + +extern int fmtinstall(int, int (*)(Fmt*)); +extern void quotefmtinstall(void); +extern int fmtprint(Fmt*, char*, ...); +extern int fmtstrcpy(Fmt*, char*); + +#pragma varargck argpos fmtprint 2 +#pragma varargck argpos print 1 +#pragma varargck argpos seprint 3 +#pragma varargck argpos snprint 3 +#pragma varargck argpos sprint 2 +#pragma varargck argpos vseprint 3 +#pragma varargck argpos vsnprint 3 + +extern int fltconv(Fmt*); + +/* + * math + */ +extern int isNaN(double); +extern int isInf(double, int); +extern double floor(double); +extern double frexp(double, int*); +extern double pow10(int); + +/* + * one-of-a-kind + */ +extern char* cleanname(char*); +extern ulong getcallerpc(void*); + +extern long strtol(char*, char**, int); +extern ulong strtoul(char*, char**, int); +extern vlong strtoll(char*, char**, int); +extern uvlong strtoull(char*, char**, int); +extern char etext[]; +extern char edata[]; +extern char end[]; +extern int getfields(char*, char**, int, int, char*); +extern int tokenize(char*, char**, int); +extern int dec64(uchar*, int, char*, int); + +extern int toupper(int); +extern char* netmkaddr(char*, char*, char*); +extern int myetheraddr(uchar*, char*); +extern int parseether(uchar*, char*); + +/* + * network dialling + */ +#define NETPATHLEN 40 + +/* + * Syscall data structures + */ +#define MORDER 0x0003 /* mask for bits defining order of mounting */ +#define MREPL 0x0000 /* mount replaces object */ +#define MBEFORE 0x0001 /* mount goes before others in union directory */ +#define MAFTER 0x0002 /* mount goes after others in union directory */ +#define MCREATE 0x0004 /* permit creation in mounted directory */ +#define MCACHE 0x0010 /* cache some data */ +#define MMASK 0x0017 /* all bits on */ + +#define OREAD 0 /* open for read */ +#define OWRITE 1 /* write */ +#define ORDWR 2 /* read and write */ +#define OEXEC 3 /* execute, == read but check execute permission */ +#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ +#define OCEXEC 32 /* or'ed in, close on exec */ +#define ORCLOSE 64 /* or'ed in, remove on close */ +#define OEXCL 0x1000 /* or'ed in, exclusive create */ + +#define NCONT 0 /* continue after note */ +#define NDFLT 1 /* terminate after note */ +#define NSAVE 2 /* clear note but hold state */ +#define NRSTR 3 /* restore saved state */ + +typedef struct Qid Qid; +typedef struct Dir Dir; +typedef struct Waitmsg Waitmsg; + +#define ERRMAX 128 /* max length of error string */ +#define KNAMELEN 28 /* max length of name held in kernel */ + +/* bits in Qid.type */ +#define QTDIR 0x80 /* type bit for directories */ +#define QTAPPEND 0x40 /* type bit for append only files */ +#define QTEXCL 0x20 /* type bit for exclusive use files */ +#define QTMOUNT 0x10 /* type bit for mounted channel */ +#define QTAUTH 0x08 /* type bit for authentication file */ +#define QTFILE 0x00 /* plain file */ + +/* bits in Dir.mode */ +#define DMDIR 0x80000000 /* mode bit for directories */ +#define DMAPPEND 0x40000000 /* mode bit for append only files */ +#define DMEXCL 0x20000000 /* mode bit for exclusive use files */ +#define DMMOUNT 0x10000000 /* mode bit for mounted channel */ +#define DMREAD 0x4 /* mode bit for read permission */ +#define DMWRITE 0x2 /* mode bit for write permission */ +#define DMEXEC 0x1 /* mode bit for execute permission */ + +struct Qid +{ + uvlong path; + ulong vers; + uchar type; +}; + +struct Dir { + /* system-modified data */ + ushort type; /* server type */ + uint dev; /* server subtype */ + /* file data */ + Qid qid; /* unique id from server */ + ulong mode; /* permissions */ + ulong atime; /* last read time */ + ulong mtime; /* last write time */ + vlong length; /* file length: see */ + char *name; /* last element of path */ + char *uid; /* owner name */ + char *gid; /* group name */ + char *muid; /* last modifier name */ +}; + +struct Waitmsg +{ + int pid; /* of loved one */ + ulong time[3]; /* of loved one and descendants */ + char msg[ERRMAX]; /* actually variable-size in user mode */ +}; diff --git a/os/port/log.c b/os/port/log.c new file mode 100644 index 00000000..14708233 --- /dev/null +++ b/os/port/log.c @@ -0,0 +1,186 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +static char Ebadlogctl[] = "unknown log ctl message"; + +void +logopen(Log *alog) +{ + lock(alog); + if(waserror()){ + unlock(alog); + nexterror(); + } + if(alog->opens == 0){ + if(alog->nlog == 0) + alog->nlog = 4*1024; + if(alog->minread == 0) + alog->minread = 1; + if(alog->buf == nil) + alog->buf = malloc(alog->nlog); + alog->rptr = alog->buf; + alog->end = alog->buf + alog->nlog; + alog->len = 0; + } + alog->opens++; + unlock(alog); + poperror(); +} + +void +logclose(Log *alog) +{ + lock(alog); + alog->opens--; + if(alog->opens == 0){ + free(alog->buf); + alog->buf = nil; + } + unlock(alog); +} + +static int +logready(void *a) +{ + Log *alog = a; + + return alog->len >= alog->minread; +} + +long +logread(Log *alog, void *a, ulong, long n) +{ + int i, d; + char *p, *rptr; + + qlock(&alog->readq); + if(waserror()){ + qunlock(&alog->readq); + nexterror(); + } + + for(;;){ + lock(alog); + if(alog->len >= alog->minread || alog->len >= n){ + if(n > alog->len) + n = alog->len; + d = 0; + rptr = alog->rptr; + alog->rptr += n; + if(alog->rptr >= alog->end){ + d = alog->rptr - alog->end; + alog->rptr = alog->buf + d; + } + alog->len -= n; + unlock(alog); + + i = n-d; + p = a; + memmove(p, rptr, i); + memmove(p+i, alog->buf, d); + break; + } + else + unlock(alog); + + sleep(&alog->readr, logready, alog); + } + + qunlock(&alog->readq); + poperror(); + + return n; +} + +char* +logctl(Log *alog, int argc, char *argv[], Logflag *flags) +{ + int i, set; + Logflag *fp; + + if(argc < 2) + return Ebadlogctl; + + if(strcmp("set", argv[0]) == 0) + set = 1; + else if(strcmp("clear", argv[0]) == 0) + set = 0; + else + return Ebadlogctl; + + for(i = 1; i < argc; i++){ + for(fp = flags; fp->name; fp++) + if(strcmp(fp->name, argv[i]) == 0) + break; + if(fp->name == nil) + continue; + if(set) + alog->logmask |= fp->mask; + else + alog->logmask &= ~fp->mask; + } + + return nil; +} + +void +logn(Log *alog, int mask, void *buf, int n) +{ + char *fp, *t; + int dowake, i; + + if(!(alog->logmask & mask)) + return; + + if(alog->opens == 0) + return; + + if(n > alog->nlog) + return; + + lock(alog); + i = alog->len + n - alog->nlog; + if(i > 0){ + alog->len -= i; + alog->rptr += i; + if(alog->rptr >= alog->end) + alog->rptr = alog->buf + (alog->rptr - alog->end); + } + t = alog->rptr + alog->len; + fp = buf; + alog->len += n; + while(n-- > 0){ + if(t >= alog->end) + t = alog->buf + (t - alog->end); + *t++ = *fp++; + } + dowake = alog->len >= alog->minread; + unlock(alog); + + if(dowake) + wakeup(&alog->readr); +} + +void +logb(Log *alog, int mask, char *fmt, ...) +{ + int n; + va_list arg; + char buf[128]; + + if(!(alog->logmask & mask)) + return; + + if(alog->opens == 0) + return; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + + logn(alog, mask, buf, n); +} diff --git a/os/port/master b/os/port/master new file mode 100644 index 00000000..fe8ffa00 --- /dev/null +++ b/os/port/master @@ -0,0 +1,89 @@ +/ root +A audio +A beep +B boot +B bridge +D ssl +E mpeg +F flash +F tinyfs +G fpga +G gpio +H ata +I ip +J i2c +J isdn +K kprof +L ds1620 +L lm78 +L lpt +M mnt +O omap +O sm # altivec soft modem +P prof +P tad +P telephony +Q ssfdc +R cdrom +R msg +S scsi +T kprof # old letter +T pri +T touch +U usb +V tv +V voice +W flash +X XXX +X ftl +Y pbus +Z doc +Z sadb +Z test +Z zt5512 +a atm +a beep +b dbg +c cons +d dup +e env +f floppy +i draw +k kprof +k fs +l ether +l lance +m midi +m mouse +m pointer +p prog +r rtc +s srv +t duart +t ns16552 +t uart +t z8530 +v vid +v vsc +w sd +x bench +y i82365 +y pcmcia +y sapcm +¤ cap +Û isp1161 +Û usbh +Σ sign +τ tk +↓ power + +α local use +β local use +γ local use +δ local use +ε local use +ζ local use +η local use +θ local use +ι local use +κ local use diff --git a/os/port/master.local b/os/port/master.local new file mode 100644 index 00000000..5d9438d3 --- /dev/null +++ b/os/port/master.local @@ -0,0 +1,10 @@ +α local use +β local use +γ local use +δ local use +ε local use +ζ local use +η local use +θ local use +ι local use +κ local use diff --git a/os/port/mkdevc b/os/port/mkdevc new file mode 100644 index 00000000..afb7caa7 --- /dev/null +++ b/os/port/mkdevc @@ -0,0 +1,216 @@ +$AWK -v 'objtype='$OBJTYPE ' +BEGIN{ + if(ARGC < 2) + exit +} + +/^$/{ + next; +} +/^#/{ + next; +} +collect && /^[^ \t]/{ + collect = 0; +} +collect && section ~ "dev"{ + dev[ndev++] = $1; + if($1 ~ "vga") + devvga = 1; +} +collect && section ~ "ip"{ + ip[nip++] = $1; +} +collect && (section ~ "ether" || section ~ "link") { + link[nlink++] = $1; +} +collect && section ~ "mod"{ + mod[nmod++] = $1; +} +collect && section ~ "vga"{ + option = 0; + for(i = 2; i < NF; i++){ + if($i ~ "[+]hwgc"){ + hwgc[nhwgc++] = $1; + option = 1; + } else if($i ~ "[+=]hwgc"){ + hwgc[nhwgc++] = $1; + if(option == 0) + option = 2; + } + } + if(option < 2) + vga[nvga++] = $1; +} +collect && section ~ "misc"{ + misc[nmisc++] = $1; + if($1 ~ "^arch.*") + arch[narch++] = $1; + else if($1 ~ "^sd.*") + sdifc[nsdifc++] = $1; + else if($1 ~ "^uart.*") + physuart[nphysuart++] = substr($1, 5, length($1)-4) "physuart"; + else if($1 ~ "^vga.*"){ + if(NF == 1) + vgadev[nvgadev++] = $1; + else for(i = 2; i <= NF; i++){ + if($i ~ "[+]cur") + vgadev[nvgadev++] = $1; + if($i ~ "[+=]cur") + vgacur[nvgacur++] = $1; + } + } + else if($1 ~ ".*\.root"){ + x = substr($1, 1, index($1, ".")-1); + if(x ~ "(dossrv|kfs)") + x = "fs"; + fs[nfs++] = x; + } +} +collect && section ~ "port"{ + port[nport++] = $0; +} +collect && section ~ "code"{ + code[ncode++] = $0; +} +$0 ~ /^[^ \t]/{ + if($0 ~ "(code|dev|ether|ip|lib|link|mod|misc|port|root|vga)"){ + section = $0; + collect = 1; + } + next; +} + +END{ + if(ARGC < 2) + exit "usage" + + printf "#include \"u.h\"\n" + printf "#include \"../port/lib.h\"\n" + printf "#include \"mem.h\"\n" + printf "#include \"dat.h\"\n" + printf "#include \"fns.h\"\n" + printf "#include \"io.h\"\n" + if(nphysuart) + printf "#include \"../port/uart.h\"\n" + printf "#include \"../port/error.h\"\n" + printf "#include \"interp.h\"\n\n" + printf "#include \"%s.root.h\"\n\n", ARGV[1]; + + printf "ulong ndevs = %d;\n", ndev+8 + for(i = 0; i < ndev; i++) + printf "extern Dev %sdevtab;\n", dev[i]; + printf "Dev* devtab[%d]={\n", ndev+8 + for(i = 0; i < ndev; i++) + printf "\t&%sdevtab,\n", dev[i]; + printf "\tnil,\n};\n\n"; + + + for(i = 0; i < nfs; i++){ + printf "extern uchar %scode[];\n", fs[i]; + printf "extern ulong %slen;\n", fs[i]; + } + for(i = 0; i < nlink; i++) + printf "extern void %slink(void);\n", link[i]; + + printf "void links(void){\n"; + for(i = 0; i < nfs; i++) + printf "\taddrootfile(\"%s\", %scode, %slen);\n", fs[i], fs[i], fs[i]; + for(i = 0; i < nlink; i++) + printf "\t%slink();\n", link[i]; + printf "}\n\n"; + + for(i = 0; i < nmod; i++) + printf "extern void %smodinit(void);\n", mod[i]; + printf "void modinit(void){\n"; + for(i = 0; i < nmod; i++) + printf("\t%smodinit();\n",mod[i]); + printf("}\n\n"); + + if(narch || objtype ~ "386"){ + for(i = 0; i < narch; i++) + printf "extern PCArch %s;\n", arch[i]; + printf "PCArch* knownarch[] = {\n"; + for(i = 0; i < narch; i++) + printf "\t&%s,\n", arch[i]; + printf "\tnil,\n};\n\n"; + } + + if(nsdifc){ + printf "#include \"../port/sd.h\"\n"; + for(i = 0; i < nsdifc; i++) + printf "extern SDifc %sifc;\n", sdifc[i]; + printf "SDifc* sdifc[] = {\n"; + for(i = 0; i < nsdifc; i++) + printf "\t&%sifc,\n", sdifc[i]; + printf "\tnil,\n};\n\n"; + } + + if(nphysuart){ + for(i = 0; i < nphysuart; i++) + printf "extern PhysUart %s;\n", physuart[i]; + printf "PhysUart* physuart[] = {\n"; + for(i = 0; i < nphysuart; i++) + printf "\t&%s,\n", physuart[i]; + printf "\tnil,\n};\n\n"; + } + + if(devvga || nvga || nvgadev){ + printf "#include \n" + printf "#include \n" + + if(nvga){ + printf "#include \"vga.h\"\n" + for(i = 0; i < nvga; i++) + printf "extern Vgac %s;\n", vga[i]; + printf "Vgac* knownvga[] = {\n"; + for(i = 0; i < nvga; i++) + printf "\t&%s,\n", vga[i]; + printf "\tnil,\n};\n\n"; + + if(nhwgc){ + for(i = 0; i < nhwgc; i++) + printf "extern Hwgc %shwgc;\n", hwgc[i]; + printf "Hwgc* knownhwgc[] = {\n"; + for(i = 0; i < nhwgc; i++) + printf "\t&%shwgc,\n", hwgc[i]; + printf "\tnil,\n};\n\n"; + } + } + + if(nvgadev){ + printf "#include \"screen.h\"\n"; + for(i = 0; i < nvgadev; i++) + printf "extern VGAdev %sdev;\n", vgadev[i]; + printf "VGAdev* vgadev[] = {\n"; + for(i = 0; i < nvgadev; i++) + printf "\t&%sdev,\n", vgadev[i]; + printf "\tnil,\n};\n\n"; + + for(i = 0; i < nvgacur; i++) + printf "extern VGAcur %scur;\n", vgacur[i]; + printf "VGAcur* vgacur[] = {\n"; + for(i = 0; i < nvgacur; i++) + printf "\t&%scur,\n", vgacur[i]; + printf "\tnil,\n};\n\n"; + } + } + + if(nip){ + printf "#include \"../ip/ip.h\"\n"; + for(i = 0; i < nip; i++) + printf "extern void %sinit(Fs*);\n", ip[i]; + printf "void (*ipprotoinit[])(Fs*) = {\n"; + for(i = 0; i < nip; i++) + printf "\t%sinit,\n", ip[i]; + printf "\tnil,\n};\n\n"; + } + + for(i = 0; i < ncode; i++) + printf "%s\n", code[i]; + + printf "char* conffile = \"%s\";\n", ARGV[1]; + printf "ulong kerndate = KERNDATE;\n"; + + exit +}' $* diff --git a/os/port/mkdevlist b/os/port/mkdevlist new file mode 100644 index 00000000..6fdc6866 --- /dev/null +++ b/os/port/mkdevlist @@ -0,0 +1,86 @@ +$AWK ' +BEGIN{ + var["dev"] = "DEVS="; + var["vga"] = "VGAS="; + var["ether"] = "ETHERS="; + var["init"] = "INIT="; + var["ip"] = "IP="; + var["port"] = "PORT="; + var["misc"] = "MISC="; + var["lib"] = "LIBS="; + var["link"] = "LINKS="; + var["root"] = "ROOTFILES="; + infernoroot = ENVIRON["ROOT"]; +} +/^$/{ next; +} +/^#/{ next; +} +/^env/{ + inenv = 1; + next; +} +inenv != 0 && /^[ ]/{ + sub("^[ ]*", "", $0) + printf "%s\n", $0 + next +} +/^(code|dev|ether|init|ip|lib|link|mod|misc|port|root|vga)/{ + inenv = 0; + if(current != "") + print current; + current = var[$1]; + type = $1; + next; +} +/^[^ ]/ { + inenv = 0; + if(current != "") + print current; + current = ""; +} +current && /^[ ]/{ + if(type == "dev") + file = "dev" $1; + else if(type == "root"){ + if (NF > 1) + file = $2; + else if ($1 == "/osinit.dis") + next; # handled via explicit dependency + else + file = $1; + if(have[file] == 0){ + current = current " " infernoroot file; + have[file]++; + } + next; + } + else + file = $1; + if(type == "init" || type == "lib") + current = current " " file; + else if(have[file] == 0){ + if(type == "lib") + current = current " " file; + else + current = current " " file "'.$O'"; + have[file]++; + } + for(i = 2; i <= NF; i++){ + if($i !~ "^[+=-].*"){ + if(have[$i] == 0){ + others[++nothers] = $i; + have[$i]++; + } + } + } + next; +} +END{ + if(current != "") + print current; + for(i = 1; i <= nothers; i++) + x = x " " others[i] "'.$O' "; + if(x) + printf("OTHERS=%s\n", x); +}' $* diff --git a/os/port/mkroot b/os/port/mkroot new file mode 100644 index 00000000..9bcf3598 --- /dev/null +++ b/os/port/mkroot @@ -0,0 +1,119 @@ +$AWK ' +BEGIN{ + if (ARGC < 2) + exit "usage"; + + conf = ARGV[1]; + infernoroot = ENVIRON["ROOT"]; + init = ENVIRON["INIT"]; + data2s = ENVIRON["DATA2S"]; + nroot = 0; +} +/^$/{ + next; +} +/^#/{ + next; +} +collect && /^[^ \t]/{ + collect = 0; +} +collect && section ~ "root"{ + dst[nroot] = $1; + if (NF > 1) + src[nroot] = infernoroot $2; + else if (dst[nroot] == "/osinit.dis") + src[nroot] = infernoroot "/os/init/" init ".dis"; + else + src[nroot] = infernoroot $1; + for(i=0; irootdata; + close(rootdata); + isdir[0] = 1; + dotdot[0] = 0; + qid = 1; + for (i = 0; i < nroot; i++) { + ncomp = split(dst[i], comp, "/"); + if (comp[1] != "" || ncomp < 2) + continue; + q = 0; + for (j = 2; j <= ncomp; j++) { + key = q "/" comp[j]; + if (walk[key] == 0) { + walk[key] = qid; + dotdot[qid] = q; + q = qid++; + name[q] = comp[j]; + if (j < ncomp) + isdir[q] = 1; + } + else + q = walk[key]; + } + if (system("test -d " src[i]) == 0) + isdir[q] = 1; + else { + if (system(data2s " root" q " <" src[i] " >>" rootdata) != 0) + exit 1; + print("extern unsigned char root" q "code[];"); + print("extern int root" q "len;"); + } + } + + x = 1; + sort[0] = 0; + unsort[0] = 0; + for (q = 0; q < qid; q++) { + if (isdir[q]) { + nchild[q] = 0; + for (q2 = 1; q2 < qid; q2++) { + if (dotdot[q2] == q) { + if (nchild[q]++ == 0) + child0[q] = x; + sort[q2] = x++; + unsort[sort[q2]] = q2; + } + } + } + } + + print("int rootmaxq = " qid ";"); + + print("Dirtab roottab[" qid "] = {"); + for (oq = 0; oq < qid; oq++) { + q = unsort[oq]; + if (!isdir[q]) + print("\t\"" name[q] "\",\t{" oq ", 0, QTFILE},\t", "0,\t0444,"); + else + print("\t\"" name[q] "\",\t{" oq ", 0, QTDIR},\t", "0,\t0555,"); + } + print("};"); + + print("Rootdata rootdata[" qid "] = {"); + for (oq = 0; oq < qid; oq++) { + q = unsort[oq]; + if (!isdir[q]) + print("\t" sort[dotdot[q]] ",\t", "root" q "code,\t", "0,\t", "&root" q "len,"); + else if (nchild[q]) + print("\t" sort[dotdot[q]] ",\t", "&roottab[" child0[q] "],\t", nchild[q] ",\tnil,"); + else + print("\t" sort[dotdot[q]] ",\t", "nil,\t", "0,\t", "nil,"); + } + print("};"); +} +' $1 >$1.root.h diff --git a/os/port/mul64fract.c b/os/port/mul64fract.c new file mode 100644 index 00000000..ca627073 --- /dev/null +++ b/os/port/mul64fract.c @@ -0,0 +1,39 @@ +#include + +/* mul64fract(uvlong*r, uvlong a, uvlong b) + * + * Multiply two 64 numbers and return the middle 64 bits of the 128 bit result. + * + * The assumption is that one of the numbers is a + * fixed point number with the integer portion in the + * high word and the fraction in the low word. + * + * There should be an assembler version of this routine + * for each architecture. This one is intended to + * make ports easier. + * + * ignored r0 = lo(a0*b0) + * lsw of result r1 = hi(a0*b0) +lo(a0*b1) +lo(a1*b0) + * msw of result r2 = hi(a0*b1) +hi(a1*b0) +lo(a1*b1) + * ignored r3 = hi(a1*b1) + */ + +void +mul64fract(uvlong *r, uvlong a, uvlong b) +{ + uvlong bh, bl; + uvlong ah, al; + uvlong res; + + bl = b & 0xffffffffULL; + bh = b >> 32; + al = a & 0xffffffffULL; + ah = a >> 32; + + res = (al*bl)>>32; + res += (al*bh); + res += (ah*bl); + res += (ah*bh)<<32; + + *r = res; +} diff --git a/os/port/netaux.c b/os/port/netaux.c new file mode 100644 index 00000000..0c5d6a55 --- /dev/null +++ b/os/port/netaux.c @@ -0,0 +1,67 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "../port/netif.h" + + +void +hnputv(void *p, vlong v) +{ + uchar *a; + + a = p; + hnputl(a, v>>32); + hnputl(a+4, v); +} + +void +hnputl(void *p, ulong v) +{ + uchar *a; + + a = p; + a[0] = v>>24; + a[1] = v>>16; + a[2] = v>>8; + a[3] = v; +} + +void +hnputs(void *p, ushort v) +{ + uchar *a; + + a = p; + a[0] = v>>8; + a[1] = v; +} + +vlong +nhgetv(void *p) +{ + uchar *a; + + a = p; + return ((vlong)nhgetl(a) << 32) | nhgetl(a+4); +} + +ulong +nhgetl(void *p) +{ + uchar *a; + + a = p; + return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0); +} + +ushort +nhgets(void *p) +{ + uchar *a; + + a = p; + return (a[0]<<8)|(a[1]<<0); +} diff --git a/os/port/netif.c b/os/port/netif.c new file mode 100644 index 00000000..60567f5e --- /dev/null +++ b/os/port/netif.c @@ -0,0 +1,671 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "../port/netif.h" + +static int netown(Netfile*, char*, int); +static int openfile(Netif*, int); +static char* matchtoken(char*, char*); +static char* netmulti(Netif*, Netfile*, uchar*, int); +static int parseaddr(uchar*, char*, int); + +/* + * set up a new network interface + */ +void +netifinit(Netif *nif, char *name, int nfile, ulong limit) +{ + strncpy(nif->name, name, KNAMELEN-1); + nif->name[KNAMELEN-1] = 0; + nif->nfile = nfile; + nif->f = malloc(nfile*sizeof(Netfile*)); + if(nif->f) + memset(nif->f, 0, nfile*sizeof(Netfile*)); + else + nif->nfile = 0; + nif->limit = limit; +} + +/* + * generate a 3 level directory + */ +static int +netifgen(Chan *c, char*, Dirtab *vp, int, int i, Dir *dp) +{ + Qid q; + Netif *nif = (Netif*)vp; + Netfile *f; + int t; + int perm; + char *o; + + q.type = QTFILE; + q.vers = 0; + + /* top level directory contains the name of the network */ + if(c->qid.path == 0){ + switch(i){ + case DEVDOTDOT: + q.path = 0; + q.type = QTDIR; + devdir(c, q, ".", 0, eve, 0555, dp); + break; + case 0: + q.path = N2ndqid; + q.type = QTDIR; + strcpy(up->genbuf, nif->name); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + break; + default: + return -1; + } + return 1; + } + + /* second level contains clone plus all the conversations */ + t = NETTYPE(c->qid.path); + if(t == N2ndqid || t == Ncloneqid || t == Naddrqid){ + switch(i) { + case DEVDOTDOT: + q.type = QTDIR; + q.path = 0; + devdir(c, q, ".", 0, eve, DMDIR|0555, dp); + break; + case 0: + q.path = Ncloneqid; + devdir(c, q, "clone", 0, eve, 0666, dp); + break; + case 1: + q.path = Naddrqid; + devdir(c, q, "addr", 0, eve, 0666, dp); + break; + case 2: + q.path = Nstatqid; + devdir(c, q, "stats", 0, eve, 0444, dp); + break; + case 3: + q.path = Nifstatqid; + devdir(c, q, "ifstats", 0, eve, 0444, dp); + break; + default: + i -= 4; + if(i >= nif->nfile) + return -1; + if(nif->f[i] == 0) + return 0; + q.type = QTDIR; + q.path = NETQID(i, N3rdqid); + sprint(up->genbuf, "%d", i); + devdir(c, q, up->genbuf, 0, eve, DMDIR|0555, dp); + break; + } + return 1; + } + + /* third level */ + f = nif->f[NETID(c->qid.path)]; + if(f == 0) + return 0; + if(*f->owner){ + o = f->owner; + perm = f->mode; + } else { + o = eve; + perm = 0666; + } + switch(i){ + case DEVDOTDOT: + q.type = QTDIR; + q.path = N2ndqid; + strcpy(up->genbuf, nif->name); + devdir(c, q, up->genbuf, 0, eve, DMDIR|0555, dp); + break; + case 0: + q.path = NETQID(NETID(c->qid.path), Ndataqid); + devdir(c, q, "data", 0, o, perm, dp); + break; + case 1: + q.path = NETQID(NETID(c->qid.path), Nctlqid); + devdir(c, q, "ctl", 0, o, perm, dp); + break; + case 2: + q.path = NETQID(NETID(c->qid.path), Nstatqid); + devdir(c, q, "stats", 0, eve, 0444, dp); + break; + case 3: + q.path = NETQID(NETID(c->qid.path), Ntypeqid); + devdir(c, q, "type", 0, eve, 0444, dp); + break; + case 4: + q.path = NETQID(NETID(c->qid.path), Nifstatqid); + devdir(c, q, "ifstats", 0, eve, 0444, dp); + break; + default: + return -1; + } + return 1; +} + +Walkqid* +netifwalk(Netif *nif, Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, (Dirtab *)nif, 0, netifgen); +} + +Chan* +netifopen(Netif *nif, Chan *c, int omode) +{ + int id; + Netfile *f; + + id = 0; + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Eperm); + } else { + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + case Nctlqid: + id = NETID(c->qid.path); + openfile(nif, id); + break; + case Ncloneqid: + id = openfile(nif, -1); + c->qid.path = NETQID(id, Nctlqid); + break; + default: + if(omode != OREAD) + error(Ebadarg); + } + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + case Nctlqid: + f = nif->f[id]; + if(netown(f, up->env->user, omode&7) < 0) + error(Eperm); + break; + } + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + c->iounit = qiomaxatomic; + return c; +} + +long +netifread(Netif *nif, Chan *c, void *a, long n, ulong offset) +{ + int i, j; + Netfile *f; + char *p; + + if(c->qid.type&QTDIR) + return devdirread(c, a, n, (Dirtab*)nif, 0, netifgen); + + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + f = nif->f[NETID(c->qid.path)]; + return qread(f->in, a, n); + case Nctlqid: + return readnum(offset, a, n, NETID(c->qid.path), NUMSIZE); + case Nstatqid: + p = malloc(READSTR); + if(p == nil) + return 0; + j = snprint(p, READSTR, "in: %d\n", nif->inpackets); + j += snprint(p+j, READSTR-j, "link: %d\n", nif->link); + j += snprint(p+j, READSTR-j, "out: %d\n", nif->outpackets); + j += snprint(p+j, READSTR-j, "crc errs: %d\n", nif->crcs); + j += snprint(p+j, READSTR-j, "overflows: %d\n", nif->overflows); + j += snprint(p+j, READSTR-j, "soft overflows: %d\n", nif->soverflows); + j += snprint(p+j, READSTR-j, "framing errs: %d\n", nif->frames); + j += snprint(p+j, READSTR-j, "buffer errs: %d\n", nif->buffs); + j += snprint(p+j, READSTR-j, "output errs: %d\n", nif->oerrs); + j += snprint(p+j, READSTR-j, "prom: %d\n", nif->prom); + j += snprint(p+j, READSTR-j, "mbps: %d\n", nif->mbps); + j += snprint(p+j, READSTR-j, "addr: "); + for(i = 0; i < nif->alen; i++) + j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]); + snprint(p+j, READSTR-j, "\n"); + n = readstr(offset, a, n, p); + free(p); + return n; + case Naddrqid: + p = malloc(READSTR); + if(p == nil) + return 0; + j = 0; + for(i = 0; i < nif->alen; i++) + j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]); + n = readstr(offset, a, n, p); + free(p); + return n; + case Ntypeqid: + f = nif->f[NETID(c->qid.path)]; + return readnum(offset, a, n, f->type, NUMSIZE); + case Nifstatqid: + return 0; + } + error(Ebadarg); + return -1; /* not reached */ +} + +Block* +netifbread(Netif *nif, Chan *c, long n, ulong offset) +{ + if((c->qid.type & QTDIR) || NETTYPE(c->qid.path) != Ndataqid) + return devbread(c, n, offset); + + return qbread(nif->f[NETID(c->qid.path)]->in, n); +} + +/* + * make sure this type isn't already in use on this device + */ +static int +typeinuse(Netif *nif, int type) +{ + Netfile *f, **fp, **efp; + + if(type <= 0) + return 0; + + efp = &nif->f[nif->nfile]; + for(fp = nif->f; fp < efp; fp++){ + f = *fp; + if(f == 0) + continue; + if(f->type == type) + return 1; + } + return 0; +} + +/* + * the devxxx.c that calls us handles writing data, it knows best + */ +long +netifwrite(Netif *nif, Chan *c, void *a, long n) +{ + Netfile *f; + int type; + char *p, buf[64]; + uchar binaddr[Nmaxaddr]; + + if(NETTYPE(c->qid.path) != Nctlqid) + error(Eperm); + + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = 0; + + if(waserror()){ + qunlock(nif); + nexterror(); + } + + qlock(nif); + f = nif->f[NETID(c->qid.path)]; + if((p = matchtoken(buf, "connect")) != 0){ + type = atoi(p); + if(typeinuse(nif, type)) + error(Einuse); + f->type = type; + if(f->type < 0) + nif->all++; + } else if(matchtoken(buf, "promiscuous")){ + if(f->prom == 0){ + if(nif->prom == 0 && nif->promiscuous != nil) + nif->promiscuous(nif->arg, 1); + f->prom = 1; + nif->prom++; + } + } else if((p = matchtoken(buf, "scanbs")) != 0){ + /* scan for base stations */ + if(f->scan == 0){ + type = atoi(p); + if(type < 5) + type = 5; + if(nif->scanbs != nil) + nif->scanbs(nif->arg, type); + f->scan = type; + nif->scan++; + } + } else if(matchtoken(buf, "bridge")){ + f->bridge = 1; + } else if(matchtoken(buf, "headersonly")){ + f->headersonly = 1; + } else if((p = matchtoken(buf, "addmulti")) != 0){ + if(parseaddr(binaddr, p, nif->alen) < 0) + error("bad address"); + p = netmulti(nif, f, binaddr, 1); + if(p) + error(p); + } else if((p = matchtoken(buf, "remmulti")) != 0){ + if(parseaddr(binaddr, p, nif->alen) < 0) + error("bad address"); + p = netmulti(nif, f, binaddr, 0); + if(p) + error(p); + } else + n = -1; + qunlock(nif); + poperror(); + return n; +} + +int +netifwstat(Netif *nif, Chan *c, uchar *db, int n) +{ + Dir *dir; + Netfile *f; + int m; + + f = nif->f[NETID(c->qid.path)]; + if(f == 0) + error(Enonexist); + + if(netown(f, up->env->user, OWRITE) < 0) + error(Eperm); + + dir = smalloc(sizeof(Dir)+n); + m = convM2D(db, n, &dir[0], (char*)&dir[1]); + if(m == 0){ + free(dir); + error(Eshortstat); + } + if(!emptystr(dir[0].uid)) + strncpy(f->owner, dir[0].uid, KNAMELEN); + if(dir[0].mode != ~0UL) + f->mode = dir[0].mode; + free(dir); + return m; +} + +int +netifstat(Netif *nif, Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, (Dirtab *)nif, 0, netifgen); +} + +void +netifclose(Netif *nif, Chan *c) +{ + Netfile *f; + int t; + Netaddr *ap; + + if((c->flag & COPEN) == 0) + return; + + t = NETTYPE(c->qid.path); + if(t != Ndataqid && t != Nctlqid) + return; + + f = nif->f[NETID(c->qid.path)]; + qlock(f); + if(--(f->inuse) == 0){ + if(f->prom){ + qlock(nif); + if(--(nif->prom) == 0 && nif->promiscuous != nil) + nif->promiscuous(nif->arg, 0); + qunlock(nif); + f->prom = 0; + } + if(f->scan){ + qlock(nif); + if(--(nif->scan) == 0 && nif->scanbs != nil) + nif->scanbs(nif->arg, 0); + qunlock(nif); + f->prom = 0; + f->scan = 0; + } + if(f->nmaddr){ + qlock(nif); + t = 0; + for(ap = nif->maddr; ap; ap = ap->next){ + if(f->maddr[t/8] & (1<<(t%8))) + netmulti(nif, f, ap->addr, 0); + } + qunlock(nif); + f->nmaddr = 0; + } + if(f->type < 0){ + qlock(nif); + --(nif->all); + qunlock(nif); + } + f->owner[0] = 0; + f->type = 0; + f->bridge = 0; + f->headersonly = 0; + qclose(f->in); + } + qunlock(f); +} + +Lock netlock; + +static int +netown(Netfile *p, char *o, int omode) +{ + static int access[] = { 0400, 0200, 0600, 0100 }; + int mode; + int t; + + lock(&netlock); + if(*p->owner){ + if(strncmp(o, p->owner, KNAMELEN) == 0) /* User */ + mode = p->mode; + else if(strncmp(o, eve, KNAMELEN) == 0) /* Bootes is group */ + mode = p->mode<<3; + else + mode = p->mode<<6; /* Other */ + + t = access[omode&3]; + if((t & mode) == t){ + unlock(&netlock); + return 0; + } else { + unlock(&netlock); + return -1; + } + } + strncpy(p->owner, o, KNAMELEN); + p->mode = 0660; + unlock(&netlock); + return 0; +} + +/* + * Increment the reference count of a network device. + * If id < 0, return an unused ether device. + */ +static int +openfile(Netif *nif, int id) +{ + Netfile *f, **fp, **efp; + + if(id >= 0){ + f = nif->f[id]; + if(f == 0) + error(Enodev); + qlock(f); + qreopen(f->in); + f->inuse++; + qunlock(f); + return id; + } + + qlock(nif); + if(waserror()){ + qunlock(nif); + nexterror(); + } + efp = &nif->f[nif->nfile]; + for(fp = nif->f; fp < efp; fp++){ + f = *fp; + if(f == 0){ + f = malloc(sizeof(Netfile)); + if(f == 0) + exhausted("memory"); + f->in = qopen(nif->limit, Qmsg, 0, 0); + if(f->in == nil){ + free(f); + exhausted("memory"); + } + *fp = f; + qlock(f); + } else { + qlock(f); + if(f->inuse){ + qunlock(f); + continue; + } + } + f->inuse = 1; + qreopen(f->in); + netown(f, up->env->user, 0); + qunlock(f); + qunlock(nif); + poperror(); + return fp - nif->f; + } + error(Enodev); + return -1; /* not reached */ +} + +/* + * look for a token starting a string, + * return a pointer to first non-space char after it + */ +static char* +matchtoken(char *p, char *token) +{ + int n; + + n = strlen(token); + if(strncmp(p, token, n)) + return 0; + p += n; + if(*p == 0) + return p; + if(*p != ' ' && *p != '\t' && *p != '\n') + return 0; + while(*p == ' ' || *p == '\t' || *p == '\n') + p++; + return p; +} + +static ulong +hash(uchar *a, int len) +{ + ulong sum = 0; + + while(len-- > 0) + sum = (sum << 1) + *a++; + return sum%Nmhash; +} + +int +activemulti(Netif *nif, uchar *addr, int alen) +{ + Netaddr *hp; + + for(hp = nif->mhash[hash(addr, alen)]; hp; hp = hp->hnext) + if(memcmp(addr, hp->addr, alen) == 0){ + if(hp->ref) + return 1; + else + break; + } + return 0; +} + +static int +parseaddr(uchar *to, char *from, int alen) +{ + char nip[4]; + char *p; + int i; + + p = from; + for(i = 0; i < alen; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +/* + * keep track of multicast addresses + */ +static char* +netmulti(Netif *nif, Netfile *f, uchar *addr, int add) +{ + Netaddr **l, *ap; + int i; + ulong h; + + if(nif->multicast == nil) + return "interface does not support multicast"; + + l = &nif->maddr; + i = 0; + for(ap = *l; ap; ap = *l){ + if(memcmp(addr, ap->addr, nif->alen) == 0) + break; + i++; + l = &ap->next; + } + + if(add){ + if(ap == 0){ + *l = ap = smalloc(sizeof(*ap)); + memmove(ap->addr, addr, nif->alen); + ap->next = 0; + ap->ref = 1; + h = hash(addr, nif->alen); + ap->hnext = nif->mhash[h]; + nif->mhash[h] = ap; + } else { + ap->ref++; + } + if(ap->ref == 1){ + nif->nmaddr++; + nif->multicast(nif->arg, addr, 1); + } + if(i < 8*sizeof(f->maddr)){ + if((f->maddr[i/8] & (1<<(i%8))) == 0) + f->nmaddr++; + f->maddr[i/8] |= 1<<(i%8); + } + } else { + if(ap == 0 || ap->ref == 0) + return 0; + ap->ref--; + if(ap->ref == 0){ + nif->nmaddr--; + nif->multicast(nif->arg, addr, 0); + } + if(i < 8*sizeof(f->maddr)){ + if((f->maddr[i/8] & (1<<(i%8))) != 0) + f->nmaddr--; + f->maddr[i/8] &= ~(1<<(i%8)); + } + } + return 0; +} diff --git a/os/port/netif.h b/os/port/netif.h new file mode 100644 index 00000000..6fbe6b82 --- /dev/null +++ b/os/port/netif.h @@ -0,0 +1,134 @@ +typedef struct Etherpkt Etherpkt; +typedef struct Netaddr Netaddr; +typedef struct Netfile Netfile; +typedef struct Netif Netif; + +enum +{ + Nmaxaddr= 64, + Nmhash= 31, + + Ncloneqid= 1, + Naddrqid, + N2ndqid, + N3rdqid, + Ndataqid, + Nctlqid, + Nstatqid, + Ntypeqid, + Nifstatqid, +}; + +/* + * Macros to manage Qid's used for multiplexed devices + */ +#define NETTYPE(x) (((ulong)x)&0x1f) +#define NETID(x) ((((ulong)x))>>5) +#define NETQID(i,t) ((((ulong)i)<<5)|(t)) + +/* + * one per multiplexed connection + */ +struct Netfile +{ + QLock; + + int inuse; + ulong mode; + char owner[KNAMELEN]; + + int type; /* multiplexor type */ + int prom; /* promiscuous mode */ + int scan; /* base station scanning interval */ + int bridge; /* bridge mode */ + int headersonly; /* headers only - no data */ + uchar maddr[8]; /* bitmask of multicast addresses requested */ + int nmaddr; /* number of multicast addresses */ + + Queue *in; /* input buffer */ +}; + +/* + * a network address + */ +struct Netaddr +{ + Netaddr *next; /* allocation chain */ + Netaddr *hnext; + uchar addr[Nmaxaddr]; + int ref; +}; + +/* + * a network interface + */ +struct Netif +{ + QLock; + + /* multiplexing */ + char name[KNAMELEN]; /* for top level directory */ + int nfile; /* max number of Netfiles */ + Netfile **f; + + /* about net */ + int limit; /* flow control */ + int alen; /* address length */ + int mbps; /* megabits per sec */ + int link; /* link status */ + uchar addr[Nmaxaddr]; + uchar bcast[Nmaxaddr]; + Netaddr *maddr; /* known multicast addresses */ + int nmaddr; /* number of known multicast addresses */ + Netaddr *mhash[Nmhash]; /* hash table of multicast addresses */ + int prom; /* number of promiscuous opens */ + int scan; /* number of base station scanners */ + int all; /* number of -1 multiplexors */ + + /* statistics */ + int misses; + int inpackets; + int outpackets; + int crcs; /* input crc errors */ + int oerrs; /* output errors */ + int frames; /* framing errors */ + int overflows; /* packet overflows */ + int buffs; /* buffering errors */ + int soverflows; /* software overflow */ + + /* routines for touching the hardware */ + void *arg; + void (*promiscuous)(void*, int); + void (*multicast)(void*, uchar*, int); + void (*scanbs)(void*, uint); /* scan for base stations */ +}; + +void netifinit(Netif*, char*, int, ulong); +Walkqid* netifwalk(Netif*, Chan*, Chan*, char **, int); +Chan* netifopen(Netif*, Chan*, int); +void netifclose(Netif*, Chan*); +long netifread(Netif*, Chan*, void*, long, ulong); +Block* netifbread(Netif*, Chan*, long, ulong); +long netifwrite(Netif*, Chan*, void*, long); +int netifwstat(Netif*, Chan*, uchar*, int); +int netifstat(Netif*, Chan*, uchar*, int); +int activemulti(Netif*, uchar*, int); + +/* + * Ethernet specific + */ +enum +{ + Eaddrlen= 6, + ETHERMINTU = 60, /* minimum transmit size */ + ETHERMAXTU = 1514, /* maximum transmit size */ + ETHERHDRSIZE = 14, /* size of an ethernet header */ +}; + +struct Etherpkt +{ + uchar d[Eaddrlen]; + uchar s[Eaddrlen]; + uchar type[2]; + uchar data[1500]; +}; diff --git a/os/port/nocache.c b/os/port/nocache.c new file mode 100644 index 00000000..0eed67e1 --- /dev/null +++ b/os/port/nocache.c @@ -0,0 +1,49 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +/* + * stubs when no devmnt cache + */ +void +cinit(void) +{ +} + +void +copen(Chan *c) +{ + c->flag &= ~CCACHE; +} + +int +cread(Chan *c, uchar *b, int n, vlong off) +{ + USED(c); + USED(b); + USED(n); + USED(off); + return 0; +} + +void +cwrite(Chan *c, uchar *buf, int n, vlong off) +{ + USED(c); + USED(buf); + USED(n); + USED(off); +} + +void +cupdate(Chan *c, uchar *buf, int n, vlong off) +{ + USED(c); + USED(buf); + USED(n); + USED(off); +} + diff --git a/os/port/nodynld.c b/os/port/nodynld.c new file mode 100644 index 00000000..2ba8c7c2 --- /dev/null +++ b/os/port/nodynld.c @@ -0,0 +1,48 @@ +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include +#include + +/* + * null kernel interface to dynld, to stop libinterp moaning + */ + +void* +dynimport(Dynobj*, char*, ulong) +{ + return nil; +} + +void +dynobjfree(Dynobj*) +{ +} + +Dynobj* +kdynloadfd(int fd, Dynsym *tab, int ntab) +{ + USED(fd, tab, ntab); + return nil; +} + +int +kdynloadable(int) +{ + return 0; +} + +Dynobj* +dynld(int) +{ + return nil; +} + +int +dynldable(int) +{ + return 0; +} diff --git a/os/port/noenv.c b/os/port/noenv.c new file mode 100644 index 00000000..446945ac --- /dev/null +++ b/os/port/noenv.c @@ -0,0 +1,33 @@ +/* + * use this when devenv.c not used + */ +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +/* + * null kernel interface + */ +Egrp* +newegrp(void) +{ + return nil; +} + +void +closeegrp(Egrp*) +{ +} + +void +egrpcpy(Egrp*, Egrp*) +{ +} + +void +ksetenv(char*, char*, int) +{ +} diff --git a/os/port/noscreen.c b/os/port/noscreen.c new file mode 100644 index 00000000..af76178e --- /dev/null +++ b/os/port/noscreen.c @@ -0,0 +1,9 @@ +void +screeninit(void) +{ +} + +void +screenrotate(int) +{ +} diff --git a/os/port/parse.c b/os/port/parse.c new file mode 100644 index 00000000..9d59b567 --- /dev/null +++ b/os/port/parse.c @@ -0,0 +1,114 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +/* + * Generous estimate of number of fields, including terminal nil pointer + */ +static int +ncmdfield(char *p, int n) +{ + int white, nwhite; + char *ep; + int nf; + + if(p == nil) + return 1; + + nf = 0; + ep = p+n; + white = 1; /* first text will start field */ + while(p < ep){ + nwhite = (strchr(" \t\r\n", *p++ & 0xFF) != 0); /* UTF is irrelevant */ + if(white && !nwhite) /* beginning of field */ + nf++; + white = nwhite; + } + return nf+1; /* +1 for nil */ +} + +/* + * parse a command written to a device + */ +Cmdbuf* +parsecmd(char *p, int n) +{ + Cmdbuf *volatile cb; + int nf; + char *sp; + + nf = ncmdfield(p, n); + + /* allocate Cmdbuf plus string pointers plus copy of string including \0 */ + sp = smalloc(sizeof(*cb) + nf * sizeof(char*) + n + 1); + cb = (Cmdbuf*)sp; + cb->f = (char**)(&cb[1]); + cb->buf = (char*)(&cb->f[nf]); + + if(up!=nil && waserror()){ + free(cb); + nexterror(); + } + memmove(cb->buf, p, n); + if(up != nil) + poperror(); + + /* dump new line and null terminate */ + if(n > 0 && cb->buf[n-1] == '\n') + n--; + cb->buf[n] = '\0'; + + cb->nf = tokenize(cb->buf, cb->f, nf-1); + cb->f[cb->nf] = nil; + + return cb; +} + +/* + * Reconstruct original message, for error diagnostic + */ +void +cmderror(Cmdbuf *cb, char *s) +{ + int i; + char *p, *e; + + p = up->genbuf; + e = p+ERRMAX-10; + p = seprint(p, e, "%s \"", s); + for(i=0; inf; i++){ + if(i > 0) + p = seprint(p, e, " "); + p = seprint(p, e, "%q", cb->f[i]); + } + strcpy(p, "\""); + error(up->genbuf); +} + +/* + * Look up entry in table + */ +Cmdtab* +lookupcmd(Cmdbuf *cb, Cmdtab *ctab, int nctab) +{ + int i; + Cmdtab *ct; + + if(cb->nf == 0) + error("empty control message"); + + for(ct = ctab, i=0; icmd, "*") !=0) /* wildcard always matches */ + if(strcmp(ct->cmd, cb->f[0]) != 0) + continue; + if(ct->narg != 0 && ct->narg != cb->nf) + cmderror(cb, Ecmdargs); + return ct; + } + + cmderror(cb, "unknown control message"); + return nil; +} diff --git a/os/port/pgrp.c b/os/port/pgrp.c new file mode 100644 index 00000000..6b2d7d5c --- /dev/null +++ b/os/port/pgrp.c @@ -0,0 +1,278 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +static Ref pgrpid; +static Ref mountid; + +Pgrp* +newpgrp(void) +{ + Pgrp *p; + + p = smalloc(sizeof(Pgrp)); + p->ref = 1; + p->pgrpid = incref(&pgrpid); + p->pin = Nopin; + p->progmode = 0644; + p->privatemem = 0; + return p; +} + +void +closepgrp(Pgrp *p) +{ + Mhead **h, **e, *f, *next; + + if(p == nil || decref(p) != 0) + return; + + wlock(&p->ns); + p->pgrpid = -1; + + e = &p->mnthash[MNTHASH]; + for(h = p->mnthash; h < e; h++) { + for(f = *h; f; f = next) { + wlock(&f->lock); + cclose(f->from); + mountfree(f->mount); + f->mount = nil; + next = f->hash; + wunlock(&f->lock); + putmhead(f); + } + } + wunlock(&p->ns); + cclose(p->dot); + cclose(p->slash); + free(p); +} + +void +pgrpinsert(Mount **order, Mount *m) +{ + Mount *f; + + m->order = 0; + if(*order == 0) { + *order = m; + return; + } + for(f = *order; f; f = f->order) { + if(m->mountid < f->mountid) { + m->order = f; + *order = m; + return; + } + order = &f->order; + } + *order = m; +} + +/* + * pgrpcpy MUST preserve the mountid allocation order of the parent group + */ +void +pgrpcpy(Pgrp *to, Pgrp *from) +{ + int i; + Mount *n, *m, **link, *order; + Mhead *f, **tom, **l, *mh; + + wlock(&from->ns); + order = 0; + tom = to->mnthash; + for(i = 0; i < MNTHASH; i++) { + l = tom++; + for(f = from->mnthash[i]; f; f = f->hash) { + rlock(&f->lock); + mh = malloc(sizeof(Mhead)); + if(mh == nil) { + runlock(&f->lock); + wunlock(&from->ns); + error(Enomem); + } + mh->from = f->from; + mh->ref = 1; + incref(mh->from); + *l = mh; + l = &mh->hash; + link = &mh->mount; + for(m = f->mount; m; m = m->next) { + n = newmount(mh, m->to, m->mflag, m->spec); + if(n == nil) { + runlock(&f->lock); + wunlock(&from->ns); + error(Enomem); + } + m->copy = n; + pgrpinsert(&order, m); + *link = n; + link = &n->next; + } + runlock(&f->lock); + } + } + /* + * Allocate mount ids in the same sequence as the parent group + */ + lock(&mountid.l); + for(m = order; m; m = m->order) + m->copy->mountid = mountid.ref++; + unlock(&mountid.l); + + to->pin = from->pin; + + to->slash = cclone(from->slash); + to->dot = cclone(from->dot); + to->nodevs = from->nodevs; + + wunlock(&from->ns); +} + +Fgrp* +newfgrp(Fgrp *old) +{ + Fgrp *new; + int n; + + new = smalloc(sizeof(Fgrp)); + new->ref = 1; + n = DELTAFD; + if(old != nil){ + lock(old); + if(old->maxfd >= n) + n = (old->maxfd+1 + DELTAFD-1)/DELTAFD * DELTAFD; + new->maxfd = old->maxfd; + unlock(old); + } + new->nfd = n; + new->fd = smalloc(n*sizeof(Chan*)); + return new; +} + +Fgrp* +dupfgrp(Fgrp *f) +{ + int i; + Chan *c; + Fgrp *new; + int n; + + new = smalloc(sizeof(Fgrp)); + new->ref = 1; + lock(f); + n = DELTAFD; + if(f->maxfd >= n) + n = (f->maxfd+1 + DELTAFD-1)/DELTAFD * DELTAFD; + new->nfd = n; + new->fd = malloc(n*sizeof(Chan*)); + if(new->fd == nil){ + unlock(f); + free(new); + error(Enomem); + } + new->maxfd = f->maxfd; + new->minfd = f->minfd; + for(i = 0; i <= f->maxfd; i++) { + if(c = f->fd[i]){ + incref(c); + new->fd[i] = c; + } + } + unlock(f); + + return new; +} + +void +closefgrp(Fgrp *f) +{ + int i; + Chan *c; + + if(f == nil || decref(f) != 0) + return; + + for(i = 0; i <= f->maxfd; i++) + if(c = f->fd[i]) + cclose(c); + + free(f->fd); + free(f); +} + +Mount* +newmount(Mhead *mh, Chan *to, int flag, char *spec) +{ + Mount *m; + + m = smalloc(sizeof(Mount)); + m->to = to; + m->head = mh; + incref(to); + m->mountid = incref(&mountid); + m->mflag = flag; + if(spec != 0) + kstrdup(&m->spec, spec); + + return m; +} + +void +mountfree(Mount *m) +{ + Mount *f; + + while(m) { + f = m->next; + cclose(m->to); + m->mountid = 0; + free(m->spec); + free(m); + m = f; + } +} + +void +resrcwait(char *reason) +{ + char *p; + + if(up == 0) + panic("resrcwait"); + + p = up->psstate; + if(reason) { + up->psstate = reason; + print("%s\n", reason); + } + + tsleep(&up->sleep, return0, 0, 300); + up->psstate = p; +} + +void +closesigs(Skeyset *s) +{ + int i; + + if(s == nil || decref(s) != 0) + return; + for(i=0; inkey; i++) + freeskey(s->keys[i]); + free(s); +} + +void +freeskey(Signerkey *key) +{ + if(key == nil || decref(key) != 0) + return; + free(key->owner); + (*key->pkfree)(key->pk); + free(key); +} diff --git a/os/port/portbreak.c b/os/port/portbreak.c new file mode 100644 index 00000000..d64f40f6 --- /dev/null +++ b/os/port/portbreak.c @@ -0,0 +1,161 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "portfns.h" +#include "ureg.h" +#include "../port/error.h" + +// +// These bits used to be in port/devdbg but were removed in +// order to allow for using hardware debug features on certain +// architectures +// + +extern void breakset(Bkpt *b); +extern void breakrestore(Bkpt *b); +extern Bkpt* breakclear(int id); +extern void breaknotify(Bkpt *b, Proc *p); +extern int breakmatch(BkptCond *cond, Ureg *ur, Proc *p); + +void skipfree(Bkpt *b); +Bkpt*newskip(ulong addr, Bkpt *skipb, Proc *skipp); +Bkpt *skipalloc; +extern Bkpt *breakpoints; +typedef struct SkipArg SkipArg; +struct SkipArg +{ + Bkpt *b; + Proc *p; +}; + +void +skiphandler(Bkpt *b) +{ + SkipArg *a = b->aux; + Bkpt *l; + + if(breakclear(b->id) == nil) + panic("skiphandler: breakclear() failed"); + breakrestore(a->b); + l = a->b->link; + while(l != nil) { + breakrestore(l); + l = l->link; + } + skipfree(b); + a->p->dbgstop = 0; // Whoo! + if(a->p->state == Stopped) + ready(a->p); +} + +Bkpt* +newskip(ulong addr, Bkpt *skipb, Proc *skipp) +{ + Bkpt *b; + SkipArg *a; + + b = skipalloc; + if(b == nil) + panic("newskip(): no free skips\n"); + skipalloc = b->next; + + b->addr = addr; + b->conditions->val = addr; + b->link = nil; + a = b->aux; + a->b = skipb; + a->p = skipp; + + return b; +} + +void +skipfree(Bkpt *b) +{ + b->next = skipalloc; + skipalloc = b; +} + +// +// Called from the exception handler when a breakpoint instruction has been +// hit. This cannot not be called unless at least one breakpoint with this +// address is in the list of breakpoints. (All breakpoint notifications must +// previously have been set via setbreak()) +// +// foreach breakpoint in list +// if breakpoint matches conditions +// notify the break handler +// if no breakpoints matched the conditions +// pick a random breakpoint set to this address +// +// set a breakpoint at the next instruction to be executed, +// and pass the current breakpoint to the "skiphandler" +// +// clear the current breakpoint +// +// Tell the scheduler to stop scheduling, so the caller is +// guaranteed to execute the instruction, followed by the +// added breakpoint. +// +// +int +breakhit(Ureg *ur, Proc *p) +{ + Bkpt *b; + int nmatched; + Bkpt *skip; + + nmatched = 0; + for(b = breakpoints; b != nil; b = b->next) { + if(breakmatch(b->conditions, ur, p)) { + breaknotify(b, p); + ++nmatched; + } + } + + if(nmatched) + return BrkSched; + + skip = nil; + for(b = breakpoints; b != nil; b = b->next) { + if(b->addr == ur->pc) { + if(breakclear(b->id) == nil) + panic("breakhit: breakclear() failed"); + + if(skip == nil) + skip = newskip(machnextaddr(ur), b, p); + else { + b->link = skip->link; + skip->link = b; + } + } + } + if(skip == nil) + return BrkSched; + breakset(skip); + return BrkNoSched; +} + +void +portbreakinit(void) +{ + Bkpt *b; + int i; + + skipalloc = mallocz(conf.nproc*(sizeof(Bkpt)+sizeof(BkptCond)+sizeof(SkipArg)), 1); + if(skipalloc == nil) + error(Enomem); + + b = skipalloc; + for(i=0; i < conf.nproc-1; i++) { + b->id = -(i+1); + b->conditions = (BkptCond*)((uchar*)b + sizeof(Bkpt)); + b->conditions->op = 'b'; + b->handler = skiphandler; + b->aux = (SkipArg*)((uchar*)b+sizeof(Bkpt)+sizeof(BkptCond)); + b->next = (Bkpt*)((uchar*)b+sizeof(Bkpt)+sizeof(BkptCond)+sizeof(SkipArg)); + b = b->next; + } + b->next = nil; +} diff --git a/os/port/portclock.c b/os/port/portclock.c new file mode 100644 index 00000000..04d1b110 --- /dev/null +++ b/os/port/portclock.c @@ -0,0 +1,277 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +struct Timers +{ + Lock; + Timer *head; +}; + +static Timers timers[MAXMACH]; + +ulong intrcount[MAXMACH]; +ulong fcallcount[MAXMACH]; + +static uvlong +tadd(Timers *tt, Timer *nt) +{ + Timer *t, **last; + + /* Called with tt locked */ + assert(nt->tt == nil); + switch(nt->tmode){ + default: + panic("timer"); + break; + case Trelative: + assert(nt->tns > 0); + nt->twhen = fastticks(nil) + ns2fastticks(nt->tns); + break; + case Tabsolute: + nt->twhen = tod2fastticks(nt->tns); + break; + case Tperiodic: + assert(nt->tns >= 100000); /* At least 100 µs period */ + if(nt->twhen == 0){ + /* look for another timer at same frequency for combining */ + for(t = tt->head; t; t = t->tnext){ + if(t->tmode == Tperiodic && t->tns == nt->tns) + break; + } + if (t) + nt->twhen = t->twhen; + else + nt->twhen = fastticks(nil); + } + nt->twhen += ns2fastticks(nt->tns); + break; + } + + for(last = &tt->head; t = *last; last = &t->tnext){ + if(t->twhen > nt->twhen) + break; + } + nt->tnext = *last; + *last = nt; + nt->tt = tt; + if(last == &tt->head) + return nt->twhen; + return 0; +} + +static uvlong +tdel(Timer *dt) +{ + + Timer *t, **last; + Timers *tt; + + tt = dt->tt; + if (tt == nil) + return 0; + for(last = &tt->head; t = *last; last = &t->tnext){ + if(t == dt){ + assert(dt->tt); + dt->tt = nil; + *last = t->tnext; + break; + } + } + if(last == &tt->head && tt->head) + return tt->head->twhen; + return 0; +} + +/* add or modify a timer */ +void +timeradd(Timer *nt) +{ + Timers *tt; + vlong when; + + if (nt->tmode == Tabsolute){ + when = todget(nil); + if (nt->tns <= when){ + // if (nt->tns + MS2NS(10) <= when) /* small deviations will happen */ + // print("timeradd (%lld %lld) %lld too early 0x%lux\n", + // when, nt->tns, when - nt->tns, getcallerpc(&nt)); + nt->tns = when; + } + } + /* Must lock Timer struct before Timers struct */ + ilock(nt); + if(tt = nt->tt){ + ilock(tt); + tdel(nt); + iunlock(tt); + } + tt = &timers[m->machno]; + ilock(tt); + when = tadd(tt, nt); + if(when) + timerset(when); + iunlock(tt); + iunlock(nt); +} + + +void +timerdel(Timer *dt) +{ + Timers *tt; + uvlong when; + + ilock(dt); + if(tt = dt->tt){ + ilock(tt); + when = tdel(dt); + if(when && tt == &timers[m->machno]) + timerset(tt->head->twhen); + iunlock(tt); + } + iunlock(dt); +} + +void +hzclock(Ureg *ur) +{ + m->ticks++; + if(m->proc) + m->proc->pc = ur->pc; + + kmapinval(); + + if(kproftick != nil) + kproftick(ur->pc); + + if((active.machs&(1<machno)) == 0) + return; + + if(active.exiting) { + print("someone's exiting\n"); + exit(0); + } + + checkalarms(); + + if(up && up->state == Running){ + if(anyready()){ + sched(); + splhi(); + } + } +} + +void +timerintr(Ureg *u, uvlong) +{ + Timer *t; + Timers *tt; + uvlong when, now; + int callhzclock; + static int sofar; + + intrcount[m->machno]++; + callhzclock = 0; + tt = &timers[m->machno]; + now = fastticks(nil); + ilock(tt); + while(t = tt->head){ + /* + * No need to ilock t here: any manipulation of t + * requires tdel(t) and this must be done with a + * lock to tt held. We have tt, so the tdel will + * wait until we're done + */ + when = t->twhen; + if(when > now){ + timerset(when); + iunlock(tt); + if(callhzclock) + hzclock(u); + return; + } + tt->head = t->tnext; + assert(t->tt == tt); + t->tt = nil; + fcallcount[m->machno]++; + iunlock(tt); + if(t->tf) + (*t->tf)(u, t); + else + callhzclock++; + ilock(tt); + if(t->tmode == Tperiodic) + tadd(tt, t); + } + iunlock(tt); +} + +void +timersinit(void) +{ + Timer *t; + + todinit(); + t = malloc(sizeof(*t)); + t->tmode = Tperiodic; + t->tt = nil; + t->tns = 1000000000/HZ; + t->tf = nil; + timeradd(t); +} + +Timer* +addclock0link(void (*f)(void), int ms) +{ + Timer *nt; + uvlong when; + + /* Synchronize to hztimer if ms is 0 */ + nt = malloc(sizeof(Timer)); + if(ms == 0) + ms = 1000/HZ; + nt->tns = (vlong)ms*1000000LL; + nt->tmode = Tperiodic; + nt->tt = nil; + nt->tf = (void (*)(Ureg*, Timer*))f; + + ilock(&timers[0]); + when = tadd(&timers[0], nt); + if(when) + timerset(when); + iunlock(&timers[0]); + return nt; +} + +/* + * This tk2ms avoids overflows that the macro version is prone to. + * It is a LOT slower so shouldn't be used if you're just converting + * a delta. + */ +ulong +tk2ms(ulong ticks) +{ + uvlong t, hz; + + t = ticks; + hz = HZ; + t *= 1000L; + t = t/hz; + ticks = t; + return ticks; +} + +ulong +ms2tk(ulong ms) +{ + /* avoid overflows at the cost of precision */ + if(ms >= 1000000000/HZ) + return (ms/1000)*HZ; + return (ms*HZ+500)/1000; +} diff --git a/os/port/portdat.h b/os/port/portdat.h new file mode 100644 index 00000000..103961e0 --- /dev/null +++ b/os/port/portdat.h @@ -0,0 +1,673 @@ +typedef struct Alarms Alarms; +typedef struct Block Block; +typedef struct Bkpt Bkpt; +typedef struct BkptCond BkptCond; +typedef struct Chan Chan; +typedef struct Cmdbuf Cmdbuf; +typedef struct Cmdtab Cmdtab; +typedef struct Cname Cname; +typedef struct Crypt Crypt; +typedef struct Dev Dev; +typedef struct DevConf DevConf; +typedef struct Dirtab Dirtab; +typedef struct Edf Edf; +typedef struct Egrp Egrp; +typedef struct Evalue Evalue; +typedef struct Fgrp Fgrp; +typedef struct List List; +typedef struct Log Log; +typedef struct Logflag Logflag; +typedef struct Mntcache Mntcache; +typedef struct Mntparam Mntparam; +typedef struct Mount Mount; +typedef struct Mntrpc Mntrpc; +typedef struct Mntwalk Mntwalk; +typedef struct Mnt Mnt; +typedef struct Mhead Mhead; +typedef struct Osenv Osenv; +typedef struct Pgrp Pgrp; +typedef struct Proc Proc; +typedef struct QLock QLock; +typedef struct Queue Queue; +typedef struct Ref Ref; +typedef struct Rendez Rendez; +typedef struct Rept Rept; +typedef struct Rootdata Rootdata; +typedef struct RWlock RWlock; +typedef struct Signerkey Signerkey; +typedef struct Skeyset Skeyset; +typedef struct Talarm Talarm; +typedef struct Timer Timer; +typedef struct Timers Timers; +typedef struct Uart Uart; +typedef struct Walkqid Walkqid; +typedef int Devgen(Chan*, char*, Dirtab*, int, int, Dir*); + +#pragma incomplete DevConf +#pragma incomplete Edf +#pragma incomplete Mntcache +#pragma incomplete Mntrpc +#pragma incomplete Queue +#pragma incomplete Timers + +#include "fcall.h" +#include + +#define nelem(n) (sizeof(n)/sizeof(n[0])) + +struct Ref +{ + Lock l; + long ref; +}; + +struct Rendez +{ + Lock; + Proc *p; +}; + +struct Rept +{ + Lock l; + Rendez r; + void *o; + int t; + int (*active)(void*); + int (*ck)(void*, int); + void (*f)(void*); /* called with VM acquire()'d */ +}; + +struct Osenv +{ + char *syserrstr; /* last error from a system call, errbuf0 or 1 */ + char *errstr; /* reason we're unwinding the error stack, errbuf1 or 0 */ + char errbuf0[ERRMAX]; + char errbuf1[ERRMAX]; + Pgrp* pgrp; /* Ref to namespace, working dir and root */ + Fgrp* fgrp; /* Ref to file descriptors */ + Egrp* egrp; /* Environment vars */ + Skeyset* sigs; /* Signed module keys */ + Rendez* rend; /* Synchro point */ + Queue* waitq; /* Info about dead children */ + Queue* childq; /* Info about children for debuggers */ + void* debug; /* Debugging master */ + int uid; /* Numeric user id for system */ + int gid; /* Numeric group id for system */ + char* user; /* Inferno user name */ + FPenv fpu; /* Floating point thread state */ +}; + +enum +{ + Nopin = -1 +}; + +struct QLock +{ + Lock use; /* to access Qlock structure */ + Proc *head; /* next process waiting for object */ + Proc *tail; /* last process waiting for object */ + int locked; /* flag */ +}; + +struct RWlock +{ + Lock; /* Lock modify lock */ + QLock x; /* Mutual exclusion lock */ + QLock k; /* Lock for waiting writers */ + int readers; /* Count of readers in lock */ +}; + +struct Talarm +{ + Lock; + Proc* list; +}; + +struct Alarms +{ + QLock; + Proc* head; +}; + +struct Rootdata +{ + int dotdot; + void *ptr; + int size; + int *sizep; +}; + +/* + * Access types in namec & channel flags + */ +enum +{ + Aaccess, /* as in stat, wstat */ + Abind, /* for left-hand-side of bind */ + Atodir, /* as in chdir */ + Aopen, /* for i/o */ + Amount, /* to be mounted or mounted upon */ + Acreate, /* is to be created */ + Aremove, /* will be removed by caller */ + + COPEN = 0x0001, /* for i/o */ + CMSG = 0x0002, /* the message channel for a mount */ + CCEXEC = 0x0008, /* close on exec */ + CFREE = 0x0010, /* not in use */ + CRCLOSE = 0x0020, /* remove on close */ + CCACHE = 0x0080, /* client cache */ +}; + +enum +{ + BINTR = (1<<0), + BFREE = (1<<1), + Bipck = (1<<2), /* ip checksum */ + Budpck = (1<<3), /* udp checksum */ + Btcpck = (1<<4), /* tcp checksum */ + Bpktck = (1<<5), /* packet checksum */ +}; + +struct Block +{ + Block* next; + Block* list; + uchar* rp; /* first unconsumed byte */ + uchar* wp; /* first empty byte */ + uchar* lim; /* 1 past the end of the buffer */ + uchar* base; /* start of the buffer */ + void (*free)(Block*); + ushort flag; + ushort checksum; /* IP checksum of complete packet (minus media header) */ +}; +#define BLEN(s) ((s)->wp - (s)->rp) +#define BALLOC(s) ((s)->lim - (s)->base) + +struct Chan +{ + Lock; + Ref; + Chan* next; /* allocation */ + Chan* link; + vlong offset; /* in file */ + ushort type; + ulong dev; + ushort mode; /* read/write */ + ushort flag; + Qid qid; + int fid; /* for devmnt */ + ulong iounit; /* chunk size for i/o; 0==default */ + Mhead* umh; /* mount point that derived Chan; used in unionread */ + Chan* umc; /* channel in union; held for union read */ + QLock umqlock; /* serialize unionreads */ + int uri; /* union read index */ + int dri; /* devdirread index */ + ulong mountid; + Mntcache *mcp; /* Mount cache pointer */ + Mnt *mux; /* Mnt for clients using me for messages */ + union { + void* aux; + char tag[4]; /* for iproute */ + }; + Chan* mchan; /* channel to mounted server */ + Qid mqid; /* qid of root of mount point */ + Cname *name; +}; + +struct Cname +{ + Ref; + int alen; /* allocated length */ + int len; /* strlen(s) */ + char *s; +}; + +struct Dev +{ + int dc; + char* name; + + void (*reset)(void); + void (*init)(void); + void (*shutdown)(void); + Chan* (*attach)(char*); + Walkqid* (*walk)(Chan*, Chan*, char**, int); + int (*stat)(Chan*, uchar*, int); + Chan* (*open)(Chan*, int); + void (*create)(Chan*, char*, int, ulong); + void (*close)(Chan*); + long (*read)(Chan*, void*, long, vlong); + Block* (*bread)(Chan*, long, ulong); + long (*write)(Chan*, void*, long, vlong); + long (*bwrite)(Chan*, Block*, ulong); + void (*remove)(Chan*); + int (*wstat)(Chan*, uchar*, int); + void (*power)(int); /* power mgt: power(1) → on, power (0) → off */ + int (*config)(int, char*, DevConf*); +}; + +struct Dirtab +{ + char name[KNAMELEN]; + Qid qid; + vlong length; + long perm; +}; + +struct Walkqid +{ + Chan *clone; + int nqid; + Qid qid[1]; +}; + +enum +{ + NSMAX = 1000, + NSLOG = 7, + NSCACHE = (1<ref; channels on this mount point incref(c->mchan) == Mnt.c */ + Chan *c; /* Channel to file service */ + Proc *rip; /* Reader in progress */ + Mntrpc *queue; /* Queue of pending requests on this channel */ + ulong id; /* Multiplexer id for channel check */ + Mnt *list; /* Free list */ + int flags; /* cache */ + int msize; /* data + IOHDRSZ */ + char *version; /* 9P version */ + Queue *q; /* input queue */ +}; + +enum +{ + RENDLOG = 5, + RENDHASH = 1<mnthash[(qid).path&((1< variadic */ +}; + +enum +{ + MAXPOOL = 8, +}; + +extern Pool* mainmem; +extern Pool* heapmem; +extern Pool* imagmem; + +/* queue state bits, Qmsg, Qcoalesce, and Qkick can be set in qopen */ +enum +{ + /* Queue.state */ + Qstarve = (1<<0), /* consumer starved */ + Qmsg = (1<<1), /* message stream */ + Qclosed = (1<<2), /* queue has been closed/hungup */ + Qflow = (1<<3), /* producer flow controlled */ + Qcoalesce = (1<<4), /* coallesce packets on read */ + Qkick = (1<<5), /* always call the kick routine after qwrite */ +}; + +#define DEVDOTDOT -1 + +#pragma varargck argpos print 1 +#pragma varargck argpos snprint 3 +#pragma varargck argpos seprint 3 +#pragma varargck argpos sprint 2 +#pragma varargck argpos fprint 2 +#pragma varargck argpos iprint 1 +#pragma varargck argpos panic 1 +#pragma varargck argpos kwerrstr 1 +#pragma varargck argpos kprint 1 + +#pragma varargck type "lld" vlong +#pragma varargck type "llx" vlong +#pragma varargck type "lld" uvlong +#pragma varargck type "llx" uvlong +#pragma varargck type "lx" void* +#pragma varargck type "ld" long +#pragma varargck type "lx" long +#pragma varargck type "ld" ulong +#pragma varargck type "lx" ulong +#pragma varargck type "d" int +#pragma varargck type "x" int +#pragma varargck type "c" int +#pragma varargck type "C" int +#pragma varargck type "d" uint +#pragma varargck type "x" uint +#pragma varargck type "c" uint +#pragma varargck type "C" uint +#pragma varargck type "f" double +#pragma varargck type "e" double +#pragma varargck type "g" double +#pragma varargck type "s" char* +#pragma varargck type "S" Rune* +#pragma varargck type "r" void +#pragma varargck type "%" void +#pragma varargck type "I" uchar* +#pragma varargck type "V" uchar* +#pragma varargck type "E" uchar* +#pragma varargck type "M" uchar* +#pragma varargck type "p" void* +#pragma varargck type "q" char* diff --git a/os/port/portfns.h b/os/port/portfns.h new file mode 100644 index 00000000..d6bcdcf0 --- /dev/null +++ b/os/port/portfns.h @@ -0,0 +1,319 @@ +#define FPinit() fpinit() /* remove this if math lib is linked */ +void FPrestore(void*); +void FPsave(void*); +Timer* addclock0link(void (*)(void), int); +Cname* addelem(Cname*, char*); +void addprog(Proc*); +void addrootfile(char*, uchar*, ulong); +Block* adjustblock(Block*, int); +Block* allocb(int); +int anyhigher(void); +int anyready(void); +#define assert(x) if(x){}else _assert("assert(x) failed") +void _assert(char*); +Block* bl2mem(uchar*, Block*, int); +int blocklen(Block*); +int breakhit(Ureg *ur, Proc*); +void callwithureg(void(*)(Ureg*)); +char* channame(Chan*); +int canlock(Lock*); +int canqlock(QLock*); +void cclose(Chan*); +int canrlock(RWlock*); +void chandevinit(void); +void chandevreset(void); +void chandevshutdown(void); +Dir* chandirstat(Chan*); +void chanfree(Chan*); +void chanrec(Mnt*); +void checkalarms(void); +void checkb(Block*, char*); +void cinit(void); +Chan* cclone(Chan*); +void cclose(Chan*); +void closeegrp(Egrp*); +void closefgrp(Fgrp*); +void closemount(Mount*); +void closepgrp(Pgrp*); +void closesigs(Skeyset*); +void cmderror(Cmdbuf*, char*); +int cmount(Chan*, Chan*, int, char*); +void cnameclose(Cname*); +Block* concatblock(Block*); +void confinit(void); +void copen(Chan*); +Block* copyblock(Block*, int); +int cread(Chan*, uchar*, int, vlong); +Chan* cunique(Chan*); +Chan* createdir(Chan*, Mhead*); +void cunmount(Chan*, Chan*); +void cupdate(Chan*, uchar*, int, vlong); +void cursorenable(void); +void cursordisable(void); +int cursoron(int); +void cursoroff(int); +void cwrite(Chan*, uchar*, int, vlong); +void debugkey(Rune, char *, void(*)(), int); +int decref(Ref*); +Chan* devattach(int, char*); +Block* devbread(Chan*, long, ulong); +long devbwrite(Chan*, Block*, ulong); +Chan* devclone(Chan*); +void devcreate(Chan*, char*, int, ulong); +void devdir(Chan*, Qid, char*, vlong, char*, long, Dir*); +long devdirread(Chan*, char*, long, Dirtab*, int, Devgen*); +Devgen devgen; +void devinit(void); +int devno(int, int); +void devpower(int); +Dev* devbyname(char*); +Chan* devopen(Chan*, int, Dirtab*, int, Devgen*); +void devpermcheck(char*, ulong, int); +void devremove(Chan*); +void devreset(void); +void devshutdown(void); +int devstat(Chan*, uchar*, int, Dirtab*, int, Devgen*); +Walkqid* devwalk(Chan*, Chan*, char**, int, Dirtab*, int, Devgen*); +int devwstat(Chan*, uchar*, int); +void disinit(void*); +void disfault(void*, char*); +int domount(Chan**, Mhead**); +void drawactive(int); +void drawcmap(void); +void dumpstack(void); +Fgrp* dupfgrp(Fgrp*); +void egrpcpy(Egrp*, Egrp*); +int emptystr(char*); +int eqchan(Chan*, Chan*, int); +int eqqid(Qid, Qid); +void error(char*); +void errorf(char*, ...); +#pragma varargck argpos errorf 1 +void errstr(char*, int); +void excinit(void); +void exhausted(char*); +void exit(int); +void reboot(void); +void halt(void); +int export(int, char*, int); +uvlong fastticks(uvlong*); +uvlong fastticks2ns(uvlong); +void fdclose(Fgrp*, int); +Chan* fdtochan(Fgrp*, int, int, int, int); +int findmount(Chan**, Mhead**, int, int, Qid); +void free(void*); +void freeb(Block*); +void freeblist(Block*); +void freeskey(Signerkey*); +void getcolor(ulong, ulong*, ulong*, ulong*); +ulong getmalloctag(void*); +ulong getrealloctag(void*); +void gotolabel(Label*); +void hnputl(void*, ulong); +void hnputs(void*, ushort); +Block* iallocb(int); +void iallocsummary(void); +void ilock(Lock*); +int incref(Ref*); +int iprint(char*, ...); +#pragma varargck argpos iprint 1 +void isdir(Chan*); +int iseve(void); +int islo(void); +void iunlock(Lock*); +void ixsummary(void); +void kbdclock(void); +int kbdcr2nl(Queue*, int); +int kbdputc(Queue*, int); +void kbdrepeat(int); +void kproc(char*, void(*)(void*), void*, int); +int kfgrpclose(Fgrp*, int); +void kprocchild(Proc*, void (*)(void*), void*); +int kprint(char*, ...); +void (*kproftick)(ulong); +void ksetenv(char*, char*, int); +void kstrcpy(char*, char*, int); +void kstrdup(char**, char*); +long latin1(Rune*, int); +void lock(Lock*); +void logopen(Log*); +void logclose(Log*); +char* logctl(Log*, int, char**, Logflag*); +void logn(Log*, int, void*, int); +long logread(Log*, void*, ulong, long); +void logb(Log*, int, char*, ...); +#define pragma varargck argpos logb 3 +Cmdtab* lookupcmd(Cmdbuf*, Cmdtab*, int); +void machinit(void); +extern void machbreakinit(void); +extern Instr machinstr(ulong addr); +extern void machbreakset(ulong addr); +extern void machbreakclear(ulong addr, Instr i); +extern ulong machnextaddr(Ureg *ur); +void* malloc(ulong); +void* mallocz(ulong, int); +Block* mem2bl(uchar*, int); +int memusehigh(void); +void microdelay(int); +uvlong mk64fract(uvlong, uvlong); +void mkqid(Qid*, vlong, ulong, int); +void modinit(void); +Chan* mntauth(Chan*, char*); +long mntversion(Chan*, char*, int, int); +void mountfree(Mount*); +void mousetrack(int, int, int, int); +uvlong ms2fastticks(ulong); +ulong msize(void*); +void mul64fract(uvlong*, uvlong, uvlong); +void muxclose(Mnt*); +Chan* namec(char*, int, int, ulong); +Chan* newchan(void); +Egrp* newegrp(void); +Fgrp* newfgrp(Fgrp*); +Mount* newmount(Mhead*, Chan*, int, char*); +Pgrp* newpgrp(void); +Proc* newproc(void); +char* nextelem(char*, char*); +void nexterror(void); +Cname* newcname(char*); +int notify(Ureg*); +void notkilled(void); +int nrand(int); +uvlong ns2fastticks(uvlong); +int okaddr(ulong, ulong, int); +int openmode(ulong); +Block* packblock(Block*); +Block* padblock(Block*, int); +void panic(char*, ...); +Cmdbuf* parsecmd(char*, int); +void pexit(char*, int); +void pgrpcpy(Pgrp*, Pgrp*); +#define poperror() up->nerrlab-- +int poolread(char*, int, ulong); +void poolsize(Pool *, int, int); +int postnote(Proc *, int, char *, int); +int pprint(char*, ...); +int preemption(int); +void printinit(void); +void procctl(Proc*); +void procdump(void); +void procinit(void); +Proc* proctab(int); +void (*proctrace)(Proc*, int, vlong); +int progfdprint(Chan*, int, int, char*, int); +int pullblock(Block**, int); +Block* pullupblock(Block*, int); +Block* pullupqueue(Queue*, int); +void putmhead(Mhead*); +void putstrn(char*, int); +void qaddlist(Queue*, Block*); +Block* qbread(Queue*, int); +long qbwrite(Queue*, Block*); +Queue* qbypass(void (*)(void*, Block*), void*); +int qcanread(Queue*); +void qclose(Queue*); +int qconsume(Queue*, void*, int); +Block* qcopy(Queue*, int, ulong); +int qdiscard(Queue*, int); +void qflush(Queue*); +void qfree(Queue*); +int qfull(Queue*); +Block* qget(Queue*); +void qhangup(Queue*, char*); +int qisclosed(Queue*); +int qiwrite(Queue*, void*, int); +int qlen(Queue*); +void qlock(QLock*); +void qnoblock(Queue*, int); +Queue* qopen(int, int, void (*)(void*), void*); +int qpass(Queue*, Block*); +int qpassnolim(Queue*, Block*); +int qproduce(Queue*, void*, int); +void qputback(Queue*, Block*); +long qread(Queue*, void*, int); +Block* qremove(Queue*); +void qreopen(Queue*); +void qsetlimit(Queue*, int); +void qunlock(QLock*); +int qwindow(Queue*); +int qwrite(Queue*, void*, int); +void randominit(void); +ulong randomread(void*, ulong); +void* realloc(void*, ulong); +int readnum(ulong, char*, ulong, ulong, int); +int readnum_vlong(ulong, char*, ulong, vlong, int); +int readstr(ulong, char*, ulong, char*); +void ready(Proc*); +void renameproguser(char*, char*); +void renameuser(char*, char*); +void resrcwait(char*); +int return0(void*); +void rlock(RWlock*); +void runlock(RWlock*); +Proc* runproc(void); +void sched(void); +void schedinit(void); +long seconds(void); +void (*serwrite)(char*, int); +int setcolor(ulong, ulong, ulong, ulong); +int setlabel(Label*); +void setmalloctag(void*, ulong); +int setpri(int); +void setrealloctag(void*, ulong); +char* skipslash(char*); +void sleep(Rendez*, int(*)(void*), void*); +void* smalloc(ulong); +int splhi(void); +int spllo(void); +void splx(int); +void splxpc(int); +void swiproc(Proc*, int); +ulong _tas(ulong*); +void timeradd(Timer*); +void timerdel(Timer*); +void timersinit(void); +void timerintr(Ureg*, uvlong); +void timerset(uvlong); +ulong tk2ms(ulong); +#define TK2MS(x) ((x)*(1000/HZ)) +uvlong tod2fastticks(vlong); +vlong todget(vlong*); +void todfix(void); +void todsetfreq(vlong); +void todinit(void); +void todset(vlong, vlong, int); +int tready(void*); +Block* trimblock(Block*, int, int); +void tsleep(Rendez*, int (*)(void*), void*, int); +int uartgetc(void); +void uartputc(int); +void uartputs(char*, int); +long unionread(Chan*, void*, long); +void unlock(Lock*); +void userinit(void); +ulong userpc(void); +void validname(char*, int); +void validstat(uchar*, int); +void validwstatname(char*); +int wakeup(Rendez*); +int walk(Chan**, char**, int, int, int*); +void werrstr(char*, ...); +void wlock(RWlock*); +void wunlock(RWlock*); +void* xalloc(ulong); +void* xallocz(ulong, int); +void xfree(void*); +void xhole(ulong, ulong); +void xinit(void); +int xmerge(void*, void*); +void* xspanalloc(ulong, int, ulong); +void xsummary(void); + +void validaddr(void*, ulong, int); +void* vmemchr(void*, int, int); +void hnputv(void*, vlong); +void hnputl(void*, ulong); +void hnputs(void*, ushort); +vlong nhgetv(void*); +ulong nhgetl(void*); +ushort nhgets(void*); diff --git a/os/port/portmkfile b/os/port/portmkfile new file mode 100644 index 00000000..9ee9a7f7 --- /dev/null +++ b/os/port/portmkfile @@ -0,0 +1,152 @@ +PORTHFILES=\ + ../port/error.h\ + ../port/lib.h\ + ../port/portdat.h\ + ../port/portfns.h\ + +LIBFILES=${LIBS:%=$ROOT/Inferno/$OBJTYPE/lib/lib%.a} + +CLEANEXTRA= + +%.$O: %.s + $AS $stem.s + +%.$O: %.c + $CC $CFLAGS $stem.c + +%.$O: ../port/%.c + $CC $CFLAGS -I. ../port/$stem.c + +%.$O: ../ip/%.c + $CC $CFLAGS -I. ../ip/$stem.c + +%.$O: ../kfs/%.c + $CC $CFLAGS -I. ../kfs/$stem.c + +&.$O: $HFILES $PORTHFILES + +$INSTALLDIR/%: % + cp $stem $INSTALLDIR/$stem + +installall:V: install-$SHELLTYPE +all:V: default-$SHELLTYPE + +acid:V: i$CONF.acid +i$CONF.acid:V: $SHELLTYPE-i$CONF.acid + +LIBHDIRS= -I$ROOT/libmp/port -I$ROOT/libsec/port + + +rc-i$CONF.acid nt-i$CONF.acid:V: i$CONF + { + x=i$CONF; test -e i$CONF.p9 && x=i$CONF.p9 + for (i in `{srclist -ec -r $ROOT/ $x}) { + echo '//FILE: ' $i + $CC -I. $CFLAGS $LIBHDIRS '-DKERNDATE='$KERNDATE -a $i + } + echo 'include ("inferno");' + } >i$CONF.acid + +sh-i$CONF.acid:V: i$CONF + x=i$CONF; test -e i$CONF.p9 && x=i$CONF.p9 + for i in `srclist -ec -r $ROOT/ $x` + do + echo '//FILE: ' $i + $CC -I. $CFLAGS $LIBHDIRS '-DKERNDATE='$KERNDATE -a $i + done >i$CONF.acid + echo 'include ("inferno");' >> i$CONF.acid + +lib%.a:V: $SHELLTYPE-lib%.a + +rc-lib%.a nt-lib%.a:VQ: + echo '@{builtin cd' $ROOT/lib$stem ';' mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install'}' + @{builtin cd $ROOT/lib$stem; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install} + +sh-lib%.a:VQ: + echo "(cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install)" + (cd $ROOT/lib$stem; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install) + +%-rc %-nt:V: + for(i in $CONFLIST) + mk 'CONF='$i $stem + +%-sh:V: + for i in $CONFLIST + do + mk 'CONF='$i $stem + done + +clean:V: cleanconf-$SHELLTYPE + rm -f *.[$OS] *.root.[sh] errstr.h *.out $CLEANEXTRA + +cleanconf-sh:V: + for i in $CONFLIST $CLEANCONFLIST + do + rm -f $i.c i$i i$i.* $i.ver + done + +cleanconf-rc cleanconf-nt:V: + for(i in $CONFLIST $CLEANCONFLIST) + rm -f $i.c i$i i$i.* $i.ver + +nuke-sh:QV: + for i in $LIBDIRS + do + echo "(cd $ROOT/lib$i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE nuke)" + (cd $ROOT/lib$i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE nuke) + done + +nuke-rc nuke-nt:QV: + for (i in $LIBDIRS) + { + echo '@{cd $ROOT/lib$i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE nuke}' + @{cd $ROOT/lib$i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE nuke} + } + +nuke:V: clean nuke-$SHELLTYPE + +$CONF.c: ../port/mkdevc $CONF + $SHELLNAME ../port/mkdevc $CONF > $CONF.c + +errstr.h: ../port/error.h + sed 's/extern //;s,;.*/\* , = ",;s, \*/,";,' < ../port/error.h > errstr.h + +../init/%.dis: ../init/%.b + cd ../init; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE $stem.dis + +$ROOT/libinterp/runt.h: + cd $ROOT/libinterp + mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE runt.h + +RUNT=$ROOT/libinterp/runt.h # for culling dependencies +INTERP=$ROOT/include/interp.h + +alloc.$O: $INTERP +devdbg.$O: $INTERP + +devmnt.$O: $ROOT/include/fcall.h +devns16552.$O: ../port/netif.h +devns16552.$O: ns16552.h +devpipe.$O: $INTERP +devprof.$O: $RUNT $INTERP +devprog.$O: $RUNT $INTERP +devroot.$O: errstr.h +devsign.$O: $RUNT $INTERP +devsrv.$O: $RUNT $INTERP +dis.$O: $INTERP +discall.$O: $INTERP +exception.$O: $RUNT $INTERP +inferno.$O: $RUNT $INTERP +latin1.$O: ../port/latin1.h +main.$O: ../port/error.h +netif.$O: ../port/netif.h +proc.$O: errstr.h $INTERP +screen.$O: screen.h +trap.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h + +devroot.$O: $CONF.root.h +$CONF.$O: $CONF.root.h +$CONF.root.s $CONF.root.h: $CONF ../init/$INIT.dis ../port/mkroot $ROOTFILES + $SHELLNAME ../port/mkroot $CONF + +%.$O: $ROOT/Inferno/$OBJTYPE/include/u.h ../port/lib.h mem.h dat.h fns.h io.h ../port/error.h ../port/portdat.h ../port/portfns.h diff --git a/os/port/print.c b/os/port/print.c new file mode 100644 index 00000000..8a7a86fd --- /dev/null +++ b/os/port/print.c @@ -0,0 +1,31 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +static Lock fmtl; + +void +_fmtlock(void) +{ + lock(&fmtl); +} + +void +_fmtunlock(void) +{ + unlock(&fmtl); +} + +int +_efgfmt(Fmt*) +{ + return -1; +} + +int +errfmt(Fmt*) +{ + return -1; +} diff --git a/os/port/proc.c b/os/port/proc.c new file mode 100644 index 00000000..a652669a --- /dev/null +++ b/os/port/proc.c @@ -0,0 +1,788 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include + +Ref pidalloc; + +struct +{ + Lock; + Proc* arena; + Proc* free; +}procalloc; + +typedef struct +{ + Lock; + Proc* head; + Proc* tail; +}Schedq; + +static Schedq runq[Nrq]; +static ulong occupied; +int nrdy; + +char *statename[] = +{ /* BUG: generate automatically */ + "Dead", + "Moribund", + "Ready", + "Scheding", + "Running", + "Queueing", + "Wakeme", + "Broken", + "Stopped", + "Rendez", +}; + +/* + * Always splhi()'ed. + */ +void +schedinit(void) /* never returns */ +{ + setlabel(&m->sched); + if(up) { +/* + if((e = up->edf) && (e->flags & Admitted)) + edfrecord(up); +*/ + m->proc = nil; + switch(up->state) { + case Running: + ready(up); + break; + case Moribund: + up->state = Dead; +/* + edfstop(up); + if(up->edf){ + free(up->edf); + up->edf = nil; + } +*/ + /* + * Holding locks from pexit: + * procalloc + */ + up->qnext = procalloc.free; + procalloc.free = up; + unlock(&procalloc); + break; + } + up->mach = nil; + up = nil; + } + sched(); +} + +void +sched(void) +{ + if(up) { + splhi(); + procsave(up); + if(setlabel(&up->sched)) { + /* procrestore(up); */ + spllo(); + return; + } + gotolabel(&m->sched); + } + up = runproc(); + up->state = Running; + up->mach = MACHP(m->machno); /* m might be a fixed address; use MACHP */ + m->proc = up; + gotolabel(&up->sched); +} + +void +ready(Proc *p) +{ + int s; + Schedq *rq; + + s = splhi(); +/* + if(edfready(p)){ + splx(s); + return; + } +*/ + rq = &runq[p->pri]; + lock(runq); + p->rnext = 0; + if(rq->tail) + rq->tail->rnext = p; + else + rq->head = p; + rq->tail = p; + + nrdy++; + occupied |= 1<pri; + p->state = Ready; + unlock(runq); + splx(s); +} + +int +anyready(void) +{ + /* same priority only */ + return occupied & (1<pri); +} + +int +anyhigher(void) +{ + return occupied & ((1<pri)-1); +} + +int +preemption(int tick) +{ + if(up != nil && up->state == Running && !up->preempted && + (anyhigher() || tick && anyready())){ + up->preempted = 1; + sched(); + splhi(); + up->preempted = 0; + return 1; + } + return 0; +} + +Proc* +runproc(void) +{ + Proc *p, *l; + Schedq *rq, *erq; + + erq = runq + Nrq - 1; +loop: + splhi(); + for(rq = runq; rq->head == 0; rq++) + if(rq >= erq) { + idlehands(); + spllo(); + goto loop; + } + + if(!canlock(runq)) + goto loop; + /* choose first one we last ran on this processor at this level or hasn't moved recently */ + l = nil; + for(p = rq->head; p != nil; p = p->rnext) + if(p->mp == nil || p->mp == MACHP(m->machno) || p->movetime < MACHP(0)->ticks) + break; + if(p == nil) + p = rq->head; + /* p->mach==0 only when process state is saved */ + if(p == 0 || p->mach) { + unlock(runq); + goto loop; + } + if(p->rnext == nil) + rq->tail = l; + if(l) + l->rnext = p->rnext; + else + rq->head = p->rnext; + if(rq->head == nil){ + rq->tail = nil; + occupied &= ~(1<pri); + } + nrdy--; + if(p->dbgstop){ + p->state = Stopped; + unlock(runq); + goto loop; + } + if(p->state != Ready) + print("runproc %s %lud %s\n", p->text, p->pid, statename[p->state]); + unlock(runq); + p->state = Scheding; + if(p->mp != MACHP(m->machno)) + p->movetime = MACHP(0)->ticks + HZ/10; + p->mp = MACHP(m->machno); + +/* + if(edflock(p)){ + edfrun(p, rq == &runq[PriEdf]); // start deadline timer and do admin + edfunlock(); + } +*/ + return p; +} + +int +setpri(int pri) +{ + int p; + + /* called by up so not on run queue */ + p = up->pri; + up->pri = pri; + if(up->state == Running && anyhigher()) + sched(); + return p; +} + +Proc* +newproc(void) +{ + Proc *p; + + lock(&procalloc); + for(;;) { + if(p = procalloc.free) + break; + + unlock(&procalloc); + resrcwait("no procs"); + lock(&procalloc); + } + procalloc.free = p->qnext; + unlock(&procalloc); + + p->type = Unknown; + p->state = Scheding; + p->pri = PriNormal; + p->psstate = "New"; + p->mach = 0; + p->qnext = 0; + p->fpstate = FPINIT; + p->kp = 0; + p->killed = 0; + p->swipend = 0; + p->mp = 0; + p->movetime = 0; + p->delaysched = 0; + p->edf = nil; + memset(&p->defenv, 0, sizeof(p->defenv)); + p->env = &p->defenv; + p->dbgreg = 0; + kstrdup(&p->env->user, "*nouser"); + p->env->errstr = p->env->errbuf0; + p->env->syserrstr = p->env->errbuf1; + + p->pid = incref(&pidalloc); + if(p->pid == 0) + panic("pidalloc"); + if(p->kstack == 0) + p->kstack = smalloc(KSTACK); + addprog(p); + + return p; +} + +void +procinit(void) +{ + Proc *p; + int i; + + procalloc.free = xalloc(conf.nproc*sizeof(Proc)); + procalloc.arena = procalloc.free; + + p = procalloc.free; + for(i=0; iqnext = p+1; + p->qnext = 0; + + debugkey('p', "processes", procdump, 0); +} + +void +sleep(Rendez *r, int (*f)(void*), void *arg) +{ + int s; + + if(up == nil) + panic("sleep() not in process (%lux)", getcallerpc(&r)); + /* + * spl is to allow lock to be called + * at interrupt time. lock is mutual exclusion + */ + s = splhi(); + + lock(&up->rlock); + lock(r); + + /* + * if killed or condition happened, never mind + */ + if(up->killed || f(arg)){ + unlock(r); + }else{ + + /* + * now we are committed to + * change state and call scheduler + */ + if(r->p != nil) { + print("double sleep pc=0x%lux %lud %lud r=0x%lux\n", getcallerpc(&r), r->p->pid, up->pid, r); + dumpstack(); + panic("sleep"); + } + up->state = Wakeme; + r->p = up; + unlock(r); + up->swipend = 0; + up->r = r; /* for swiproc */ + unlock(&up->rlock); + + sched(); + splhi(); /* sched does spllo */ + + lock(&up->rlock); + up->r = nil; + } + + if(up->killed || up->swipend) { + up->killed = 0; + up->swipend = 0; + unlock(&up->rlock); + splx(s); + error(Eintr); + } + unlock(&up->rlock); + splx(s); +} + +int +tfn(void *arg) +{ + return MACHP(0)->ticks >= up->twhen || (*up->tfn)(arg); +} + +void +tsleep(Rendez *r, int (*fn)(void*), void *arg, int ms) +{ + ulong when; + Proc *f, **l; + + if(up == nil) + panic("tsleep() not in process (0x%lux)", getcallerpc(&r)); + + when = MS2TK(ms)+MACHP(0)->ticks; + lock(&talarm); + /* take out of list if checkalarm didn't */ + if(up->trend) { + l = &talarm.list; + for(f = *l; f; f = f->tlink) { + if(f == up) { + *l = up->tlink; + break; + } + l = &f->tlink; + } + } + /* insert in increasing time order */ + l = &talarm.list; + for(f = *l; f; f = f->tlink) { + if(f->twhen >= when) + break; + l = &f->tlink; + } + up->trend = r; + up->twhen = when; + up->tfn = fn; + up->tlink = *l; + *l = up; + unlock(&talarm); + + if(waserror()){ + up->twhen = 0; + nexterror(); + } + sleep(r, tfn, arg); + up->twhen = 0; + poperror(); +} + +int +wakeup(Rendez *r) +{ + Proc *p; + int s; + + s = splhi(); + lock(r); + p = r->p; + if(p){ + r->p = nil; + if(p->state != Wakeme) + panic("wakeup: state"); + ready(p); + } + unlock(r); + splx(s); + return p != nil; +} + +void +swiproc(Proc *p, int interp) +{ + ulong s; + Rendez *r; + + if(p == nil) + return; + + s = splhi(); + lock(&p->rlock); + if(!interp) + p->killed = 1; + r = p->r; + if(r != nil) { + lock(r); + if(r->p == p){ + p->swipend = 1; + r->p = nil; + ready(p); + } + unlock(r); + } + unlock(&p->rlock); + splx(s); +} + +void +notkilled(void) +{ + lock(&up->rlock); + up->killed = 0; + unlock(&up->rlock); +} + +void +pexit(char*, int) +{ + Osenv *o; + + up->alarm = 0; + + o = up->env; + if(o != nil){ + closefgrp(o->fgrp); + closepgrp(o->pgrp); + closeegrp(o->egrp); + closesigs(o->sigs); + } + + /* Sched must not loop for this lock */ + lock(&procalloc); + +/* + edfstop(up); +*/ + up->state = Moribund; + sched(); + panic("pexit"); +} + +Proc* +proctab(int i) +{ + return &procalloc.arena[i]; +} + +void +procdump(void) +{ + int i; + char *s; + Proc *p; + char tmp[14]; + + for(i=0; istate == Dead) + continue; + + s = p->psstate; + if(s == nil) + s = "kproc"; + if(p->state == Wakeme) + snprint(tmp, sizeof(tmp), " /%.8lux", p->r); + else + *tmp = '\0'; + print("%lux:%3lud:%14s pc %.8lux %s/%s qpc %.8lux pri %d%s\n", + p, p->pid, p->text, p->pc, s, statename[p->state], p->qpc, p->pri, tmp); + } +} + +void +kproc(char *name, void (*func)(void *), void *arg, int flags) +{ + Proc *p; + Pgrp *pg; + Fgrp *fg; + Egrp *eg; + + p = newproc(); + p->psstate = 0; + p->kp = 1; + + p->fpsave = up->fpsave; + p->scallnr = up->scallnr; + p->nerrlab = 0; + + kstrdup(&p->env->user, up->env->user); + if(flags & KPDUPPG) { + pg = up->env->pgrp; + incref(pg); + p->env->pgrp = pg; + } + if(flags & KPDUPFDG) { + fg = up->env->fgrp; + incref(fg); + p->env->fgrp = fg; + } + if(flags & KPDUPENVG) { + eg = up->env->egrp; + if(eg != nil) + incref(eg); + p->env->egrp = eg; + } + + kprocchild(p, func, arg); + + strcpy(p->text, name); + + ready(p); +} + +void +errorf(char *fmt, ...) +{ + va_list arg; + char buf[PRINTSIZE]; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + error(buf); +} + +void +error(char *err) +{ + if(up == nil) + panic("error(%s) not in a process", err); + spllo(); + if(up->nerrlab > NERR) + panic("error stack too deep"); + if(err != up->env->errstr) + kstrcpy(up->env->errstr, err, ERRMAX); + setlabel(&up->errlab[NERR-1]); + nexterror(); +} + +#include "errstr.h" + +/* Set kernel error string */ +void +kerrstr(char *err, uint size) +{ + + char tmp[ERRMAX]; + + kstrcpy(tmp, up->env->errstr, sizeof(tmp)); + kstrcpy(up->env->errstr, err, ERRMAX); + kstrcpy(err, tmp, size); +} + +/* Get kernel error string */ +void +kgerrstr(char *err, uint size) +{ + char tmp[ERRMAX]; + + kstrcpy(tmp, up->env->errstr, sizeof(tmp)); + kstrcpy(up->env->errstr, err, ERRMAX); + kstrcpy(err, tmp, size); +} + +/* Set kernel error string, using formatted print */ +void +kwerrstr(char *fmt, ...) +{ + va_list arg; + char buf[ERRMAX]; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + kstrcpy(up->env->errstr, buf, ERRMAX); +} + +void +werrstr(char *fmt, ...) +{ + va_list arg; + char buf[ERRMAX]; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + kstrcpy(up->env->errstr, buf, ERRMAX); +} + +void +nexterror(void) +{ + gotolabel(&up->errlab[--up->nerrlab]); +} + +/* for dynamic modules - functions not macros */ + +void* +waserr(void) +{ + up->nerrlab++; + return &up->errlab[up->nerrlab-1]; +} + +void +poperr(void) +{ + up->nerrlab--; +} + +char* +enverror(void) +{ + return up->env->errstr; +} + +void +exhausted(char *resource) +{ + char buf[64]; + + snprint(buf, sizeof(buf), "no free %s", resource); + iprint("%s\n", buf); + error(buf); +} + +/* + * change ownership to 'new' of all processes owned by 'old'. Used when + * eve changes. + */ +void +renameuser(char *old, char *new) +{ + Proc *p, *ep; + Osenv *o; + + ep = procalloc.arena+conf.nproc; + for(p = procalloc.arena; p < ep; p++) { + o = &p->defenv; + if(o->user != nil && strcmp(o->user, old) == 0) + kstrdup(&o->user, new); + } +} + +int +return0(void*) +{ + return 0; +} + +void +setid(char *name, int owner) +{ + if(!owner || iseve()) + kstrdup(&up->env->user, name); +} + +void +rptwakeup(void *o, void *ar) +{ + Rept *r; + + r = ar; + if(r == nil) + return; + lock(&r->l); + r->o = o; + unlock(&r->l); + wakeup(&r->r); +} + +static int +rptactive(void *a) +{ + Rept *r = a; + int i; + lock(&r->l); + i = r->active(r->o); + unlock(&r->l); + return i; +} + +static void +rproc(void *a) +{ + long now, then; + ulong t; + int i; + void *o; + Rept *r; + + r = a; + t = r->t; + +Wait: + sleep(&r->r, rptactive, r); + lock(&r->l); + o = r->o; + unlock(&r->l); + then = TK2MS(MACHP(0)->ticks); + for(;;){ + tsleep(&up->sleep, return0, nil, t); + now = TK2MS(MACHP(0)->ticks); + if(waserror()) + break; + i = r->ck(o, now-then); + poperror(); + if(i == -1) + goto Wait; + if(i == 0) + continue; + then = now; + acquire(); + if(waserror()) { + release(); + break; + } + r->f(o); + poperror(); + release(); + } + pexit("", 0); +} + +void* +rptproc(char *s, int t, void *o, int (*active)(void*), int (*ck)(void*, int), void (*f)(void*)) +{ + Rept *r; + + r = mallocz(sizeof(Rept), 1); + if(r == nil) + return nil; + r->t = t; + r->active = active; + r->ck = ck; + r->f = f; + r->o = o; + kproc(s, rproc, r, KPDUP); + return r; +} diff --git a/os/port/qio.c b/os/port/qio.c new file mode 100644 index 00000000..d44ea6b8 --- /dev/null +++ b/os/port/qio.c @@ -0,0 +1,1529 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +static ulong padblockcnt; +static ulong concatblockcnt; +static ulong pullupblockcnt; +static ulong copyblockcnt; +static ulong consumecnt; +static ulong producecnt; +static ulong qcopycnt; + +static int debugging; + +#define QDEBUG if(0) + +/* + * IO queues + */ +typedef struct Queue Queue; + +struct Queue +{ + Lock; + + Block* bfirst; /* buffer */ + Block* blast; + + int len; /* bytes allocated to queue */ + int dlen; /* data bytes in queue */ + int limit; /* max bytes in queue */ + int inilim; /* initial limit */ + int state; + int noblock; /* true if writes return immediately when q full */ + int eof; /* number of eofs read by user */ + + void (*kick)(void*); /* restart output */ + void (*bypass)(void*, Block*); /* bypass queue altogether */ + void* arg; /* argument to kick */ + + QLock rlock; /* mutex for reading processes */ + Rendez rr; /* process waiting to read */ + QLock wlock; /* mutex for writing processes */ + Rendez wr; /* process waiting to write */ + + char err[ERRMAX]; +}; + +enum +{ + Maxatomic = 64*1024, +}; + +uint qiomaxatomic = Maxatomic; + +void +ixsummary(void) +{ + debugging ^= 1; + iallocsummary(); + print("pad %lud, concat %lud, pullup %lud, copy %lud\n", + padblockcnt, concatblockcnt, pullupblockcnt, copyblockcnt); + print("consume %lud, produce %lud, qcopy %lud\n", + consumecnt, producecnt, qcopycnt); +} + +/* + * free a list of blocks + */ +void +freeblist(Block *b) +{ + Block *next; + + for(; b != 0; b = next){ + next = b->next; + b->next = 0; + freeb(b); + } +} + +/* + * pad a block to the front (or the back if size is negative) + */ +Block* +padblock(Block *bp, int size) +{ + int n; + Block *nbp; + + QDEBUG checkb(bp, "padblock 1"); + if(size >= 0){ + if(bp->rp - bp->base >= size){ + bp->rp -= size; + return bp; + } + + if(bp->next) + panic("padblock 0x%luX", getcallerpc(&bp)); + n = BLEN(bp); + padblockcnt++; + nbp = allocb(size+n); + nbp->rp += size; + nbp->wp = nbp->rp; + memmove(nbp->wp, bp->rp, n); + nbp->wp += n; + freeb(bp); + nbp->rp -= size; + } else { + size = -size; + + if(bp->next) + panic("padblock 0x%luX", getcallerpc(&bp)); + + if(bp->lim - bp->wp >= size) + return bp; + + n = BLEN(bp); + padblockcnt++; + nbp = allocb(size+n); + memmove(nbp->wp, bp->rp, n); + nbp->wp += n; + freeb(bp); + } + QDEBUG checkb(nbp, "padblock 1"); + return nbp; +} + +/* + * return count of bytes in a string of blocks + */ +int +blocklen(Block *bp) +{ + int len; + + len = 0; + while(bp) { + len += BLEN(bp); + bp = bp->next; + } + return len; +} + +/* + * return count of space in blocks + */ +int +blockalloclen(Block *bp) +{ + int len; + + len = 0; + while(bp) { + len += BALLOC(bp); + bp = bp->next; + } + return len; +} + +/* + * copy the string of blocks into + * a single block and free the string + */ +Block* +concatblock(Block *bp) +{ + int len; + Block *nb, *f; + + if(bp->next == 0) + return bp; + + nb = allocb(blocklen(bp)); + for(f = bp; f; f = f->next) { + len = BLEN(f); + memmove(nb->wp, f->rp, len); + nb->wp += len; + } + concatblockcnt += BLEN(nb); + freeblist(bp); + QDEBUG checkb(nb, "concatblock 1"); + return nb; +} + +/* + * make sure the first block has at least n bytes + */ +Block* +pullupblock(Block *bp, int n) +{ + int i; + Block *nbp; + + /* + * this should almost always be true, it's + * just to avoid every caller checking. + */ + if(BLEN(bp) >= n) + return bp; + + /* + * if not enough room in the first block, + * add another to the front of the list. + */ + if(bp->lim - bp->rp < n){ + nbp = allocb(n); + nbp->next = bp; + bp = nbp; + } + + /* + * copy bytes from the trailing blocks into the first + */ + n -= BLEN(bp); + while(nbp = bp->next){ + i = BLEN(nbp); + if(i > n) { + memmove(bp->wp, nbp->rp, n); + pullupblockcnt++; + bp->wp += n; + nbp->rp += n; + QDEBUG checkb(bp, "pullupblock 1"); + return bp; + } + else { + memmove(bp->wp, nbp->rp, i); + pullupblockcnt++; + bp->wp += i; + bp->next = nbp->next; + nbp->next = 0; + freeb(nbp); + n -= i; + if(n == 0){ + QDEBUG checkb(bp, "pullupblock 2"); + return bp; + } + } + } + freeb(bp); + return 0; +} + +/* + * make sure the first block has at least n bytes + */ +Block* +pullupqueue(Queue *q, int n) +{ + Block *b; + + if(BLEN(q->bfirst) >= n) + return q->bfirst; + q->bfirst = pullupblock(q->bfirst, n); + for(b = q->bfirst; b != nil && b->next != nil; b = b->next) + ; + q->blast = b; + return q->bfirst; +} + +/* + * trim to len bytes starting at offset + */ +Block * +trimblock(Block *bp, int offset, int len) +{ + ulong l; + Block *nb, *startb; + + QDEBUG checkb(bp, "trimblock 1"); + if(blocklen(bp) < offset+len) { + freeblist(bp); + return nil; + } + + while((l = BLEN(bp)) < offset) { + offset -= l; + nb = bp->next; + bp->next = nil; + freeb(bp); + bp = nb; + } + + startb = bp; + bp->rp += offset; + + while((l = BLEN(bp)) < len) { + len -= l; + bp = bp->next; + } + + bp->wp -= (BLEN(bp) - len); + + if(bp->next) { + freeblist(bp->next); + bp->next = nil; + } + + return startb; +} + +/* + * copy 'count' bytes into a new block + */ +Block* +copyblock(Block *bp, int count) +{ + int l; + Block *nbp; + + QDEBUG checkb(bp, "copyblock 0"); + nbp = allocb(count); + for(; count > 0 && bp != 0; bp = bp->next){ + l = BLEN(bp); + if(l > count) + l = count; + memmove(nbp->wp, bp->rp, l); + nbp->wp += l; + count -= l; + } + if(count > 0){ + memset(nbp->wp, 0, count); + nbp->wp += count; + } + copyblockcnt++; + QDEBUG checkb(nbp, "copyblock 1"); + + return nbp; +} + +Block* +adjustblock(Block* bp, int len) +{ + int n; + Block *nbp; + + if(len < 0){ + freeb(bp); + return nil; + } + + if(bp->rp+len > bp->lim){ + nbp = copyblock(bp, len); + freeblist(bp); + QDEBUG checkb(nbp, "adjustblock 1"); + + return nbp; + } + + n = BLEN(bp); + if(len > n) + memset(bp->wp, 0, len-n); + bp->wp = bp->rp+len; + QDEBUG checkb(bp, "adjustblock 2"); + + return bp; +} + + +/* + * throw away up to count bytes from a + * list of blocks. Return count of bytes + * thrown away. + */ +int +pullblock(Block **bph, int count) +{ + Block *bp; + int n, bytes; + + bytes = 0; + if(bph == nil) + return 0; + + while(*bph != nil && count != 0) { + bp = *bph; + n = BLEN(bp); + if(count < n) + n = count; + bytes += n; + count -= n; + bp->rp += n; + QDEBUG checkb(bp, "pullblock "); + if(BLEN(bp) == 0) { + *bph = bp->next; + bp->next = nil; + freeb(bp); + } + } + return bytes; +} + +/* + * get next block from a queue, return null if nothing there + */ +Block* +qget(Queue *q) +{ + int dowakeup; + Block *b; + + /* sync with qwrite */ + ilock(q); + + b = q->bfirst; + if(b == nil){ + q->state |= Qstarve; + iunlock(q); + return nil; + } + q->bfirst = b->next; + b->next = 0; + q->len -= BALLOC(b); + q->dlen -= BLEN(b); + QDEBUG checkb(b, "qget"); + + /* if writer flow controlled, restart */ + if((q->state & Qflow) && q->len < q->limit/2){ + q->state &= ~Qflow; + dowakeup = 1; + } else + dowakeup = 0; + + iunlock(q); + + if(dowakeup) + wakeup(&q->wr); + + return b; +} + +/* + * throw away the next 'len' bytes in the queue + * returning the number actually discarded + */ +int +qdiscard(Queue *q, int len) +{ + Block *b; + int dowakeup, n, sofar; + + ilock(q); + for(sofar = 0; sofar < len; sofar += n){ + b = q->bfirst; + if(b == nil) + break; + QDEBUG checkb(b, "qdiscard"); + n = BLEN(b); + if(n <= len - sofar){ + q->bfirst = b->next; + b->next = 0; + q->len -= BALLOC(b); + q->dlen -= BLEN(b); + freeb(b); + } else { + n = len - sofar; + b->rp += n; + q->dlen -= n; + } + } + + /* + * if writer flow controlled, restart + * + * This used to be + * q->len < q->limit/2 + * but it slows down tcp too much for certain write sizes. + * I really don't understand it completely. It may be + * due to the queue draining so fast that the transmission + * stalls waiting for the app to produce more data. - presotto + */ + if((q->state & Qflow) && q->len < q->limit){ + q->state &= ~Qflow; + dowakeup = 1; + } else + dowakeup = 0; + + iunlock(q); + + if(dowakeup) + wakeup(&q->wr); + + return sofar; +} + +/* + * Interrupt level copy out of a queue, return # bytes copied. + */ +int +qconsume(Queue *q, void *vp, int len) +{ + Block *b; + int n, dowakeup; + uchar *p = vp; + Block *tofree = nil; + + /* sync with qwrite */ + ilock(q); + + for(;;) { + b = q->bfirst; + if(b == 0){ + q->state |= Qstarve; + iunlock(q); + return -1; + } + QDEBUG checkb(b, "qconsume 1"); + + n = BLEN(b); + if(n > 0) + break; + q->bfirst = b->next; + q->len -= BALLOC(b); + + /* remember to free this */ + b->next = tofree; + tofree = b; + }; + + if(n < len) + len = n; + memmove(p, b->rp, len); + consumecnt += n; + b->rp += len; + q->dlen -= len; + + /* discard the block if we're done with it */ + if((q->state & Qmsg) || len == n){ + q->bfirst = b->next; + b->next = 0; + q->len -= BALLOC(b); + q->dlen -= BLEN(b); + + /* remember to free this */ + b->next = tofree; + tofree = b; + } + + /* if writer flow controlled, restart */ + if((q->state & Qflow) && q->len < q->limit/2){ + q->state &= ~Qflow; + dowakeup = 1; + } else + dowakeup = 0; + + iunlock(q); + + if(dowakeup) + wakeup(&q->wr); + + if(tofree != nil) + freeblist(tofree); + + return len; +} + +int +qpass(Queue *q, Block *b) +{ + int dlen, len, dowakeup; + + /* sync with qread */ + dowakeup = 0; + ilock(q); + if(q->len >= q->limit){ + freeblist(b); + iunlock(q); + return -1; + } + if(q->state & Qclosed){ + len = blocklen(b); + freeblist(b); + iunlock(q); + return len; + } + + /* add buffer to queue */ + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + len = BALLOC(b); + dlen = BLEN(b); + QDEBUG checkb(b, "qpass"); + while(b->next){ + b = b->next; + QDEBUG checkb(b, "qpass"); + len += BALLOC(b); + dlen += BLEN(b); + } + q->blast = b; + q->len += len; + q->dlen += dlen; + + if(q->len >= q->limit/2) + q->state |= Qflow; + + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + iunlock(q); + + if(dowakeup) + wakeup(&q->rr); + + return len; +} + +int +qpassnolim(Queue *q, Block *b) +{ + int dlen, len, dowakeup; + + /* sync with qread */ + dowakeup = 0; + ilock(q); + + if(q->state & Qclosed){ + freeblist(b); + iunlock(q); + return BALLOC(b); + } + + /* add buffer to queue */ + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + len = BALLOC(b); + dlen = BLEN(b); + QDEBUG checkb(b, "qpass"); + while(b->next){ + b = b->next; + QDEBUG checkb(b, "qpass"); + len += BALLOC(b); + dlen += BLEN(b); + } + q->blast = b; + q->len += len; + q->dlen += dlen; + + if(q->len >= q->limit/2) + q->state |= Qflow; + + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + iunlock(q); + + if(dowakeup) + wakeup(&q->rr); + + return len; +} + +/* + * if the allocated space is way out of line with the used + * space, reallocate to a smaller block + */ +Block* +packblock(Block *bp) +{ + Block **l, *nbp; + int n; + + for(l = &bp; *l; l = &(*l)->next){ + nbp = *l; + n = BLEN(nbp); + if((n<<2) < BALLOC(nbp)){ + *l = allocb(n); + memmove((*l)->wp, nbp->rp, n); + (*l)->wp += n; + (*l)->next = nbp->next; + freeb(nbp); + } + } + + return bp; +} + +int +qproduce(Queue *q, void *vp, int len) +{ + Block *b; + int dowakeup; + uchar *p = vp; + + /* sync with qread */ + dowakeup = 0; + ilock(q); + + /* no waiting receivers, room in buffer? */ + if(q->len >= q->limit){ + q->state |= Qflow; + iunlock(q); + return -1; + } + + /* save in buffer */ + /* use Qcoalesce here to save storage */ + b = q->blast; + if((q->state & Qcoalesce)==0 || q->bfirst==nil || b->lim-b->wp < len){ + /* need a new block */ + b = iallocb(len); + if(b == 0){ + iunlock(q); + return 0; + } + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + q->blast = b; + /* b->next = 0; done by iallocb() */ + q->len += BALLOC(b); + } + memmove(b->wp, p, len); + producecnt += len; + b->wp += len; + q->dlen += len; + QDEBUG checkb(b, "qproduce"); + + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + + if(q->len >= q->limit) + q->state |= Qflow; + iunlock(q); + + if(dowakeup) + wakeup(&q->rr); + + return len; +} + +/* + * copy from offset in the queue + */ +Block* +qcopy(Queue *q, int len, ulong offset) +{ + int sofar; + int n; + Block *b, *nb; + uchar *p; + + nb = allocb(len); + + ilock(q); + + /* go to offset */ + b = q->bfirst; + for(sofar = 0; ; sofar += n){ + if(b == nil){ + iunlock(q); + return nb; + } + n = BLEN(b); + if(sofar + n > offset){ + p = b->rp + offset - sofar; + n -= offset - sofar; + break; + } + QDEBUG checkb(b, "qcopy"); + b = b->next; + } + + /* copy bytes from there */ + for(sofar = 0; sofar < len;){ + if(n > len - sofar) + n = len - sofar; + memmove(nb->wp, p, n); + qcopycnt += n; + sofar += n; + nb->wp += n; + b = b->next; + if(b == nil) + break; + n = BLEN(b); + p = b->rp; + } + iunlock(q); + + return nb; +} + +/* + * called by non-interrupt code + */ +Queue* +qopen(int limit, int msg, void (*kick)(void*), void *arg) +{ + Queue *q; + + q = malloc(sizeof(Queue)); + if(q == 0) + return 0; + + q->limit = q->inilim = limit; + q->kick = kick; + q->arg = arg; + q->state = msg; + q->state |= Qstarve; + q->eof = 0; + q->noblock = 0; + + return q; +} + +/* open a queue to be bypassed */ +Queue* +qbypass(void (*bypass)(void*, Block*), void *arg) +{ + Queue *q; + + q = malloc(sizeof(Queue)); + if(q == 0) + return 0; + + q->limit = 0; + q->arg = arg; + q->bypass = bypass; + q->state = 0; + + return q; +} + +static int +notempty(void *a) +{ + Queue *q = a; + + return (q->state & Qclosed) || q->bfirst != 0; +} + +/* + * wait for the queue to be non-empty or closed. + * called with q ilocked. + */ +static int +qwait(Queue *q) +{ + /* wait for data */ + for(;;){ + if(q->bfirst != nil) + break; + + if(q->state & Qclosed){ + if(++q->eof > 3) + return -1; + if(*q->err && strcmp(q->err, Ehungup) != 0) + return -1; + return 0; + } + + q->state |= Qstarve; /* flag requesting producer to wake me */ + iunlock(q); + sleep(&q->rr, notempty, q); + ilock(q); + } + return 1; +} + +/* + * add a block list to a queue + */ +void +qaddlist(Queue *q, Block *b) +{ + /* queue the block */ + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + q->len += blockalloclen(b); + q->dlen += blocklen(b); + while(b->next) + b = b->next; + q->blast = b; +} + +/* + * called with q ilocked + */ +Block* +qremove(Queue *q) +{ + Block *b; + + b = q->bfirst; + if(b == nil) + return nil; + q->bfirst = b->next; + b->next = nil; + q->dlen -= BLEN(b); + q->len -= BALLOC(b); + QDEBUG checkb(b, "qremove"); + return b; +} + +/* + * copy the contents of a string of blocks into + * memory. emptied blocks are freed. return + * pointer to first unconsumed block. + */ +Block* +bl2mem(uchar *p, Block *b, int n) +{ + int i; + Block *next; + + for(; b != nil; b = next){ + i = BLEN(b); + if(i > n){ + memmove(p, b->rp, n); + b->rp += n; + return b; + } + memmove(p, b->rp, i); + n -= i; + p += i; + b->rp += i; + next = b->next; + freeb(b); + } + return nil; +} + +/* + * copy the contents of memory into a string of blocks. + * return nil on error. + */ +Block* +mem2bl(uchar *p, int len) +{ + int n; + Block *b, *first, **l; + + first = nil; + l = &first; + if(waserror()){ + freeblist(first); + nexterror(); + } + do { + n = len; + if(n > Maxatomic) + n = Maxatomic; + + *l = b = allocb(n); + setmalloctag(b, getcallerpc(&p)); + memmove(b->wp, p, n); + b->wp += n; + p += n; + len -= n; + l = &b->next; + } while(len > 0); + poperror(); + + return first; +} + +/* + * put a block back to the front of the queue + * called with q ilocked + */ +void +qputback(Queue *q, Block *b) +{ + b->next = q->bfirst; + if(q->bfirst == nil) + q->blast = b; + q->bfirst = b; + q->len += BALLOC(b); + q->dlen += BLEN(b); +} + +/* + * flow control, get producer going again + * called with q ilocked + */ +static void +qwakeup_iunlock(Queue *q) +{ + int dowakeup = 0; + + /* if writer flow controlled, restart */ + if((q->state & Qflow) && q->len < q->limit/2){ + q->state &= ~Qflow; + dowakeup = 1; + } + + iunlock(q); + + /* wakeup flow controlled writers */ + if(dowakeup){ + if(q->kick) + q->kick(q->arg); + wakeup(&q->wr); + } +} + +/* + * get next block from a queue (up to a limit) + */ +Block* +qbread(Queue *q, int len) +{ + Block *b, *nb; + int n; + + qlock(&q->rlock); + if(waserror()){ + qunlock(&q->rlock); + nexterror(); + } + + ilock(q); + switch(qwait(q)){ + case 0: + /* queue closed */ + iunlock(q); + qunlock(&q->rlock); + poperror(); + return nil; + case -1: + /* multiple reads on a closed queue */ + iunlock(q); + error(q->err); + } + + /* if we get here, there's at least one block in the queue */ + b = qremove(q); + n = BLEN(b); + + /* split block if it's too big and this is not a message queue */ + nb = b; + if(n > len){ + if((q->state&Qmsg) == 0){ + n -= len; + b = allocb(n); + memmove(b->wp, nb->rp+len, n); + b->wp += n; + qputback(q, b); + } + nb->wp = nb->rp + len; + } + + /* restart producer */ + qwakeup_iunlock(q); + + poperror(); + qunlock(&q->rlock); + return nb; +} + +/* + * read a queue. if no data is queued, post a Block + * and wait on its Rendez. + */ +long +qread(Queue *q, void *vp, int len) +{ + Block *b, *first, **l; + int m, n; + + qlock(&q->rlock); + if(waserror()){ + qunlock(&q->rlock); + nexterror(); + } + + ilock(q); +again: + switch(qwait(q)){ + case 0: + /* queue closed */ + iunlock(q); + qunlock(&q->rlock); + poperror(); + return 0; + case -1: + /* multiple reads on a closed queue */ + iunlock(q); + error(q->err); + } + + /* if we get here, there's at least one block in the queue */ + if(q->state & Qcoalesce){ + /* when coalescing, 0 length blocks just go away */ + b = q->bfirst; + if(BLEN(b) <= 0){ + freeb(qremove(q)); + goto again; + } + + /* grab the first block plus as many + * following blocks as will completely + * fit in the read. + */ + n = 0; + l = &first; + m = BLEN(b); + for(;;) { + *l = qremove(q); + l = &b->next; + n += m; + + b = q->bfirst; + if(b == nil) + break; + m = BLEN(b); + if(n+m > len) + break; + } + } else { + first = qremove(q); + n = BLEN(first); + } + + /* copy to user space outside of the ilock */ + iunlock(q); + b = bl2mem(vp, first, len); + ilock(q); + + /* take care of any left over partial block */ + if(b != nil){ + n -= BLEN(b); + if(q->state & Qmsg) + freeb(b); + else + qputback(q, b); + } + + /* restart producer */ + qwakeup_iunlock(q); + + poperror(); + qunlock(&q->rlock); + return n; +} + +static int +qnotfull(void *a) +{ + Queue *q = a; + + return q->len < q->limit || (q->state & Qclosed); +} + +ulong noblockcnt; + +/* + * add a block to a queue obeying flow control + */ +long +qbwrite(Queue *q, Block *b) +{ + int n, dowakeup; + + n = BLEN(b); + + if(q->bypass){ + (*q->bypass)(q->arg, b); + return n; + } + + dowakeup = 0; + qlock(&q->wlock); + if(waserror()){ + if(b != nil) + freeb(b); + qunlock(&q->wlock); + nexterror(); + } + + ilock(q); + + /* give up if the queue is closed */ + if(q->state & Qclosed){ + iunlock(q); + error(q->err); + } + + /* if nonblocking, don't queue over the limit */ + if(q->len >= q->limit){ + if(q->noblock){ + iunlock(q); + freeb(b); + noblockcnt += n; + qunlock(&q->wlock); + poperror(); + return n; + } + } + + /* queue the block */ + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + q->blast = b; + b->next = 0; + q->len += BALLOC(b); + q->dlen += n; + QDEBUG checkb(b, "qbwrite"); + b = nil; + + /* make sure other end gets awakened */ + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + iunlock(q); + + /* get output going again */ + if(q->kick && (dowakeup || (q->state&Qkick))) + q->kick(q->arg); + + /* wakeup anyone consuming at the other end */ + if(dowakeup) + wakeup(&q->rr); + + /* + * flow control, wait for queue to get below the limit + * before allowing the process to continue and queue + * more. We do this here so that postnote can only + * interrupt us after the data has been queued. This + * means that things like 9p flushes and ssl messages + * will not be disrupted by software interrupts. + * + * Note - this is moderately dangerous since a process + * that keeps getting interrupted and rewriting will + * queue infinite crud. + */ + for(;;){ + if(q->noblock || qnotfull(q)) + break; + + ilock(q); + q->state |= Qflow; + iunlock(q); + sleep(&q->wr, qnotfull, q); + } + USED(b); + + qunlock(&q->wlock); + poperror(); + return n; +} + +/* + * write to a queue. only Maxatomic bytes at a time is atomic. + */ +int +qwrite(Queue *q, void *vp, int len) +{ + int n, sofar; + Block *b; + uchar *p = vp; + + QDEBUG if(!islo()) + print("qwrite hi %lux\n", getcallerpc(&q)); + + sofar = 0; + do { + n = len-sofar; + if(n > Maxatomic) + n = Maxatomic; + + b = allocb(n); + setmalloctag(b, getcallerpc(&q)); + if(waserror()){ + freeb(b); + nexterror(); + } + memmove(b->wp, p+sofar, n); + poperror(); + b->wp += n; + + qbwrite(q, b); + + sofar += n; + } while(sofar < len && (q->state & Qmsg) == 0); + + return len; +} + +/* + * used by print() to write to a queue. Since we may be splhi or not in + * a process, don't qlock. + */ +int +qiwrite(Queue *q, void *vp, int len) +{ + int n, sofar, dowakeup; + Block *b; + uchar *p = vp; + + dowakeup = 0; + + sofar = 0; + do { + n = len-sofar; + if(n > Maxatomic) + n = Maxatomic; + + b = iallocb(n); + if(b == nil) + break; + memmove(b->wp, p+sofar, n); + b->wp += n; + + ilock(q); + + QDEBUG checkb(b, "qiwrite"); + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + q->blast = b; + q->len += BALLOC(b); + q->dlen += n; + + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + + iunlock(q); + + if(dowakeup){ + if(q->kick) + q->kick(q->arg); + wakeup(&q->rr); + } + + sofar += n; + } while(sofar < len && (q->state & Qmsg) == 0); + + return sofar; +} + +/* + * be extremely careful when calling this, + * as there is no reference accounting + */ +void +qfree(Queue *q) +{ + qclose(q); + free(q); +} + +/* + * Mark a queue as closed. No further IO is permitted. + * All blocks are released. + */ +void +qclose(Queue *q) +{ + Block *bfirst; + + if(q == nil) + return; + + /* mark it */ + ilock(q); + q->state |= Qclosed; + q->state &= ~(Qflow|Qstarve); + strcpy(q->err, Ehungup); + bfirst = q->bfirst; + q->bfirst = 0; + q->len = 0; + q->dlen = 0; + q->noblock = 0; + iunlock(q); + + /* free queued blocks */ + freeblist(bfirst); + + /* wake up readers/writers */ + wakeup(&q->rr); + wakeup(&q->wr); +} + +/* + * Mark a queue as closed. Wakeup any readers. Don't remove queued + * blocks. + */ +void +qhangup(Queue *q, char *msg) +{ + /* mark it */ + ilock(q); + q->state |= Qclosed; + if(msg == 0 || *msg == 0) + strcpy(q->err, Ehungup); + else + strncpy(q->err, msg, ERRMAX-1); + iunlock(q); + + /* wake up readers/writers */ + wakeup(&q->rr); + wakeup(&q->wr); +} + +/* + * return non-zero if the q is hungup + */ +int +qisclosed(Queue *q) +{ + return q->state & Qclosed; +} + +/* + * mark a queue as no longer hung up + */ +void +qreopen(Queue *q) +{ + ilock(q); + q->state &= ~Qclosed; + q->state |= Qstarve; + q->eof = 0; + q->limit = q->inilim; + iunlock(q); +} + +/* + * return bytes queued + */ +int +qlen(Queue *q) +{ + return q->dlen; +} + +/* + * return space remaining before flow control + */ +int +qwindow(Queue *q) +{ + int l; + + l = q->limit - q->len; + if(l < 0) + l = 0; + return l; +} + +/* + * return true if we can read without blocking + */ +int +qcanread(Queue *q) +{ + return q->bfirst!=0; +} + +/* + * change queue limit + */ +void +qsetlimit(Queue *q, int limit) +{ + q->limit = limit; +} + +/* + * set blocking/nonblocking + */ +void +qnoblock(Queue *q, int onoff) +{ + q->noblock = onoff; +} + +/* + * flush the output queue + */ +void +qflush(Queue *q) +{ + Block *bfirst; + + /* mark it */ + ilock(q); + bfirst = q->bfirst; + q->bfirst = 0; + q->len = 0; + q->dlen = 0; + iunlock(q); + + /* free queued blocks */ + freeblist(bfirst); + + /* wake up readers/writers */ + wakeup(&q->wr); +} + +int +qfull(Queue *q) +{ + return q->state & Qflow; +} + +int +qstate(Queue *q) +{ + return q->state; +} + +void +qdump(Queue *q) +{ + if(q) + kprint("q=%p bfirst=%p blast=%p len=%d dlen=%d limit=%d state=#%x\n", + q, q->bfirst, q->blast, q->len, q->dlen, q->limit, q->state); +} diff --git a/os/port/qlock.c b/os/port/qlock.c new file mode 100644 index 00000000..63d7c329 --- /dev/null +++ b/os/port/qlock.c @@ -0,0 +1,111 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +void +qlock(QLock *q) +{ + Proc *p, *mp; + + lock(&q->use); + if(!q->locked) { + q->locked = 1; + unlock(&q->use); + return; + } + p = q->tail; + mp = up; + if(p == 0) + q->head = mp; + else + p->qnext = mp; + q->tail = mp; + mp->qnext = 0; + mp->state = Queueing; + up->qpc = getcallerpc(&q); + unlock(&q->use); + sched(); +} + +int +canqlock(QLock *q) +{ + if(!canlock(&q->use)) + return 0; + if(q->locked){ + unlock(&q->use); + return 0; + } + q->locked = 1; + unlock(&q->use); + return 1; +} + +void +qunlock(QLock *q) +{ + Proc *p; + + lock(&q->use); + p = q->head; + if(p) { + q->head = p->qnext; + if(q->head == 0) + q->tail = 0; + unlock(&q->use); + ready(p); + return; + } + q->locked = 0; + unlock(&q->use); +} + +void +rlock(RWlock *l) +{ + qlock(&l->x); /* wait here for writers and exclusion */ + lock(l); + l->readers++; + canqlock(&l->k); /* block writers if we are the first reader */ + unlock(l); + qunlock(&l->x); +} + +/* same as rlock but punts if there are any writers waiting */ +int +canrlock(RWlock *l) +{ + if (!canqlock(&l->x)) + return 0; + lock(l); + l->readers++; + canqlock(&l->k); /* block writers if we are the first reader */ + unlock(l); + qunlock(&l->x); + return 1; +} + +void +runlock(RWlock *l) +{ + lock(l); + if(--l->readers == 0) /* last reader out allows writers */ + qunlock(&l->k); + unlock(l); +} + +void +wlock(RWlock *l) +{ + qlock(&l->x); /* wait here for writers and exclusion */ + qlock(&l->k); /* wait here for last reader */ +} + +void +wunlock(RWlock *l) +{ + qunlock(&l->k); + qunlock(&l->x); +} diff --git a/os/port/random.c b/os/port/random.c new file mode 100644 index 00000000..720541c0 --- /dev/null +++ b/os/port/random.c @@ -0,0 +1,156 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +static struct +{ + QLock; + Rendez producer; + Rendez consumer; + ulong randomcount; + uchar buf[1024]; + uchar *ep; + uchar *rp; + uchar *wp; + uchar next; + uchar bits; + uchar wakeme; + uchar filled; + int target; + int kprocstarted; + ulong randn; +} rb; + +static int +rbnotfull(void*) +{ + int i; + + i = rb.wp - rb.rp; + if(i < 0) + i += sizeof(rb.buf); + return i < rb.target; +} + +static int +rbnotempty(void*) +{ + return rb.wp != rb.rp; +} + +static void +genrandom(void*) +{ + setpri(PriBackground); + + for(;;) { + for(;;) + if(++rb.randomcount > 100000) + break; + if(anyhigher()) + sched(); + if(!rbnotfull(0)) + sleep(&rb.producer, rbnotfull, 0); + } +} + +/* + * produce random bits in a circular buffer + */ +static void +randomclock(void) +{ + uchar *p; + + if(rb.randomcount == 0) + return; + + if(!rbnotfull(0)) { + rb.filled = 1; + return; + } + + rb.bits = (rb.bits<<2) ^ (rb.randomcount&3); + rb.randomcount = 0; + + rb.next += 2; + if(rb.next != 8) + return; + + rb.next = 0; + *rb.wp ^= rb.bits ^ *rb.rp; + p = rb.wp+1; + if(p == rb.ep) + p = rb.buf; + rb.wp = p; + + if(rb.wakeme) + wakeup(&rb.consumer); +} + +void +randominit(void) +{ + /* Frequency close but not equal to HZ */ + addclock0link(randomclock, 13); + rb.target = 16; + rb.ep = rb.buf + sizeof(rb.buf); + rb.rp = rb.wp = rb.buf; +} + +/* + * consume random bytes from a circular buffer + */ +ulong +randomread(void *xp, ulong n) +{ + int i, sofar; + uchar *e, *p; + + p = xp; + + qlock(&rb); + if(waserror()){ + qunlock(&rb); + nexterror(); + } + if(!rb.kprocstarted){ + rb.kprocstarted = 1; + kproc("genrand", genrandom, nil, 0); + } + + for(sofar = 0; sofar < n; sofar += i){ + i = rb.wp - rb.rp; + if(i == 0){ + rb.wakeme = 1; + wakeup(&rb.producer); + sleep(&rb.consumer, rbnotempty, 0); + rb.wakeme = 0; + continue; + } + if(i < 0) + i = rb.ep - rb.rp; + if((i+sofar) > n) + i = n - sofar; + memmove(p + sofar, rb.rp, i); + e = rb.rp + i; + if(e == rb.ep) + e = rb.buf; + rb.rp = e; + } + if(rb.filled && rb.wp == rb.rp){ + i = 2*rb.target; + if(i > sizeof(rb.buf) - 1) + i = sizeof(rb.buf) - 1; + rb.target = i; + rb.filled = 0; + } + poperror(); + qunlock(&rb); + + wakeup(&rb.producer); + + return n; +} diff --git a/os/port/rdb.c b/os/port/rdb.c new file mode 100644 index 00000000..7386bcc6 --- /dev/null +++ b/os/port/rdb.c @@ -0,0 +1,112 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +/* + * alternative debug protocol for plan 9's rdbfs(4) for plan 9's own acid + */ +#define DBG if(0)scrprint +#pragma varargck argpos scrprint 1 +static Ureg ureg; +extern Queue *klogq; + +static void +scrprint(char *fmt, ...) +{ + char buf[128]; + va_list va; + int n; + + va_start(va, fmt); + n = vseprint(buf, buf+sizeof buf, fmt, va)-buf; + va_end(va); + putstrn(buf, n); +} + +static char* +getline(void) +{ + static char buf[128]; + int i, c; + + for(;;){ + for(i=0; i 4){ + mesg(Rerr, Ecount); + break; + } + a = addr(min+0); + scrprint("mput %.8lux\n", a); + memmove(a, min+5, n); + mesg(Rmput, mout); + break; + * + */ + default: + DBG("unknown %c\n", *req); + iprint("Eunknown message\n"); + break; + } + } +} + +void +rdb(void) +{ + splhi(); + iprint("rdb..."); + callwithureg(talkrdb); +} diff --git a/os/port/sd.h b/os/port/sd.h new file mode 100644 index 00000000..a469cff6 --- /dev/null +++ b/os/port/sd.h @@ -0,0 +1,132 @@ +/* + * Storage Device. + */ +typedef struct SDev SDev; +typedef struct SDifc SDifc; +typedef struct SDpart SDpart; +typedef struct SDperm SDperm; +typedef struct SDreq SDreq; +typedef struct SDunit SDunit; + +struct SDperm { + char* name; + char* user; + ulong perm; +}; + +struct SDpart { + ulong start; + ulong end; + SDperm; + int valid; + ulong vers; +}; + +struct SDunit { + SDev* dev; + int subno; + uchar inquiry[256]; /* format follows SCSI spec */ + SDperm; + + QLock ctl; + ulong sectors; + ulong secsize; + SDpart* part; /* nil or array of size npart */ + int npart; + ulong vers; + SDperm ctlperm; + + QLock raw; /* raw read or write in progress */ + ulong rawinuse; /* really just a test-and-set */ + int state; + SDreq* req; + SDperm rawperm; +}; + +/* + * Each controller is represented by a SDev. + * Each controller is responsible for allocating its unit structures. + * Each controller has at least one unit. + */ +struct SDev { + Ref r; /* Number of callers using device */ + SDifc* ifc; /* pnp/legacy */ + void* ctlr; + int idno; + char* name; + SDev* next; + + QLock; /* enable/disable */ + int enabled; + int nunit; /* Number of units */ + QLock unitlock; /* `Loading' of units */ + int* unitflg; /* Unit flags */ + SDunit**unit; +}; + +struct SDifc { + char* name; + + SDev* (*pnp)(void); + SDev* (*legacy)(int, int); + SDev* (*id)(SDev*); + int (*enable)(SDev*); + int (*disable)(SDev*); + + int (*verify)(SDunit*); + int (*online)(SDunit*); + int (*rio)(SDreq*); + int (*rctl)(SDunit*, char*, int); + int (*wctl)(SDunit*, Cmdbuf*); + + long (*bio)(SDunit*, int, int, void*, long, long); + SDev* (*probe)(DevConf*); + void (*clear)(SDev*); + char* (*stat)(SDev*, char*, char*); +}; + +struct SDreq { + SDunit* unit; + int lun; + int write; + uchar cmd[16]; + int clen; + void* data; + int dlen; + + int flags; + + int status; + long rlen; + uchar sense[256]; +}; + +enum { + SDnosense = 0x00000001, + SDvalidsense = 0x00010000, +}; + +enum { + SDretry = -5, /* internal to controllers */ + SDmalloc = -4, + SDeio = -3, + SDtimeout = -2, + SDnostatus = -1, + + SDok = 0, + + SDcheck = 0x02, /* check condition */ + SDbusy = 0x08, /* busy */ + + SDmaxio = 2048*1024, + SDnpart = 16, +}; + +#define sdmalloc(n) malloc(n) +#define sdfree(p) free(p) + +/* sdscsi.c */ +extern int scsiverify(SDunit*); +extern int scsionline(SDunit*); +extern long scsibio(SDunit*, int, int, void*, long, long); +extern SDev* scsiid(SDev*, SDifc*); diff --git a/os/port/swcursor.c b/os/port/swcursor.c new file mode 100644 index 00000000..b4e2628c --- /dev/null +++ b/os/port/swcursor.c @@ -0,0 +1,358 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +#include +#include +#include + +#include "screen.h" + +typedef struct SWcursor SWcursor; + +/* + * Software cursor code: done by hand, might be better to use memdraw + */ + +struct SWcursor { + ulong *fb; /* screen frame buffer */ + Rectangle r; + int d; /* ldepth of screen */ + int width; /* width of screen in ulongs */ + int x; + int y; + int hotx; + int hoty; + uchar cbwid; /* cursor byte width */ + uchar f; /* flags */ + uchar cwid; + uchar chgt; + int hidecount; + uchar data[CURSWID*CURSHGT]; + uchar mask[CURSWID*CURSHGT]; + uchar save[CURSWID*CURSHGT]; +}; + +enum { + CUR_ENA = 0x01, /* cursor is enabled */ + CUR_DRW = 0x02, /* cursor is currently drawn */ + CUR_SWP = 0x10, /* bit swap */ +}; + +static Rectangle cursoroffrect; +static int cursorisoff; + +static void swcursorflush(int, int); +static void swcurs_draw_or_undraw(SWcursor *); + +static void +cursorupdate0(void) +{ + int inrect, x, y; + Point m; + + m = mousexy(); + x = m.x - swc->hotx; + y = m.y - swc->hoty; + inrect = (x >= cursoroffrect.min.x && x < cursoroffrect.max.x + && y >= cursoroffrect.min.y && y < cursoroffrect.max.y); + if (cursorisoff == inrect) + return; + cursorisoff = inrect; + if (inrect) + swcurs_hide(swc); + else { + swc->hidecount = 0; + swcurs_draw_or_undraw(swc); + } + swcursorflush(m.x, m.y); +} + +void +cursorupdate(Rectangle r) +{ + lock(vd); + r.min.x -= 16; + r.min.y -= 16; + cursoroffrect = r; + if (swc != nil) + cursorupdate0(); + unlock(vd); +} + +void +cursorenable(void) +{ + Point m; + + lock(vd); + if(swc != nil) { + swcurs_enable(swc); + m = mousexy(); + swcursorflush(m.x, m.y); + } + unlock(vd); +} + +void +cursordisable(void) +{ + Point m; + + lock(vd); + if(swc != nil) { + swcurs_disable(swc); + m = mousexy(); + swcursorflush(m.x, m.y); + } + unlock(vd); +} + +void +swcursupdate(int oldx, int oldy, int x, int y) +{ + + if(!canlock(vd)) + return; /* if can't lock, don't wake up stuff */ + + if(x < gscreen->r.min.x) + x = gscreen->r.min.x; + if(x >= gscreen->r.max.x) + x = gscreen->r.max.x; + if(y < gscreen->r.min.y) + y = gscreen->r.min.y; + if(y >= gscreen->r.max.y) + y = gscreen->r.max.y; + if(swc != nil) { + swcurs_hide(swc); + swc->x = x; + swc->y = y; + cursorupdate0(); + swcurs_unhide(swc); + swcursorflush(oldx, oldy); + swcursorflush(x, y); + } + + unlock(vd); +} + +void +drawcursor(Drawcursor* c) +{ + Point p, m; + Cursor curs, *cp; + int j, i, h, bpl; + uchar *bc, *bs, *cclr, *cset; + + if(swc == nil) + return; + + /* Set the default system cursor */ + if(c == nil || c->data == nil){ + swcurs_disable(swc); + return; + } + else { + cp = &curs; + p.x = c->hotx; + p.y = c->hoty; + cp->offset = p; + bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1); + + h = (c->maxy-c->miny)/2; + if(h > 16) + h = 16; + + bc = c->data; + bs = c->data + h*bpl; + + cclr = cp->clr; + cset = cp->set; + for(i = 0; i < h; i++) { + for(j = 0; j < 2; j++) { + cclr[j] = bc[j]; + cset[j] = bs[j]; + } + bc += bpl; + bs += bpl; + cclr += 2; + cset += 2; + } + } + swcurs_load(swc, cp); + m = mousexy(); + swcursorflush(m.x, m.y); + swcurs_enable(swc); +} + +SWcursor* +swcurs_create(ulong *fb, int width, int ldepth, Rectangle r, int bitswap) +{ + SWcursor *swc; + + swc = (SWcursor*)malloc(sizeof(SWcursor)); + swc->fb = fb; + swc->r = r; + swc->d = ldepth; + swc->width = width; + swc->f = bitswap ? CUR_SWP : 0; + swc->x = swc->y = 0; + swc->hotx = swc->hoty = 0; + swc->hidecount = 0; + return swc; +} + +void +swcurs_destroy(SWcursor *swc) +{ + swcurs_disable(swc); + free(swc); +} + +static void +swcursorflush(int x, int y) +{ + Rectangle r; + + /* XXX a little too paranoid here */ + r.min.x = x-16; + r.min.y = y-16; + r.max.x = x+17; + r.max.y = y+17; + flushmemscreen(r); +} + +static void +swcurs_draw_or_undraw(SWcursor *swc) +{ + uchar *p; + uchar *cs; + int w, vw; + int x1 = swc->r.min.x; + int y1 = swc->r.min.y; + int x2 = swc->r.max.x; + int y2 = swc->r.max.y; + int xp = swc->x - swc->hotx; + int yp = swc->y - swc->hoty; + int ofs; + + if(((swc->f & CUR_ENA) && (swc->hidecount <= 0)) + == ((swc->f & CUR_DRW) != 0)) + return; + w = swc->cbwid*BI2BY/(1 << swc->d); + x1 = xp < x1 ? x1 : xp; + y1 = yp < y1 ? y1 : yp; + x2 = xp+w >= x2 ? x2 : xp+w; + y2 = yp+swc->chgt >= y2 ? y2 : yp+swc->chgt; + if(x2 <= x1 || y2 <= y1) + return; + p = (uchar*)(swc->fb + swc->width*y1) + + x1*(1 << swc->d)/BI2BY; + y2 -= y1; + x2 = (x2-x1)*(1 << swc->d)/BI2BY; + vw = swc->width*BY2WD - x2; + w = swc->cbwid - x2; + ofs = swc->cbwid*(y1-yp)+(x1-xp); + cs = swc->save + ofs; + if((swc->f ^= CUR_DRW) & CUR_DRW) { + uchar *cm = swc->mask + ofs; + uchar *cd = swc->data + ofs; + while(y2--) { + x1 = x2; + while(x1--) { + *p = ((*cs++ = *p) & *cm++) ^ *cd++; + p++; + } + cs += w; + cm += w; + cd += w; + p += vw; + } + } else { + while(y2--) { + x1 = x2; + while(x1--) + *p++ = *cs++; + cs += w; + p += vw; + } + } +} + +void +swcurs_hide(SWcursor *swc) +{ + ++swc->hidecount; + swcurs_draw_or_undraw(swc); +} + +void +swcurs_unhide(SWcursor *swc) +{ + if (--swc->hidecount < 0) + swc->hidecount = 0; + swcurs_draw_or_undraw(swc); +} + +void +swcurs_enable(SWcursor *swc) +{ + swc->f |= CUR_ENA; + swcurs_draw_or_undraw(swc); +} + +void +swcurs_disable(SWcursor *swc) +{ + swc->f &= ~CUR_ENA; + swcurs_draw_or_undraw(swc); +} + +void +swcurs_load(SWcursor *swc, Cursor *c) +{ + int i, k; + uchar *bc, *bs, *cd, *cm; + static uchar bdv[4] = {0,Backgnd,Foregnd,0xff}; + static uchar bmv[4] = {0xff,0,0,0xff}; + int bits = 1<d; + uchar mask = (1<f&CUR_SWP) ? 8-bits : 0; + + bc = c->clr; + bs = c->set; + + swcurs_hide(swc); + cd = swc->data; + cm = swc->mask; + swc->hotx = c->offset.x; + swc->hoty = c->offset.y; + swc->chgt = CURSHGT; + swc->cwid = CURSWID; + swc->cbwid = CURSWID*(1<d)/BI2BY; + for(i = 0; i < CURSWID/BI2BY*CURSHGT; i++) { + uchar bcb = *bc++; + uchar bsb = *bs++; + for(k=0; k>7; + int s = z^bswp; + cdv |= (bdv[n]&mask) << s; + cmv |= (bmv[n]&mask) << s; + bcb <<= 1; + bsb <<= 1; + k++; + } + *cd++ = cdv; + *cm++ = cmv; + } + } + swcurs_unhide(swc); +} diff --git a/os/port/sysfile.c b/os/port/sysfile.c new file mode 100644 index 00000000..1ff0fe5f --- /dev/null +++ b/os/port/sysfile.c @@ -0,0 +1,1125 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +static int +growfd(Fgrp *f, int fd) +{ + int n; + Chan **nfd, **ofd; + + if(fd < f->nfd) + return 0; + n = f->nfd+DELTAFD; + if(n > MAXNFD) + n = MAXNFD; + if(fd >= n) + return -1; + nfd = malloc(n*sizeof(Chan*)); + if(nfd == nil) + return -1; + ofd = f->fd; + memmove(nfd, ofd, f->nfd*sizeof(Chan *)); + f->fd = nfd; + f->nfd = n; + free(ofd); + return 0; +} + +int +newfd(Chan *c) +{ + int i; + Fgrp *f = up->env->fgrp; + + lock(f); + for(i=f->minfd; infd; i++) + if(f->fd[i] == 0) + break; + if(i >= f->nfd && growfd(f, i) < 0){ + unlock(f); + exhausted("file descriptors"); + return -1; + } + f->minfd = i + 1; + if(i > f->maxfd) + f->maxfd = i; + f->fd[i] = c; + unlock(f); + return i; +} + +Chan* +fdtochan(Fgrp *f, int fd, int mode, int chkmnt, int iref) +{ + Chan *c; + + c = 0; + + lock(f); + if(fd<0 || f->maxfdfd[fd])==0) { + unlock(f); + error(Ebadfd); + } + if(iref) + incref(c); + unlock(f); + + if(chkmnt && (c->flag&CMSG)) { + if(iref) + cclose(c); + error(Ebadusefd); + } + + if(mode<0 || c->mode==ORDWR) + return c; + + if((mode&OTRUNC) && c->mode==OREAD) { + if(iref) + cclose(c); + error(Ebadusefd); + } + + if((mode&~OTRUNC) != c->mode) { + if(iref) + cclose(c); + error(Ebadusefd); + } + + return c; +} + +long +kchanio(void *vc, void *buf, int n, int mode) +{ + int r; + Chan *c; + + c = vc; + if(waserror()) + return -1; + + if(mode == OREAD) + r = devtab[c->type]->read(c, buf, n, c->offset); + else + r = devtab[c->type]->write(c, buf, n, c->offset); + + lock(c); + c->offset += r; + unlock(c); + poperror(); + return r; +} + +int +openmode(ulong o) +{ + if(o >= (OTRUNC|OCEXEC|ORCLOSE|OEXEC)) + error(Ebadarg); + o &= ~(OTRUNC|OCEXEC|ORCLOSE); + if(o > OEXEC) + error(Ebadarg); + if(o == OEXEC) + return OREAD; + return o; +} + +void +fdclose(Fgrp *f, int fd) +{ + int i; + Chan *c; + + lock(f); + c = f->fd[fd]; + if(c == 0){ + /* can happen for users with shared fd tables */ + unlock(f); + return; + } + f->fd[fd] = 0; + if(fd == f->maxfd) + for(i=fd; --i>=0 && f->fd[i]==0; ) + f->maxfd = i; + if(fd < f->minfd) + f->minfd = fd; + unlock(f); + cclose(c); +} + +int +kchdir(char *path) +{ + Chan *c; + Pgrp *pg; + + if(waserror()) + return -1; + + c = namec(path, Atodir, 0, 0); + pg = up->env->pgrp; + cclose(pg->dot); + pg->dot = c; + poperror(); + return 0; +} + +int +kfgrpclose(Fgrp *f, int fd) +{ + if(waserror()) + return -1; + + /* + * Take no reference on the chan because we don't really need the + * data structure, and are calling fdtochan only for error checks. + * fdclose takes care of processes racing through here. + */ + fdtochan(f, fd, -1, 0, 0); + fdclose(f, fd); + poperror(); + return 0; +} + +int +kclose(int fd) +{ + return kfgrpclose(up->env->fgrp, fd); +} + +int +kcreate(char *path, int mode, ulong perm) +{ + int fd; + Chan *c; + + if(waserror()) + return -1; + + openmode(mode&~OEXCL); /* error check only; OEXCL okay here */ + c = namec(path, Acreate, mode, perm); + if(waserror()) { + cclose(c); + nexterror(); + } + fd = newfd(c); + if(fd < 0) + error(Enofd); + poperror(); + + poperror(); + return fd; +} + +int +kdup(int old, int new) +{ + int fd; + Chan *c, *oc; + Fgrp *f = up->env->fgrp; + + if(waserror()) + return -1; + + c = fdtochan(up->env->fgrp, old, -1, 0, 1); + if(c->qid.type & QTAUTH) + error(Eperm); + fd = new; + if(fd != -1){ + lock(f); + if(fd<0 || growfd(f, fd) < 0) { + unlock(f); + cclose(c); + error(Ebadfd); + } + if(fd > f->maxfd) + f->maxfd = fd; + oc = f->fd[fd]; + f->fd[fd] = c; + unlock(f); + if(oc) + cclose(oc); + }else{ + if(waserror()) { + cclose(c); + nexterror(); + } + fd = newfd(c); + if(fd < 0) + error(Enofd); + poperror(); + } + poperror(); + return fd; +} + +int +kfstat(int fd, uchar *buf, int n) +{ + Chan *c; + + if(waserror()) + return -1; + + c = fdtochan(up->env->fgrp, fd, -1, 0, 1); + if(waserror()) { + cclose(c); + nexterror(); + } + devtab[c->type]->stat(c, buf, n); + + poperror(); + cclose(c); + + poperror(); + return n; +} + +char* +kfd2path(int fd) +{ + Chan *c; + char *s; + + if(waserror()) + return nil; + c = fdtochan(up->env->fgrp, fd, -1, 0, 1); + s = nil; + if(c->name != nil){ + s = malloc(c->name->len+1); + if(s == nil){ + cclose(c); + error(Enomem); + } + memmove(s, c->name->s, c->name->len+1); + cclose(c); + } + poperror(); + return s; +} + +int +kfauth(int fd, char *aname) +{ + Chan *c, *ac; + + if(waserror()) + return -1; + + validname(aname, 0); + c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1); + if(waserror()){ + cclose(c); + nexterror(); + } + + ac = mntauth(c, aname); + + /* at this point ac is responsible for keeping c alive */ + poperror(); /* c */ + cclose(c); + + if(waserror()){ + cclose(ac); + nexterror(); + } + + fd = newfd(ac); + if(fd < 0) + error(Enofd); + poperror(); /* ac */ + + poperror(); + + return fd; +} + +int +kfversion(int fd, uint msize, char *vers, uint arglen) +{ + int m; + Chan *c; + + if(waserror()) + return -1; + + /* check there's a NUL in the version string */ + if(arglen==0 || memchr(vers, 0, arglen)==0) + error(Ebadarg); + + c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1); + if(waserror()){ + cclose(c); + nexterror(); + } + + m = mntversion(c, vers, msize, arglen); + + poperror(); + cclose(c); + + poperror(); + return m; +} + +int +kpipe(int fd[2]) +{ + Dev *d; + Fgrp *f; + Chan *c[2]; + static char *names[] = {"data", "data1"}; + + f = up->env->fgrp; + + d = devtab[devno('|', 0)]; + c[0] = namec("#|", Atodir, 0, 0); + c[1] = 0; + fd[0] = -1; + fd[1] = -1; + if(waserror()) { + if(c[0] != 0) + cclose(c[0]); + if(c[1] != 0) + cclose(c[1]); + if(fd[0] >= 0) + f->fd[fd[0]]=0; + if(fd[1] >= 0) + f->fd[fd[1]]=0; + return -1; + } + c[1] = cclone(c[0]); + if(walk(&c[0], &names[0], 1, 1, nil) < 0) + error(Egreg); + if(walk(&c[1], &names[1], 1, 1, nil) < 0) + error(Egreg); + c[0] = d->open(c[0], ORDWR); + c[1] = d->open(c[1], ORDWR); + fd[0] = newfd(c[0]); + if(fd[0] < 0) + error(Enofd); + fd[1] = newfd(c[1]); + if(fd[1] < 0) + error(Enofd); + poperror(); + return 0; +} + +int +kfwstat(int fd, uchar *buf, int n) +{ + Chan *c; + + if(waserror()) + return -1; + + validstat(buf, n); + c = fdtochan(up->env->fgrp, fd, -1, 1, 1); + if(waserror()) { + cclose(c); + nexterror(); + } + n = devtab[c->type]->wstat(c, buf, n); + poperror(); + cclose(c); + + poperror(); + return n; +} + +long +bindmount(Chan *c, char *old, int flag, char *spec) +{ + int ret; + Chan *c1; + + if(flag>MMASK || (flag&MORDER) == (MBEFORE|MAFTER)) + error(Ebadarg); + + c1 = namec(old, Amount, 0, 0); + if(waserror()){ + cclose(c1); + nexterror(); + } + ret = cmount(c, c1, flag, spec); + + poperror(); + cclose(c1); + return ret; +} + +int +kbind(char *new, char *old, int flags) +{ + long r; + Chan *c0; + + if(waserror()) + return -1; + + c0 = namec(new, Abind, 0, 0); + if(waserror()) { + cclose(c0); + nexterror(); + } + r = bindmount(c0, old, flags, ""); + poperror(); + cclose(c0); + + poperror(); + return r; +} + +int +kmount(int fd, int afd, char *old, int flags, char *spec) +{ + long r; + volatile struct { Chan *c; } c0; + volatile struct { Chan *c; } bc; + volatile struct { Chan *c; } ac; + Mntparam mntparam; + + ac.c = nil; + bc.c = nil; + c0.c = nil; + if(waserror()) { + cclose(ac.c); + cclose(bc.c); + cclose(c0.c); + return -1; + } + bc.c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1); + if(afd >= 0) + ac.c = fdtochan(up->env->fgrp, afd, ORDWR, 0, 1); + mntparam.chan = bc.c; + mntparam.authchan = ac.c; + mntparam.spec = spec; + mntparam.flags = flags; + c0.c = devtab[devno('M', 0)]->attach((char*)&mntparam); + + r = bindmount(c0.c, old, flags, spec); + poperror(); + cclose(ac.c); + cclose(bc.c); + cclose(c0.c); + + return r; +} + +int +kunmount(char *old, char *new) +{ + volatile struct { Chan *c; } cmount; + volatile struct { Chan *c; } cmounted; + + cmount.c = nil; + cmounted.c = nil; + if(waserror()) { + cclose(cmount.c); + cclose(cmounted.c); + return -1; + } + + cmount.c = namec(new, Amount, 0, 0); + if(old != nil && old[0] != '\0') { + /* + * This has to be namec(..., Aopen, ...) because + * if arg[0] is something like /srv/cs or /fd/0, + * opening it is the only way to get at the real + * Chan underneath. + */ + cmounted.c = namec(old, Aopen, OREAD, 0); + } + + cunmount(cmount.c, cmounted.c); + poperror(); + cclose(cmount.c); + cclose(cmounted.c); + return 0; +} + +int +kopen(char *path, int mode) +{ + int fd; + Chan *c; + + if(waserror()) + return -1; + + openmode(mode); /* error check only */ + c = namec(path, Aopen, mode, 0); + if(waserror()){ + cclose(c); + nexterror(); + } + fd = newfd(c); + if(fd < 0) + error(Enofd); + poperror(); + + poperror(); + return fd; +} + +long +unionread(Chan *c, void *va, long n) +{ + int i; + long nr; + Mhead *m; + Mount *mount; + + qlock(&c->umqlock); + m = c->umh; + rlock(&m->lock); + mount = m->mount; + /* bring mount in sync with c->uri and c->umc */ + for(i = 0; mount != nil && i < c->uri; i++) + mount = mount->next; + + nr = 0; + while(mount != nil) { + /* Error causes component of union to be skipped */ + if(mount->to && !waserror()) { + if(c->umc == nil){ + c->umc = cclone(mount->to); + c->umc = devtab[c->umc->type]->open(c->umc, OREAD); + } + + nr = devtab[c->umc->type]->read(c->umc, va, n, c->umc->offset); + if(nr < 0) + nr = 0; /* dev.c can return -1 */ + c->umc->offset += nr; + poperror(); + } + if(nr > 0) + break; + + /* Advance to next element */ + c->uri++; + if(c->umc) { + cclose(c->umc); + c->umc = nil; + } + mount = mount->next; + } + runlock(&m->lock); + qunlock(&c->umqlock); + return nr; +} + +static void +unionrewind(Chan *c) +{ + qlock(&c->umqlock); + c->uri = 0; + if(c->umc){ + cclose(c->umc); + c->umc = nil; + } + qunlock(&c->umqlock); +} + +static long +rread(int fd, void *va, long n, vlong *offp) +{ + int dir; + Chan *c; + vlong off; + + if(waserror()) + return -1; + + c = fdtochan(up->env->fgrp, fd, OREAD, 1, 1); + if(waserror()) { + cclose(c); + nexterror(); + } + + if(n < 0) + error(Etoosmall); + + dir = c->qid.type & QTDIR; + if(dir && c->umh) + n = unionread(c, va, n); + else{ + if(offp == nil){ + lock(c); /* lock for vlong assignment */ + off = c->offset; + unlock(c); + }else + off = *offp; + if(off < 0) + error(Enegoff); + if(off == 0){ + if(offp == nil){ + lock(c); + c->offset = 0; + c->dri = 0; + unlock(c); + } + unionrewind(c); + } + n = devtab[c->type]->read(c, va, n, off); + lock(c); + c->offset += n; + unlock(c); + } + + poperror(); + cclose(c); + + poperror(); + return n; +} + +long +kread(int fd, void *va, long n) +{ + return rread(fd, va, n, nil); +} + +long +kpread(int fd, void *va, long n, vlong off) +{ + return rread(fd, va, n, &off); +} + +int +kremove(char *path) +{ + Chan *c; + + if(waserror()) + return -1; + + c = namec(path, Aremove, 0, 0); + if(waserror()) { + c->type = 0; /* see below */ + cclose(c); + nexterror(); + } + devtab[c->type]->remove(c); + /* + * Remove clunks the fid, but we need to recover the Chan + * so fake it up. rootclose() is known to be a nop. + */ + c->type = 0; + poperror(); + cclose(c); + + poperror(); + return 0; +} + +vlong +kseek(int fd, vlong off, int whence) +{ + Dir *dir; + Chan *c; + + if(waserror()) + return -1; + + c = fdtochan(up->env->fgrp, fd, -1, 1, 1); + if(waserror()) { + cclose(c); + nexterror(); + } + + if(devtab[c->type]->dc == '|') + error(Eisstream); + + switch(whence) { + case 0: + if(c->qid.type & QTDIR){ + if(off != 0) + error(Eisdir); + unionrewind(c); + }else if(off < 0) + error(Enegoff); + lock(c); /* lock for vlong assignment */ + c->offset = off; + unlock(c); + break; + + case 1: + if(c->qid.type & QTDIR) + error(Eisdir); + lock(c); /* lock for read/write update */ + off += c->offset; + if(off < 0){ + unlock(c); + error(Enegoff); + } + c->offset = off; + unlock(c); + break; + + case 2: + if(c->qid.type & QTDIR) + error(Eisdir); + dir = chandirstat(c); + if(dir == nil) + error("internal error: stat error in seek"); + off += dir->length; + free(dir); + if(off < 0) + error(Enegoff); + lock(c); /* lock for read/write update */ + c->offset = off; + unlock(c); + break; + + default: + error(Ebadarg); + break; + } + poperror(); + c->dri = 0; + cclose(c); + poperror(); + return off; +} + +void +validstat(uchar *s, int n) +{ + int m; + char buf[64]; + + if(statcheck(s, n) < 0) + error(Ebadstat); + /* verify that name entry is acceptable */ + s += STATFIXLEN - 4*BIT16SZ; /* location of first string */ + /* + * s now points at count for first string. + * if it's too long, let the server decide; this is + * only for his protection anyway. otherwise + * we'd have to allocate and waserror. + */ + m = GBIT16(s); + s += BIT16SZ; + if(m+1 > sizeof buf) + return; + memmove(buf, s, m); + buf[m] = '\0'; + /* name could be '/' */ + if(strcmp(buf, "/") != 0) + validname(buf, 0); +} + +int +kstat(char *path, uchar *buf, int n) +{ + Chan *c; + + if(waserror()) + return -1; + + c = namec(path, Aaccess, 0, 0); + if(waserror()){ + cclose(c); + nexterror(); + } + devtab[c->type]->stat(c, buf, n); + poperror(); + cclose(c); + + poperror(); + return 0; +} + +static long +rwrite(int fd, void *va, long n, vlong *offp) +{ + Chan *c; + vlong off; + long m; + + if(waserror()) + return -1; + c = fdtochan(up->env->fgrp, fd, OWRITE, 1, 1); + if(waserror()) { + cclose(c); + nexterror(); + } + if(c->qid.type & QTDIR) + error(Eisdir); + + if(n < 0) + error(Etoosmall); + + if(offp == nil){ + lock(c); + off = c->offset; + c->offset += n; + unlock(c); + }else + off = *offp; + + if(waserror()){ + if(offp == nil){ + lock(c); + c->offset -= n; + unlock(c); + } + nexterror(); + } + if(off < 0) + error(Enegoff); + m = devtab[c->type]->write(c, va, n, off); + poperror(); + + if(offp == nil && m < n){ + lock(c); + c->offset -= n - m; + unlock(c); + } + + poperror(); + cclose(c); + + poperror(); + return n; +} + +long +kwrite(int fd, void *va, long n) +{ + return rwrite(fd, va, n, nil); +} + +long +kpwrite(int fd, void *va, long n, vlong off) +{ + return rwrite(fd, va, n, &off); +} + +int +kwstat(char *path, uchar *buf, int n) +{ + Chan *c; + + if(waserror()) + return -1; + + validstat(buf, n); + c = namec(path, Aaccess, 0, 0); + if(waserror()){ + cclose(c); + nexterror(); + } + n = devtab[c->type]->wstat(c, buf, n); + poperror(); + cclose(c); + + poperror(); + return n; +} + +enum +{ + DIRSIZE = STATFIXLEN + 32 * 4, + DIRREADLIM = 2048, /* should handle the largest reasonable directory entry */ +}; + +Dir* +chandirstat(Chan *c) +{ + Dir *d; + uchar *buf; + int n, nd, i; + + nd = DIRSIZE; + for(i=0; i<2; i++){ /* should work by the second try */ + d = smalloc(sizeof(Dir) + nd); + buf = (uchar*)&d[1]; + if(waserror()){ + free(d); + return nil; + } + n = devtab[c->type]->stat(c, buf, nd); + poperror(); + if(n < BIT16SZ){ + free(d); + return nil; + } + nd = GBIT16((uchar*)buf) + BIT16SZ; /* size needed to store whole stat buffer including count */ + if(nd <= n){ + convM2D(buf, n, d, (char*)&d[1]); + return d; + } + /* else sizeof(Dir)+nd is plenty */ + free(d); + } + return nil; + +} + +Dir* +kdirstat(char *name) +{ + Chan *c; + Dir *d; + + if(waserror()) + return nil; + + c = namec(name, Aaccess, 0, 0); + if(waserror()){ + cclose(c); + nexterror(); + } + d = chandirstat(c); + poperror(); + cclose(c); + + poperror(); + return d; +} + +Dir* +kdirfstat(int fd) +{ + Chan *c; + Dir *d; + + if(waserror()) + return nil; + + c = fdtochan(up->env->fgrp, fd, -1, 0, 1); + if(waserror()) { + cclose(c); + nexterror(); + } + d = chandirstat(c); + poperror(); + cclose(c); + + poperror(); + return d; +} + +int +kdirwstat(char *name, Dir *dir) +{ + uchar *buf; + int r; + + r = sizeD2M(dir); + buf = smalloc(r); + convD2M(dir, buf, r); + r = kwstat(name, buf, r); + free(buf); + return r < 0? r: 0; +} + +int +kdirfwstat(int fd, Dir *dir) +{ + uchar *buf; + int r; + + r = sizeD2M(dir); + buf = smalloc(r); + convD2M(dir, buf, r); + r = kfwstat(fd, buf, r); + free(buf); + return r < 0? r: 0; +} + +static long +dirpackage(uchar *buf, long ts, Dir **d) +{ + char *s; + long ss, i, n, nn, m; + + *d = nil; + if(ts <= 0) + return ts; + + /* + * first find number of all stats, check they look like stats, & size all associated strings + */ + ss = 0; + n = 0; + for(i = 0; i < ts; i += m){ + m = BIT16SZ + GBIT16(&buf[i]); + if(statcheck(&buf[i], m) < 0) + break; + ss += m; + n++; + } + + if(i != ts) + error("bad directory format"); + + *d = malloc(n * sizeof(Dir) + ss); + if(*d == nil) + error(Enomem); + + /* + * then convert all buffers + */ + s = (char*)*d + n * sizeof(Dir); + nn = 0; + for(i = 0; i < ts; i += m){ + m = BIT16SZ + GBIT16((uchar*)&buf[i]); + if(nn >= n || convM2D(&buf[i], m, *d + nn, s) != m){ + free(*d); + *d = nil; + error("bad directory entry"); + } + nn++; + s += m; + } + + return nn; +} + +long +kdirread(int fd, Dir **d) +{ + uchar *buf; + long ts; + + *d = nil; + if(waserror()) + return -1; + buf = malloc(DIRREADLIM); + if(buf == nil) + error(Enomem); + if(waserror()){ + free(buf); + nexterror(); + } + ts = kread(fd, buf, DIRREADLIM); + if(ts >= 0) + ts = dirpackage(buf, ts, d); + poperror(); + free(buf); + poperror(); + return ts; +} + +int +kiounit(int fd) +{ + Chan *c; + int n; + + c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1); + if(waserror()){ + cclose(c); + return 0; /* n.b. */ + } + n = c->iounit; + poperror(); + cclose(c); + return n; +} diff --git a/os/port/taslock.c b/os/port/taslock.c new file mode 100644 index 00000000..7914b80a --- /dev/null +++ b/os/port/taslock.c @@ -0,0 +1,126 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +static void +lockloop(Lock *l, ulong pc) +{ + setpanic(); + print("lock loop 0x%lux key 0x%lux pc 0x%lux held by pc 0x%lux\n", l, l->key, pc, l->pc); + panic("lockloop"); +} + +void +lock(Lock *l) +{ + int i; + ulong pc; + + pc = getcallerpc(&l); + if(up == 0) { + if (_tas(&l->key) != 0) { + for(i=0; ; i++) { + if(_tas(&l->key) == 0) + break; + if (i >= 1000000) { + lockloop(l, pc); + break; + } + } + } + l->pc = pc; + return; + } + + for(i=0; ; i++) { + if(_tas(&l->key) == 0) + break; + if (i >= 1000) { + lockloop(l, pc); + break; + } + if(conf.nmach == 1 && up->state == Running && islo()) { + up->pc = pc; + sched(); + } + } + l->pri = up->pri; + up->pri = PriLock; + l->pc = pc; +} + +void +ilock(Lock *l) +{ + ulong x, pc; + int i; + + pc = getcallerpc(&l); + x = splhi(); + for(;;) { + if(_tas(&l->key) == 0) { + l->sr = x; + l->pc = pc; + return; + } + if(conf.nmach < 2) + panic("ilock: no way out: pc 0x%lux: lock 0x%lux held by pc 0x%lux", pc, l, l->pc); + for(i=0; ; i++) { + if(l->key == 0) + break; + clockcheck(); + if (i > 100000) { + lockloop(l, pc); + break; + } + } + } +} + +int +canlock(Lock *l) +{ + if(_tas(&l->key)) + return 0; + if(up){ + l->pri = up->pri; + up->pri = PriLock; + } + l->pc = getcallerpc(&l); + return 1; +} + +void +unlock(Lock *l) +{ + int p; + + if(l->key == 0) + print("unlock: not locked: pc %lux\n", getcallerpc(&l)); + p = l->pri; + l->pc = 0; + l->key = 0; + coherence(); + if(up){ + up->pri = p; + if(up->state == Running && anyhigher()) + sched(); + } +} + +void +iunlock(Lock *l) +{ + ulong sr; + + if(l->key == 0) + print("iunlock: not locked: pc %lux\n", getcallerpc(&l)); + sr = l->sr; + l->pc = 0; + l->key = 0; + coherence(); + splxpc(sr); +} diff --git a/os/port/tod.c b/os/port/tod.c new file mode 100644 index 00000000..55486453 --- /dev/null +++ b/os/port/tod.c @@ -0,0 +1,283 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +/* compute nanosecond epoch time from the fastest ticking clock + * on the system. converting the time to nanoseconds requires + * the following formula + * + * t = (((1000000000<<31)/f)*ticks)>>31 + * + * where + * + * 'f' is the clock frequency + * 'ticks' are clock ticks + * + * to avoid too much calculation in todget(), we calculate + * + * mult = (1000000000<<32)/f + * + * each time f is set. f is normally set by a user level + * program writing to /dev/fastclock. mul64fract will then + * take that fractional multiplier and a 64 bit integer and + * return the resulting integer product. + * + * We assume that the cpu's of a multiprocessor are synchronized. + * This assumption needs to be questioned with each new architecture. + */ + +/* frequency of the tod clock */ +#define TODFREQ 1000000000ULL + +struct { + int init; // true if initialized + ulong cnt; + Lock; + uvlong multiplier; // t = off + (multiplier*ticks)>>31 + uvlong divider; // ticks = (divider*(ticks-off))>>31 + vlong hz; // frequency of fast clock + vlong last; // last reading of fast clock + vlong off; // offset from epoch to last + vlong lasttime; // last return value from todget + vlong delta; // add 'delta' each slow clock tick from sstart to send + ulong sstart; // ... + ulong send; // ... +} tod; + +void +todinit(void) +{ + if(tod.init) + return; + ilock(&tod); + tod.last = fastticks((uvlong*)&tod.hz); + iunlock(&tod); + todsetfreq(tod.hz); + tod.init = 1; + addclock0link(todfix, 100); +} + +/* + * calculate multiplier + */ +void +todsetfreq(vlong f) +{ + ilock(&tod); + tod.hz = f; + + /* calculate multiplier for time conversion */ + tod.multiplier = mk64fract(TODFREQ, f); + tod.divider = mk64fract(f, TODFREQ); + + iunlock(&tod); +} + +/* + * Set the time of day struct + */ +void +todset(vlong t, vlong delta, int n) +{ + if(!tod.init) + todinit(); + + ilock(&tod); + if(t >= 0){ + tod.off = t; + tod.last = fastticks(nil); + tod.lasttime = 0; + tod.delta = 0; + tod.sstart = tod.send; + } else { + if(n <= 0) + n = 1; + n *= HZ; + if(delta < 0 && n > -delta) + n = -delta; + if(delta > 0 && n > delta) + n = delta; + delta = delta/n; + tod.sstart = MACHP(0)->ticks; + tod.send = tod.sstart + n; + tod.delta = delta; + } + iunlock(&tod); +} + +/* + * get time of day + */ +vlong +todget(vlong *ticksp) +{ + uvlong x; + vlong ticks, diff; + ulong t; + + if(!tod.init) + todinit(); + + // we don't want time to pass twixt the measuring of fastticks + // and grabbing tod.last. Also none of the vlongs are atomic so + // we have to look at them inside the lock. + ilock(&tod); + tod.cnt++; + ticks = fastticks(nil); + + // add in correction + if(tod.sstart != tod.send){ + t = MACHP(0)->ticks; + if(t >= tod.send) + t = tod.send; + tod.off = tod.off + tod.delta*(t - tod.sstart); + tod.sstart = t; + } + + // convert to epoch + diff = ticks - tod.last; + if(diff < 0) + diff = 0; + mul64fract(&x, diff, tod.multiplier); + x += tod.off; + + // time can't go backwards + if(x < tod.lasttime) + x = tod.lasttime; + else + tod.lasttime = x; + + iunlock(&tod); + + if(ticksp != nil) + *ticksp = ticks; + + return x; +} + +/* + * convert time of day to ticks + */ +uvlong +tod2fastticks(vlong ns) +{ + uvlong x; + + ilock(&tod); + mul64fract(&x, ns-tod.off, tod.divider); + x += tod.last; + iunlock(&tod); + return x; +} + +/* + * called regularly to avoid calculation overflows + */ +void +todfix(void) +{ + vlong ticks, diff; + uvlong x; + + ticks = fastticks(nil); + + diff = ticks - tod.last; + if(diff > tod.hz){ + ilock(&tod); + + // convert to epoch + mul64fract(&x, diff, tod.multiplier); +if(x > 30000000000ULL) print("todfix %llud\n", x); + x += tod.off; + + // protect against overflows + tod.last = ticks; + tod.off = x; + + iunlock(&tod); + } +} + +long +tseconds(void) +{ + vlong x; + int i; + + x = todget(nil); + x = x/TODFREQ; + i = x; + return i; +} + +// convert milliseconds to fast ticks +// +uvlong +ms2fastticks(ulong ms) +{ + if(!tod.init) + todinit(); + return (tod.hz*ms)/1000ULL; +} + +/* + * convert nanoseconds to fast ticks + */ +uvlong +ns2fastticks(uvlong ns) +{ + uvlong res; + + if(!tod.init) + todinit(); + mul64fract(&res, ns, tod.divider); + return res; +} + +/* + * convert fast ticks to ns + */ +uvlong +fastticks2ns(uvlong ticks) +{ + uvlong res; + + if(!tod.init) + todinit(); + mul64fract(&res, ticks, tod.multiplier); + return res; +} + +/* + * Make a 64 bit fixed point number that has a decimal point + * to the left of the low order 32 bits. This is used with + * mul64fract for converting twixt nanoseconds and fastticks. + * + * multiplier = (to<<32)/from + */ +uvlong +mk64fract(uvlong to, uvlong from) +{ +/* + int shift; + + if(to == 0ULL) + return 0ULL; + + shift = 0; + while(shift < 32 && to < (1ULL<<(32+24))){ + to <<= 8; + shift += 8; + } + while(shift < 32 && to < (1ULL<<(32+31))){ + to <<= 1; + shift += 1; + } + + return (to/from)<<(32-shift); +*/ + return (to<<32)/from; +} diff --git a/os/port/uart.h b/os/port/uart.h new file mode 100644 index 00000000..370ee09a --- /dev/null +++ b/os/port/uart.h @@ -0,0 +1,104 @@ +typedef struct PhysUart PhysUart; +typedef struct Uart Uart; + +/* + * routines to access UART hardware + */ +struct PhysUart +{ + char* name; + Uart* (*pnp)(void); + void (*enable)(Uart*, int); + void (*disable)(Uart*); + void (*kick)(Uart*); + void (*dobreak)(Uart*, int); + int (*baud)(Uart*, int); + int (*bits)(Uart*, int); + int (*stop)(Uart*, int); + int (*parity)(Uart*, int); + void (*modemctl)(Uart*, int); + void (*rts)(Uart*, int); + void (*dtr)(Uart*, int); + long (*status)(Uart*, void*, long, long); + void (*fifo)(Uart*, int); + void (*power)(Uart*, int); + int (*getc)(Uart*); /* polling versions, for iprint, rdb */ + void (*putc)(Uart*, int); +}; + +enum { + Stagesize= 1024 +}; + +/* + * software UART + */ +struct Uart +{ + void* regs; /* hardware stuff */ + void* saveregs; /* place to put registers on power down */ + char* name; /* internal name */ + ulong freq; /* clock frequency */ + int bits; /* bits per character */ + int stop; /* stop bits */ + int parity; /* even, odd or no parity */ + int baud; /* baud rate */ + PhysUart*phys; + int console; /* used as a serial console */ + int special; /* internal kernel device */ + Uart* next; /* list of allocated uarts */ + + QLock; + int type; /* ?? */ + int dev; + int opens; + + int enabled; + Uart *elist; /* next enabled interface */ + + int perr; /* parity errors */ + int ferr; /* framing errors */ + int oerr; /* rcvr overruns */ + int berr; /* no input buffers */ + int serr; /* input queue overflow */ + + /* buffers */ + int (*putc)(Queue*, int); + Queue *iq; + Queue *oq; + + Lock rlock; + uchar istage[Stagesize]; + uchar *iw; + uchar *ir; + uchar *ie; + + Lock tlock; /* transmit */ + uchar ostage[Stagesize]; + uchar *op; + uchar *oe; + int drain; + + int modem; /* hardware flow control on */ + int xonoff; /* software flow control on */ + int blocked; + int cts, dsr, dcd, dcdts; /* keep track of modem status */ + int ctsbackoff; + int hup_dsr, hup_dcd; /* send hangup upstream? */ + int dohup; + + Rendez r; +}; + +extern Uart* consuart; + +extern int uartctl(Uart*, char*); +extern int uartgetc(void); +extern void uartkick(void*); +extern void uartmouse(Uart*, int (*)(Queue*, int), int); +extern void uartsetmouseputc(Uart*, int (*)(Queue*, int)); +extern void uartputc(int); +extern void uartputs(char*, int); +extern void uartrecv(Uart*, char); +extern Uart* uartsetup(Uart*); +extern int uartstageoutput(Uart*); diff --git a/os/port/xalloc.c b/os/port/xalloc.c new file mode 100644 index 00000000..73310089 --- /dev/null +++ b/os/port/xalloc.c @@ -0,0 +1,246 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +#define datoff ((ulong)((Xhdr*)0)->data) + +enum +{ + Chunk = 64*1024, + Nhole = 128, + Magichole = 0xDeadBabe, +}; + +typedef struct Hole Hole; +typedef struct Xalloc Xalloc; +typedef struct Xhdr Xhdr; + +struct Hole +{ + ulong addr; + ulong size; + ulong top; + Hole* link; +}; + +struct Xhdr +{ + ulong size; + ulong magix; + char data[1]; +}; + +struct Xalloc +{ + Lock; + Hole hole[Nhole]; + Hole* flist; + Hole* table; +}; + +static Xalloc xlists; + +static void +ixprt() +{ + xsummary(); + ixsummary(); +} + +void +xinit(void) +{ + Hole *h, *eh; + + eh = &xlists.hole[Nhole-1]; + for(h = xlists.hole; h < eh; h++) + h->link = h+1; + + xlists.flist = xlists.hole; + + if(conf.npage1) + xhole(conf.base1, conf.npage1*BY2PG); + conf.npage1 = conf.base1+(conf.npage1*BY2PG); + + if(conf.npage0) + xhole(conf.base0, conf.npage0*BY2PG); + conf.npage0 = conf.base0+(conf.npage0*BY2PG); + + /* Save the bounds of kernel alloc memory for kernel mmu mapping */ + conf.base0 = (ulong)KADDR(conf.base0); + conf.base1 = (ulong)KADDR(conf.base1); + conf.npage0 = (ulong)KADDR(conf.npage0); + conf.npage1 = (ulong)KADDR(conf.npage1); + + debugkey('x', "xalloc/ialloc", ixprt, 0); +} + +void* +xspanalloc(ulong size, int align, ulong span) +{ + ulong a, v, t; + + a = (ulong)xalloc(size+align+span); + if(a == 0) + panic("xspanalloc: %lud %d %lux\n", size, align, span); + + if(span > 2) { + v = (a + span) & ~(span-1); + t = v - a; + if(t > 0) + xhole(PADDR(a), t); + t = a + span - v; + if(t > 0) + xhole(PADDR(v+size+align), t); + } + else + v = a; + + if(align > 1) + v = (v + align) & ~(align-1); + + return (void*)v; +} + +void* +xallocz(ulong size, int zero) +{ + Xhdr *p; + Hole *h, **l; + + size += BY2V + sizeof(Xhdr); + size &= ~(BY2V-1); + + ilock(&xlists); + l = &xlists.table; + for(h = *l; h; h = h->link) { + if(h->size >= size) { + p = (Xhdr*)h->addr; + h->addr += size; + h->size -= size; + if(h->size == 0) { + *l = h->link; + h->link = xlists.flist; + xlists.flist = h; + } + iunlock(&xlists); + p = KADDR(p); + p->magix = Magichole; + p->size = size; + if(zero) + memset(p->data, 0, size); + return p->data; + } + l = &h->link; + } + iunlock(&xlists); + return nil; +} + +void* +xalloc(ulong size) +{ + return xallocz(size, 1); +} + +void +xfree(void *p) +{ + Xhdr *x; + + x = (Xhdr*)((ulong)p - datoff); + if(x->magix != Magichole) { + xsummary(); + panic("xfree(0x%lux) 0x%lux!=0x%lux", p, (ulong)Magichole, x->magix); + } + xhole(PADDR(x), x->size); +} + +int +xmerge(void *vp, void *vq) +{ + Xhdr *p, *q; + + p = vp; + if((uchar*)vp+p->size == (uchar*)vq) { + q = vq; + p->size += q->size; + return 1; + } + return 0; +} + +void +xhole(ulong addr, ulong size) +{ + ulong top; + Hole *h, *c, **l; + + if(size == 0) + return; + + top = addr + size; + ilock(&xlists); + l = &xlists.table; + for(h = *l; h; h = h->link) { + if(h->top == addr) { + h->size += size; + h->top = h->addr+h->size; + c = h->link; + if(c && h->top == c->addr) { + h->top += c->size; + h->size += c->size; + h->link = c->link; + c->link = xlists.flist; + xlists.flist = c; + } + iunlock(&xlists); + return; + } + if(h->addr > addr) + break; + l = &h->link; + } + if(h && top == h->addr) { + h->addr -= size; + h->size += size; + iunlock(&xlists); + return; + } + + if(xlists.flist == nil) { + iunlock(&xlists); + print("xfree: no free holes, leaked %lud bytes\n", size); + return; + } + + h = xlists.flist; + xlists.flist = h->link; + h->addr = addr; + h->top = top; + h->size = size; + h->link = *l; + *l = h; + iunlock(&xlists); +} + +void +xsummary(void) +{ + int i; + Hole *h; + + i = 0; + for(h = xlists.flist; h; h = h->link) + i++; + + print("%d holes free\n", i); + i = 0; + for(h = xlists.table; h; h = h->link) { + print("%.8lux %.8lux %lud\n", h->addr, h->top, h->size); + i += h->size; + } + print("%d bytes free\n", i); +} diff --git a/os/pxa/NOTICE b/os/pxa/NOTICE new file mode 100644 index 00000000..61b95632 --- /dev/null +++ b/os/pxa/NOTICE @@ -0,0 +1,2 @@ +Inferno® Copyright © 1996-1999 Lucent Technologies Inc. All rights reserved. +PXA 25x Inferno port Copyright © 2000,2003,2004 Vita Nuova Holdings Limited. All rights reserved. diff --git a/os/pxa/clock.c b/os/pxa/clock.c new file mode 100644 index 00000000..2fd6eba6 --- /dev/null +++ b/os/pxa/clock.c @@ -0,0 +1,318 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "ureg.h" + +static ulong timer_incr[4] = { 0, 0, 0, -1 }; + +typedef struct Clock0link Clock0link; +typedef struct Clock0link { + void (*clock)(void); + Clock0link* link; +} Clock0link; + +static Clock0link *clock0link; +static Lock clock0lock; +static void (*prof_fcn)(Ureg *, int); + +Timer* +addclock0link(void (*clock)(void), int) +{ + Clock0link *lp; + + if((lp = malloc(sizeof(Clock0link))) == 0){ + print("addclock0link: too many links\n"); + return nil; + } + ilock(&clock0lock); + lp->clock = clock; + lp->link = clock0link; + clock0link = lp; + iunlock(&clock0lock); + return nil; +} + +static void +profintr(Ureg *ur, void*) +{ + OstmrReg *ost = OSTMRREG; + int t; + + if((ost->osmr[3] - ost->oscr) < 2*CLOCKFREQ) { + /* less than 2 seconds before reset, say something */ + setpanic(); + clockpoll(); + dumpregs(ur); + panic("Watchdog timer will expire"); + } + + /* advance the profile clock tick */ + ost->osmr[2] += timer_incr[2]; + ost->ossr = (1 << 2); /* Clear the SR */ + t = 1; + while((ost->osmr[2] - ost->oscr) > 0x80000000) { + ost->osmr[2] += timer_incr[2]; + t++; + } + if(prof_fcn) + prof_fcn(ur, t); +} + +static void +clockintr(Ureg*, void*) +{ + Clock0link *lp; + int losttick = 0; + OstmrReg *ost = OSTMRREG; + +{static int tag, led; if(++tag >= HZ){ledset(led ^= 1);tag=0;}} + m->ticks++; +// ost->osmr[3] = ost->oscr + timer_incr[3]; /* restart the watchdog */ + ost->osmr[0] += timer_incr[0]; /* advance the clock tick */ + ost->ossr = (1<<0); /* Clear the SR */ + + while((ost->osmr[0] - ost->oscr) >= 0x80000000) { + ost->osmr[0] += timer_incr[0]; + losttick++; + m->ticks++; + } + + checkalarms(); + + if(canlock(&clock0lock)){ + for(lp = clock0link; lp; lp = lp->link) + if(lp->clock) + lp->clock(); + unlock(&clock0lock); + } + + /* round robin time slice is done by trap.c and proc.c */ +} + +void +timerenable( int timer, int Hz, void (*f)(Ureg *, void*), void* a) +{ + OstmrReg *ost = OSTMRREG; + char name[KNAMELEN]; + + if(timer < 0 || timer > 3) + return; + timer_incr[timer] = CLOCKFREQ/Hz; /* set up freq */ + ost->osmr[timer] = ost->oscr+timer_incr[timer]; + snprint(name, sizeof(name), "timer%d", timer); + intrenable(IRQ, IRQtimer0+timer, f, a, name); + ost->ossr = (1 << timer); /* clear any pending interrupt */ + ost->oier |= (1 << timer); /* enable interrupt */ +} + +void +timerdisable( int timer ) +{ + OstmrReg *ost = OSTMRREG; + + if(timer < 0 || timer > 3) + return; + ost->osmr[timer] = 0; /* clear freq */ + ost->oier &= ~(1 << timer); /* disable interrupt */ +} + +void +installprof(void (*pf)(Ureg *, int)) +{ + int s; + + s = splfhi(); + prof_fcn = pf; + timerenable( 2, HZ+1, profintr, 0); + timer_incr[2] = timer_incr[0]+63; /* fine tuning */ + splx(s); +} + +void +clockinit(void) +{ + OstmrReg *ost = OSTMRREG; + m->ticks = 0; + /* Set up timer registers */ + ost->ossr = 0xf; /* clear all four OSSR trigger bits */ + ost->oier = 0; + ost->osmr[0] = 0; + ost->osmr[1] = 0; + ost->osmr[2] = 0; + // ost->osmr[3] = 0; + timerenable( 0, HZ, clockintr, 0); +// timer_incr[3] = CLOCKFREQ*10; /* 10 second watchdog */ +// timer_setwatchdog(timer_incr[3]); +// timerenable( 2, 1, profintr, 0); /* watch the watchdog */ +} + +void +clockpoll(void) +{ + OstmrReg *ost = OSTMRREG; +// ost->osmr[3] = ost->oscr + timer_incr[3]; /* restart the watchdog */ +} + +void +clockcheck(void) +{ + OstmrReg *ost = OSTMRREG; + + if((ost->osmr[3] - ost->oscr) < CLOCKFREQ) { + setpanic(); + clockpoll(); + dumpstack(); + panic("Watchdog timer will expire"); + } +} + +// macros for fixed-point math + +ulong _mularsv(ulong m0, ulong m1, ulong a, ulong s); + +/* truncated: */ +#define FXDPTDIV(a,b,n) ((ulong)(((uvlong)(a) << (n)) / (b))) +#define MAXMUL(a,n) ((ulong)((((uvlong)1<<(n))-1)/(a))) +#define MULDIV(x,a,b,n) (((x)*FXDPTDIV(a,b,n)) >> (n)) +#define MULDIV64(x,a,b,n) ((ulong)_mularsv(x, FXDPTDIV(a,b,n), 0, (n))) + +/* rounded: */ +#define FXDPTDIVR(a,b,n) ((ulong)((((uvlong)(a) << (n))+((b)/2)) / (b))) +#define MAXMULR(a,n) ((ulong)((((uvlong)1<<(n))-1)/(a))) +#define MULDIVR(x,a,b,n) (((x)*FXDPTDIVR(a,b,n)+(1<<((n)-1))) >> (n)) +#define MULDIVR64(x,a,b,n) ((ulong)_mularsv(x, FXDPTDIVR(a,b,n), 1<<((n)-1), (n))) + + +// these routines are all limited to a maximum of 1165 seconds, +// due to the wrap-around of the OSTIMER + +ulong +timer_start(void) +{ + return OSTMRREG->oscr; +} + +ulong +timer_ticks(ulong t0) +{ + return OSTMRREG->oscr - t0; +} + +int +timer_devwait(ulong *adr, ulong mask, ulong val, int ost) +{ + int i; + ulong t0 = timer_start(); + while((*adr & mask) != val) + if(timer_ticks(t0) > ost) + return ((*adr & mask) == val) ? 0 : -1; + else + for(i = 0; i < 10; i++); /* don't pound OSCR too hard! (why not?) */ + return 0; +} + +void +timer_setwatchdog(int t) +{ + OstmrReg *ost = OSTMRREG; + ost->osmr[3] = ost->oscr + t; + if(t) { + ost->ossr = (1<<3); + ost->oier |= (1<<3); + ost->ower = 1; + } else + ost->oier &= ~(1<<3); +} + +void +timer_delay(int t) +{ + ulong t0 = timer_start(); + while(timer_ticks(t0) < t) + ; +} + + +ulong +us2tmr(int us) +{ + return MULDIV64(us, CLOCKFREQ, 1000000, 24); +} + +int +tmr2us(ulong t) +{ + return MULDIV64(t, 1000000, CLOCKFREQ, 24); +} + +void +microdelay(int us) +{ + ulong t0 = timer_start(); + ulong t = us2tmr(us); + while(timer_ticks(t0) <= t) + ; +} + +ulong +ms2tmr(int ms) +{ + return MULDIV64(ms, CLOCKFREQ, 1000, 20); +} + +int +tmr2ms(ulong t) +{ + return MULDIV64(t, 1000, CLOCKFREQ, 32); +} + +void +delay(int ms) +{ + ulong t0 = timer_start(); + ulong t = ms2tmr(ms); + while(timer_ticks(t0) <= t) + clockpoll(); +} + +/* + * for devbench.c + */ +vlong +archrdtsc(void) +{ + return OSTMRREG->oscr; +} + +ulong +archrdtsc32(void) +{ + return OSTMRREG->oscr; +} + +/* + * for devkprof.c + */ +long +archkprofmicrosecondspertick(void) +{ + return MS2HZ*1000; +} + +void +archkprofenable(int) +{ + /* TO DO */ +} + +uvlong +fastticks(uvlong *hz) +{ + if(hz) + *hz = HZ; + return m->ticks; +} diff --git a/os/pxa/devether.c b/os/pxa/devether.c new file mode 100644 index 00000000..4b9da104 --- /dev/null +++ b/os/pxa/devether.c @@ -0,0 +1,617 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +static Ether *etherxx[MaxEther]; + +Chan* +etherattach(char* spec) +{ + ulong ctlrno; + char *p; + Chan *chan; + Ether *ether; + + ctlrno = 0; + if(spec && *spec){ + ctlrno = strtoul(spec, &p, 0); + if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther)) + error(Ebadarg); + } + if((ether = etherxx[ctlrno]) == 0) + error(Enodev); + rlock(ether); + if(waserror()){ + runlock(ether); + nexterror(); + } + chan = devattach('l', spec); + chan->dev = ctlrno; + if(ether->attach) + ether->attach(etherxx[ctlrno]); + poperror(); + runlock(ether); + return chan; +} + +static void +ethershutdown(void) +{ + Ether *ether; + int i; + + for(i=0; idetach != nil) + ether->detach(ether); + } +} + +static Walkqid* +etherwalk(Chan* chan, Chan *nchan, char **name, int nname) +{ + Walkqid *wq; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + wq = netifwalk(etherxx[chan->dev], chan, nchan, name, nname); + poperror(); + runlock(ether); + return wq; +} + +static int +etherstat(Chan* chan, uchar* dp, int n) +{ + int s; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + s = netifstat(ether, chan, dp, n); + poperror(); + runlock(ether); + return s; +} + +static Chan* +etheropen(Chan* chan, int omode) +{ + Chan *c; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + c = netifopen(ether, chan, omode); + poperror(); + runlock(ether); + return c; +} + +static void +ethercreate(Chan*, char*, int, ulong) +{ +} + +static void +etherclose(Chan* chan) +{ + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + netifclose(ether, chan); + poperror(); + runlock(ether); +} + +static long +etherread(Chan* chan, void* buf, long n, vlong off) +{ + Ether *ether; + ulong offset = off; + long r; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + if((chan->qid.type & QTDIR) == 0 && ether->ifstat){ + /* + * With some controllers it is necessary to reach + * into the chip to extract statistics. + */ + if(NETTYPE(chan->qid.path) == Nifstatqid){ + r = ether->ifstat(ether, buf, n, offset); + goto out; + } + if(NETTYPE(chan->qid.path) == Nstatqid) + ether->ifstat(ether, buf, 0, offset); + } + r = netifread(ether, chan, buf, n, offset); +out: + poperror(); + runlock(ether); + return r; +} + +static Block* +etherbread(Chan* chan, long n, ulong offset) +{ + Block *b; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + b = netifbread(ether, chan, n, offset); + poperror(); + runlock(ether); + return b; +} + +static int +etherwstat(Chan* chan, uchar* dp, int n) +{ + Ether *ether; + int r; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + r = netifwstat(ether, chan, dp, n); + poperror(); + runlock(ether); + return r; +} + +static void +etherrtrace(Netfile* f, Etherpkt* pkt, int len) +{ + int i, n; + Block *bp; + + if(qwindow(f->in) <= 0) + return; + if(len > 58) + n = 58; + else + n = len; + bp = iallocb(64); + if(bp == nil) + return; + memmove(bp->wp, pkt->d, n); + i = TK2MS(MACHP(0)->ticks); + bp->wp[58] = len>>8; + bp->wp[59] = len; + bp->wp[60] = i>>24; + bp->wp[61] = i>>16; + bp->wp[62] = i>>8; + bp->wp[63] = i; + bp->wp += 64; + qpass(f->in, bp); +} + +Block* +etheriq(Ether* ether, Block* bp, int fromwire) +{ + Etherpkt *pkt; + ushort type; + int len, multi, tome, fromme; + Netfile **ep, *f, **fp, *fx; + Block *xbp; + + ether->inpackets++; + + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + type = (pkt->type[0]<<8)|pkt->type[1]; + fx = 0; + ep = ðer->f[Ntypes]; + + multi = pkt->d[0] & 1; + /* check for valid multcast addresses */ + if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){ + if(!activemulti(ether, pkt->d, sizeof(pkt->d))){ + if(fromwire){ + freeb(bp); + bp = 0; + } + return bp; + } + } + + /* is it for me? */ + tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0; + + /* + * Multiplex the packet to all the connections which want it. + * If the packet is not to be used subsequently (fromwire != 0), + * attempt to simply pass it into one of the connections, thereby + * saving a copy of the data (usual case hopefully). + */ + for(fp = ether->f; fp < ep; fp++){ + if((f = *fp) && (f->type == type || f->type < 0)) + if(tome || multi || f->prom){ + /* Don't want to hear bridged packets */ + if(f->bridge && !fromwire && !fromme) + continue; + if(!f->headersonly){ + if(fromwire && fx == 0) + fx = f; + else if(xbp = iallocb(len)){ + memmove(xbp->wp, pkt, len); + xbp->wp += len; + if(qpass(f->in, xbp) < 0) + ether->soverflows++; + } + else + ether->soverflows++; + } + else + etherrtrace(f, pkt, len); + } + } + + if(fx){ + if(qpass(fx->in, bp) < 0) + ether->soverflows++; + return 0; + } + if(fromwire){ + freeb(bp); + return 0; + } + + return bp; +} + +static int +etheroq(Ether* ether, Block* bp) +{ + int len, loopback, s; + Etherpkt *pkt; + + ether->outpackets++; + + /* + * Check if the packet has to be placed back onto the input queue, + * i.e. if it's a loopback or broadcast packet or the interface is + * in promiscuous mode. + * If it's a loopback packet indicate to etheriq that the data isn't + * needed and return, etheriq will pass-on or free the block. + * To enable bridging to work, only packets that were originated + * by this interface are fed back. + */ + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){ + s = splhi(); + etheriq(ether, bp, 0); + splx(s); + } + + if(!loopback){ + qbwrite(ether->oq, bp); + if(ether->transmit != nil) + ether->transmit(ether); + }else + freeb(bp); + + return len; +} + +static long +etherwrite(Chan* chan, void* buf, long n, vlong) +{ + Ether *ether; + Block *bp; + int onoff; + Cmdbuf *cb; + long l; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + if(NETTYPE(chan->qid.path) != Ndataqid) { + l = netifwrite(ether, chan, buf, n); + if(l >= 0) + goto out; + cb = parsecmd(buf, n); + if(strcmp(cb->f[0], "nonblocking") == 0){ + if(cb->nf <= 1) + onoff = 1; + else + onoff = atoi(cb->f[1]); + qnoblock(ether->oq, onoff); + free(cb); + goto out; + } + free(cb); + if(ether->ctl!=nil){ + l = ether->ctl(ether,buf,n); + goto out; + } + error(Ebadctl); + } + + if(n > ether->maxmtu) + error(Etoobig); + if(n < ether->minmtu) + error(Etoosmall); + bp = allocb(n); + if(waserror()){ + freeb(bp); + nexterror(); + } + memmove(bp->rp, buf, n); + memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen); + bp->wp += n; + poperror(); + + l = etheroq(ether, bp); +out: + poperror(); + runlock(ether); + return l; +} + +static long +etherbwrite(Chan* chan, Block* bp, ulong) +{ + Ether *ether; + long n; + + n = BLEN(bp); + if(NETTYPE(chan->qid.path) != Ndataqid){ + if(waserror()) { + freeb(bp); + nexterror(); + } + n = etherwrite(chan, bp->rp, n, 0); + poperror(); + freeb(bp); + return n; + } + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + if(n > ether->maxmtu){ + freeb(bp); + error(Etoobig); + } + if(n < ether->minmtu){ + freeb(bp); + error(Etoosmall); + } + n = etheroq(ether, bp); + poperror(); + runlock(ether); + return n; +} + +static struct { + char* type; + int (*reset)(Ether*); +} cards[MaxEther+1]; + +void +addethercard(char* t, int (*r)(Ether*)) +{ + static int ncard; + + if(ncard == MaxEther) + panic("too many ether cards"); + cards[ncard].type = t; + cards[ncard].reset = r; + ncard++; +} + +int +parseether(uchar *to, char *from) +{ + char nip[4]; + char *p; + int i; + + p = from; + for(i = 0; i < Eaddrlen; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +static void +etherreset(void) +{ + Ether *ether; + int i, n, ctlrno; + char name[KNAMELEN], buf[128]; + + for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){ + if(ether == 0) + ether = malloc(sizeof(Ether)); + memset(ether, 0, sizeof(Ether)); + ether->ctlrno = ctlrno; + ether->mbps = 10; + ether->minmtu = ETHERMINTU; + ether->maxmtu = ETHERMAXTU; + ether->itype = -1; + + if(archether(ctlrno, ether) <= 0) + continue; + + for(n = 0; cards[n].type; n++){ + if(cistrcmp(cards[n].type, ether->type)) + continue; + for(i = 0; i < ether->nopt; i++){ + if(cistrncmp(ether->opt[i], "ea=", 3) == 0){ + if(parseether(ether->ea, ðer->opt[i][3]) == -1) + memset(ether->ea, 0, Eaddrlen); + }else if(cistrcmp(ether->opt[i], "fullduplex") == 0 || + cistrcmp(ether->opt[i], "10BASE-TFD") == 0) + ether->fullduplex = 1; + else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0) + ether->mbps = 100; + } + if(cards[n].reset(ether)) + break; + snprint(name, sizeof(name), "ether%d", ctlrno); + + if(ether->interrupt != nil) + intrenable(ether->itype, ether->irq, ether->interrupt, ether, name); + + i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %lud", + ctlrno, ether->type, ether->mbps, ether->port, ether->irq); + if(ether->mem) + i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem)); + if(ether->size) + i += sprint(buf+i, " size 0x%luX", ether->size); + i += sprint(buf+i, ": %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX", + ether->ea[0], ether->ea[1], ether->ea[2], + ether->ea[3], ether->ea[4], ether->ea[5]); + sprint(buf+i, "\n"); + iprint(buf); + + if(ether->mbps == 100){ + netifinit(ether, name, Ntypes, 256*1024); + if(ether->oq == 0) + ether->oq = qopen(256*1024, Qmsg, 0, 0); + } + else{ + netifinit(ether, name, Ntypes, 64*1024); + if(ether->oq == 0) + ether->oq = qopen(64*1024, Qmsg, 0, 0); + } + if(ether->oq == 0) + panic("etherreset %s", name); + ether->alen = Eaddrlen; + memmove(ether->addr, ether->ea, Eaddrlen); + memset(ether->bcast, 0xFF, Eaddrlen); + + etherxx[ctlrno] = ether; + ether = 0; + break; + } + } + if(ether) + free(ether); +} + +static void +etherpower(int on) +{ + int i; + Ether *ether; + + for(i = 0; i < MaxEther; i++){ + if((ether = etherxx[i]) == nil || ether->power == nil) + continue; + if(on){ + if(canrlock(ether)) + continue; + if(ether->power != nil) + ether->power(ether, on); + wunlock(ether); + }else{ + if(!canrlock(ether)) + continue; + wlock(ether); + if(ether->power != nil) + ether->power(ether, on); + /* Keep locked until power goes back on */ + } + } +} + +#define POLY 0xedb88320 + +/* really slow 32 bit crc for ethers */ +ulong +ethercrc(uchar *p, int len) +{ + int i, j; + ulong crc, b; + + crc = 0xffffffff; + for(i = 0; i < len; i++){ + b = *p++; + for(j = 0; j < 8; j++){ + crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0); + b >>= 1; + } + } + return crc; +} + +Dev etherdevtab = { + 'l', + "ether", + + etherreset, + devinit, + ethershutdown, + etherattach, + etherwalk, + etherstat, + etheropen, + ethercreate, + etherclose, + etherread, + etherbread, + etherwrite, + etherbwrite, + devremove, + etherwstat, + etherpower, +}; diff --git a/os/pxa/devrtc.c b/os/pxa/devrtc.c new file mode 100644 index 00000000..ace6cc82 --- /dev/null +++ b/os/pxa/devrtc.c @@ -0,0 +1,169 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "io.h" + +/* + * SA11x0 real time clock + * TO DO: alarms, wakeup, allow trim setting(?) + */ + +enum{ + Qdir, + Qrtc, + Qrtctrim, +}; + +static Dirtab rtcdir[]={ + ".", {Qdir,0,QTDIR}, 0, 0555, + "rtc", {Qrtc}, NUMSIZE, 0664, + "rtctrim", {Qrtctrim}, 0, 0664, +}; +#define NRTC (sizeof(rtcdir)/sizeof(rtcdir[0])) + +extern ulong boottime; + +enum { + RTSR_al= 1<<0, /* RTC alarm detected */ + RTSR_hz= 1<<1, /* 1-Hz rising-edge detected */ + RTSR_ale= 1<<2, /* RTC alarm interrupt enabled */ + RTSR_hze= 1<<3, /* 1-Hz interrupt enable */ +}; + +static void +rtcreset(void) +{ + RTCreg *r; + + r = RTCREG; + if((r->rttr & 0xFFFF) == 0){ /* reset state */ + r->rttr = 32768-1; + r->rcnr = boottime; /* typically zero */ + } + r->rtar = ~0; + r->rtsr = RTSR_al | RTSR_hz; +} + +static Chan* +rtcattach(char *spec) +{ + return devattach('r', spec); +} + +static Walkqid* +rtcwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, rtcdir, NRTC, devgen); +} + +static int +rtcstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, rtcdir, NRTC, devgen); +} + +static Chan* +rtcopen(Chan *c, int omode) +{ + return devopen(c, omode, rtcdir, NRTC, devgen); +} + +static void +rtcclose(Chan*) +{ +} + +static long +rtcread(Chan *c, void *buf, long n, vlong off) +{ + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, rtcdir, NRTC, devgen); + + switch((ulong)c->qid.path){ + case Qrtc: + return readnum(off, buf, n, RTCREG->rcnr, NUMSIZE); + case Qrtctrim: + return readnum(off, buf, n, RTCREG->rttr, NUMSIZE); + } + error(Egreg); + return 0; /* not reached */ +} + +static long +rtcwrite(Chan *c, void *buf, long n, vlong off) +{ + ulong offset = off; + ulong secs; + char *cp, sbuf[32]; + + switch((ulong)c->qid.path){ + case Qrtc: + /* + * write the time + */ + if(offset != 0 || n >= sizeof(sbuf)-1) + error(Ebadarg); + memmove(sbuf, buf, n); + sbuf[n] = '\0'; + cp = sbuf; + while(*cp){ + if(*cp>='0' && *cp<='9') + break; + cp++; + } + secs = strtoul(cp, 0, 0); + RTCREG->rcnr = secs; + return n; + + case Qrtctrim: + if(offset != 0 || n >= sizeof(sbuf)-1) + error(Ebadarg); + memmove(sbuf, buf, n); + sbuf[n] = '\0'; + RTCREG->rttr = strtoul(sbuf, 0, 0); + return n; + } + error(Egreg); + return 0; /* not reached */ +} + +static void +rtcpower(int on) +{ + if(on) + boottime = RTCREG->rcnr - TK2SEC(MACHP(0)->ticks); + else + RTCREG->rcnr = seconds(); +} + +long +rtctime(void) +{ + return RTCREG->rcnr; +} + +Dev rtcdevtab = { + 'r', + "rtc", + + rtcreset, + devinit, + devshutdown, + rtcattach, + rtcwalk, + rtcstat, + rtcopen, + devcreate, + rtcclose, + rtcread, + devbread, + rtcwrite, + devbwrite, + devremove, + devwstat, + rtcpower, +}; diff --git a/os/pxa/devuart.c b/os/pxa/devuart.c new file mode 100644 index 00000000..4b8cc140 --- /dev/null +++ b/os/pxa/devuart.c @@ -0,0 +1,1070 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "../port/netif.h" + +/* + * Driver for the uart. + * TO DO: replace by uartpxa.c + */ +enum +{ + /* + * register numbers + */ + Data= 0, /* xmit/rcv buffer */ + Iena= 1, /* interrupt enable */ + Ircv= (1<<0), /* for char rcv'd */ + Ixmt= (1<<1), /* for xmit buffer empty */ + Irstat=(1<<2), /* for change in rcv'er status */ + Imstat=(1<<3), /* for change in modem status */ + Rtoie= 1<<4, /* character timeout indication */ + Nrze= 1<<5, /* NRZ encoding enabled */ + Uue= 1<<6, /* Uart unit enabled */ + Dmae= 1<<7, /* DMA requests enabled */ + Istat= 2, /* interrupt flag (read) */ + Ipend= 1, /* interrupt pending (not) */ + Fenabd=(3<<6), /* on if fifo's enabled */ + Fifoctl=2, /* fifo control (write) */ + Fena= (1<<0), /* enable xmit/rcv fifos */ + Fdma= (1<<3), /* dma on */ + Ftrig= (1<<6), /* trigger after 4 input characters */ + Fclear=(3<<1), /* clear xmit & rcv fifos */ + Format= 3, /* byte format */ + Bits8= (3<<0), /* 8 bits/byte */ + Stop2= (1<<2), /* 2 stop bits */ + Pena= (1<<3), /* generate parity */ + Peven= (1<<4), /* even parity */ + Pforce=(1<<5), /* force parity */ + Break= (1<<6), /* generate a break */ + Dra= (1<<7), /* address the divisor */ + Mctl= 4, /* modem control */ + Dtr= (1<<0), /* data terminal ready */ + Rts= (1<<1), /* request to send */ + Ri= (1<<2), /* ring */ + Inton= (1<<3), /* turn on interrupts */ + Loop= (1<<4), /* loop back */ + Lstat= 5, /* line status */ + Inready=(1<<0), /* receive buffer full */ + Oerror=(1<<1), /* receiver overrun */ + Perror=(1<<2), /* receiver parity error */ + Ferror=(1<<3), /* rcv framing error */ + Berror=(1<<4), /* break alarm */ + Outready=(1<<5), /* output buffer full */ + Mstat= 6, /* modem status */ + Ctsc= (1<<0), /* clear to send changed */ + Dsrc= (1<<1), /* data set ready changed */ + Rire= (1<<2), /* rising edge of ring indicator */ + Dcdc= (1<<3), /* data carrier detect changed */ + Cts= (1<<4), /* complement of clear to send line */ + Dsr= (1<<5), /* complement of data set ready line */ + Ringl= (1<<6), /* complement of ring indicator line */ + Dcd= (1<<7), /* complement of data carrier detect line */ + Scratch=7, /* scratchpad */ + Dlsb= 0, /* divisor lsb */ + Dmsb= 1, /* divisor msb */ + + CTLS= 023, + CTLQ= 021, + + Stagesize= 1024, + Nuart= 4, /* max per machine */ +}; + +typedef struct Uart Uart; +struct Uart +{ + QLock; + int opens; + + int enabled; + Uart *elist; /* next enabled interface */ + char name[KNAMELEN]; + + ulong sticky[8]; /* sticky write register values */ + void* regs; + ulong port; + ulong freq; /* clock frequency */ + uchar mask; /* bits/char */ + int dev; + int baud; /* baud rate */ + + uchar istat; /* last istat read */ + int frame; /* framing errors */ + int overrun; /* rcvr overruns */ + + /* buffers */ + int (*putc)(Queue*, int); + Queue *iq; + Queue *oq; + + Lock flock; /* fifo */ + uchar fifoon; /* fifo's enabled */ + uchar nofifo; /* earlier chip version with nofifo */ + + Lock rlock; /* receive */ + uchar istage[Stagesize]; + uchar *ip; + uchar *ie; + + int haveinput; + + Lock tlock; /* transmit */ + uchar ostage[Stagesize]; + uchar *op; + uchar *oe; + + int modem; /* hardware flow control on */ + int xonoff; /* software flow control on */ + int blocked; + int cts, dsr, dcd; /* keep track of modem status */ + int ctsbackoff; + int hup_dsr, hup_dcd; /* send hangup upstream? */ + int dohup; + + Rendez r; +}; + +static Uart *uart[Nuart]; +static int nuart; + +struct Uartalloc { + Lock; + Uart *elist; /* list of enabled interfaces */ +} uartalloc; + +static void uartintr(Uart*); + +/* + * pick up architecture specific routines and definitions + */ +#include "uart.h" + +/* + * set the baud rate by calculating and setting the baudrate + * generator constant. This will work with fairly non-standard + * baud rates. + */ +static void +uartsetbaud(Uart *p, int rate) +{ + ulong brconst; + + if(rate <= 0) + return; + + p->freq = archuartclock(p->port, rate); + if(p->freq == 0) + return; + + brconst = (p->freq+8*rate-1)/(16*rate); + + uartwrreg(p, Format, Dra); + uartwr(p, Dmsb, (brconst>>8) & 0xff); + uartwr(p, Dlsb, brconst & 0xff); + uartwrreg(p, Format, 0); + + p->baud = rate; +} + +/* + * decide if we should hangup when dsr or dcd drops. + */ +static void +uartdsrhup(Uart *p, int n) +{ + p->hup_dsr = n; +} + +static void +uartdcdhup(Uart *p, int n) +{ + p->hup_dcd = n; +} + +static void +uartparity(Uart *p, char type) +{ + switch(type){ + case 'e': + p->sticky[Format] |= Pena|Peven; + break; + case 'o': + p->sticky[Format] &= ~Peven; + p->sticky[Format] |= Pena; + break; + default: + p->sticky[Format] &= ~(Pena|Peven); + break; + } + uartwrreg(p, Format, 0); +} + +/* + * set bits/character, default 8 + */ +void +uartbits(Uart *p, int bits) +{ + if(bits < 5 || bits > 8) + error(Ebadarg); + + p->sticky[Format] &= ~3; + p->sticky[Format] |= bits-5; + + uartwrreg(p, Format, 0); +} + + +/* + * toggle DTR + */ +void +uartdtr(Uart *p, int n) +{ + if(n) + p->sticky[Mctl] |= Dtr; + else + p->sticky[Mctl] &= ~Dtr; + + uartwrreg(p, Mctl, 0); +} + +/* + * toggle RTS + */ +void +uartrts(Uart *p, int n) +{ + if(n) + p->sticky[Mctl] |= Rts; + else + p->sticky[Mctl] &= ~Rts; + + uartwrreg(p, Mctl, 0); +} + +/* + * send break + */ +static void +uartbreak(Uart *p, int ms) +{ + if(ms == 0) + ms = 200; + + uartwrreg(p, Format, Break); + tsleep(&up->sleep, return0, 0, ms); + uartwrreg(p, Format, 0); +} + +static void +uartfifoon(Uart *p) +{ + ulong i, x; + + if(p->nofifo || uartrdreg(p, Istat) & Fenabd) + return; + + x = splhi(); + + /* reset fifos */ + p->sticky[Fifoctl] = 0; + uartwrreg(p, Fifoctl, Fclear); + + /* empty buffer and interrupt conditions */ + for(i = 0; i < 16; i++){ + if(uartrdreg(p, Istat)){ + /* nothing to do */ + } + if(uartrdreg(p, Data)){ + /* nothing to do */ + } + } + + /* turn on fifo */ + p->fifoon = 1; + p->sticky[Fifoctl] = Fena|Ftrig; + uartwrreg(p, Fifoctl, 0); + p->istat = uartrdreg(p, Istat); + if((p->istat & Fenabd) == 0) { + /* didn't work, must be an earlier chip type */ + p->nofifo = 1; + } + + splx(x); +} + +/* + * modem flow control on/off (rts/cts) + */ +static void +uartmflow(Uart *p, int n) +{ + ilock(&p->tlock); + if(n){ + p->sticky[Iena] |= Imstat; + uartwrreg(p, Iena, 0); + p->modem = 1; + p->cts = uartrdreg(p, Mstat) & Cts; + } else { + p->sticky[Iena] &= ~Imstat; + uartwrreg(p, Iena, 0); + p->modem = 0; + p->cts = 1; + } + iunlock(&p->tlock); + +// ilock(&p->flock); +// if(1) +// /* turn on fifo's */ +// uartfifoon(p); +// else { +// /* turn off fifo's */ +// p->fifoon = 0; +// p->sticky[Fifoctl] = 0; +// uartwrreg(p, Fifoctl, Fclear); +// } +// iunlock(&p->flock); +} + +/* + * turn on a port's interrupts. set DTR and RTS + */ +static void +uartenable(Uart *p) +{ + Uart **l; + + if(p->enabled) + return; + + uartportpower(p, 1); + + p->hup_dsr = p->hup_dcd = 0; + p->cts = p->dsr = p->dcd = 0; + + /* + * turn on interrupts + */ + p->sticky[Iena] = Ircv | Ixmt | Irstat | Uue; + uartwrreg(p, Iena, 0); + + /* + * turn on DTR and RTS + */ + uartdtr(p, 1); + uartrts(p, 1); + + uartfifoon(p); + + /* + * assume we can send + */ + ilock(&p->tlock); + p->cts = 1; + p->blocked = 0; + iunlock(&p->tlock); + + /* + * set baud rate to the last used + */ + uartsetbaud(p, p->baud); + + lock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p) + break; + } + if(*l == 0){ + p->elist = uartalloc.elist; + uartalloc.elist = p; + } + p->enabled = 1; + unlock(&uartalloc); +} + +/* + * turn off a port's interrupts. reset DTR and RTS + */ +static void +uartdisable(Uart *p) +{ + Uart **l; + + /* + * turn off interrupts + */ + p->sticky[Iena] = Uue; + uartwrreg(p, Iena, 0); + + /* + * revert to default settings + */ + p->sticky[Format] = Bits8; + uartwrreg(p, Format, 0); + + /* + * turn off DTR, RTS, hardware flow control & fifo's + */ + uartdtr(p, 0); + uartrts(p, 0); + uartmflow(p, 0); + ilock(&p->tlock); + p->xonoff = p->blocked = 0; + iunlock(&p->tlock); + + uartportpower(p, 0); + + lock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p){ + *l = p->elist; + break; + } + } + p->enabled = 0; + unlock(&uartalloc); +} + +/* + * put some bytes into the local queue to avoid calling + * qconsume for every character + */ +static int +stageoutput(Uart *p) +{ + int n; + + n = qconsume(p->oq, p->ostage, Stagesize); + if(n <= 0) + return 0; + p->op = p->ostage; + p->oe = p->ostage + n; + return n; +} + +/* + * (re)start output + */ +static void +uartkick0(Uart *p) +{ + int i; + if((p->modem && (p->cts == 0)) || p->blocked) + return; + + /* + * 128 here is an arbitrary limit to make sure + * we don't stay in this loop too long. If the + * chips output queue is longer than 128, too + * bad -- presotto + */ + for(i = 0; i < 128; i++){ + if(!(uartrdreg(p, Lstat) & Outready)) + break; + if(p->op >= p->oe && stageoutput(p) == 0) + break; + uartwr(p, Data, *p->op++); + } +} + +static void +uartkick(void *v) +{ + Uart *p; + + p = v; + ilock(&p->tlock); + uartkick0(p); + iunlock(&p->tlock); +} + +/* + * restart input if it's off + */ +static void +uartflow(void *v) +{ + Uart *p; + + p = v; + if(p->modem) + uartrts(p, 1); + ilock(&p->rlock); + p->haveinput = 1; + iunlock(&p->rlock); +} + +/* + * default is 9600 baud, 1 stop bit, 8 bit chars, no interrupts, + * transmit and receive enabled, interrupts disabled. + */ +static void +uartsetup0(Uart *p) +{ + memset(p->sticky, 0, sizeof(p->sticky)); + /* + * set rate to 9600 baud. + * 8 bits/character. + * 1 stop bit. + * interrupts enabled. + */ + p->sticky[Format] = Bits8; + uartwrreg(p, Format, 0); + p->sticky[Mctl] |= Inton; + uartwrreg(p, Mctl, 0x0); + +// uartsetbaud(p, 9600); + uartsetbaud(p, 38400); + + p->iq = qopen(4*1024, 0, uartflow, p); + p->oq = qopen(4*1024, 0, uartkick, p); + if(p->iq == nil || p->oq == nil) + panic("uartsetup0"); + + p->ip = p->istage; + p->ie = &p->istage[Stagesize]; + p->op = p->ostage; + p->oe = p->ostage; +} + +/* + * called by uartinstall() to create a new duart + */ +void +uartsetup(ulong port, void *regs, ulong freq, char *name) +{ + Uart *p; + + if(nuart >= Nuart) + return; + + p = xalloc(sizeof(Uart)); + uart[nuart] = p; + strcpy(p->name, name); + p->dev = nuart; + nuart++; + p->port = port; + p->regs = regs; + p->freq = freq; + uartsetup0(p); +} + +/* + * called by main() to configure a duart port as a console or a mouse + */ +void +uartspecial(int port, int baud, Queue **in, Queue **out, int (*putc)(Queue*, int)) +{ + Uart *p = uart[port]; + uartenable(p); + if(baud) + uartsetbaud(p, baud); + p->putc = putc; + if(in) + *in = p->iq; + if(out) + *out = p->oq; + p->opens++; +} + +/* + * handle an interrupt to a single uart + */ +static void +uartintr(Uart *p) +{ + uchar ch; + int s, l; + + for (s = uartrdreg(p, Istat); !(s&Ipend); s = uartrdreg(p, Istat)) { + switch(s&0x3f){ + case 4: /* received data available */ + case 6: /* receiver line status (alarm or error) */ + case 12: /* character timeout indication */ + while ((l = uartrdreg(p, Lstat)) & Inready) { + if(l & Ferror) + p->frame++; + if(l & Oerror) + p->overrun++; + ch = uartrdreg(p, Data) & 0xff; + if (l & (Berror|Perror|Ferror)) { + /* ch came with break, parity or framing error - consume */ + continue; + } + if (ch == CTLS || ch == CTLQ) { + ilock(&p->tlock); + if(p->xonoff){ + if(ch == CTLS) + p->blocked = 1; + else + p->blocked = 0; /* clock gets output going again */ + } + iunlock(&p->tlock); + } + if(p->putc) + p->putc(p->iq, ch); + else { + ilock(&p->rlock); + if(p->ip < p->ie) + *p->ip++ = ch; + else + p->overrun++; + p->haveinput = 1; + iunlock(&p->rlock); + } + } + break; + + case 2: /* transmitter not full */ + uartkick(p); + break; + + case 0: /* modem status */ + ch = uartrdreg(p, Mstat); + if(ch & Ctsc){ + ilock(&p->tlock); + l = p->cts; + p->cts = ch & Cts; + if(l == 0 && p->cts) + p->ctsbackoff = 2; /* clock gets output going again */ + iunlock(&p->tlock); + } + if (ch & Dsrc) { + l = ch & Dsr; + if(p->hup_dsr && p->dsr && !l){ + ilock(&p->rlock); + p->dohup = 1; + iunlock(&p->rlock); + } + p->dsr = l; + } + if (ch & Dcdc) { + l = ch & Dcd; + if(p->hup_dcd && p->dcd && !l){ + ilock(&p->rlock); + p->dohup = 1; + iunlock(&p->rlock); + } + p->dcd = l; + } + break; + + default: + iprint("weird uart interrupt #%2.2ux\n", s); + break; + } + } + p->istat = s; +} + +/* + * we save up input characters till clock time + * + * There's also a bit of code to get a stalled print going. + * It shouldn't happen, but it does. Obviously I don't + * understand something. Since it was there, I bundled a + * restart after flow control with it to give some hysteresis + * to the hardware flow control. This makes compressing + * modems happier but will probably bother something else. + * -- presotto + */ +void +uartclock(void) +{ + int n; + Uart *p; + + for(p = uartalloc.elist; p; p = p->elist){ + + /* this amortizes cost of qproduce to many chars */ + if(p->haveinput){ + ilock(&p->rlock); + if(p->haveinput){ + n = p->ip - p->istage; + if(n > 0 && p->iq){ + if(n > Stagesize) + panic("uartclock"); + if(qproduce(p->iq, p->istage, n) < 0) + uartrts(p, 0); + else + p->ip = p->istage; + } + p->haveinput = 0; + } + iunlock(&p->rlock); + } + if(p->dohup){ + ilock(&p->rlock); + if(p->dohup){ + qhangup(p->iq, 0); + qhangup(p->oq, 0); + } + p->dohup = 0; + iunlock(&p->rlock); + } + + /* this adds hysteresis to hardware flow control */ + if(p->ctsbackoff){ + ilock(&p->tlock); + if(p->ctsbackoff){ + if(--(p->ctsbackoff) == 0) + uartkick0(p); + } + iunlock(&p->tlock); + } + } +} + +Dirtab *uartdir; +int ndir; + +static void +setlength(int i) +{ + Uart *p; + + if(i >= 0){ + p = uart[i]; + if(p && p->opens && p->iq) + uartdir[1+3*i].length = qlen(p->iq); + } else for(i = 0; i < nuart; i++){ + p = uart[i]; + if(p && p->opens && p->iq) + uartdir[1+3*i].length = qlen(p->iq); + } +} + +/* + * all uarts must be uartsetup() by this point or inside of uartinstall() + */ +static void +uartreset(void) +{ + int i; + Dirtab *dp; + uartinstall(); /* architecture specific */ + + ndir = 1+3*nuart; + uartdir = xalloc(ndir * sizeof(Dirtab)); + dp = uartdir; + strcpy(dp->name, "."); + mkqid(&dp->qid, 0, 0, QTDIR); + dp->length = 0; + dp->perm = DMDIR|0555; + dp++; + for(i = 0; i < nuart; i++){ + /* 3 directory entries per port */ + strcpy(dp->name, uart[i]->name); + dp->qid.path = NETQID(i, Ndataqid); + dp->perm = 0666; + dp++; + sprint(dp->name, "%sctl", uart[i]->name); + dp->qid.path = NETQID(i, Nctlqid); + dp->perm = 0666; + dp++; + sprint(dp->name, "%sstatus", uart[i]->name); + dp->qid.path = NETQID(i, Nstatqid); + dp->perm = 0444; + dp++; + } +} + +static Chan* +uartattach(char *spec) +{ + return devattach('t', spec); +} + +static Walkqid* +uartwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, uartdir, ndir, devgen); +} + +static int +uartstat(Chan *c, uchar *dp, int n) +{ + if(NETTYPE(c->qid.path) == Ndataqid) + setlength(NETID(c->qid.path)); + return devstat(c, dp, n, uartdir, ndir, devgen); +} + +static Chan* +uartopen(Chan *c, int omode) +{ + Uart *p; + + c = devopen(c, omode, uartdir, ndir, devgen); + + switch(NETTYPE(c->qid.path)){ + case Nctlqid: + case Ndataqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(p->opens++ == 0){ + uartenable(p); + qreopen(p->iq); + qreopen(p->oq); + } + qunlock(p); + break; + } + + return c; +} + +static void +uartclose(Chan *c) +{ + Uart *p; + + if(c->qid.type & QTDIR) + return; + if((c->flag & COPEN) == 0) + return; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + case Nctlqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(--(p->opens) == 0){ + uartdisable(p); + qclose(p->iq); + qclose(p->oq); + p->ip = p->istage; + p->dcd = p->dsr = p->dohup = 0; + } + qunlock(p); + break; + } +} + +static long +uartstatus(Chan*, Uart *p, void *buf, long n, long offset) +{ + uchar mstat, fstat, istat, tstat; + char str[256]; + + str[0] = 0; + tstat = p->sticky[Mctl]; + mstat = uartrdreg(p, Mstat); + istat = p->sticky[Iena]; + fstat = p->sticky[Format]; + snprint(str, sizeof str, + "b%d c%d d%d e%d l%d m%d p%c r%d s%d\n" + "%d %d %d%s%s%s%s%s\n", + + p->baud, + p->hup_dcd, + (tstat & Dtr) != 0, + p->hup_dsr, + (fstat & Bits8) + 5, + (istat & Imstat) != 0, + (fstat & Pena) ? ((fstat & Peven) ? 'e' : 'o') : 'n', + (tstat & Rts) != 0, + (fstat & Stop2) ? 2 : 1, + + p->dev, + p->frame, + p->overrun, + uartrdreg(p, Istat) & Fenabd ? " fifo" : "", + (mstat & Cts) ? " cts" : "", + (mstat & Dsr) ? " dsr" : "", + (mstat & Dcd) ? " dcd" : "", + (mstat & Ringl) ? " ring" : "" + ); + return readstr(offset, buf, n, str); +} + +static long +uartread(Chan *c, void *buf, long n, vlong off) +{ + Uart *p; + ulong offset = off; + + if(c->qid.type & QTDIR){ + setlength(-1); + return devdirread(c, buf, n, uartdir, ndir, devgen); + } + + p = uart[NETID(c->qid.path)]; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qread(p->iq, buf, n); + case Nctlqid: + return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE); + case Nstatqid: + return uartstatus(c, p, buf, n, offset); + } + + return 0; +} + +static void +uartctl(Uart *p, char *cmd) +{ + int i, n; + + /* let output drain for a while */ + for(i = 0; i < 16 && qlen(p->oq); i++) + tsleep(&p->r, (int(*)(void*))qlen, p->oq, 125); + + if(strncmp(cmd, "break", 5) == 0){ + uartbreak(p, 0); + return; + } + + + n = atoi(cmd+1); + switch(*cmd){ + case 'B': + case 'b': + uartsetbaud(p, n); + break; + case 'C': + case 'c': + uartdcdhup(p, n); + break; + case 'D': + case 'd': + uartdtr(p, n); + break; + case 'E': + case 'e': + uartdsrhup(p, n); + break; + case 'f': + case 'F': + qflush(p->oq); + break; + case 'H': + case 'h': + qhangup(p->iq, 0); + qhangup(p->oq, 0); + break; + case 'L': + case 'l': + uartbits(p, n); + break; + case 'm': + case 'M': + uartmflow(p, n); + break; + case 'n': + case 'N': + qnoblock(p->oq, n); + break; + case 'P': + case 'p': + uartparity(p, *(cmd+1)); + break; + case 'K': + case 'k': + uartbreak(p, n); + break; + case 'R': + case 'r': + uartrts(p, n); + break; + case 'Q': + case 'q': + qsetlimit(p->iq, n); + qsetlimit(p->oq, n); + break; + case 'W': + case 'w': + /* obsolete */ + break; + case 'X': + case 'x': + ilock(&p->tlock); + p->xonoff = n; + iunlock(&p->tlock); + break; + } +} + +static long +uartwrite(Chan *c, void *buf, long n, vlong) +{ + Uart *p; + char cmd[32]; + + if(c->qid.type & QTDIR) + error(Eperm); + + p = uart[NETID(c->qid.path)]; + + /* + * The fifo's turn themselves off sometimes. + * It must be something I don't understand. -- presotto + */ + lock(&p->flock); + if((p->istat & Fenabd) == 0 && p->fifoon && p->nofifo == 0) + uartfifoon(p); + unlock(&p->flock); + + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qwrite(p->oq, buf, n); + case Nctlqid: + if(n >= sizeof(cmd)) + n = sizeof(cmd)-1; + memmove(cmd, buf, n); + cmd[n] = 0; + uartctl(p, cmd); + return n; + } +} + +static int +uartwstat(Chan *c, uchar *dp, int n) +{ + Dir d; + Dirtab *dt; + + if(!iseve()) + error(Eperm); + if(c->qid.type & QTDIR) + error(Eperm); + if(NETTYPE(c->qid.path) == Nstatqid) + error(Eperm); + + dt = &uartdir[1+3 * NETID(c->qid.path)]; + n = convM2D(dp, n, &d, nil); + if(n == 0) + error(Eshortstat); + if(d.mode != ~0UL){ + d.mode &= 0666; + dt[0].perm = dt[1].perm = d.mode; + } + return n; +} + +Dev uartdevtab = { + 't', + "uart", + + uartreset, + devinit, + devshutdown, + uartattach, + uartwalk, + uartstat, + uartopen, + devcreate, + uartclose, + uartread, + devbread, + uartwrite, + devbwrite, + devremove, + uartwstat, +}; diff --git a/os/pxa/dma.c b/os/pxa/dma.c new file mode 100644 index 00000000..9fcdbb4f --- /dev/null +++ b/os/pxa/dma.c @@ -0,0 +1,244 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +#define DMAREGS ((Dmaregs*)PHYSDMA) +typedef struct Dmadesc Dmadesc; +typedef struct Dmaregs Dmaregs; + +struct Dmadesc { + ulong ddadr; /* next descriptor address (0 mod 16) */ + ulong dsadr; /* source address (0 mod 8 if external, 0 mod 4 internal) */ + ulong dtadr; /* target address (same) */ + ulong dcmd; /* command */ +}; + +struct Dmaregs { + ulong dcsr[16]; /* control and status */ + uchar pad0[0xF0-0x40]; + ulong dint; /* mask of interrupting channels: 0 is bit 0 */ + uchar pad1[0x100-0xF4]; + ulong drcmr[40]; + Dmadesc chan[16]; /* offset 0x200 */ +}; + +enum { + /* dcsr */ + DcsRun= 1<<31, /* start the channel */ + DcsNodesc= 1<<30, /* set if channel is in no-descriptor fetch mode */ + DcsStopirq= 1<<29, /* enable interrupt if channel is uninitialised or stopped */ + DcsReqpend= 1<<8, /* channel has pending request */ + DcsStopstate= 1<<3, /* channel is uninitialised or stopped */ + DcsEndintr= 1<<2, /* transaction complete, length now 0 */ + DcsStartintr= 1<<1, /* successful descriptor fetch */ + DcsBuserr= 1<<0, /* bus error */ + + /* drcmr */ + DmrValid= 1<<7, /* mapped to channel given by bits 0-3 */ + DmrChan= 0xF, /* channel number mask */ + + /* ddadr */ + DdaStop= 1<<1, /* =0, run channel; =1, stop channel after this descriptor */ + + /* dcmd */ + DcmIncsrc= 1<<31, /* increment source address after use */ + DcmIncdest= 1<<30, /* increment destination address after use */ + DcmFlowsrc= 1<<29, /* enable flow control on source */ + DcmFlowdest= 1<<28, /* enable flow control on target */ + DcmStartirq= 1<<22, /* interrupt when descriptor loaded (fetch mode) */ + DcmEndirq= 1<<21, /* interrupt when transfer complete */ + DcmEndian= 1<<18, /* must be zero (little endian) */ + DcmBurst8= 1<<16, /* burst size in bytes */ + DcmBurst16= 2<<16, + DcmBurst32= 3<<16, + DcmWidth0= 0<<14, /* width for external memory */ + DcmWidth1= 1<<14, /* width of on-chip peripheral */ + DcmWidth2= 2<<14, + DcmWidth4= 3<<14, + DcmLength= (1<<13)-1, + + Ndma= 16, /* number of dma channels */ + MaxDMAbytes= 8192-1, /* annoyingly small limit */ +}; + +struct Dma { + int chan; + Dmadesc* desc; + Dmadesc stop; + ulong *csr; + void (*interrupt)(void*, ulong); + void* arg; + Rendez r; + ulong attrs; /* transfer attributes: flow control, burst size, width */ +}; + +static struct { + Lock; + ulong avail; + Dma dma[Ndma]; +} dmachans; + +static void dmaintr(Ureg*, void*); + +void +dmareset(void) +{ + int i; + Dma *d; + + for(i=0; ichan = i; + d->csr = &DMAREGS->dcsr[i]; + d->desc = &DMAREGS->chan[i]; + d->stop.ddadr = (ulong)&d->stop | DdaStop; + d->stop.dcmd = 0; + } + intrenable(IRQ, IRQdma, dmaintr, nil, "dma"); +} + +/* + * allocate a DMA channel, reset it, and configure it for the given device + */ +Dma* +dmasetup(int owner, void (*interrupt)(void*, ulong), void *arg, ulong attrs) +{ + Dma *d; + Dmadesc *dc; + int i; + + ilock(&dmachans); + for(i=0; (dmachans.avail & (1<= Ndma){ + iunlock(&dmachans); + return nil; + } + dmachans.avail &= ~(1<owner = owner; + d->interrupt = interrupt; + d->arg = arg; + d->attrs = attrs; + dc = d->desc; + dc->ddadr = (ulong)&d->stop | DdaStop; /* empty list */ + dc->dcmd = 0; + *d->csr = DcsEndintr | DcsStartintr | DcsBuserr; /* clear status, stopped */ + DMAREGS->drcmr[owner] = DmrValid | i; + return d; +} + +void +dmafree(Dma *dma) +{ + dmastop(dma); + DMAREGS->drcmr[d->owner] = 0; + ilock(&dmachans); + dmachans.avail |= 1<chan; + dma->interrupt = nil; + iunlock(&dmachans); +} + +/* + * simple dma transfer on a channel, using `no fetch descriptor' mode. + * virtual buffer addresses are assumed to refer to contiguous physical addresses. + */ +int +dmastart(Dma *dma, void *from, void *to, int nbytes) +{ + Dmadesc *dc; + + if((ulong)nbytes > MaxDMAbytes) + panic("dmastart"); + if((*dma->csr & DcsStopstate) == 0) + return 0; /* busy */ + dc = dma->desc; + dc->ddadr = DdaStop; + dc->dsadr = PADDR(from); + dc->dtadr = PADDR(to); + dc->dcmd = dma->attrs | DcmEndirq | nbytes; + *dma->csr = DcsRun | DcsNodesc | DcsEndintr | DcsStartintr | DcsBuserr; + return 1; +} + +/* + * stop dma on a channel + */ +void +dmastop(Dma *dma) +{ + *dma->csr = 0; + while((*dma->csr & DcsStopstate) == 0) + ; + *dma->csr = DcsStopstate; +} + +/* + * return nonzero if there was a memory error during DMA, + * and clear the error state + */ +int +dmaerror(Dma *dma) +{ + ulong e; + + e = *dma->csr & DcsBuserr; + *dma->csr |= e; + return e; +} + +/* + * return nonzero if the DMA channel is not busy + */ +int +dmaidle(Dma *d) +{ + return (*d->csr & DcsStopstate) == 0; +} + +static int +dmaidlep(void *a) +{ + return dmaidle((Dma*)a); +} + +void +dmawait(Dma *d) +{ + while(!dmaidle(d)) + sleep(&d->r, dmaidlep, d); +} + +/* + * this interface really only copes with one buffer at once + */ +static void +dmaintr(Ureg*, void*) +{ + Dma *d; + Dmaregs *dr; + int i; + ulong s, csr; + + dr = DMAREGS; + s = dr->dint; + dr->dint = s; + for(i=0; icsr; + if(csr & DcsBuserr) + iprint("DMA error, chan %d status #%8.8lux\n", d->chan, csr); + *d->csr = csr & (DcsRun | DcsNodesc | DcsEndintr | DcsStartintr | DcsBuserr); + if(d->interrupt != nil) + d->interrupt(d->arg, csr); + else + wakeup(&d->r); + } +} diff --git a/os/pxa/etherif.h b/os/pxa/etherif.h new file mode 100644 index 00000000..5c5c679b --- /dev/null +++ b/os/pxa/etherif.h @@ -0,0 +1,41 @@ +enum { + MaxEther = 3, + Ntypes = 8, +}; + +typedef struct Ether Ether; +struct Ether { +RWlock; /* TO DO */ + ISAConf; /* hardware info */ + int ctlrno; + int minmtu; + int maxmtu; + uchar ea[Eaddrlen]; + int encry; + + void (*attach)(Ether*); /* filled in by reset routine */ + void (*closed)(Ether*); + void (*detach)(Ether*); + void (*transmit)(Ether*); + void (*interrupt)(Ureg*, void*); + long (*ifstat)(Ether*, void*, long, ulong); + long (*ctl)(Ether*, void*, long); /* custom ctl messages */ + void (*power)(Ether*, int); /* power on/off */ + void (*shutdown)(Ether*); /* shutdown hardware before reboot */ + void *ctlr; + int pcmslot; /* PCMCIA */ + int fullduplex; /* non-zero if full duplex */ + + Queue* oq; + + Netif; +}; + +extern Block* etheriq(Ether*, Block*, int); +extern void addethercard(char*, int(*)(Ether*)); +extern int archether(int, Ether*); + +#define NEXT(x, l) (((x)+1)%(l)) +#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1) +#define HOWMANY(x, y) (((x)+((y)-1))/(y)) +#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) diff --git a/os/pxa/fpi.h b/os/pxa/fpi.h new file mode 100644 index 00000000..dfb9b1df --- /dev/null +++ b/os/pxa/fpi.h @@ -0,0 +1,61 @@ +typedef long Word; +typedef unsigned long Single; +typedef struct { + unsigned long h; + unsigned long l; +} Double; + +enum { + FractBits = 28, + CarryBit = 0x10000000, + HiddenBit = 0x08000000, + MsBit = HiddenBit, + NGuardBits = 3, + GuardMask = 0x07, + LsBit = (1<e >= ExpInfinity) +#define IsInfinity(n) (IsWeird(n) && (n)->h == HiddenBit && (n)->l == 0) +#define SetInfinity(n) ((n)->e = ExpInfinity, (n)->h = HiddenBit, (n)->l = 0) +#define IsNaN(n) (IsWeird(n) && (((n)->h & ~HiddenBit) || (n)->l)) +#define SetQNaN(n) ((n)->s = 0, (n)->e = ExpInfinity, \ + (n)->h = HiddenBit|(LsBit<<1), (n)->l = 0) +#define IsZero(n) ((n)->e == 1 && (n)->h == 0 && (n)->l == 0) +#define SetZero(n) ((n)->e = 1, (n)->h = 0, (n)->l = 0) + +/* + * fpi.c + */ +extern void fpiround(Internal *); +extern void fpiadd(Internal *, Internal *, Internal *); +extern void fpisub(Internal *, Internal *, Internal *); +extern void fpimul(Internal *, Internal *, Internal *); +extern void fpidiv(Internal *, Internal *, Internal *); +extern int fpicmp(Internal *, Internal *); +extern void fpinormalise(Internal*); + +/* + * fpimem.c + */ +extern void fpis2i(Internal *, void *); +extern void fpid2i(Internal *, void *); +extern void fpiw2i(Internal *, void *); +extern void fpii2s(void *, Internal *); +extern void fpii2d(void *, Internal *); +extern void fpii2w(Word *, Internal *); diff --git a/os/pxa/fpiarm.c b/os/pxa/fpiarm.c new file mode 100644 index 00000000..4acfcd1d --- /dev/null +++ b/os/pxa/fpiarm.c @@ -0,0 +1,483 @@ +/* + * this doesn't attempt to implement ARM floating-point properties + * that aren't visible in the Inferno environment. + * all arithmetic is done in double precision. + * the FP trap status isn't updated. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "ureg.h" + +#include "fpi.h" + +// #define R13OK undef this if correct kernel r13 isn't in Ureg; check calculation in fpiarm below + +#define REG(x) (*(long*)(((char*)(ur))+roff[(x)])) +#define FPENV (*(ufp)) +#define FR(x) (*(Internal*)(ufp)->regs[(x)&7]) + +/* BUG: check fetch (not worthwhile in Inferno) */ +#define getubyte(a) (*(uchar*)(a)) +#define getuword(a) (*(ushort*)(a)) +#define getulong(a) (*(ulong*)(a)) + +typedef struct FP2 FP2; +typedef struct FP1 FP1; + +struct FP2 { + char* name; + void (*f)(Internal, Internal, Internal*); +}; + +struct FP1 { + char* name; + void (*f)(Internal*, Internal*); +}; + +enum { + N = 1<<31, + Z = 1<<30, + C = 1<<29, + V = 1<<28, + REGPC = 15, +}; + +int fpemudebug = 0; + +#undef OFR +#define OFR(X) ((ulong)&((Ureg*)0)->X) + +static int roff[] = { + OFR(r0), OFR(r1), OFR(r2), OFR(r3), + OFR(r4), OFR(r5), OFR(r6), OFR(r7), + OFR(r8), OFR(r9), OFR(r10), OFR(r11), +#ifdef R13OK + OFR(r12), OFR(r13), OFR(r14), OFR(pc), +#else + OFR(r12), OFR(type), OFR(r14), OFR(pc), +#endif +}; + +static Internal fpconst[8] = { /* indexed by op&7 */ + /* s, e, l, h */ + {0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */ + {0, 0x3FF, 0x00000000, 0x08000000}, /* 1.0 */ + {0, 0x400, 0x00000000, 0x08000000}, /* 2.0 */ + {0, 0x400, 0x00000000, 0x0C000000}, /* 3.0 */ + {0, 0x401, 0x00000000, 0x08000000}, /* 4.0 */ + {0, 0x401, 0x00000000, 0x0A000000}, /* 5.0 */ + {0, 0x3FE, 0x00000000, 0x08000000}, /* 0.5 */ + {0, 0x402, 0x00000000, 0x0A000000}, /* 10.0 */ +}; + +/* + * arm binary operations + */ + +static void +fadd(Internal m, Internal n, Internal *d) +{ + (m.s == n.s? fpiadd: fpisub)(&m, &n, d); +} + +static void +fsub(Internal m, Internal n, Internal *d) +{ + m.s ^= 1; + (m.s == n.s? fpiadd: fpisub)(&m, &n, d); +} + +static void +fsubr(Internal m, Internal n, Internal *d) +{ + n.s ^= 1; + (n.s == m.s? fpiadd: fpisub)(&n, &m, d); +} + +static void +fmul(Internal m, Internal n, Internal *d) +{ + fpimul(&m, &n, d); +} + +static void +fdiv(Internal m, Internal n, Internal *d) +{ + fpidiv(&m, &n, d); +} + +static void +fdivr(Internal m, Internal n, Internal *d) +{ + fpidiv(&n, &m, d); +} + +/* + * arm unary operations + */ + +static void +fmov(Internal *m, Internal *d) +{ + *d = *m; +} + +static void +fmovn(Internal *m, Internal *d) +{ + *d = *m; + d->s ^= 1; +} + +static void +fabsf(Internal *m, Internal *d) +{ + *d = *m; + d->s = 0; +} + +static void +frnd(Internal *m, Internal *d) +{ + short e; + + (m->s? fsub: fadd)(fpconst[6], *m, d); + if(IsWeird(d)) + return; + fpiround(d); + e = (d->e - ExpBias) + 1; + if(e <= 0) + SetZero(d); + else if(e > FractBits){ + if(e < 2*FractBits) + d->l &= ~((1<<(2*FractBits - e))-1); + }else{ + d->l = 0; + if(e < FractBits) + d->h &= ~((1<<(FractBits-e))-1); + } +} + +static FP1 optab1[16] = { /* Fd := OP Fm */ +[0] {"MOVF", fmov}, +[1] {"NEGF", fmovn}, +[2] {"ABSF", fabsf}, +[3] {"RNDF", frnd}, +[4] {"SQTF", /*fsqt*/0}, +/* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */ +/* URD and NRM aren't implemented */ +}; + +static FP2 optab2[16] = { /* Fd := Fn OP Fm */ +[0] {"ADDF", fadd}, +[1] {"MULF", fmul}, +[2] {"SUBF", fsub}, +[3] {"RSUBF", fsubr}, +[4] {"DIVF", fdiv}, +[5] {"RDIVF", fdivr}, +/* POW, RPW deprecated */ +[8] {"REMF", /*frem*/0}, +[9] {"FMF", fmul}, /* fast multiply */ +[10] {"FDV", fdiv}, /* fast divide */ +[11] {"FRD", fdivr}, /* fast reverse divide */ +/* POL deprecated */ +}; + +static ulong +fcmp(Internal *n, Internal *m) +{ + int i; + + if(IsWeird(m) || IsWeird(n)){ + /* BUG: should trap if not masked */ + return V|C; + } + i = fpicmp(n, m); + if(i > 0) + return C; + else if(i == 0) + return C|Z; + else + return N; +} + +static void +fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPenv *ufp) +{ + void *mem; + + mem = (void*)ea; + (*f)(&FR(d), mem); + if(fpemudebug) + print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d); +} + +static void +fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPenv *ufp) +{ + Internal tmp; + void *mem; + + mem = (void*)ea; + tmp = FR(s); + if(fpemudebug) + print("MOV%c F%d,#%lux\n", n==8? 'D': 'F', s, ea); + (*f)(mem, &tmp); +} + +static int +condok(int cc, int c) +{ + switch(c){ + case 0: /* Z set */ + return cc&Z; + case 1: /* Z clear */ + return (cc&Z) == 0; + case 2: /* C set */ + return cc&C; + case 3: /* C clear */ + return (cc&C) == 0; + case 4: /* N set */ + return cc&N; + case 5: /* N clear */ + return (cc&N) == 0; + case 6: /* V set */ + return cc&V; + case 7: /* V clear */ + return (cc&V) == 0; + case 8: /* C set and Z clear */ + return cc&C && (cc&Z) == 0; + case 9: /* C clear or Z set */ + return (cc&C) == 0 || cc&Z; + case 10: /* N set and V set, or N clear and V clear */ + return (~cc&(N|V))==0 || (cc&(N|V)) == 0; + case 11: /* N set and V clear, or N clear and V set */ + return (cc&(N|V))==N || (cc&(N|V))==V; + case 12: /* Z clear, and either N set and V set or N clear and V clear */ + return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0); + case 13: /* Z set, or N set and V clear or N clear and V set */ + return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V; + case 14: /* always */ + return 1; + case 15: /* never (reserved) */ + return 0; + } + return 0; /* not reached */ +} + +static void +unimp(ulong pc, ulong op) +{ + char buf[60]; + + snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op); + if(fpemudebug) + print("FPE: %s\n", buf); + error(buf); + /* no return */ +} + +static void +fpemu(ulong pc, ulong op, Ureg *ur, FPenv *ufp) +{ + int rn, rd, tag, o; + long off; + ulong ea; + Internal tmp, *fm, *fn; + + /* note: would update fault status here if we noted numeric exceptions */ + + /* + * LDF, STF; 10.1.1 + */ + if(((op>>25)&7) == 6){ + if(op & (1<<22)) + unimp(pc, op); /* packed or extended */ + rn = (op>>16)&0xF; + off = (op&0xFF)<<2; + if((op & (1<<23)) == 0) + off = -off; + ea = REG(rn); + if(rn == REGPC) + ea += 8; + if(op & (1<<24)) + ea += off; + rd = (op>>12)&7; + if(op & (1<<20)){ + if(op & (1<<15)) + fld(fpid2i, rd, ea, 8, ufp); + else + fld(fpis2i, rd, ea, 4, ufp); + }else{ + if(op & (1<<15)) + fst(fpii2d, ea, rd, 8, ufp); + else + fst(fpii2s, ea, rd, 4, ufp); + } + if((op & (1<<24)) == 0) + ea += off; + if(op & (1<<21)) + REG(rn) = ea; + return; + } + + /* + * CPRT/transfer, 10.3 + */ + if(op & (1<<4)){ + rd = (op>>12) & 0xF; + + /* + * compare, 10.3.1 + */ + if(rd == 15 && op & (1<<20)){ + rn = (op>>16)&7; + fn = &FR(rn); + if(op & (1<<3)){ + fm = &fpconst[op&7]; + tag = 'C'; + }else{ + fm = &FR(op&7); + tag = 'F'; + } + switch((op>>21)&7){ + default: + unimp(pc, op); + case 4: /* CMF: Fn :: Fm */ + case 6: /* CMFE: Fn :: Fm (with exception) */ + ur->psr &= ~(N|C|Z|V); + ur->psr |= fcmp(fn, fm); + break; + case 5: /* CNF: Fn :: -Fm */ + case 7: /* CNFE: Fn :: -Fm (with exception) */ + tmp = *fm; + tmp.s ^= 1; + ur->psr &= ~(N|C|Z|V); + ur->psr |= fcmp(fn, &tmp); + break; + } + if(fpemudebug) + print("CMPF %c%d,F%ld =%x\n", tag, rn, op&7, ur->psr>>28); + return; + } + + /* + * other transfer, 10.3 + */ + switch((op>>20)&0xF){ + default: + unimp(pc, op); + case 0: /* FLT */ + rn = (op>>16) & 7; + fpiw2i(&FR(rn), ®(rd)); + if(fpemudebug) + print("MOVW[FD] R%d, F%d\n", rd, rn); + break; + case 1: /* FIX */ + if(op & (1<<3)) + unimp(pc, op); + rn = op & 7; + tmp = FR(rn); + fpii2w(®(rd), &tmp); + if(fpemudebug) + print("MOV[FD]W F%d, R%d =%ld\n", rn, rd, REG(rd)); + break; + case 2: /* FPSR := Rd */ + FPENV.status = REG(rd); + if(fpemudebug) + print("MOVW R%d, FPSR\n", rd); + break; + case 3: /* Rd := FPSR */ + REG(rd) = FPENV.status; + if(fpemudebug) + print("MOVW FPSR, R%d\n", rd); + break; + case 4: /* FPCR := Rd */ + FPENV.control = REG(rd); + if(fpemudebug) + print("MOVW R%d, FPCR\n", rd); + break; + case 5: /* Rd := FPCR */ + REG(rd) = FPENV.control; + if(fpemudebug) + print("MOVW FPCR, R%d\n", rd); + break; + } + return; + } + + /* + * arithmetic + */ + + if(op & (1<<3)){ /* constant */ + fm = &fpconst[op&7]; + tag = 'C'; + }else{ + fm = &FR(op&7); + tag = 'F'; + } + rd = (op>>12)&7; + o = (op>>20)&0xF; + if(op & (1<<15)){ /* monadic */ + FP1 *fp; + fp = &optab1[o]; + if(fp->f == nil) + unimp(pc, op); + if(fpemudebug) + print("%s %c%ld,F%d\n", fp->name, tag, op&7, rd); + (*fp->f)(fm, &FR(rd)); + } else { + FP2 *fp; + fp = &optab2[o]; + if(fp->f == nil) + unimp(pc, op); + rn = (op>>16)&7; + if(fpemudebug) + print("%s %c%ld,F%d,F%d\n", fp->name, tag, op&7, rn, rd); + (*fp->f)(*fm, FR(rn), &FR(rd)); + } +} + +/* + * returns the number of FP instructions emulated + */ +int +fpiarm(Ureg *ur) +{ + ulong op, o; + FPenv *ufp; + int n; + +#ifndef R13OK +/* ur->type = &ur->pc+1; /* calculate kernel sp/R13 and put it here for roff[13] */ + ur->type = (ulong)(ur + 1); +#endif + if (up == nil) + panic("fpiarm not in a process"); + ufp = &up->env->fpu; /* because all the state is in Osenv, it need not be saved/restored */ + if(ufp->fpistate != FPACTIVE) { + ufp->fpistate = FPACTIVE; + ufp->control = 0; + ufp->status = (0x01<<28)|(1<<12); /* software emulation, alternative C flag */ + for(n = 0; n < 8; n++) + FR(n) = fpconst[0]; + } + for(n=0;;n++){ + op = getulong(ur->pc); + o = (op>>24) & 0xF; + if(((op>>8) & 0xF) != 1 || o != 0xE && (o&~1) != 0xC) + break; + if(condok(ur->psr, op>>28)) + fpemu(ur->pc, op, ur, ufp); + ur->pc += 4; + if(anyhigher()) + sched(); + } + return n; +} diff --git a/os/pxa/gpio.c b/os/pxa/gpio.c new file mode 100644 index 00000000..5914f8c4 --- /dev/null +++ b/os/pxa/gpio.c @@ -0,0 +1,88 @@ +#include "u.h" +#include "mem.h" +#include "../port/lib.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +static ulong gpioreserved[3]; +static Lock gpiolock; + +void +gpioreserve(int n) +{ + ulong mask, *r; + + r = &gpioreserved[GPR(n)]; + mask = GPB(n); + ilock(&gpiolock); + if(*r & mask) + panic("gpioreserve: duplicate use of GPIO %d", n); + *r |= mask; + iunlock(&gpiolock); +} + +/* + * set direction and alternative function bits in the GPIO control register, + * following the configuration bits in cfg. + */ +void +gpioconfig(int n, ulong cfg) +{ + GpioReg *g; + ulong o, m, *r; + + m = GPB(n); + o = n>>5; + ilock(&gpiolock); + g = GPIOREG; + r = &g->gpdr[o]; + if(cfg & Gpio_out) + *r |= m; + else + *r &= ~m; + r = &g->gafr[o*2]; + *r = (*r & ~GPAF(n, 3)) | GPAF(n, cfg&3); + iunlock(&gpiolock); +} + +ulong +gpioget(int n) +{ + ulong mask, o; + + mask = GPB(n); + o = GPR(n); + return GPIOREG->gplr[o] & mask; +} + +void +gpioset(int n, int v) +{ + GpioReg *g; + ulong mask, o; + + g = GPIOREG; + mask = GPB(n); + o = GPR(n); + ilock(&gpiolock); + if(v) + g->gpsr[o] = mask; + else + g->gpcr[o] = mask; + iunlock(&gpiolock); +} + +void +gpiorelease(int n) +{ + ulong mask, *r; + + mask = GPB(n); + r = &gpioreserved[GPR(n)]; + ilock(&gpiolock); + if((*r & mask) != mask) + panic("gpiorelease: unexpected release of GPIO %d", n); + *r &= ~mask; + iunlock(&gpiolock); +} diff --git a/os/pxa/i2c.c b/os/pxa/i2c.c new file mode 100644 index 00000000..b45542ae --- /dev/null +++ b/os/pxa/i2c.c @@ -0,0 +1,561 @@ +/* + * basic read/write interface to PXA25x I⁲C bus (master mode) + * 7 bit addressing only. + * TO DO: + * - enable unit clock + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +typedef struct Ctlr Ctlr; +typedef struct I2Cregs I2Cregs; +struct I2Cregs { + ulong ibmr; /* bus monitor */ + ulong pad0; + ulong idbr; /* data buffer */ + ulong pad1; + ulong icr; /* control */ + ulong pad2; + ulong isr; /* status */ + ulong pad3; + ulong isar; /* slave address */ +}; + +enum { + /* ibmr */ + Scls= 1<<1, /* SCL pin status */ + Sdas= 1<<0, /* SDA pin status */ + + /* icr */ + Fm= 1<<15, /* =0, 100 kb/sec; =1, 400 kb/sec */ + Ur= 1<<14, /* reset the i2c unit only */ + Sadie= 1<<13, /* slave address detected interrupt enable */ + Aldie= 1<<12, /* arbitration loss detected interrupt enable (master mode) */ + Ssdie= 1<<11, /* stop detected interrupt enable (slave mode) */ + Beie= 1<<10, /* bus error interrupt enable */ + Irfie= 1<<9, /* idbr receive full, interrupt enable */ + Iteie= 1<<8, /* idbr transmit empty interrupt enable */ + Gcd= 1<<7, /* disable response to general call message (slave); must be set if master uses g.c. */ + Scle= 1<<6, /* SCL enable: enable clock output for master mode */ + Iue= 1<<5, /* enable i2c (default: slave) */ + Ma= 1<<4, /* master abort (send STOP without data) */ + Tb= 1<<3, /* transfer byte on i2c bus */ + Ack= 0<<2, + Nak= 1<<2, + Stop= 1<<1, /* send a stop */ + Start= 1<<0, /* send a stop */ + + /* isr */ + Bed= 1<<10, /* bus error detected */ + Sad= 1<<9, /* slave address detected */ + Gcad= 1<<8, /* general call address detected */ + Irf= 1<<7, /* idbr receive full */ + Ite= 1<<6, /* idbr transmit empty */ + Ald= 1<<5, /* arbitration loss detected (multi-master) */ + Ssd= 1<<4, /* slave stop detected */ + Ibb= 1<<3, /* i2c bus is busy */ + Ub= 1<<2, /* unit is busy (between start and stop) */ + Nakrcv= 1<<1, /* nak received or sent a NAK */ + Rwm= 1<<0, /* =0, master transmit (or slave receive); =1, master receive (or slave transmit) */ + Err= Bed | Ssd, + + /* isar address (0x7F bits) */ + + /* others */ + Rbit = 1<<0, /* bit in address byte denoting read */ + Wbit= 0<<0, + + MaxIO = 8192, /* largest transfer done at once (can change) */ + MaxSA= 2, /* largest subaddress; could be FIFOsize */ + Bufsize = MaxIO, /* subaddress bytes don't go in buffer */ + Freq = 0, /* set to Fm for high-speed */ +// I2Ctimeout = 125, /* msec (can change) */ + I2Ctimeout = 10000, /* msec when Chatty */ + + Chatty = 0, +}; + +#define DPRINT if(Chatty)print + +/* + * I2C software structures + */ + +struct Ctlr { + Lock; + QLock io; + int init; + int polling; /* eg, when running before system set up */ + I2Cregs* regs; /* hardware registers */ + + /* controller state (see below) */ + int status; + int phase; + Rendez r; + + /* transfer parameters */ + int addr; + int salen; /* bytes remaining of subaddress */ + int offset; /* sub-addressed offset */ + int cntl; /* everything but transfer length */ + int rdcount; /* requested read transfer size */ + Block* b; +}; + +enum { + /* Ctlr.state */ + Idle, + Done, + Failed, + Busy, + Address, + Subaddress, + Read, + Write, + Halting, +}; + +static Ctlr i2cctlr[1]; + +static void interrupt(Ureg*, void*); +static int readyxfer(Ctlr*, int); +static void rxstart(Ctlr*); +static void txstart(Ctlr*); +static void stopxfer(Ctlr*); +static void txoffset(Ctlr*, ulong, int); +static int idlectlr(Ctlr*); + +static void +i2cdump(char *t, I2Cregs *i2c) +{ + iprint("i2c %s: ibmr=%.4lux icr=%.4lux isr=%.4lux\n", t, i2c->ibmr, i2c->icr, i2c->isr); +} + +static void +initialise(I2Cregs *i2c, int eintr) +{ + int ctl; + + /* initialisation (see p. 9-11 on) */ + i2c->isar = 0; + ctl = Freq | Gcd | Scle | Iue; + if(eintr) + ctl |= Beie | Irfie; /* Iteie set by txstart */ + i2c->icr = ctl; + if(Chatty) + iprint("ctl=%4.4ux icr=%4.4lux\n", ctl, i2c->icr); +} + +/* + * called by the reset routine of any driver using the IIC + */ +void +i2csetup(int polling) +{ + I2Cregs *i2c; + Ctlr *ctlr; + + ctlr = i2cctlr; + ctlr->polling = polling; + i2c = KADDR(PHYSI2C); + ctlr->regs = i2c; + if(!polling){ + if(ctlr->init == 0){ + initialise(i2c, 1); + ctlr->init = 1; + intrenable(IRQ, IRQi2c, interrupt, i2cctlr, "i2c"); + if(Chatty) + i2cdump("init", i2c); + } + }else + initialise(i2c, 0); +} + +static void +done(Ctlr *ctlr) +{ + ctlr->phase = Done; + wakeup(&ctlr->r); +} + +static void +failed(Ctlr *ctlr) +{ + ctlr->phase = Failed; + wakeup(&ctlr->r); +} + +static void +interrupt(Ureg*, void *arg) +{ + int sts, idl; + Ctlr *ctlr; + Block *b; + I2Cregs *i2c; + char xx[12]; + + ctlr = arg; + i2c = ctlr->regs; + idl = (i2c->ibmr & 3) == 3; + if(Chatty && ctlr->phase != Read && ctlr->phase != Write){ + snprint(xx, sizeof(xx), "intr %d", ctlr->phase); + i2cdump(xx, i2c); + } + sts = i2c->isr; + if(sts & (Bed | Sad | Gcad | Ald)) + iprint("i2c: unexpected status: %.4ux", sts); + i2c->isr = sts; + ctlr->status = sts; + i2c->icr &= ~(Start | Stop | Nak | Ma | Iteie); + if(sts & Err){ + failed(ctlr); + return; + } + switch(ctlr->phase){ + default: + iprint("i2c: unexpected interrupt: p-%d s=%.4ux\n", ctlr->phase, sts); + break; + + case Halting: + ctlr->phase = Idle; + break; + + case Subaddress: + if(ctlr->salen){ + /* push out next byte of subaddress */ + ctlr->salen -= 8; + i2c->idbr = ctlr->offset >> ctlr->salen; + i2c->icr |= Aldie | Tb | Iteie; + break; + } + /* subaddress finished */ + if(ctlr->cntl & Rbit){ + /* must readdress if reading to change mode */ + i2c->idbr = (ctlr->addr << 1) | Rbit; + i2c->icr |= Start | Tb | Iteie; + ctlr->phase = Address; /* readdress */ + break; + } + /* FALL THROUGH if writing */ + case Address: + /* if not sub-addressed, rxstart/txstart */ + if(ctlr->cntl & Rbit) + rxstart(ctlr); + else + txstart(ctlr); + break; + + case Read: + b = ctlr->b; + if(b == nil) + panic("i2c: no buffer"); + /* master receive: next byte */ + if(sts & Irf){ + ctlr->rdcount--; + if(b->wp < b->lim) + *b->wp++ = i2c->idbr; + } + if(ctlr->rdcount <= 0 || sts & Nakrcv || idl){ + if(Chatty) + iprint("done: %.4ux\n", sts); + done(ctlr); + break; + } + rxstart(ctlr); + break; + + case Write: + b = ctlr->b; + if(b == nil) + panic("i2c: no buffer"); + /* account for data transmitted */ + if(BLEN(b) <= 0 || sts & Nakrcv){ + done(ctlr); + break; + } + txstart(ctlr); + break; + } +} + +static int +isdone(void *a) +{ + return ((Ctlr*)a)->phase < Busy; +} + +static int +i2cerror(char *s) +{ + DPRINT("i2c error: %s\n", s); + if(up) + error(s); + /* no current process, don't call error */ + return -1; +} + +static char* +startxfer(I2Cdev *d, int op, Block *b, int n, ulong offset) +{ + I2Cregs *i2c; + Ctlr *ctlr; + int i, p, s; + + ctlr = i2cctlr; + if(up){ + qlock(&ctlr->io); + if(waserror()){ + qunlock(&ctlr->io); + nexterror(); + } + } + ilock(ctlr); + if(!idlectlr(ctlr)){ + iunlock(ctlr); + if(up) + error("bus confused"); + return "bus confused"; + } + if(ctlr->phase >= Busy) + panic("i2c: ctlr busy"); + ctlr->cntl = op; + ctlr->b = b; + ctlr->rdcount = n; + ctlr->addr = d->addr; + i2c = ctlr->regs; + ctlr->salen = d->salen*8; + ctlr->offset = offset; + if(ctlr->salen){ + ctlr->phase = Subaddress; + op = Wbit; + }else + ctlr->phase = Address; + i2c->idbr = (d->addr<<1) | op; /* 7-bit address + R/nW */ + i2c->icr |= Start | Tb | Iteie; + if(Chatty) + i2cdump("start", i2c); + iunlock(ctlr); + + /* wait for it */ + if(ctlr->polling){ + for(i=0; !isdone(ctlr); i++){ + delay(2); + interrupt(nil, ctlr); + } + }else + tsleep(&ctlr->r, isdone, ctlr, I2Ctimeout); + + ilock(ctlr); + p = ctlr->phase; + s = ctlr->status; + ctlr->b = nil; + if(ctlr->phase != Done && ctlr->phase != Idle) + stopxfer(ctlr); + iunlock(ctlr); + + if(up){ + poperror(); + qunlock(&ctlr->io); + } + if(p != Done || s & (Bed|Ald)){ /* CHECK; time out */ + if(s & Ald) + return "i2c lost arbitration"; + if(s & Bed) + return "i2c bus error"; + if(s & Ssd) + return "i2c transfer aborted"; /* ?? */ + if(0 && p != Done) + return "i2c timed out"; + sprint(up->genbuf, "i2c error: phase=%d status=%.4ux", p, s); + return up->genbuf; + } + return nil; +} + +long +i2csend(I2Cdev *d, void *buf, long n, ulong offset) +{ + Block *b; + char *e; + + if(n <= 0) + return 0; + if(n > MaxIO) + n = MaxIO; + + if(up){ + b = allocb(n); + if(b == nil) + error(Enomem); + if(waserror()){ + freeb(b); + nexterror(); + } + }else{ + b = iallocb(n); + if(b == nil) + return -1; + } + memmove(b->wp, buf, n); + b->wp += n; + e = startxfer(d, 0, b, 0, offset); + if(up) + poperror(); + n -= BLEN(b); /* residue */ + freeb(b); + if(e) + return i2cerror(e); + return n; +} + +long +i2crecv(I2Cdev *d, void *buf, long n, ulong offset) +{ + Block *b; + long nr; + char *e; + + if(n <= 0) + return 0; + if(n > MaxIO) + n = MaxIO; + + if(up){ + b = allocb(n); + if(b == nil) + error(Enomem); + if(waserror()){ + freeb(b); + nexterror(); + } + }else{ + b = iallocb(n); + if(b == nil) + return -1; + } + e = startxfer(d, Rbit, b, n, offset); + nr = BLEN(b); + if(nr > 0) + memmove(buf, b->rp, nr); + if(up) + poperror(); + freeb(b); + if(e) + return i2cerror(e); + return nr; +} + +/* + * the controller must be locked for the following functions + */ + +static int +readyxfer(Ctlr *ctlr, int phase) +{ + I2Cregs *i2c; + + i2c = ctlr->regs; + if((i2c->isr & Bed) != 0){ + failed(ctlr); + return 0; + } + ctlr->phase = phase; + return 1; +} + +/* + * start a master transfer to receive the next byte of data + */ +static void +rxstart(Ctlr *ctlr) +{ + Block *b; + int cntl; + + b = ctlr->b; + if(b == nil || ctlr->rdcount<= 0){ + done(ctlr); + return; + } + if(!readyxfer(ctlr, Read)) + return; + cntl = Aldie | Tb; + if(ctlr->rdcount == 1) + cntl |= Stop | Nak | Iteie; /* last byte of transfer */ + ctlr->regs->icr |= cntl; +} + +/* + * start a master transfer to send the next chunk of data + */ +static void +txstart(Ctlr *ctlr) +{ + Block *b; + int cntl; + long nb; + I2Cregs *i2c; + + b = ctlr->b; + if(b == nil || (nb = BLEN(b)) <= 0){ + done(ctlr); + return; + } + if(!readyxfer(ctlr, Write)) + return; + i2c = ctlr->regs; + i2c->idbr = *b->rp++; + cntl = Aldie | Tb | Iteie; + if(nb == 1) + cntl |= Stop; + i2c->icr |= cntl; +} + +/* + * stop a transfer if one is in progress + */ +static void +stopxfer(Ctlr *ctlr) +{ + I2Cregs *i2c; + + i2c = ctlr->regs; + if((i2c->isr & Ub) == 0){ + ctlr->phase = Idle; + return; + } + if((i2c->isr & Ibb) == 0 && ctlr->phase != Halting){ + ctlr->phase = Halting; /* interrupt will clear the state */ + i2c->icr |= Ma; + } + /* if that doesn't clear it by the next operation, idlectlr will do so below */ +} + +static int +idlectlr(Ctlr *ctlr) +{ + I2Cregs *i2c; + + i2c = ctlr->regs; + if((i2c->isr & Ibb) == 0){ + if((i2c->isr & Ub) == 0){ + ctlr->phase = Idle; + return 1; + } + iprint("i2c: bus free, ctlr busy: isr=%.4lux icr=%.4lux\n", i2c->isr, i2c->icr); + } + /* hit it with the hammer, soft reset */ + iprint("i2c: soft reset\n"); + i2c->icr = Ur; + iunlock(ctlr); + delay(1); + ilock(ctlr); + initialise(i2c, !ctlr->polling); + ctlr->phase = Idle; + return (i2c->isr & (Ibb | Ub)) == 0; +} diff --git a/os/pxa/l.s b/os/pxa/l.s new file mode 100644 index 00000000..df246a04 --- /dev/null +++ b/os/pxa/l.s @@ -0,0 +1,548 @@ +#include "mem.h" + +#define CPWAIT MRC CpMMU, 0, R2, C(2), C(0), 0; MOVW R2, R2; SUB $4, R15 + +/* + * Entered here from the boot loader with + * supervisor mode, interrupts disabled; + * MMU, IDC and WB disabled. + */ + +TEXT _startup(SB), $-4 + MOVW $setR12(SB), R12 /* static base (SB) */ + MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R1 /* ensure SVC mode with interrupts disabled */ + MOVW R1, CPSR + MOVW $(MACHADDR+BY2PG-4), R13 /* stack; 4 bytes for link */ + + BL main(SB) +dead: + B dead + BL _div(SB) /* hack to get _div etc loaded */ + +TEXT getcpuid(SB), $-4 + MRC CpMMU, 0, R0, C(CpCPUID), C(0) + RET + +TEXT mmugetctl(SB), $-4 + MRC CpMMU, 0, R0, C(CpControl), C(0) + RET + +TEXT mmugetdac(SB), $-4 + MRC CpMMU, 0, R0, C(CpDAC), C(0) + RET + +TEXT mmugetfar(SB), $-4 + MRC CpMMU, 0, R0, C(CpFAR), C(0) + RET + +TEXT mmugetfsr(SB), $-4 + MRC CpMMU, 0, R0, C(CpFSR), C(0) + RET + +TEXT mmuputdac(SB), $-4 + MCR CpMMU, 0, R0, C(CpDAC), C(0) + CPWAIT + RET + +TEXT mmuputfsr(SB), $-4 + MCR CpMMU, 0, R0, C(CpFSR), C(0) + CPWAIT + RET + +TEXT mmuputttb(SB), $-4 + MCR CpMMU, 0, R0, C(CpTTB), C(0) + CPWAIT + RET + +TEXT mmuputctl(SB), $-4 + MCR CpMMU, 0, R0, C(CpControl), C(0) + CPWAIT + RET + +TEXT tlbinvalidateall(SB), $-4 + MCR CpMMU, 0, R0, C(CpTLBops), C(7) + CPWAIT + RET + +TEXT itlbinvalidate(SB), $-4 + MCR CpMMU, 0, R0, C(CpTLBops), C(5), 1 + CPWAIT + RET + +TEXT dtlbinvalidate(SB), $-4 + MCR CpMMU, 0, R0, C(CpTLBops), C(6), 1 + CPWAIT + RET + +TEXT mmuenable(SB), $-4 + + MOVW $1, R1 + MCR CpMMU, 0, R1, C(CpDAC), C(3) /* set domain 0 to client */ + + /* disable and invalidate all caches and TLB's before (re-)enabling MMU */ + MOVW $(CpCwpd | CpCsystem), R1 + MRC CpMMU, 0, R1, C(CpControl), C(0) + CPWAIT + + MOVW $0, R1 /* disable everything */ + MCR CpMMU, 0, R1, C(CpCacheCtl), C(7), 0 /* invalidate I&D Caches and BTB */ + MCR CpMMU, 0, R1, C(CpCacheCtl), C(10), 4 /* drain write and fill buffer */ + MCR CpMMU, 0, R1, C(CpTLBops), C(7), 0 /* invalidate I&D TLB */ + + /* enable desired mmu mode (R0) */ + MCR CpMMU, 0, R0, C(1), C(0) + CPWAIT + RET /* start running in remapped area */ + +TEXT setr13(SB), $-4 + MOVW 4(FP), R1 + + MOVW CPSR, R2 + BIC $PsrMask, R2, R3 + ORR R0, R3 + MOVW R3, CPSR + + MOVW R13, R0 + MOVW R1, R13 + + MOVW R2, CPSR + RET + +TEXT vectors(SB), $-4 + MOVW 0x18(R15), R15 /* reset */ + MOVW 0x18(R15), R15 /* undefined */ + MOVW 0x18(R15), R15 /* SWI */ + MOVW 0x18(R15), R15 /* prefetch abort */ + MOVW 0x18(R15), R15 /* data abort */ + MOVW 0x18(R15), R15 /* reserved */ + MOVW 0x18(R15), R15 /* IRQ */ + MOVW 0x18(R15), R15 /* FIQ */ + +TEXT vtable(SB), $-4 + WORD $_vsvc(SB) /* reset, in svc mode already */ + WORD $_vund(SB) /* undefined, switch to svc mode */ + WORD $_vsvc(SB) /* swi, in svc mode already */ + WORD $_vpab(SB) /* prefetch abort, switch to svc mode */ + WORD $_vdab(SB) /* data abort, switch to svc mode */ + WORD $_vsvc(SB) /* reserved */ + WORD $_virq(SB) /* IRQ, switch to svc mode */ + WORD $_vfiq(SB) /* FIQ, switch to svc mode */ + +TEXT _vund(SB), $-4 + MOVM.DB [R0-R3], (R13) + MOVW $PsrMund, R0 + B _vswitch + +TEXT _vsvc(SB), $-4 + MOVW.W R14, -4(R13) + MOVW CPSR, R14 + MOVW.W R14, -4(R13) + BIC $PsrMask, R14 + ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14 + MOVW R14, CPSR + MOVW $PsrMsvc, R14 + MOVW.W R14, -4(R13) + B _vsaveu + +TEXT _vpab(SB), $-4 + MOVM.DB [R0-R3], (R13) + MOVW $PsrMabt, R0 + B _vswitch + +TEXT _vdab(SB), $-4 + MOVM.DB [R0-R3], (R13) + MOVW $(PsrMabt+1), R0 + B _vswitch + +TEXT _vfiq(SB), $-4 /* FIQ */ + MOVM.DB [R0-R3], (R13) + MOVW $PsrMfiq, R0 + B _vswitch + +TEXT _virq(SB), $-4 /* IRQ */ + MOVM.DB [R0-R3], (R13) + MOVW $PsrMirq, R0 + +_vswitch: /* switch to svc mode */ + MOVW SPSR, R1 + MOVW R14, R2 + MOVW R13, R3 + + MOVW CPSR, R14 + BIC $PsrMask, R14 + ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14 + MOVW R14, CPSR + + MOVM.DB.W [R0-R2], (R13) + MOVM.DB (R3), [R0-R3] + +_vsaveu: /* Save Registers */ + MOVW.W R14, -4(R13) /* save link */ + MCR CpMMU, 0, R0, C(0), C(0), 0 + + SUB $8, R13 + MOVM.DB.W [R0-R12], (R13) + + MOVW R0, R0 /* gratuitous noop */ + + MOVW $setR12(SB), R12 /* static base (SB) */ + MOVW R13, R0 /* argument is ureg */ + SUB $8, R13 /* space for arg+lnk*/ + BL trap(SB) + +_vrfe: /* Restore Regs */ + MOVW CPSR, R0 /* splhi on return */ + ORR $(PsrDirq|PsrDfiq), R0, R1 + MOVW R1, CPSR + ADD $(8+4*15), R13 /* [r0-R14]+argument+link */ + MOVW (R13), R14 /* restore link */ + MOVW 8(R13), R0 + MOVW R0, SPSR + MOVM.DB.S (R13), [R0-R14] /* restore user registers */ + MOVW R0, R0 /* gratuitous nop */ + ADD $12, R13 /* skip saved link+type+SPSR*/ + RFE /* MOVM.IA.S.W (R13), [R15] */ + +TEXT splhi(SB), $-4 + MOVW CPSR, R0 + ORR $(PsrDirq), R0, R1 + MOVW R1, CPSR + MOVW $(MACHADDR), R6 + MOVW R14, (R6) /* m->splpc */ + RET + +TEXT spllo(SB), $-4 + MOVW CPSR, R0 + BIC $(PsrDirq|PsrDfiq), R0, R1 + MOVW R1, CPSR + RET + +TEXT splx(SB), $-4 + MOVW $(MACHADDR), R6 + MOVW R14, (R6) /* m->splpc */ + +TEXT splxpc(SB), $-4 + MOVW R0, R1 + MOVW CPSR, R0 + MOVW R1, CPSR + RET + +TEXT spldone(SB), $-4 + RET + +TEXT islo(SB), $-4 + MOVW CPSR, R0 + AND $(PsrDirq), R0 + EOR $(PsrDirq), R0 + RET + +TEXT splfhi(SB), $-4 + MOVW CPSR, R0 + ORR $(PsrDfiq|PsrDirq), R0, R1 + MOVW R1, CPSR + RET + +TEXT splflo(SB), $-4 + MOVW CPSR, R0 + BIC $(PsrDfiq), R0, R1 + MOVW R1, CPSR + RET + +TEXT getcpsr(SB), $-4 + MOVW CPSR, R0 + RET + +TEXT getspsr(SB), $-4 + MOVW SPSR, R0 + RET + +TEXT getcallerpc(SB), $-4 + MOVW 0(R13), R0 + RET + +TEXT _tas(SB), $-4 + MOVW R0, R1 + MOVW $0xDEADDEAD, R2 + SWPW R2, (R1), R0 + RET + +TEXT setlabel(SB), $-4 + MOVW R13, 0(R0) /* sp */ + MOVW R14, 4(R0) /* pc */ + MOVW $0, R0 + RET + +TEXT gotolabel(SB), $-4 + MOVW 0(R0), R13 /* sp */ + MOVW 4(R0), R14 /* pc */ + MOVW $1, R0 + RET + +/* + * flush (invalidate) the whole icache + */ +TEXT icflushall(SB), $-4 +_icflushall: + MCR CpMMU, 0, R0, C(CpCacheCtl), C(5), 0 /* clean i-cache and branch buffer */ + CPWAIT + RET + +/* + * invalidate part of i-cache and invalidate branch target buffer + */ +TEXT icflush(SB), $-4 + MOVW 4(FP), R1 + CMP $(CACHESIZE/2), R1 + BGE _icflushall + ADD R0, R1 + BIC $(CACHELINESZ-1), R0 +icflush1: + MCR CpMMU, 0, R0, C(CpCacheCtl), C(5), 1 /* clean entry */ + ADD $CACHELINESZ, R0 + CMP R1, R0 + BLO icflush1 + MCR CpMMU, 0, R0, C(CpCacheCtl), C(5), 6 /* invalidate branch target buffer */ + CPWAIT + RET + +/* + * write back whole data cache and drain write buffer + */ +TEXT dcflushall(SB), $-4 +_dcflushall: + MOVW $(DCFADDR), R0 + ADD $CACHESIZE, R0, R1 +dcflushall1: + MCR CpMMU, 0, R0, C(CpCacheCtl), C(2), 5 /* allocate line */ + ADD $CACHELINESZ, R0 + CMP R1,R0 + BNE dcflushall1 + MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4 /* drain write buffer */ + CPWAIT + RET + +/* + * write back a given region and drain write buffer + */ +TEXT dcflush(SB), $-4 + MOVW 4(FP), R1 + CMP $(CACHESIZE/2), R1 + BGE _dcflushall + ADD R0, R1 + BIC $(CACHELINESZ-1), R0 +dcflush1: + MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 1 /* clean entry */ + ADD $CACHELINESZ, R0 + CMP R1, R0 + BLO dcflush1 + MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4 /* drain write buffer */ + CPWAIT + RET + +#ifdef NOTYET +/* + * write back mini data cache + * TO DO: need to allocate pair of unused 2k blocks and read them alternately + */ +TEXT minidcflush(SB), $-4 + MOVW $(MCFADDR), R0 + ADD $(16*CACHELINESZ), R0, R1 + +wbbflush: + MOVW.P CACHELINESZ(R0), R2 + CMP R1,R0 + BNE wbbflush + MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4 /* drain write buffer */ + CPWAIT + RET +#endif + +/* + * invalidate data caches (main and mini) + */ +TEXT dcinval(SB), $-4 + MCR CpMMU, 0, R0, C(CpCacheCtl), C(6), 0 + CPWAIT + RET + +/* for devboot */ +TEXT gotopc(SB), $-4 + MOVW R0, R1 + MOVW $0, R0 + MOVW R1, PC + RET + +TEXT idle(SB), $-4 + MOVW $1, R0 /* idle mode */ + MCR CpPWR, 0, R0, C(7), C(0), 0 + RET + +TEXT getcclkcfg(SB), $-4 + MRC CpPWR, 0, R0, C(6), C(0), 0 + RET + +TEXT putcclkcfg(SB), $-4 + MCR CpPWR, 0, R0, C(6), C(0), 0 + RET + +#ifdef NOTYET +/* + * the following code is considerably modified from the + * sleep code by nemo@gsyc.escet.urjc.es for Plan 9, but that's + * where it started. in particular, there's no need to save regs in all modes, + * since here we're called from kernel main level (a kproc) so nothing is live; + * the only regs needed are the various R13s, but we have trap restore them on resume. + * similarly there's no need to save SPSR, CPSR, etc. (even CpPID isn't really needed here). + */ + +#define MDREFR_k1db2 (1<<22) +#define MDREFR_slfrsh (1<<31) /* self refresh */ +#define MDREFR_e1pin (1<<20) +#define MSC_rt ((3<<16)|(3<<0)) +#define MDCFNG_de ((3<<16)|(3<<0)) /* dram enable, banks (3 2), (1 0) */ + +TEXT suspenditall(SB), $-4 + MOVW.W R14, -4(R13) + /* push mmu state on stack */ + MRC CpMMU, 0, R1, C(CpDAC), C(0) + MRC CpMMU, 0, R2, C(CpTTB), C(0) + MRC CpMMU, 0, R3, C(CpPID), C(0) + MRC CpMMU, 0, R4, C(CpControl), C(0) + MOVM.DB.W [R1-R4], (R13) + /* if pspr by convention held a stack pointer pointing to a pc we wouldn't need power_state */ + MOVW R13, power_state+0(SB) + + BL dcflushall(SB) + /* don't write DRAM after this */ + + MOVW $PHYSPOWER, R3 + + /* put resume address in scratchpad for boot loader */ + MOVW $power_resume+0(SB), R2 + MOVW R2, 0x8(R3) /* pspr */ + + /* disable clock switching */ + MCR CpPWR, 0, R1, C(CpTest), C(0x2), 2 + + /* adjust mem timing first to avoid processor bug causing hang */ + MOVW $MDCNFG, R5 + MOVW 0x1c(R5), R2 + ORR $(MDREFR_k1db2), R2 + MOVW R2, 0x1c(R5) + + /* set PLL to lower speed w/ delay */ + MOVW $(120*206),R0 +l11: SUB $1,R0 + BGT l11 + MOVW $0, R2 + MOVW R2, 0x14(R3) /* ppcr */ + MOVW $(120*206),R0 +l12: SUB $1,R0 + BGT l12 + + /* + * SA1110 fix for various suspend bugs in pre-B4 chips (err. 14-16, 18): + * set up register values here for use in code below that is at most + * one cache line (32 bytes) long, to run without DRAM. + */ + /* 1. clear RT in MSCx (R1, R7, R8) without changing other bits */ + MOVW 0x10(R5), R1 /* MSC0 */ + BIC $(MSC_rt), R1 + MOVW 0x14(R5), R7 /* MSC1 */ + BIC $(MSC_rt), R7 + MOVW 0x2c(R5), R8 /* MSC2 */ + BIC $(MSC_rt), R8 + /* 2. clear DRI0-11 in MDREFR (R4) without changing other bits */ + MOVW 0x1c(R5), R4 + BIC $(0xfff0), R4 + /* 3. set SLFRSH in MDREFR (R6) without changing other bits */ + ORR $(MDREFR_slfrsh), R4, R6 + /* 4. clear DE in MDCNFG (R9), and any other bits desired */ + MOVW 0x0(R5), R9 + BIC $(MDCFNG_de), R9 + /* 5. clear SLFRSH and E1PIN (R10), without changing other bits */ + BIC $(MDREFR_slfrsh), R4, R10 + BIC $(MDREFR_e1pin), R10 + /* 6. force sleep mode in PMCR (R2) */ + MOVW $1,R2 + MOVW suspendcode+0(SB), R0 + B (R0) /* off to do it */ + +/* + * the following is copied by trap.c to a cache-aligned area (suspendcode), + * so that it can all run during disabling of DRAM + */ +TEXT _suspendcode(SB), $-4 + /* 1: clear RT field of all MSCx registers */ + MOVW R1, 0x10(R5) + MOVW R7, 0x14(R5) + MOVW R8, 0x2c(R5) + /* 2: clear DRI field in MDREFR */ + MOVW R4, 0x1c(R5) + /* 3: set SLFRSH bit in MDREFR */ + MOVW R6, 0x1c(R5) + /* 4: clear DE bits in MDCFNG */ + MOVW R9, 0x0(R5) + /* 5: clear SLFRSH and E1PIN in MDREFR */ + MOVW R10, 0x1c(R5) + /* 6: suspend request */ + MOVW R2, 0x0(R3) /* pmcr */ + B 0(PC) /* wait for it */ + +/* + * The boot loader comes here after the resume. + */ +TEXT power_resume(SB), $-4 + MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R0 + MOVW R0, CPSR /* svc mode, interrupts off */ + MOVW $setR12(SB), R12 + + /* flush caches */ + MCR CpMMU, 0, R0, C(CpCacheCtl), C(7), 0 + /* drain prefetch */ + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + /* drain write buffer */ + MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4 + /* flush tlb */ + MCR CpMMU, 0, R0, C(CpTLBops), C(7) + + /* restore state */ + MOVW power_state+0(SB), R13 + MOVM.IA.W (R13), [R1-R4] + MOVW.P 4(R13), R14 + + MCR CpMMU, 0, R1, C(CpDAC), C(0x0) + MCR CpMMU, 0, R2, C(CpTTB), C(0x0) + MCR CpMMU, 0, R3, C(CpPID), C(0x0) + MCR CpMMU, 0, R4, C(CpControl), C(0x0) /* enable cache and mmu */ + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + /* flush i&d caches */ + MCR CpMMU, 0, R0, C(CpCacheCtl), C(7) + /* flush tlb */ + MCR CpMMU, 0, R0, C(CpTLBops), C(7) + /* drain prefetch */ + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + /* enable clock switching */ + MCR CpPWR, 0, R1, C(CpTest), C(1), 2 + RET +#endif + + GLOBL power_state+0(SB), $4 + +#ifdef YYY +/* for debugging sleep code: */ +TEXT fastreset(SB), $-4 + MOVW $PHYSRESET, R7 + MOVW $1, R1 + MOVW R1, (R7) + RET +#endif diff --git a/os/pxa/mmu.c b/os/pxa/mmu.c new file mode 100644 index 00000000..52326237 --- /dev/null +++ b/os/pxa/mmu.c @@ -0,0 +1,252 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +/* + * Small pages: + * L1: 12-bit index -> 4096 descriptors -> 16Kb + * L2: 8-bit index -> 256 descriptors -> 1Kb + * Each L2 descriptor has access permissions for 4 1Kb sub-pages. + * + * TTB + L1Tx gives address of L1 descriptor + * L1 descriptor gives PTBA + * PTBA + L2Tx gives address of L2 descriptor + * L2 descriptor gives PBA + * + * on the Xscale, X, C & B are interpreted as follows: + * X = 0: + * C=0 B=0 uncached, unbuffered, stall until data access complete + * C=0 B=1 uncached, buffered (different from Strongarm) + * C=1 B=0 cached, buffered, write through, read allocate (different from Strongarm) + * C=1 B=1 cached, buffered, write back, read allocate + * X = 1: + * C=0 B=0 undefined + * C=0 B=1 uncached, buffered, writes will not coalesce + * C=1 B=0 mini data cache (policy set by auxiliary control reg) + * C=1 B=1 cached, buffered, write back, read/write allocate + * and the i-cache uses only the C bit (cached if non-zero). + */ +#define TTB(pa) ((pa) & ~0x3FFF) /* translation table base */ +#define L1x(pa) (((pa)>>20) & 0xFFF) /* L1 table index */ +#define PTBA(pa) ((pa) & ~0x3FF) /* page table base address */ +#define L2x(pa) (((pa)>>12) & 0xFF) /* L2 table index */ +#define PBA(pa) ((pa) & ~0xFFF) /* page base address */ +#define SBA(pa) ((pa) & ~0xFFFFF) /* section base address */ + +enum { + /* sizes */ + Section= 1<<20, + LargePage= 1<<16, + SmallPage= 1<<12, + EsmallPage= 1<<10, + SectionPages = Section/SmallPage, + PtAlign = 1<<10, + + /* L1 descriptor format */ + L1type= 3<<0, /* mask for type */ + L1page= 1<<0, /* descriptor is for L2 pages */ + L1section= 2<<0, /* descriptor is for section */ + L1fpage= 3<<0, /* descriptor is for fine (1k) L2 pages */ + L1buffered= 1<<2, + L1cached= 1<<3, + L1P= 1<<9, /* application-processor specific */ + L1sectionX= 1<<12, /* X bit in section descriptor */ + L1minicache= (L1sectionX | L1cached), + + /* L2 descriptor format for coarse page table */ + L2type= 3<<0, /* mask for type */ + L2invalid= 0<<0, + L2large= 1<<0, /* large page */ + L2small= 2<<0, /* small page */ + L2esmall= 3<<0, /* extended small page */ + L2buffered= 1<<2, + L2cached= 1<<3, + /* then access permissions */ + L2smallX= 1<<6, + L2largeX= 1<<12, + + /* domains */ + Dnone= 0, + Dclient= 1, + Dmanager= 3, + + /* access permissions */ + APsro= 0, /* supervisor ro if S|R */ + APsrw= 1, /* supervisor rw */ + APuro= 2, /* supervisor rw + user ro */ + APurw= 3, /* supervisor rw + user rw */ + + MINICACHED = 0x10000000, +}; + +#define L1dom(d) (((d) & 0xF)<<5) /* L1 domain */ +#define AP(i, v) ((v)<<(((i)*2)+4)) /* access permissions */ +#define L1AP(v) AP(3, (v)) +#define L2AP(v) AP(3, (v))|AP(2, (v))|AP(1, (v))|AP(0, (v)) + +#define L1krw (L1AP(APsrw) | L1dom(0)) + +/* + * return physical address corresponding to a given virtual address, + * or 0 if there is no such address + */ +ulong +va2pa(void *v) +{ + int idx; + ulong pte, ste, *ttb; + + idx = L1x((ulong)v); + ttb = (ulong*)KTTB; + ste = ttb[idx]; + switch(ste & L1type) { + case L1section: + return SBA(ste)|((ulong)v & 0x000fffff); + case L1page: + pte = ((ulong *)PTBA(ste))[L2x((ulong)v)]; + switch(pte & 3) { + case L2large: + return (pte & 0xffff0000)|((ulong)v & 0x0000ffff); + case L2small: + return (pte & 0xfffff000)|((ulong)v & 0x00000fff); + } + } + return 0; +} + +/* for debugging */ +void +prs(char *s) +{ + for(; *s; s++) + uartputc(*s); +} + +void +pr16(ulong n) +{ + int i, c; + + for(i=28; i>=0; i-=4){ + c = (n>>i) & 0xF; + if(c >= 0 && c <= 9) + c += '0'; + else + c += 'A'-10; + uartputc(c); + } +} + +void +xdelay(int n) +{ + int j; + + for(j=0; j<1000000/4; j++) + n++; + USED(n); +} + +void* +mmuphysmap(ulong phys, ulong) +{ + ulong *ttb; + void *va; + + ttb = (ulong*)KTTB; + va = KADDR(phys); + ttb[L1x((ulong)va)] = phys | L1krw | L1section; + return va; +} + +/* + * Set a 1-1 map of virtual to physical memory, except: + * doubly-map page0 at the alternative interrupt vector address, + * doubly-map physical memory at KZERO+256*MB as uncached but buffered, + * map flash to virtual space away from 0, + * disable access to 0 (nil pointers). + * + * Other section maps are added later as required by mmuphysmap. + */ +void +mmuinit(void) +{ + int i; + ulong *ttb, *ptable, va; + + ttb = (ulong*)KTTB; + for(i=0; i>5) +#define GPAF(n,v) ((v)<<(((n)&15)*2)) + +void gpioreserve(int); +void gpioconfig(int, ulong); +ulong gpioget(int); +void gpioset(int, int); +void gpiorelease(int); + +enum { + /* software configuration bits for gpioconfig */ + Gpio_gpio= 0<<0, + Gpio_Alt1= 1<<0, + Gpio_Alt2= 2<<0, + Gpio_Alt3= 3<<0, + Gpio_in= 1<<2, + Gpio_out= 1<<3, +}; + +/* + * software structures used by ../port/devi2c.c and iic.c + */ +struct I2Cdev { + int addr; + int salen; /* length in bytes of subaddress, if used; 0 otherwise */ + int tenbit; /* 10-bit addresses */ +}; + +long i2crecv(I2Cdev*, void*, long, ulong); +long i2csend(I2Cdev*, void*, long, ulong); +void i2csetup(int); + +#define COREREG ((Coreregs*)PHYSCORE) +typedef struct Coreregs Coreregs; +struct Coreregs { + ulong cccr; /* core clock config */ + ulong cken; /* clock enable */ + ulong oscc; /* oscillator configuration */ +}; + +#define RTCREG ((RTCreg*)PHYSRTC) +typedef struct RTCreg RTCreg; +struct RTCreg { + ulong rcnr; /* count */ + ulong rtar; /* alarm */ + ulong rtsr; /* status */ + ulong rttr; /* trim */ +}; + +#define OSTMRREG ((OstmrReg*)PHYSOSTMR) +typedef struct OstmrReg OstmrReg; +struct OstmrReg { + ulong osmr[4]; /* match */ + ulong oscr; /* counter */ + ulong ossr; /* status */ + ulong ower; /* watchdog */ + ulong oier; /* interrupt enable */ +}; + +#define PMGRREG ((PmgrReg*)PHYSPOWER) +typedef struct PmgrReg PmgrReg; +struct PmgrReg { + ulong pmcr; /* ctl register */ + ulong pssr; /* sleep status */ + ulong pspr; /* scratch pad */ + ulong pwer; /* wakeup enable */ + ulong prer; /* rising-edge detect enable */ + ulong pfer; /* falling-edge detect enable */ + ulong pedr; /* GPIO edge detect status */ + ulong pcfr; /* general configuration */ + ulong pgsr[3]; /* GPIO sleep state */ + ulong rsvd; + ulong rcsr; /* reset controller status register */ +}; + +enum { + /* pp. 3-25 to 3-31 */ + PWER_rtc = 1<<31, /* wakeup by RTC alarm */ + PWER_we0 = 1<<0, /* wake-up on GP0 edge detect */ + + PSSR_sss = 1<<0, /* software sleep status */ + PSSR_bfs = 1<<1, /* battery fault status */ + PSSR_vfs = 1<<2, /* VDD fault status */ + PSSR_ph = 1<<4, /* peripheral control hold */ + PSSR_rdh = 1<<5, /* read disable hold */ + + PMFW_fwake= 1<<1, /* fast wakeup enable (no power stabilisation delay) */ + + RSCR_gpr= 1<<3, /* gpio reset has occurred */ + RSCR_smr= 1<<2, /* sleep mode has occurred */ + RSCR_wdr= 1<<1, /* watchdog reset has occurred */ + RSCR_hwr= 1<<0, /* hardware reset has occurred */ +}; + +#define MEMCFGREG ((MemcfgReg*)PHYSMEMCFG) +typedef struct MemcfgReg MemcfgReg; +struct MemcfgReg { + ulong mdcnfg; /* SDRAM config */ + ulong mdrefr; /* dram refresh */ + ulong msc0; /* static memory or devices */ + ulong msc1; + ulong msc2; /* static memory or devices */ + ulong mecr; /* expansion bus (pcmcia, CF) */ + ulong sxcnfg; /* synchronous static memory control */ + ulong sxmrs; /* MRS value to write to SMROM */ + ulong mcmem0; /* card interface socket 0 memory timing */ + ulong mcmem1; /* card interface socket 1 memory timing */ + ulong mcatt0; /* socket 0 attribute memory timing */ + ulong mcatt1; /* socket 1 attribute memory timing */ + ulong mcio0; /* socket 0 i/o timing */ + ulong mcio1; /* socket 1 i/o timing */ + ulong mdmrs; /* MRS value to write to SDRAM */ + ulong boot_def; /* read-only boot-time register */ + ulong mdmrslp; /* low-power SDRAM mode register set config */ + ulong sa1111cr; /* SA1111 compatibility */ +}; + +#define LCDREG ((LcdReg*)PHYSLCD) +typedef struct LcdReg LcdReg; +struct LcdReg { + ulong lccr0; /* control 0 */ + ulong lccr1; /* control 1 */ + ulong lccr2; /* control 2 */ + ulong lccr3; /* control 3 */ + struct { + ulong fdadr; /* dma frame descriptor address register */ + ulong fsadr; /* dma frame source address register */ + ulong fidr; /* dma frame ID register */ + ulong ldcmd; /* dma command */ + } frame[2]; + ulong fbr[2]; /* frame branch register */ + ulong lcsr; /* status */ + ulong liidr; /* interrupt ID register */ + ulong trgbr; /* TMED RGB seed register */ + ulong tcr; /* TMED control register */ +}; + +#define USBREG ((UsbReg*)PHYSUSB) +typedef struct UsbReg UsbReg; +struct UsbReg { + ulong udccr; /* control */ + ulong udccs[16]; /* endpoint control/status */ + ulong ufnrh; /* frame number high */ + ulong ufnrl; /* frame number low */ + ulong udbcr2; + ulong udbcr4; + ulong udbcr7; + ulong udbcr9; + ulong udbcr12; + ulong udbcr14; + ulong uddr[16]; /* endpoint data */ + ulong uicr0; + ulong uicr1; + ulong usir0; + ulong usir1; +}; + +enum { + /* DMA configuration parameters */ + + /* DMA Direction */ + DmaOut= 0, + DmaIn= 1, + + /* dma devices */ + DmaDREQ0= 0, + DmaDREQ1, + DmaI2S_i, + DmaI2S_o, + DmaBTUART_i, + DmaBTUART_o, + DmaFFUART_i, + DmaFFUART_o, + DmaAC97mic, + DmaAC97modem_i, + DmaAC97modem_o, + DmaAC97audio_i, + DmaAC97audio_o, + DmaSSP_i, + DmaSSP_o, + DmaNSSP_i, + DmaNSSP_o, + DmaICP_i, + DmaICP_o, + DmaSTUART_i, + DmaSTUART_o, + DmaMMC_i, + DmaMMC_o, + DmaRsvd0, + DmaRsvd1, + DmaUSB1, + DmaUSB2, + DmaUSB3, + DmaUSB4, + DmaHWUART_i, + DmaUSB6, + DmaUSB7, + DmaUSB8, + DmaUSB9, + DmaHWUART_o, + DmaUSB11, + DmaUSB12, + DmaUSB13, + DmaUSB14, + DmaRsvd2, +}; + +/* + * Interface to platform-specific PCMCIA signals, in arch*.c + */ +enum { + /* argument to pcmpin() */ + PCMready, + PCMeject, + PCMstschng, +}; + +/* + * physical device addresses are mapped to the same virtual ones, + * allowing the same addresses to be used with or without mmu. + */ + +#define PCMCIAcard(n) (PHYSPCMCIA0+((n)*PCMCIASIZE)) +#define PCMCIAIO(n) (PCMCIAcard(n)+0x0) /* I/O space */ +#define PCMCIAAttr(n) (PCMCIAcard(n)+0x8000000) /* Attribute space*/ +#define PCMCIAMem(n) (PCMCIAcard(n)+0xC000000) /* Memory space */ + +/* + * PCMCIA structures known by both port/cis.c and the pcmcia driver + */ + +/* + * Map between ISA memory space and PCMCIA card memory space. + */ +struct PCMmap { + ulong ca; /* card address */ + ulong cea; /* card end address */ + ulong isa; /* local virtual address */ + int len; /* length of the ISA area */ + int attr; /* attribute memory */ +}; + +/* + * a PCMCIA configuration entry + */ +struct PCMconftab +{ + int index; + ushort irqs; /* legal irqs */ + uchar irqtype; + uchar bit16; /* true for 16 bit access */ + uchar nlines; + struct { + ulong start; + ulong len; + } io[16]; + int nio; + uchar vcc; + uchar vpp1; + uchar vpp2; + uchar memwait; + ulong maxwait; + ulong readywait; + ulong otherwait; +}; + +/* + * PCMCIA card slot + */ +struct PCMslot +{ + RWlock; + + Ref ref; + + long memlen; /* memory length */ + uchar slotno; /* slot number */ + void *regs; /* i/o registers */ + void *mem; /* memory */ + void *attr; /* attribute memory */ + + /* status */ + uchar occupied; /* card in the slot */ + uchar configed; /* card configured */ + uchar busy; + uchar powered; + uchar battery; + uchar wrprot; + uchar enabled; + uchar special; + uchar dsize; + + /* cis info */ + int cisread; /* set when the cis has been read */ + char verstr[512]; /* version string */ + uchar cpresent; /* config registers present */ + ulong caddr; /* relative address of config registers */ + int nctab; /* number of config table entries */ + PCMconftab ctab[8]; + PCMconftab *def; /* default conftab */ + + /* maps are fixed */ + PCMmap memmap; + PCMmap attrmap; +}; diff --git a/os/pxa/sa1110break.c b/os/pxa/sa1110break.c new file mode 100644 index 00000000..3ab87b06 --- /dev/null +++ b/os/pxa/sa1110break.c @@ -0,0 +1,360 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" +#include "../port/error.h" + +// +// from trap.c +// +extern int (*breakhandler)(Ureg *ur, Proc*); +extern Instr BREAK; +extern void portbreakinit(void); + +// +// Instructions that can have the PC as a destination register +// +enum { + IADD = 1, + IBRANCH, + ILDM, + ILDR, + IMOV, + + // + // These should eventually be implemented + // + IADC, + IAND, + IBIC, + IEOR, + ILDRT, + IMRS, + IMVN, + IORR, + IRSB, + IRSC, + ISBC, + ISUB, +}; + +static int instrtype(Instr i); +static ulong iadd(Ureg *ur, Instr i); +static ulong ibranch(Ureg *ur, Instr i); +static ulong ildm(Ureg *ur, Instr i); +static ulong ildr(Ureg *ur, Instr i); +static ulong imov(Ureg *ur, Instr i); +static ulong shifterval(Ureg *ur, Instr i); +static int condpass(Instr i, ulong psr); +static ulong *address(Ureg *ur, Instr i); +static ulong* multiaddr(Ureg *ur, Instr i); +static int nbits(ulong v); + +#define COND_N(psr) (((psr) >> 31) & 1) +#define COND_Z(psr) (((psr) >> 30) & 1) +#define COND_C(psr) (((psr) >> 29) & 1) +#define COND_V(psr) (((psr) >> 28) & 1) +#define REG(i, a, b) (((i) & BITS((a), (b))) >> (a)) +#define REGVAL(ur, r) (*((ulong*)(ur) + (r))) +#define LSR(v, s) ((ulong)(v) >> (s)) +#define ASR(v, s) ((long)(v) >> (s)) +#define ROR(v, s) (LSR((v), (s)) | (((v) & ((1 << (s))-1)) << (32 - (s)))) + +void +machbreakinit(void) +{ + portbreakinit(); + breakhandler = breakhit; +} + +Instr +machinstr(ulong addr) +{ + if (addr < KTZERO) + error(Ebadarg); + return *(Instr*)addr; +} + +void +machbreakset(ulong addr) +{ + if (addr < KTZERO) + error(Ebadarg); + *(Instr*)addr = BREAK; + segflush((void*)addr, sizeof(Instr)); +} + +void +machbreakclear(ulong addr, Instr i) +{ + if (addr < KTZERO) + error(Ebadarg); + *(Instr*)addr = i; + segflush((void*)addr, sizeof(Instr)); +} + +// +// Return the address of the instruction that will be executed after the +// instruction at address ur->pc. +// +// This means decoding the instruction at ur->pc. +// +// In the simple case, the PC will simply be the address of the next +// sequential instruction following ur->pc. +// +// In the complex case, the instruction is a branch of some sort, so the +// value of the PC after the instruction must be computed by decoding +// and simulating the instruction enough to determine the PC. +// + +ulong +machnextaddr(Ureg *ur) +{ + Instr i; + i = machinstr(ur->pc); + switch(instrtype(i)) { + case IADD: return iadd(ur,i); + case IBRANCH: return ibranch(ur,i); + case ILDM: return ildm(ur,i); + case ILDR: return ildr(ur,i); + case IMOV: return imov(ur,i); + + case IADC: + case IAND: + case IBIC: + case IEOR: + case ILDRT: + case IMRS: + case IMVN: + case IORR: + case IRSB: + case IRSC: + case ISBC: + case ISUB: + // XXX - Tad: unimplemented + // + // any of these instructions could possibly have the + // PC as Rd. Eventually, these should all be + // checked just like the others. + default: + return ur->pc+4; + } + + return 0; +} + +static int +instrtype(Instr i) +{ + if(i & BITS(26,27) == 0) { + switch((i >> 21) & 0xF) { + case 0: return IAND; + case 1: return IEOR; + case 2: return ISUB; + case 3: return IRSB; + case 4: return IADD; + case 5: return IADC; + case 6: return ISBC; + case 7: return IRSC; + case 0xD: return IMOV; + case 0xC: return IORR; + case 0xE: return IBIC; + case 0xF: return IMVN; + } + if(((i & BIT(25)|BITS(23,24)|BITS(20,21))) >> 20 == 0x10) + return IMRS; + return 0; + } + + if(((i & BITS(27,25)|BIT(20)) >> 20) == 0x81) return ILDM; + if(((i & BITS(26,27)|BIT(22)|BIT(20)) >> 20) == 0x41) return ILDR; + if(((i & BITS(25,27)) >> 25) == 5) return IBRANCH; + + return 0; +} + +static ulong +iadd(Ureg *ur, Instr i) +{ + ulong Rd = REG(i, 12, 15); + ulong Rn = REG(i, 16, 19); + + if(Rd != 15 || !condpass(i, ur->psr)) + return ur->pc+4; + + return REGVAL(ur, Rn) + shifterval(ur, i); +} + +static ulong +ibranch(Ureg *ur, Instr i) +{ + if(!condpass(i, ur->psr)) + return ur->pc+4; + return ur->pc + ((signed long)(i << 8) >> 6) + 8; +} + +static ulong +ildm(Ureg *ur, Instr i) +{ + if((i & BIT(15)) == 0) + return ur->pc+4; + + return *(multiaddr(ur, i) + nbits(i & BITS(15, 0))); +} + +static ulong +ildr(Ureg *ur, Instr i) +{ + if(REG(i, 12, 19) != 15 || !condpass(i, ur->psr)) + return ur->pc+4; + + return *address(ur, i); +} + +static ulong +imov(Ureg *ur, Instr i) +{ + if(REG(i, 12, 15) != 15 || !condpass(i, ur->psr)) + return ur->pc+4; + + return shifterval(ur, i); +} + +static int +condpass(Instr i, ulong psr) +{ + uchar n = COND_N(psr); + uchar z = COND_Z(psr); + uchar c = COND_C(psr); + uchar v = COND_V(psr); + + switch(LSR(i,28)) { + case 0: return z; + case 1: return !z; + case 2: return c; + case 3: return !c; + case 4: return n; + case 5: return !n; + case 6: return v; + case 7: return !v; + case 8: return c && !z; + case 9: return !c || z; + case 10: return n == v; + case 11: return n != v; + case 12: return !z && (n == v); + case 13: return z && (n != v); + case 14: return 1; + case 15: return 0; + } +} + +static ulong +shifterval(Ureg *ur, Instr i) +{ + if(i & BIT(25)) { // IMMEDIATE + ulong imm = i & BITS(0,7); + ulong s = (i & BITS(8,11)) >> 7; // this contains the * 2 + return ROR(imm, s); + } else { + ulong Rm = REGVAL(ur, REG(i, 0, 3)); + ulong s = (i & BITS(7,11)) >> 7; + + switch((i & BITS(6,4)) >> 4) { + case 0: // LSL + return Rm << s; + case 1: // LSLREG + s = REGVAL(ur, s >> 1) & 0xFF; + if(s >= 32) return 0; + return Rm << s; + case 2: // LSRIMM + return LSR(Rm, s); + case 3: // LSRREG + s = REGVAL(ur, s >> 1) & 0xFF; + if(s >= 32) return 0; + return LSR(Rm, s); + case 4: // ASRIMM + if(s == 0) { + if(Rm & BIT(31) == 0) + return 0; + return 0xFFFFFFFF; + } + return ASR(Rm, s); + case 5: // ASRREG + s = REGVAL(ur, s >> 1) & 0xFF; + if(s >= 32) { + if(Rm & BIT(31) == 0) + return 0; + return 0xFFFFFFFF; + } + return ASR(Rm, s); + case 6: // RORIMM + if(s == 0) + return (COND_C(ur->psr) << 31) | LSR(Rm, 1); + return ROR(Rm, s); + case 7: // RORREG + s = REGVAL(ur, s >> 1) & 0xFF; + if(s == 0 || (s & 0xF) == 0) + return Rm; + return ROR(Rm, s & 0xF); + } + } +} + +static ulong* +address(Ureg *ur, Instr i) +{ + ulong Rn = REGVAL(ur, REG(i, 16, 19)); + + if(i & BIT(24) == 0) // POSTIDX + return (ulong*)REGVAL(ur, Rn); + if(i & BIT(25) == 0) { // OFFSET + if(i & BIT(23)) + return (ulong*)(REGVAL(ur, Rn) + (i & BITS(0, 11))); + return (ulong*)(REGVAL(ur, Rn) - (i & BITS(0, 11))); + } else { // REGOFF + ulong Rm = REGVAL(ur, REG(i, 0, 3)); + ulong index = 0; + switch(i & BITS(5,6) >> 5) { + case 0: index = Rm << ((i & BITS(7, 11)) >> 7); break; + case 1: index = LSR(Rm, ((i & BITS(7, 11)) >> 7)); break; + case 2: index = ASR(Rm, ((i & BITS(7, 11)) >> 7)); break; + case 3: + if(i & BITS(7, 11) == 0) + index = (COND_C(ur->psr) << 31) | LSR(Rm, 1); + else + index = ROR(Rm, (i & BITS(7, 11)) >> 7); + break; + } + if(i & BIT(23)) + return (ulong*)(Rn + index); + return (ulong*)(Rn - index); + } +} + +static ulong* +multiaddr(Ureg *ur, Instr i) +{ + ulong Rn = REGVAL(ur, REG(i, 16, 19)); + + switch((i >> 23) & 3) { + case 0: return (ulong*)(Rn - (nbits(i & BITS(0,15))*4)+4); + case 1: return (ulong*)Rn; + case 2: return (ulong*)(Rn - (nbits(i & BITS(0,15))*4)); + case 3: return (ulong*)(Rn + 4); + } +} + +static int +nbits(ulong v) +{ + int n = 0; + int i; + for(i = 0; i < 32; i++) { + if(v & 1) + ++n; + v = LSR(v, 1); + } + return n; +} diff --git a/os/pxa/trap.c b/os/pxa/trap.c new file mode 100644 index 00000000..43117330 --- /dev/null +++ b/os/pxa/trap.c @@ -0,0 +1,587 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +#define waslo(sr) (!((sr) & (PsrDirq|PsrDfiq))) + +typedef struct Handler Handler; +struct Handler { + void (*r)(Ureg*, void*); + void *a; + char name[KNAMELEN]; + Handler *next; +}; + +static Handler irqvec[32]; +static Handler gpiovec[MaxGPIObit+1]; +static Lock veclock; + +Instr BREAK = 0xE6BAD010; + +int (*breakhandler)(Ureg*, Proc*); +int (*catchdbg)(Ureg *, uint); +void (*suspendcode)(void); + +extern void (*serwrite)(char *, int); + +/* + * Interrupt sources not masked by splhi(): special + * interrupt handlers (eg, profiler or watchdog), not allowed + * to share regular kernel data structures. All interrupts are + * masked by splfhi(), which should only be used sparingly. + * splflo enables FIQ but no others. + */ +enum { + IRQ_NONMASK = (1 << IRQtimer3) | (1 << IRQtimer2), +}; + +void +intrenable(int sort, int v, void (*f)(Ureg*, void*), void* a, char *name) +{ + int x, o; + ulong m; + GpioReg *g; + Handler *ie; + + ilock(&veclock); + switch(sort) { + case GPIOfalling: + case GPIOrising: + case GPIOboth: + if(v < 0 || v > MaxGPIObit) + panic("intrenable: gpio %d out of range", v); + g = GPIOREG; + o = GPR(v); + m = GPB(v); + switch(sort){ + case GPIOfalling: + g->gfer[o] |= m; + g->grer[o] &= ~m; + break; + case GPIOrising: + g->grer[o] |= m; + g->gfer[o] &= ~m; + break; + case GPIOboth: + g->grer[o] |= m; + g->gfer[o] |= m; + break; + } + g->gpdr[o] &= ~m; /* must be input */ + if(v > MaxGPIOIRQ) { + ie = &gpiovec[v]; + if(ie->r != nil) + iprint("duplicate gpio irq: %d (%s)\n", v, ie->name); + ie->r = f; + ie->a = a; + strncpy(ie->name, name, KNAMELEN-1); + ie->name[KNAMELEN-1] = 0; + iunlock(&veclock); + return; + } + v += IRQgpio0; + /*FALLTHROUGH for GPIO sources 0-MaxGPIOIRQ */ + case IRQ: + if(v < 0 || v > 31) + panic("intrenable: irq source %d out of range", v); + ie = &irqvec[v]; + if(ie->r != nil) + iprint("duplicate irq: %d (%s)\n", v, ie->name); + ie->r = f; + ie->a = a; + strncpy(ie->name, name, KNAMELEN-1); + ie->name[KNAMELEN-1] = 0; + + x = splfhi(); + /* Enable the interrupt by setting the mask bit */ + INTRREG->icmr |= 1 << v; + splx(x); + break; + default: + panic("intrenable: unknown irq bus %d", sort); + } + iunlock(&veclock); +} + +void +intrdisable(int sort, int v, void (*f)(Ureg*, void*), void* a, char *name) +{ + int x, o; + GpioReg *g; + Handler *ie; + ulong m; + + ilock(&veclock); + switch(sort) { + case GPIOfalling: + case GPIOrising: + case GPIOboth: + if(v < 0 || v > MaxGPIObit) + panic("intrdisable: gpio source %d out of range", v); + if(v > MaxGPIOIRQ) + ie = &gpiovec[v]; + else + ie = &irqvec[v]; + if(ie->r != f || ie->a != a || strcmp(ie->name, name) != 0) + break; + ie->r = nil; + if(v <= MaxGPIOIRQ){ + v += IRQgpio0; + x = splfhi(); + INTRREG->icmr &= ~(1<gfer[o] &= ~m; + break; + case GPIOrising: + g->grer[o] &= ~m; + break; + case GPIOboth: + g->grer[o] &= ~m; + g->gfer[o] &= ~m; + break; + } + break; + case IRQ: + if(v < 0 || v > 31) + panic("intrdisable: irq source %d out of range", v); + ie = &irqvec[v]; + if(ie->r != f || ie->a != a || strcmp(ie->name, name) != 0) + break; + ie->r = nil; + x = splfhi(); + INTRREG->icmr &= ~(1<gedr[o]; + GPIOREG->gedr[o] = e; + for(i = 0; i < 32 && e != 0; i++){ + if(e & (1<r != nil){ + cur->r(ur, cur->a); + e &= ~(1<gfer[o] &= ~e; + GPIOREG->grer[o] &= ~e; + iprint("spurious GPIO[%d] %8.8lux interrupt\n", o,e); + } + } +} + +static void +intrs(Ureg *ur, ulong ibits) +{ + Handler *cur; + int i, s; + + for(i=0; ir != nil){ + cur->r(ur, cur->a); + ibits &= ~(1<icmr &= ~ibits; + splx(s); + } +} + +/* + * initialise R13 in each trap mode, at the start and after suspend reset. + */ +void +trapstacks(void) +{ + setr13(PsrMfiq, m->fiqstack+nelem(m->fiqstack)); + setr13(PsrMirq, m->irqstack+nelem(m->irqstack)); + setr13(PsrMabt, m->abtstack+nelem(m->abtstack)); + setr13(PsrMund, m->undstack+nelem(m->undstack)); +} + +void +trapinit(void) +{ + IntrReg *intr = INTRREG; + + intr->icmr = 0; /* mask everything */ + intr->iccr = 1; /* only enabled and unmasked interrupts wake us */ + intr->iclr = IRQ_NONMASK; + + trapstacks(); + + memmove(page0->vectors, vectors, sizeof(page0->vectors)); + memmove(page0->vtable, vtable, sizeof(page0->vtable)); + dcflush(page0, sizeof(*page0)); + +#ifdef NOTYET + suspendcode = xspanalloc(16*sizeof(ulong), CACHELINESZ, 0); + memmove(suspendcode, _suspendcode, 16*sizeof(ulong)); + dcflush(suspendcode, 8*sizeof(ulong)); +#endif + + icflushall(); + + intrenable(IRQ, IRQgpio, gpiointr, nil, "gpio"); +} + +static char *trapnames[PsrMask+1] = { + [ PsrMfiq ] "Fiq interrupt", + [ PsrMirq ] "Mirq interrupt", + [ PsrMsvc ] "SVC/SWI Exception", + [ PsrMabt ] "Prefetch Abort/Data Abort", + [ PsrMabt+1 ] "Data Abort", + [ PsrMund ] "Undefined instruction", + [ PsrMsys ] "Sys trap" +}; + +static char * +trapname(int psr) +{ + char *s; + + s = trapnames[psr & PsrMask]; + if(s == nil) + s = "Undefined trap"; + return s; +} + +static void +sys_trap_error(int type) +{ + char errbuf[ERRMAX]; + sprint(errbuf, "sys: trap: %s\n", trapname(type)); + error(errbuf); +} + +static void +faultarm(Ureg *ureg, ulong far) +{ + char buf[ERRMAX]; + + sprint(buf, "sys: trap: fault pc=%8.8lux addr=0x%lux", (ulong)ureg->pc, far); + if(1){ + iprint("%s\n", buf); + dumpregs(ureg); + } + if(far == ~0) + disfault(ureg, "dereference of nil"); + disfault(ureg, buf); +} + +/* + * All traps come here. It might be slightly slower to have all traps call trap + * rather than directly vectoring the handler. + * However, this avoids a lot of code duplication and possible bugs. + * trap is called splfhi(). + */ +void +trap(Ureg* ureg) +{ + ulong far, fsr; + int rem, t, itype; + Proc *oup; + + if(up != nil) + rem = ((char*)ureg)-up->kstack; + else + rem = ((char*)ureg)-(char*)m->stack; + if(ureg->type != PsrMfiq && rem < 256) + panic("trap %d bytes remaining (%s), up=#%8.8lux ureg=#%8.8lux pc=#%8.8ux", + rem, up?up->text:"", up, ureg, ureg->pc); + + /* + * All interrupts/exceptions should be resumed at ureg->pc-4, + * except for Data Abort which resumes at ureg->pc-8. + */ + itype = ureg->type; + if(itype == PsrMabt+1) + ureg->pc -= 8; + else + ureg->pc -= 4; + ureg->sp = (ulong)(ureg+1); + if(itype == PsrMfiq){ /* fast interrupt (eg, profiler) */ + oup = up; + up = nil; + intrs(ureg, INTRREG->icfp); + up = oup; + return; + } + + /* All other traps */ + + if(0 && ureg->psr & PsrDfiq) + panic("FIQ disabled"); + + if(up){ + up->pc = ureg->pc; + up->dbgreg = ureg; + } + switch(itype) { + case PsrMirq: + t = m->ticks; /* CPU time per proc */ + up = nil; /* no process at interrupt level */ + splflo(); /* allow fast interrupts */ + intrs(ureg, INTRREG->icip); + up = m->proc; + preemption(m->ticks - t); + break; + + case PsrMund: /* Undefined instruction */ + if(*(ulong*)ureg->pc == BREAK && breakhandler) { + int s; + Proc *p; + + p = up; + /* if(!waslo(ureg->psr) || ureg->pc >= (ulong)splhi && ureg->pc < (ulong)islo) + p = 0; */ + s = breakhandler(ureg, p); + if(s == BrkSched) { + p->preempted = 0; + sched(); + } else if(s == BrkNoSched) { + p->preempted = 1; /* stop it being preempted until next instruction */ + if(up) + up->dbgreg = 0; + return; + } + break; + } + if(up == nil) + goto faultpanic; + spllo(); + if(waserror()) { + if(waslo(ureg->psr) && up->type == Interp) + disfault(ureg, up->env->errstr); + setpanic(); + dumpregs(ureg); + panic("%s", up->env->errstr); + } + if(!fpiarm(ureg)) { + dumpregs(ureg); + sys_trap_error(ureg->type); + } + poperror(); + break; + + case PsrMsvc: /* Jump through 0 or SWI */ + if(waslo(ureg->psr) && up && up->type == Interp) { + spllo(); + dumpregs(ureg); + sys_trap_error(ureg->type); + } + setpanic(); + dumpregs(ureg); + panic("SVC/SWI exception"); + break; + + case PsrMabt: /* Prefetch abort */ + if(catchdbg && catchdbg(ureg, 0)) + break; + /* FALL THROUGH */ + case PsrMabt+1: /* Data abort */ + fsr = mmugetfsr(); + far = mmugetfar(); + if(fsr & (1<<9)) { + mmuputfsr(fsr & ~(1<<9)); + if(catchdbg && catchdbg(ureg, fsr)) + break; + print("Debug/"); + } + if(waslo(ureg->psr) && up && up->type == Interp) { + spllo(); + faultarm(ureg, far); + } + iprint("Data Abort: FSR %8.8luX FAR %8.8luX\n", fsr, far); xdelay(1); + /* FALL THROUGH */ + + default: /* ??? */ +faultpanic: + setpanic(); + dumpregs(ureg); + panic("exception %uX %s\n", ureg->type, trapname(ureg->type)); + break; + } + + splhi(); + if(up) + up->dbgreg = 0; /* becomes invalid after return from trap */ +} + +void +setpanic(void) +{ + if(breakhandler != 0) /* don't mess up debugger */ + return; + INTRREG->icmr = 0; + spllo(); + consoleprint = 1; + serwrite = uartputs; +} + +int +isvalid_wa(void *v) +{ + return (ulong)v >= KZERO && (ulong)v < conf.topofmem && !((ulong)v & 3); +} + +int +isvalid_va(void *v) +{ + return (ulong)v >= KZERO && (ulong)v < conf.topofmem; +} + +void +dumplongs(char *msg, ulong *v, int n) +{ + int i, l; + + l = 0; + iprint("%s at %.8p: ", msg, v); + for(i=0; i= 4){ + iprint("\n %.8p: ", v); + l = 0; + } + if(isvalid_va(v)){ + iprint(" %.8lux", *v++); + l++; + }else{ + iprint(" invalid"); + break; + } + } + iprint("\n"); +} + +static void +_dumpstack(Ureg *ureg) +{ + ulong *v, *l; + ulong inst; + ulong *estack; + int i; + + l = (ulong*)(ureg+1); + if(!isvalid_wa(l)){ + iprint("invalid ureg/stack: %.8p\n", l); + return; + } + print("ktrace /kernel/path %.8ux %.8ux %.8ux\n", ureg->pc, ureg->sp, ureg->r14); + if(up != nil && l >= (ulong*)up->kstack && l <= (ulong*)(up->kstack+KSTACK-4)) + estack = (ulong*)(up->kstack+KSTACK); + else if(l >= (ulong*)m->stack && l <= (ulong*)((ulong)m+BY2PG-4)) + estack = (ulong*)((ulong)m+BY2PG-4); + else{ + iprint("unknown stack\n"); + return; + } + i = 0; + for(; ltype)); + if((ureg->psr & PsrMask) != PsrMsvc) + print(" in %s", trapname(ureg->psr)); + print("\n"); + print("PSR %8.8uX type %2.2uX PC %8.8uX LINK %8.8uX\n", + ureg->psr, ureg->type, ureg->pc, ureg->link); + print("R14 %8.8uX R13 %8.8uX R12 %8.8uX R11 %8.8uX R10 %8.8uX\n", + ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10); + print("R9 %8.8uX R8 %8.8uX R7 %8.8uX R6 %8.8uX R5 %8.8uX\n", + ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5); + print("R4 %8.8uX R3 %8.8uX R2 %8.8uX R1 %8.8uX R0 %8.8uX\n", + ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0); + print("Stack is at: %8.8luX\n", ureg); + print("PC %8.8lux LINK %8.8lux\n", (ulong)ureg->pc, (ulong)ureg->link); + + if(up) + print("Process stack: %8.8lux-%8.8lux\n", + up->kstack, up->kstack+KSTACK-4); + else + print("System stack: %8.8lux-%8.8lux\n", + (ulong)(m+1), (ulong)m+BY2PG-4); + dumplongs("stack", (ulong *)(ureg + 1), 16); + _dumpstack(ureg); +} + +/* + * Fill in enough of Ureg to get a stack trace, and call a function. + * Used by debugging interface rdb. + */ +void +callwithureg(void (*fn)(Ureg*)) +{ + Ureg ureg; + ureg.pc = getcallerpc(&fn); + ureg.sp = (ulong)&fn; + ureg.r14 = 0; + fn(&ureg); +} + +void +dumpstack(void) +{ + callwithureg(_dumpstack); +} + +void +trapspecial(int (*f)(Ureg *, uint)) +{ + catchdbg = f; +} diff --git a/os/rpcg/NOTICE b/os/rpcg/NOTICE new file mode 100644 index 00000000..0e031173 --- /dev/null +++ b/os/rpcg/NOTICE @@ -0,0 +1,4 @@ +Inferno® Copyright © 1996-1999 Lucent Technologies Inc. All rights reserved. +PowerPC support Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net). All rights reserved. +MPC8xx Inferno PowerPC port Copyright © 1998-2003 Vita Nuova Holdings Limited. All rights reserved. +RPCG Inferno PowerPC port Copyright © 2002-2003 Vita Nuova Holdings Limited. All rights reserved. diff --git a/os/rpcg/archrpcg.c b/os/rpcg/archrpcg.c new file mode 100644 index 00000000..8da2cab1 --- /dev/null +++ b/os/rpcg/archrpcg.c @@ -0,0 +1,428 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include +#include +#include +#include "screen.h" + +#include "../port/netif.h" +#include "../mpc/etherif.h" +#include "../port/flashif.h" +#include "archrpcg.h" + +/* + * board-specific support for the 850/823 RPCG board + */ + +enum { + /* sccr */ + COM3= IBIT(1)|IBIT(2), /* clock output disabled */ + TBS = IBIT(6), /* =0, time base is OSCCLK/{4,16}; =1, time base is GCLK2/16 */ + RTSEL = IBIT(8), /* =0, select main oscillator (OSCM); =1, select external crystal (EXTCLK) */ + RTDIV = IBIT(7), /* =0, divide by 4; =1, divide by 512 */ + CRQEN = IBIT(9), /* =1, switch to high frequency when CPM active */ + PRQEN = IBIT(10), /* =1, switch to high frequency when interrupt pending */ + EDBF2 = IBIT(14), /* =1, CLKOUT is GCLK2/2 */ + + /* plprcr */ + CSRC = IBIT(21), /* =0, clock is DFNH; =1, clock is DFNL */ +}; + +/* + * called early in main.c, after machinit: + * using board and architecture specific registers, initialise + * 8xx registers that need it and complete initialisation of the Mach structure. + */ +void +archinit(void) +{ + IMM *io; + int mf, i; + + m->bcsr = KADDR(PHYSBCSR); + m->bcsr[0] &= ~EnableEnet; + io = m->iomem; /* run by reset code: no need to lock */ + m->clockgen = 8000000; /* crystal frequency */ + m->oscclk = m->clockgen/MHz; + io->plprcrk = KEEP_ALIVE_KEY; + io->plprcr &= ~CSRC; /* general system clock is DFNH */ + mf = (io->plprcr >> 20)+1; /* use timing set by bootstrap */ + m->cpuhz = m->clockgen*mf; + m->speed = m->cpuhz/MHz; + io->plprcrk = ~KEEP_ALIVE_KEY; + io->sccrk = KEEP_ALIVE_KEY; + io->sccr |= COM3 | TBS | CRQEN | PRQEN; + io->sccrk = ~KEEP_ALIVE_KEY; + if(0){ + /* reset PCMCIA in case monitor hasn't */ + io->pgcr[1] = 1<<7; /* OP2 high to disable PCMCIA */ + io->per = 0; + io->pscr = ~0; + for(i=0; i<8; i++) + io->pcmr[i].base = io->pcmr[i].option = 0; + } +} + +static ulong +banksize(int x, ulong *pa) +{ + IMM *io; + + io = m->iomem; + if((io->memc[x].base & 1) == 0) + return 0; /* bank not valid */ + *pa = io->memc[x].base & ~0x7FFF; + return -(io->memc[x].option&~0x7FFF); +} + +/* + * initialise the kernel's memory configuration: + * there are two banks (base0, npage0) and (base1, npage1). + * initialise any other values in conf that are board-specific. + */ +void +archconfinit(void) +{ + ulong nbytes, pa, ktop; + + conf.nscc = 2; + conf.nocts2 = 1; /* TO DO: check this */ + + conf.npage0 = 0; + nbytes = banksize(DRAM1CS, &pa); + if(nbytes){ + conf.npage0 = nbytes/BY2PG; + conf.base0 = pa; + } + + conf.npage1 = 0; + + /* the following assumes the kernel text and/or data is in bank 0 */ + ktop = PGROUND((ulong)end); + ktop = PADDR(ktop) - conf.base0; + conf.npage0 -= ktop/BY2PG; + conf.base0 += ktop; + + /* check for NVRAM */ + if(m->bcsr[0] & NVRAMBattGood){ + conf.nvramsize = banksize(NVRAMCS, &pa); + conf.nvrambase = KADDR(pa); + } +} + +void +cpuidprint(void) +{ + ulong v; + int i; + + print("PVR: "); + switch(m->cputype){ + case 0x01: print("MPC601"); break; + case 0x03: print("MPC603"); break; + case 0x04: print("MPC604"); break; + case 0x06: print("MPC603e"); break; + case 0x07: print("MPC603e-v7"); break; + case 0x50: print("MPC8xx"); break; + default: print("PowerPC version #%x", m->cputype); break; + } + print(", revision #%lux\n", getpvr()&0xffff); + print("IMMR: "); + v = getimmr() & 0xFFFF; + switch(v>>8){ + case 0x00: print("MPC860/821"); break; + case 0x20: print("MPC823"); break; + case 0x21: print("MPC823A"); break; + default: print("Type #%lux", v>>8); break; + } + print(", mask #%lux\n", v&0xFF); + print("plprcr=%8.8lux sccr=%8.8lux bcsr=%8.8lux\n", m->iomem->plprcr, m->iomem->sccr, m->bcsr[0]); + print("%lud MHz system\n", m->cpuhz/MHz); + print("%lud pages\n", (conf.npage0-conf.base0)/BY2PG); + print("%ludK NVRAM\n", conf.nvramsize/1024); + print("\n"); + for(i=0; iiomem->pcmr); i++) + print("%d: %8.8lux %8.8lux\n", i, m->iomem->memc[i].base, m->iomem->memc[i].option); +} + +/* + * provide value for #r/switch (devrtc.c) + */ +int +archoptionsw(void) +{ + return (m->bcsr[0]&DipSwitchMask)>>4; +} + +/* + * invoked by clock.c:/^clockintr + */ +static void +twinkle(void) +{ + if(m->ticks%MS2TK(1000) == 0) + m->bcsr[0] ^= LedOff; +} + +void (*archclocktick)(void) = twinkle; + +/* + * invoked by ../port/taslock.c:/^ilock: + * reset watchdog timer here, if there is one and it is enabled + * (qboot currently disables it on the FADS board) + */ +void +clockcheck(void) +{ +} + +/* + * for devflash.c:/^flashreset + * retrieve flash type, virtual base and length and return 0; + * return -1 on error (no flash) + */ +int +archflashreset(int bank, Flash *f) +{ + if(bank != 0) + return -1; + f->type = "AMD29F0x0"; +// f->type = "cfi16"; + f->addr = KADDR(PHYSFLASH); + f->size = 4*1024*1024; + f->width = 4; + f->interleave = 1; + return 0; +} + +int +archether(int ctlrno, Ether *ether) +{ + if(isaconfig("ether", ctlrno, ether) == 0) + return -1; + return 1; +} + +/* + * enable the clocks for the given SCC ether and reveal them to the caller. + * do anything else required to prepare the transceiver (eg, set full-duplex, reset loopback). + */ +int +archetherenable(int cpmid, int *rcs, int *tcs, int mbps, int fullduplex) +{ + IMM *io; + + if(cpmid != CPscc2) + return -1; + USED(mbps); + USED(fullduplex); + io = ioplock(); + m->bcsr[0] = (m->bcsr[0] & ~(EnableXcrLB|DisableColTest)) | EnableEnet; + eieio(); + io->papar |= SIBIT(6)|SIBIT(4); /* enable CLK2 and CLK4 */ + io->padir &= ~(SIBIT(6)|SIBIT(4)); + iopunlock(); + *rcs = CLK4; + *tcs = CLK2; + return 0; +} + +/* + * do anything extra required to enable the UART on the given CPM port + */ +void +archenableuart(int id, int irda) +{ + USED(id, irda); +} + +/* + * do anything extra required to disable the UART on the given CPM port + */ +void +archdisableuart(int id) +{ + USED(id); +} + +/* + * enable the external USB transceiver + * speed is 12MHz if highspeed is non-zero; 1.5MHz if zero + * master is non-zero if the node is acting as USB Host and should provide power + */ +void +archenableusb(int highspeed, int master) +{ + ioplock(); + if(master) + m->bcsr[0] |= EnableUSBPwr; + else + m->bcsr[0] &= ~EnableUSBPwr; + m->bcsr[0] &= ~DisableUSB; + if(highspeed) + m->bcsr[0] |= HighSpdUSB; + else + m->bcsr[0] &= ~HighSpdUSB; + iopunlock(); +} + +/* + * shut down the USB transceiver + */ +void +archdisableusb(void) +{ + ioplock(); + m->bcsr[0] |= DisableUSB; + m->bcsr[0] &= ~EnableUSBPwr; + iopunlock(); +} + +/* + * set the external infrared transceiver to the given speed + */ +void +archsetirxcvr(int highspeed) +{ + USED(highspeed); +} + +/* + * force hardware reset/reboot + */ +void +archreboot(void) +{ + IMM *io; + + io = m->iomem; + io->plprcrk = KEEP_ALIVE_KEY; + io->plprcr |= 1<<7; /* checkstop reset enable */ + io->plprcrk = ~KEEP_ALIVE_KEY; + eieio(); + io->sdcr = 1; + eieio(); + io->lccr = 0; /* switch LCD off */ + eieio(); + firmware(0); +} + +/* + * board-specific PCMCIA support: assumes slot B on 82xFADS + */ +int +pcmslotavail(int slotno) +{ + return slotno == 1; +} + +void +pcmenable(void) +{ + ioplock(); + m->bcsr[0] = m->bcsr[0] & ~(VPPMask|VCCMask); /* power off */ + eieio(); + m->bcsr[0] |= VCC5V | VPPVCC; /* apply Vcc */ + eieio(); + m->iomem->pgcr[1] = 0; /* OP2 low to enable PCMCIA */ + iopunlock(); +iprint("B=%8.8lux\n", m->bcsr[0]); +} + +int +pcmpowered(int) +{ + ulong r; + + r = m->bcsr[0]&VCCMask; + if(r == VCC5V) + return 5; + if(r == VCC3V) + return 3; + return 0; +} + +void +pcmsetvcc(int, int v) +{ + if(v == 5) + v = VCC5V; + else if(v == 3) + v = VCC3V; + else + v = VCC0V; + ioplock(); + m->bcsr[0] = (m->bcsr[0] & ~VCCMask) | v; + iopunlock(); +} + +void +pcmsetvpp(int, int v) +{ + if(v == 5 || v == 3) + v = VPPVCC; + else if(v == 12) + v = VPP12V; + else if(v == 0) + v = VPP0V; + else + v = VPPHiZ; + ioplock(); + m->bcsr[0] = (m->bcsr[0] & ~VPPMask) | v; + iopunlock(); +} + +void +pcmpower(int slotno, int on) +{ + if(!on){ + pcmsetvcc(slotno, 0); /* turn off card power */ + pcmsetvpp(slotno, -1); /* turn off programming voltage (Hi-Z) */ + }else + pcmsetvcc(slotno, 5); +} + +/* + * enable/disable the LCD panel's backlight + */ +void +archbacklight(int on) +{ + USED(on); +} + +/* + * set parameters to describe the screen + */ +int +archlcdmode(Mode *m) +{ + m->x = 640; + m->y = 480; + m->d = 3; + m->lcd.freq = 25000000; + m->lcd.ac = 0; + m->lcd.vpw = 1; + m->lcd.wbf = 33; + m->lcd.wbl = 228; + m->lcd.flags = IsColour | IsTFT | OELow | VsyncLow | ClockLow; + return -1; /* there isn't a screen */ +} + +/* + * there isn't a keyboard port + */ +void +archkbdinit(void) +{ +} + +void +archflashwp(Flash*, int) +{ +} diff --git a/os/rpcg/archrpcg.h b/os/rpcg/archrpcg.h new file mode 100644 index 00000000..400dc2f4 --- /dev/null +++ b/os/rpcg/archrpcg.h @@ -0,0 +1,48 @@ +/* + * values for RPXLite AW + */ +enum { + /* CS assignment */ + BOOTCS = 0, + DRAM1CS = 1, + DRAM2CS = 2, + BCSRCS = 3, + NVRAMCS = 4, + /* expansion header on CS5 */ + PCMCIA0CS= 6, /* even bytes(!); CS6 to header if OP2 high */ + PCMCIA1CS= 7, /* odd bytes(!); CS7 to header if OP2 high */ +}; + +/* + * BCSR bits (there's only one register) + */ +enum { + EnableEnet = IBIT(0), + EnableXcrLB= IBIT(1), + DisableColTest= IBIT(2), + DisableFullDplx=IBIT(3), + LedOff= IBIT(4), + DisableUSB= IBIT(5), + HighSpdUSB= IBIT(6), + EnableUSBPwr= IBIT(7), + /* 8,9,10 unused */ + VCCMask= IBIT(12)|IBIT(13), + VPPMask= IBIT(14)|IBIT(15), + VCC0V= 0, + VCC5V= IBIT(13), + VCC3V= IBIT(12), + VPP0V= 0, + VPPVCC= IBIT(14), + VPP12V= IBIT(15), + VPPHiZ= IBIT(14)|IBIT(15), + /* 16-23 NYI */ + DipSwitchMask= IBIT(24)|IBIT(25)|IBIT(26)|IBIT(27), + DipSwitch0= IBIT(24), + DipSwitch1= IBIT(25), + DipSwitch2= IBIT(26), + DipSwitch3= IBIT(27), + /* bit 28 RESERVED */ + FlashComplete= IBIT(29), + NVRAMBattGood= IBIT(30), + RTCBattGood= IBIT(31), +}; diff --git a/os/rpcg/clock.c b/os/rpcg/clock.c new file mode 100644 index 00000000..05f79891 --- /dev/null +++ b/os/rpcg/clock.c @@ -0,0 +1,145 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +#include +#include + +typedef struct Clock0link Clock0link; +typedef struct Clock0link { + void (*clock)(void); + Clock0link* link; +} Clock0link; + +static Clock0link *clock0link; +static Lock clock0lock; +ulong clkrelinq; +void (*kproftick)(ulong); /* set by devkprof.c when active */ +void (*archclocktick)(void); /* set by arch*.c when desired */ + +Timer* +addclock0link(void (*clock)(void), int) +{ + Clock0link *lp; + + if((lp = malloc(sizeof(Clock0link))) == 0){ + print("addclock0link: too many links\n"); + return nil; + } + ilock(&clock0lock); + lp->clock = clock; + lp->link = clock0link; + clock0link = lp; + iunlock(&clock0lock); + return nil; +} + +void +delay(int l) +{ + ulong i, j; + + j = m->delayloop; + while(l-- > 0) + for(i=0; i < j; i++) + ; +} + +void +microdelay(int l) +{ + ulong i; + + l *= m->delayloop; + l /= 1000; + if(l <= 0) + l = 1; + for(i = 0; i < l; i++) + ; +} + +enum { + Timebase = 2, /* system clock cycles per time base cycle */ +}; + +static ulong clkreload; + +void +clockinit(void) +{ + long x; + + m->delayloop = m->cpuhz/1000; /* initial estimate */ + do { + x = gettbl(); + delay(10); + x = gettbl() - x; + } while(x < 0); + + /* + * fix count + */ + m->delayloop = ((vlong)m->delayloop*(10*m->clockgen/1000))/(x*Timebase); + if(m->delayloop == 0) + m->delayloop = 1; + + clkreload = (m->clockgen/Timebase)/HZ-1; + putdec(clkreload); +} + +void +clockintr(Ureg *ur) +{ + Clock0link *lp; + long v; + + v = -getdec(); + if(v > clkreload/2){ + if(v > clkreload) + m->ticks += v/clkreload; + v = 0; + } + putdec(clkreload-v); + + /* watchdog */ + if(m->iomem->sypcr & (1<<2)){ + m->iomem->swsr = 0x556c; + m->iomem->swsr = 0xaa39; + } + + m->ticks++; + if(archclocktick != nil) + archclocktick(); + + if(up) + up->pc = ur->pc; + + checkalarms(); + if(m->machno == 0) { + if(kproftick != nil) + (*kproftick)(ur->pc); + if(canlock(&clock0lock)){ + for(lp = clock0link; lp; lp = lp->link) + lp->clock(); + unlock(&clock0lock); + } + } + + if(up && up->state == Running){ + if(cflag && up->type == Interp && tready(nil)) + ur->cr |= 1; /* set flag in condition register for ../../libinterp/comp-power.c:/^schedcheck */ + } + /* other preemption checks are done by trap.c */ +} + +uvlong +fastticks(uvlong *hz) +{ + if(hz) + *hz = HZ; + return m->ticks; +} diff --git a/os/rpcg/dat.h b/os/rpcg/dat.h new file mode 100644 index 00000000..19d7c560 --- /dev/null +++ b/os/rpcg/dat.h @@ -0,0 +1,162 @@ +typedef struct Conf Conf; +typedef struct FPU FPU; +typedef struct FPenv FPenv; +typedef struct IMM IMM; +typedef struct Irqctl Irqctl; +typedef struct ISAConf ISAConf; +typedef struct Label Label; +typedef struct Lock Lock; +typedef struct Mach Mach; +typedef struct Map Map; +typedef struct Power Power; +typedef struct RMap RMap; +typedef struct Ureg Ureg; + +typedef ulong Instr; + +#define MACHP(n) (n==0? &mach0 : *(Mach**)0) + +struct Lock +{ + ulong key; + ulong pc; + ulong sr; + int pri; +}; + +struct Label +{ + ulong sp; + ulong pc; +}; + +/* + * Proc.fpstate + */ +enum +{ + FPINIT, + FPACTIVE, + FPINACTIVE, +}; + +/* + * This structure must agree with FPsave and FPrestore asm routines + */ +struct FPenv +{ + union { + double fpscrd; + struct { + ulong pad; + ulong fpscr; + }; + }; + int fpistate; /* emulated fp */ + ulong emreg[32][3]; /* emulated fp */ +}; +/* + * This structure must agree with fpsave and fprestore asm routines + */ +struct FPU +{ + double fpreg[32]; + FPenv env; +}; + +struct Conf +{ + ulong nmach; /* processors */ + ulong nproc; /* processes */ + ulong npage0; /* total physical pages of memory */ + ulong npage1; /* total physical pages of memory */ + ulong npage; /* total physical pages of memory */ + ulong base0; /* base of bank 0 */ + ulong base1; /* base of bank 1 */ + ulong ialloc; /* max interrupt time allocation in bytes */ + + int nscc; /* number of SCCs implemented */ + ulong smcuarts; /* bits for SMCs to define as eiaN */ + ulong sccuarts; /* bits for SCCs to define as eiaN */ + int nocts2; /* CTS2 and CD2 aren't connected */ + uchar* nvrambase; /* virtual address of nvram */ + ulong nvramsize; /* size in bytes */ +}; + +#include "../port/portdat.h" + +/* + * machine dependent definitions not used by ../port/dat.h + */ + +struct Mach +{ + /* OFFSETS OF THE FOLLOWING KNOWN BY l.s */ + int machno; /* physical id of processor (unused) */ + ulong splpc; /* pc of last caller to splhi (unused) */ + int mmask; /* 1<machno (unused) */ + + /* ordering from here on irrelevant */ + ulong ticks; /* of the clock since boot time */ + Proc *proc; /* current process on this processor */ + Label sched; /* scheduler wakeup */ + Lock alarmlock; /* access to alarm list */ + void *alarm; /* alarms bound to this clock */ + int nrdy; + int speed; /* general system clock in MHz */ + long oscclk; /* oscillator frequency (MHz) */ + long cpuhz; /* general system clock (cycles) */ + long clockgen; /* clock generator frequency (cycles) */ + int cputype; + ulong delayloop; + ulong* bcsr; + IMM* iomem; /* MPC8xx internal i/o control memory */ + + /* MUST BE LAST */ + int stack[1]; +}; +extern Mach mach0; + + +/* + * a parsed .ini line + */ +#define NISAOPT 8 + +struct ISAConf { + char* type; + ulong port; + ulong irq; + ulong mem; + int dma; + ulong size; + ulong freq; + uchar bus; + + int nopt; + char* opt[NISAOPT]; +}; + +struct Map { + int size; + ulong addr; +}; + +struct RMap { + char* name; + Map* map; + Map* mapend; + + Lock; +}; + +struct Power { + Dev* dev; + int (*powerdown)(Power*); + int (*powerup)(Power*); + int state; + void* arg; +}; + +extern register Mach *m; +extern register Proc *up; diff --git a/os/rpcg/fns.h b/os/rpcg/fns.h new file mode 100644 index 00000000..d776078d --- /dev/null +++ b/os/rpcg/fns.h @@ -0,0 +1,119 @@ +#include "../port/portfns.h" + +void addpower(Power*); +void archbacklight(int); +void archconfinit(void); +void archdisableuart(int); +void archdisableusb(void); +void archdisablevideo(void); +void archenableuart(int, int); +void archenableusb(int, int); +void archenablevideo(void); +void archkbdinit(void); +void archresetvideo(void); +int archetherenable(int, int*, int*, int, int); +void archinit(void); +int archoptionsw(void); +void archreboot(void); +void archsetirxcvr(int); +uchar* archvideobuffer(long); +ulong baudgen(int, int); +int brgalloc(void); +void brgfree(int); +int cistrcmp(char*, char*); +int cistrncmp(char*, char*, int); +void clockcheck(void); +void clockinit(void); +void clockintr(Ureg*); +void clrfptrap(void); +#define coherence() /* nothing needed for uniprocessor */ +void cpminit(void); +void cpuidprint(void); +void dcflush(void*, ulong); +void dcinval(void*, ulong); +void delay(int); +void dtlbmiss(void); +void dumplongs(char*, ulong*, int); +void dumpregs(Ureg*); +void eieio(void); +void faultpower(Ureg*); +void firmware(int); +void fpinit(void); +int fpipower(Ureg*); +void fpoff(void); +void fprestore(FPU*); +void fpsave(FPU*); +ulong fpstatus(void); +char* getconf(char*); +ulong getdar(void); +ulong getdec(void); +ulong getdepn(void); +ulong getdsisr(void); +ulong getimmr(void); +ulong getmsr(void); +ulong getpvr(void); +ulong gettbl(void); +ulong gettbu(void); +void gotopc(ulong); +void icflush(void*, ulong); +void idle(void); +#define idlehands() /* nothing to do in the runproc */ +void intr(Ureg*); +void intrenable(int, void (*)(Ureg*, void*), void*, int, char*); +void intrdisable(int, void (*)(Ureg*, void*), void*, int, char*); +int intrstats(char*, int); +void intrvec(void); +int isaconfig(char*, int, ISAConf*); +int isvalid_va(void*); +void itlbmiss(void); +void kbdinit(void); +void kbdreset(void); +void lcdpanel(int); +void links(void); +void mapfree(RMap*, ulong, int); +void mapinit(RMap*, Map*, int); +void mathinit(void); +void mmuinit(void); +ulong* mmuwalk(ulong*, ulong, int); +void pcmenable(void); +void pcmintrenable(int, void (*)(Ureg*, void*), void*); +int pcmpin(int slot, int type); +void pcmpower(int, int); +int pcmpowered(int); +void pcmsetvcc(int, int); +void pcmsetvpp(int, int); +int pcmslotavail(int); +void procsave(Proc*); +void procsetup(Proc*); +void putdec(ulong); +void putmsr(ulong); +void puttwb(ulong); +ulong rmapalloc(RMap*, ulong, int, int); +void screeninit(void); +int screenprint(char*, ...); /* debugging */ +void screenputs(char*, int); +int segflush(void*, ulong); +void setpanic(void); +long spioutin(void*, long, void*); +void spireset(void); +ulong _tas(ulong*); +void trapinit(void); +void trapvec(void); +void uartinstall(void); +void uartspecial(int, int, Queue**, Queue**, int (*)(Queue*, int)); +void uartwait(void); /* debugging */ +void videoreset(void); +void videotest(void); +void wbflush(void); + +#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) +ulong getcallerpc(void*); + +#define KADDR(a) ((void*)((ulong)(a)|KZERO)) +#define PADDR(a) ((((ulong)(a)&KSEGM)!=KSEG0)?(ulong)(a):((ulong)(a)&~KZERO)) + +/* IBM bit field order */ +#define IBIT(b) ((ulong)1<<(31-(b))) +#define SIBIT(n) ((ushort)1<<(15-(n))) + +/* compatibility with inf2.1 */ diff --git a/os/rpcg/io.h b/os/rpcg/io.h new file mode 100644 index 00000000..30312c68 --- /dev/null +++ b/os/rpcg/io.h @@ -0,0 +1 @@ +#include "../mpc/800io.h" diff --git a/os/rpcg/main.c b/os/rpcg/main.c new file mode 100644 index 00000000..5251e074 --- /dev/null +++ b/os/rpcg/main.c @@ -0,0 +1,392 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "version.h" + +/* where b.com or qboot leaves configuration info */ +#define BOOTARGS ((char*)CONFADDR) +#define BOOTARGSLEN 1024 +#define MAXCONF 32 + +extern ulong kerndate; +extern int cflag; +int remotedebug; + +extern int main_pool_pcnt; +extern int heap_pool_pcnt; +extern int image_pool_pcnt; + +char bootargs[BOOTARGSLEN+1]; +char bootdisk[KNAMELEN]; +char *confname[MAXCONF]; +char *confval[MAXCONF]; +int nconf; + +extern void addconf(char *, char *); + +/* + * arguments passed to initcode and /boot + */ +char argbuf[128]; + +static void +options(void) +{ + long i, n; + char *cp, *line[MAXCONF], *p, *q; + + /* + * parse configuration args from bootstrap + */ + memmove(bootargs, BOOTARGS, BOOTARGSLEN); /* where b.com leaves its config */ + cp = bootargs; + cp[BOOTARGSLEN-1] = 0; + + /* + * Strip out '\r', change '\t' -> ' '. + */ + p = cp; + for(q = cp; *q; q++){ + if(*q == '\r') + continue; + if(*q == '\t') + *q = ' '; + *p++ = *q; + } + *p = 0; + + n = getfields(cp, line, MAXCONF, 1, "\n"); + for(i = 0; i < n; i++){ + if(*line[i] == '#') + continue; + cp = strchr(line[i], '='); + if(cp == 0) + continue; + *cp++ = 0; + confname[nconf] = line[i]; + confval[nconf] = cp; + nconf++; + } +} + +void +doc(char *m) +{ + USED(m); + print("%s...\n", m); uartwait(); +} + +static void +poolsizeinit(void) +{ + ulong nb; + + nb = conf.npage*BY2PG; + poolsize(mainmem, (nb*main_pool_pcnt)/100, 0); + poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0); + poolsize(imagmem, (nb*image_pool_pcnt)/100, 1); +} + +static void +serialconsole(void) +{ + char *p; + int port, baud; + + p = getconf("console"); + if(p == nil) + p = "0"; + if(p != nil && !remotedebug){ + port = strtol(p, nil, 0); + baud = 9600; + p = getconf("baud"); + if(p != nil){ + baud = strtol(p, nil, 0); + if(baud < 9600) + baud = 9600; + } + uartspecial(port, baud, &kbdq, &printq, kbdcr2nl); + } +} + +void +main(void) +{ + machinit(); + options(); + archinit(); + quotefmtinstall(); + confinit(); + cpminit(); + xinit(); + poolsizeinit(); + trapinit(); + mmuinit(); + printinit(); + uartinstall(); + serialconsole(); + doc("screeninit"); + screeninit(); + doc("kbdinit"); + kbdinit(); + doc("clockinit"); + clockinit(); + doc("procinit"); + procinit(); + cpuidprint(); + doc("links"); + links(); + doc("chandevreset"); + chandevreset(); + + eve = strdup("inferno"); + + print("\nInferno %s\n", VERSION); + print("Vita Nuova\n"); + print("conf %s (%lud) jit %d\n\n",conffile, kerndate, cflag); + + doc("userinit"); + userinit(); + doc("schedinit"); + schedinit(); +} + +void +machinit(void) +{ + int n; + + n = m->machno; + memset(m, 0, sizeof(Mach)); + m->machno = n; + m->mmask = 1<machno; + m->iomem = KADDR(getimmr() & ~0xFFFF); + m->cputype = getpvr()>>16; + m->delayloop = 20000; /* initial estimate only; set by clockinit */ + m->speed = 50; /* initial estimate only; set by archinit */ +} + +void +init0(void) +{ + Osenv *o; + int i; + char buf[2*KNAMELEN]; + + up->nerrlab = 0; + + spllo(); + + if(waserror()) + panic("init0"); + /* + * These are o.k. because rootinit is null. + * Then early kproc's will have a root and dot. + */ + o = up->env; + o->pgrp->slash = namec("#/", Atodir, 0, 0); + cnameclose(o->pgrp->slash->name); + o->pgrp->slash->name = newcname("/"); + o->pgrp->dot = cclone(o->pgrp->slash); + + chandevinit(); + + if(!waserror()){ + ksetenv("cputype", "power", 0); + snprint(buf, sizeof(buf), "power %s", conffile); + ksetenv("terminal", buf, 0); + poperror(); + } + for(i = 0; i < nconf; i++) + if(confname[i][0] != '*'){ + if(!waserror()){ + ksetenv(confname[i], confval[i], 0); + poperror(); + } + } + + poperror(); + disinit("/osinit.dis"); +} + +void +userinit(void) +{ + Proc *p; + Osenv *o; + + p = newproc(); + o = p->env; + + o->fgrp = newfgrp(nil); + + o->pgrp = newpgrp(); + o->egrp = newegrp(); + kstrdup(&o->user, eve); + + strcpy(p->text, "interp"); + + /* + * Kernel Stack + */ + p->sched.pc = (ulong)init0; + p->sched.sp = (ulong)p->kstack+KSTACK; + + ready(p); +} + +Conf conf; + +void +addconf(char *name, char *val) +{ + if(nconf >= MAXCONF) + return; + confname[nconf] = name; + confval[nconf] = val; + nconf++; +} + +char* +getconf(char *name) +{ + int i; + + for(i = 0; i < nconf; i++) + if(cistrcmp(confname[i], name) == 0) + return confval[i]; + return 0; +} + +void +confinit(void) +{ + char *p; + int pcnt; + + if(p = getconf("*kernelpercent")) + pcnt = 100 - strtol(p, 0, 0); + else + pcnt = 0; + + conf.nscc = 4; + conf.smcuarts = 1<<0; /* SMC1 (usual console) */ + conf.sccuarts = 0; /* SCC2 not available by default (it's Ether) */ + + archconfinit(); + + conf.npage = conf.npage0 + conf.npage1; + if(pcnt < 10) + pcnt = 70; + conf.ialloc = (((conf.npage*(100-pcnt))/100)/2)*BY2PG; + + conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5; + conf.nmach = MAXMACH; +} + +void +exit(int ispanic) +{ + up = 0; + spllo(); + print("cpu %d exiting\n", m->machno); + + /* Shutdown running devices */ + chandevshutdown(); + + delay(1000); + splhi(); + if(ispanic) + for(;;); + archreboot(); +} + +void +reboot(void) +{ + exit(0); +} + +void +halt(void) +{ + print("cpu halted\n"); + microdelay(1000); + for(;;) + ; +} + +int +isaconfig(char *class, int ctlrno, ISAConf *isa) +{ + char cc[KNAMELEN], *p; + int i; + + snprint(cc, sizeof cc, "%s%d", class, ctlrno); + p = getconf(cc); + if(p == nil) + return 0; + + isa->nopt = tokenize(p, isa->opt, NISAOPT); + for(i = 0; i < isa->nopt; i++){ + p = isa->opt[i]; + if(cistrncmp(p, "type=", 5) == 0) + isa->type = p + 5; + else if(cistrncmp(p, "port=", 5) == 0) + isa->port = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "irq=", 4) == 0) + isa->irq = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "mem=", 4) == 0) + isa->mem = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "size=", 5) == 0) + isa->size = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "freq=", 5) == 0) + isa->freq = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "dma=", 4) == 0) + isa->dma = strtoul(p+4, &p, 0); + } + return 1; +} + +/* + * Save the mach dependent part of the process state. + */ +void +procsave(Proc*) +{ +} + +void +uartputs(char *s, int n) +{ +// screenputs(buf, n); + putstrn(s, n); + uartwait(); +} + +/* stubs */ +void +setfsr(ulong) +{ +} + +ulong +getfsr() +{ + return 0; +} + +void +setfcr(ulong) +{ +} + +ulong +getfcr() +{ + return 0; +} diff --git a/os/rpcg/mem.h b/os/rpcg/mem.h new file mode 100644 index 00000000..160569cc --- /dev/null +++ b/os/rpcg/mem.h @@ -0,0 +1,157 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ + +/* + * Sizes + */ + +#define BI2BY 8 /* bits per byte */ +#define BI2WD 32 /* bits per word */ +#define BY2WD 4 /* bytes per word */ +#define BY2V 8 /* bytes per double word */ +#define BY2PG 4096 /* bytes per page */ +#define WD2PG (BY2PG/BY2WD) /* words per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1)) +#define PGROUND(s) ROUND(s, BY2PG) +#define CACHELINELOG 4 +#define CACHELINESZ (1< */ +#define USER 29 /* R29 is up-> */ +#define IOMEMR 28 /* R28 will be iomem-> */ + +/* + * Fundamental addresses + */ + +#define UREGSIZE ((8+32)*4) + +/* + * MMU + */ + +/* L1 table entry and Mx_TWC flags */ +#define PTEVALID (1<<0) +#define PTEWT (1<<1) /* write through */ +#define PTE4K (0<<2) +#define PTE512K (1<<2) +#define PTE8MB (3<<2) +#define PTEG (1<<4) /* guarded */ + +/* L2 table entry and Mx_RPN flags (also PTEVALID) */ +#define PTECI (1<<1) /* cache inhibit */ +#define PTESH (1<<2) /* page is shared; ASID ignored */ +#define PTELPS (1<<3) /* large page size */ +#define PTEWRITE 0x9F0 + +/* TLB and MxEPN flag */ +#define TLBVALID (1<<9) + +/* + * Address spaces + */ + +#define KUSEG 0x00000000 +#define KSEG0 0x20000000 +#define KSEGM 0xE0000000 /* mask to check which seg */ + +#define KZERO KSEG0 /* base of kernel address space */ +#define KTZERO (KZERO+0x3000) /* first address in kernel text */ +#define KSTACK 8192 /* Size of kernel stack */ + +#define CONFADDR (KZERO|0x200000) /* where qboot leaves configuration info */ + +/* + * Exception codes (trap vectors) + */ +#define CRESET 0x01 +#define CMCHECK 0x02 +#define CDSI 0x03 +#define CISI 0x04 +#define CEI 0x05 +#define CALIGN 0x06 +#define CPROG 0x07 +#define CFPU 0x08 +#define CDEC 0x09 +#define CSYSCALL 0x0C +#define CTRACE 0x0D +#define CFPA 0x0E +/* rest are power-implementation dependent (8xx) */ +#define CEMU 0x10 +#define CIMISS 0x11 +#define CDMISS 0x12 +#define CITLBE 0x13 +#define CDTLBE 0x14 +#define CDBREAK 0x1C +#define CIBREAK 0x1D +#define CPBREAK 0x1E +#define CDPORT 0x1F + +/* + * MPC8xx physical addresses + */ + +/* those encouraged by rpx lite */ +#define PHYSDRAM 0x00000000 +#define PHYSNVRAM 0xFA000000 +#define PHYSIMM 0xFA200000 +#define PHYSBCSR 0xFA400000 +#define PHYSFLASH 0xFE000000 + +/* remaining ones are our choice */ +#define PHYSPCMCIA 0x04000000 +#define PCMCIALEN (8*MB) /* chosen to allow mapping by single TLB entry */ +#define ISAIO (KZERO|PHYSPCMCIA) /* for inb.s */ + +/* + * MPC8xx dual-ported CPM memory physical addresses + */ +#define PHYSDPRAM (PHYSIMM+0x2000) +#define DPLEN1 0x200 +#define DPLEN2 0x400 +#define DPLEN3 0x800 +#define DPBASE (PHYSDPRAM+DPLEN1) + +#define KEEP_ALIVE_KEY 0x55ccaa33 /* clock and rtc register key */ diff --git a/os/rpcg/mkfile b/os/rpcg/mkfile new file mode 100644 index 00000000..d0ac4390 --- /dev/null +++ b/os/rpcg/mkfile @@ -0,0 +1,108 @@ +SYSTARG=Inferno +OBJTYPE=power +<../../mkconfig + +#Configurable parameters + +CONF=rpcg #default configuration +CONFLIST=rpcg +KZERO=0x20003020 + +SYSTARG=$OSTARG +OBJTYPE=power +INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin #path of directory where kernel is installed +#INSTALLDIR=/$OBJTYPE + +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS + +OBJ=\ + l.$O\ + tlb.$O\ + nofp.$O\ + clock.$O\ + cpm.$O\ + faultpower.$O\ + fpi.$O\ + fpimem.$O\ + fpipower.$O\ + kbd.$O\ + main.$O\ + mmu.$O\ + rmap.$O\ + trap.$O\ + $CONF.root.$O\ + $IP\ + $DEVS\ + $ETHERS\ + $LINKS\ + $VGAS\ + $PORT\ + $MISC\ + $OTHERS\ + +LIBNAMES=${LIBS:%=lib%.a} + +HFILES=\ + mem.h\ + dat.h\ + fns.h\ + io.h\ + ../mpc/800io.h\ + ../mpc/screen.h\ + +CFLAGS=-wFV -I. -I../mpc -I../port -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp +KERNDATE=`{$NDATE} + +#default:V: i$CONF.sq +default:V: i$CONF + +i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD -o $target -T$KZERO -l $OBJ $CONF.$O $LIBFILES + $KSIZE $target + +i$CONF.sq: i$CONF + sqz -w i$CONF >$target + +install:V: i$CONF # i$CONF.sq + cp i$CONF $INSTALLDIR/i$CONF + #cp i$CONF.sq $INSTALLDIR/i$CONF.sq + +uninstall:V: + rm -f $ROOT/$OBJDIR/bin/i$CONF + rm -f $ROOT/$OBJDIR/bin/i$CONF.sq + +<../port/portmkfile + +../init/$INIT.dis: ../init/$INIT.b + cd ../init; mk $INIT.dis + +clock.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +devether.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +faultpower.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +main.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +trap.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h + +devether.$O $ETHERS: ../mpc/etherif.h ../port/netif.h + +#$VGAS: screen.h vga.h +$IP devip.$O: ../ip/ip.h + +%.$O: ../mpc/%.c + $CC $CFLAGS -I. -I../mpc ../mpc/$stem.c + +%.$O: ../mpc/%.s + $AS -I. -I../mpc ../mpc/$stem.s + +clock.$O: clock.c + $CC $CFLAGS -I. clock.c + +devboot.$O: devboot.c + $CC $CFLAGS devboot.c + +devuart.$O: ../mpc/devuart.c + $CC $CFLAGS ../mpc/devuart.c diff --git a/os/rpcg/mmu.c b/os/rpcg/mmu.c new file mode 100644 index 00000000..c9cd758b --- /dev/null +++ b/os/rpcg/mmu.c @@ -0,0 +1,20 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +void +mmuinit(void) +{ + /* the l.s initial TLB settings do all that's required */ +} + +int +segflush(void *a, ulong n) +{ + /* flush dcache then invalidate icache */ + dcflush(a, n); + icflush(a, n); + return 0; +} diff --git a/os/rpcg/rpcg b/os/rpcg/rpcg new file mode 100644 index 00000000..c5c90ece --- /dev/null +++ b/os/rpcg/rpcg @@ -0,0 +1,122 @@ +# rpcg RPXLite +dev + root + cons archrpcg + env + mnt + pipe + prog + rtc + srv + dup + ssl + cap + + + ip bootp ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium + ether netif netaux + uart + flash +# usb +# pcmcia cis +# ata inb + + ftl +# kfs chk kcon console dat dentry fcall fs fswren iobuf kfs sub uid +# kprof + +# vid i2c + i2c i2c + +ip + il + tcp + udp + ipifc + icmp + icmp6 + ipmux + +lib + interp + tk + draw + memlayer + memdraw + keyring + sec + mp + math + kern + +link + etherscc + ethermedium + flashamd29f0x0 +# flashcfi16 +# pppmedium ppp compress + +mod + sys +# draw +# tk + math + keyring + +port + alarm + alloc + allocb + chan + dev + dial + dis + discall + exception + exportfs + inferno + latin1 + nocache + nodynld + parse + pgrp + print + proc + qio + qlock + random + sysfile + taslock + xalloc + +code + int cflag = 0; + int consoleprint = 1; + int panicreset = 0; + int kernel_pool_pcnt = 10; + int main_pool_pcnt = 40; + int heap_pool_pcnt = 20; + int image_pool_pcnt = 40; + void screenputs(char*, int){} + void screeninit(void){} /* disabled until we've got one */ + +init + rpcginit + +root + /chan + /dev + /dis + /env + /fd / + /n + /net + /nvfs / + /prog + /icons + /osinit.dis + /dis/lib/auth.dis + /dis/lib/ssl.dis + /n/local / + /n/remote / + /nvfs/default /usr/inferno/keyring/default diff --git a/os/rpcg/tlb.s b/os/rpcg/tlb.s new file mode 100644 index 00000000..7005afbe --- /dev/null +++ b/os/rpcg/tlb.s @@ -0,0 +1,24 @@ +#include "mem.h" + +#define MB (1024*1024) + +/* + * TLB prototype entries, loaded once-for-all at startup, + * remaining unchanged thereafter. + * Limit the table to at most 8 entries to ensure + * it works on the 823 (other 8xx processors allow up to 32 TLB entries). + */ +#define TLBE(epn,rpn,twc) WORD $(epn); WORD $(twc); WORD $(rpn) + +TEXT tlbtab(SB), $-4 + + /* epn, rpn, twc */ + TLBE(KZERO|PHYSDRAM|TLBVALID, PHYSDRAM|PTEWRITE|PTELPS|PTESH|PTEVALID, PTE8MB|/*PTEWT|*/PTEVALID) /* DRAM, 8M */ + TLBE(KZERO|(PHYSDRAM+8*MB)|TLBVALID, (PHYSDRAM+8*MB)|PTEWRITE|PTELPS|PTESH|PTEVALID, PTE8MB|PTEVALID) /* DRAM, 8M */ + TLBE(KZERO|PHYSBCSR|TLBVALID, PHYSBCSR|PTEWRITE|PTESH|PTECI|PTEVALID, PTE4K|PTEWT|PTEVALID) /* Board CSR, 4K */ + TLBE(KZERO|PHYSIMM|TLBVALID, PHYSIMM|PTEWRITE|PTELPS|PTESH|PTECI|PTEVALID, PTE4K|PTEWT|PTEVALID) /* IMMR, 16K */ + TLBE(KZERO|PHYSFLASH|TLBVALID, PHYSFLASH|PTEWRITE|PTELPS|PTESH|PTECI|PTEVALID, PTE8MB|PTEWT|PTEVALID) /* Flash, 8M */ + TLBE(KZERO|PHYSPCMCIA|TLBVALID, PHYSPCMCIA|PTEWRITE|PTELPS|PTESH|PTECI|PTEVALID, PTE8MB|PTEWT|PTEG|PTEVALID) /* PCMCIA, 8M */ + TLBE(KZERO|PHYSNVRAM|TLBVALID, PHYSNVRAM|PTEWRITE|PTELPS|PTESH|PTECI|PTEVALID, PTE512K|PTEWT|PTEG|PTEVALID) /* NVRAM, 512K */ +TEXT tlbtabe(SB), $-4 + RETURN diff --git a/os/sa1110/clock.c b/os/sa1110/clock.c new file mode 100644 index 00000000..784844e9 --- /dev/null +++ b/os/sa1110/clock.c @@ -0,0 +1,317 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "ureg.h" + +static ulong timer_incr[4] = { 0, 0, 0, -1 }; + +typedef struct Clock0link Clock0link; +typedef struct Clock0link { + void (*clock)(void); + Clock0link* link; +} Clock0link; + +static Clock0link *clock0link; +static Lock clock0lock; +static void (*prof_fcn)(Ureg *, int); + +Timer* +addclock0link(void (*clock)(void), int) +{ + Clock0link *lp; + + if((lp = malloc(sizeof(Clock0link))) == 0){ + print("addclock0link: too many links\n"); + return nil; + } + ilock(&clock0lock); + lp->clock = clock; + lp->link = clock0link; + clock0link = lp; + iunlock(&clock0lock); + return nil; +} + +static void +profintr(Ureg *ur, void*) +{ + OstmrReg *ost = OSTMRREG; + int t; + + if((ost->osmr[3] - ost->oscr) < 2*CLOCKFREQ) { + /* less than 2 seconds before reset, say something */ + setpanic(); + clockpoll(); + dumpregs(ur); + panic("Watchdog timer will expire"); + } + + /* advance the profile clock tick */ + ost->osmr[2] += timer_incr[2]; + ost->ossr = (1 << 2); /* Clear the SR */ + t = 1; + while((ost->osmr[2] - ost->oscr) > 0x80000000) { + ost->osmr[2] += timer_incr[2]; + t++; + } + if(prof_fcn) + prof_fcn(ur, t); +} + +static void +clockintr(Ureg*, void*) +{ + Clock0link *lp; + int losttick = 0; + OstmrReg *ost = OSTMRREG; + + m->ticks++; + ost->osmr[3] = ost->oscr + timer_incr[3]; /* restart the watchdog */ + ost->osmr[0] += timer_incr[0]; /* advance the clock tick */ + ost->ossr = (1<<0); /* Clear the SR */ + + while((ost->osmr[0] - ost->oscr) >= 0x80000000) { + ost->osmr[0] += timer_incr[0]; + losttick++; + m->ticks++; + } + + checkalarms(); + + if(canlock(&clock0lock)){ + for(lp = clock0link; lp; lp = lp->link) + if(lp->clock) + lp->clock(); + unlock(&clock0lock); + } + + /* round robin time slice is done by trap.c and proc.c */ +} + +void +timerenable( int timer, int Hz, void (*f)(Ureg *, void*), void* a) +{ + OstmrReg *ost = OSTMRREG; + char name[KNAMELEN]; + + if(timer < 0 || timer > 3) + return; + timer_incr[timer] = CLOCKFREQ/Hz; /* set up freq */ + ost->osmr[timer] = ost->oscr+timer_incr[timer]; + snprint(name, sizeof(name), "timer%d", timer); + intrenable(OSTimerbit(timer), f, a, BusCPU, name); + ost->ossr = (1 << timer); /* clear any pending interrupt */ + ost->oier |= (1 << timer); /* enable interrupt */ +} + +void +timerdisable( int timer ) +{ + OstmrReg *ost = OSTMRREG; + + if(timer < 0 || timer > 3) + return; + ost->osmr[timer] = 0; /* clear freq */ + ost->oier &= ~(1 << timer); /* disable interrupt */ +} + +void +installprof(void (*pf)(Ureg *, int)) +{ + int s; + + s = splfhi(); + prof_fcn = pf; + timerenable( 2, HZ+1, profintr, 0); + timer_incr[2] = timer_incr[0]+63; /* fine tuning */ + splx(s); +} + +void +clockinit(void) +{ + OstmrReg *ost = OSTMRREG; + m->ticks = 0; + /* Set up timer registers */ + ost->ossr = 0xf; /* clear all four OSSR trigger bits */ + ost->oier = 0; + ost->osmr[0] = 0; + ost->osmr[1] = 0; + ost->osmr[2] = 0; + // ost->osmr[3] = 0; + timerenable( 0, HZ, clockintr, 0); + timer_incr[3] = CLOCKFREQ*10; /* 10 second watchdog */ + timer_setwatchdog(timer_incr[3]); + timerenable( 2, 1, profintr, 0); /* watch the watchdog */ +} + +void +clockpoll(void) +{ + OstmrReg *ost = OSTMRREG; + ost->osmr[3] = ost->oscr + timer_incr[3]; /* restart the watchdog */ +} + +void +clockcheck(void) +{ + OstmrReg *ost = OSTMRREG; + + if((ost->osmr[3] - ost->oscr) < CLOCKFREQ) { + setpanic(); + clockpoll(); + dumpstack(); + panic("Watchdog timer will expire"); + } +} + +// macros for fixed-point math + +ulong _mularsv(ulong m0, ulong m1, ulong a, ulong s); + +/* truncated: */ +#define FXDPTDIV(a,b,n) ((ulong)(((uvlong)(a) << (n)) / (b))) +#define MAXMUL(a,n) ((ulong)((((uvlong)1<<(n))-1)/(a))) +#define MULDIV(x,a,b,n) (((x)*FXDPTDIV(a,b,n)) >> (n)) +#define MULDIV64(x,a,b,n) ((ulong)_mularsv(x, FXDPTDIV(a,b,n), 0, (n))) + +/* rounded: */ +#define FXDPTDIVR(a,b,n) ((ulong)((((uvlong)(a) << (n))+((b)/2)) / (b))) +#define MAXMULR(a,n) ((ulong)((((uvlong)1<<(n))-1)/(a))) +#define MULDIVR(x,a,b,n) (((x)*FXDPTDIVR(a,b,n)+(1<<((n)-1))) >> (n)) +#define MULDIVR64(x,a,b,n) ((ulong)_mularsv(x, FXDPTDIVR(a,b,n), 1<<((n)-1), (n))) + + +// these routines are all limited to a maximum of 1165 seconds, +// due to the wrap-around of the OSTIMER + +ulong +timer_start(void) +{ + return OSTMRREG->oscr; +} + +ulong +timer_ticks(ulong t0) +{ + return OSTMRREG->oscr - t0; +} + +int +timer_devwait(ulong *adr, ulong mask, ulong val, int ost) +{ + int i; + ulong t0 = timer_start(); + while((*adr & mask) != val) + if(timer_ticks(t0) > ost) + return ((*adr & mask) == val) ? 0 : -1; + else + for(i = 0; i < 10; i++); /* don't pound OSCR too hard! (why not?) */ + return 0; +} + +void +timer_setwatchdog(int t) +{ + OstmrReg *ost = OSTMRREG; + ost->osmr[3] = ost->oscr + t; + if(t) { + ost->ossr = (1<<3); + ost->oier |= (1<<3); + ost->ower = 1; + } else + ost->oier &= ~(1<<3); +} + +void +timer_delay(int t) +{ + ulong t0 = timer_start(); + while(timer_ticks(t0) < t) + ; +} + + +ulong +us2tmr(int us) +{ + return MULDIV64(us, CLOCKFREQ, 1000000, 24); +} + +int +tmr2us(ulong t) +{ + return MULDIV64(t, 1000000, CLOCKFREQ, 24); +} + +void +microdelay(int us) +{ + ulong t0 = timer_start(); + ulong t = us2tmr(us); + while(timer_ticks(t0) <= t) + ; +} + +ulong +ms2tmr(int ms) +{ + return MULDIV64(ms, CLOCKFREQ, 1000, 20); +} + +int +tmr2ms(ulong t) +{ + return MULDIV64(t, 1000, CLOCKFREQ, 32); +} + +void +delay(int ms) +{ + ulong t0 = timer_start(); + ulong t = ms2tmr(ms); + while(timer_ticks(t0) <= t) + clockpoll(); +} + +/* + * for devbench.c + */ +vlong +archrdtsc(void) +{ + return OSTMRREG->oscr; +} + +ulong +archrdtsc32(void) +{ + return OSTMRREG->oscr; +} + +/* + * for devkprof.c + */ +long +archkprofmicrosecondspertick(void) +{ + return MS2HZ*1000; +} + +void +archkprofenable(int) +{ + /* TO DO */ +} + +uvlong +fastticks(uvlong *hz) +{ + if(hz) + *hz = HZ; + return m->ticks; +} diff --git a/os/sa1110/devether.c b/os/sa1110/devether.c new file mode 100644 index 00000000..f4f0c456 --- /dev/null +++ b/os/sa1110/devether.c @@ -0,0 +1,617 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +static Ether *etherxx[MaxEther]; + +Chan* +etherattach(char* spec) +{ + ulong ctlrno; + char *p; + Chan *chan; + Ether *ether; + + ctlrno = 0; + if(spec && *spec){ + ctlrno = strtoul(spec, &p, 0); + if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther)) + error(Ebadarg); + } + if((ether = etherxx[ctlrno]) == 0) + error(Enodev); + rlock(ether); + if(waserror()){ + runlock(ether); + nexterror(); + } + chan = devattach('l', spec); + chan->dev = ctlrno; + if(ether->attach) + ether->attach(etherxx[ctlrno]); + poperror(); + runlock(ether); + return chan; +} + +static void +ethershutdown(void) +{ + Ether *ether; + int i; + + for(i=0; idetach != nil) + ether->detach(ether); + } +} + +static Walkqid* +etherwalk(Chan* chan, Chan *nchan, char **name, int nname) +{ + Walkqid *wq; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + wq = netifwalk(etherxx[chan->dev], chan, nchan, name, nname); + poperror(); + runlock(ether); + return wq; +} + +static int +etherstat(Chan* chan, uchar* dp, int n) +{ + int s; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + s = netifstat(ether, chan, dp, n); + poperror(); + runlock(ether); + return s; +} + +static Chan* +etheropen(Chan* chan, int omode) +{ + Chan *c; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + c = netifopen(ether, chan, omode); + poperror(); + runlock(ether); + return c; +} + +static void +ethercreate(Chan*, char*, int, ulong) +{ +} + +static void +etherclose(Chan* chan) +{ + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + netifclose(ether, chan); + poperror(); + runlock(ether); +} + +static long +etherread(Chan* chan, void* buf, long n, vlong off) +{ + Ether *ether; + ulong offset = off; + long r; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + if((chan->qid.type & QTDIR) == 0 && ether->ifstat){ + /* + * With some controllers it is necessary to reach + * into the chip to extract statistics. + */ + if(NETTYPE(chan->qid.path) == Nifstatqid){ + r = ether->ifstat(ether, buf, n, offset); + goto out; + } + if(NETTYPE(chan->qid.path) == Nstatqid) + ether->ifstat(ether, buf, 0, offset); + } + r = netifread(ether, chan, buf, n, offset); +out: + poperror(); + runlock(ether); + return r; +} + +static Block* +etherbread(Chan* chan, long n, ulong offset) +{ + Block *b; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + b = netifbread(ether, chan, n, offset); + poperror(); + runlock(ether); + return b; +} + +static int +etherwstat(Chan* chan, uchar* dp, int n) +{ + Ether *ether; + int r; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + r = netifwstat(ether, chan, dp, n); + poperror(); + runlock(ether); + return r; +} + +static void +etherrtrace(Netfile* f, Etherpkt* pkt, int len) +{ + int i, n; + Block *bp; + + if(qwindow(f->in) <= 0) + return; + if(len > 58) + n = 58; + else + n = len; + bp = iallocb(64); + if(bp == nil) + return; + memmove(bp->wp, pkt->d, n); + i = TK2MS(MACHP(0)->ticks); + bp->wp[58] = len>>8; + bp->wp[59] = len; + bp->wp[60] = i>>24; + bp->wp[61] = i>>16; + bp->wp[62] = i>>8; + bp->wp[63] = i; + bp->wp += 64; + qpass(f->in, bp); +} + +Block* +etheriq(Ether* ether, Block* bp, int fromwire) +{ + Etherpkt *pkt; + ushort type; + int len, multi, tome, fromme; + Netfile **ep, *f, **fp, *fx; + Block *xbp; + + ether->inpackets++; + + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + type = (pkt->type[0]<<8)|pkt->type[1]; + fx = 0; + ep = ðer->f[Ntypes]; + + multi = pkt->d[0] & 1; + /* check for valid multcast addresses */ + if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){ + if(!activemulti(ether, pkt->d, sizeof(pkt->d))){ + if(fromwire){ + freeb(bp); + bp = 0; + } + return bp; + } + } + + /* is it for me? */ + tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0; + + /* + * Multiplex the packet to all the connections which want it. + * If the packet is not to be used subsequently (fromwire != 0), + * attempt to simply pass it into one of the connections, thereby + * saving a copy of the data (usual case hopefully). + */ + for(fp = ether->f; fp < ep; fp++){ + if((f = *fp) && (f->type == type || f->type < 0)) + if(tome || multi || f->prom){ + /* Don't want to hear bridged packets */ + if(f->bridge && !fromwire && !fromme) + continue; + if(!f->headersonly){ + if(fromwire && fx == 0) + fx = f; + else if(xbp = iallocb(len)){ + memmove(xbp->wp, pkt, len); + xbp->wp += len; + if(qpass(f->in, xbp) < 0) + ether->soverflows++; + } + else + ether->soverflows++; + } + else + etherrtrace(f, pkt, len); + } + } + + if(fx){ + if(qpass(fx->in, bp) < 0) + ether->soverflows++; + return 0; + } + if(fromwire){ + freeb(bp); + return 0; + } + + return bp; +} + +static int +etheroq(Ether* ether, Block* bp) +{ + int len, loopback, s; + Etherpkt *pkt; + + ether->outpackets++; + + /* + * Check if the packet has to be placed back onto the input queue, + * i.e. if it's a loopback or broadcast packet or the interface is + * in promiscuous mode. + * If it's a loopback packet indicate to etheriq that the data isn't + * needed and return, etheriq will pass-on or free the block. + * To enable bridging to work, only packets that were originated + * by this interface are fed back. + */ + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){ + s = splhi(); + etheriq(ether, bp, 0); + splx(s); + } + + if(!loopback){ + qbwrite(ether->oq, bp); + if(ether->transmit != nil) + ether->transmit(ether); + }else + freeb(bp); + + return len; +} + +static long +etherwrite(Chan* chan, void* buf, long n, vlong) +{ + Ether *ether; + Block *bp; + int onoff; + Cmdbuf *cb; + long l; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + if(NETTYPE(chan->qid.path) != Ndataqid) { + l = netifwrite(ether, chan, buf, n); + if(l >= 0) + goto out; + cb = parsecmd(buf, n); + if(strcmp(cb->f[0], "nonblocking") == 0){ + if(cb->nf <= 1) + onoff = 1; + else + onoff = atoi(cb->f[1]); + qnoblock(ether->oq, onoff); + free(cb); + goto out; + } + free(cb); + if(ether->ctl!=nil){ + l = ether->ctl(ether,buf,n); + goto out; + } + error(Ebadctl); + } + + if(n > ether->maxmtu) + error(Etoobig); + if(n < ether->minmtu) + error(Etoosmall); + bp = allocb(n); + if(waserror()){ + freeb(bp); + nexterror(); + } + memmove(bp->rp, buf, n); + memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen); + bp->wp += n; + poperror(); + + l = etheroq(ether, bp); +out: + poperror(); + runlock(ether); + return l; +} + +static long +etherbwrite(Chan* chan, Block* bp, ulong) +{ + Ether *ether; + long n; + + n = BLEN(bp); + if(NETTYPE(chan->qid.path) != Ndataqid){ + if(waserror()) { + freeb(bp); + nexterror(); + } + n = etherwrite(chan, bp->rp, n, 0); + poperror(); + freeb(bp); + return n; + } + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + if(n > ether->maxmtu){ + freeb(bp); + error(Etoobig); + } + if(n < ether->minmtu){ + freeb(bp); + error(Etoosmall); + } + n = etheroq(ether, bp); + poperror(); + runlock(ether); + return n; +} + +static struct { + char* type; + int (*reset)(Ether*); +} cards[MaxEther+1]; + +void +addethercard(char* t, int (*r)(Ether*)) +{ + static int ncard; + + if(ncard == MaxEther) + panic("too many ether cards"); + cards[ncard].type = t; + cards[ncard].reset = r; + ncard++; +} + +int +parseether(uchar *to, char *from) +{ + char nip[4]; + char *p; + int i; + + p = from; + for(i = 0; i < Eaddrlen; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +static void +etherreset(void) +{ + Ether *ether; + int i, n, ctlrno; + char name[KNAMELEN], buf[128]; + + for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){ + if(ether == 0) + ether = malloc(sizeof(Ether)); + memset(ether, 0, sizeof(Ether)); + ether->ctlrno = ctlrno; + ether->mbps = 10; + ether->minmtu = ETHERMINTU; + ether->maxmtu = ETHERMAXTU; + ether->itype = -1; + + if(archether(ctlrno, ether) <= 0) + continue; + + for(n = 0; cards[n].type; n++){ + if(cistrcmp(cards[n].type, ether->type)) + continue; + for(i = 0; i < ether->nopt; i++){ + if(cistrncmp(ether->opt[i], "ea=", 3) == 0){ + if(parseether(ether->ea, ðer->opt[i][3]) == -1) + memset(ether->ea, 0, Eaddrlen); + }else if(cistrcmp(ether->opt[i], "fullduplex") == 0 || + cistrcmp(ether->opt[i], "10BASE-TFD") == 0) + ether->fullduplex = 1; + else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0) + ether->mbps = 100; + } + if(cards[n].reset(ether)) + break; + snprint(name, sizeof(name), "ether%d", ctlrno); + + if(ether->interrupt != nil) + intrenable(ether->irq, ether->interrupt, ether, ether->itype, name); + + i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %lud", + ctlrno, ether->type, ether->mbps, ether->port, ether->irq); + if(ether->mem) + i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem)); + if(ether->size) + i += sprint(buf+i, " size 0x%luX", ether->size); + i += sprint(buf+i, ": %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX", + ether->ea[0], ether->ea[1], ether->ea[2], + ether->ea[3], ether->ea[4], ether->ea[5]); + sprint(buf+i, "\n"); + iprint(buf); + + if(ether->mbps == 100){ + netifinit(ether, name, Ntypes, 256*1024); + if(ether->oq == 0) + ether->oq = qopen(256*1024, Qmsg, 0, 0); + } + else{ + netifinit(ether, name, Ntypes, 64*1024); + if(ether->oq == 0) + ether->oq = qopen(64*1024, Qmsg, 0, 0); + } + if(ether->oq == 0) + panic("etherreset %s", name); + ether->alen = Eaddrlen; + memmove(ether->addr, ether->ea, Eaddrlen); + memset(ether->bcast, 0xFF, Eaddrlen); + + etherxx[ctlrno] = ether; + ether = 0; + break; + } + } + if(ether) + free(ether); +} + +static void +etherpower(int on) +{ + int i; + Ether *ether; + + for(i = 0; i < MaxEther; i++){ + if((ether = etherxx[i]) == nil || ether->power == nil) + continue; + if(on){ + if(canrlock(ether)) + continue; + if(ether->power != nil) + ether->power(ether, on); + wunlock(ether); + }else{ + if(!canrlock(ether)) + continue; + wlock(ether); + if(ether->power != nil) + ether->power(ether, on); + /* Keep locked until power goes back on */ + } + } +} + +#define POLY 0xedb88320 + +/* really slow 32 bit crc for ethers */ +ulong +ethercrc(uchar *p, int len) +{ + int i, j; + ulong crc, b; + + crc = 0xffffffff; + for(i = 0; i < len; i++){ + b = *p++; + for(j = 0; j < 8; j++){ + crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0); + b >>= 1; + } + } + return crc; +} + +Dev etherdevtab = { + 'l', + "ether", + + etherreset, + devinit, + ethershutdown, + etherattach, + etherwalk, + etherstat, + etheropen, + ethercreate, + etherclose, + etherread, + etherbread, + etherwrite, + etherbwrite, + devremove, + etherwstat, + etherpower, +}; diff --git a/os/sa1110/devgpio.c b/os/sa1110/devgpio.c new file mode 100644 index 00000000..f12d5951 --- /dev/null +++ b/os/sa1110/devgpio.c @@ -0,0 +1,165 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +enum{ + Qdir, + Qgpioset, + Qgpioclear, + Qgpioedge, + Qgpioctl, + Qgpiostatus, +}; + +Dirtab gpiodir[]={ + ".", {Qdir,0}, 0, 0555, + "gpioset", {Qgpioset, 0}, 0, 0664, + "gpioclear", {Qgpioclear, 0}, 0, 0664, + "gpioedge", {Qgpioedge, 0}, 0, 0664, + "gpioctl", {Qgpioctl,0}, 0, 0664, + "gpiostatus", {Qgpiostatus,0}, 0, 0444, +}; + +static Chan* +gpioattach(char* spec) +{ + return devattach('G', spec); +} + +static Walkqid* +gpiowalk(Chan* c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, gpiodir, nelem(gpiodir), devgen); +} + +static int +gpiostat(Chan* c, uchar *dp, int n) +{ + return devstat(c, dp, n, gpiodir, nelem(gpiodir), devgen); +} + +static Chan* +gpioopen(Chan* c, int omode) +{ + return devopen(c, omode, gpiodir, nelem(gpiodir), devgen); +} + +static void +gpioclose(Chan*) +{ +} + +static long +gpioread(Chan* c, void *buf, long n, vlong offset) +{ + char str[128]; + GpioReg *g; + + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, gpiodir, nelem(gpiodir), devgen); + + g = GPIOREG; + switch((ulong)c->qid.path){ + case Qgpioset: + case Qgpioclear: + sprint(str, "%8.8lux", g->gplr); + break; + case Qgpioedge: + sprint(str, "%8.8lux", g->gedr); + break; + case Qgpioctl: + /* return 0; */ + case Qgpiostatus: + snprint(str, sizeof(str), "GPDR:%8.8lux\nGRER:%8.8lux\nGFER:%8.8lux\nGAFR:%8.8lux\nGPLR:%8.8lux\n", g->gpdr, g->grer, g->gfer, g->gafr, g->gplr); + break; + default: + error(Ebadarg); + return 0; + } + return readstr(offset, buf, n, str); +} + +static long +gpiowrite(Chan *c, void *a, long n, vlong) +{ + char buf[128], *field[3]; + int pin, set; + ulong *r; + GpioReg *g; + + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = 0; + g = GPIOREG; + switch((ulong)c->qid.path){ + case Qgpioset: + g->gpsr = strtol(buf, 0, 16); + break; + case Qgpioclear: + g->gpcr = strtol(buf, 0, 16); + break; + case Qgpioedge: + g->gedr = strtol(buf, 0, 16); + break; + case Qgpioctl: + if(getfields(buf, field, 3, 1, " \n\t") == 3) { + pin = strtol(field[1], 0, 0); + if(pin < 0 || pin >= 32) + error(Ebadarg); + set = strtol(field[2], 0, 0); + switch(*field[0]) { + case 'd': + r = &g->gpdr; + break; + case 'r': + r = &g->grer; + break; + case 'f': + r = &g->gfer; + break; + case 'a': + r = &g->gafr; + break; + default: + error(Ebadarg); + return 0; + } + if(set) + *r |= 1 << pin; + else + *r &= ~(1 << pin); + } else + error(Ebadarg); + break; + default: + error(Ebadusefd); + return 0; + } + return n; +} + +Dev gpiodevtab = { + 'G', + "gpio", + + devreset, + devinit, + devshutdown, + gpioattach, + gpiowalk, + gpiostat, + gpioopen, + devcreate, + gpioclose, + gpioread, + devbread, + gpiowrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/sa1110/devpcmcia.c b/os/sa1110/devpcmcia.c new file mode 100644 index 00000000..288295b4 --- /dev/null +++ b/os/sa1110/devpcmcia.c @@ -0,0 +1,761 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +int pcmdebug=0; +#define DPRINT if(pcmdebug)iprint +#define DPRINT1 if(pcmdebug > 1)iprint +#define DPRINT2 if(pcmdebug > 2)iprint +#define PCMERR(x) pce(x); + +enum +{ + Qdir, + Qmem, + Qattr, + Qctl, +}; + +#define SLOTNO(c) (((ulong)c->qid.path>>8)&0xff) +#define TYPE(c) ((ulong)c->qid.path&0xff) +#define QID(s,t) (((s)<<8)|(t)) + +/* + * Support for 2 card slots usng StrongArm pcmcia support. + * + */ +enum +{ + /* + * configuration registers - they start at an offset in attribute + * memory found in the CIS. + */ + Rconfig= 0, + Creset= (1<<7), /* reset device */ + Clevel= (1<<6), /* level sensitive interrupt line */ + +}; + + +enum { + Maxctab= 8, /* maximum configuration table entries */ + Maxslot= 2 +}; + +static struct { + Ref; +} pcmcia; + +static PCMslot slot[Maxslot]; +static PCMslot *lastslot ; +static int nslot = Maxslot; + +static void slotdis(PCMslot *); +static void pcmciaintr(Ureg*, void*); +static void pcmciareset(void); +static int pcmio(int, ISAConf*); +static long pcmread(int, int, void*, long, ulong); +static long pcmwrite(int, int, void*, long, ulong); +static void slottiming(int, int, int, int, int); +static void slotmap(int, ulong, ulong, ulong); + +static void pcmciadump(PCMslot*); + +static ulong GPIOrdy[2]; +static ulong GPIOeject[2]; +static ulong GPIOall[2]; + +/* + * get info about card + */ +static void +slotinfo(PCMslot *pp) +{ + ulong gplr; + int was; + + gplr = GPIOREG->gplr; + was = pp->occupied; + pp->occupied = (gplr & GPIOeject[pp->slotno]) ? 0 : 1; + pp->busy = (gplr & GPIOrdy[pp->slotno]) ? 0 : 1; + pp->powered = pcmpowered(pp->slotno); + pp->battery = 0; + pp->wrprot = 0; + if (!was & pp->occupied) + print("PCMCIA card %d inserted\n", pp->slotno); + if (was & !pp->occupied) + print("PCMCIA card %d removed!\n", pp->slotno); +} + +/* + * enable the slot card + */ +static void +slotena(PCMslot *pp) +{ + if(pp->enabled) + return; + DPRINT("Enable slot# %d\n", pp->slotno); + DPRINT("pcmcia ready %8.8lux\n", GPIOREG->gplr & GPIOrdy[pp->slotno]); + + /* get configuration */ + slotinfo(pp); + if(pp->occupied){ + if(pp->cisread == 0){ + pcmcisread(pp); + pp->cisread = 1; + } + pp->enabled = 1; + } else + slotdis(pp); +} + +/* + * disable the slot card + */ +static void +slotdis(PCMslot *pp) +{ + if (pp->enabled) + DPRINT("Disable slot# %d\n", pp->slotno); + pp->enabled = 0; + pp->cisread = 0; +} + +/* + * status change interrupt + */ +static void +pcmciaintr(Ureg*, void*) +{ + uchar was; + PCMslot *pp; + + if(slot == 0) + return; + for(pp = slot; pp < lastslot; pp++){ + was = pp->occupied; + slotinfo(pp); + if(0 && !pp->occupied){ + if(was != pp->occupied){ + slotdis(pp); +// if (pp->special && pp->notify.f) +// (*pp->notify.f)(ur, pp->notify.a, 1); + } + } + } +} + +static void +increfp(PCMslot *pp) +{ + if(up){ + wlock(pp); + if(waserror()){ + wunlock(pp); + nexterror(); + } + } + if(incref(&pcmcia) == 1){ + pcmpower(pp->slotno, 1); + pcmreset(pp->slotno); + delay(500); + } + + if(incref(&pp->ref) == 1) + slotena(pp); + if(up){ + poperror(); + wunlock(pp); + } +} + +static void +decrefp(PCMslot *pp) +{ + if(decref(&pp->ref) == 0) + slotdis(pp); + if(decref(&pcmcia) == 0) + pcmpower(pp->slotno, 0); +} + +/* + * look for a card whose version contains 'idstr' + */ +int +pcmspecial(char *idstr, ISAConf *isa) +{ + PCMslot *pp; + + pcmciareset(); + for(pp = slot; pp < lastslot; pp++){ + if(pp->special) + continue; /* already taken */ + increfp(pp); + + if(pp->occupied) + if(strstr(pp->verstr, idstr)){ + DPRINT("PCMslot #%d: Found %s - ",pp->slotno, idstr); + if(isa == 0 || pcmio(pp->slotno, isa) == 0){ + DPRINT("ok.\n"); + pp->special = 1; + return pp->slotno; + } + print("error with isa io for %s\n", idstr); + } + decrefp(pp); + } + return -1; +} + +void +pcmspecialclose(int slotno) +{ + PCMslot *pp; + int s; + + if(slotno < 0 || slotno >= nslot) + panic("pcmspecialclose"); + pp = slot + slotno; + pp->special = 0; /* Is this OK ? */ + s = splhi(); + GPIOREG->gfer &= ~GPIOrdy[pp->slotno]; /* TO DO: intrdisable */ + GPIOREG->grer &= ~GPIOrdy[pp->slotno]; + splx(s); + decrefp(pp); +} + +static int +pcmgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp) +{ + int slotno; + Qid qid; + long len; + PCMslot *pp; + + if(i == DEVDOTDOT){ + mkqid(&qid, Qdir, 0, QTDIR); + devdir(c, qid, "#y", 0, eve, 0555, dp); + return 1; + } + + if(i>=3*nslot) + return -1; + slotno = i/3; + pp = slot + slotno; + len = 0; + switch(i%3){ + case 0: + qid.path = QID(slotno, Qmem); + sprint(up->genbuf, "pcm%dmem", slotno); + len = pp->memlen; + break; + case 1: + qid.path = QID(slotno, Qattr); + sprint(up->genbuf, "pcm%dattr", slotno); + len = pp->memlen; + break; + case 2: + qid.path = QID(slotno, Qctl); + sprint(up->genbuf, "pcm%dctl", slotno); + break; + } + qid.vers = 0; + qid.type = QTFILE; + devdir(c, qid, up->genbuf, len, eve, 0660, dp); + return 1; +} + +static void +pcmciadump(PCMslot *pp) +{ + USED(pp); +} + +/* + * set up for slot cards + */ +static void +pcmciareset(void) +{ + static int already; + int slotno, v, rdypin; + PCMslot *pp; + + if(already) + return; + already = 1; + DPRINT("pcmcia reset\n"); + + lastslot = slot; + + nslot = 0; + for(slotno = 0; slotno < Maxslot; slotno++){ + rdypin = pcmpin(slotno, PCMready); + if(rdypin < 0) + break; + nslot = slotno+1; + slotmap(slotno, PCMCIAIO(slotno), PCMCIAAttr(slotno), PCMCIAMem(slotno)); + slottiming(slotno, 300, 300, 300, 0); /* set timing to the default, 300 */ + pp = lastslot++; + GPIOeject[slotno] = (1<gafr &= ~GPIOall[slotno]; + slotdis(pp); + intrenable(pcmpin(slotno, PCMeject), pcmciaintr, 0, BusGPIOrising, "pcmcia eject"); + if((v = pcmpin(slotno, PCMstschng)) >= 0) /* status change interrupt */ + intrenable(v, pcmciaintr, 0, BusGPIOrising, "pcmcia status"); + } +} + +static Chan* +pcmciaattach(char *spec) +{ + return devattach('y', spec); +} + +static Walkqid* +pcmciawalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, pcmgen); +} + +static int +pcmciastat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, pcmgen); +} + +static Chan* +pcmciaopen(Chan *c, int omode) +{ + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Eperm); + } else + increfp(slot + SLOTNO(c)); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +pcmciaclose(Chan *c) +{ + if(c->flag & COPEN) + if((c->qid.type & QTDIR) == 0) + decrefp(slot+SLOTNO(c)); +} + +/* a memmove using only bytes */ +static void +memmoveb(uchar *to, uchar *from, int n) +{ + while(n-- > 0) + *to++ = *from++; +} + +static long +pcmread(int slotno, int attr, void *a, long n, ulong offset) +{ + PCMslot *pp; + long i; + uchar *b, *p; + + pp = slot + slotno; + rlock(pp); + if(waserror()){ + runlock(pp); + nexterror(); + } + if(!pp->occupied) + error(Eio); + if(pp->memlen < offset){ + runlock(pp); + poperror(); + return 0; + } + if(pp->memlen < offset + n) + n = pp->memlen - offset; + if (attr){ + b = a; + p = (uchar*)PCMCIAAttr(slotno) + offset; + for(i=0; ioccupied) + error(Eio); + b[0] = *p; + i++; + if(ioccupied) + cp += sprint(cp, "occupied\n"); + if(pp->enabled) + cp += sprint(cp, "enabled\n"); + if(pp->powered) + cp += sprint(cp, "powered\n"); + if(pp->configed) + cp += sprint(cp, "configed\n"); + if(pp->busy) + cp += sprint(cp, "busy\n"); + if(pp->enabled && (i = strlen(pp->verstr)) > 0) + cp += sprint(cp, "verstr %d\n%s\n", i, pp->verstr); + cp += sprint(cp, "battery lvl %d\n", pp->battery); + /* DUMP registers here */ + cp += sprint(cp, "mecr 0x%lux\n", + (SLOTNO(c) ? MEMCFGREG->mecr >> 16 : MEMCFGREG->mecr) & 0x7fff); + *cp = 0; + n = readstr(offset, a, n, buf); + poperror(); + free(buf); + break; + default: + n=0; + break; + } + return n; +} + +static long +pcmwrite(int slotno, int attr, void *a, long n, ulong offset) +{ + PCMslot *pp; + + pp = slot + slotno; + rlock(pp); + if(waserror()){ + runlock(pp); + nexterror(); + } + if(pp->memlen < offset) + error(Eio); + if(pp->memlen < offset + n) + error(Eio); + memmoveb((uchar *)(attr ? PCMCIAAttr(slotno) : PCMCIAMem(slotno)) + offset, a, n); + poperror(); + runlock(pp); + return n; +} + +/* + * the regions are staticly mapped + */ +static void +slotmap(int slotno, ulong regs, ulong attr, ulong mem) +{ + PCMslot *sp; + + if(slotno >= Maxslot) + return; + + sp = &slot[slotno]; + sp->slotno = slotno; + sp->memlen = 64*MB; + sp->verstr[0] = 0; + + sp->mem = (void*)mem; + sp->memmap.ca = 0; + sp->memmap.cea = 64*MB; + sp->memmap.isa = (ulong)mem; + sp->memmap.len = 64*MB; + sp->memmap.attr = 0; + + sp->attr = (void*)attr; + sp->attrmap.ca = 0; + sp->attrmap.cea = MB; + sp->attrmap.isa = (ulong)attr; + sp->attrmap.len = MB; + sp->attrmap.attr = 1; + + sp->regs = (void*)regs; +} + +PCMmap* +pcmmap(int slotno, ulong, int, int attr) +{ + if(slotno >= nslot) + panic("pcmmap"); + if(attr) + return &slot[slotno].attrmap; + else + return &slot[slotno].memmap; +} +void +pcmunmap(int, PCMmap*) +{ +} + +/* + * setup card timings + * times are in ns + * count = ceiling[access-time/(2*3*T)] - 1, where T is a processor cycle + * + */ +static int +ns2count(int ns) +{ + ulong y; + + /* get 100 times cycle time */ + y = 100000000/(m->cpuhz/1000); + + /* get 10 times ns/(cycle*6) */ + y = (1000*ns)/(6*y); + + /* round up */ + y += 9; + y /= 10; + + /* subtract 1 */ + y = y-1; + if(y < 0) + y = 0; + if(y > 0x1F) + y = 0x1F; + + return y & 0x1F; +} +static void +slottiming(int slotno, int tio, int tattr, int tmem, int fast) +{ + ulong x; + MemcfgReg *memconfregs = MEMCFGREG; + + x = ns2count(tio) << 0; + x |= ns2count(tattr) << 5; + x |= ns2count(tmem) << 10; + if(fast) + x |= 1<<15; + if(slotno == 0){ + x |= memconfregs->mecr & 0xffff0000; + } else { + x <<= 16; + x |= memconfregs->mecr & 0xffff; + } + memconfregs->mecr = x; +} + +static long +pcmciawrite(Chan *c, void *a, long n, vlong offset) +{ + ulong p; + PCMslot *pp; + char buf[32]; + + p = TYPE(c); + switch(p){ + case Qctl: + if(n >= sizeof(buf)) + n = sizeof(buf) - 1; + strncpy(buf, a, n); + buf[n] = 0; + pp = slot + SLOTNO(c); + if(!pp->occupied) + error(Eio); + + if(strncmp(buf, "vpp", 3) == 0) + pcmsetvpp(pp->slotno, atoi(buf+3)); + break; + case Qmem: + case Qattr: + pp = slot + SLOTNO(c); + if(pp->occupied == 0 || pp->enabled == 0) + error(Eio); + n = pcmwrite(SLOTNO(c), p == Qattr, a, n, offset); + if(n < 0) + error(Eio); + break; + default: + error(Ebadusefd); + } + return n; +} + +Dev pcmciadevtab = { + 'y', + "pcmcia", + + pcmciareset, + devinit, + devshutdown, + pcmciaattach, + pcmciawalk, + pcmciastat, + pcmciaopen, + devcreate, + pcmciaclose, + pcmciaread, + devbread, + pcmciawrite, + devbwrite, + devremove, + devwstat, +}; + +/* + * configure the PCMslot for IO. We assume very heavily that we can read + * configuration info from the CIS. If not, we won't set up correctly. + */ + +static int +pce(char *s) +{ + USED(s); + DPRINT("pcmio failed: %s\n", s); + return -1; +} + +static int +pcmio(int slotno, ISAConf *isa) +{ + uchar *p; + PCMslot *pp; + int i, index; + char *cp; + + if(slotno >= nslot) + return PCMERR("bad slot#"); + pp = slot + slotno; + + if(!pp->occupied) + return PCMERR("empty slot"); + + index = 0; + if(pp->def) + index = pp->def->index; + for(i = 0; i < isa->nopt; i++){ + if(strncmp(isa->opt[i], "index=", 6)) + continue; + index = strtol(&isa->opt[i][6], &cp, 0); + if(cp == &isa->opt[i][6] || index < 0 || index >= pp->nctab) + return PCMERR("bad index"); + break; + } + /* only touch Rconfig if it is present */ + if(pp->cfg[0].cpresent & (1<cfg[0].caddr + Rconfig); + *p = index; + delay(5); + } + isa->port = (ulong)pp->regs; + isa->mem = (ulong)pp->mem; + isa->irq = pcmpin(pp->slotno, PCMready); + isa->itype = BusGPIOfalling; + return 0; +} + +int +inb(ulong p) +{ + return *(uchar*)p; +} + +int +ins(ulong p) +{ + return *(ushort*)p; +} + +ulong +inl(ulong p) +{ + return *(ulong*)p; +} + +void +outb(ulong p, int v) +{ + *(uchar*)p = v; +} + +void +outs(ulong p, int v) +{ + *(ushort*)p = v; +} + +void +outl(ulong p, ulong v) +{ + *(ulong*)p = v; +} + +void +inss(ulong p, void* buf, int ns) +{ + ushort *addr; + + addr = (ushort*)buf; + for(;ns > 0; ns--) + *addr++ = *(ushort*)p; +} + +void +outss(ulong p, void* buf, int ns) +{ + ushort *addr; + + addr = (ushort*)buf; + for(;ns > 0; ns--) + *(ushort*)p = *addr++; +} + +void +insb(ulong p, void* buf, int ns) +{ + uchar *addr; + + addr = (uchar*)buf; + for(;ns > 0; ns--) + *addr++ = *(uchar*)p; +} + +void +outsb(ulong p, void* buf, int ns) +{ + uchar *addr; + + addr = (uchar*)buf; + for(;ns > 0; ns--) + *(uchar*)p = *addr++; +} diff --git a/os/sa1110/devpower.c b/os/sa1110/devpower.c new file mode 100644 index 00000000..c7cd5095 --- /dev/null +++ b/os/sa1110/devpower.c @@ -0,0 +1,377 @@ +/* + * power management + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +typedef struct Power Power; +typedef struct Puser Puser; + +enum{ + Qdir, + Qctl, + Qdata +}; + +static +Dirtab powertab[]={ + ".", {Qdir, 0, QTDIR}, 0, 0500, + "powerctl", {Qctl, 0}, 0, 0600, + "powerdata", {Qdata, 0}, 0, 0666, +}; + +struct Puser { + Ref; + ulong alarm; /* real time clock alarm time, if non-zero */ + QLock rl; /* mutual exclusion to protect r */ + Rendez r; /* wait for event of interest */ + int state; /* shutdown state of this process */ + Puser* next; +}; + +enum{ + Pwridle, + Pwroff, + Pwrack +}; + +static struct { + QLock; + Puser *list; + Lock l; /* protect shutdown, nwaiting */ + int shutdown; /* non-zero if currently shutting down */ + int nwaiting; /* waiting for this many processes */ + Rendez ackr; /* wait here for all acks */ +} pwrusers; + + +static Chan* +powerattach(char* spec) +{ + return devattach(L'↓', spec); +} + +static int +powerwalk(Chan* c, char* name) +{ + return devwalk(c, name, powertab, nelem(powertab), devgen); +} + +static void +powerstat(Chan* c, char* db) +{ + devstat(c, db, powertab, nelem(powertab), devgen); +} + +static Chan* +poweropen(Chan* c, int omode) +{ + Puser *p; + + if(c->qid.type & QTDIR) + return devopen(c, omode, powertab, nelem(powertab), devgen); + switch(c->qid.path){ + case Qdata: + p = mallocz(sizeof(Puser), 1); + if(p == nil) + error(Enovmem); + p->state = Pwridle; + p->ref = 1; + if(waserror()){ + free(p); + nexterror(); + } + c = devopen(c, omode, powertab, nelem(powertab), devgen); + c->aux = p; + qlock(&pwrusers); + p->next = pwrusers.list; + pwrusers.list = p; /* note: must place on front of list for correct shutdown ordering */ + qunlock(&pwrusers); + poperror(); + break; + case Qctl: + c = devopen(c, omode, powertab, nelem(powertab), devgen); + break; + } + return c; +} + +static Chan * +powerclone(Chan *c, Chan *nc) +{ + Puser *p; + + nc = devclone(c, nc); + if((p = nc->aux) != nil) + incref(p); + return nc; +} + +static void +powerclose(Chan* c) +{ + Puser *p, **l; + + if(c->qid.type & QTDIR || (c->flag & COPEN) == 0) + return; + p = c->aux; + if(p != nil && decref(p) == 0){ + /* TO DO: cancel alarm */ + qlock(&pwrusers); + for(l = &pwrusers.list; *l != nil; l = &(*l)->next) + if(*l == p){ + *l = p->next; + break; + } + qunlock(&pwrusers); + free(p); + } +} + +static int +isshutdown(void *a) +{ + return ((Puser*)a)->state == Pwroff; +} + +static long +powerread(Chan* c, void* a, long n, vlong offset) +{ + Puser *p; + char *msg; + + switch(c->qid.path & ~CHDIR){ + case Qdir: + return devdirread(c, a, n, powertab, nelem(powertab), devgen); + case Qdata: + p = c->aux; + for(;;){ + if(!canqlock(&p->rl)) + error(Einuse); /* only one reader at a time */ + if(waserror()){ + qunlock(&p->rl); + nexterror(); + } + sleep(&p->r, isshutdown, p); + poperror(); + qunlock(&p->rl); + msg = nil; + lock(p); + if(p->state == Pwroff){ + msg = "power off"; + p->state = Pwrack; + } + unlock(p); + if(msg != nil) + return readstr(offset, a, n, msg); + } + break; + case Qctl: + default: + n=0; + break; + } + return n; +} + +static int +alldown(void*) +{ + return pwrusers.nwaiting == 0; +} + +static long +powerwrite(Chan* c, void *a, long n, vlong) +{ + Cmdbuf *cmd; + Puser *p; + + if(c->qid.type & QTDIR) + error(Ebadusefd); + cmd = parsecmd(a, n); + if(waserror()){ + free(cmd); + nexterror(); + } + switch(c->qid.path & ~CHDIR){ + case Qdata: + p = c->aux; + if(cmd->nf < 2) + error(Ebadarg); + if(strcmp(cmd->f[0], "ack") == 0){ + if(strcmp(cmd->f[1], "power") == 0){ + lock(p); + if(p->state == Pwrack){ + lock(&pwrusers.l); + if(pwrusers.shutdown && pwrusers.nwaiting > 0) + pwrusers.nwaiting--; + unlock(&pwrusers.l); + wakeup(&pwrusers.ackr); + p->state = Pwridle; + } + unlock(p); + }else + error(Ebadarg); + }else if(strcmp(cmd->f[0], "alarm") == 0){ + /* set alarm */ + }else + error(Ebadarg); + break; + case Qctl: + if(cmd->nf < 1) + error(Ebadarg); + if(strcmp(cmd->f[0], "suspend") == 0){ + /* start the suspend action */ + qlock(&pwrusers); + //powersuspend(0); /* calls poweringdown, then archsuspend() */ + qunlock(&pwrusers); + }else if(strcmp(cmd->f[0], "shutdown") == 0){ + /* go to it */ + qlock(&pwrusers); + if(waserror()){ + lock(&pwrusers.l); + pwrusers.shutdown = 0; /* hard luck for those already notified */ + unlock(&pwrusers.l); + qunlock(&pwrusers); + nexterror(); + } + lock(&pwrusers.l); + pwrusers.shutdown = 1; + pwrusers.nwaiting = 0; + unlock(&pwrusers.l); + for(p = pwrusers.list; p != nil; p = p->next){ + lock(p); + if(p->state == Pwridle){ + p->state = Pwroff; + lock(&pwrusers.l); + pwrusers.nwaiting++; + unlock(&pwrusers.l); + } + unlock(p); + wakeup(&p->r); + /* putting the tsleep here does each in turn; move out of loop to multicast */ + tsleep(&pwrusers.ackr, alldown, nil, 1000); + } + poperror(); + qunlock(&pwrusers); + //powersuspend(1); + }else + error(Ebadarg); + free(cmd); + break; + default: + error(Ebadusefd); + } + poperror(); + return n; +} + +/* + * device-level power management: suspend/resume/shutdown + */ + +struct Power { + void (*f)(int); + Power* prev; + Power* next; +}; + +static struct { + Lock; + Power list; +} power; + +void +powerenablereset(void) +{ + power.list.next = power.list.prev = &power.list; + power.list.f = (void*)-1; /* something not nil */ +} + +void +powerenable(void (*f)(int)) +{ + Power *p, *l; + + p = malloc(sizeof(*p)); + p->f = f; + p->prev = nil; + p->next = nil; + ilock(&power); + for(l = power.list.next; l != &power.list; l = l->next) + if(l->f == f){ + iunlock(&power); + free(p); + return; + } + l = &power.list; + p->prev = l->prev; + l->prev = p; + p->next = l; + p->prev->next = p; + iunlock(&power); +} + +void +powerdisable(void (*f)(int)) +{ + Power *l; + + ilock(&power); + for(l = power.list.next; l != &power.list; l = l->next) + if(l->f == f){ + l->prev->next = l->next; + l->next->prev = l->prev; + free(l); + break; + } + iunlock(&power); +} + +/* + * interrupts are assumed off so there's no need to lock + */ +void +poweringup(void) +{ + Power *l; + + for(l = power.list.next; l != &power.list; l = l->next) + (*l->f)(1); +} + +void +poweringdown(void) +{ + Power *l; + + for(l = power.list.prev; l != &power.list; l = l->prev) + (*l->f)(0); +} + +Dev powerdevtab = { + L'↓', + "power", + + devreset, + devinit, + powerattach, + devdetach, + powerclone, + powerwalk, + powerstat, + poweropen, + devcreate, + powerclose, + powerread, + devbread, + powerwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/sa1110/devrtc.c b/os/sa1110/devrtc.c new file mode 100644 index 00000000..54680fc2 --- /dev/null +++ b/os/sa1110/devrtc.c @@ -0,0 +1,169 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "io.h" + +/* + * SA11x0 real time clock + * TO DO: alarms, wakeup, allow trim setting(?) + */ + +enum{ + Qdir, + Qrtc, + Qrtctrim, +}; + +static Dirtab rtcdir[]={ + ".", {Qdir,0,QTDIR}, 0, 0555, + "rtc", {Qrtc}, NUMSIZE, 0664, + "rtctrim", {Qrtctrim}, 0, 0664, +}; +#define NRTC (sizeof(rtcdir)/sizeof(rtcdir[0])) + +extern ulong boottime; + +enum { + RTSR_al= 1<<0, /* RTC alarm detected */ + RTSR_hz= 1<<1, /* 1-Hz rising-edge detected */ + RTSR_ale= 1<<2, /* RTC alarm interrupt enabled */ + RTSR_hze= 1<<3, /* 1-Hz interrupt enable */ +}; + +static void +rtcreset(void) +{ + RtcReg *r; + + r = RTCREG; + if((r->rttr & 0xFFFF) == 0){ /* reset state */ + r->rttr = 32768-1; + r->rcnr = boottime; /* typically zero */ + } + r->rtar = ~0; + r->rtsr = RTSR_al | RTSR_hz; +} + +static Chan* +rtcattach(char *spec) +{ + return devattach('r', spec); +} + +static Walkqid* +rtcwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, rtcdir, NRTC, devgen); +} + +static int +rtcstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, rtcdir, NRTC, devgen); +} + +static Chan* +rtcopen(Chan *c, int omode) +{ + return devopen(c, omode, rtcdir, NRTC, devgen); +} + +static void +rtcclose(Chan*) +{ +} + +static long +rtcread(Chan *c, void *buf, long n, vlong off) +{ + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, rtcdir, NRTC, devgen); + + switch((ulong)c->qid.path){ + case Qrtc: + return readnum(off, buf, n, RTCREG->rcnr, NUMSIZE); + case Qrtctrim: + return readnum(off, buf, n, RTCREG->rttr, NUMSIZE); + } + error(Egreg); + return 0; /* not reached */ +} + +static long +rtcwrite(Chan *c, void *buf, long n, vlong off) +{ + ulong offset = off; + ulong secs; + char *cp, sbuf[32]; + + switch((ulong)c->qid.path){ + case Qrtc: + /* + * write the time + */ + if(offset != 0 || n >= sizeof(sbuf)-1) + error(Ebadarg); + memmove(sbuf, buf, n); + sbuf[n] = '\0'; + cp = sbuf; + while(*cp){ + if(*cp>='0' && *cp<='9') + break; + cp++; + } + secs = strtoul(cp, 0, 0); + RTCREG->rcnr = secs; + return n; + + case Qrtctrim: + if(offset != 0 || n >= sizeof(sbuf)-1) + error(Ebadarg); + memmove(sbuf, buf, n); + sbuf[n] = '\0'; + RTCREG->rttr = strtoul(sbuf, 0, 0); + return n; + } + error(Egreg); + return 0; /* not reached */ +} + +static void +rtcpower(int on) +{ + if(on) + boottime = RTCREG->rcnr - TK2SEC(MACHP(0)->ticks); + else + RTCREG->rcnr = seconds(); +} + +long +rtctime(void) +{ + return RTCREG->rcnr; +} + +Dev rtcdevtab = { + 'r', + "rtc", + + rtcreset, + devinit, + devshutdown, + rtcattach, + rtcwalk, + rtcstat, + rtcopen, + devcreate, + rtcclose, + rtcread, + devbread, + rtcwrite, + devbwrite, + devremove, + devwstat, + rtcpower, +}; diff --git a/os/sa1110/devuart.c b/os/sa1110/devuart.c new file mode 100644 index 00000000..65eb17a3 --- /dev/null +++ b/os/sa1110/devuart.c @@ -0,0 +1,781 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +/* + * currently no DMA or flow control (hardware or software) + */ + +enum +{ + Stagesize= 1024, + Dmabufsize=Stagesize/2, + Nuart=7, /* max per machine */ + + CTLS= 023, + CTLQ= 021, +}; + +typedef struct Uart Uart; +struct Uart +{ + QLock; + + int opens; + + int enabled; + + int frame; /* framing errors */ + int overrun; /* rcvr overruns */ + int soverrun; /* software overruns */ + int perror; /* parity error */ + int bps; /* baud rate */ + uchar bits; + char parity; + + int inters; /* total interrupt count */ + int rinters; /* interrupts due to read */ + int winters; /* interrupts due to write */ + + int rcount; /* total read count */ + int wcount; /* total output count */ + + int xonoff; /* software flow control on */ + int blocked; /* output blocked */ + + /* buffers */ + int (*putc)(Queue*, int); + Queue *iq; + Queue *oq; + + int port; + UartReg *reg; + + /* staging areas to avoid some of the per character costs */ + uchar *ip; + uchar *ie; + uchar *op; + uchar *oe; + + /* put large buffers last to aid register-offset optimizations: */ + char name[KNAMELEN]; + uchar istage[Stagesize]; + uchar ostage[Stagesize]; +}; + +enum { + UTCR0_PE= 0x01, + UTCR0_OES= 0x02, + UTCR0_SBS= 0x04, + UTCR0_DSS= 0x08, + UTCR0_SCE= 0x10, + UTCR0_RCE= 0x20, + UTCR0_TCE= 0x40, + + UTCR3_RXE= 0x01, + UTCR3_TXE= 0x02, + UTCR3_BRK= 0x04, + UTCR3_RIM= 0x08, + UTCR3_TIM= 0x10, + UTCR3_LBM= 0x20, + + UTSR0_TFS= 0x01, + UTSR0_RFS= 0x02, + UTSR0_RID= 0x04, + UTSR0_RBB= 0x08, + UTSR0_REB= 0x10, + UTSR0_EIF= 0x20, + + UTSR1_TBY= 0x01, + UTSR1_RNE= 0x02, + UTSR1_TNF= 0x04, + UTSR1_PRE= 0x08, + UTSR1_FRE= 0x10, + UTSR1_ROR= 0x20, +}; + +static Uart *uart[Nuart]; +static int nuart; +static int uartspcl; +int redirectconsole; + +static void +uartset(Uart *p) +{ + UartReg *reg = p->reg; + ulong ocr3; + ulong brdiv; + int n; + + brdiv = CLOCKFREQ/16/p->bps - 1; + ocr3 = reg->utcr3; + reg->utcr3 = ocr3&~(UTCR3_RXE|UTCR3_TXE); + reg->utcr1 = brdiv >> 8; + reg->utcr2 = brdiv & 0xff; + /* set PE and OES appropriately for o/e/n: */ + reg->utcr0 = ((p->parity&3)^UTCR0_OES)|(p->bits&UTCR0_DSS); + reg->utcr3 = ocr3; + + /* set buffer length according to speed, to allow + * at most a 200ms delay before dumping the staging buffer + * into the input queue + */ + n = p->bps/(10*1000/200); + p->ie = &p->istage[n < Stagesize ? n : Stagesize]; +} + +/* + * send break + */ +static void +uartbreak(Uart *p, int ms) +{ + UartReg *reg = p->reg; + if(ms == 0) + ms = 200; + reg->utcr3 |= UTCR3_BRK; + tsleep(&up->sleep, return0, 0, ms); + reg->utcr3 &= ~UTCR3_BRK; +} + +/* + * turn on a port + */ +static void +uartenable(Uart *p) +{ + UartReg *reg = p->reg; + + if(p->enabled) + return; + + archuartpower(p->port, 1); + uartset(p); + reg->utsr0 = 0xff; // clear all sticky status bits + // enable receive, transmit, and receive interrupt: + reg->utcr3 = UTCR3_RXE|UTCR3_TXE|UTCR3_RIM; + p->blocked = 0; + p->xonoff = 0; + p->enabled = 1; +} + +/* + * turn off a port + */ +static void +uartdisable(Uart *p) +{ + p->reg->utcr3 = 0; // disable TX, RX, and ints + p->blocked = 0; + p->xonoff = 0; + p->enabled = 0; + archuartpower(p->port, 0); +} + +/* + * put some bytes into the local queue to avoid calling + * qconsume for every character + */ +static int +stageoutput(Uart *p) +{ + int n; + Queue *q = p->oq; + + if(q == nil) + return 0; + n = qconsume(q, p->ostage, Stagesize); + if(n <= 0) + return 0; + p->op = p->ostage; + p->oe = p->ostage + n; + return n; +} + +static void +uartxmit(Uart *p) +{ + UartReg *reg = p->reg; + ulong e = 0; + + if(!p->blocked) { + while(p->op < p->oe || stageoutput(p)) { + if(reg->utsr1 & UTSR1_TNF) { + reg->utdr = *(p->op++); + p->wcount++; + } else { + e = UTCR3_TIM; + break; + } + } + } + reg->utcr3 = (reg->utcr3&~UTCR3_TIM)|e; +} + +static void +uartrecvq(Uart *p) +{ + uchar *cp = p->istage; + int n = p->ip - cp; + + if(n == 0) + return; + if(p->putc) + while(n-- > 0) + p->putc(p->iq, *cp++); + else if(p->iq) + if(qproduce(p->iq, p->istage, n) < n){ + /* if xonoff, should send XOFF when qwindow(p->iq) < threshold */ + p->soverrun++; + //print("qproduce flow control"); + } + p->ip = p->istage; +} + +static void +uartrecv(Uart *p) +{ + UartReg *reg = p->reg; + ulong n; + while(reg->utsr1 & UTSR1_RNE) { + int c; + n = reg->utsr1; + c = reg->utdr; + if(n & (UTSR1_PRE|UTSR1_FRE|UTSR1_ROR)) { + if(n & UTSR1_PRE) + p->perror++; + if(n & UTSR1_FRE) + p->frame++; + if(n & UTSR1_ROR) + p->overrun++; + continue; + } + if(p->xonoff){ + if(c == CTLS){ + p->blocked = 1; + }else if (c == CTLQ){ + p->blocked = 0; + } + } + *p->ip++ = c; + if(p->ip >= p->ie) + uartrecvq(p); + p->rcount++; + } + if(reg->utsr0 & UTSR0_RID) { + reg->utsr0 = UTSR0_RID; + uartrecvq(p); + } +} + +static void +uartclock(void) +{ + Uart *p; + int i; + + for(i=0; ireg; + ulong m = reg->utsr0; + int dokick; + + dokick = p->blocked; + p->inters++; + if(m & (UTSR0_RFS|UTSR0_RID|UTSR0_EIF)) { + p->rinters++; + uartrecv(p); + } + if(p->blocked) + dokick = 0; + if((m & UTSR0_TFS) && (reg->utcr3&UTCR3_TIM || dokick)) { + p->winters++; + uartxmit(p); + } + + if(m & (UTSR0_RBB|UTSR0_REB)) { + //print(""); + /* reg->utsr0 = UTSR0_RBB|UTSR0_REB; */ + reg->utsr0 = m & (UTSR0_RBB|UTSR0_REB); + /* what to do? if anything */ + } +} + +static void +uartsetup(ulong port, char *name) +{ + Uart *p; + + if(nuart >= Nuart) + return; + + p = xalloc(sizeof(Uart)); + uart[nuart++] = p; + strcpy(p->name, name); + + p->port = port; + p->reg = UARTREG(port); + p->bps = 9600; + p->bits = 8; + p->parity = 'n'; + + p->iq = qopen(4*1024, 0, 0 , p); + p->oq = qopen(4*1024, 0, uartkick, p); + + p->ip = p->istage; + p->ie = &p->istage[Stagesize]; + p->op = p->ostage; + p->oe = p->ostage; + if(port == 1) + GPCLKREG->gpclkr0 |= 1; /* SUS=1 for uart on serial 1 */ + + intrenable(UARTbit(port), uartintr, p, BusCPU, name); +} + +static void +uartinstall(void) +{ + static int already; + + if(already) + return; + already = 1; + + uartsetup(3, "eia0"); + uartsetup(1, "eia1"); + addclock0link(uartclock, 22); +} + +/* + * called by main() to configure a duart port as a console or a mouse + */ +void +uartspecial(int port, int bps, char parity, Queue **in, Queue **out, int (*putc)(Queue*, int)) +{ + Uart *p; + + uartinstall(); + if(port >= nuart) + return; + p = uart[port]; + if(bps) + p->bps = bps; + if(parity) + p->parity = parity; + uartenable(p); + p->putc = putc; + if(in) + *in = p->iq; + if(out) + *out = p->oq; + p->opens++; + uartspcl = 1; +} + +Dirtab *uartdir; +int ndir; + +static void +setlength(int i) +{ + Uart *p; + + if(i > 0){ + p = uart[i]; + if(p && p->opens && p->iq) + uartdir[1+3*i].length = qlen(p->iq); + } else for(i = 0; i < nuart; i++){ + p = uart[i]; + if(p && p->opens && p->iq) + uartdir[1+3*i].length = qlen(p->iq); + } +} + +/* + * all uarts must be uartsetup() by this point or inside of uartinstall() + */ +static void +uartreset(void) +{ + int i; + Dirtab *dp; + + uartinstall(); + + ndir = 1+3*nuart; + uartdir = xalloc(ndir * sizeof(Dirtab)); + dp = uartdir; + strcpy(dp->name, "."); + mkqid(&dp->qid, 0, 0, QTDIR); + dp->length = 0; + dp->perm = DMDIR|0555; + dp++; + for(i = 0; i < nuart; i++){ + /* 3 directory entries per port */ + strcpy(dp->name, uart[i]->name); + dp->qid.path = NETQID(i, Ndataqid); + dp->perm = 0660; + dp++; + sprint(dp->name, "%sctl", uart[i]->name); + dp->qid.path = NETQID(i, Nctlqid); + dp->perm = 0660; + dp++; + sprint(dp->name, "%sstatus", uart[i]->name); + dp->qid.path = NETQID(i, Nstatqid); + dp->perm = 0444; + dp++; + } +} + +static Chan* +uartattach(char *spec) +{ + return devattach('t', spec); +} + +static Walkqid* +uartwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, uartdir, ndir, devgen); +} + +static int +uartstat(Chan *c, uchar *dp, int n) +{ + if(NETTYPE(c->qid.path) == Ndataqid) + setlength(NETID(c->qid.path)); + return devstat(c, dp, n, uartdir, ndir, devgen); +} + +static Chan* +uartopen(Chan *c, int omode) +{ + Uart *p; + + c = devopen(c, omode, uartdir, ndir, devgen); + + switch(NETTYPE(c->qid.path)){ + case Nctlqid: + case Ndataqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(p->opens++ == 0){ + uartenable(p); + qreopen(p->iq); + qreopen(p->oq); + } + qunlock(p); + break; + } + + return c; +} + +static void +uartclose(Chan *c) +{ + Uart *p; + + if(c->qid.type & QTDIR) + return; + if((c->flag & COPEN) == 0) + return; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + case Nctlqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(--(p->opens) == 0){ + uartdisable(p); + qclose(p->iq); + qclose(p->oq); + p->ip = p->istage; + } + qunlock(p); + break; + } +} + +static long +uartstatus(Chan *c, Uart *p, void *buf, long n, long offset) +{ + char str[256]; + USED(c); + + str[0] = 0; + snprint(str, sizeof(str), + "b%d l%d p%c s%d x%d\n" + "opens %d ferr %d oerr %d perr %d baud %d parity %c" + " intr %d rintr %d wintr %d" + " rcount %d wcount %d", + p->bps, p->bits, p->parity, (p->reg->utcr0&UTCR0_SBS)?2:1, p->xonoff, + p->opens, p->frame, p->overrun+p->soverrun, p->perror, p->bps, p->parity, + p->inters, p->rinters, p->winters, + p->rcount, p->wcount); + + strcat(str, "\n"); + return readstr(offset, buf, n, str); +} + +static long +uartread(Chan *c, void *buf, long n, vlong offset) +{ + Uart *p; + + if(c->qid.type & QTDIR){ + setlength(-1); + return devdirread(c, buf, n, uartdir, ndir, devgen); + } + + p = uart[NETID(c->qid.path)]; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qread(p->iq, buf, n); + case Nctlqid: + return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE); + case Nstatqid: + return uartstatus(c, p, buf, n, offset); + } + + return 0; +} + +static void +uartctl(Uart *p, char *cmd) +{ + int i, n; + + /* let output drain for a while (up to 4 secs) */ + for(i = 0; i < 200 && (qlen(p->oq) || p->reg->utsr1 & UTSR1_TBY); i++) + tsleep(&up->sleep, return0, 0, 20); + + if(strncmp(cmd, "break", 5) == 0){ + uartbreak(p, 0); + return; + } + + n = atoi(cmd+1); + switch(*cmd){ + case 'B': + case 'b': + if(n <= 0) + error(Ebadarg); + p->bps = n; + uartset(p); + break; + case 'f': + case 'F': + qflush(p->oq); + break; + case 'H': + case 'h': + qhangup(p->iq, 0); + qhangup(p->oq, 0); + break; + case 'L': + case 'l': + if(n < 7 || n > 8) + error(Ebadarg); + p->bits = n; + uartset(p); + break; + case 'n': + case 'N': + qnoblock(p->oq, n); + break; + case 'P': + case 'p': + p->parity = *(cmd+1); + uartset(p); + break; + case 'K': + case 'k': + uartbreak(p, n); + break; + case 'Q': + case 'q': + qsetlimit(p->iq, n); + qsetlimit(p->oq, n); + break; + case 'X': + case 'x': + p->xonoff = n; + break; + } +} + +static long +uartwrite(Chan *c, void *buf, long n, vlong offset) +{ + Uart *p; + char cmd[32]; + + USED(offset); + + if(c->qid.type & QTDIR) + error(Eperm); + + p = uart[NETID(c->qid.path)]; + + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qwrite(p->oq, buf, n); + case Nctlqid: + + if(n >= sizeof(cmd)) + n = sizeof(cmd)-1; + memmove(cmd, buf, n); + cmd[n] = 0; + uartctl(p, cmd); + return n; + } +} + +static int +uartwstat(Chan *c, uchar *dp, int n) +{ + Dir d; + Dirtab *dt; + + if(!iseve()) + error(Eperm); + if(c->qid.type & QTDIR) + error(Eperm); + if(NETTYPE(c->qid.path) == Nstatqid) + error(Eperm); + + dt = &uartdir[1+3 * NETID(c->qid.path)]; + n = convM2D(dp, n, &d, nil); + if(d.mode != ~0UL){ + d.mode &= 0666; + dt[0].perm = dt[1].perm = d.mode; + } + return n; +} + +void +uartpower(int on) +{ + Uart *p; + int i; + + for(i=0; iopens){ + if(on && !p->enabled){ + p->enabled = 0; + uartenable(p); + uartkick(p); + }else{ + if(p->port != 3) /* leave the console */ + uartdisable(p); + p->enabled = 0; + } + } + } +} + +Dev uartdevtab = { + 't', + "uart", + + uartreset, + devinit, + devshutdown, + uartattach, + uartwalk, + uartstat, + uartopen, + devcreate, + uartclose, + uartread, + devbread, + uartwrite, + devbwrite, + devremove, + uartwstat, + uartpower, +}; + +/* + * for use by iprint + */ +void +uartputc(int c) +{ + UartReg *r; + + if(!uartspcl && !redirectconsole) + return; + if(c == 0) + return; + r = UARTREG(3); + while((r->utsr1 & UTSR1_TNF) == 0) + {} + r->utdr = c; + if(c == '\n') + while(r->utsr1 & UTSR1_TBY) /* flush xmit fifo */ + {} +} + +void +uartputs(char *data, int len) +{ + int s; + + if(!uartspcl && !redirectconsole) + return; + clockpoll(); + s = splfhi(); + while(--len >= 0){ + if(*data == '\n') + uartputc('\r'); + uartputc(*data++); + } + splx(s); +} + +/* + * for use by debugger + */ +int +uartgetc(void) +{ + UartReg *r; + + if(!uartspcl) + return -1; + clockcheck(); + r = UARTREG(3); + while(!(r->utsr1 & UTSR1_RNE)) + clockcheck(); + return r->utdr; +} diff --git a/os/sa1110/dma.c b/os/sa1110/dma.c new file mode 100644 index 00000000..c52e28bb --- /dev/null +++ b/os/sa1110/dma.c @@ -0,0 +1,233 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +enum { + /* DMA CSR bits */ + CSRrun= 1 << 0, + CSRie= 1 << 1, + CSRerror= 1 << 2, + CSRdonea= 1 << 3, + CSRstrta= 1 << 4, + CSRdoneb= 1 << 5, + CSRstrtb= 1 << 6, + CSRbiu= 1 << 7, + + Ndma= 6, /* number of dma channels */ +}; + +/* DDAR configuration: DA 31:8, DS 3:0, data width, burst size */ +#define DMACFG(da, ds, dw, bs) (((da)<<8)|((ds)<<4)|((dw)<<3)|((bs)<<2)) + +static ulong dmaconfig[16] = { +[DmaUDC] DMACFG(0x80000A, 0, 0, 1), +[DmaUART0] DMACFG(0x804005, 4, 0, 0), +[DmaHSSP] DMACFG(0x81001B, 6, 0, 1), +[DmaUART1] DMACFG(0x80C005, 6, 0, 0), +[DmaUART2] DMACFG(0x814005, 8, 0, 0), +[DmaMCPaudio] DMACFG(0x818002, 10, 1, 1), +[DmaMCPtelecom] DMACFG(0x818003, 12, 1, 1), +[DmaSSP] DMACFG(0x81C01B, 14, 1, 0), /* see SSP description not DMA section for correct burst size */ +}; + +struct Dma { + int chan; + DmaReg* reg; + void (*interrupt)(void*, ulong); + void* arg; + Rendez r; + int intrset; +}; + +static struct { + Lock; + int avail; + Dma dma[Ndma]; +} dmachans; + +static void dmaintr(Ureg*, void*); + +void +dmareset(void) +{ + int i; + Dma *d; + + for(i=0; ichan = i; + d->reg = DMAREG(i); + d->reg->dcsr_c = 0xFF; + } + /* this is the place to mask off bits in avail corresponding to broken channels in old revisions */ +} + +/* + * allocate a DMA channel, reset it, and configure it for the given device + */ +Dma* +dmasetup(int device, int direction, int bigend, void (*interrupt)(void*, ulong), void *arg) +{ + Dma *d; + DmaReg *dr; + ulong cfg; + int i; + char name[KNAMELEN]; + + cfg = dmaconfig[device]; + if(cfg == 0){ + print("dmasetup: no device %d\n", device); + return nil; + } + + ilock(&dmachans); + for(i=0; (dmachans.avail & (1<= nelem(dmachans.dma)){ + iunlock(&dmachans); + return nil; + } + dmachans.avail &= ~(1<interrupt = interrupt; + d->arg = arg; + dr = d->reg; + dr->dcsr_c = CSRrun | CSRie | CSRerror | CSRdonea | CSRstrta | CSRdoneb | CSRstrtb; + dr->ddar = cfg | (direction<<4) | (bigend<<1); + if(d->intrset == 0){ + d->intrset = 1; + snprint(name, sizeof(name), "dma%d", i); + intrenable(DMAbit(i), dmaintr, d, BusCPU, name); + } + return d; +} + +void +dmafree(Dma *dma) +{ + dma->reg->dcsr_c = CSRrun | CSRie; + ilock(&dmachans); + dmachans.avail |= 1<chan; + dma->interrupt = nil; + iunlock(&dmachans); +} + +/* + * start dma on the given channel on one or two buffers, + * each of which must adhere to DMA controller restrictions. + * (eg, on some versions of the StrongArm it musn't span 256-byte boundaries). + * virtual buffer addresses are assumed to refer to contiguous physical addresses. + */ +int +dmastart(Dma *dma, void *buf, int nbytes) +{ + ulong v, csr; + DmaReg *dr; + int b; + + dr = dma->reg; + v = dr->dcsr; + if((v & (CSRstrta|CSRstrtb|CSRrun)) == (CSRstrta|CSRstrtb|CSRrun)) + return 0; /* fully occupied */ + + dcflush(buf, nbytes); + + csr = CSRrun | CSRie; + + /* start first xfer with buffer B or A? */ + b = (v & CSRbiu) != 0 && (v & CSRstrtb) == 0 || (v & CSRstrta) != 0; + if(b) + csr |= CSRstrtb; + else + csr |= CSRstrta; + + if(v & csr & (CSRstrtb|CSRstrta)) + panic("dmasetup csr=%2.2lux %2.2lux", v, csr); + + /* set first src/dst and size */ + dr->buf[b].start = (ulong)buf; + dr->buf[b].count = nbytes; + dr->dcsr_s = csr; + return 1; +} + +/* + * stop dma on a channel + */ +void +dmastop(Dma *dma) +{ + // print("dmastop (was %ux)\n", dma->reg->dcsr); + + dma->reg->dcsr_c = CSRrun | + CSRie | + CSRerror | + CSRdonea | + CSRstrta | + CSRdoneb | + CSRstrtb; +} + +/* + * return nonzero if there was a memory error during DMA, + * and clear the error state + */ +int +dmaerror(Dma *dma) +{ + DmaReg *dr; + ulong e; + + dr = dma->reg; + e = dr->dcsr & CSRerror; + dr->dcsr_c = e; + return e; +} + +/* + * return nonzero if the DMA channel is not busy + */ +int +dmaidle(Dma *d) +{ + return (d->reg->dcsr & (CSRstrta|CSRstrtb)) == 0; +} + +static int +dmaidlep(void *a) +{ + return dmaidle((Dma*)a); +} + +void +dmawait(Dma *d) +{ + while(!dmaidle(d)) + sleep(&d->r, dmaidlep, d); +} + +/* + * this interface really only copes with one buffer at once + */ +static void +dmaintr(Ureg*, void *a) +{ + Dma *d; + ulong s; + + d = (Dma*)a; + s = d->reg->dcsr; + if(s & CSRerror) + iprint("DMA error, chan %d status #%2.2lux\n", d->chan, s); + s &= (CSRdonea|CSRdoneb|CSRerror); + d->reg->dcsr_c = s; + if(d->interrupt != nil) + d->interrupt(d->arg, s & (CSRdonea|CSRdoneb)); + wakeup(&d->r); +} diff --git a/os/sa1110/etherif.h b/os/sa1110/etherif.h new file mode 100644 index 00000000..5c5c679b --- /dev/null +++ b/os/sa1110/etherif.h @@ -0,0 +1,41 @@ +enum { + MaxEther = 3, + Ntypes = 8, +}; + +typedef struct Ether Ether; +struct Ether { +RWlock; /* TO DO */ + ISAConf; /* hardware info */ + int ctlrno; + int minmtu; + int maxmtu; + uchar ea[Eaddrlen]; + int encry; + + void (*attach)(Ether*); /* filled in by reset routine */ + void (*closed)(Ether*); + void (*detach)(Ether*); + void (*transmit)(Ether*); + void (*interrupt)(Ureg*, void*); + long (*ifstat)(Ether*, void*, long, ulong); + long (*ctl)(Ether*, void*, long); /* custom ctl messages */ + void (*power)(Ether*, int); /* power on/off */ + void (*shutdown)(Ether*); /* shutdown hardware before reboot */ + void *ctlr; + int pcmslot; /* PCMCIA */ + int fullduplex; /* non-zero if full duplex */ + + Queue* oq; + + Netif; +}; + +extern Block* etheriq(Ether*, Block*, int); +extern void addethercard(char*, int(*)(Ether*)); +extern int archether(int, Ether*); + +#define NEXT(x, l) (((x)+1)%(l)) +#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1) +#define HOWMANY(x, y) (((x)+((y)-1))/(y)) +#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) diff --git a/os/sa1110/fpi.h b/os/sa1110/fpi.h new file mode 100644 index 00000000..dfb9b1df --- /dev/null +++ b/os/sa1110/fpi.h @@ -0,0 +1,61 @@ +typedef long Word; +typedef unsigned long Single; +typedef struct { + unsigned long h; + unsigned long l; +} Double; + +enum { + FractBits = 28, + CarryBit = 0x10000000, + HiddenBit = 0x08000000, + MsBit = HiddenBit, + NGuardBits = 3, + GuardMask = 0x07, + LsBit = (1<e >= ExpInfinity) +#define IsInfinity(n) (IsWeird(n) && (n)->h == HiddenBit && (n)->l == 0) +#define SetInfinity(n) ((n)->e = ExpInfinity, (n)->h = HiddenBit, (n)->l = 0) +#define IsNaN(n) (IsWeird(n) && (((n)->h & ~HiddenBit) || (n)->l)) +#define SetQNaN(n) ((n)->s = 0, (n)->e = ExpInfinity, \ + (n)->h = HiddenBit|(LsBit<<1), (n)->l = 0) +#define IsZero(n) ((n)->e == 1 && (n)->h == 0 && (n)->l == 0) +#define SetZero(n) ((n)->e = 1, (n)->h = 0, (n)->l = 0) + +/* + * fpi.c + */ +extern void fpiround(Internal *); +extern void fpiadd(Internal *, Internal *, Internal *); +extern void fpisub(Internal *, Internal *, Internal *); +extern void fpimul(Internal *, Internal *, Internal *); +extern void fpidiv(Internal *, Internal *, Internal *); +extern int fpicmp(Internal *, Internal *); +extern void fpinormalise(Internal*); + +/* + * fpimem.c + */ +extern void fpis2i(Internal *, void *); +extern void fpid2i(Internal *, void *); +extern void fpiw2i(Internal *, void *); +extern void fpii2s(void *, Internal *); +extern void fpii2d(void *, Internal *); +extern void fpii2w(Word *, Internal *); diff --git a/os/sa1110/fpiarm.c b/os/sa1110/fpiarm.c new file mode 100644 index 00000000..4acfcd1d --- /dev/null +++ b/os/sa1110/fpiarm.c @@ -0,0 +1,483 @@ +/* + * this doesn't attempt to implement ARM floating-point properties + * that aren't visible in the Inferno environment. + * all arithmetic is done in double precision. + * the FP trap status isn't updated. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "ureg.h" + +#include "fpi.h" + +// #define R13OK undef this if correct kernel r13 isn't in Ureg; check calculation in fpiarm below + +#define REG(x) (*(long*)(((char*)(ur))+roff[(x)])) +#define FPENV (*(ufp)) +#define FR(x) (*(Internal*)(ufp)->regs[(x)&7]) + +/* BUG: check fetch (not worthwhile in Inferno) */ +#define getubyte(a) (*(uchar*)(a)) +#define getuword(a) (*(ushort*)(a)) +#define getulong(a) (*(ulong*)(a)) + +typedef struct FP2 FP2; +typedef struct FP1 FP1; + +struct FP2 { + char* name; + void (*f)(Internal, Internal, Internal*); +}; + +struct FP1 { + char* name; + void (*f)(Internal*, Internal*); +}; + +enum { + N = 1<<31, + Z = 1<<30, + C = 1<<29, + V = 1<<28, + REGPC = 15, +}; + +int fpemudebug = 0; + +#undef OFR +#define OFR(X) ((ulong)&((Ureg*)0)->X) + +static int roff[] = { + OFR(r0), OFR(r1), OFR(r2), OFR(r3), + OFR(r4), OFR(r5), OFR(r6), OFR(r7), + OFR(r8), OFR(r9), OFR(r10), OFR(r11), +#ifdef R13OK + OFR(r12), OFR(r13), OFR(r14), OFR(pc), +#else + OFR(r12), OFR(type), OFR(r14), OFR(pc), +#endif +}; + +static Internal fpconst[8] = { /* indexed by op&7 */ + /* s, e, l, h */ + {0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */ + {0, 0x3FF, 0x00000000, 0x08000000}, /* 1.0 */ + {0, 0x400, 0x00000000, 0x08000000}, /* 2.0 */ + {0, 0x400, 0x00000000, 0x0C000000}, /* 3.0 */ + {0, 0x401, 0x00000000, 0x08000000}, /* 4.0 */ + {0, 0x401, 0x00000000, 0x0A000000}, /* 5.0 */ + {0, 0x3FE, 0x00000000, 0x08000000}, /* 0.5 */ + {0, 0x402, 0x00000000, 0x0A000000}, /* 10.0 */ +}; + +/* + * arm binary operations + */ + +static void +fadd(Internal m, Internal n, Internal *d) +{ + (m.s == n.s? fpiadd: fpisub)(&m, &n, d); +} + +static void +fsub(Internal m, Internal n, Internal *d) +{ + m.s ^= 1; + (m.s == n.s? fpiadd: fpisub)(&m, &n, d); +} + +static void +fsubr(Internal m, Internal n, Internal *d) +{ + n.s ^= 1; + (n.s == m.s? fpiadd: fpisub)(&n, &m, d); +} + +static void +fmul(Internal m, Internal n, Internal *d) +{ + fpimul(&m, &n, d); +} + +static void +fdiv(Internal m, Internal n, Internal *d) +{ + fpidiv(&m, &n, d); +} + +static void +fdivr(Internal m, Internal n, Internal *d) +{ + fpidiv(&n, &m, d); +} + +/* + * arm unary operations + */ + +static void +fmov(Internal *m, Internal *d) +{ + *d = *m; +} + +static void +fmovn(Internal *m, Internal *d) +{ + *d = *m; + d->s ^= 1; +} + +static void +fabsf(Internal *m, Internal *d) +{ + *d = *m; + d->s = 0; +} + +static void +frnd(Internal *m, Internal *d) +{ + short e; + + (m->s? fsub: fadd)(fpconst[6], *m, d); + if(IsWeird(d)) + return; + fpiround(d); + e = (d->e - ExpBias) + 1; + if(e <= 0) + SetZero(d); + else if(e > FractBits){ + if(e < 2*FractBits) + d->l &= ~((1<<(2*FractBits - e))-1); + }else{ + d->l = 0; + if(e < FractBits) + d->h &= ~((1<<(FractBits-e))-1); + } +} + +static FP1 optab1[16] = { /* Fd := OP Fm */ +[0] {"MOVF", fmov}, +[1] {"NEGF", fmovn}, +[2] {"ABSF", fabsf}, +[3] {"RNDF", frnd}, +[4] {"SQTF", /*fsqt*/0}, +/* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */ +/* URD and NRM aren't implemented */ +}; + +static FP2 optab2[16] = { /* Fd := Fn OP Fm */ +[0] {"ADDF", fadd}, +[1] {"MULF", fmul}, +[2] {"SUBF", fsub}, +[3] {"RSUBF", fsubr}, +[4] {"DIVF", fdiv}, +[5] {"RDIVF", fdivr}, +/* POW, RPW deprecated */ +[8] {"REMF", /*frem*/0}, +[9] {"FMF", fmul}, /* fast multiply */ +[10] {"FDV", fdiv}, /* fast divide */ +[11] {"FRD", fdivr}, /* fast reverse divide */ +/* POL deprecated */ +}; + +static ulong +fcmp(Internal *n, Internal *m) +{ + int i; + + if(IsWeird(m) || IsWeird(n)){ + /* BUG: should trap if not masked */ + return V|C; + } + i = fpicmp(n, m); + if(i > 0) + return C; + else if(i == 0) + return C|Z; + else + return N; +} + +static void +fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPenv *ufp) +{ + void *mem; + + mem = (void*)ea; + (*f)(&FR(d), mem); + if(fpemudebug) + print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d); +} + +static void +fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPenv *ufp) +{ + Internal tmp; + void *mem; + + mem = (void*)ea; + tmp = FR(s); + if(fpemudebug) + print("MOV%c F%d,#%lux\n", n==8? 'D': 'F', s, ea); + (*f)(mem, &tmp); +} + +static int +condok(int cc, int c) +{ + switch(c){ + case 0: /* Z set */ + return cc&Z; + case 1: /* Z clear */ + return (cc&Z) == 0; + case 2: /* C set */ + return cc&C; + case 3: /* C clear */ + return (cc&C) == 0; + case 4: /* N set */ + return cc&N; + case 5: /* N clear */ + return (cc&N) == 0; + case 6: /* V set */ + return cc&V; + case 7: /* V clear */ + return (cc&V) == 0; + case 8: /* C set and Z clear */ + return cc&C && (cc&Z) == 0; + case 9: /* C clear or Z set */ + return (cc&C) == 0 || cc&Z; + case 10: /* N set and V set, or N clear and V clear */ + return (~cc&(N|V))==0 || (cc&(N|V)) == 0; + case 11: /* N set and V clear, or N clear and V set */ + return (cc&(N|V))==N || (cc&(N|V))==V; + case 12: /* Z clear, and either N set and V set or N clear and V clear */ + return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0); + case 13: /* Z set, or N set and V clear or N clear and V set */ + return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V; + case 14: /* always */ + return 1; + case 15: /* never (reserved) */ + return 0; + } + return 0; /* not reached */ +} + +static void +unimp(ulong pc, ulong op) +{ + char buf[60]; + + snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op); + if(fpemudebug) + print("FPE: %s\n", buf); + error(buf); + /* no return */ +} + +static void +fpemu(ulong pc, ulong op, Ureg *ur, FPenv *ufp) +{ + int rn, rd, tag, o; + long off; + ulong ea; + Internal tmp, *fm, *fn; + + /* note: would update fault status here if we noted numeric exceptions */ + + /* + * LDF, STF; 10.1.1 + */ + if(((op>>25)&7) == 6){ + if(op & (1<<22)) + unimp(pc, op); /* packed or extended */ + rn = (op>>16)&0xF; + off = (op&0xFF)<<2; + if((op & (1<<23)) == 0) + off = -off; + ea = REG(rn); + if(rn == REGPC) + ea += 8; + if(op & (1<<24)) + ea += off; + rd = (op>>12)&7; + if(op & (1<<20)){ + if(op & (1<<15)) + fld(fpid2i, rd, ea, 8, ufp); + else + fld(fpis2i, rd, ea, 4, ufp); + }else{ + if(op & (1<<15)) + fst(fpii2d, ea, rd, 8, ufp); + else + fst(fpii2s, ea, rd, 4, ufp); + } + if((op & (1<<24)) == 0) + ea += off; + if(op & (1<<21)) + REG(rn) = ea; + return; + } + + /* + * CPRT/transfer, 10.3 + */ + if(op & (1<<4)){ + rd = (op>>12) & 0xF; + + /* + * compare, 10.3.1 + */ + if(rd == 15 && op & (1<<20)){ + rn = (op>>16)&7; + fn = &FR(rn); + if(op & (1<<3)){ + fm = &fpconst[op&7]; + tag = 'C'; + }else{ + fm = &FR(op&7); + tag = 'F'; + } + switch((op>>21)&7){ + default: + unimp(pc, op); + case 4: /* CMF: Fn :: Fm */ + case 6: /* CMFE: Fn :: Fm (with exception) */ + ur->psr &= ~(N|C|Z|V); + ur->psr |= fcmp(fn, fm); + break; + case 5: /* CNF: Fn :: -Fm */ + case 7: /* CNFE: Fn :: -Fm (with exception) */ + tmp = *fm; + tmp.s ^= 1; + ur->psr &= ~(N|C|Z|V); + ur->psr |= fcmp(fn, &tmp); + break; + } + if(fpemudebug) + print("CMPF %c%d,F%ld =%x\n", tag, rn, op&7, ur->psr>>28); + return; + } + + /* + * other transfer, 10.3 + */ + switch((op>>20)&0xF){ + default: + unimp(pc, op); + case 0: /* FLT */ + rn = (op>>16) & 7; + fpiw2i(&FR(rn), ®(rd)); + if(fpemudebug) + print("MOVW[FD] R%d, F%d\n", rd, rn); + break; + case 1: /* FIX */ + if(op & (1<<3)) + unimp(pc, op); + rn = op & 7; + tmp = FR(rn); + fpii2w(®(rd), &tmp); + if(fpemudebug) + print("MOV[FD]W F%d, R%d =%ld\n", rn, rd, REG(rd)); + break; + case 2: /* FPSR := Rd */ + FPENV.status = REG(rd); + if(fpemudebug) + print("MOVW R%d, FPSR\n", rd); + break; + case 3: /* Rd := FPSR */ + REG(rd) = FPENV.status; + if(fpemudebug) + print("MOVW FPSR, R%d\n", rd); + break; + case 4: /* FPCR := Rd */ + FPENV.control = REG(rd); + if(fpemudebug) + print("MOVW R%d, FPCR\n", rd); + break; + case 5: /* Rd := FPCR */ + REG(rd) = FPENV.control; + if(fpemudebug) + print("MOVW FPCR, R%d\n", rd); + break; + } + return; + } + + /* + * arithmetic + */ + + if(op & (1<<3)){ /* constant */ + fm = &fpconst[op&7]; + tag = 'C'; + }else{ + fm = &FR(op&7); + tag = 'F'; + } + rd = (op>>12)&7; + o = (op>>20)&0xF; + if(op & (1<<15)){ /* monadic */ + FP1 *fp; + fp = &optab1[o]; + if(fp->f == nil) + unimp(pc, op); + if(fpemudebug) + print("%s %c%ld,F%d\n", fp->name, tag, op&7, rd); + (*fp->f)(fm, &FR(rd)); + } else { + FP2 *fp; + fp = &optab2[o]; + if(fp->f == nil) + unimp(pc, op); + rn = (op>>16)&7; + if(fpemudebug) + print("%s %c%ld,F%d,F%d\n", fp->name, tag, op&7, rn, rd); + (*fp->f)(*fm, FR(rn), &FR(rd)); + } +} + +/* + * returns the number of FP instructions emulated + */ +int +fpiarm(Ureg *ur) +{ + ulong op, o; + FPenv *ufp; + int n; + +#ifndef R13OK +/* ur->type = &ur->pc+1; /* calculate kernel sp/R13 and put it here for roff[13] */ + ur->type = (ulong)(ur + 1); +#endif + if (up == nil) + panic("fpiarm not in a process"); + ufp = &up->env->fpu; /* because all the state is in Osenv, it need not be saved/restored */ + if(ufp->fpistate != FPACTIVE) { + ufp->fpistate = FPACTIVE; + ufp->control = 0; + ufp->status = (0x01<<28)|(1<<12); /* software emulation, alternative C flag */ + for(n = 0; n < 8; n++) + FR(n) = fpconst[0]; + } + for(n=0;;n++){ + op = getulong(ur->pc); + o = (op>>24) & 0xF; + if(((op>>8) & 0xF) != 1 || o != 0xE && (o&~1) != 0xC) + break; + if(condok(ur->psr, op>>28)) + fpemu(ur->pc, op, ur, ufp); + ur->pc += 4; + if(anyhigher()) + sched(); + } + return n; +} diff --git a/os/sa1110/gscreen.c b/os/sa1110/gscreen.c new file mode 100644 index 00000000..9949f9d1 --- /dev/null +++ b/os/sa1110/gscreen.c @@ -0,0 +1,587 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +#include +#include +#include + +#include "screen.h" + +enum { + Backgnd = 0xFF, /* white */ + Foregnd = 0x00, /* black */ +}; + +#define DPRINT if(1)iprint + +static Memdata xgdata; +static Memimage xgscreen = +{ + {0, 0, 0, 0}, /* r */ + {0, 0, 0, 0}, /* clipr */ + 8, /* depth */ + 1, /* nchan */ + CMAP8, /* chan */ + nil, /* cmap */ + &xgdata, /* data */ + 0, /* zero */ + 0, /* width */ + nil, /* layer */ + 0, /* flags */ +}; + +Memimage *gscreen; +Memimage *conscol; +Memimage *back; + +Memsubfont *memdefont; + +static Point curpos; +static Rectangle window; + +typedef struct SWcursor SWcursor; + +static Vdisplay *vd; + +static char printbuf[1024]; +static int printbufpos = 0; +static void lcdscreenputs(char*, int); +static void screenpbuf(char*, int); +void (*screenputs)(char*, int) = screenpbuf; + +static Cursor arrow = { + { -1, -1 }, + { 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04, + 0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04, + 0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40, + }, + { 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0, + 0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8, + 0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8, + 0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00, + }, +}; + +static ushort palette16[256]; +static void (*flushpixels)(Rectangle, ulong*, int, ulong*, int); +static void flush8to4(Rectangle, ulong*, int, ulong*, int); +static void flush8to4r(Rectangle, ulong*, int, ulong*, int); +static void flush8to16(Rectangle, ulong*, int, ulong*, int); +static void flush8to16r(Rectangle, ulong*, int, ulong*, int); + +/* +lccr0=000000b9 lccr1=0b100930 lccr2=0a0108ef lccr3=00300010 + --- +vd->wid=320 bwid=640 gscreen->width=60 fb=d0b7cb80 data=d0ba25c0 + */ + +int +setcolor(ulong p, ulong r, ulong g, ulong b) +{ + if(vd->depth >= 8) + p &= 0xff; + else + p &= 0xf; + vd->colormap[p][0] = r; + vd->colormap[p][1] = g; + vd->colormap[p][2] = b; + palette16[p] = ((r>>(32-4))<<12)|((g>>(32-4))<<7)|((b>>(32-4))<<1); + lcd_setcolor(p, r, g, b); + return ~0; +} + +void +getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb) +{ + if(vd->depth >= 8) + p = (p&0xff)^0xff; + else + p = (p&0xf)^0xf; + *pr = vd->colormap[p][0]; + *pg = vd->colormap[p][1]; + *pb = vd->colormap[p][2]; +} + +void +graphicscmap(int invert) +{ + int num, den, i, j; + int r, g, b, cr, cg, cb, v, p; + + for(r=0,i=0;r!=4;r++) for(v=0;v!=4;v++,i+=16){ + for(g=0,j=v-r;g!=4;g++) for(b=0;b!=4;b++,j++){ + den=r; + if(g>den) den=g; + if(b>den) den=b; + if(den==0) /* divide check -- pick grey shades */ + cr=cg=cb=v*17; + else{ + num=17*(4*den+v); + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + p = (i+(j&15)); + if(invert) + p ^= 0xFF; + if(vd->depth == 4) { + if((p&0xf) != (p>>4)) + continue; + p &= 0xf; + } + setcolor(p, + cr*0x01010101, + cg*0x01010101, + cb*0x01010101); + } + } + lcd_flush(); +} + +static uchar lum[256]={ + 0, 7, 15, 23, 39, 47, 55, 63, 79, 87, 95, 103, 119, 127, 135, 143, +154, 17, 9, 17, 25, 49, 59, 62, 68, 89, 98, 107, 111, 129, 138, 146, +157, 166, 34, 11, 19, 27, 59, 71, 69, 73, 99, 109, 119, 119, 139, 148, +159, 169, 178, 51, 13, 21, 29, 69, 83, 75, 78, 109, 120, 131, 128, 149, + 28, 35, 43, 60, 68, 75, 83, 100, 107, 115, 123, 140, 147, 155, 163, 20, + 25, 35, 40, 47, 75, 85, 84, 89, 112, 121, 129, 133, 151, 159, 168, 176, +190, 30, 42, 44, 50, 90, 102, 94, 97, 125, 134, 144, 143, 163, 172, 181, +194, 204, 35, 49, 49, 54, 105, 119, 103, 104, 137, 148, 158, 154, 175, 184, + 56, 63, 80, 88, 96, 103, 120, 128, 136, 143, 160, 168, 175, 183, 40, 48, + 54, 63, 69, 90, 99, 107, 111, 135, 144, 153, 155, 173, 182, 190, 198, 45, + 50, 60, 70, 74, 100, 110, 120, 120, 150, 160, 170, 167, 186, 195, 204, 214, +229, 55, 66, 77, 79, 110, 121, 131, 129, 165, 176, 187, 179, 200, 210, 219, + 84, 100, 108, 116, 124, 140, 148, 156, 164, 180, 188, 196, 204, 60, 68, 76, + 82, 91, 108, 117, 125, 134, 152, 160, 169, 177, 195, 204, 212, 221, 66, 74, + 80, 89, 98, 117, 126, 135, 144, 163, 172, 181, 191, 210, 219, 228, 238, 71, + 76, 85, 95, 105, 126, 135, 145, 155, 176, 185, 195, 205, 225, 235, 245, 255, +}; + +void flushmemscreen(Rectangle r); + +void +screenclear(void) +{ + memimagedraw(gscreen, gscreen->r, memwhite, ZP, memopaque, ZP, SoverD); + curpos = window.min; + flushmemscreen(gscreen->r); +} + +static void +setscreen(LCDmode *mode) +{ + int h; + +// if(swc != nil) +// swcurs_destroy(swc); + + vd = lcd_init(mode); + if(vd == nil) + panic("can't initialise LCD"); + + if(lum[255] == 255) { + int i; + for(i=0; i<256; i++) + lum[i] >>= 4; /* could support depths other than 4 */ + } + + gscreen = &xgscreen; + xgdata.ref = 1; + + if(conf.portrait == 0) + gscreen->r = Rect(0, 0, vd->x, vd->y); + else + gscreen->r = Rect(0, 0, vd->y, vd->x); + gscreen->clipr = gscreen->r; + gscreen->depth = 8; + gscreen->width = wordsperline(gscreen->r, gscreen->depth); + if(vd->depth == 4 || vd->depth == 16 || conf.portrait) { /* use 8 to 4 bit fakeout for speed */ + if((xgdata.bdata = xspanalloc(gscreen->width*gscreen->r.max.y*BY2WD+CACHELINESZ, CACHELINESZ, 0)) == nil) + panic("can't alloc vidmem"); + xgdata.bdata = minicached(xgdata.bdata); + if(conf.portrait == 0) + flushpixels = vd->depth==4? flush8to4: flush8to16; + else + flushpixels = vd->depth==4? flush8to4r: flush8to16r; + } else{ + xgdata.bdata = (uchar*)vd->fb; + flushpixels = nil; + } + memimageinit(); + memdefont = getmemdefont(); + + memsetchan(gscreen, CMAP8); /* TO DO: could now use RGB16 */ + back = memwhite; + conscol = memblack; + memimagedraw(gscreen, gscreen->r, memwhite, ZP, memopaque, ZP, SoverD); + + DPRINT("vd->wid=%d bwid=%d gscreen->width=%ld fb=%p data=%p\n", + vd->x, vd->bwid, gscreen->width, vd->fb, xgdata.bdata); + graphicscmap(0); + h = memdefont->height; + window = insetrect(gscreen->r, 4); + window.max.y = window.min.y+(Dy(window)/h)*h; + screenclear(); + +// swc = swcurs_create(gscreendata.data, gscreen.width, gscreen.ldepth, gscreen.r, 1); + + drawcursor(nil); +} + +void +screeninit(void) +{ + LCDmode lcd; + + memset(&lcd, 0, sizeof(lcd)); + if(archlcdmode(&lcd) < 0) + return; + setscreen(&lcd); + screenputs = lcdscreenputs; + if(printbufpos) + screenputs("", 0); + blanktime = 3; /* minutes */ +} + +uchar* +attachscreen(Rectangle *r, ulong *chan, int* d, int *width, int *softscreen) +{ + *r = gscreen->r; + *d = gscreen->depth; + *chan = gscreen->chan; + *width = gscreen->width; + *softscreen = (gscreen->data->bdata != (uchar*)vd->fb); + + return (uchar*)gscreen->data->bdata; +} + +void +detachscreen(void) +{ +} + +static void +flush8to4(Rectangle r, ulong *s, int sw, ulong *d, int dw) +{ + int i, h, w; + +/* + print("1) s=%ux sw=%d d=%ux dw=%d r=(%d,%d)(%d,%d)\n", + s, sw, d, dw, r.min.x, r.min.y, r.max.x, r.max.y); +*/ + + r.min.x &= ~7; + r.max.x = (r.max.x + 7) & ~7; + s += (r.min.y*sw)+(r.min.x>>2); + d += (r.min.y*dw)+(r.min.x>>3); + h = Dy(r); + w = Dx(r) >> 3; + sw -= w*2; + dw -= w; + + while(h--) { + for(i=w; i; i--) { + ulong v1 = *s++; + ulong v2 = *s++; + *d++ = (lum[v2>>24]<<28) + |(lum[(v2>>16)&0xff]<<24) + |(lum[(v2>>8)&0xff]<<20) + |(lum[v2&0xff]<<16) + |(lum[v1>>24]<<12) + |(lum[(v1>>16)&0xff]<<8) + |(lum[(v1>>8)&0xff]<<4) + |(lum[v1&0xff]) + ; + } + s += sw; + d += dw; + } +} + +static void +flush8to16(Rectangle r, ulong *s, int sw, ulong *d, int dw) +{ + int i, h, w; + ushort *p; + + if(0) + iprint("1) s=%p sw=%d d=%p dw=%d r=[%d,%d, %d,%d]\n", + s, sw, d, dw, r.min.x, r.min.y, r.max.x, r.max.y); + + r.min.x &= ~3; + r.max.x = (r.max.x + 3) & ~3; /* nearest ulong */ + s += (r.min.y*sw)+(r.min.x>>2); + d += (r.min.y*dw)+(r.min.x>>1); + h = Dy(r); + w = Dx(r) >> 2; /* also ulong */ + sw -= w; + dw -= w*2; + if(0) + iprint("h=%d w=%d sw=%d dw=%d\n", h, w, sw, dw); + + p = palette16; + while(--h >= 0){ + for(i=w; --i>=0;){ + ulong v = *s++; + *d++ = (p[(v>>8)&0xFF]<<16) | p[v & 0xFF]; + *d++ = (p[v>>24]<<16) | p[(v>>16)&0xFF]; + } + s += sw; + d += dw; + } +} + +static void +flush8to4r(Rectangle r, ulong *s, int sw, ulong *d, int dw) +{ + flush8to4(r, s, sw, d, dw); /* rotation not implemented */ +} + +static void +flush8to16r(Rectangle r, ulong *s, int sw, ulong *d, int dw) +{ + int x, y, w, dws; + ushort *p; + ushort *ds; + + if(0) + iprint("1) s=%p sw=%d d=%p dw=%d r=[%d,%d, %d,%d]\n", + s, sw, d, dw, r.min.x, r.min.y, r.max.x, r.max.y); + + r.min.y &= ~3; + r.max.y = (r.max.y+3) & ~3; + r.min.x &= ~7; + r.max.x = (r.max.x + 7) & ~7; + s += (r.min.y*sw)+(r.min.x>>2); +// d += (r.min.y*dw)+(r.min.x>>1); + w = Dx(r) >> 2; /* also ulong */ + sw -= w; + dws = dw*2; + if(0) + iprint("h=%d w=%d sw=%d dw=%d x,y=%d,%d %d\n", Dy(r), w, sw, dw, r.min.x,r.min.y, dws); + + p = palette16; + for(y=r.min.y; yr.max.y-(y+1)); + ds[0] = p[v & 0xFF]; + ds[dws] = p[(v>>8)&0xFF]; + ds[dws*2] = p[(v>>16)&0xFF]; + ds[dws*3] = p[(v>>24)&0xFF]; + } + s += sw; + } +} + +void +flushmemscreen(Rectangle r) +{ + if(rectclip(&r, gscreen->r) == 0) + return; + if(r.min.x >= r.max.x || r.min.y >= r.max.y) + return; + if(flushpixels != nil) + flushpixels(r, (ulong*)gscreen->data->bdata, gscreen->width, (ulong*)vd->fb, vd->bwid >> 2); + lcd_flush(); +} + +static void +scroll(void) +{ + int o; + Point p; + Rectangle r; + + o = 4*memdefont->height; + r = Rpt(window.min, Pt(window.max.x, window.max.y-o)); + p = Pt(window.min.x, window.min.y+o); + memimagedraw(gscreen, r, gscreen, p, nil, p, SoverD); + flushmemscreen(r); + r = Rpt(Pt(window.min.x, window.max.y-o), window.max); + memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD); + flushmemscreen(r); + + curpos.y -= o; +} + +static void +clearline(void) +{ + Rectangle r; + int yloc = curpos.y; + + r = Rpt(Pt(window.min.x, window.min.y + yloc), + Pt(window.max.x, window.min.y+yloc+memdefont->height)); + memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD); +} + +static void +screenputc(char *buf) +{ + Point p; + int h, w, pos; + Rectangle r; + static int *xp; + static int xbuf[256]; + + h = memdefont->height; + if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)]) + xp = xbuf; + + switch(buf[0]) { + case '\n': + if(curpos.y+h >= window.max.y) + scroll(); + curpos.y += h; + /* fall through */ + case '\r': + xp = xbuf; + curpos.x = window.min.x; + break; + case '\t': + if(curpos.x == window.min.x) + clearline(); + p = memsubfontwidth(memdefont, " "); + w = p.x; + *xp++ = curpos.x; + pos = (curpos.x-window.min.x)/w; + pos = 8-(pos%8); + r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y+h); + memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD); + flushmemscreen(r); + curpos.x += pos*w; + break; + case '\b': + if(xp <= xbuf) + break; + xp--; + r = Rpt(Pt(*xp, curpos.y), Pt(curpos.x, curpos.y + h)); + memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD); + flushmemscreen(r); + curpos.x = *xp; + break; + case '\0': + break; + default: + p = memsubfontwidth(memdefont, buf); + w = p.x; + + if(curpos.x >= window.max.x-w) + screenputc("\n"); + + if(curpos.x == window.min.x) + clearline(); + if(xp < xbuf+nelem(xbuf)) + *xp++ = curpos.x; + r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y+h); + memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD); + memimagestring(gscreen, curpos, conscol, ZP, memdefont, buf); + flushmemscreen(r); + curpos.x += w; + } +} + +static void +screenpbuf(char *s, int n) +{ + if(printbufpos+n > sizeof(printbuf)) + n = sizeof(printbuf)-printbufpos; + if(n > 0) { + memmove(&printbuf[printbufpos], s, n); + printbufpos += n; + } +} + +static void +screendoputs(char *s, int n) +{ + int i; + Rune r; + char buf[4]; + + while(n > 0) { + i = chartorune(&r, s); + if(i == 0){ + s++; + --n; + continue; + } + memmove(buf, s, i); + buf[i] = 0; + n -= i; + s += i; + screenputc(buf); + } +} + +void +screenflush(void) +{ + int j = 0; + int k; + + for (k = printbufpos; j < k; k = printbufpos) { + screendoputs(printbuf + j, k - j); + j = k; + } + printbufpos = 0; +} + +static void +lcdscreenputs(char *s, int n) +{ + static Proc *me; + + if(!canlock(vd)) { + /* don't deadlock trying to print in interrupt */ + /* don't deadlock trying to print while in print */ + if(islo() == 0 || up != nil && up == me){ + /* save it for later... */ + /* In some cases this allows seeing a panic message + that would be locked out forever */ + screenpbuf(s, n); + return; + } + lock(vd); + } + + me = up; + if(printbufpos) + screenflush(); + screendoputs(s, n); + if(printbufpos) + screenflush(); + me = nil; + + unlock(vd); +} + +/* + * interface between draw, mouse and cursor + */ +void +cursorupdate(Rectangle r) +{ +} + +void +cursorenable(void) +{ +} + +void +cursordisable(void) +{ +} + +void +drawcursor(Drawcursor* c) +{ +} diff --git a/os/sa1110/gscreen.h b/os/sa1110/gscreen.h new file mode 100644 index 00000000..7c1de585 --- /dev/null +++ b/os/sa1110/gscreen.h @@ -0,0 +1,87 @@ +typedef struct Cursor Cursor; +typedef struct LCDmode LCDmode; +typedef struct LCDparam LCDparam; +typedef struct Vmode Vmode; +typedef struct Physdisplay Physdisplay; +typedef struct Physcursor Physcursor; + +#define CURSWID 16 +#define CURSHGT 16 + +struct Cursor { + Point offset; + uchar clr[CURSWID/BI2BY*CURSHGT]; + uchar set[CURSWID/BI2BY*CURSHGT]; +}; + +struct Vmode { + int x; /* 0 -> default or any match for all fields */ + int y; + uchar depth; + uchar hz; +}; + +struct Physdisplay { + uchar* fb; /* frame buffer */ + ulong colormap[256][3]; + Rectangle r; + int bwid; + void* aux; + Memimage* gscreen; + Memsubfont* memdefont; + Physcursor* cursor; + void* cdata; /* cursor data */ + Lock; + Vmode; +}; + +struct LCDparam { + uchar pbs; + uchar dual; + uchar mono; + uchar active; + uchar hsync_wid; + uchar sol_wait; + uchar eol_wait; + uchar vsync_hgt; + uchar sof_wait; + uchar eof_wait; + uchar lines_per_int; + uchar palette_delay; + uchar acbias_lines; + uchar obits; + uchar vsynclow; + uchar hsynclow; +}; + +struct LCDmode { + Vmode; + LCDparam; +}; + +int archlcdmode(LCDmode*); + +Vdisplay *lcd_init(LCDmode*); +void lcd_setcolor(ulong, ulong, ulong, ulong); +void lcd_flush(void); + +extern void blankscreen(int); +extern void drawblankscreen(int); +extern ulong blanktime; +extern Point mousexy(void); + +enum { + Backgnd = 0xFF, /* white */ + Foregnd = 0x00, /* black */ +}; + +struct Physcursor { + char* name; + + void (*create)(Physdisplay*); + void (*enable)(Physdisplay*); + void (*disable)(Physdisplay*); + void (*load)(Physdisplay*, Cursor*); + int (*move)(Physdisplay*, Point); + void (*destroy)(Physdisplay*); +}; diff --git a/os/sa1110/i2c.h b/os/sa1110/i2c.h new file mode 100644 index 00000000..fb179606 --- /dev/null +++ b/os/sa1110/i2c.h @@ -0,0 +1,16 @@ +/* i2cgpio.c */ + +int i2c_write_byte(uchar addr, uchar data); +int i2c_read_byte(uchar addr, uchar *data); +void i2c_reset(void); + +extern unsigned char i2c_iactl[]; +int i2c_setpin(int b); +int i2c_clrpin(int b); +int i2c_getpin(int b); + +/* GPIO pin assignments (0->31) - defined in arch????.c */ +extern int gpio_i2c_sda; /* in/out, as per i2c protocol */ +extern int gpio_i2c_scl; /* in/out, as per i2c protocol */ + + diff --git a/os/sa1110/i2cgpio.c b/os/sa1110/i2cgpio.c new file mode 100644 index 00000000..1eb69387 --- /dev/null +++ b/os/sa1110/i2cgpio.c @@ -0,0 +1,274 @@ +/* + * I2C master emulation using GPIO pins. + * 7 bit addressing only. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" +#include "i2c.h" + +/* GPIO bitmasks */ +static struct { + Lock; + ulong sda; + ulong scl; +} i2c; + + +/* set pin level high by disabling output drive and allowing pull-up to work */ +static void +i2c_set(int pin) +{ + GPIOREG->gpdr &= ~pin; /* configure pin as input */ +} + +/* set pin level low with output drive */ +static void +i2c_clear(int pin) +{ + GPIOREG->gpcr = pin; /* set pin output low */ + GPIOREG->gpdr |= pin; /* configure pin as output */ +} + +static int +i2c_getack(void) +{ + /* scl is low, sda is not defined */ + + i2c_set(i2c.sda); /* set data high */ + timer_delay(US2TMR(3)); + + i2c_set(i2c.scl); /* raise clock */ + timer_delay(US2TMR(5)); + + /* check for ack from slave! */ + if (GPIOREG->gplr & i2c.sda) + print("I2C: Warning did not get ack!\n"); + + i2c_clear(i2c.sda); /* lower data */ + i2c_clear(i2c.scl); /* lower clock */ + timer_delay(US2TMR(3)); + + /* scl is low, sda is low */ + return 1; +} + + +static void +i2c_putack(void) +{ + /* scl is low, sda is not defined */ + + timer_delay(US2TMR(3)); /* lower data */ + i2c_clear(i2c.sda); + + i2c_set(i2c.scl); /* pulse clock */ + timer_delay(US2TMR(5)); + + i2c_clear(i2c.scl); /* lower clock */ + timer_delay(US2TMR(3)); + + /* scl is low, sda is low */ +} + + +static void +i2c_putbyte(uchar b) +{ + uchar m; + + /* start condition has been sent */ + /* scl is low, sda is low */ + + for(m=0x80; m; m >>= 1) { + + /* set data bit */ + if(b&m) + i2c_set(i2c.sda); + else + i2c_clear(i2c.sda); + + /* pulse clock */ + timer_delay(US2TMR(3)); + i2c_set(i2c.scl); + timer_delay(US2TMR(5)); + i2c_clear(i2c.scl); + timer_delay(US2TMR(3)); + } + + i2c_clear(i2c.sda); + /* scl is low, sda is low */ +} + + +static uchar +i2c_getbyte(void) +{ + /* start condition, address and ack been done */ + /* scl is low, sda is high */ + uchar data = 0x00; + int i; + + i2c_set(i2c.sda); + for (i=7; i >= 0; i--) { + + timer_delay(US2TMR(3)); + + /* raise clock */ + i2c_set(i2c.scl); + timer_delay(US2TMR(5)); + + /* sample data */ + if(GPIOREG->gplr & i2c.sda) + data |= 1<gplr & (i2c.sda | i2c.scl)) != (i2c.sda | i2c.scl)) + print("I2C: Bus not clear when attempting start condition\n"); + + i2c_clear(i2c.sda); /* lower sda */ + timer_delay(US2TMR(5)); + + i2c_clear(i2c.scl); /* lower scl */ + timer_delay(US2TMR(3)); + + return 1; +} + +/* generate I2C stop condition */ +static int +i2c_stop(void) +{ + /* clock is low, data is low */ + timer_delay(US2TMR(3)); + + i2c_set(i2c.scl); + timer_delay(US2TMR(5)); + + i2c_set(i2c.sda); + + timer_delay(MS2TMR(1)); /* ensure separation between commands */ + + return 1; +} + +/* + * external I2C interface + */ + +/* write a byte over the i2c bus */ +int +i2c_write_byte(uchar addr, uchar data) +{ + int rc = 0; + + ilock(&i2c); + if(i2c_start() < 0) /* start condition */ + rc = -1; + + i2c_putbyte(addr & 0xfe); /* address byte (LSB = 0 -> write) */ + + if (i2c_getack() < 0) /* get ack */ + rc = -2; + + i2c_putbyte(data); /* data byte */ + + if (i2c_getack() < 0) /* get ack */ + rc = -3; + + if (i2c_stop() < 0) + rc = -4; /* stop condition */ + iunlock(&i2c); + + return rc; +} + +/* read a byte over the i2c bus */ +int +i2c_read_byte(uchar addr, uchar *data) +{ + int rc = 0; + + ilock(&i2c); + if(i2c_start() < 0) /* start condition */ + rc = -1; + + i2c_putbyte(addr | 0x01); /* address byte (LSB = 1 -> read) */ + + if(i2c_getack() < 0) /* get ack */ + rc = -2; + + *data = i2c_getbyte(); /* data byte */ + + i2c_putack(); /* put ack */ + + if (i2c_stop() < 0) /* stop condition */ + rc = -4; + iunlock(&i2c); + + return rc; +} + +void +i2c_reset(void) +{ + /* initialise bitmasks */ + i2c.sda = (1 << gpio_i2c_sda); + i2c.scl = (1 << gpio_i2c_scl); + + /* ensure that both clock and data are high */ + i2c_set(i2c.sda); + i2c_set(i2c.scl); + timer_delay(MS2TMR(5)); +} + + +/* + * external pin set/clear interface + */ +uchar i2c_iactl[2] = { 0xff, 0xff }; /* defaults overridden in arch?????.c */ + +int +i2c_setpin(int b) +{ + int i = b>>3; + + ilock(&i2c); + i2c_iactl[i] |= (1 << (b&7)); + iunlock(&i2c); + return i2c_write_byte(0x40 | (i << 1), i2c_iactl[i]); +} + +int +i2c_clrpin(int b) +{ + int i = b>>3; + + ilock(&i2c); + i2c_iactl[i] &= ~(1 << (b&7)); + iunlock(&i2c); + return i2c_write_byte(0x40 | (i << 1), i2c_iactl[i]); +} + +int +i2c_getpin(int b) +{ + return (i2c_iactl[(b>>3)&1] & (1<<(b&7))) != 0; +} diff --git a/os/sa1110/l.s b/os/sa1110/l.s new file mode 100644 index 00000000..7f731fe6 --- /dev/null +++ b/os/sa1110/l.s @@ -0,0 +1,530 @@ +#include "mem.h" + +/* + * Entered here from the boot loader with + * supervisor mode, interrupts disabled; + * MMU, IDC and WB disabled. + */ + +TEXT _startup(SB), $-4 + MOVW $setR12(SB), R12 /* static base (SB) */ + MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R1 /* ensure SVC mode with interrupts disabled */ + MOVW R1, CPSR + MOVW $(MACHADDR+BY2PG-4), R13 /* stack; 4 bytes for link */ + + BL main(SB) +dead: + B dead + BL _div(SB) /* hack to get _div etc loaded */ + +TEXT getcpuid(SB), $-4 + MRC CpMMU, 0, R0, C(CpCPUID), C(0) + RET + +TEXT mmugetctl(SB), $-4 + MRC CpMMU, 0, R0, C(CpControl), C(0) + RET + +TEXT mmugetdac(SB), $-4 + MRC CpMMU, 0, R0, C(CpDAC), C(0) + RET + +TEXT mmugetfar(SB), $-4 + MRC CpMMU, 0, R0, C(CpFAR), C(0) + RET + +TEXT mmugetfsr(SB), $-4 + MRC CpMMU, 0, R0, C(CpFSR), C(0) + RET + +TEXT mmuputdac(SB), $-4 + MCR CpMMU, 0, R0, C(CpDAC), C(0) + RET + +TEXT mmuputfsr(SB), $-4 + MCR CpMMU, 0, R0, C(CpFSR), C(0) + RET + +TEXT mmuputttb(SB), $-4 + MCR CpMMU, 0, R0, C(CpTTB), C(0) + RET + +TEXT mmuputctl(SB), $-4 + MCR CpMMU, 0, R0, C(CpControl), C(0) + MOVW R0, R0 + MOVW R0, R0 + RET + +TEXT tlbinvalidateall(SB), $-4 + MCR CpMMU, 0, R0, C(CpTLBops), C(7) + RET + +TEXT tlbinvalidate(SB), $-4 + MCR CpMMU, 0, R0, C(CpTLBops), C(7), 1 + RET + +TEXT mmuenable(SB), $-4 + + MOVW $1, R1 + MCR CpMMU, 0, R1, C(CpDAC), C(3) /* set domain 0 to client */ + + /* disable and flush all caches and TLB's before (re-)enabling MMU */ + MOVW $(CpCi32 | CpCd32 | (1<<6) | CpCsystem), R1 + MRC CpMMU, 0, R1, C(CpControl), C(0) + MOVW $0, R1 /* disable everything */ + MCR CpMMU, 0, R1, C(CpCacheCtl), C(7), 0 /* Flush I&D Caches */ + MCR CpMMU, 0, R1, C(CpCacheCtl), C(10), 4 /* drain write buffer */ + MCR CpMMU, 0, R1, C(CpTLBops), C(7), 0 /* Flush I&D TLB */ + MCR CpMMU, 0, R1, C(CpRBops), C(0), 0 /* Flush Read Buffer */ + + /* enable desired mmu mode (R0) */ + MCR CpMMU, 0, R0, C(1), C(0) + MOVW R0, R0 + MOVW R0, R0 + MOVW R0, R0 + MOVW R0, R0 + RET /* start running in remapped area */ + +TEXT setr13(SB), $-4 + MOVW 4(FP), R1 + + MOVW CPSR, R2 + BIC $PsrMask, R2, R3 + ORR R0, R3 + MOVW R3, CPSR + + MOVW R13, R0 + MOVW R1, R13 + + MOVW R2, CPSR + RET + +TEXT vectors(SB), $-4 + MOVW 0x18(R15), R15 /* reset */ + MOVW 0x18(R15), R15 /* undefined */ + MOVW 0x18(R15), R15 /* SWI */ + MOVW 0x18(R15), R15 /* prefetch abort */ + MOVW 0x18(R15), R15 /* data abort */ + MOVW 0x18(R15), R15 /* reserved */ + MOVW 0x18(R15), R15 /* IRQ */ + MOVW 0x18(R15), R15 /* FIQ */ + +TEXT vtable(SB), $-4 + WORD $_vsvc(SB) /* reset, in svc mode already */ + WORD $_vund(SB) /* undefined, switch to svc mode */ + WORD $_vsvc(SB) /* swi, in svc mode already */ + WORD $_vpab(SB) /* prefetch abort, switch to svc mode */ + WORD $_vdab(SB) /* data abort, switch to svc mode */ + WORD $_vsvc(SB) /* reserved */ + WORD $_virq(SB) /* IRQ, switch to svc mode */ + WORD $_vfiq(SB) /* FIQ, switch to svc mode */ + +TEXT _vund(SB), $-4 + MOVM.DB [R0-R3], (R13) + MOVW $PsrMund, R0 + B _vswitch + +TEXT _vsvc(SB), $-4 + MOVW.W R14, -4(R13) + MOVW CPSR, R14 + MOVW.W R14, -4(R13) + BIC $PsrMask, R14 + ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14 + MOVW R14, CPSR + MOVW $PsrMsvc, R14 + MOVW.W R14, -4(R13) + B _vsaveu + +TEXT _vpab(SB), $-4 + MOVM.DB [R0-R3], (R13) + MOVW $PsrMabt, R0 + B _vswitch + +TEXT _vdab(SB), $-4 + MOVM.DB [R0-R3], (R13) + MOVW $(PsrMabt+1), R0 + B _vswitch + +TEXT _vfiq(SB), $-4 /* FIQ */ + MOVM.DB [R0-R3], (R13) + MOVW $PsrMfiq, R0 + B _vswitch + +TEXT _virq(SB), $-4 /* IRQ */ + MOVM.DB [R0-R3], (R13) + MOVW $PsrMirq, R0 + +_vswitch: /* switch to svc mode */ + MOVW SPSR, R1 + MOVW R14, R2 + MOVW R13, R3 + + MOVW CPSR, R14 + BIC $PsrMask, R14 + ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14 + MOVW R14, CPSR + + MOVM.DB.W [R0-R2], (R13) + MOVM.DB (R3), [R0-R3] + +_vsaveu: /* Save Registers */ + MOVW.W R14, -4(R13) /* save link */ + MCR CpMMU, 0, R0, C(0), C(0), 0 + + SUB $8, R13 + MOVM.DB.W [R0-R12], (R13) + + MOVW R0, R0 /* gratuitous noop */ + + MOVW $setR12(SB), R12 /* static base (SB) */ + MOVW R13, R0 /* argument is ureg */ + SUB $8, R13 /* space for arg+lnk*/ + BL trap(SB) + +_vrfe: /* Restore Regs */ + MOVW CPSR, R0 /* splhi on return */ + ORR $(PsrDirq|PsrDfiq), R0, R1 + MOVW R1, CPSR + ADD $(8+4*15), R13 /* [r0-R14]+argument+link */ + MOVW (R13), R14 /* restore link */ + MOVW 8(R13), R0 + MOVW R0, SPSR + MOVM.DB.S (R13), [R0-R14] /* restore user registers */ + MOVW R0, R0 /* gratuitous nop */ + ADD $12, R13 /* skip saved link+type+SPSR*/ + RFE /* MOVM.IA.S.W (R13), [R15] */ + +TEXT splhi(SB), $-4 + MOVW CPSR, R0 + ORR $(PsrDirq), R0, R1 + MOVW R1, CPSR + MOVW $(MACHADDR), R6 + MOVW R14, (R6) /* m->splpc */ + RET + +TEXT spllo(SB), $-4 + MOVW CPSR, R0 + BIC $(PsrDirq|PsrDfiq), R0, R1 + MOVW R1, CPSR + RET + +TEXT splx(SB), $-4 + MOVW $(MACHADDR), R6 + MOVW R14, (R6) /* m->splpc */ + +TEXT splxpc(SB), $-4 + MOVW R0, R1 + MOVW CPSR, R0 + MOVW R1, CPSR + RET + +TEXT spldone(SB), $-4 + RET + +TEXT islo(SB), $-4 + MOVW CPSR, R0 + AND $(PsrDirq), R0 + EOR $(PsrDirq), R0 + RET + +TEXT splfhi(SB), $-4 + MOVW CPSR, R0 + ORR $(PsrDfiq|PsrDirq), R0, R1 + MOVW R1, CPSR + RET + +TEXT splflo(SB), $-4 + MOVW CPSR, R0 + BIC $(PsrDfiq), R0, R1 + MOVW R1, CPSR + RET + +TEXT getcpsr(SB), $-4 + MOVW CPSR, R0 + RET + +TEXT getspsr(SB), $-4 + MOVW SPSR, R0 + RET + +TEXT getcallerpc(SB), $-4 + MOVW 0(R13), R0 + RET + +TEXT _tas(SB), $-4 + MOVW R0, R1 + MOVW $0xDEADDEAD, R2 + SWPW R2, (R1), R0 + RET + +TEXT setlabel(SB), $-4 + MOVW R13, 0(R0) /* sp */ + MOVW R14, 4(R0) /* pc */ + MOVW $0, R0 + RET + +TEXT gotolabel(SB), $-4 + MOVW 0(R0), R13 /* sp */ + MOVW 4(R0), R14 /* pc */ + MOVW $1, R0 + RET + +/* + * flush the whole icache + */ +TEXT icflushall(SB), $-4 + MCR CpMMU, 0, R0, C(CpCacheCtl), C(5), 0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + RET + +/* + * write back whole data cache and drain write buffer + */ +TEXT dcflushall(SB), $-4 +_dcflushall: + MOVW $(DCFADDR), R0 + ADD $8192, R0, R1 +dcflushall1: + MOVW.P CACHELINESZ(R0), R2 + CMP R1,R0 + BNE dcflushall1 + MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4 /* drain write buffer */ + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + RET + +/* + * write back a given region and drain write buffer + */ +TEXT dcflush(SB), $-4 + MOVW 4(FP), R1 + CMP $(4*1024), R1 + BGE _dcflushall + ADD R0, R1 + BIC $(CACHELINESZ-1), R0 +dcflush1: + MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 1 /* clean entry */ + ADD $CACHELINESZ, R0 + CMP R1, R0 + BLO dcflush1 + MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4 /* drain write buffer */ + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + RET + +/* + * write back mini data cache + */ +TEXT minidcflush(SB), $-4 + MOVW $(MCFADDR), R0 + ADD $(16*CACHELINESZ), R0, R1 + +wbbflush: + MOVW.P CACHELINESZ(R0), R2 + CMP R1,R0 + BNE wbbflush + MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4 /* drain write buffer */ + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + RET + +/* + * invalidate data caches (main and mini) + */ +TEXT dcinval(SB), $-4 + MCR CpMMU, 0, R0, C(CpCacheCtl), C(6), 0 + RET + +/* for devboot */ +TEXT gotopc(SB), $-4 + MOVW R0, R1 + MOVW $0, R0 + MOVW R1, PC + RET + +/* + * See page 9-26 of the SA1110 developer's manual. + * trap copies this to a cache-aligned area. + */ +TEXT _idlemode(SB), $-4 + MOVW $UCDRAMZERO, R1 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + /* the following must be on a cache line boundary */ + MCR CpPWR, 0, R0, C(CpTest), C(0x2), 2 /* disable clock switching */ + MOVW (R1), R0 /* non-cacheable memory read */ + MCR CpPWR, 0, R0, C(CpTest), C(0x8), 2 + MCR CpPWR, 0, R0, C(CpTest), C(0x2), 1 /* enable clock switching */ + RET + +/* + * the following code is considerably modified from the + * sleep code by nemo@gsyc.escet.urjc.es for Plan 9, but that's + * where it started. in particular, there's no need to save regs in all modes, + * since here we're called from kernel main level (a kproc) so nothing is live; + * the only regs needed are the various R13s, but we have trap restore them on resume. + * similarly there's no need to save SPSR, CPSR, etc. (even CpPID isn't really needed here). + */ + +#define MDREFR_k1db2 (1<<22) +#define MDREFR_slfrsh (1<<31) /* self refresh */ +#define MDREFR_e1pin (1<<20) +#define MSC_rt ((3<<16)|(3<<0)) +#define MDCFNG_de ((3<<16)|(3<<0)) /* dram enable, banks (3 2), (1 0) */ + +TEXT suspenditall(SB), $-4 + MOVW.W R14, -4(R13) + /* push mmu state on stack */ + MRC CpMMU, 0, R1, C(CpDAC), C(0) + MRC CpMMU, 0, R2, C(CpTTB), C(0) + MRC CpMMU, 0, R3, C(CpPID), C(0) + MRC CpMMU, 0, R4, C(CpControl), C(0) + MOVM.DB.W [R1-R4], (R13) + /* if pspr by convention held a stack pointer pointing to a pc we wouldn't need power_state */ + MOVW R13, power_state+0(SB) + + BL dcflushall(SB) + /* don't write DRAM after this */ + + MOVW $PHYSPOWER, R3 + + /* put resume address in scratchpad for boot loader */ + MOVW $power_resume+0(SB), R2 + MOVW R2, 0x8(R3) /* pspr */ + + /* disable clock switching */ + MCR CpPWR, 0, R1, C(CpTest), C(0x2), 2 + + /* adjust mem timing first to avoid processor bug causing hang */ + MOVW $MDCNFG, R5 + MOVW 0x1c(R5), R2 + ORR $(MDREFR_k1db2), R2 + MOVW R2, 0x1c(R5) + + /* set PLL to lower speed w/ delay */ + MOVW $(120*206),R0 +l11: SUB $1,R0 + BGT l11 + MOVW $0, R2 + MOVW R2, 0x14(R3) /* ppcr */ + MOVW $(120*206),R0 +l12: SUB $1,R0 + BGT l12 + + /* + * SA1110 fix for various suspend bugs in pre-B4 chips (err. 14-16, 18): + * set up register values here for use in code below that is at most + * one cache line (32 bytes) long, to run without DRAM. + */ + /* 1. clear RT in MSCx (R1, R7, R8) without changing other bits */ + MOVW 0x10(R5), R1 /* MSC0 */ + BIC $(MSC_rt), R1 + MOVW 0x14(R5), R7 /* MSC1 */ + BIC $(MSC_rt), R7 + MOVW 0x2c(R5), R8 /* MSC2 */ + BIC $(MSC_rt), R8 + /* 2. clear DRI0-11 in MDREFR (R4) without changing other bits */ + MOVW 0x1c(R5), R4 + BIC $(0xfff0), R4 + /* 3. set SLFRSH in MDREFR (R6) without changing other bits */ + ORR $(MDREFR_slfrsh), R4, R6 + /* 4. clear DE in MDCNFG (R9), and any other bits desired */ + MOVW 0x0(R5), R9 + BIC $(MDCFNG_de), R9 + /* 5. clear SLFRSH and E1PIN (R10), without changing other bits */ + BIC $(MDREFR_slfrsh), R4, R10 + BIC $(MDREFR_e1pin), R10 + /* 6. force sleep mode in PMCR (R2) */ + MOVW $1,R2 + MOVW suspendcode+0(SB), R0 + B (R0) /* off to do it */ + +/* + * the following is copied by trap.c to a cache-aligned area (suspendcode), + * so that it can all run during disabling of DRAM + */ +TEXT _suspendcode(SB), $-4 + /* 1: clear RT field of all MSCx registers */ + MOVW R1, 0x10(R5) + MOVW R7, 0x14(R5) + MOVW R8, 0x2c(R5) + /* 2: clear DRI field in MDREFR */ + MOVW R4, 0x1c(R5) + /* 3: set SLFRSH bit in MDREFR */ + MOVW R6, 0x1c(R5) + /* 4: clear DE bits in MDCFNG */ + MOVW R9, 0x0(R5) + /* 5: clear SLFRSH and E1PIN in MDREFR */ + MOVW R10, 0x1c(R5) + /* 6: suspend request */ + MOVW R2, 0x0(R3) /* pmcr */ + B 0(PC) /* wait for it */ + +/* + * The boot loader comes here after the resume. + */ +TEXT power_resume(SB), $-4 + MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R0 + MOVW R0, CPSR /* svc mode, interrupts off */ + MOVW $setR12(SB), R12 + + /* flush caches */ + MCR CpMMU, 0, R0, C(CpCacheCtl), C(7), 0 + /* drain prefetch */ + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + /* drain write buffer */ + MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4 + /* flush tlb */ + MCR CpMMU, 0, R0, C(CpTLBops), C(7) + + /* restore state */ + MOVW power_state+0(SB), R13 + MOVM.IA.W (R13), [R1-R4] + MOVW.P 4(R13), R14 + + MCR CpMMU, 0, R1, C(CpDAC), C(0x0) + MCR CpMMU, 0, R2, C(CpTTB), C(0x0) + MCR CpMMU, 0, R3, C(CpPID), C(0x0) + MCR CpMMU, 0, R4, C(CpControl), C(0x0) /* enable cache and mmu */ + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + /* flush i&d caches */ + MCR CpMMU, 0, R0, C(CpCacheCtl), C(7) + /* flush tlb */ + MCR CpMMU, 0, R0, C(CpTLBops), C(7) + /* drain prefetch */ + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + MOVW R0,R0 + /* enable clock switching */ + MCR CpPWR, 0, R1, C(CpTest), C(1), 2 + RET + + GLOBL power_state+0(SB), $4 + +/* for debugging sleep code: */ +TEXT fastreset(SB), $-4 + MOVW $PHYSRESET, R7 + MOVW $1, R1 + MOVW R1, (R7) + RET diff --git a/os/sa1110/l3gpio.c b/os/sa1110/l3gpio.c new file mode 100644 index 00000000..8f162fe6 --- /dev/null +++ b/os/sa1110/l3gpio.c @@ -0,0 +1,246 @@ +/* + * L3 emulation using GPIO pins + * + * from the Linux sa1100-uda1341.c, + * Copyright (c) 2000 Nicolas Pitre + * Portions are Copyright (C) 2000 Lernout & Hauspie Speech Products, N.V. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * Modified by Vita Nuova 2001 + * + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +/* + * GPIO based L3 bus support. + * + * This provides control of Philips L3 type devices. + * GPIO lines are used for clock, data and mode pins. + * + * Note: The L3 pins are shared with I2C devices. This should not present + * any problems as long as an I2C start sequence is not generated. This is + * defined as a 1->0 transition on the data lines when the clock is high. + * It is critical this code only allow data transitions when the clock + * is low. This is always legal in L3. + * + * The IIC interface requires the clock and data pin to be LOW when idle. We + * must make sure we leave them in this state. + * + * It appears the read data is generated on the falling edge of the clock + * and should be held stable during the clock high time. + */ + +/* + * L3 setup and hold times (expressed in us) + */ +enum { + L3DataSetupTime = 1, /* 190 ns */ + L3DataHoldTime = 1, /* 30 ns */ + L3ModeSetupTime = 1, /* 190 ns */ + L3ModeHoldTime = 1, /* 190 ns */ + L3ClockHighTime = 1, /* 250 ns (min is 64*fs, 35us @ 44.1 Khz) */ + L3ClockLowTime = 1, /* 250 ns (min is 64*fs, 35us @ 44.1 Khz) */ + L3HaltTime = 1, /* 190 ns */ +}; + +/* + * Grab control of the IIC/L3 shared pins + */ +static void +L3acquirepins(void) +{ + GpioReg *g = GPIOREG; + int s; + + s = splhi(); + g->gpsr = (L3Mode | L3Clock | L3Data); + g->gpdr |= (L3Mode | L3Clock | L3Data); + splx(s); +// microdelay(2); +} + +/* + * Release control of the IIC/L3 shared pins + */ +static void +L3releasepins(void) +{ + GpioReg *g = GPIOREG; + int s; + + s = splhi(); + g->gpdr &= ~(L3Mode | L3Clock | L3Data); + splx(s); +} + +/* + * Initialize the interface + */ +void +L3init(void) +{ + GpioReg *g = GPIOREG; + int s; + + s = splhi(); + g->gafr &= ~(L3Data | L3Clock | L3Mode); + splx(s); + L3releasepins(); +} + +/* + * Send a byte. The mode line is set or pulsed based on the mode sequence + * count. The mode line is high on entry and exit. The mod line is pulsed + * before the second data byte and before ech byte thereafter. + */ +static void +L3sendbyte(int data, int mode) +{ + int i; + GpioReg *g = GPIOREG; + + switch(mode) { + case 0: /* Address mode */ + g->gpcr = L3Mode; + break; + case 1: /* First data byte */ + break; + default: /* Subsequent bytes */ + g->gpcr = L3Mode; + microdelay(L3HaltTime); + g->gpsr = L3Mode; + break; + } + + microdelay(L3ModeSetupTime); + + for (i = 0; i < 8; i++){ + microdelay(2); + /* + * Send a bit. The clock is high on entry and on exit. Data is sent only + * when the clock is low (I2C compatibility). + */ + g->gpcr = L3Clock; + + if (data & (1<gpsr = L3Data; + else + g->gpcr = L3Data; + + /* Assumes L3DataSetupTime < L3ClockLowTime */ + microdelay(L3ClockLowTime); + + g->gpsr = L3Clock; + microdelay(L3ClockHighTime); + } + + if (mode == 0) /* Address mode */ + g->gpsr = L3Mode; + + microdelay(L3ModeHoldTime); + +} + +/* + * Get a byte. The mode line is set or pulsed based on the mode sequence + * count. The mode line is high on entry and exit. The mod line is pulsed + * before the second data byte and before each byte thereafter. This + * function is never valid with mode == 0 (address cycle) as the address + * is always sent on the bus, not read. + */ +static int +L3getbyte(int mode) +{ + int data = 0; + int i; + GpioReg *g = GPIOREG; + + switch(mode) { + case 0: /* Address mode - never valid */ + break; + case 1: /* First data byte */ + break; + default: /* Subsequent bytes */ + g->gpcr = L3Mode; + microdelay(L3HaltTime); + g->gpsr = L3Mode; + break; + } + + microdelay(L3ModeSetupTime); + + for (i = 0; i < 8; i++){ + /* + * Get a bit. The clock is high on entry and on exit. Data is read after + * the clock low time has expired. + */ + g->gpcr = L3Clock; + microdelay(L3ClockLowTime); + + if(g->gplr & L3Data) + data |= 1<gpsr = L3Clock; + microdelay(L3ClockHighTime); + } + + microdelay(L3ModeHoldTime); + + return data; +} + +/* + * Write data to a device on the L3 bus. The address is passed as well as + * the data and length. The length written is returned. The register space + * is encoded in the address (low two bits are set and device address is + * in the upper 6 bits). + */ +int +L3write(int addr, void *data, int len) +{ + int mode = 0; + int bytes = len; + uchar *b; + + L3acquirepins(); + L3sendbyte(addr, mode++); + for(b = data; --len >= 0;) + L3sendbyte(*b++, mode++); + L3releasepins(); + + return bytes; +} + +/* + * Read data from a device on the L3 bus. The address is passed as well as + * the data and length. The length read is returned. The register space + * is encoded in the address (low two bits are set and device address is + * in the upper 6 bits). + */ +int +L3read(int addr, void *data, int len) +{ + int mode = 0; + int bytes = len; + uchar *b; + int s; + + L3acquirepins(); + L3sendbyte(addr, mode++); + s = splhi(); + GPIOREG->gpdr &= ~(L3Data); + splx(s); + for(b = data; --len >= 0;) + *b++ = L3getbyte(mode++); + L3releasepins(); + + return bytes; +} diff --git a/os/sa1110/mmu.c b/os/sa1110/mmu.c new file mode 100644 index 00000000..1dab239a --- /dev/null +++ b/os/sa1110/mmu.c @@ -0,0 +1,140 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +/* + * return physical address corresponding to a given virtual address, + * or 0 if there is no such address + */ +ulong +va2pa(void *v) +{ + int idx; + ulong pte, ste, *ttb; + + idx = MmuL1x((ulong)v); + ttb = (ulong*)KTTB; + ste = ttb[idx]; + switch(ste & MmuL1type) { + case MmuL1section: + return MmuSBA(ste)|((ulong)v & 0x000fffff); + case MmuL1page: + pte = ((ulong *)MmuPTBA(ste))[MmuL2x((ulong)v)]; + switch(pte & 3) { + case MmuL2large: + return (pte & 0xffff0000)|((ulong)v & 0x0000ffff); + case MmuL2small: + return (pte & 0xfffff000)|((ulong)v & 0x00000fff); + } + } + return 0; +} + +enum { + SectionPages = MmuSection/MmuSmallPage, + PtAlign = 1<<10, + + MINICACHED = 0x10000000, +}; + +/* for debugging */ +void +prs(char *s) +{ + for(; *s; s++) + uartputc(*s); +} + +void +pr16(ulong n) +{ + int i; + + for(i=28; i>=0; i-=4) + uartputc("0123456789ABCDEF"[(n>>i)&0xF]); +} + +void* +mmuphysmap(ulong phys, ulong) +{ + ulong *ttb; + void *va; + + ttb = (ulong*)KTTB; + va = KADDR(phys); + ttb[MmuL1x((ulong)va)] = phys | 0xC10 | MmuL1section; + return va; +} + +/* + * Set a 1-1 map of virtual to physical memory, except: + * doubly-map page0 at the alternative interrupt vector address, + * doubly-map physical memory at KZERO+256*MB as uncached but buffered, and + * disable access to 0 (nil pointers). + */ +void +mmuinit(void) +{ + int i; + ulong *ttb, *ptable, va; + + ttb = (ulong*)KTTB; + for(i=0; i> 31) & 1) +#define COND_Z(psr) (((psr) >> 30) & 1) +#define COND_C(psr) (((psr) >> 29) & 1) +#define COND_V(psr) (((psr) >> 28) & 1) +#define REG(i, a, b) (((i) & BITS((a), (b))) >> (a)) +#define REGVAL(ur, r) (*((ulong*)(ur) + (r))) +#define LSR(v, s) ((ulong)(v) >> (s)) +#define ASR(v, s) ((long)(v) >> (s)) +#define ROR(v, s) (LSR((v), (s)) | (((v) & ((1 << (s))-1)) << (32 - (s)))) + +void +machbreakinit(void) +{ + portbreakinit(); + breakhandler = breakhit; +} + +Instr +machinstr(ulong addr) +{ + if (addr < KTZERO) + error(Ebadarg); + return *(Instr*)addr; +} + +void +machbreakset(ulong addr) +{ + if (addr < KTZERO) + error(Ebadarg); + *(Instr*)addr = BREAK; + segflush((void*)addr, sizeof(Instr)); +} + +void +machbreakclear(ulong addr, Instr i) +{ + if (addr < KTZERO) + error(Ebadarg); + *(Instr*)addr = i; + segflush((void*)addr, sizeof(Instr)); +} + +// +// Return the address of the instruction that will be executed after the +// instruction at address ur->pc. +// +// This means decoding the instruction at ur->pc. +// +// In the simple case, the PC will simply be the address of the next +// sequential instruction following ur->pc. +// +// In the complex case, the instruction is a branch of some sort, so the +// value of the PC after the instruction must be computed by decoding +// and simulating the instruction enough to determine the PC. +// + +ulong +machnextaddr(Ureg *ur) +{ + Instr i; + i = machinstr(ur->pc); + switch(instrtype(i)) { + case IADD: return iadd(ur,i); + case IBRANCH: return ibranch(ur,i); + case ILDM: return ildm(ur,i); + case ILDR: return ildr(ur,i); + case IMOV: return imov(ur,i); + + case IADC: + case IAND: + case IBIC: + case IEOR: + case ILDRT: + case IMRS: + case IMVN: + case IORR: + case IRSB: + case IRSC: + case ISBC: + case ISUB: + // XXX - Tad: unimplemented + // + // any of these instructions could possibly have the + // PC as Rd. Eventually, these should all be + // checked just like the others. + default: + return ur->pc+4; + } + + return 0; +} + +static int +instrtype(Instr i) +{ + if(i & BITS(26,27) == 0) { + switch((i >> 21) & 0xF) { + case 0: return IAND; + case 1: return IEOR; + case 2: return ISUB; + case 3: return IRSB; + case 4: return IADD; + case 5: return IADC; + case 6: return ISBC; + case 7: return IRSC; + case 0xD: return IMOV; + case 0xC: return IORR; + case 0xE: return IBIC; + case 0xF: return IMVN; + } + if(((i & BIT(25)|BITS(23,24)|BITS(20,21))) >> 20 == 0x10) + return IMRS; + return 0; + } + + if(((i & BITS(27,25)|BIT(20)) >> 20) == 0x81) return ILDM; + if(((i & BITS(26,27)|BIT(22)|BIT(20)) >> 20) == 0x41) return ILDR; + if(((i & BITS(25,27)) >> 25) == 5) return IBRANCH; + + return 0; +} + +static ulong +iadd(Ureg *ur, Instr i) +{ + ulong Rd = REG(i, 12, 15); + ulong Rn = REG(i, 16, 19); + + if(Rd != 15 || !condpass(i, ur->psr)) + return ur->pc+4; + + return REGVAL(ur, Rn) + shifterval(ur, i); +} + +static ulong +ibranch(Ureg *ur, Instr i) +{ + if(!condpass(i, ur->psr)) + return ur->pc+4; + return ur->pc + ((signed long)(i << 8) >> 6) + 8; +} + +static ulong +ildm(Ureg *ur, Instr i) +{ + if((i & BIT(15)) == 0) + return ur->pc+4; + + return *(multiaddr(ur, i) + nbits(i & BITS(15, 0))); +} + +static ulong +ildr(Ureg *ur, Instr i) +{ + if(REG(i, 12, 19) != 15 || !condpass(i, ur->psr)) + return ur->pc+4; + + return *address(ur, i); +} + +static ulong +imov(Ureg *ur, Instr i) +{ + if(REG(i, 12, 15) != 15 || !condpass(i, ur->psr)) + return ur->pc+4; + + return shifterval(ur, i); +} + +static int +condpass(Instr i, ulong psr) +{ + uchar n = COND_N(psr); + uchar z = COND_Z(psr); + uchar c = COND_C(psr); + uchar v = COND_V(psr); + + switch(LSR(i,28)) { + case 0: return z; + case 1: return !z; + case 2: return c; + case 3: return !c; + case 4: return n; + case 5: return !n; + case 6: return v; + case 7: return !v; + case 8: return c && !z; + case 9: return !c || z; + case 10: return n == v; + case 11: return n != v; + case 12: return !z && (n == v); + case 13: return z && (n != v); + case 14: return 1; + case 15: return 0; + } +} + +static ulong +shifterval(Ureg *ur, Instr i) +{ + if(i & BIT(25)) { // IMMEDIATE + ulong imm = i & BITS(0,7); + ulong s = (i & BITS(8,11)) >> 7; // this contains the * 2 + return ROR(imm, s); + } else { + ulong Rm = REGVAL(ur, REG(i, 0, 3)); + ulong s = (i & BITS(7,11)) >> 7; + + switch((i & BITS(6,4)) >> 4) { + case 0: // LSL + return Rm << s; + case 1: // LSLREG + s = REGVAL(ur, s >> 1) & 0xFF; + if(s >= 32) return 0; + return Rm << s; + case 2: // LSRIMM + return LSR(Rm, s); + case 3: // LSRREG + s = REGVAL(ur, s >> 1) & 0xFF; + if(s >= 32) return 0; + return LSR(Rm, s); + case 4: // ASRIMM + if(s == 0) { + if(Rm & BIT(31) == 0) + return 0; + return 0xFFFFFFFF; + } + return ASR(Rm, s); + case 5: // ASRREG + s = REGVAL(ur, s >> 1) & 0xFF; + if(s >= 32) { + if(Rm & BIT(31) == 0) + return 0; + return 0xFFFFFFFF; + } + return ASR(Rm, s); + case 6: // RORIMM + if(s == 0) + return (COND_C(ur->psr) << 31) | LSR(Rm, 1); + return ROR(Rm, s); + case 7: // RORREG + s = REGVAL(ur, s >> 1) & 0xFF; + if(s == 0 || (s & 0xF) == 0) + return Rm; + return ROR(Rm, s & 0xF); + } + } +} + +static ulong* +address(Ureg *ur, Instr i) +{ + ulong Rn = REGVAL(ur, REG(i, 16, 19)); + + if(i & BIT(24) == 0) // POSTIDX + return (ulong*)REGVAL(ur, Rn); + if(i & BIT(25) == 0) { // OFFSET + if(i & BIT(23)) + return (ulong*)(REGVAL(ur, Rn) + (i & BITS(0, 11))); + return (ulong*)(REGVAL(ur, Rn) - (i & BITS(0, 11))); + } else { // REGOFF + ulong Rm = REGVAL(ur, REG(i, 0, 3)); + ulong index = 0; + switch(i & BITS(5,6) >> 5) { + case 0: index = Rm << ((i & BITS(7, 11)) >> 7); break; + case 1: index = LSR(Rm, ((i & BITS(7, 11)) >> 7)); break; + case 2: index = ASR(Rm, ((i & BITS(7, 11)) >> 7)); break; + case 3: + if(i & BITS(7, 11) == 0) + index = (COND_C(ur->psr) << 31) | LSR(Rm, 1); + else + index = ROR(Rm, (i & BITS(7, 11)) >> 7); + break; + } + if(i & BIT(23)) + return (ulong*)(Rn + index); + return (ulong*)(Rn - index); + } +} + +static ulong* +multiaddr(Ureg *ur, Instr i) +{ + ulong Rn = REGVAL(ur, REG(i, 16, 19)); + + switch((i >> 23) & 3) { + case 0: return (ulong*)(Rn - (nbits(i & BITS(0,15))*4)+4); + case 1: return (ulong*)Rn; + case 2: return (ulong*)(Rn - (nbits(i & BITS(0,15))*4)); + case 3: return (ulong*)(Rn + 4); + } +} + +static int +nbits(ulong v) +{ + int n = 0; + int i; + for(i = 0; i < 32; i++) { + if(v & 1) + ++n; + v = LSR(v, 1); + } + return n; +} diff --git a/os/sa1110/sa1110io.h b/os/sa1110/sa1110io.h new file mode 100644 index 00000000..53913c3d --- /dev/null +++ b/os/sa1110/sa1110io.h @@ -0,0 +1,500 @@ +typedef struct PCMconftab PCMconftab; +typedef struct PCMmap PCMmap; +typedef struct PCMslot PCMslot; + +/* + * physical device addresses are mapped to the same virtual ones, + * allowing the same addresses to be used with or without mmu. + */ + +#define PCMCIAcard(n) (PHYSPCMCIA0+((n)*PCMCIASIZE)) +#define PCMCIAIO(n) (PCMCIAcard(n)+0x0) /* I/O space */ +#define PCMCIAAttr(n) (PCMCIAcard(n)+0x8000000) /* Attribute space*/ +#define PCMCIAMem(n) (PCMCIAcard(n)+0xC000000) /* Memory space */ + +#define INTRREG ((IntrReg*)PHYSINTR) +typedef struct IntrReg IntrReg; +struct IntrReg { + ulong icip; /* IRQ pending */ + ulong icmr; /* mask */ + ulong iclr; /* level */ + ulong iccr; /* control */ + ulong icfp; /* FIQ pending */ + ulong rsvd[3]; + ulong icpr; /* pending */ +}; + +#define GPIObit(n) (n) /* GPIO Edge Detect bits */ +#define LCDbit (12) /* LCD Service Request */ +#define UDCbit (13) /* UDC Service Request */ +#define SDLCbit (14) /* SDLC Service Request */ +#define UARTbit(n) (15+((n)-1)) /* UART Service Request */ +#define HSSPbit (16) /* HSSP Service Request */ +#define MCPbit (18) /* MCP Service Request */ +#define SSPbit (19) /* SSP Serivce Request */ +#define DMAbit(chan) (20+(chan)) /* DMA channel Request */ +#define OSTimerbit(n) (26+(n)) /* OS Timer Request */ +#define RTCticbit (30) /* One Hz tic occured */ +#define RTCalarmbit (31) /* RTC = alarm register */ +#define MaxIRQbit 31 /* Maximum IRQ */ +#define MaxGPIObit 27 /* Maximum GPIO */ + +#define GPIOREG ((GpioReg*)PHYSGPIO) +typedef struct GpioReg GpioReg; +struct GpioReg { + ulong gplr; + ulong gpdr; + ulong gpsr; + ulong gpcr; + ulong grer; + ulong gfer; + ulong gedr; + ulong gafr; +}; + +enum { + /* GPIO alternative functions if gafr bit set (see table on page 9-9) */ + GPIO_32KHZ_OUT_o = 1<<27, /* raw 32.768kHz oscillator output */ + GPIO_RCLK_OUT_o = 1<<26, /* internal clock/2 (must also set TUCR) */ + GPIO_RTC_clock_o = 1<<25, /* real time clock out */ + GPIO_TREQB_i = 1<<23, /* TIC request B */ + GPIO_TREQA_i = 1<<22, /* TIC request A (or MBREQ) */ + GPIO_TICK_ACK_o = 1<<21, /* TIC ack (or MBGNT), when output */ + GPIO_MCP_CLK_i = 1<<21, /* MCP clock in, when input */ + GPIO_UART_SCLK3_i = 1<<20, /* serial port 3 UART sample clock input */ + GPIO_SSP_CLK_i = 1<<19, /* serial port 2 SSP sample clock input */ + GPIO_UART_SCLK1_i = 1<<18, /* serial port 1 UART sample clock input */ + GPIO_GPCLK_OUT_o = 1<<16, /* serial port 1 general-purpose clock out */ + GPIO_UART_RXD_i = 1<<15, /* serial port 1 UART receive */ + GPIO_UART_TXD_o = 1<<14, /* serial port 1 UART transmit */ + GPIO_SSP_SFRM_o = 1<<13, /* SSP frame clock out */ + GPIO_SSP_SCLK_o = 1<<12, /* SSP serial clock out */ + GPIO_SSP_RXD_i = 1<<11, /* SSP receive */ + GPIO_SSP_TXD_o = 1<<10, /* SSP transmit */ + GPIO_LDD8_15_o = 0xFF<<2, /* high-order LCD data (bits 8-15) */ + GPIO_LDD15_o = 1<<9, + GPIO_LDD14_o = 1<<8, + GPIO_LDD13_o = 1<<7, + GPIO_LDD12_o = 1<<6, + GPIO_LDD11_o = 1<<5, + GPIO_LDD10_o = 1<<4, + GPIO_LDD9_o = 1<<3, + GPIO_LDD8_o = 1<<2, +}; + +#define RTCREG ((RtcReg*)PHYSRTC) +typedef struct RtcReg RtcReg; +struct RtcReg { + ulong rtar; /* alarm */ + ulong rcnr; /* count */ + ulong rttr; /* trim */ + ulong rsvd; + ulong rtsr; /* status */ +}; + +#define OSTMRREG ((OstmrReg*)PHYSOSTMR) +typedef struct OstmrReg OstmrReg; +struct OstmrReg { + ulong osmr[4]; /* match */ + ulong oscr; /* counter */ + ulong ossr; /* status */ + ulong ower; /* watchdog */ + ulong oier; /* interrupt enable */ +}; + +#define PMGRREG ((PmgrReg*)PHYSPOWER) +typedef struct PmgrReg PmgrReg; +struct PmgrReg { + ulong pmcr; /* ctl register */ + ulong pssr; /* sleep status */ + ulong pspr; /* scratch pad */ + ulong pwer; /* wakeup enable */ + ulong pcfr; /* general conf */ + ulong ppcr; /* PLL configuration */ + ulong pgsr; /* GPIO sleep state */ + ulong posr; /* oscillator status */ +}; + +enum +{ + /* page 9-35 to 40 */ + PCFR_opde = 1<<0, /* oscillator powers down in sleep */ + PCFR_fp = 1<<1, /* float pcmcia */ + PCFR_fs = 1<<2, /* float static memory */ + PCFR_fo = 1<<3, /* force 32k oscillator on */ + + PWER_rtc = 1<<31, /* wakeup by RTC alarm */ + + PSSR_sss = 1<<0, /* software sleep status */ + PSSR_bfs = 1<<1, /* battery fault status */ + PSSR_vfs = 1<<2, /* VDD fault status */ + PSSR_dh = 1<<3, /* DRAM control held */ + PSSR_ph = 1<<4, /* peripheral control hold */ +}; + +#define RESETREG ((ResetReg*)PHYSRESET) +typedef struct ResetReg ResetReg; +struct ResetReg { + ulong rsrr; /* software reset */ + ulong rcsr; /* status */ + ulong tucr; /* reserved for test */ +}; + +#define MEMCFGREG ((MemcfgReg*)PHYSMEMCFG) +typedef struct MemcfgReg MemcfgReg; +struct MemcfgReg { + ulong mdcnfg; /* DRAM config */ + ulong mdcas0[3]; /* dram banks 0/1 */ + ulong msc0; /* static memory or devices */ + ulong msc1; + ulong mecr; /* expansion bus (pcmcia, CF) */ + ulong mdrefr; /* dram refresh */ + ulong mdcas2[3]; /* dram banks 2/3 */ + ulong msc2; /* static memory or devices */ + ulong smcnfg; /* SMROM config */ +}; + +#define DMAREG(n) ((DmaReg*)(PHYSDMA+0x20*(n))) +typedef struct DmaReg DmaReg; +struct DmaReg { + ulong ddar; /* DMA device address */ + ulong dcsr_s; /* set */ + ulong dcsr_c; /* clear */ + ulong dcsr; /* read */ + struct { + ulong start; + ulong count; + } buf[2]; +}; + +#define LCDREG ((LcdReg*)PHYSLCD) +typedef struct LcdReg LcdReg; +struct LcdReg { + ulong lccr0; /* control 0 */ + ulong lcsr; /* status */ + ulong rsvd[2]; + ulong dbar1; /* DMA chan 1, base */ + ulong dcar1; /* DMA chan 1, count */ + ulong dbar2; /* DMA chan 2, base */ + ulong dcar2; /* DMA chan 2, count */ + ulong lccr1; /* control 1 */ + ulong lccr2; /* control 2 */ + ulong lccr3; /* control 3 */ +}; + +/* Serial devices: + * 0 USB Serial Port 0 + * 1 UART Serial Port 1 + * 2 SDLC " + * 3 UART Serial Port 2 (eia1) + * 4 ICP/HSSP " + * 5 ICP/UART Serial Port 3 (eia0) + * 6 MPC Serial Port 4 + * 7 SSP " + */ + +#define USBREG ((UsbReg*)PHYSUSB) +typedef struct UsbReg UsbReg; +struct UsbReg { + ulong udccr; /* control */ + ulong udcar; /* address */ + ulong udcomp; /* out max packet */ + ulong udcimp; /* in max packet */ + ulong udccs0; /* endpoint 0 control/status */ + ulong udccs1; /* endpoint 1(out) control/status */ + ulong udccs2; /* endpoint 2(int) control/status */ + ulong udcd0; /* endpoint 0 data register */ + ulong udcwc; /* endpoint 0 write control register */ + ulong rsvd1; + ulong udcdr; /* transmit/receive data register (FIFOs) */ + ulong rsvd2; + ulong dcsr; /* status/interrupt register */ +}; + +#define GPCLKREG ((GpclkReg*)PHYSGPCLK) +typedef struct GpclkReg GpclkReg; +struct GpclkReg { + ulong gpclkr0; + ulong rsvd[2]; + ulong gpclkr1; + ulong gpclkr2; +}; + +/* UARTs 1, 2, 3 are mapped to serial devices 1, 3, and 5 */ +#define UARTREG(n) ((UartReg*)(PHYSSERIAL(2*(n)-1))) +typedef struct UartReg UartReg; +struct UartReg { + ulong utcr0; /* control 0 (bits, parity, clocks) */ + ulong utcr1; /* control 1 (bps div hi) */ + ulong utcr2; /* control 2 (bps div lo) */ + ulong utcr3; /* control 3 */ + ulong utcr4; /* control 4 (only serial port 2 (device 3)) */ + ulong utdr; /* data */ + ulong rsvd; + ulong utsr0; /* status 0 */ + ulong utsr1; /* status 1 */ +}; + +#define HSSPREG ((HsspReg*)(0x80040060)) +typedef struct HsspReg HsspReg; +struct HsspReg { + ulong hscr0; /* control 0 */ + ulong hscr1; /* control 1 */ + ulong rsvd1; + ulong hsdr; /* data */ + ulong rsvd2; + ulong hssr0; /* status 0 */ + ulong hssr1; /* status 1 */ +}; + +#define MCPREG ((McpReg*)(PHYSMCP)) +typedef struct McpReg McpReg; +struct McpReg { + ulong mccr; + ulong rsvd1; + ulong mcdr0; + ulong mcdr1; + ulong mcdr2; + ulong rsvd2; + ulong mcsr; +}; + +enum { + MCCR_M_LBM= 0x800000, + MCCR_M_ARM= 0x400000, + MCCR_M_ATM= 0x200000, + MCCR_M_TRM= 0x100000, + MCCR_M_TTM= 0x080000, + MCCR_M_ADM= 0x040000, + MCCR_M_ECS= 0x020000, + MCCR_M_MCE= 0x010000, + MCCR_V_TSD= 8, + MCCR_V_ASD= 0, + + MCDR2_M_nRW= 0x010000, + MCDR2_V_RN= 17, + + MCSR_M_TCE= 0x8000, + MCSR_M_ACE= 0X4000, + MCSR_M_CRC= 0x2000, + MCSR_M_CWC= 0x1000, + MCSR_M_TNE= 0x0800, + MCSR_M_TNF= 0x0400, + MCSR_M_ANE= 0x0200, + MCSR_M_ANF= 0x0100, + MCSR_M_TRO= 0x0080, + MCSR_M_TTU= 0x0040, + MCSR_M_ARO= 0x0020, + MCSR_M_ATU= 0x0010, + MCSR_M_TRS= 0x0008, + MCSR_M_TTS= 0x0004, + MCSR_M_ARS= 0x0002, + MCSR_M_ATS= 0x0001, +}; + +#define SSPREG ((SspReg*)PHYSSSP) +typedef struct SspReg SspReg; +struct SspReg { + ulong sscr0; /* control 0 */ + ulong sscr1; /* control 1 */ + ulong rsvd1; + ulong ssdr; /* data */ + ulong rsvd2; + ulong sssr; /* status */ +}; + +enum { + SSCR0_V_SCR= 0x08, + SSCR0_V_SSE= 0x07, + SSCR0_V_ECS= 0x06, + SSCR0_V_FRF= 0x04, + + SSPCR0_M_DSS= 0x0000000F, + SSPCR0_M_FRF= 0x00000030, + SSPCR0_M_SSE= 0x00000080, + SSPCR0_M_SCR= 0x0000FF00, + SSPCR0_V_DSS= 0, + SSPCR0_V_FRF= 4, + SSPCR0_V_SSE= 7, + SSPCR0_V_SCR= 8, + + SSPCR1_M_RIM= 0x00000001, + SSPCR1_M_TIN= 0x00000002, + SSPCR1_M_LBM= 0x00000004, + SSPCR1_V_RIM= 0, + SSPCR1_V_TIN= 1, + SSPCR1_V_LBM= 2, + + SSPSR_M_TNF= 0x00000002, + SSPSR_M_RNE= 0x00000004, + SSPSR_M_BSY= 0x00000008, + SSPSR_M_TFS= 0x00000010, + SSPSR_M_RFS= 0x00000020, + SSPSR_M_ROR= 0x00000040, + SSPSR_V_TNF= 1, + SSPSR_V_RNE= 2, + SSPSR_V_BSY= 3, + SSPSR_V_TFS= 4, + SSPSR_V_RFS= 5, + SSPSR_V_ROR= 6, +}; + +#define PPCREG ((PpcReg*)PHYSPPC) +typedef struct PpcReg PpcReg; +struct PpcReg { + ulong ppdr; /* pin direction */ + ulong ppsr; /* pin state */ + ulong ppar; /* pin assign */ + ulong psdr; /* sleep mode */ + ulong ppfr; /* pin flag reg */ + uchar rsvd[0x1c]; /* pad to 0x30 */ + ulong mccr1; /* MCP control register 1 */ +}; + +enum { + /* ppdr and ppsr: =0, pin is general-purpose input; =1, pin is general-purpose output (11-168)*/ + PPC_LDD0_7= 0xFF<<0, /* LCD data pins 0 to 7 */ + PPC_L_PCLK= 1<<8, /* LCD pixel clock */ + PPC_L_LCLK= 1<<9, /* LCD line clock */ + PPC_L_FCLK= 1<<10, /* LCD frame clock */ + PPC_L_BIAS= 1<<11, /* LCD AC bias */ + PPC_TXD1= 1<<12, /* serial port 1 UART transmit */ + PPC_RXD1= 1<<13, /* serial port 1 UART receive */ + PPC_TXD2= 1<<14, /* serial port 2 IPC transmit */ + PPC_RXD2= 1<<15, /* serial port 2 IPC receive */ + PPC_TXD3= 1<<16, /* serial port 3 UART transmit */ + PPC_RXD3= 1<<17, /* serial port 3 UART receive */ + PPC_TXD4= 1<<18, /* serial port 4 MCP/SSP transmit */ + PPC_RXD4= 1<<19, /* serial port 4 MCP/SSP receive */ + PPC_SCLK= 1<<20, /* serial port 4 MCP/SSP serial clock */ + PPC_SFRM= 1<<21, /* serial port 4 MCP/SSP frame clock */ + + PPAR_UPR= 1<<12, /* =1, serial port 1 GPCLK/UART pins reassigned */ + PPAR_SPR= 1<<18, /* =1, SSP pins reassigned */ +}; + +/* + * Irq Bus goo + */ + +enum { + BusCPU= 1, + BusGPIOfalling= 2, /* falling edge */ + BusGPIOrising = 3, /* rising edge */ + BusGPIOboth = 4, /* both edges */ + BusMAX= 4, + BUSUNKNOWN= -1, +}; + +enum { + /* DMA configuration parameters */ + + /* DMA Direction */ + DmaOUT= 0, + DmaIN= 1, + + /* dma endianess */ + DmaLittle= 0, + DmaBig= 1, + + /* dma devices */ + DmaUDC= 0, + DmaSDLC= 2, + DmaUART0= 4, + DmaHSSP= 6, + DmaUART1= 7, /* special case (is really 6) */ + DmaUART2= 8, + DmaMCPaudio= 10, + DmaMCPtelecom= 12, + DmaSSP= 14, +}; + +/* + * Interface to platform-specific PCMCIA signals, in arch*.c + */ +enum { + /* argument to pcmpin() */ + PCMready, + PCMeject, + PCMstschng, +}; + +/* + * PCMCIA structures known by both port/cis.c and the pcmcia driver + */ + +/* + * Map between ISA memory space and PCMCIA card memory space. + */ +struct PCMmap { + ulong ca; /* card address */ + ulong cea; /* card end address */ + ulong isa; /* local virtual address */ + int len; /* length of the ISA area */ + int attr; /* attribute memory */ +}; + +/* + * a PCMCIA configuration entry + */ +struct PCMconftab +{ + int index; + ushort irqs; /* legal irqs */ + uchar irqtype; + uchar bit16; /* true for 16 bit access */ + struct { + ulong start; + ulong len; + } io[16]; + int nio; + uchar vpp1; + uchar vpp2; + uchar memwait; + ulong maxwait; + ulong readywait; + ulong otherwait; +}; + +/* + * PCMCIA card slot + */ +struct PCMslot +{ + RWlock; + + Ref ref; + + long memlen; /* memory length */ + uchar slotno; /* slot number */ + void *regs; /* i/o registers */ + void *mem; /* memory */ + void *attr; /* attribute memory */ + + /* status */ + uchar occupied; /* card in the slot */ + uchar configed; /* card configured */ + uchar busy; + uchar powered; + uchar battery; + uchar wrprot; + uchar enabled; + uchar special; + uchar dsize; + + /* cis info */ + int cisread; /* set when the cis has been read */ + char verstr[512]; /* version string */ + int ncfg; /* number of configurations */ + struct { + ushort cpresent; /* config registers present */ + ulong caddr; /* relative address of config registers */ + } cfg[8]; + int nctab; /* number of config table entries */ + PCMconftab ctab[8]; + PCMconftab *def; /* default conftab */ + + /* maps are fixed */ + PCMmap memmap; + PCMmap attrmap; +}; diff --git a/os/sa1110/softcursor.c b/os/sa1110/softcursor.c new file mode 100644 index 00000000..b138cdcc --- /dev/null +++ b/os/sa1110/softcursor.c @@ -0,0 +1,365 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +#include +#include +#include + +#include "gscreen.h" + +/* + * Software cursor code: done by hand, might be better to use memimagedraw + * but that would need to be done by a process + */ + +typedef struct Cursordata Cursordata; +struct Cursordata { + Physdisplay *vd; + ulong *fb; /* screen frame buffer */ + Rectangle r; + int depth; /* depth of screen */ + int width; /* width of screen in ulongs */ + int x; + int y; + int hotx; + int hoty; + int cbwid; /* cursor byte width */ + int f; /* flags */ + int dx; + int dy; + int hidecount; + uchar data[CURSWID*CURSHGT]; + uchar mask[CURSWID*CURSHGT]; + uchar save[CURSWID*CURSHGT]; +}; + +static Cursordata *cd = nil; + +enum { + Enabled = 0x01, /* cursor is enabled */ + Drawn = 0x02, /* cursor is currently drawn */ + Bitswap = 0x10, +}; + +static Rectangle cursoroffrect; +static int cursorisoff; + +static void swcursorflush(Point); +static void swcurs_draw_or_undraw(Cursordata *); + +static void +cursorupdate0(void) +{ + int inrect, x, y; + Point m; + + m = mousexy(); + x = m.x - cd->hotx; + y = m.y - cd->hoty; + inrect = (x >= cursoroffrect.min.x && x < cursoroffrect.max.x + && y >= cursoroffrect.min.y && y < cursoroffrect.max.y); + if (cursorisoff == inrect) + return; + cursorisoff = inrect; + if (inrect) + swcurs_hide(swc); + else { + cd->hidecount = 0; + swcurs_draw_or_undraw(swc); + } + swcursorflush(m); +} + +void +cursorupdate(Rectangle r) +{ + lock(vd); + r.min.x -= 16; + r.min.y -= 16; + cursoroffrect = r; + if (vd->cursor != nil) + cursorupdate0(); + unlock(vd); +} + +void +cursorenable(void) +{ + lock(vd); + if(vd->cursor != nil) + vd->cursor->enable(swc); +// swcursorflush(mousexy()); + unlock(vd); +} + +void +cursordisable(void) +{ + + lock(vd); + if(swc != nil) { + swcurs_disable(swc); + swcursorflush(mousexy()); + } + unlock(vd); +} + +static void +swcursupdate(int oldx, int oldy, int x, int y) +{ + + if(!canlock(vd)) + return; /* if can't lock, don't wake up stuff */ + + if(x < gscreen->r.min.x) + x = gscreen->r.min.x; + if(x >= gscreen->r.max.x) + x = gscreen->r.max.x; + if(y < gscreen->r.min.y) + y = gscreen->r.min.y; + if(y >= gscreen->r.max.y) + y = gscreen->r.max.y; + if(swc != nil) { + swcurs_hide(swc); + cd->x = x; + cd->y = y; + cursorupdate0(); + swcurs_unhide(swc); + swcursorflush(oldx, oldy); + swcursorflush(x, y); + } + + unlock(vd); +} + +void +drawcursor(Drawcursor* c) +{ + Point p; + Cursor curs, *cp; + int j, i, h, bpl; + uchar *bc, *bs, *cclr, *cset; + + if(swc == nil) + return; + + /* Set the default system cursor */ + if(c == nil || c->data == nil){ + swcurs_disable(swc); + return; + } + else { + cp = &curs; + p.x = c->hotx; + p.y = c->hoty; + cp->offset = p; + bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1); + + h = (c->maxy-c->miny)/2; + if(h > 16) + h = 16; + + bc = c->data; + bs = c->data + h*bpl; + + cclr = cp->clr; + cset = cp->set; + for(i = 0; i < h; i++) { + for(j = 0; j < 2; j++) { + cclr[j] = bc[j]; + cset[j] = bs[j]; + } + bc += bpl; + bs += bpl; + cclr += 2; + cset += 2; + } + } + swcurs_load(swc, cp); + swcursorflush(mousexy()); + swcurs_enable(swc); +} + +void* +create(Physdisplay *vd) +{ + Cursordata *cd; + + swc = (Cursordata*)malloc(sizeof(Cursordata)); + cd->vd = vd; + cd->fb = vd->gscreen->data->bdata; /* or vd->fb? */ + cd->r = vd->gscreen->r; + cd->d = vd->gscreen->depth; + cd->width = vd->gscreen->width; +// cd->f = bitswap ? Bitswap : 0; + cd->f = Bitswap; /* ??? */ + cd->x = cd->y = 0; + cd->hotx = cd->hoty = 0; + cd->hidecount = 0; + return cd; +} + +void +swcurs_destroy(Cursordata *cd) +{ + swcurs_disable(cd); + free(cd); +} + +static void +swcursorflush(Point p) +{ + Rectangle r; + + /* XXX a little too paranoid here */ + r.min.x = p.x-16; + r.min.y = p.y-16; + r.max.x = p.x+17; + r.max.y = p.y+17; + flushmemscreen(r); +} + +static void +swcurs_draw_or_undraw(Cursordata *cd) +{ + uchar *p; + uchar *cs; + int w, vw; + int x1 = cd->r.min.x; + int y1 = cd->r.min.y; + int x2 = cd->r.max.x; + int y2 = cd->r.max.y; + int xp = cd->x - cd->hotx; + int yp = cd->y - cd->hoty; + int ofs; + + if(((cd->f & Enabled) && (cd->hidecount <= 0)) + == ((cd->f & Drawn) != 0)) + return; + w = cd->cbwid*BI2BY/cd->depth; + x1 = xp < x1 ? x1 : xp; + y1 = yp < y1 ? y1 : yp; + x2 = xp+w >= x2 ? x2 : xp+w; + y2 = yp+cd->dy >= y2 ? y2 : yp+cd->dy; + if(x2 <= x1 || y2 <= y1) + return; + p = (uchar*)(cd->fb + cd->width*y1) + x1*(1 << cd->d)/BI2BY; + y2 -= y1; + x2 = (x2-x1)*cd->depth/BI2BY; + vw = cd->width*BY2WD - x2; + w = cd->cbwid - x2; + ofs = cd->cbwid*(y1-yp)+(x1-xp); + cs = cd->save + ofs; + if((cd->f ^= Drawn) & Drawn) { + uchar *cm = cd->mask + ofs; + uchar *cd = cd->data + ofs; + while(y2--) { + x1 = x2; + while(x1--) { + *cs++ = *p; + *p = (*p & *cm++) ^ *cd++; + p++; + } + cs += w; + cm += w; + cd += w; + p += vw; + } + } else { + while(--y2 >= 0){ + for(x1 = x2; --x1 >= 0;) + *p++ = *cs++; + cs += w; + p += vw; + } + } +} + +static void +swcurs_hide(Cursordata *cd) +{ + ++cd->hidecount; + swcurs_draw_or_undraw(swc); +} + +static void +swcurs_unhide(Cursordata *cd) +{ + if (--cd->hidecount < 0) + cd->hidecount = 0; + swcurs_draw_or_undraw(swc); +} + +static void +swcurs_enable(Cursordata *cd) +{ + cd->f |= Enabled; + swcurs_draw_or_undraw(swc); +} + +void +swcurs_disable(Cursordata *cd) +{ + cd->f &= ~Enabled; + swcurs_draw_or_undraw(swc); +} + +static void +load(Cursordata *cd, Cursor *c) +{ + int i, k; + uchar *bc, *bs, *cd, *cm; + static uchar bdv[4] = {0,Backgnd,Foregnd,0xff}; + static uchar bmv[4] = {0xff,0,0,0xff}; + int bits = 1<depth; + uchar mask = (1<f&Bitswap) ? 8-bits : 0; + + bc = c->clr; + bs = c->set; + + swcurs_hide(swc); + cd = cd->data; + cm = cd->mask; + cd->hotx = c->offset.x; + cd->hoty = c->offset.y; + cd->dy = CURSHGT; + cd->dx = CURSWID; + cd->cbwid = CURSWID*(1<d)/BI2BY; + for(i = 0; i < CURSWID/BI2BY*CURSHGT; i++) { + uchar bcb = *bc++; + uchar bsb = *bs++; + for(k=0; k>7; + int s = z^bswp; + cdv |= (bdv[n]&mask) << s; + cmv |= (bmv[n]&mask) << s; + bcb <<= 1; + bsb <<= 1; + k++; + } + *cd++ = cdv; + *cm++ = cmv; + } + } + swcurs_unhide(swc); +} + +Physcursor softcursor = { + .name = "softcursor", + .create = create, + .enable = swenable, + .disable = swdisable, + .load = load, + .move = move, + .destroy = destroy, +}; diff --git a/os/sa1110/suspend.c b/os/sa1110/suspend.c new file mode 100644 index 00000000..873e23cb --- /dev/null +++ b/os/sa1110/suspend.c @@ -0,0 +1,161 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +/* + * Originally written by nemo@gsyc.escet.urjc.es, and + * reworked by forsyth@vitanuova.com + */ +enum { + DEBUG = 0, +}; + +/* + * TO DO: pcmcia, lcd properly + */ + +/* + * it's not clear yet whether we should do it this way, + * or using powerenable/powerdisable + */ +void +chandevpower(int up) +{ + int i; + + if(up){ + for(i=0; devtab[i] != nil; i++) + if(devtab[i]->power != nil) + devtab[i]->power(1); + }else{ + /* power down in reverse order */ + for(i=0; devtab[i] != nil; i++) + ; + while(--i >= 0) + if(devtab[i]->power != nil) + devtab[i]->power(0); + } +} + +static void +dumpitall(void) +{ + iprint("intr: icip %lux iclr %lux iccr %lux icmr %lux\n", + INTRREG->icip, + INTRREG->iclr, INTRREG->iccr, INTRREG->icmr ); + iprint("gpio: lvl %lux dir %lux, re %lux, fe %lux sts %lux alt %lux\n", + GPIOREG->gplr, + GPIOREG->gpdr, GPIOREG->grer, GPIOREG->gfer, + GPIOREG->gpsr, GPIOREG->gafr); + iprint("uart1: %lux %lux %lux\nuart3: %lux %lux %lux\n", + UARTREG(1)->utcr0, UARTREG(1)->utsr0, UARTREG(1)->utsr1, + UARTREG(3)->utcr0, UARTREG(3)->utsr0, UARTREG(3)->utsr1); + iprint("tmr: osmr %lux %lux %lux %lux oscr %lux ossr %lux oier %lux\n", + OSTMRREG->osmr[0], OSTMRREG->osmr[1], + OSTMRREG->osmr[2], OSTMRREG->osmr[3], + OSTMRREG->oscr, OSTMRREG->ossr, OSTMRREG->oier); + iprint("dram: mdcnfg %lux mdrefr %lux cas %lux %lux %lux %lux %lux %lux\n", + MEMCFGREG->mdcnfg, MEMCFGREG->mdrefr, + MEMCFGREG->mdcas0[0], MEMCFGREG->mdcas0[1],MEMCFGREG->mdcas0[2], + MEMCFGREG->mdcas2[0], MEMCFGREG->mdcas2[1],MEMCFGREG->mdcas2[2]); + iprint("dram: mdcnfg msc %lux %lux %lux mecr %lux\n", + MEMCFGREG->msc0, MEMCFGREG->msc1,MEMCFGREG->msc2, + MEMCFGREG->mecr); +} + +static ulong *coreregs[] = { + /* can't trust the bootstrap to put these back */ + &MEMCFGREG->mecr, + &MEMCFGREG->msc0, + &MEMCFGREG->msc1, + &MEMCFGREG->msc2, + + &PPCREG->ppdr, + &PPCREG->ppsr, /* should save? */ + &PPCREG->ppar, + &PPCREG->psdr, + + &GPIOREG->grer, + &GPIOREG->gfer, + &GPIOREG->gafr, + &GPIOREG->gpdr, + /* gplr handled specially */ + + &GPCLKREG->gpclkr1, + &GPCLKREG->gpclkr2, + &GPCLKREG->gpclkr0, + + &OSTMRREG->osmr[0], + &OSTMRREG->osmr[1], + &OSTMRREG->osmr[2], + &OSTMRREG->osmr[3], + &OSTMRREG->oscr, + &OSTMRREG->oier, + /* skip ower */ + + &INTRREG->iclr, + &INTRREG->iccr, + &INTRREG->icmr, /* interrupts enabled */ + + nil, +}; + +static ulong corestate[nelem(coreregs)]; + +void +powersuspend(void) +{ + extern void suspenditall(void); + GpioReg *g; + ulong back = 0x43219990; /* check that the stack's right */ + ulong pwer, gplr; + ulong *rp; + int i, s; + + s = splfhi(); + archpowerdown(); /* sets PMGR and PPC appropriately */ + if(DEBUG) + dumpitall(); + blankscreen(1); + chandevpower(0); + gplr = GPIOREG->gplr; + for(i=0; (rp = coreregs[i]) != nil; i++) + corestate[i] = *rp; + pwer = PMGRREG->pwer; + if(pwer == 0) + pwer = 1<<0; + g = GPIOREG; + g->grer &= pwer; /* just the ones archpowerdown requested */ + g->gfer &= pwer; + g->gedr = g->gedr; + RESETREG->rcsr = 0xF; /* reset all status */ + minidcflush(); + if(DEBUG) + iprint("suspenditall...\n"); + + suspenditall(); /* keep us in suspense */ + + PMGRREG->pspr = 0; + archpowerup(); + trapstacks(); + /* set output latches before gpdr restored */ + GPIOREG->gpsr = gplr; + GPIOREG->gpcr = ~gplr; + for(i=0; (rp = coreregs[i]) != nil; i++) + *rp = corestate[i]; + GPIOREG->gedr = GPIOREG->gedr; /* reset GPIO interrupts (should we?) */ + PMGRREG->pssr = PSSR_ph; /* cancel peripheral hold */ + chandevpower(1); + if(back != 0x43219990){ + iprint("back %8.8lux\n", back); + panic("powersuspend"); + } + blankscreen(0); + if(DEBUG) + dumpitall(); + splx(s); +} diff --git a/os/sa1110/trap.c b/os/sa1110/trap.c new file mode 100644 index 00000000..a625535d --- /dev/null +++ b/os/sa1110/trap.c @@ -0,0 +1,601 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +#define waslo(sr) (!((sr) & (PsrDirq|PsrDfiq))) + +typedef struct Handler Handler; + +struct Handler { + void (*r)(Ureg*, void*); + void *a; + int v; + char name[KNAMELEN]; + Handler *next; +}; + +enum { + MinGpioIRQbit = 11, + NumGpioIRQbits = MaxGPIObit-MinGpioIRQbit+1, + GpioIRQmask = ((1< MaxGPIObit) + panic("intrenable: gpio source %d out of range", v); + g = GPIOREG; + switch(tbdf){ + case BusGPIOfalling: + g->gfer |= 1<grer &= ~(1<grer |= 1<gfer &= ~(1<grer |= 1<gfer |= 1<gpdr &= ~(1<= MinGpioIRQbit) { + ie = &gpiovec[v-MinGpioIRQbit]; + if(ie->r != nil) + iprint("duplicate gpio irq: %d (%s)\n", v, ie->name); + ie->r = f; + ie->a = a; + strncpy(ie->name, name, KNAMELEN-1); + ie->name[KNAMELEN-1] = 0; + iunlock(&veclock); + return; + } + /*FALLTHROUGH for GPIO sources 0-10 */ + case BUSUNKNOWN: + case BusCPU: + if(v < 0 || v > MaxIRQbit) + panic("intrenable: irq source %d out of range", v); + ie = &irqvec[v]; + if(ie->r != nil) + iprint("duplicate irq: %d (%s)\n", v, ie->name); + ie->r = f; + ie->a = a; + strncpy(ie->name, name, KNAMELEN-1); + ie->name[KNAMELEN-1] = 0; + + x = splfhi(); + /* Enable the interrupt by setting the mask bit */ + INTRREG->icmr |= 1 << v; + splx(x); + break; + default: + panic("intrenable: unknown irq bus %d", tbdf); + } + iunlock(&veclock); +} + +void +intrdisable(int v, void (*f)(Ureg*, void*), void* a, int tbdf, char *name) +{ + int x; + GpioReg *g; + Handler *ie; + + ilock(&veclock); + switch(tbdf) { + case BusGPIOfalling: + case BusGPIOrising: + case BusGPIOboth: + if(v < 0 || v > MaxGPIObit) + panic("intrdisable: gpio source %d out of range", v); + if(v >= MinGpioIRQbit) + ie = &gpiovec[v-MinGpioIRQbit]; + else + ie = &irqvec[v]; + if(ie->r != f || ie->a != a || strcmp(ie->name, name) != 0) + break; + ie->r = nil; + if(v < MinGpioIRQbit){ + x = splfhi(); + INTRREG->icmr &= ~(1<gfer &= ~(1<grer &= ~(1<grer &= ~(1<gfer &= ~(1< MaxIRQbit) + panic("intrdisable: irq source %d out of range", v); + ie = &irqvec[v]; + if(ie->r != f || ie->a != a || strcmp(ie->name, name) != 0) + break; + ie->r = nil; + x = splfhi(); + INTRREG->icmr &= ~(1<gedr & GpioIRQmask; + GPIOREG->gedr = e; + for(i = MinGpioIRQbit; i <= MaxGPIObit && e != 0; i++){ + if(e & (1<r != nil){ + cur->r(ur, cur->a); + e &= ~(1<gfer &= ~e; + GPIOREG->grer &= ~e; + iprint("spurious GPIO interrupt: %8.8lux\n", e); + } +} + +static void +intrs(Ureg *ur, ulong ibits) +{ + Handler *cur; + int i, s; + + for(i=0; ir != nil){ + cur->r(ur, cur->a); + ibits &= ~(1<icmr &= ~ibits; + splx(s); + } +} + +/* + * initialise R13 in each trap mode, at the start and after suspend reset. + */ +void +trapstacks(void) +{ + setr13(PsrMfiq, m->fiqstack+nelem(m->fiqstack)); + setr13(PsrMirq, m->irqstack+nelem(m->irqstack)); + setr13(PsrMabt, m->abtstack+nelem(m->abtstack)); + setr13(PsrMund, m->undstack+nelem(m->undstack)); +} + +void +trapinit(void) +{ + int v; + IntrReg *intr = INTRREG; + + intr->icmr = 0; + intr->iclr = IRQ_NONMASK; + + trapstacks(); + + for(v = 0; v < nelem(irqvec); v++) { + irqvec[v].r = nil; + irqvec[v].a = nil; + irqvec[v].v = v; + } + for(v = 0; v < nelem(gpiovec); v++) { + gpiovec[v].r = nil; + gpiovec[v].a = nil; + gpiovec[v].v = v+MinGpioIRQbit; + } + + memmove(page0->vectors, vectors, sizeof(page0->vectors)); + memmove(page0->vtable, vtable, sizeof(page0->vtable)); + dcflush(page0, sizeof(*page0)); + + idle = xspanalloc(13*sizeof(ulong), CACHELINESZ, 0); + memmove(idle, _idlemode, 13*sizeof(ulong)); + dcflush(idle, 13*sizeof(ulong)); + + suspendcode = xspanalloc(16*sizeof(ulong), CACHELINESZ, 0); + memmove(suspendcode, _suspendcode, 16*sizeof(ulong)); + dcflush(suspendcode, 8*sizeof(ulong)); + + icflushall(); + + intrenable(MinGpioIRQbit, gpiointr, nil, BusCPU, "gpio"); +} + +static char *trapnames[PsrMask+1] = { + [ PsrMfiq ] "Fiq interrupt", + [ PsrMirq ] "Mirq interrupt", + [ PsrMsvc ] "SVC/SWI Exception", + [ PsrMabt ] "Prefetch Abort/Data Abort", + [ PsrMabt+1 ] "Data Abort", + [ PsrMund ] "Undefined instruction", + [ PsrMsys ] "Sys trap" +}; + +static char * +trapname(int psr) +{ + char *s; + + s = trapnames[psr & PsrMask]; + if(s == nil) + s = "Undefined trap"; + return s; +} + +static void +sys_trap_error(int type) +{ + char errbuf[ERRMAX]; + sprint(errbuf, "sys: trap: %s\n", trapname(type)); + error(errbuf); +} + +static void +faultarm(Ureg *ureg, ulong far) +{ + char buf[ERRMAX]; + + sprint(buf, "sys: trap: fault pc=%8.8lux addr=0x%lux", (ulong)ureg->pc, far); + if(0){ + iprint("%s\n", buf); + dumpregs(ureg); + } + if(far == ~0) + disfault(ureg, "dereference of nil"); + disfault(ureg, buf); +} + +/* + * All traps come here. It might be slightly slower to have all traps call trap + * rather than directly vectoring the handler. + * However, this avoids a lot of code duplication and possible bugs. + * trap is called splfhi(). + */ +void +trap(Ureg* ureg) +{ + ulong far, fsr; + int rem, t, itype; + Proc *oup; + + if(up != nil) + rem = ((char*)ureg)-up->kstack; + else + rem = ((char*)ureg)-(char*)m->stack; + if(ureg->type != PsrMfiq && rem < 256) + panic("trap %d bytes remaining (%s), up=#%8.8lux ureg=#%8.8lux pc=#%8.8ux", + rem, up?up->text:"", up, ureg, ureg->pc); + + /* + * All interrupts/exceptions should be resumed at ureg->pc-4, + * except for Data Abort which resumes at ureg->pc-8. + */ + itype = ureg->type; + if(itype == PsrMabt+1) + ureg->pc -= 8; + else + ureg->pc -= 4; + ureg->sp = (ulong)(ureg+1); + if(itype == PsrMfiq){ /* fast interrupt (eg, profiler) */ + oup = up; + up = nil; + intrs(ureg, INTRREG->icfp); + up = oup; + return; + } + + /* All other traps */ + + if(ureg->psr & PsrDfiq) + panic("FIQ disabled"); + + if(up){ + up->pc = ureg->pc; + up->dbgreg = ureg; + } + switch(itype) { + case PsrMirq: + t = m->ticks; /* CPU time per proc */ + up = nil; /* no process at interrupt level */ + splflo(); /* allow fast interrupts */ + intrs(ureg, INTRREG->icip); + up = m->proc; + preemption(m->ticks - t); + break; + + case PsrMund: /* Undefined instruction */ + if(*(ulong*)ureg->pc == BREAK && breakhandler) { + int s; + Proc *p; + + p = up; + /* if(!waslo(ureg->psr) || ureg->pc >= (ulong)splhi && ureg->pc < (ulong)islo) + p = 0; */ + s = breakhandler(ureg, p); + if(s == BrkSched) { + p->preempted = 0; + sched(); + } else if(s == BrkNoSched) { + p->preempted = 1; /* stop it being preempted until next instruction */ + if(up) + up->dbgreg = 0; + return; + } + break; + } + if(up == nil) + goto faultpanic; + spllo(); + if(waserror()) { + if(waslo(ureg->psr) && up->type == Interp) + disfault(ureg, up->env->errstr); + setpanic(); + dumpregs(ureg); + panic("%s", up->env->errstr); + } + if(!fpiarm(ureg)) { + dumpregs(ureg); + sys_trap_error(ureg->type); + } + poperror(); + break; + + case PsrMsvc: /* Jump through 0 or SWI */ + if(waslo(ureg->psr) && up && up->type == Interp) { + spllo(); + dumpregs(ureg); + sys_trap_error(ureg->type); + } + setpanic(); + dumpregs(ureg); + panic("SVC/SWI exception"); + break; + + case PsrMabt: /* Prefetch abort */ + if(catchdbg && catchdbg(ureg, 0)) + break; + /* FALL THROUGH */ + case PsrMabt+1: /* Data abort */ + fsr = mmugetfsr(); + far = mmugetfar(); + if(fsr & (1<<9)) { + mmuputfsr(fsr & ~(1<<9)); + if(catchdbg && catchdbg(ureg, fsr)) + break; + print("Debug/"); + } + if(waslo(ureg->psr) && up && up->type == Interp) { + spllo(); + faultarm(ureg, far); + } + print("Data Abort: FSR %8.8luX FAR %8.8luX\n", fsr, far); + /* FALL THROUGH */ + + default: /* ??? */ +faultpanic: + setpanic(); + dumpregs(ureg); + panic("exception %uX %s\n", ureg->type, trapname(ureg->type)); + break; + } + + splhi(); + if(up) + up->dbgreg = 0; /* becomes invalid after return from trap */ +} + +void +setpanic(void) +{ + if(breakhandler != 0) /* don't mess up debugger */ + return; + INTRREG->icmr = 0; + spllo(); + consoleprint = 1; + serwrite = uartputs; +} + +int +isvalid_wa(void *v) +{ + return (ulong)v >= KZERO && (ulong)v < conf.topofmem && !((ulong)v & 3); +} + +int +isvalid_va(void *v) +{ + return (ulong)v >= KZERO && (ulong)v < conf.topofmem; +} + +void +dumplongs(char *msg, ulong *v, int n) +{ + int i, l; + + l = 0; + iprint("%s at %.8p: ", msg, v); + for(i=0; i= 4){ + iprint("\n %.8p: ", v); + l = 0; + } + if(isvalid_va(v)){ + iprint(" %.8lux", *v++); + l++; + }else{ + iprint(" invalid"); + break; + } + } + iprint("\n"); +} + +static void +_dumpstack(Ureg *ureg) +{ + ulong *v, *l; + ulong inst; + ulong *estack; + int i; + + l = (ulong*)(ureg+1); + if(!isvalid_wa(l)){ + iprint("invalid ureg/stack: %.8p\n", l); + return; + } + print("ktrace /kernel/path %.8ux %.8ux %.8ux\n", ureg->pc, ureg->sp, ureg->r14); + if(up != nil && l >= (ulong*)up->kstack && l <= (ulong*)(up->kstack+KSTACK-4)) + estack = (ulong*)(up->kstack+KSTACK); + else if(l >= (ulong*)m->stack && l <= (ulong*)((ulong)m+BY2PG-4)) + estack = (ulong*)((ulong)m+BY2PG-4); + else{ + iprint("unknown stack\n"); + return; + } + i = 0; + for(; ltype)); + if((ureg->psr & PsrMask) != PsrMsvc) + print(" in %s", trapname(ureg->psr)); + print("\n"); + print("PSR %8.8uX type %2.2uX PC %8.8uX LINK %8.8uX\n", + ureg->psr, ureg->type, ureg->pc, ureg->link); + print("R14 %8.8uX R13 %8.8uX R12 %8.8uX R11 %8.8uX R10 %8.8uX\n", + ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10); + print("R9 %8.8uX R8 %8.8uX R7 %8.8uX R6 %8.8uX R5 %8.8uX\n", + ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5); + print("R4 %8.8uX R3 %8.8uX R2 %8.8uX R1 %8.8uX R0 %8.8uX\n", + ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0); + print("Stack is at: %8.8luX\n", ureg); + print("PC %8.8lux LINK %8.8lux\n", (ulong)ureg->pc, (ulong)ureg->link); + + if(up) + print("Process stack: %8.8lux-%8.8lux\n", + up->kstack, up->kstack+KSTACK-4); + else + print("System stack: %8.8lux-%8.8lux\n", + (ulong)(m+1), (ulong)m+BY2PG-4); + dumplongs("stack", (ulong *)(ureg + 1), 16); + _dumpstack(ureg); +} + +/* + * Fill in enough of Ureg to get a stack trace, and call a function. + * Used by debugging interface rdb. + */ +void +callwithureg(void (*fn)(Ureg*)) +{ + Ureg ureg; + ureg.pc = getcallerpc(&fn); + ureg.sp = (ulong)&fn; + ureg.r14 = 0; + fn(&ureg); +} + +void +dumpstack(void) +{ + callwithureg(_dumpstack); +} + +void +trapspecial(int (*f)(Ureg *, uint)) +{ + catchdbg = f; +} -- cgit v1.2.3