From 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a Mon Sep 17 00:00:00 2001 From: "Charles.Forsyth" Date: Fri, 22 Dec 2006 21:39:35 +0000 Subject: 20060303 --- 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 ++++ 258 files changed, 69594 insertions(+) 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 (limited to 'os/boot') 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<